From 434200ec8b1df3a576aab1c492dd4b5111f30400 Mon Sep 17 00:00:00 2001 From: appujet Date: Sun, 28 Jul 2024 13:29:44 +0530 Subject: [PATCH] refactor: Update SetupSystem to handle voice channel queueing and permission checks --- src/events/client/InteractionCreate.ts | 78 ++++++++++---------- src/events/client/MessageCreate.ts | 99 +++++++++++++------------- src/events/client/SetupButtons.ts | 88 ++++++++++++----------- src/events/client/SetupSystem.ts | 8 ++- src/events/player/TrackStart.ts | 64 ++++++++++------- 5 files changed, 179 insertions(+), 158 deletions(-) diff --git a/src/events/client/InteractionCreate.ts b/src/events/client/InteractionCreate.ts index 90b72f2ec..8528b07fe 100644 --- a/src/events/client/InteractionCreate.ts +++ b/src/events/client/InteractionCreate.ts @@ -14,6 +14,7 @@ import { } from "discord.js"; import { LoadType } from "shoukaku"; import { Context, Event, type Lavamusic } from "../../structures/index.js"; +import { T } from "../../structures/I18n.js"; export default class InteractionCreate extends Event { constructor(client: Lavamusic, file: string) { @@ -28,6 +29,7 @@ export default class InteractionCreate extends Event { const setup = await this.client.db.getSetup(interaction.guildId); const allowedCategories = ["filters", "music", "playlist"]; const commandInSetup = this.client.commands.get(interaction.commandName); + const locale = await this.client.db.getLanguage(interaction.guildId); if ( setup && @@ -35,14 +37,14 @@ export default class InteractionCreate extends Event { !(commandInSetup && allowedCategories.includes(commandInSetup.category)) ) { return await interaction.reply({ - content: `You can't use this command in setup channel.`, + content: T(locale, "event.interaction.setup_channel"), ephemeral: true, }); } const { commandName } = interaction; await this.client.db.get(interaction.guildId); - const locale = await this.client.db.getLanguage(interaction.guildId); + const command = this.client.commands.get(commandName); if (!command) return; @@ -52,47 +54,34 @@ export default class InteractionCreate extends Event { const clientMember = interaction.guild.members.resolve(this.client.user); if (!(interaction.inGuild() && interaction.channel.permissionsFor(clientMember)?.has(PermissionFlagsBits.ViewChannel))) return; - const logs = this.client.channels.cache.get(this.client.config.commandLogs); - - if (logs) { - const embed = new EmbedBuilder() - .setAuthor({ - name: "Slash-Command Command Logs", - iconURL: this.client.user?.avatarURL({ size: 2048 }), - }) - .setColor(this.client.config.color.blue) - .setDescription( - `**\`${command.name}\`** | Used By **${interaction.user.tag} \`${interaction.user.id}\`** From **${interaction.guild.name} \`${interaction.guild.id}\`**`, - ) - .setTimestamp(); - - await (logs as TextChannel).send({ embeds: [embed] }); - } - if (!clientMember.permissions.has(PermissionFlagsBits.SendMessages)) { return await (interaction.member as GuildMember) .send({ - content: `I don't have **\`SendMessage\`** permission in \`${interaction.guild.name}\`\nchannel: <#${interaction.channelId}>.`, + content: T(locale, "event.interaction.no_send_message", { + guild: interaction.guild.name, + channel: `<#${interaction.channelId}>`, + }), }) .catch(() => {}); } if (!clientMember.permissions.has(PermissionFlagsBits.EmbedLinks)) { return await interaction.reply({ - content: "I don't have **`EmbedLinks`** permission.", + content: T(locale, "event.interaction.no_embed_links"), }); } + const logs = this.client.channels.cache.get(this.client.config.commandLogs); if (command.permissions) { if (command.permissions.client && !clientMember.permissions.has(command.permissions.client)) { return await interaction.reply({ - content: "I don't have enough permissions to execute this command.", + content: T(locale, "event.interaction.no_permission"), }); } if (command.permissions.user && !(interaction.member as GuildMember).permissions.has(command.permissions.user)) { await interaction.reply({ - content: "You don't have enough permissions to use this command.", + content: T(locale, "event.interaction.no_user_permission"), ephemeral: true, }); return; @@ -103,7 +92,6 @@ export default class InteractionCreate extends Event { if (!isDev) return; } } - if (command.vote) { const voted = await this.client.topGG.hasVoted(interaction.user.id); if (!voted) { @@ -121,24 +109,23 @@ export default class InteractionCreate extends Event { }); } } - if (command.player) { if (command.player.voice) { if (!(interaction.member as GuildMember).voice.channel) { return await interaction.reply({ - content: `You must be connected to a voice channel to use this \`${command.name}\` command.`, + content: T(locale, "event.interaction.no_voice_channel", { command: command.name }), }); } if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { return await interaction.reply({ - content: `I don't have \`CONNECT\` permissions to execute this \`${command.name}\` command.`, + content: T(locale, "event.interaction.no_connect_permission", { command: command.name }), }); } if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { return await interaction.reply({ - content: `I don't have \`SPEAK\` permissions to execute this \`${command.name}\` command.`, + content: T(locale, "event.interaction.no_speak_permission", { command: command.name }), }); } @@ -147,7 +134,7 @@ export default class InteractionCreate extends Event { !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) ) { return await interaction.reply({ - content: `I don't have \`REQUEST TO SPEAK\` permission to execute this \`${command.name}\` command.`, + content: T(locale, "event.interaction.no_request_to_speak", { command: command.name }), }); } @@ -156,7 +143,10 @@ export default class InteractionCreate extends Event { clientMember.voice.channelId !== (interaction.member as GuildMember).voice.channelId ) { return await interaction.reply({ - content: `You are not connected to <#${clientMember.voice.channelId}> to use this \`${command.name}\` command.`, + content: T(locale, "event.interaction.different_voice_channel", { + channel: `<#${clientMember.voice.channelId}>`, + command: command.name, + }), }); } } @@ -165,7 +155,7 @@ export default class InteractionCreate extends Event { const queue = this.client.queue.get(interaction.guildId); if (!(queue?.queue && queue.current)) { return await interaction.reply({ - content: "Nothing is playing right now.", + content: T(locale, "event.interaction.no_music_playing"), }); } } @@ -176,7 +166,7 @@ export default class InteractionCreate extends Event { const djRole = await this.client.db.getRoles(interaction.guildId); if (!djRole) { return await interaction.reply({ - content: "DJ role is not set.", + content: T(locale, "event.interaction.no_dj_role"), }); } @@ -185,7 +175,7 @@ export default class InteractionCreate extends Event { ); if (!(hasDJRole && !(interaction.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild))) { return await interaction.reply({ - content: "You need to have the DJ role to use this command.", + content: T(locale, "event.interaction.no_dj_permission"), ephemeral: true, }); } @@ -206,7 +196,7 @@ export default class InteractionCreate extends Event { const timeLeft = (expirationTime - now) / 1000; if (now < expirationTime && timeLeft > 0.9) { return await interaction.reply({ - content: `Please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${commandName}\` command.`, + content: T(locale, "event.interaction.cooldown", { time: timeLeft.toFixed(1), command: commandName }), }); } timestamps.set(interaction.user.id, now); @@ -217,16 +207,30 @@ export default class InteractionCreate extends Event { } try { - const _reply = await command.run(this.client, ctx, ctx.args); + await command.run(this.client, ctx, ctx.args); if (setup && interaction.channelId === setup.textId && allowedCategories.includes(command.category)) { setTimeout(() => { - (interaction as CommandInteraction).deleteReply().catch(() => {}); + interaction.deleteReply().catch(() => {}); }, 5000); } + if (logs) { + const embed = new EmbedBuilder() + .setAuthor({ + name: "Slash-Command Command Logs", + iconURL: this.client.user?.avatarURL({ size: 2048 }), + }) + .setColor(this.client.config.color.blue) + .setDescription( + `**\`${command.name}\`** | Used By **${interaction.user.tag} \`${interaction.user.id}\`** From **${interaction.guild.name} \`${interaction.guild.id}\`**`, + ) + .setTimestamp(); + + await (logs as TextChannel).send({ embeds: [embed] }); + } } catch (error) { this.client.logger.error(error); await interaction.reply({ - content: `An error occurred: \`${error}\``, + content: T(locale, "event.interaction.error", { error }), }); } } else if (interaction.type === InteractionType.ApplicationCommandAutocomplete) { diff --git a/src/events/client/MessageCreate.ts b/src/events/client/MessageCreate.ts index 49e6f6d2d..bd1d30328 100644 --- a/src/events/client/MessageCreate.ts +++ b/src/events/client/MessageCreate.ts @@ -1,15 +1,6 @@ -import { - ActionRowBuilder, - ButtonBuilder, - ButtonStyle, - ChannelType, - Collection, - EmbedBuilder, - type Message, - PermissionFlagsBits, - type TextChannel, -} from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Collection, EmbedBuilder, type Message, PermissionFlagsBits, type TextChannel } from "discord.js"; import { Context, Event, type Lavamusic } from "../../structures/index.js"; +import { T } from "../../structures/I18n.js"; export default class MessageCreate extends Event { constructor(client: Lavamusic, file: string) { @@ -32,7 +23,7 @@ export default class MessageCreate extends Event { const mention = new RegExp(`^<@!?${this.client.user.id}>( |)$`); if (mention.test(message.content)) { await message.reply({ - content: `Hey, my prefix for this server is \`${guild.prefix}\` Want more info? then do \`${guild.prefix}help\`\nStay Safe, Stay Awesome!`, + content: T(locale, "event.message.prefix_mention", { prefix: guild.prefix }), }); return; } @@ -52,28 +43,14 @@ export default class MessageCreate extends Event { ctx.guildLocale = locale; if (!message.guild.members.resolve(this.client.user)?.permissions.has(PermissionFlagsBits.ViewChannel)) return; - const logs = this.client.channels.cache.get(this.client.config.commandLogs); - - if (logs) { - const embed = new EmbedBuilder() - .setAuthor({ - name: "Prefix Command Logs", - iconURL: this.client.user?.avatarURL({ size: 2048 }), - }) - .setColor(this.client.config.color.green) - .setDescription( - `**\`${command.name}\`** | Used By **${message.author.tag} \`${message.author.id}\`** From **${message.guild.name} \`${message.guild.id}\`**`, - ) - .setTimestamp(); - - await (logs as TextChannel).send({ embeds: [embed] }); - } - const clientMember = message.guild.members.resolve(this.client.user); if (!clientMember.permissions.has(PermissionFlagsBits.SendMessages)) { await message.author .send({ - content: `I don't have **\`SendMessage\`** permission in \`${message.guild.name}\`\nchannel: <#${message.channelId}>`, + content: T(locale, "event.message.no_send_message", { + guild: message.guild.name, + channel: `<#${message.channelId}>`, + }), }) .catch(() => {}); return; @@ -81,22 +58,25 @@ export default class MessageCreate extends Event { if (!clientMember.permissions.has(PermissionFlagsBits.EmbedLinks)) { await message.reply({ - content: "I don't have **`EmbedLinks`** permission.", + content: T(locale, "event.message.no_embed_links"), }); return; } + const logs = this.client.channels.cache.get(this.client.config.commandLogs); + + if (command.permissions) { if (command.permissions.client && !clientMember.permissions.has(command.permissions.client)) { await message.reply({ - content: "I don't have enough permissions to execute this command.", + content: T(locale, "event.message.no_permission"), }); return; } if (command.permissions.user && !message.member.permissions.has(command.permissions.user)) { await message.reply({ - content: "You don't have enough permissions to use this command.", + content: T(locale, "event.message.no_user_permission"), }); return; } @@ -106,7 +86,6 @@ export default class MessageCreate extends Event { if (!isDev) return; } } - if (command.vote) { const voted = await this.client.topGG.hasVoted(message.author.id); if (!voted) { @@ -123,26 +102,25 @@ export default class MessageCreate extends Event { }); } } - if (command.player) { if (command.player.voice) { if (!message.member.voice.channel) { await message.reply({ - content: `You must be connected to a voice channel to use this \`${command.name}\` command.`, + content: T(locale, "event.message.no_voice_channel", { command: command.name }), }); return; } if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { await message.reply({ - content: `I don't have \`CONNECT\` permissions to execute this \`${command.name}\` command.`, + content: T(locale, "event.message.no_connect_permission", { command: command.name }), }); return; } if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { await message.reply({ - content: `I don't have \`SPEAK\` permissions to execute this \`${command.name}\` command.`, + content: T(locale, "event.message.no_speak_permission", { command: command.name }), }); return; } @@ -152,14 +130,17 @@ export default class MessageCreate extends Event { !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) ) { await message.reply({ - content: `I don't have \`REQUEST TO SPEAK\` permission to execute this \`${command.name}\` command.`, + content: T(locale, "event.message.no_request_to_speak", { command: command.name }), }); return; } if (clientMember.voice.channel && clientMember.voice.channelId !== message.member.voice.channelId) { await message.reply({ - content: `You are not connected to <#${clientMember.voice.channelId}> to use this \`${command.name}\` command.`, + content: T(locale, "event.message.different_voice_channel", { + channel: `<#${clientMember.voice.channelId}>`, + command: command.name, + }), }); return; } @@ -169,7 +150,7 @@ export default class MessageCreate extends Event { const queue = this.client.queue.get(message.guildId); if (!queue?.queue && queue.current) { await message.reply({ - content: "Nothing is playing right now.", + content: T(locale, "event.message.no_music_playing"), }); return; } @@ -181,7 +162,7 @@ export default class MessageCreate extends Event { const djRole = await this.client.db.getRoles(message.guildId); if (!djRole) { await message.reply({ - content: "DJ role is not set.", + content: T(locale, "event.message.no_dj_role"), }); return; } @@ -190,7 +171,7 @@ export default class MessageCreate extends Event { if (!message.member.permissions.has(PermissionFlagsBits.ManageGuild)) { await message .reply({ - content: "You need to have the DJ role to use this command.", + content: T(locale, "event.message.no_dj_permission"), }) .then((msg) => setTimeout(() => msg.delete(), 5000)); return; @@ -204,13 +185,14 @@ export default class MessageCreate extends Event { const embed = this.client .embed() .setColor(this.client.color.red) - .setTitle("Missing Arguments") + .setTitle(T(locale, "event.message.missing_arguments")) .setDescription( - `Please provide the required arguments for the \`${command.name}\` command.\n\nExamples:\n${ - command.description.examples ? command.description.examples.join("\n") : "None" - }`, + T(locale, "event.message.missing_arguments_description", { + command: command.name, + examples: command.description.examples ? command.description.examples.join("\n") : "None", + }), ) - .setFooter({ text: "Syntax: [] = optional, <> = required" }); + .setFooter({ text: T(locale, "event.message.syntax_footer") }); await message.reply({ embeds: [embed] }); return; } @@ -228,7 +210,7 @@ export default class MessageCreate extends Event { const timeLeft = (expirationTime - now) / 1000; if (now < expirationTime && timeLeft > 0.9) { await message.reply({ - content: `Please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${cmd}\` command.`, + content: T(locale, "event.message.cooldown", { time: timeLeft.toFixed(1), command: cmd }), }); return; } @@ -241,7 +223,7 @@ export default class MessageCreate extends Event { if (args.includes("@everyone") || args.includes("@here")) { await message.reply({ - content: "You can't use this command with everyone or here.", + content: T(locale, "event.message.no_mention_everyone"), }); return; } @@ -250,7 +232,22 @@ export default class MessageCreate extends Event { return command.run(this.client, ctx, ctx.args); } catch (error) { this.client.logger.error(error); - await message.reply({ content: `An error occurred: \`${error}\`` }); + await message.reply({ content: T(locale, "event.message.error", { error }) }); + } finally { + if (logs) { + const embed = new EmbedBuilder() + .setAuthor({ + name: "Prefix Command Logs", + iconURL: this.client.user?.avatarURL({ size: 2048 }), + }) + .setColor(this.client.config.color.green) + .setDescription( + `**\`${command.name}\`** | Used By **${message.author.tag} \`${message.author.id}\`** From **${message.guild.name} \`${message.guild.id}\`**`, + ) + .setTimestamp(); + + await (logs as TextChannel).send({ embeds: [embed] }); + } } } } diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts index b37798363..b637232b5 100644 --- a/src/events/client/SetupButtons.ts +++ b/src/events/client/SetupButtons.ts @@ -3,6 +3,7 @@ import { Event, type Lavamusic } from "../../structures/index.js"; import { getButtons } from "../../utils/Buttons.js"; import { buttonReply } from "../../utils/SetupSystem.js"; import { checkDj } from "../player/TrackStart.js"; +import { T } from "../../structures/I18n.js"; export default class SetupButtons extends Event { constructor(client: Lavamusic, file: string) { @@ -13,22 +14,24 @@ export default class SetupButtons extends Event { // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: public async run(interaction: any): Promise { + const locale = await this.client.db.getLanguage(interaction.guildId); + if (!interaction.replied) await interaction.deferReply().catch(() => {}); if (!interaction.member.voice.channel) { - return await buttonReply(interaction, "You are not connected to a voice channel to use this button.", this.client.color.red); + return await buttonReply(interaction, T(locale, "event.setupButton.no_voice_channel_button"), this.client.color.red); } const clientMember = interaction.guild.members.cache.get(this.client.user.id); if (clientMember.voice.channel && clientMember.voice.channelId !== interaction.member.voice.channelId) { return await buttonReply( interaction, - `You are not connected to ${clientMember.voice.channel} to use these buttons.`, + T(locale, "event.setupButton.different_voice_channel_button", { channel: clientMember.voice.channel }), this.client.color.red, ); } const player = this.client.queue.get(interaction.guildId); - if (!player) return await buttonReply(interaction, "There is no music playing in this server.", this.client.color.red); - if (!player.queue) return await buttonReply(interaction, "There is no music playing in this server.", this.client.color.red); - if (!player.current) return await buttonReply(interaction, "There is no music playing in this server.", this.client.color.red); + if (!player) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); + if (!player.queue) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); + if (!player.current) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); const data = await this.client.db.getSetup(interaction.guildId); const { title, uri, length, artworkUrl, sourceName, isStream, requester } = player.current.info; let message: Message; @@ -41,22 +44,29 @@ export default class SetupButtons extends Event { const iconUrl = this.client.config.icons[sourceName] || this.client.user.displayAvatarURL({ extension: "png" }); const embed = this.client .embed() - .setAuthor({ name: "Now Playing", iconURL: iconUrl }) + .setAuthor({ name: T(locale, "event.setupButton.now_playing"), iconURL: iconUrl }) .setColor(this.client.color.main) - .setDescription(`[${title}](${uri}) - ${isStream ? "LIVE" : this.client.utils.formatTime(length)} - Requested by ${requester}`) + .setDescription( + `[${title}](${uri}) - ${isStream ? T(locale, "event.setupButton.live") : this.client.utils.formatTime(length)} - ${T(locale, "event.setupButton.requested_by", { requester })}`, + ) .setImage(artworkUrl || this.client.user.displayAvatarURL({ extension: "png" })); if (!interaction.isButton()) return; if (!(await checkDj(this.client, interaction))) { - return await buttonReply(interaction, "You need to have the DJ role to use this command.", this.client.color.red); + return await buttonReply(interaction, T(locale, "event.setupButton.no_dj_permission"), this.client.color.red); } if (message) { const handleVolumeChange = async (change: number) => { const vol = player.player.volume + change; player.player.setGlobalVolume(vol); - await buttonReply(interaction, `Volume set to ${vol}%`, this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.volume_set", { vol }), this.client.color.main); await message.edit({ - embeds: [embed.setFooter({ text: `Volume: ${vol}%`, iconURL: interaction.member.displayAvatarURL({}) })], + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.volume_footer", { vol, displayName: interaction.member.displayName }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], }); }; switch (interaction.customId) { @@ -67,13 +77,16 @@ export default class SetupButtons extends Event { await handleVolumeChange(10); break; case "PAUSE_BUT": { - const name = player.player.paused ? "Resumed" : "Paused"; + const name = player.player.paused ? T(locale, "event.setupButton.resumed") : T(locale, "event.setupButton.paused"); player.pause(); - await buttonReply(interaction, `${name} the music.`, this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.pause_resume", { name }), this.client.color.main); await message.edit({ embeds: [ embed.setFooter({ - text: `${name} by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.pause_resume_footer", { + name, + displayName: interaction.member.displayName, + }), iconURL: interaction.member.displayAvatarURL({}), }), ], @@ -83,14 +96,14 @@ export default class SetupButtons extends Event { } case "SKIP_BUT": if (!player.queue.length) { - return await buttonReply(interaction, "There is no music to skip.", this.client.color.main); + return await buttonReply(interaction, T(locale, "event.setupButton.no_music_to_skip"), this.client.color.main); } player.skip(); - await buttonReply(interaction, "Skipped the music.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.skipped"), this.client.color.main); await message.edit({ embeds: [ embed.setFooter({ - text: `Skipped by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.skipped_footer", { displayName: interaction.member.displayName }), iconURL: interaction.member.displayAvatarURL({}), }), ], @@ -98,15 +111,15 @@ export default class SetupButtons extends Event { break; case "STOP_BUT": player.stop(); - await buttonReply(interaction, "Stopped the music.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.stopped"), this.client.color.main); await message.edit({ embeds: [ embed .setFooter({ - text: `Stopped by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.stopped_footer", { displayName: interaction.member.displayName }), iconURL: interaction.member.displayAvatarURL({}), }) - .setDescription("Nothing playing right now") + .setDescription(T(locale, "event.setupButton.nothing_playing")) .setImage(this.client.config.links.img) .setAuthor({ name: this.client.user.username, @@ -119,11 +132,14 @@ export default class SetupButtons extends Event { const loopOptions: Array<"off" | "queue" | "repeat"> = ["off", "queue", "repeat"]; const newLoop = loopOptions[(loopOptions.indexOf(player.loop) + 1) % loopOptions.length]; player.setLoop(newLoop); - await buttonReply(interaction, `Loop set to ${player.loop}.`, this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.loop_set", { loop: newLoop }), this.client.color.main); await message.edit({ embeds: [ embed.setFooter({ - text: `Loop set to ${player.loop} by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.loop_footer", { + loop: newLoop, + displayName: interaction.member.displayName, + }), iconURL: interaction.member.displayAvatarURL({}), }), ], @@ -132,18 +148,18 @@ export default class SetupButtons extends Event { } case "SHUFFLE_BUT": player.setShuffle(); - await buttonReply(interaction, "Shuffled the queue.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.shuffled"), this.client.color.main); break; case "PREV_BUT": if (!player.previous) { - return await buttonReply(interaction, "There is no previous track.", this.client.color.main); + return await buttonReply(interaction, T(locale, "event.setupButton.no_previous_track"), this.client.color.main); } player.previousTrack(); - await buttonReply(interaction, "Playing the previous track.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.playing_previous"), this.client.color.main); await message.edit({ embeds: [ embed.setFooter({ - text: `Playing the previous track by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.previous_footer", { displayName: interaction.member.displayName }), iconURL: interaction.member.displayAvatarURL({}), }), ], @@ -152,18 +168,14 @@ export default class SetupButtons extends Event { case "REWIND_BUT": { const time = player.player.position - 10000; if (time < 0) { - return await buttonReply( - interaction, - "You cannot rewind the music more than the length of the song.", - this.client.color.main, - ); + return await buttonReply(interaction, T(locale, "event.setupButton.rewind_limit"), this.client.color.main); } player.seek(time); - await buttonReply(interaction, "Rewinded the music.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.rewinded"), this.client.color.main); await message.edit({ embeds: [ embed.setFooter({ - text: `Rewinded by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.rewind_footer", { displayName: interaction.member.displayName }), iconURL: interaction.member.displayAvatarURL({}), }), ], @@ -173,18 +185,14 @@ export default class SetupButtons extends Event { case "FORWARD_BUT": { const time = player.player.position + 10000; if (time > player.current.info.length) { - return await buttonReply( - interaction, - "You cannot forward the music more than the length of the song.", - this.client.color.main, - ); + return await buttonReply(interaction, T(locale, "event.setupButton.forward_limit"), this.client.color.main); } player.seek(time); - await buttonReply(interaction, "Forwarded the music.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.forwarded"), this.client.color.main); await message.edit({ embeds: [ embed.setFooter({ - text: `Forwarded by ${interaction.member.displayName}`, + text: T(locale, "event.setupButton.forward_footer", { displayName: interaction.member.displayName }), iconURL: interaction.member.displayAvatarURL({}), }), ], @@ -192,7 +200,7 @@ export default class SetupButtons extends Event { break; } default: - await buttonReply(interaction, "This button is not available.", this.client.color.main); + await buttonReply(interaction, T(locale, "event.setupButton.button_not_available"), this.client.color.main); break; } } diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index e4e7c0274..3ef96a8e7 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -1,6 +1,7 @@ import { type Message, PermissionsBitField, TextChannel } from "discord.js"; import { Event, type Lavamusic } from "../../structures/index.js"; import { oops, setupStart } from "../../utils/SetupSystem.js"; +import { T } from "../../structures/I18n.js"; export default class SetupSystem extends Event { constructor(client: Lavamusic, file: string) { @@ -10,10 +11,11 @@ export default class SetupSystem extends Event { } public async run(message: Message): Promise { + const locale = await this.client.db.getLanguage(message.guildId); const channel = message.channel as TextChannel; if (!(channel instanceof TextChannel)) return; if (!message.member?.voice.channel) { - await oops(channel, "You are not connected to a voice channel to queue songs."); + await oops(channel, T(locale, "event.message.no_voice_channel_queue")); await message.delete().catch(() => {}); return; } @@ -23,13 +25,13 @@ export default class SetupSystem extends Event { const clientMember = message.guild.members.cache.get(clientUser.id); if (!voiceChannel.permissionsFor(clientUser).has(PermissionsBitField.Flags.Connect | PermissionsBitField.Flags.Speak)) { - await oops(channel, `I don't have enough permission to connect/speak in <#${voiceChannel.id}>`); + await oops(channel, T(locale, "event.message.no_permission_connect_speak", { channel: voiceChannel.id })); await message.delete().catch(() => {}); return; } if (clientMember?.voice.channel && clientMember.voice.channelId !== voiceChannel.id) { - await oops(channel, `You are not connected to <#${clientMember.voice.channelId}> to queue songs`); + await oops(channel, T(locale, "event.message.different_voice_channel_queue", { channel: clientMember.voice.channelId })); await message.delete().catch(() => {}); return; } diff --git a/src/events/player/TrackStart.ts b/src/events/player/TrackStart.ts index 6e8f2301c..2f86d72c6 100644 --- a/src/events/player/TrackStart.ts +++ b/src/events/player/TrackStart.ts @@ -16,6 +16,7 @@ import type { Player } from "shoukaku"; import type { Song } from "../../structures/Dispatcher.js"; import { type Dispatcher, Event, type Lavamusic } from "../../structures/index.js"; import { trackStart } from "../../utils/SetupSystem.js"; +import { T } from "../../structures/I18n.js"; export default class TrackStart extends Event { constructor(client: Lavamusic, file: string) { @@ -35,26 +36,28 @@ export default class TrackStart extends Event { this.client.utils.updateStatus(this.client, guild.id); + const locale = await this.client.db.getLanguage(guild.id); + const embed = this.client .embed() .setAuthor({ - name: "Now Playing", + name: T(locale, "player.trackStart.now_playing"), iconURL: this.client.config.icons[track.info.sourceName] ?? this.client.user.displayAvatarURL({ extension: "png" }), }) .setColor(this.client.color.main) .setDescription(`**[${track.info.title}](${track.info.uri})**`) .setFooter({ - text: `Requested by ${track.info.requester.tag}`, + text: T(locale, "player.trackStart.requested_by", { user: track.info.requester.tag }), iconURL: track.info.requester.avatarURL({}), }) .setThumbnail(track.info.artworkUrl) .addFields( { - name: "Duration", + name: T(locale, "player.trackStart.duration"), value: track.info.isStream ? "LIVE" : this.client.utils.formatTime(track.info.length), inline: true, }, - { name: "Author", value: track.info.author, inline: true }, + { name: T(locale, "player.trackStart.author"), value: track.info.author, inline: true }, ) .setTimestamp(); @@ -70,40 +73,41 @@ export default class TrackStart extends Event { } else { const message = await channel.send({ embeds: [embed], - components: [createButtonRow(dispatcher, this.client)], + components: [createButtonRow(dispatcher, locale)], }); dispatcher.nowPlayingMessage = message; - createCollector(message, dispatcher, track, embed, this.client); + createCollector(message, dispatcher, track, embed, this.client, locale); } } } -function createButtonRow(dispatcher: Dispatcher, client: Lavamusic): ActionRowBuilder { +function createButtonRow(dispatcher: Dispatcher, locale: string): ActionRowBuilder { const previousButton = new ButtonBuilder() - .setCustomId("previous") - .setEmoji(client.emoji.previous) + + .setCustomId(T(locale, "buttons.previous")) + .setEmoji("⏪") .setStyle(ButtonStyle.Secondary) .setDisabled(!dispatcher.previous); const resumeButton = new ButtonBuilder() - .setCustomId("resume") - .setEmoji(dispatcher.paused ? client.emoji.resume : client.emoji.pause) + .setCustomId(T(locale, "buttons.resume")) + .setEmoji(dispatcher.paused ? "▶️" : "⏸️") .setStyle(dispatcher.paused ? ButtonStyle.Success : ButtonStyle.Secondary); - const stopButton = new ButtonBuilder().setCustomId("stop").setEmoji(client.emoji.stop).setStyle(ButtonStyle.Danger); + const stopButton = new ButtonBuilder().setCustomId(T(locale, "buttons.stop")).setEmoji("⏹️").setStyle(ButtonStyle.Danger); - const skipButton = new ButtonBuilder().setCustomId("skip").setEmoji(client.emoji.skip).setStyle(ButtonStyle.Secondary); + const skipButton = new ButtonBuilder().setCustomId(T(locale, "buttons.skip")).setEmoji("⏩").setStyle(ButtonStyle.Secondary); const loopButton = new ButtonBuilder() - .setCustomId("loop") - .setEmoji(dispatcher.loop === "repeat" ? client.emoji.loop.track : client.emoji.loop.none) + .setCustomId(T(locale, "buttons.loop")) + .setEmoji(dispatcher.loop === "repeat" ? "🔂" : "🔁") .setStyle(dispatcher.loop !== "off" ? ButtonStyle.Success : ButtonStyle.Secondary); return new ActionRowBuilder().addComponents(resumeButton, previousButton, stopButton, skipButton, loopButton); } -function createCollector(message: any, dispatcher: Dispatcher, _track: Song, embed: any, client: Lavamusic): void { +function createCollector(message: any, dispatcher: Dispatcher, _track: Song, embed: any, client: Lavamusic, locale: string): void { const collector = message.createMessageComponentCollector({ filter: async (b: ButtonInteraction) => { if (b.member instanceof GuildMember) { @@ -111,7 +115,9 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb if (isSameVoiceChannel) return true; } await b.reply({ - content: `You are not connected to <#${b.guild.members.me?.voice.channelId ?? "None"}> to use these buttons.`, + content: T(locale, "player.trackStart.not_connected_to_voice_channel", { + channel: b.guild.members.me?.voice.channelId ?? "None", + }), ephemeral: true, }); return false; @@ -121,7 +127,7 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb collector.on("collect", async (interaction) => { if (!(await checkDj(client, interaction))) { await interaction.reply({ - content: "You need to have the DJ role to use this command.", + content: T(locale, "player.trackStart.need_dj_role"), ephemeral: true, }); return; @@ -131,7 +137,7 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb if (message) { await message.edit({ embeds: [embed.setFooter({ text, iconURL: interaction.user.avatarURL({}) })], - components: [createButtonRow(dispatcher, client)], + components: [createButtonRow(dispatcher, locale)], }); } }; @@ -141,10 +147,10 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb if (dispatcher.previous) { await interaction.deferUpdate(); dispatcher.previousTrack(); - await editMessage(`Previous by ${interaction.user.tag}`); + await editMessage(T(locale, "player.trackStart.previous_by", { user: interaction.user.tag })); } else { await interaction.reply({ - content: "There is no previous song.", + content: T(locale, "player.trackStart.no_previous_song"), ephemeral: true, }); } @@ -152,7 +158,11 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb case "resume": dispatcher.pause(); await interaction.deferUpdate(); - await editMessage(dispatcher.paused ? `Paused by ${interaction.user.tag}` : `Resumed by ${interaction.user.tag}`); + await editMessage( + dispatcher.paused + ? T(locale, "player.trackStart.paused_by", { user: interaction.user.tag }) + : T(locale, "player.trackStart.resumed_by", { user: interaction.user.tag }), + ); break; case "stop": dispatcher.stop(); @@ -161,10 +171,10 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb if (dispatcher.queue.length) { await interaction.deferUpdate(); dispatcher.skip(); - await editMessage(`Skipped by ${interaction.user.tag}`); + await editMessage(T(locale, "player.trackStart.skipped_by", { user: interaction.user.tag })); } else { await interaction.reply({ - content: "There is no more song in the queue.", + content: T(locale, "player.trackStart.no_more_songs_in_queue"), ephemeral: true, }); } @@ -174,15 +184,15 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb switch (dispatcher.loop) { case "off": dispatcher.loop = "repeat"; - await editMessage(`Looping by ${interaction.user.tag}`); + await editMessage(T(locale, "player.trackStart.looping_by", { user: interaction.user.tag })); break; case "repeat": dispatcher.loop = "queue"; - await editMessage(`Looping Queue by ${interaction.user.tag}`); + await editMessage(T(locale, "player.trackStart.looping_queue_by", { user: interaction.user.tag })); break; case "queue": dispatcher.loop = "off"; - await editMessage(`Looping Off by ${interaction.user.tag}`); + await editMessage(T(locale, "player.trackStart.looping_off_by", { user: interaction.user.tag })); break; } break;