diff --git a/base/JaBa.js b/base/JaBa.js index fbd9ea97..e84202fb 100644 --- a/base/JaBa.js +++ b/base/JaBa.js @@ -38,14 +38,21 @@ class JaBa extends Client { this.player.extractors.loadDefault(); this.player.events.on("playerStart", async (queue, track) => { - const m = await queue.metadata.channel.send({ content: this.translate("music/play:NOW_PLAYING", { songName: track.title }, queue.metadata.channel.guild.data.language) }); + const m = (await queue.metadata.channel.send({ + content: this.translate("music/play:NOW_PLAYING", { songName: track.title }, queue.metadata.channel.guild.data.language), + })).id; + if (track.durationMS > 1) setTimeout(() => { - if (m.deletable) m.delete(); + const message = queue.metadata.channel.messages.cache.get(m); + + if (message.deletable) message.delete(); }, track.durationMS); else setTimeout(() => { - if (m.deletable) m.delete(); + const message = queue.metadata.channel.messages.cache.get(m); + + if (message.deletable) message.delete(); }, 5 * 60 * 1000); // m * s * ms }); this.player.events.on("emptyQueue", queue => queue.metadata.channel.send(this.translate("music/play:QUEUE_ENDED", null, queue.metadata.channel.guild.data.language))); @@ -227,10 +234,9 @@ class JaBa extends Client { * @param {String} locale Language */ translate(key, args, locale = this.defaultLanguage) { - const language = this.translations.get(locale); - if (!language) throw "Invalid language set in data."; + const lang = this.translations.get(locale); - return language(key, args); + return lang(key, args); } /** diff --git a/commands/!DISABLED/importmee6.js b/commands/!DISABLED/importmee6.js index 4c19b79b..9003580a 100644 --- a/commands/!DISABLED/importmee6.js +++ b/commands/!DISABLED/importmee6.js @@ -46,7 +46,7 @@ class ImportMee6 extends BaseCommand { content: interaction.translate("owner/debug:SUCCESS_LEVEL", { user: interaction.member.toString(), amount: level, - }), + }, "success"), }); } } diff --git a/commands/!DISABLED/memes.js b/commands/!DISABLED/memes.js index 25d3d536..850bb862 100644 --- a/commands/!DISABLED/memes.js +++ b/commands/!DISABLED/memes.js @@ -68,10 +68,9 @@ class Memes extends BaseCommand { ), ); - const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("memes_select").setPlaceholder(client.translate("common:NOTHING_SELECTED")).addOptions(tags)); + const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("memes_select").setPlaceholder(interaction.translate("common:NOTHING_SELECTED")).addOptions(tags)); await interaction.editReply({ - content: interaction.translate("common:AVAILABLE_OPTIONS"), components: [row], }); } diff --git a/commands/Administration/config.js b/commands/Administration/config.js index 28a14bfa..3cf3474f 100644 --- a/commands/Administration/config.js +++ b/commands/Administration/config.js @@ -207,7 +207,7 @@ async function changeSetting(interaction, setting, state, channel, guildData) { await guildData.save(); return interaction.reply({ - content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: **${interaction.translate("common:DISABLED")}**`, + content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`, null, "success")}: **${interaction.translate("common:DISABLED", null, "success")}**`, ephemeral: true, }); } else { @@ -226,7 +226,7 @@ async function changeSetting(interaction, setting, state, channel, guildData) { } else return interaction.reply({ content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: ${ - guildData.plugins[setting] ? `**${interaction.translate("common:ENABLED")}** (<#${guildData.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED")}**` + guildData.plugins[setting] ? `**${interaction.translate("common:ENABLED", null, "success")}** (<#${guildData.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED", null, "success")}**` }`, ephemeral: true, }); diff --git a/commands/Administration/selectroles.js b/commands/Administration/selectroles.js index cbaac01c..c35159f4 100644 --- a/commands/Administration/selectroles.js +++ b/commands/Administration/selectroles.js @@ -102,7 +102,7 @@ class Selectroles extends BaseCommand { } interaction.reply({ - content: interaction.translate("administration/selectroles:ROLES_UPDATED"), + content: interaction.translate("administration/selectroles:ROLES_UPDATED", null, "success"), ephemeral: true, }); } diff --git a/commands/Economy/marry.js b/commands/Economy/marry.js index 83632c5e..5f65d2f0 100644 --- a/commands/Economy/marry.js +++ b/commands/Economy/marry.js @@ -167,18 +167,16 @@ class Marry extends BaseCommand { content: interaction.translate("economy/marry:SUCCESS", { creator: interaction.member.toString(), partner: member.toString(), - }), + }, "success"), components: [], }); - } else { - return interaction.editReply({ - content: interaction.translate("economy/marry:DENIED", { - creator: interaction.member.toString(), - partner: member.toString(), - }), - components: [], - }); - } + } else return interaction.editReply({ + content: interaction.translate("economy/marry:DENIED", { + creator: interaction.member.toString(), + partner: member.toString(), + }, "error"), + components: [], + }); }); } } diff --git a/commands/General/avatar.c.js b/commands/General/avatar.c.js new file mode 100644 index 00000000..55511ff8 --- /dev/null +++ b/commands/General/avatar.c.js @@ -0,0 +1,46 @@ +const { ContextMenuCommandBuilder, ApplicationCommandType } = require("discord.js"); +const BaseCommand = require("../../base/BaseCommand"); + +class AvatarContext extends BaseCommand { + /** + * + * @param {import("../../base/JaBa")} client + */ + constructor() { + super({ + command: new ContextMenuCommandBuilder() + .setName("Avatar") + .setType(ApplicationCommandType.User) + .setDMPermission(false), + aliases: [], + dirname: __dirname, + ownerOnly: false, + }); + } + /** + * + * @param {import("../../base/JaBa")} client + */ + async onLoad() { + //... + } + /** + * + * @param {import("../../base/JaBa")} client + * @param {import("discord.js").UserContextMenuCommandInteraction} interaction + * @param {Object} data + */ + async execute(client, interaction) { + const avatar = interaction.targetUser.avatarURL({ size: 2048 }); + + interaction.reply({ + files: [ + { + attachment: avatar, + }, + ], + }); + } +} + +module.exports = AvatarContext; diff --git a/commands/General/boosters.js b/commands/General/boosters.js index 5bed3931..9630b32c 100644 --- a/commands/General/boosters.js +++ b/commands/General/boosters.js @@ -29,7 +29,7 @@ class Boosters extends BaseCommand { client.on("interactionCreate", async interaction => { if (!interaction.isButton()) return; - if (interaction.customId.includes("boosters_")) { + if (interaction.customId.startsWith("boosters_")) { const guildData = client.findOrCreateGuild(interaction.guildId), boosters = (await interaction.guild.members.fetch()).filter(m => m.premiumSince), embeds = generateBoostersEmbeds(client, interaction, boosters, guildData); @@ -41,7 +41,7 @@ class Boosters extends BaseCommand { new ButtonBuilder().setCustomId("boosters_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"), ); - let currentPage = 0; + let currentPage = Number(interaction.message.content.match(/\d+/g)[0]) - 1 ?? 0; if (interaction.customId === "boosters_prev_page") { await interaction.deferUpdate(); diff --git a/commands/General/help.js b/commands/General/help.js index 8e090287..a6ef54d6 100644 --- a/commands/General/help.js +++ b/commands/General/help.js @@ -108,10 +108,9 @@ class Help extends BaseCommand { }; }); - const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("help_category_select").setPlaceholder(client.translate("common:NOTHING_SELECTED")).addOptions(categoriesRows)); + const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("help_category_select").setPlaceholder(interaction.translate("common:NOTHING_SELECTED")).addOptions(categoriesRows)); await interaction.editReply({ - content: interaction.translate("common:AVAILABLE_OPTIONS"), fetchReply: true, components: [row], }); diff --git a/commands/Moderation/warn.js b/commands/Moderation/warn.c.js similarity index 95% rename from commands/Moderation/warn.js rename to commands/Moderation/warn.c.js index 0136e4e2..73c06e8f 100644 --- a/commands/Moderation/warn.js +++ b/commands/Moderation/warn.c.js @@ -1,14 +1,18 @@ const { ContextMenuCommandBuilder, ModalBuilder, EmbedBuilder, ActionRowBuilder, TextInputBuilder, ApplicationCommandType, PermissionsBitField, TextInputStyle } = require("discord.js"); const BaseCommand = require("../../base/BaseCommand"); -class Warn extends BaseCommand { +class WarnContext extends BaseCommand { /** * * @param {import("../../base/JaBa")} client */ constructor() { super({ - command: new ContextMenuCommandBuilder().setName("warn").setType(ApplicationCommandType.User).setDMPermission(false).setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages), + command: new ContextMenuCommandBuilder() + .setName("Warn") + .setType(ApplicationCommandType.User) + .setDMPermission(false) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages), aliases: [], dirname: __dirname, ownerOnly: false, @@ -200,4 +204,4 @@ class Warn extends BaseCommand { } } -module.exports = Warn; +module.exports = WarnContext; diff --git a/commands/Music/loop.js b/commands/Music/loop.js index 4b625aa9..db98b05e 100644 --- a/commands/Music/loop.js +++ b/commands/Music/loop.js @@ -58,12 +58,20 @@ class Loop extends BaseCommand { const queue = client.player.nodes.get(interaction.guildId); if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { edit: true }); + const translated = { + "AUTOPLAY": interaction.translate("music/loop:AUTOPLAY_ENABLED", null, "success"), + "QUEUE": interaction.translate("music/loop:QUEUE_ENABLED", null, "success"), + "TRACK": interaction.translate("music/loop:TRACK_ENABLED", null, "success"), + "OFF": interaction.translate("music/loop:LOOP_DISABLED", null, "success"), + "0": interaction.translate("music/loop:LOOP_DISABLED", null, "success"), + }; + const type = interaction.options.getString("option"), - mode = type === "3" ? QueueRepeatMode.AUTOPLAY : type === "2" ? QueueRepeatMode.QUEUE : type === "1" ? QueueRepeatMode.TRACK : QueueRepeatMode.OFF; + mode = QueueRepeatMode[type]; queue.setRepeatMode(mode); - interaction.success(`music/loop:${type === "3" ? "AUTOPLAY_ENABLED" : type === "2" ? "QUEUE_ENABLED" : type === "1" ? "TRACK_ENABLED" : "LOOP_DISABLED"}`); + interaction.reply({ content: translated[type] }); } } diff --git a/commands/Music/nowplaying.js b/commands/Music/nowplaying.js index 20ce156e..8d9bd2c6 100644 --- a/commands/Music/nowplaying.js +++ b/commands/Music/nowplaying.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"), +const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } = require("discord.js"), { QueueRepeatMode } = require("discord-player"); const BaseCommand = require("../../base/BaseCommand"); @@ -26,8 +26,156 @@ class Nowplaying extends BaseCommand { * * @param {import("../../base/JaBa")} client */ - async onLoad() { - //... + async onLoad(client) { + client.on("interactionCreate", async interaction => { + if (!interaction.isButton()) return; + + if (interaction.customId.startsWith("nowp_")) { + const voice = interaction.member.voice.channel; + if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL"); + + const queue = client.player.nodes.get(interaction.guildId); + if (!queue) return interaction.error("music/play:NOT_PLAYING"); + + const row1 = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("nowp_prev_track").setStyle(ButtonStyle.Primary).setEmoji("⬅️"), + new ButtonBuilder().setCustomId("nowp_loop").setStyle(ButtonStyle.Secondary).setEmoji("🔁"), + new ButtonBuilder().setCustomId("nowp_add_track").setStyle(ButtonStyle.Success).setEmoji("▶️"), + new ButtonBuilder().setCustomId("nowp_next_track").setStyle(ButtonStyle.Primary).setEmoji("➡️"), + ); + + const row2 = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("nowp_queue").setStyle(ButtonStyle.Secondary).setEmoji("ℹ️"), + new ButtonBuilder().setCustomId("nowp_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"), + ); + + if (interaction.customId === "nowp_prev_track") { + await interaction.deferUpdate(); + + if (queue.history.isEmpty()) return interaction.followUp({ content: interaction.translate("music/back:NO_PREV_SONG", null, "error"), ephemeral: true }); + + queue.history.back(); + + await interaction.followUp({ content: interaction.translate("music/back:SUCCESS", null, "success"), ephemeral: true }); + + const embed = await updateEmbed(interaction, queue); + + interaction.editReply({ + embeds: [embed], + }); + } else if (interaction.customId === "nowp_loop") { + await interaction.deferUpdate(); + + const selectMenu = new StringSelectMenuBuilder() + .setCustomId("nowp_select") + .setPlaceholder(interaction.translate("common:NOTHING_SELECTED")) + .addOptions( + new StringSelectMenuOptionBuilder() + .setLabel(interaction.translate("music/loop:AUTOPLAY")) + .setValue("3"), + new StringSelectMenuOptionBuilder() + .setLabel(interaction.translate("music/loop:QUEUE")) + .setValue("2"), + new StringSelectMenuOptionBuilder() + .setLabel(interaction.translate("music/loop:TRACK")) + .setValue("1"), + new StringSelectMenuOptionBuilder() + .setLabel(interaction.translate("music/loop:DISABLE")) + .setValue("0"), + ); + + const selectRow = new ActionRowBuilder().addComponents(selectMenu), + msg = await interaction.followUp({ + components: [selectRow], + ephemeral: true, + }); + + const filter = i => i.user.id === interaction.user.id, + collected = await msg.awaitMessageComponent({ filter, time: 10 * 1000 }), + mode = QueueRepeatMode[collected.values[0]], + translated = { + "AUTOPLAY": interaction.translate("music/loop:AUTOPLAY_ENABLED", null, "success"), + "QUEUE": interaction.translate("music/loop:QUEUE_ENABLED", null, "success"), + "TRACK": interaction.translate("music/loop:TRACK_ENABLED", null, "success"), + "OFF": interaction.translate("music/loop:LOOP_DISABLED", null, "success"), + "0": interaction.translate("music/loop:LOOP_DISABLED", null, "success"), + }; + + await collected.deferUpdate(); + + queue.setRepeatMode(mode); + + await interaction.followUp({ content: translated[mode] }); + + const embed = await updateEmbed(interaction, queue); + + interaction.editReply({ + embeds: [embed], + }); + } else if (interaction.customId === "nowp_add_track") { + await interaction.deferUpdate(); + + await interaction.followUp({ + content: interaction.translate("music/nowplaying:LINK"), + ephemeral: true, + }); + + const filter = m => m.author.id === interaction.user.id && m.content.startsWith("http"), + collected = (await interaction.channel.awaitMessages({ filter, time: 10 * 1000, max: 1 })).first(), + query = collected.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g)[0], + searchResult = await client.player.search(query, { + requestedBy: interaction.user, + }); + + if (!searchResult.hasTracks()) return interaction.error("music/play:NO_RESULT", { query: query }, { edit: true }); + else queue.addTrack(searchResult); + + if (collected.deletable) collected.delete(); + + await interaction.followUp({ + content: interaction.translate("music/play:ADDED_QUEUE", { + songName: searchResult.hasPlaylist() ? searchResult.playlist.title : searchResult.tracks[0].title, + }, "success"), + }); + + const embed = await updateEmbed(interaction, queue); + + interaction.editReply({ + embeds: [embed], + }); + } else if (interaction.customId === "nowp_next_track") { + await interaction.deferUpdate(); + + queue.node.skip(); + + await interaction.followUp({ content: interaction.translate("music/skip:SUCCESS", null, "success"), ephemeral: true }); + + const embed = await updateEmbed(interaction, queue); + + interaction.editReply({ + embeds: [embed], + }); + } else if (interaction.customId === "nowp_queue") { + await interaction.deferUpdate(); + + client.commands.get("queue").execute(client, interaction); + } else if (interaction.customId === "nowp_stop") { + await interaction.deferUpdate(); + + row1.components.forEach(component => { + component.setDisabled(true); + }); + + row2.components.forEach(component => { + component.setDisabled(true); + }); + + return interaction.editReply({ + components: [row1, row2], + }); + } + } + }); } /** * @@ -35,66 +183,92 @@ class Nowplaying extends BaseCommand { * @param {import("discord.js").ChatInputCommandInteraction} interaction * @param {Object} data */ - async execute(client, interaction, data) { + async execute(client, interaction) { await interaction.deferReply(); const queue = client.player.nodes.get(interaction.guildId); if (!queue) return interaction.error("music/play:NOT_PLAYING", null, { edit: true }); - const progressBar = queue.node.createProgressBar(), - track = queue.currentTrack; - const embed = new EmbedBuilder() - .setAuthor({ - name: interaction.translate("music/nowplaying:CURRENTLY_PLAYING"), - }) - .setThumbnail(track.thumbnail) - .addFields([ - { - name: interaction.translate("music/nowplaying:T_TITLE"), - value: `[${track.title}](${track.url})`, - inline: true, - }, - { - name: interaction.translate("music/nowplaying:T_AUTHOR"), - value: track.author || interaction.translate("common:UNKNOWN"), - inline: true, - }, - { name: "\u200B", value: "\u200B", inline: true }, - { - name: interaction.translate("common:VIEWS"), - value: track.raw.live ? "" : new Intl.NumberFormat(client.languages.find(language => language.name === data.guildData.language).moment, { notation: "standard" }).format(track.raw.views), - inline: true, - }, - { - name: interaction.translate("music/queue:ADDED"), - value: track.requestedBy.toString(), - inline: true, - }, - { - name: interaction.translate("music/nowplaying:T_DURATION"), - value: progressBar, - }, - { - name: "\u200b", - value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${ - queue.repeatMode === QueueRepeatMode.AUTOPLAY - ? interaction.translate("music/nowplaying:AUTOPLAY") - : queue.repeatMode === QueueRepeatMode.QUEUE - ? interaction.translate("music/nowplaying:QUEUE") - : queue.repeatMode === QueueRepeatMode.TRACK - ? interaction.translate("music/nowplaying:TRACK") - : interaction.translate("common:DISABLED") - }\``, - }, - ]) - .setColor(client.config.embed.color) - .setFooter(client.config.embed.footer) - .setTimestamp(); + const row1 = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("nowp_prev_track").setStyle(ButtonStyle.Primary).setEmoji("⬅️"), + new ButtonBuilder().setCustomId("nowp_loop").setStyle(ButtonStyle.Secondary).setEmoji("🔁"), + new ButtonBuilder().setCustomId("nowp_add_track").setStyle(ButtonStyle.Success).setEmoji("▶️"), + new ButtonBuilder().setCustomId("nowp_next_track").setStyle(ButtonStyle.Primary).setEmoji("➡️"), + ); + + const row2 = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("nowp_queue").setStyle(ButtonStyle.Secondary).setEmoji("ℹ️"), + new ButtonBuilder().setCustomId("nowp_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"), + ); + + const embed = await updateEmbed(interaction, queue); interaction.editReply({ embeds: [embed], + components: [row1, row2], }); } } +/** + * + * @param {import("discord.js").ChatInputCommandInteraction} interaction + * @param {import("discord-player").GuildQueue} queue + * @returns + */ +async function updateEmbed(interaction, queue) { + const progressBar = queue.node.createProgressBar(), + track = queue.currentTrack, + data = await interaction.client.guildsData.findOne({ id: interaction.guildId }), + translated = { + "AUTOPLAY": interaction.translate("music/nowplaying:AUTOPLAY"), + "QUEUE": interaction.translate("music/nowplaying:QUEUE"), + "TRACK": interaction.translate("music/nowplaying:TRACK"), + "OFF": interaction.translate("common:DISABLED"), + "0": interaction.translate("common:DISABLED"), + }; + + const embed = new EmbedBuilder() + .setAuthor({ + name: interaction.translate("music/nowplaying:CURRENTLY_PLAYING"), + }) + .setThumbnail(track.thumbnail) + .addFields([ + { + name: interaction.translate("music/nowplaying:T_TITLE"), + value: `[${track.title}](${track.url})`, + inline: true, + }, + { + name: interaction.translate("music/nowplaying:T_AUTHOR"), + value: track.author || interaction.translate("common:UNKNOWN"), + inline: true, + }, + { name: "\u200B", value: "\u200B", inline: true }, + { + name: interaction.translate("common:VIEWS"), + value: track.raw.live ? "Live" : new Intl.NumberFormat(interaction.client.languages.find(language => language.name === data.language).moment, { notation: "standard" }).format(track.raw.views), + inline: true, + }, + { + name: interaction.translate("music/queue:ADDED"), + value: track.requestedBy.toString(), + inline: true, + }, + { + name: interaction.translate("music/nowplaying:T_DURATION"), + value: progressBar, + }, + { + name: "\u200b", + value: `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[queue.repeatMode]}\``, + }, + ]) + .setColor(interaction.client.config.embed.color) + .setFooter(interaction.client.config.embed.footer) + .setTimestamp(); + + return embed; +} + module.exports = Nowplaying; diff --git a/commands/Music/play.c.js b/commands/Music/play.c.js new file mode 100644 index 00000000..0a06e76b --- /dev/null +++ b/commands/Music/play.c.js @@ -0,0 +1,80 @@ +const { ContextMenuCommandBuilder, ApplicationCommandType, PermissionsBitField } = require("discord.js"); +const BaseCommand = require("../../base/BaseCommand"); + +class PlayContext extends BaseCommand { + /** + * + * @param {import("../../base/JaBa")} client + */ + constructor() { + super({ + command: new ContextMenuCommandBuilder() + .setName("Add to Queue") + .setType(ApplicationCommandType.Message) + .setDMPermission(false), + aliases: [], + dirname: __dirname, + ownerOnly: false, + }); + } + /** + * + * @param {import("../../base/JaBa")} client + */ + async onLoad() { + //... + } + /** + * + * @param {import("../../base/JaBa")} client + * @param {import("discord.js").MessageContextMenuCommandInteraction} interaction + * @param {Object} data + */ + async execute(client, interaction) { + await interaction.deferReply(); + + const query = interaction.targetMessage.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g)[0], + voice = interaction.member.voice.channel; + if (!voice) return interaction.error("music/play:NO_VOICE_CHANNEL", null, { edit: true }); + + const perms = voice.permissionsFor(client.user); + if (!perms.has(PermissionsBitField.Flags.Connect) || !perms.has(PermissionsBitField.Flags.Speak)) return interaction.error("music/play:VOICE_CHANNEL_CONNECT", null, { edit: true }); + + const searchResult = await client.player.search(query, { + requestedBy: interaction.user, + }); + + if (!searchResult.hasTracks()) return interaction.error("music/play:NO_RESULT", { query }, { edit: true }); + else { + const { queue } = await client.player.play(interaction.member.voice.channel, searchResult, { + nodeOptions: { + metadata: { + client, + channel: interaction.channel, + requestedBy: interaction.user, + }, + }, + selfDeaf: true, + leaveOnEnd: false, + leaveOnStop: true, + skipOnNoStream: true, + maxSize: 200, + maxHistorySize: 50, + }); + + interaction.editReply({ + content: interaction.translate("music/play:ADDED_QUEUE", { + songName: searchResult.hasPlaylist() ? searchResult.playlist.title : searchResult.tracks[0].title, + }, "success"), + }); + + if (client.player.nodes.get(interaction.guildId).currentTrack.url === query && query.match(/&t=[[0-9]+/g) !== null) { + const time = query.match(/&t=[[0-9]+/g)[0].split("=")[1]; + + queue.node.seek(time * 1000); + } + } + } +} + +module.exports = PlayContext; diff --git a/commands/Music/play.js b/commands/Music/play.js index 66e1b899..aa6549d8 100644 --- a/commands/Music/play.js +++ b/commands/Music/play.js @@ -80,16 +80,16 @@ class Play extends BaseCommand { interaction.editReply({ content: interaction.translate("music/play:ADDED_QUEUE", { songName: searchResult.hasPlaylist() ? searchResult.playlist.title : searchResult.tracks[0].title, - }), + }, "success"), }); - if (query.match(/&t=[[0-9]+/g) != null) { + // TODO: Seeks currently playing D: + if (query.match(/&t=[[0-9]+/g) !== null) { const time = query.match(/&t=[[0-9]+/g)[0].split("=")[1]; queue.node.seek(time * 1000); } } - } /** @@ -100,8 +100,9 @@ class Play extends BaseCommand { */ async autocompleteRun(client, interaction) { const query = interaction.options.getString("query"); + if (query === "") return; - if (query.includes("http")) return interaction.respond([ + if (query.startsWith("http")) return interaction.respond([ { name: "Current link", value: query, diff --git a/commands/Music/queue.js b/commands/Music/queue.js index 7916529a..a5b652bb 100644 --- a/commands/Music/queue.js +++ b/commands/Music/queue.js @@ -1,5 +1,4 @@ -const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"), - { QueueRepeatMode } = require("discord-player"); +const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); const BaseCommand = require("../../base/BaseCommand"); class Queue extends BaseCommand { @@ -26,8 +25,94 @@ class Queue extends BaseCommand { * * @param {import("../../base/JaBa")} client */ - async onLoad() { - //... + async onLoad(client) { + client.on("interactionCreate", async interaction => { + if (!interaction.isButton()) return; + + if (interaction.customId.startsWith("queue_")) { + const queue = client.player.nodes.get(interaction.guildId); + if (!queue) return interaction.error("music/play:NOT_PLAYING"); + + const { embeds, size } = generateQueueEmbeds(client, interaction, queue); + + let currentPage = Number(interaction.message.content.match(/\d+/g)[0]) - 1 ?? 0; + + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("queue_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"), + new ButtonBuilder().setCustomId("queue_next_page").setStyle(ButtonStyle.Primary).setEmoji("➡️"), + new ButtonBuilder().setCustomId("queue_jump_page").setStyle(ButtonStyle.Secondary).setEmoji("↗️"), + new ButtonBuilder().setCustomId("queue_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"), + ); + + if (interaction.customId === "queue_prev_page") { + await interaction.deferUpdate(); + + if (currentPage !== 0) { + --currentPage; + + interaction.editReply({ + content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${size}**`, + embeds: [embeds[currentPage]], + components: [row], + }); + } + } else if (interaction.customId === "queue_next_page") { + await interaction.deferUpdate(); + + if (currentPage < size - 1) { + currentPage++; + + interaction.editReply({ + content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${size}**`, + embeds: [embeds[currentPage]], + components: [row], + }); + } + } else if (interaction.customId === "queue_jump_page") { + await interaction.deferUpdate(); + + const selectMenu = new StringSelectMenuBuilder() + .setCustomId("queue_select") + .setPlaceholder(interaction.translate("common:NOTHING_SELECTED")); + + for (let i = 0; i < size; i++) { + selectMenu.addOptions( + new StringSelectMenuOptionBuilder() + .setLabel(`${i + 1}`) + .setValue(`${i}`), + ); + } + + const selectRow = new ActionRowBuilder().addComponents(selectMenu), + msg = await interaction.followUp({ + components: [selectRow], + ephemeral: true, + }); + + const filter = i => i.user.id === interaction.user.id, + collected = await msg.awaitMessageComponent({ filter, time: 10 * 1000 }), + page = Number(collected.values[0]); + + await collected.deferUpdate(); + + return interaction.editReply({ + content: `${interaction.translate("common:PAGE")}: **${page + 1}**/**${size}**`, + embeds: [embeds[page]], + components: [row], + }); + } else if (interaction.customId === "queue_stop") { + await interaction.deferUpdate(); + + row.components.forEach(component => { + component.setDisabled(true); + }); + + return interaction.editReply({ + components: [row], + }); + } + } + }); } /** * @@ -39,98 +124,24 @@ class Queue extends BaseCommand { const queue = client.player.nodes.get(interaction.guildId); if (!queue) return interaction.error("music/play:NOT_PLAYING"); - let currentPage = 0; - let embeds = generateQueueEmbeds(client, interaction, queue); + const { embeds, size } = generateQueueEmbeds(client, interaction, queue), + row = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("queue_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"), + new ButtonBuilder().setCustomId("queue_next_page").setStyle(ButtonStyle.Primary).setEmoji("➡️"), + new ButtonBuilder().setCustomId("queue_jump_page").setStyle(ButtonStyle.Secondary).setEmoji("↗️"), + new ButtonBuilder().setCustomId("queue_stop").setStyle(ButtonStyle.Danger).setEmoji("⏹️"), + ); - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder().setCustomId("queue_prev_page").setLabel(interaction.translate("music/queue:PREV_PAGE")).setStyle(ButtonStyle.Primary).setEmoji("⬅️"), - new ButtonBuilder().setCustomId("queue_next_page").setLabel(interaction.translate("music/queue:NEXT_PAGE")).setStyle(ButtonStyle.Primary).setEmoji("➡️"), - new ButtonBuilder().setCustomId("queue_jump_page").setLabel(interaction.translate("music/queue:JUMP_PAGE")).setStyle(ButtonStyle.Secondary).setEmoji("↗️"), - new ButtonBuilder().setCustomId("queue_stop").setLabel(interaction.translate("common:CANCEL")).setStyle(ButtonStyle.Danger).setEmoji("⏹️"), - ); - - await interaction.reply({ - content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, - embeds: [embeds[currentPage]], + if (interaction.customId) return await interaction.followUp({ + content: `${interaction.translate("common:PAGE")}: **1**/**${size}**`, + embeds: [embeds[0]], components: [row], }); - const filter = i => i.user.id === interaction.user.id; - const collector = interaction.channel.createMessageComponentCollector({ filter, idle: 20 * 1000 }); - - collector.on("collect", async i => { - if (i.isButton()) { - if (i.customId === "queue_prev_page") { - i.deferUpdate(); - if (embeds != generateQueueEmbeds(client, interaction, queue)) embeds = generateQueueEmbeds(client, interaction, queue); - - if (currentPage !== 0) { - --currentPage; - interaction.editReply({ - content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, - embeds: [embeds[currentPage]], - components: [row], - }); - } - } else if (i.customId === "queue_next_page") { - i.deferUpdate(); - if (embeds != generateQueueEmbeds(client, interaction, queue)) embeds = generateQueueEmbeds(client, interaction, queue); - - if (currentPage < embeds.length - 1) { - currentPage++; - interaction.editReply({ - content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, - embeds: [embeds[currentPage]], - components: [row], - }); - } - } else if (i.customId === "queue_jump_page") { - i.deferUpdate(); - if (embeds != generateQueueEmbeds(client, interaction, queue)) embeds = generateQueueEmbeds(client, interaction, queue); - - const msg = await interaction.followUp({ - content: interaction.translate("misc:JUMP_TO_PAGE", { - length: embeds.length, - }), - fetchReply: true, - }); - - const filter = res => { - return res.author.id === interaction.user.id && !isNaN(res.content); - }; - - interaction.channel.awaitMessages({ filter, max: 1, time: 10 * 1000 }).then(collected => { - if (embeds[collected.first().content - 1]) { - currentPage = collected.first().content - 1; - interaction.editReply({ - content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, - embeds: [embeds[currentPage]], - components: [row], - }); - - if (collected.first().deletable) collected.first().delete(); - if (msg.deletable) msg.delete(); - } else { - if (collected.first().deletable) collected.first().delete(); - if (msg.deletable) msg.delete(); - return; - } - }); - } else if (i.customId === "queue_stop") { - i.deferUpdate(); - collector.stop(); - } - } - }); - - collector.on("end", () => { - row.components.forEach(component => { - component.setDisabled(true); - }); - - return interaction.editReply({ - components: [row], - }); + await interaction.reply({ + content: `${interaction.translate("common:PAGE")}: **1**/**${size}**`, + embeds: [embeds[0]], + components: [row], }); } } @@ -144,7 +155,14 @@ class Queue extends BaseCommand { */ function generateQueueEmbeds(client, interaction, queue) { const embeds = [], - currentTrack = queue.currentTrack; + currentTrack = queue.currentTrack, + translated = { + "AUTOPLAY": interaction.translate("music/nowplaying:AUTOPLAY"), + "QUEUE": interaction.translate("music/nowplaying:QUEUE"), + "TRACK": interaction.translate("music/nowplaying:TRACK"), + "OFF": interaction.translate("common:DISABLED"), + "0": interaction.translate("common:DISABLED"), + }; let k = 10; @@ -154,22 +172,14 @@ function generateQueueEmbeds(client, interaction, queue) { .setThumbnail(currentTrack.thumbnail) .setColor(interaction.client.config.embed.color) .setDescription( - `${interaction.translate("music/nowplaying:REPEAT")}: \`${ - queue.repeatMode === QueueRepeatMode.AUTOPLAY - ? interaction.translate("music/nowplaying:AUTOPLAY") - : queue.repeatMode === QueueRepeatMode.QUEUE - ? interaction.translate("music/nowplaying:QUEUE") - : queue.repeatMode === QueueRepeatMode.TRACK - ? interaction.translate("music/nowplaying:TRACK") - : interaction.translate("common:DISABLED") - }\`\n${currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})`}\n> ${interaction.translate("music/queue:ADDED")} ${ - currentTrack.requestedBy - }\n\n**${interaction.translate("music/queue:NEXT")}**\n${interaction.translate("music/queue:NO_QUEUE")}`, + `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[queue.repeatMode]}\`\n${ + currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})` + }\n> ${interaction.translate("music/queue:ADDED")} ${currentTrack.requestedBy}\n\n**${interaction.translate("music/queue:NEXT")}**\n${interaction.translate("music/queue:NO_QUEUE")}`, ) .setTimestamp(); embeds.push(embed); - return embeds; + return { embeds: embeds, size: embeds.length }; } for (let i = 0; i < queue.getSize(); i += 10) { @@ -184,15 +194,9 @@ function generateQueueEmbeds(client, interaction, queue) { .setThumbnail(currentTrack.thumbnail) .setColor(interaction.client.config.embed.color) .setDescription( - `${interaction.translate("music/nowplaying:REPEAT")}: \`${ - queue.repeatMode === QueueRepeatMode.AUTOPLAY - ? interaction.translate("music/nowplaying:AUTOPLAY") - : queue.repeatMode === QueueRepeatMode.QUEUE - ? interaction.translate("music/nowplaying:QUEUE") - : queue.repeatMode === QueueRepeatMode.TRACK - ? interaction.translate("music/nowplaying:TRACK") - : interaction.translate("common:DISABLED") - }\`\n${currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})`}\n> ${interaction.translate("music/queue:ADDED")} ${ + `${interaction.translate("music/nowplaying:REPEAT")}: \`${translated[queue.repeatMode]}\`\n${ + currentTrack.url.startsWith("./clips") ? `${currentTrack.title} (clips)` : `[${currentTrack.title}](${currentTrack.url})` + }\n * ${interaction.translate("music/queue:ADDED")} ${ currentTrack.requestedBy }\n\n**${interaction.translate("music/queue:NEXT")}**\n${info}`, ) @@ -201,7 +205,7 @@ function generateQueueEmbeds(client, interaction, queue) { embeds.push(embed); } - return embeds; + return { embeds: embeds, size: embeds.length }; } module.exports = Queue; diff --git a/commands/NSFW/nsfw.js b/commands/NSFW/nsfw.js index 8b1de7ad..af0ee849 100644 --- a/commands/NSFW/nsfw.js +++ b/commands/NSFW/nsfw.js @@ -70,7 +70,7 @@ class NSFW extends BaseCommand { ), ); - const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("nsfw_select").setPlaceholder(client.translate("common:NOTHING_SELECTED")).addOptions(tags)); + const row = new ActionRowBuilder().addComponents(new StringSelectMenuBuilder().setCustomId("nsfw_select").setPlaceholder(interaction.translate("common:NOTHING_SELECTED")).addOptions(tags)); await interaction.editReply({ content: interaction.translate("common:AVAILABLE_OPTIONS"), diff --git a/commands/Tickets/closeticket.js b/commands/Tickets/closeticket.js index 8d24508b..791427b9 100644 --- a/commands/Tickets/closeticket.js +++ b/commands/Tickets/closeticket.js @@ -61,7 +61,7 @@ class CloseTicket extends BaseCommand { const button = new ButtonBuilder().setCustomId("cancel_closing").setLabel(interaction.translate("common:CANCEL")).setStyle(ButtonStyle.Danger); const row = new ActionRowBuilder().addComponents(button); - await interaction.reply({ + await interaction.editReply({ embeds: [embed], components: [row], }); diff --git a/dashboard/dashboard-core b/dashboard/dashboard-core index 58c20820..2826563a 160000 --- a/dashboard/dashboard-core +++ b/dashboard/dashboard-core @@ -1 +1 @@ -Subproject commit 58c2082074c0955cfafd9ebddb9ad694621df0a1 +Subproject commit 2826563af194effcb0469278ab3c38aa27bc2e82 diff --git a/dashboard/dashboard.js b/dashboard/dashboard.js index 8c09ae47..08f626f9 100644 --- a/dashboard/dashboard.js +++ b/dashboard/dashboard.js @@ -17,11 +17,11 @@ const locales = { module.exports.load = async client => { const commands = client.commands.map(v => { return { - _category: v.category, commandName: v.command.name, commandDescription: client.translate(`${v.category.toLowerCase()}/${v.command.name}:DESCRIPTION`), commandUsage: client.translate(`${v.category.toLowerCase()}/${v.command.name}:USAGE`), commandAlias: "", + _category: v.category, }; }); let categories = []; @@ -48,11 +48,6 @@ module.exports.load = async client => { id: client.config.userId, secret: client.config.dashboard.secret, }, - // SSL: { - // enabled: false, - // key: fs.readFileSync(`${__dirname}/../jababot-cloudflare.key`, "utf-8"), - // cert: fs.readFileSync(`${__dirname}/../jababot-cloudflare.crt`, "utf-8"), - // }, cookiesSecret: client.config.dashboard.secret, domain: client.config.dashboard.domain, redirectUri: `${client.config.dashboard.domain}/discord/callback`, diff --git a/dashboard/docs/updates.md b/dashboard/docs/updates.md index c625b2fe..0067e29e 100644 --- a/dashboard/docs/updates.md +++ b/dashboard/docs/updates.md @@ -1,27 +1,40 @@ # Обновления JaBa +## v4.4.0 + +* Добавлено + * Контекстная команда *Add to Queue* - Позволяет добавить в очередь трек/плейлист из сообщения.\ + ПКМ на сообщение -> Приложения -> Add to Queue.\ + Работает даже если в сообщении есть не только ссылка `Попробуй это: <ссылка>` или ссылка встроенна в текст `[текст](<ссылка>)`. + * Контекстная комана *Avatar* - Позволет получить аватар пользователя\ + Работает так же как и *Add to Queue*, только нужно нажать на пользователя. + +* Изменено + * Переписаны команды *queue*, *play*, *nowplaying*.\ + В *nowplaying* добавлен дополнительный функционал в виде кнопок. + ## v4.3.6 Скоро перепишу музыкальные команды, будет интересно, честно =) * Изменено - * Переписал команды которые используют кнопки и списки, многие из них теперь будут отвечать даже после перезагрузки бота. + * Переписаны команды которые используют кнопки и списки, многие из них теперь будут отвечать даже после перезагрузки бота. ## v4.3.5 * Добавлено * Логи удаления сообщений!\ - Настройку можно найти в *config set* и в панели управления. + Настройку можно найти в *config set* и в панели управления. * Начало проигрывания видео с указанного в ссылке времени. ### v4.3.3 * Добавлено * Система Тикетов!\ - Все необходимые команды есть в категории *Tickets*.\ - Для создавания тикетов нужно обязательно выбрать категорию где они будут сохраняться, сделать это можно через *config set*!\ - После этого используйте команду *createticketembed* в канале, где хотите чтобы появилось сообщение с кнопкой создания тикета.\ - С помощью команд *adduser* и *removeuser* можно добавлять и убирать людей из тикета соответственно.\ + Все необходимые команды есть в категории *Tickets*.\ + Для создавания тикетов нужно обязательно выбрать категорию где они будут сохраняться, сделать это можно через *config set*!\ + После этого используйте команду *createticketembed* в канале, где хотите чтобы появилось сообщение с кнопкой создания тикета.\ + С помощью команд *adduser* и *removeuser* можно добавлять и убирать людей из тикета соответственно.\ *closeticket* позволяет принудительно закрыть тикет. * Изменено @@ -31,7 +44,7 @@ * Добавлено * Мониторинг изменения сообщений!\ - Скоро добавлю другие эвенты, по типу входа, выхода, обновление участника. + Скоро добавлю другие эвенты, по типу входа, выхода, обновление участника. * Исправления * Серьёзная ошибка, из-за которой данные не сохранялись в базу данных 🤯. @@ -40,7 +53,7 @@ * Добавлено * Полностью переделанная панель управления!\ - Она ещё не закончена, так что ждите продолжения. + Она ещё не закончена, так что ждите продолжения. ### v4.2.6 @@ -49,13 +62,13 @@ * Изменено * Отключено уведомление об ачивках, включу когда переделаю.\ - Хочу отправлять их в ЛС. + Хочу отправлять их в ЛС. ### v4.2.5 * Изменено * Изменения в локализации, основным языком теперь является английский.\ - Подсказки по командам зависят от языка вашего клиента, ответ зависит от языка сервера. + Подсказки по командам зависят от языка вашего клиента, ответ зависит от языка сервера. * Отключена команда *memes*. * Отключена команда *staff*. @@ -147,9 +160,9 @@ ### v4.1.15 * Изменения - * Переписана команда *config*. - * Теперь с её помощью можно просматривать и изменять настройки сервера. - * Использование смотрите в *help*. + * Переписана команда *config*.\ + Теперь с её помощью можно просматривать и изменять настройки сервера.\ + Использование смотрите в *help*. * Удалено * Команды *setbirthdays*, *setmodlogs*, *setnews*, *setreports*, *setsuggests*. @@ -157,9 +170,9 @@ ### v4.1.14 * Добавлено - * Команда *selectroles* - Возможность выбора необязательных ролей пользователями. - * Сначала необходимо создать сообщение через */selectroles message text:<Ваш текст>*. - * После того как сообщение будет создано бот даст подсказку как добавлять роли к списку, следуйте инструкции. + * Команда *selectroles* - Возможность выбора необязательных ролей пользователями.\ + Сначала необходимо создать сообщение через */selectroles message text:<Ваш текст>*.\ + После того как сообщение будет создано бот даст подсказку как добавлять роли к списку, следуйте инструкции. * Изменения * Украинский перевод полностью обновлён. @@ -222,7 +235,8 @@ ### v4.1.6 * Изменения - * Изменён способ указания типа повтора в *loop*. Теперь вы указываете тип аргументом (подсказки имеются), а не из выпадающего списка в отдельном сообщении. Это одновременно удобно, быстро и меньше кода =) + * Изменён способ указания типа повтора в *loop*.\ + Теперь вы указываете тип аргументом (подсказки имеются), а не из выпадающего списка в отдельном сообщении. Это одновременно удобно, быстро и кода меньше =) * Исправления * Фиксы в *tictactoe*. @@ -293,7 +307,8 @@ * Перенесена категория *Administration*. * Исправления - * Изменён способ получения музыки. На данный момент не работает Spotify, в будущих обновлениях постараюсь вернуть, но это не точно. + * Изменён способ получения музыки.\ + На данный момент не работает Spotify, в будущих обновлениях постараюсь вернуть, но это не точно. * Панель управления снова работает. * Множество мелких правок. diff --git a/helpers/extenders.js b/helpers/extenders.js index 0c90c2b9..1f55f701 100644 --- a/helpers/extenders.js +++ b/helpers/extenders.js @@ -4,15 +4,15 @@ User.prototype.getUsername = function () { return this.discriminator === "0" ? this.username : this.tag; }; -BaseInteraction.prototype.translate = function (key, args) { - const language = this.client.translations.get(this.guild ? this.guild.data.language : "en-US"); - if (!language) throw "Interaction: Invalid language set in data."; +BaseInteraction.prototype.translate = function (key, args, emoji) { + const lang = this.client.translations.get(this.guild.data.language ?? "en-US"); + const string = emoji ? `${this.client.customEmojis[emoji]} | ${lang(key, args)}` : lang(key, args); - return language(key, args); + return string; }; BaseInteraction.prototype.replyT = function (key, args, options = {}) { - const translated = this.translate(key, args, this.guild ? this.guild.data.language : "en-US"); + const translated = this.translate(key, args, this.guild.data.language ?? "en-US"); const string = options.prefixEmoji ? `${this.client.customEmojis[options.prefixEmoji]} | ${translated}` : translated; if (options.edit) return this.editReply({ content: string, ephemeral: options.ephemeral || false }); @@ -31,15 +31,15 @@ BaseInteraction.prototype.error = function (key, args, options = {}) { return this.replyT(key, args, options); }; -Message.prototype.translate = function (key, args) { - const language = this.client.translations.get(this.guild ? this.guild.data.language : "en-US"); - if (!language) throw "Message: Invalid language set in data."; +Message.prototype.translate = function (key, args, emoji) { + const lang = this.client.translations.get(this.guild.data.language ?? "en-US"); + const string = emoji ? `${this.client.customEmojis[emoji]} | ${lang(key, args)}` : lang(key, args); - return language(key, args); + return string; }; Message.prototype.replyT = function (key, args, options = {}) { - const translated = this.translate(key, args, this.guild ? this.guild.data.language : "en-US"); + const translated = this.translate(key, args, this.guild.data.language ?? "en-US"); const string = options.prefixEmoji ? `${this.client.customEmojis[options.prefixEmoji]} | ${translated}` : translated; if (options.edit) return this.edit({ content: string, allowedMentions: { repliedUser: options.mention ? true : false } }); diff --git a/languages/en-US/common.json b/languages/en-US/common.json index 6c8e4852..4b6052a0 100644 --- a/languages/en-US/common.json +++ b/languages/en-US/common.json @@ -32,6 +32,7 @@ "IP": "IP Address", "JOINED": "Joined", "LANGUAGE": "Language", + "LINK": "Link", "LEVEL": "Level", "MONEY": "Money", "MEMBER": "Member", diff --git a/languages/en-US/music/nowplaying.json b/languages/en-US/music/nowplaying.json index fcd5a785..b6bd629a 100644 --- a/languages/en-US/music/nowplaying.json +++ b/languages/en-US/music/nowplaying.json @@ -3,6 +3,7 @@ "USAGE": "", "EXAMPLES": "nowplaying", "CURRENTLY_PLAYING": "Currently playing", + "LINK": "Send a link in chat", "T_TITLE": "Title", "T_AUTHOR": "Author", "T_DURATION": "Duration", diff --git a/languages/ru-RU/common.json b/languages/ru-RU/common.json index 4ca9f116..707c223c 100644 --- a/languages/ru-RU/common.json +++ b/languages/ru-RU/common.json @@ -32,6 +32,7 @@ "IP": "IP адрес", "JOINED": "Присоеденился", "LANGUAGE": "Язык", + "LINK": "Ссылка", "LEVEL": "Уровень", "MONEY": "Деньги", "MEMBER": "Участник", diff --git a/languages/ru-RU/music/back.json b/languages/ru-RU/music/back.json index ea191b6b..eec6a629 100644 --- a/languages/ru-RU/music/back.json +++ b/languages/ru-RU/music/back.json @@ -3,5 +3,5 @@ "USAGE": "", "EXAMPLES": "back", "NO_PREV_SONG": "Предыдущий трек отсутствует", - "SUCCESS": "Играет предыдущий трек" + "SUCCESS": "Включён предыдущий трек" } \ No newline at end of file diff --git a/languages/ru-RU/music/nowplaying.json b/languages/ru-RU/music/nowplaying.json index 0f27612b..2715f8b7 100644 --- a/languages/ru-RU/music/nowplaying.json +++ b/languages/ru-RU/music/nowplaying.json @@ -3,6 +3,7 @@ "USAGE": "", "EXAMPLES": "nowplaying", "CURRENTLY_PLAYING": "Сейчас играет", + "LINK": "Введите ссылку в чат", "T_TITLE": "Название", "T_AUTHOR": "Автор", "T_DURATION": "Длительность", diff --git a/languages/uk-UA/common.json b/languages/uk-UA/common.json index 2b9fe1c6..900eb444 100644 --- a/languages/uk-UA/common.json +++ b/languages/uk-UA/common.json @@ -31,6 +31,7 @@ "IP": "IP-адреса", "JOINED": "Приєднався", "LANGUAGE": "Мова", + "LINK": "Силка", "LEVEL": "Рівень", "MONEY": "Гроші", "MEMBER": "Учасник", diff --git a/languages/uk-UA/music/back.json b/languages/uk-UA/music/back.json index 648b46e9..976bb83c 100644 --- a/languages/uk-UA/music/back.json +++ b/languages/uk-UA/music/back.json @@ -3,5 +3,5 @@ "USAGE": "", "EXAMPLES": "back", "NO_PREV_SONG": "Попередній трек відсутній", - "SUCCESS": "Грає попередній трек" + "SUCCESS": "Включений попередній трек" } \ No newline at end of file diff --git a/languages/uk-UA/music/nowplaying.json b/languages/uk-UA/music/nowplaying.json index ff41a422..0496f442 100644 --- a/languages/uk-UA/music/nowplaying.json +++ b/languages/uk-UA/music/nowplaying.json @@ -3,6 +3,7 @@ "USAGE": "", "EXAMPLES": "nowplaying", "CURRENTLY_PLAYING": "Зараз грає", + "LINK": "Введіть силку на чат", "T_TITLE": "Назва", "T_AUTHOR": "Автор", "T_DURATION": "Тривалість", diff --git a/package.json b/package.json index 3f0e6b9a..fa315ea1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jaba", - "version": "4.3.6", + "version": "4.4.0", "description": "My Discord Bot", "main": "index.js", "scripts": { @@ -8,7 +8,7 @@ "start": "node .", "lint": "eslint . --ext .js" }, - "author": "Discord: jonny_bro", + "author": "Discord: @jonny_bro", "dependencies": { "@discord-player/extractor": "^4.4.5", "@discordjs/opus": "^0.9.0", @@ -35,13 +35,13 @@ "eslint": "^8.52.0" }, "eslintConfig": { + "extends": "eslint:recommended", "env": { "commonjs": true, "es6": true, "es2020": true, "node": true }, - "extends": "eslint:recommended", "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly"