From 3e64d45deffec9ba671bb02ca7f8280d6c7373ef Mon Sep 17 00:00:00 2001 From: brblacky Date: Tue, 26 Dec 2023 13:45:51 +0530 Subject: [PATCH] Update dependencies and and update to lavalink v4, shoukaku v4 --- package.json | 37 +++++++++++------------ src/commands/config/247.ts | 2 +- src/commands/dev/Eval.ts | 43 ++++++++++++++++++++++++++- src/commands/music/Grab.ts | 8 ++--- src/commands/music/Join.ts | 6 ++-- src/commands/music/Leave.ts | 2 +- src/commands/music/Nowplaying.ts | 2 +- src/commands/music/Play.ts | 31 ++++++++++--------- src/commands/music/Search.ts | 18 +++++------ src/commands/music/Volume.ts | 4 +-- src/commands/playlist/Add.ts | 8 ++++- src/commands/playlist/Load.ts | 2 +- src/events/client/GuildCreate.ts | 9 ++++-- src/events/client/GuildDelete.ts | 9 ++++-- src/events/client/SetupButtons.ts | 22 +++++++------- src/events/client/SetupSystem.ts | 15 +++++----- src/events/client/VoiceStateUpdate.ts | 8 ++--- src/events/player/NodeConnect.ts | 6 ++-- src/events/player/TrackStart.ts | 14 ++++----- src/plugin/plugins/antiCrash.ts | 12 ++++++++ src/structures/Dispatcher.ts | 40 +++++++++++-------------- src/structures/Queue.ts | 6 ++-- src/structures/Shoukaku.ts | 19 +++++++----- src/utils/SetupSystem.ts | 37 +++++++++++------------ 24 files changed, 208 insertions(+), 152 deletions(-) diff --git a/package.json b/package.json index 0e809107b..b8b120e5d 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,13 @@ { "name": "lavamusic", - "version": "4.0.1", + "version": "4.0.2", "description": "LavaMusic is a music bot for Discord, written in JavaScript using the Discord.js, Typescript, Shoukaku (Lavalink) library.", "main": "dist/index.js", "type": "module", "scripts": { - "start": "npm run start:bot", "prisma:generate": "npx prisma generate", - "start:bot": "npm run build && node dist/index.js", - "dev": "nodemon --watch src --exec npm run start:bot", + "start": "npm run build && node dist/index.js", + "dev": "npm run build && nodemon --watch dist --exec node dist/index.js", "build": "tsc --project tsconfig.json", "clean": "rm -rf dist", "lint": "eslint . --cache --ext .js,.jsx,.ts,.tsx", @@ -37,27 +36,27 @@ }, "homepage": "https://github.com/brblacky/lavamusic#readme", "devDependencies": { - "@types/node": "^20.8.7", - "@types/signale": "^1.4.6", - "@typescript-eslint/eslint-plugin": "^6.8.0", - "@typescript-eslint/parser": "^6.8.0", - "eslint": "^8.52.0", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-unicorn": "^48.0.1", - "prettier": "^3.0.3", - "prisma": "^5.5.2", - "ts-node": "^10.9.1", - "typescript": "^5.2.2" + "@types/node": "^20.10.5", + "@types/signale": "^1.4.7", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-unicorn": "^50.0.1", + "prettier": "^3.1.1", + "prisma": "^5.7.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" }, "dependencies": { - "@prisma/client": "^5.5.2", - "discord.js": "^14.12.1", + "@prisma/client": "^5.7.1", + "discord.js": "^14.14.1", "dotenv": "^16.3.1", - "shoukaku": "^3.4.0", + "shoukaku": "^4.0.1", "signale": "^1.4.0", "socket.io": "^4.7.2", "tslib": "^2.6.2", - "undici": "^5.26.4" + "undici": "^6.2.1" }, "signale": { "displayScope": true, diff --git a/src/commands/config/247.ts b/src/commands/config/247.ts index af0cb9d80..89650554e 100644 --- a/src/commands/config/247.ts +++ b/src/commands/config/247.ts @@ -46,7 +46,7 @@ export default class _247 extends Command { ctx.guild, vc.voice.channel, ctx.channel, - client.shoukaku.getNode() + client.shoukaku.options.nodeResolver(client.shoukaku.nodes) ); return await ctx.sendMessage({ embeds: [ diff --git a/src/commands/dev/Eval.ts b/src/commands/dev/Eval.ts index 87fb2fda4..f3e145a55 100644 --- a/src/commands/dev/Eval.ts +++ b/src/commands/dev/Eval.ts @@ -1,3 +1,6 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { fetch } from 'undici'; + import { Command, Context, Lavamusic } from '../../structures/index.js'; export default class Eval extends Command { @@ -33,8 +36,46 @@ export default class Eval extends Command { const code = args.join(' '); try { let evaled = eval(code); + + if (evaled == client.config) evaled = 'Nice try'; + + const button = new ButtonBuilder() + .setStyle(ButtonStyle.Danger) + .setLabel('Delete') + .setCustomId('eval-delete'); + const row = new ActionRowBuilder().addComponents(button); + if (typeof evaled !== 'string') evaled = (await import('node:util')).inspect(evaled); - ctx.sendMessage(`\`\`\`js\n${evaled}\n\`\`\``); + + if (evaled.length > 2000) { + const response = await fetch('https://hasteb.in/post', { + method: 'POST', + headers: { + 'Content-Type': 'text/plain', + }, + body: evaled, + }) + + const json = await response.json() as { key: string }; + evaled = `https://hasteb.in/${json.key}`; + return await ctx.sendMessage({ + content: evaled, + }); + } else { + const msg = await ctx.sendMessage({ + content: `\`\`\`js\n${evaled}\n\`\`\``, + components: [row], + }); + const filter = (i): boolean => i.customId === 'eval-delete' && i.user.id === ctx.author.id; + const collector = msg.createMessageComponentCollector({ + time: 60000, + filter: filter, + }); + collector.on('collect', async (i) => { + await i.deferUpdate(); + await msg.delete(); + }); + } } catch (e) { ctx.sendMessage(`\`\`\`js\n${e}\n\`\`\``); } diff --git a/src/commands/music/Grab.ts b/src/commands/music/Grab.ts index 5a5f4df56..3c7abdc8a 100644 --- a/src/commands/music/Grab.ts +++ b/src/commands/music/Grab.ts @@ -38,12 +38,10 @@ export default class Grab extends Command { .embed() .setTitle(`**${song.info.title}**`) .setURL(song.info.uri) - .setThumbnail(song.info.thumbnail) + .setThumbnail(song.info.artworkUrl) .setDescription( - `**Duration:** ${ - song.info.isStream ? 'LIVE' : client.utils.formatTime(song.info.length) - }\n**Requested by:** ${song.info.requester}\n**Link:** [Click here](${ - song.info.uri + `**Duration:** ${song.info.isStream ? 'LIVE' : client.utils.formatTime(song.info.length) + }\n**Requested by:** ${song.info.requester}\n**Link:** [Click here](${song.info.uri })` ) .setColor(client.color.main); diff --git a/src/commands/music/Join.ts b/src/commands/music/Join.ts index 06e192b9a..cd006ef29 100644 --- a/src/commands/music/Join.ts +++ b/src/commands/music/Join.ts @@ -37,13 +37,13 @@ export default class Join extends Command { ctx.guild, vc.voice.channel, ctx.channel, - client.shoukaku.getNode() + client.shoukaku.options.nodeResolver(client.shoukaku.nodes) ); return await ctx.sendMessage({ embeds: [ embed .setColor(this.client.color.main) - .setDescription(`Joined <#${player.player.connection.channelId}>`), + .setDescription(`Joined <#${player.node.manager.connections.get(ctx.guild.id).channelId}>`), ], }); } else { @@ -52,7 +52,7 @@ export default class Join extends Command { embed .setColor(this.client.color.main) .setDescription( - `I'm already connected to <#${player.player.connection.channelId}>` + `I'm already connected to <#${player.node.manager.connections.get(ctx.guild.id).channelId}>` ), ], }); diff --git a/src/commands/music/Leave.ts b/src/commands/music/Leave.ts index e1e4aec20..7427f222b 100644 --- a/src/commands/music/Leave.ts +++ b/src/commands/music/Leave.ts @@ -36,7 +36,7 @@ export default class Leave extends Command { embeds: [ embed .setColor(this.client.color.main) - .setDescription(`Left <#${player.player.connection.channelId}>`), + .setDescription(`Left <#${player.node.manager.connections.get(ctx.guild.id).channelId}>`), ], }); player.destroy(); diff --git a/src/commands/music/Nowplaying.ts b/src/commands/music/Nowplaying.ts index 01b2a6cb3..14064c533 100644 --- a/src/commands/music/Nowplaying.ts +++ b/src/commands/music/Nowplaying.ts @@ -39,7 +39,7 @@ export default class Nowplaying extends Command { .embed() .setColor(this.client.color.main) .setAuthor({ name: 'Now Playing', iconURL: ctx.guild.iconURL({}) }) - .setThumbnail(track.info.thumbnail) + .setThumbnail(track.info.artworkUrl) .setDescription( `[${track.info.title}](${track.info.uri}) - Request By: ${track.info.requester}\n\n\`${bar}\`` ) diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index f4aaed16c..7c7fc33b6 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -1,3 +1,5 @@ +import { LoadType } from 'shoukaku'; + import { Command, Context, Lavamusic } from '../../structures/index.js'; export default class Play extends Command { @@ -47,14 +49,13 @@ export default class Play extends Command { player = await client.queue.create( ctx.guild, vc.voice.channel, - ctx.channel, - client.shoukaku.getNode() + ctx.channel ); const res = await this.client.queue.search(query); const embed = this.client.embed(); switch (res.loadType) { - case 'LOAD_FAILED': + case LoadType.ERROR: ctx.sendMessage({ embeds: [ embed @@ -63,7 +64,7 @@ export default class Play extends Command { ], }); break; - case 'NO_MATCHES': + case LoadType.EMPTY: ctx.sendMessage({ embeds: [ embed @@ -72,8 +73,8 @@ export default class Play extends Command { ], }); break; - case 'TRACK_LOADED': { - const track = player.buildTrack(res.tracks[0], ctx.author); + case LoadType.TRACK: { + const track = player.buildTrack(res.data[0], ctx.author); if (player.queue.length > client.config.maxQueueSize) return await ctx.sendMessage({ embeds: [ @@ -91,14 +92,14 @@ export default class Play extends Command { embed .setColor(this.client.color.main) .setDescription( - `Added [${res.tracks[0].info.title}](${res.tracks[0].info.uri}) to the queue.` + `Added [${res.data[0].info.title}](${res.data[0].info.uri}) to the queue.` ), ], }); break; } - case 'PLAYLIST_LOADED': - if (res.length > client.config.maxPlaylistSize) + case LoadType.PLAYLIST: { + if (res.data.tracks.length > client.config.maxPlaylistSize) return await ctx.sendMessage({ embeds: [ embed @@ -108,7 +109,7 @@ export default class Play extends Command { ), ], }); - for (const track of res.tracks) { + for (const track of res.data.tracks) { const pl = player.buildTrack(track, ctx.author); if (player.queue.length > client.config.maxQueueSize) return await ctx.sendMessage({ @@ -127,12 +128,14 @@ export default class Play extends Command { embeds: [ embed .setColor(this.client.color.main) - .setDescription(`Added ${res.tracks.length} songs to the queue.`), + .setDescription(`Added ${res.data.tracks.length} songs to the queue.`), ], }); break; - case 'SEARCH_RESULT': { - const track1 = player.buildTrack(res.tracks[0], ctx.author); + } + case LoadType.SEARCH: { + + const track1 = player.buildTrack(res.data[0], ctx.author); if (player.queue.length > client.config.maxQueueSize) return await ctx.sendMessage({ embeds: [ @@ -150,7 +153,7 @@ export default class Play extends Command { embed .setColor(this.client.color.main) .setDescription( - `Added [${res.tracks[0].info.title}](${res.tracks[0].info.uri}) to the queue.` + `Added [${res.data[0].info.title}](${res.data[0].info.uri}) to the queue.` ), ], }); diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index 0edb7542a..af3acd944 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -1,4 +1,5 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { LoadType } from 'shoukaku'; import { Song } from '../../structures/Dispatcher.js'; import { Command, Context, Lavamusic } from '../../structures/index.js'; @@ -48,7 +49,7 @@ export default class Search extends Command { ctx.guild, vc.voice.channel, ctx.channel, - client.shoukaku.getNode() + client.shoukaku.options.nodeResolver(client.shoukaku.nodes) ); } const res = await this.client.queue.search(query); @@ -64,7 +65,7 @@ export default class Search extends Command { new ButtonBuilder().setCustomId('5').setLabel('5').setStyle(ButtonStyle.Primary) ); switch (res.loadType) { - case 'LOAD_FAILED': + case LoadType.ERROR: ctx.sendMessage({ embeds: [ embed @@ -73,7 +74,7 @@ export default class Search extends Command { ], }); break; - case 'NO_MATCHES': + case LoadType.EMPTY: ctx.sendMessage({ embeds: [ embed @@ -82,12 +83,11 @@ export default class Search extends Command { ], }); break; - case 'SEARCH_RESULT': { - const tracks = res.tracks.slice(0, 5); + case LoadType.SEARCH: { + const tracks = res.data.slice(0, 5); const embeds = tracks.map( (track: Song, index: number) => - `${index + 1}. [${track.info.title}](${track.info.uri}) - \`${ - track.info.author + `${index + 1}. [${track.info.title}](${track.info.uri}) - \`${track.info.author }\`` ); await ctx.sendMessage({ @@ -104,9 +104,9 @@ export default class Search extends Command { idle: 60000 / 2, }); collector.on('collect', async (int: any) => { - for (let i = 0; i < res.tracks.length; i++) { + for (let i = 0; i < res.data.length; i++) { if (int.customId === `${i + 1}`) { - let track = res.tracks[i]; + let track = res.data[i]; track = player.buildTrack(track, ctx.author); player.queue.push(track); player.isPlaying(); diff --git a/src/commands/music/Volume.ts b/src/commands/music/Volume.ts index 836d497c5..da1434efc 100644 --- a/src/commands/music/Volume.ts +++ b/src/commands/music/Volume.ts @@ -63,12 +63,12 @@ export default class Volume extends Command { .setDescription('The volume can\'t be lower than 0.'), ], }); - player.player.setVolume(number / 100); + player.player.setGlobalVolume(number); return await ctx.sendMessage({ embeds: [ embed .setColor(this.client.color.main) - .setDescription(`Set the volume to ${(player.volume * 100).toFixed()}`), + .setDescription(`Set the volume to ${(player.volume).toFixed()}`), ], }); } diff --git a/src/commands/playlist/Add.ts b/src/commands/playlist/Add.ts index 43b34f2db..ab6dadc8d 100644 --- a/src/commands/playlist/Add.ts +++ b/src/commands/playlist/Add.ts @@ -1,4 +1,5 @@ import { ApplicationCommandOptionType } from 'discord.js'; +import { LoadType } from 'shoukaku'; import { Command, Context, Lavamusic } from '../../structures/index.js'; @@ -94,7 +95,12 @@ export default class Add extends Command { }, ], }); - const trackStrings = res.tracks.map(track => JSON.stringify(track)); + let trackStrings; + if (res.loadType === LoadType.PLAYLIST) { + trackStrings = res.data.tracks.map((track) => JSON.stringify(track)); + } else { + trackStrings = [JSON.stringify(res.data[0])]; + } await client.prisma.playlist.update({ where: { id: playlistData.id, diff --git a/src/commands/playlist/Load.ts b/src/commands/playlist/Load.ts index badb34c3b..1318b1d79 100644 --- a/src/commands/playlist/Load.ts +++ b/src/commands/playlist/Load.ts @@ -62,7 +62,7 @@ export default class Load extends Command { ctx.guild, vc.voice.channel, ctx.channel, - client.shoukaku.getNode() + client.shoukaku.options.nodeResolver(client.shoukaku.nodes) ); const track = player.buildTrack(song, ctx.author); diff --git a/src/events/client/GuildCreate.ts b/src/events/client/GuildCreate.ts index 6000592d1..e5c1c113d 100644 --- a/src/events/client/GuildCreate.ts +++ b/src/events/client/GuildCreate.ts @@ -1,4 +1,4 @@ -import { EmbedBuilder, Guild, TextChannel } from 'discord.js'; +import { EmbedBuilder, Guild, GuildMember, TextChannel } from 'discord.js'; import { Event, Lavamusic } from '../../structures/index.js'; @@ -9,7 +9,12 @@ export default class GuildCreate extends Event { }); } public async run(guild: Guild): Promise { - const owner = await guild.fetchOwner(); + let owner: GuildMember | undefined; + try { + owner = await guild.fetchOwner(); + } catch (e) { + owner = await guild.members.fetch(guild.ownerId); + } const embed = new EmbedBuilder() .setColor(this.client.config.color.green) .setAuthor({ name: guild.name, iconURL: guild.iconURL({ extension: 'jpeg' }) }) diff --git a/src/events/client/GuildDelete.ts b/src/events/client/GuildDelete.ts index 66b6a77f3..907c3b2f6 100644 --- a/src/events/client/GuildDelete.ts +++ b/src/events/client/GuildDelete.ts @@ -1,4 +1,4 @@ -import { EmbedBuilder, Guild, TextChannel } from 'discord.js'; +import { EmbedBuilder, Guild, GuildMember, TextChannel } from 'discord.js'; import { Event, Lavamusic } from '../../structures/index.js'; @@ -9,7 +9,12 @@ export default class GuildDelete extends Event { }); } public async run(guild: Guild): Promise { - const owner = await guild.fetchOwner(); + let owner: GuildMember | undefined; + try { + owner = await guild.fetchOwner(); + } catch (e) { + owner = await guild.members.fetch(guild.ownerId); + } const embed = new EmbedBuilder() .setColor(this.client.config.color.red) .setAuthor({ name: guild.name, iconURL: guild.iconURL({ extension: 'jpeg' }) }) diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts index d09950986..aa3dc1c54 100644 --- a/src/events/client/SetupButtons.ts +++ b/src/events/client/SetupButtons.ts @@ -8,7 +8,7 @@ export default class SetupButtons extends Event { }); } public async run(interaction: any): Promise { - if (!interaction.replied) await interaction.deferReply().catch(() => {}); + if (!interaction.replied) await interaction.deferReply().catch(() => { }); if (!interaction.member.voice.channel) return await buttonReply( interaction, @@ -18,7 +18,7 @@ export default class SetupButtons extends Event { if ( interaction.guild.members.cache.get(this.client.user.id).voice.channel && interaction.guild.members.cache.get(this.client.user.id).voice.channelId !== - interaction.member.voice.channelId + interaction.member.voice.channelId ) return await buttonReply( interaction, @@ -57,7 +57,7 @@ export default class SetupButtons extends Event { /* empty */ } const icon = player - ? player.current.info.thumbnail + ? player.current.info.artworkUrl : this.client.user.displayAvatarURL({ extension: 'png' }); let iconUrl = this.client.config.icons[player.current.info.sourceName]; if (!iconUrl) iconUrl = this.client.user.displayAvatarURL({ extension: 'png' }); @@ -66,8 +66,7 @@ export default class SetupButtons extends Event { .embed() .setAuthor({ name: `Now Playing`, iconURL: iconUrl }) .setDescription( - `[${title}](${uri}) - ${ - player.current.info.isStream ? 'LIVE' : this.client.utils.formatTime(length) + `[${title}](${uri}) - ${player.current.info.isStream ? 'LIVE' : this.client.utils.formatTime(length) } - Requested by ${player.current.info.requester}` ) .setImage(icon); @@ -75,8 +74,8 @@ export default class SetupButtons extends Event { if (message) { switch (interaction.customId) { case 'LOW_VOL_BUT': { - const vol = player.volume * 100 - 10; - player.player.setVolume(vol / 100); + const vol = player.volume - 10; + player.player.setGlobalVolume(vol); await buttonReply( interaction, `Volume set to ${vol.toFixed()}%`, @@ -93,8 +92,8 @@ export default class SetupButtons extends Event { break; } case 'HIGH_VOL_BUT': { - const vol2 = player.volume * 100 + 10; - player.player.setVolume(vol2 / 100); + const vol2 = player.volume + 10; + player.player.setGlobalVolume(vol2); await buttonReply( interaction, `Volume set to ${vol2.toFixed()}%`, @@ -189,9 +188,8 @@ export default class SetupButtons extends Event { await message.edit({ embeds: [ embed.setFooter({ - text: `Shuffle set to ${player.shuffle ? `on` : `off`} by ${ - interaction.member.displayName - }`, + text: `Shuffle set to ${player.shuffle ? `on` : `off`} by ${interaction.member.displayName + }`, iconURL: interaction.member.displayAvatarURL({}), }), ], diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index 44a4c6177..c2c6557e6 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -15,7 +15,7 @@ export default class SetupSystem extends Event { if (!(channel instanceof TextChannel)) return; if (!message.member.voice.channel) { await oops(channel, `You are not connected to a voice channel to queue songs.`); - if (message) await message.delete().catch(() => {}); + if (message) await message.delete().catch(() => { }); return; } @@ -28,22 +28,21 @@ export default class SetupSystem extends Event { channel, `I don't have enough permission to connect/speak in <#${message.member.voice.channel.id}>` ); - if (message) await message.delete().catch(() => {}); + if (message) await message.delete().catch(() => { }); return; } if ( message.guild.members.cache.get(this.client.user.id).voice.channel && message.guild.members.cache.get(this.client.user.id).voice.channelId !== - message.member.voice.channelId + message.member.voice.channelId ) { await oops( channel, - `You are not connected to <#${ - message.guild.members.cache.get(this.client.user.id).voice.channelId + `You are not connected to <#${message.guild.members.cache.get(this.client.user.id).voice.channelId }> to queue songs` ); - if (message) await message.delete().catch(() => {}); + if (message) await message.delete().catch(() => { }); return; } let player = this.client.queue.get(message.guildId); @@ -52,11 +51,11 @@ export default class SetupSystem extends Event { message.guild, message.member.voice.channel, message.channel, - this.client.shoukaku.getNode() + this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes) ); } await setupStart(this.client, message.content, player, message); - if (message) await message.delete().catch(() => {}); + if (message) await message.delete().catch(() => { }); } } diff --git a/src/events/client/VoiceStateUpdate.ts b/src/events/client/VoiceStateUpdate.ts index 54220e240..5a6035a55 100644 --- a/src/events/client/VoiceStateUpdate.ts +++ b/src/events/client/VoiceStateUpdate.ts @@ -35,7 +35,7 @@ export default class VoiceStateUpdate extends Event { } } if (newState.id == this.client.user.id) return; - const vc = newState.guild.channels.cache.get(player.player.connection.channelId); + const vc = newState.guild.channels.cache.get(player.node.manager.connections.get(newState.guild.id).channelId); if ( newState.id === this.client.user.id && !newState.serverDeaf && @@ -48,7 +48,7 @@ export default class VoiceStateUpdate extends Event { if (newState.id === this.client.user.id && !newState.serverMute && player.paused) player.pause(); - let voiceChannel = newState.guild.channels.cache.get(player.player.connection.channelId); + let voiceChannel = newState.guild.channels.cache.get(player.node.manager.connections.get(newState.guild.id).channelId); if (newState.id === this.client.user.id && newState.channelId === null) return; @@ -60,7 +60,7 @@ export default class VoiceStateUpdate extends Event { if (!server) { setTimeout(async () => { const playerVoiceChannel = newState.guild.channels.cache.get( - player.player.connection.channelId + player.node.manager.connections.get(newState.guild.id).channelId ); if ( player && @@ -76,7 +76,7 @@ export default class VoiceStateUpdate extends Event { if (server) return; setTimeout(async () => { const playerVoiceChannel = newState.guild.channels.cache.get( - player.player.connection.channelId + player.node.manager.connections.get(newState.guild.id).channelId ); if ( player && diff --git a/src/events/player/NodeConnect.ts b/src/events/player/NodeConnect.ts index 33426cf58..2a5ef37ef 100644 --- a/src/events/player/NodeConnect.ts +++ b/src/events/player/NodeConnect.ts @@ -1,5 +1,3 @@ -import { Node } from 'shoukaku'; - import { Event, Lavamusic } from '../../structures/index.js'; export default class NodeConnect extends Event { @@ -8,8 +6,8 @@ export default class NodeConnect extends Event { name: 'nodeConnect', }); } - public async run(node: Node): Promise { - this.client.logger.success(`Node ${node.name} is ready!`); + public async run(node: string): Promise { + this.client.logger.success(`Node ${node} is ready!`); const data = await this.client.prisma.stay.findMany(); if (!data) return; for (const main of data) { diff --git a/src/events/player/TrackStart.ts b/src/events/player/TrackStart.ts index 9b7e4bcc8..7b7c58554 100644 --- a/src/events/player/TrackStart.ts +++ b/src/events/player/TrackStart.ts @@ -24,7 +24,7 @@ export default class TrackStart extends Event { }); } public async run(player: Player, track: Song, dispatcher: Dispatcher): Promise { - const guild = this.client.guilds.cache.get(player.connection.guildId); + const guild = this.client.guilds.cache.get(player.guildId); if (!guild) return; const channel = guild.channels.cache.get(dispatcher.channelId) as TextChannel; if (!channel) return; @@ -75,7 +75,7 @@ export default class TrackStart extends Event { text: `Requested by ${track.info.requester.tag}`, iconURL: track.info.requester.avatarURL({}), }) - .setThumbnail(track.info.thumbnail) + .setThumbnail(track.info.artworkUrl) .addFields( { name: 'Duration', @@ -116,9 +116,8 @@ export default class TrackStart extends Event { return true; else { b.reply({ - content: `You are not connected to <#${ - b.guild.members.me.voice?.channelId ?? 'None' - }> to use this buttons.`, + content: `You are not connected to <#${b.guild.members.me.voice?.channelId ?? 'None' + }> to use this buttons.`, ephemeral: true, }); return false; @@ -161,9 +160,8 @@ export default class TrackStart extends Event { await message.edit({ embeds: [ embed.setFooter({ - text: `${player.paused ? 'Paused' : 'Resumed'} by ${ - interaction.user.tag - }`, + text: `${player.paused ? 'Paused' : 'Resumed'} by ${interaction.user.tag + }`, iconURL: interaction.user.avatarURL({}), }), ], diff --git a/src/plugin/plugins/antiCrash.ts b/src/plugin/plugins/antiCrash.ts index cc27e2c5d..09cc7acda 100644 --- a/src/plugin/plugins/antiCrash.ts +++ b/src/plugin/plugins/antiCrash.ts @@ -12,6 +12,18 @@ const antiCrash: BotPlugin = { process.on('uncaughtException', err => { client.logger.error('Uncaught Exception thrown:', err); }); + + const handleExit = async (): Promise => { + if (client) { + client.logger.star('Disconnecting from Discord...'); + await client.destroy(); + client.logger.success('Successfully disconnected from Discord!'); + process.exit(); + } + }; + process.on('SIGINT', handleExit); + process.on('SIGTERM', handleExit); + process.on('SIGQUIT', handleExit); }, }; diff --git a/src/structures/Dispatcher.ts b/src/structures/Dispatcher.ts index 4d3b2536f..0730afd40 100644 --- a/src/structures/Dispatcher.ts +++ b/src/structures/Dispatcher.ts @@ -5,7 +5,7 @@ import { Node, Player, Track } from 'shoukaku'; import { Lavamusic } from './index.js'; export class Song implements Track { - track: string; + encoded: string; info: { identifier: string; isSeekable: boolean; @@ -14,19 +14,19 @@ export class Song implements Track { isStream: boolean; position: number; title: string; - uri: string; + uri?: string; + artworkUrl?: string; + isrc?: string; sourceName: string; - thumbnail?: string; - requester?: User; - }; + requester: User + } + pluginInfo: unknown; + constructor(track: Song, user: User) { if (!track) throw new Error('Track is not provided'); - this.track = track.track; + this.encoded = track.encoded; this.info = track.info; if (this.info && this.info.requester === undefined) this.info.requester = user; - if (track.info.sourceName === 'youtube') { - track.info.thumbnail = `https://img.youtube.com/vi/${track.info.identifier}/hqdefault.jpg`; - } } } export default class Dispatcher { @@ -39,7 +39,6 @@ export default class Dispatcher { public previous: Song | null; public current: Song | null; public loop: 'off' | 'repeat' | 'queue'; - public matchedTracks: Song[]; public requester: User; public repeat: number; public node: Node; @@ -60,7 +59,6 @@ export default class Dispatcher { this.previous = null; this.current = null; this.loop = 'off'; - this.matchedTracks = []; this.repeat = 0; this.node = options.node; this.shuffle = false; @@ -95,12 +93,8 @@ export default class Dispatcher { return; } this.current = this.queue.length !== 0 ? this.queue.shift() : this.queue[0]; - if (this.matchedTracks.length !== 0) this.matchedTracks = []; - const search = (await this.node.rest.resolve( - `${this.client.config.searchEngine}:${this.current?.info.title} ${this.current?.info.author}` - )) as any; - this.matchedTracks.push(...search.tracks); - this.player.playTrack({ track: this.current?.track }); + if (!this.current) return; + this.player.playTrack({ track: this.current?.encoded }); if (this.current) { this.history.push(this.current); if (this.history.length > 100) { @@ -132,7 +126,7 @@ export default class Dispatcher { public destroy(): void { this.queue.length = 0; this.history = []; - this.player.connection.disconnect(); + this.client.shoukaku.leaveVoiceChannel(this.guildId); this.client.queue.delete(this.guildId); if (this.stopped) return; this.client.shoukaku.emit('playerDestroy', this.player); @@ -175,7 +169,6 @@ export default class Dispatcher { this.repeat = 0; this.stopped = true; this.player.stopTrack(); - this.player.connection.disconnect(); } public setLoop(loop: any): void { this.loop = loop; @@ -193,22 +186,23 @@ export default class Dispatcher { const resolve = await this.node.rest.resolve( `${this.client.config.searchEngine}:${song.info.author}` ); - if (!resolve || !resolve.tracks.length) return this.destroy(); + if (!resolve || !resolve?.data || !Array.isArray(resolve.data)) return this.destroy(); + const metadata = (resolve.data as Array).shift(); let choosed: Song | null = null; const maxAttempts = 10; // Maximum number of attempts to find a unique song let attempts = 0; while (attempts < maxAttempts) { const potentialChoice = new Song( - resolve.tracks[Math.floor(Math.random() * resolve.tracks.length)], + metadata.encoded[Math.floor(Math.random() * metadata.encoded.length)], this.client.user ); // Check if the chosen song is not already in the queue or history if ( - !this.queue.some(s => s.track === potentialChoice.track) && - !this.history.some(s => s.track === potentialChoice.track) + !this.queue.some(s => s.encoded === potentialChoice.encoded) && + !this.history.some(s => s.encoded === potentialChoice.encoded) ) { choosed = potentialChoice; break; diff --git a/src/structures/Queue.ts b/src/structures/Queue.ts index 57d2818b4..4562dadea 100644 --- a/src/structures/Queue.ts +++ b/src/structures/Queue.ts @@ -32,8 +32,8 @@ export class Queue extends Map { if (!channel) throw new Error('No text channel was provided'); if (!guild) throw new Error('No guild was provided'); if (!dispatcher) { - const node = givenNode || this.client.shoukaku.getNode(); - const player = await node.joinChannel({ + const node = givenNode || this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes); + const player = await this.client.shoukaku.joinVoiceChannel({ guildId: guild.id, channelId: voice.id, shardId: guild.shard.id, @@ -57,7 +57,7 @@ export class Queue extends Map { } public async search(query: string): Promise { - const node = this.client.shoukaku.getNode(); + const node = this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes); const regex = /^https?:\/\//; let result: any; try { diff --git a/src/structures/Shoukaku.ts b/src/structures/Shoukaku.ts index 0621fca66..2cca96106 100644 --- a/src/structures/Shoukaku.ts +++ b/src/structures/Shoukaku.ts @@ -11,24 +11,27 @@ export default class ShoukakuClient extends Shoukaku { reconnectInterval: 30, reconnectTries: 2, restTimeout: 10000, + userAgent: `Lavamusic (@devblacky)`, // don't change this + nodeResolver: (nodes) => [...nodes.values()] + .filter(node => node.state === 2) + .sort((a, b) => a.penalties - b.penalties) + .shift() }); this.client = client; - this.on('ready', (name, resumed) => + this.on('ready', (name, reconnected) => { this.client.shoukaku.emit( - resumed ? 'nodeReconnect' : 'nodeConnect', - this.client.shoukaku.getNode(name) + reconnected ? 'nodeReconnect' : 'nodeConnect', + name, ) - ); + }); this.on('error', (name, error) => this.client.shoukaku.emit('nodeError', name, error)); this.on('close', (name, code, reason) => this.client.shoukaku.emit('nodeDestroy', name, code, reason) ); - - this.on('disconnect', (name, players, moved) => { - if (moved) this.emit('playerMove', players); - this.client.shoukaku.emit('nodeDisconnect', name, players); + this.on('disconnect', (name, count) => { + this.client.shoukaku.emit('nodeDisconnect', name, count); }); this.on('debug', (name, reason) => this.client.shoukaku.emit('nodeRaw', name, reason)); diff --git a/src/utils/SetupSystem.ts b/src/utils/SetupSystem.ts index 65fc3820c..b7e0fb022 100644 --- a/src/utils/SetupSystem.ts +++ b/src/utils/SetupSystem.ts @@ -9,14 +9,12 @@ function neb(embed: EmbedBuilder, player: Dispatcher, client: Lavamusic): EmbedB let iconUrl = client.config.icons[player.current.info.sourceName]; if (!iconUrl) iconUrl = client.user.displayAvatarURL({ extension: 'png' }); - let icon = player.current ? player.current.info.thumbnail : client.config.links.img; + let icon = player.current ? player.current.info.artworkUrl : client.config.links.img; return embed .setAuthor({ name: 'Now Playing', iconURL: iconUrl }) .setDescription( - `[${player.current.info.title}](${player.current.info.uri}) by ${ - player.current.info.author - } • \`[${client.utils.formatTime(player.current.info.length)}]\` - Requested by ${ - player.current.info.requester + `[${player.current.info.title}](${player.current.info.uri}) by ${player.current.info.author + } • \`[${client.utils.formatTime(player.current.info.length)}]\` - Requested by ${player.current.info.requester }` ) .setImage(icon) @@ -117,7 +115,7 @@ async function setupStart( }, 5000); }); neb(n, player, client); - if (m) await m.edit({ embeds: [n] }).catch(() => {}); + if (m) await m.edit({ embeds: [n] }).catch(() => { }); break; case 'PLAYLIST_LOADED': if (res.length > client.config.maxPlaylistSize) { @@ -177,7 +175,7 @@ async function setupStart( }, 5000); }); neb(n, player, client); - if (m) await m.edit({ embeds: [n] }).catch(() => {}); + if (m) await m.edit({ embeds: [n] }).catch(() => { }); break; case 'SEARCH_RESULT': const track2 = player.buildTrack(res.tracks[0], message.author); @@ -217,7 +215,7 @@ async function setupStart( }, 5000); }); neb(n, player, client); - if (m) await m.edit({ embeds: [n] }).catch(() => {}); + if (m) await m.edit({ embeds: [n] }).catch(() => { }); break; } } catch (error) { @@ -232,7 +230,7 @@ async function trackStart( track: Song, client: Lavamusic ): Promise { - let icon = player.current ? player.current.info.thumbnail : client.config.links.img; + let icon = player.current ? player.current.info.artworkUrl : client.config.links.img; let m: Message; try { m = await channel.messages.fetch({ message: msgId, cache: true }); @@ -262,7 +260,7 @@ async function trackStart( return b; }), }) - .catch(() => {}); + .catch(() => { }); } else { let iconUrl = client.config.icons[player.current.info.sourceName]; if (!iconUrl) iconUrl = client.user.displayAvatarURL({ extension: 'png' }); @@ -296,7 +294,7 @@ async function trackStart( }, }); }) - .catch(() => {}); + .catch(() => { }); } } @@ -326,13 +324,12 @@ async function updateSetup(client: Lavamusic, guild: any): Promise { .setAuthor({ name: `Now Playing`, iconURL: iconUrl }) .setColor(client.color.main) .setDescription( - `[${player.current.info.title}](${ - player.current.info.uri + `[${player.current.info.title}](${player.current.info.uri }) - \`[${client.utils.formatTime( player.current.info.length )}]\` - Requested by ${player.current.info.requester}` ) - .setImage(player.current.info.thumbnail); + .setImage(player.current.info.artworkUrl); await m .edit({ embeds: [embed], @@ -343,7 +340,7 @@ async function updateSetup(client: Lavamusic, guild: any): Promise { return b; }), }) - .catch(() => {}); + .catch(() => { }); } else { const embed = client .embed() @@ -364,7 +361,7 @@ async function updateSetup(client: Lavamusic, guild: any): Promise { return b; }), }) - .catch(() => {}); + .catch(() => { }); } } } @@ -375,15 +372,15 @@ async function buttonReply(int: any, args: string, color: ColorResolvable): Prom if (int.replied) { m = await int .editReply({ embeds: [embed.setColor(color).setDescription(args)] }) - .catch(() => {}); + .catch(() => { }); } else { m = await int .followUp({ embeds: [embed.setColor(color).setDescription(args)] }) - .catch(() => {}); + .catch(() => { }); } setTimeout(async () => { if (int && !int.ephemeral) { - await m.delete().catch(() => {}); + await m.delete().catch(() => { }); } }, 2000); } @@ -396,7 +393,7 @@ async function oops(channel: TextChannel, args: string): Promise { embeds: [embed1], }); - setTimeout(async () => await m.delete().catch(() => {}), 12000); + setTimeout(async () => await m.delete().catch(() => { }), 12000); } catch (e) { return console.error(e); }