diff --git a/commands/Music/clips.js b/commands/Music/clips.js index c328b2af..4abe0412 100644 --- a/commands/Music/clips.js +++ b/commands/Music/clips.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder, ActionRowBuilder, SelectMenuBuilder, } = require("discord.js"), +const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, EmbedBuilder, ButtonStyle } = require("discord.js"), { joinVoiceChannel, createAudioResource, createAudioPlayer, getVoiceConnection, AudioPlayerStatus } = require("@discordjs/voice"); const BaseCommand = require("../../base/BaseCommand"), fs = require("fs"); @@ -13,7 +13,10 @@ class Clips extends BaseCommand { command: new SlashCommandBuilder() .setName("clips") .setDescription(client.translate("music/clips:DESCRIPTION")) - .setDMPermission(false), + .setDMPermission(false) + .addStringOption(option => option.setName("query") + .setDescription(client.translate("music/clips:QUERY")) + .setRequired(true)), aliases: [], dirname: __dirname, ownerOnly: false @@ -36,6 +39,8 @@ class Clips extends BaseCommand { fs.readdir("./clips", async function (err, files) { await interaction.deferReply(); + const query = interaction.options.getString("query"); + if (err) { interaction.editReply({ content: "```js\n" + err + "```" @@ -43,80 +48,182 @@ class Clips extends BaseCommand { return console.log("Unable to read directory: " + err); } - const clips = files.map(file => { - const fileName = file.substring(0, file.length - 4); - return { - label: fileName, - value: fileName - }; - }); + if (query === "list") { + const clips = files.map(file => file.substring(0, file.length - 4)); + let currentPage = 0; + const embeds = generateClipsEmbeds(interaction, clips); - const row = new ActionRowBuilder() - .addComponents( - new SelectMenuBuilder() - .setCustomId("clips_select") - .setPlaceholder(client.translate("common:NOTHING_SELECTED")) - .addOptions(clips) - ); + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId("clips_prev_page") + .setStyle(ButtonStyle.Primary) + .setEmoji("⬅️"), + new ButtonBuilder() + .setCustomId("clips_next_page") + .setStyle(ButtonStyle.Primary) + .setEmoji("➡️"), + new ButtonBuilder() + .setCustomId("clips_jump_page") + .setStyle(ButtonStyle.Secondary) + .setEmoji("↗️"), + new ButtonBuilder() + .setCustomId("clips_stop") + .setStyle(ButtonStyle.Danger) + .setEmoji("⏹️"), + ); - await interaction.editReply({ - content: interaction.translate("music/clips:AVAILABLE_CLIPS"), - components: [row] - }); + await interaction.editReply({ + content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, + embeds: [embeds[currentPage]], + components: [row] + }); - const filter = i => i.user.id === interaction.user.id; - const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (15 * 1000) }); + 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.isSelectMenu() && i.customId === "clips_select") { - const clip = i?.values[0]; - const voice = i.member.voice.channel; - if (!voice) return i.update({ content: interaction.translate("music/play:NO_VOICE_CHANNEL"), components: [] }); - const queue = client.player.getQueue(i.guild.id); - if (queue) return i.update({ content: interaction.translate("music/clips:ACTIVE_QUEUE"), components: [] }); - if (getVoiceConnection(i.guild.id)) return i.update({ content: interaction.translate("music/clips:ACTIVE_CLIP"), components: [] }); - if (!fs.existsSync(`./clips/${clip}.mp3`)) return i.update({ content: interaction.translate("music/clips:NO_FILE", { file: clip }), components: [] }); + collector.on("collect", async i => { + if (i.isButton()) { + if (i.customId === "clips_prev_page") { + i.deferUpdate(); - try { - const connection = joinVoiceChannel({ - channelId: voice.id, - guildId: interaction.guild.id, - adapterCreator: interaction.guild.voiceAdapterCreator - }); + if (currentPage !== 0) { + --currentPage; + interaction.editReply({ + content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, + embeds: [embeds[currentPage]], + components: [row] + }); + } + } else if (i.customId === "clips_next_page") { + i.deferUpdate(); - const resource = createAudioResource(fs.createReadStream(`./clips/${clip}.mp3`)); - const player = createAudioPlayer() - .on("error", err => { - connection.destroy(); - console.error(err.message); + 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 === "clips_jump_page") { + i.deferUpdate(); + + const msg = await interaction.followUp({ + content: interaction.translate("music/queue:PAGE_TO_JUMP", { + length: embeds.length + }), + fetchReply: true }); - player.play(resource); - connection.subscribe(player); + const filter = res => { + return res.author.id === interaction.user.id && !isNaN(res.content); + }; - player.on(AudioPlayerStatus.Idle, () => { - connection.destroy(); - }); - } catch (error) { - console.error(error); + 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 === "clips_stop") { + i.deferUpdate(); + collector.stop(); + } } + }); - await interaction.editReply({ - content: interaction.translate("music/clips:PLAYING", { - clip - }), - components: [] + collector.on("end", () => { + row.components.forEach(component => { + component.setDisabled(true); }); - } - }); - collector.on("end", () => { - return interaction.editReply({ + return interaction.editReply({ + components: [row] + }); + }); + } else { + const voice = interaction.member.voice.channel; + if (!voice) return interaction.editReply({ content: interaction.translate("music/play:NO_VOICE_CHANNEL") }); + const queue = client.player.getQueue(interaction.guild.id); + if (queue) return interaction.editReply({ content: interaction.translate("music/clips:ACTIVE_QUEUE") }); + if (getVoiceConnection(interaction.guild.id)) return interaction.editReply({ content: interaction.translate("music/clips:ACTIVE_CLIP") }); + if (!fs.existsSync(`./clips/${query}.mp3`)) return interaction.editReply({ content: interaction.translate("music/clips:NO_FILE", { file: query }) }); + + try { + const connection = joinVoiceChannel({ + channelId: voice.id, + guildId: interaction.guild.id, + adapterCreator: interaction.guild.voiceAdapterCreator + }); + + const resource = createAudioResource(fs.createReadStream(`./clips/${query}.mp3`)); + const player = createAudioPlayer() + .on("error", err => { + connection.destroy(); + console.error(err.message); + }); + + player.play(resource); + connection.subscribe(player); + + player.on(AudioPlayerStatus.Idle, () => { + connection.destroy(); + }); + } catch (error) { + console.error(error); + } + + await interaction.editReply({ + content: interaction.translate("music/clips:PLAYING", { + clip: query + }), components: [] }); - }); + } }); } } +/** + * + * @param {import("discord.js").ChatInputCommandInteraction} interaction + * @param {Array} clips + * @returns + */ +function generateClipsEmbeds(interaction, clips) { + const embeds = []; + let k = 10; + + for (let i = 0; i < clips.length; i += 10) { + const current = clips.slice(i, k); + k += 10; + + const page = current.join("\n"); + + const embed = new EmbedBuilder() + .setColor(interaction.client.config.embed.color) + .setFooter({ + text: interaction.client.config.embed.footer + }) + .setTitle(interaction.translate("music/clips:CLIPS_LIST")) + .setDescription(page) + .setTimestamp(); + embeds.push(embed); + } + + return embeds; +} + module.exports = Clips; \ No newline at end of file diff --git a/commands/Owner/servers.js b/commands/Owner/servers.js index 944474bb..62c1a140 100644 --- a/commands/Owner/servers.js +++ b/commands/Owner/servers.js @@ -56,15 +56,14 @@ class Servers extends BaseCommand { .setEmoji("⏹️"), ); - const msg = await interaction.editReply({ + await interaction.editReply({ content: `${interaction.translate("common:PAGE")}: **${currentPage + 1}**/**${embeds.length}**`, - fetchReply: true, embeds: [embeds[currentPage]], components: [row] }); const filter = i => i.user.id === interaction.user.id; - const collector = msg.createMessageComponentCollector({ filter, idle: (20 * 1000) }); + const collector = interaction.channel.createMessageComponentCollector({ filter, idle: (20 * 1000) }); collector.on("collect", async i => { if (i.isButton()) { @@ -143,7 +142,7 @@ class Servers extends BaseCommand { /** * * @param {import("discord.js").ChatInputCommandInteraction} interaction - * @param {*} servers + * @param {Array} servers * @returns */ function generateServersEmbeds(interaction, servers) { diff --git a/dashboard/public/docs/updates.md b/dashboard/public/docs/updates.md index bf21c6fd..13971ccc 100644 --- a/dashboard/public/docs/updates.md +++ b/dashboard/public/docs/updates.md @@ -1,3 +1,7 @@ +### JaBa v4.1.13 +* Изменения + * Переписана команда *clips*. + ### JaBa v4.1.12 * Исправления * Фикс поиска по ссылкам. diff --git a/languages/ru-RU/music/clips.json b/languages/ru-RU/music/clips.json index 43b66f5e..68cf9758 100644 --- a/languages/ru-RU/music/clips.json +++ b/languages/ru-RU/music/clips.json @@ -2,8 +2,10 @@ "DESCRIPTION": "Показать список доступных звуков и воспроизвести выбранный", "USAGE": "", "EXAMPLES": "clips", - "AVAILABLE_CLIPS": "Список доступных клипов:", + "QUERY": "Название клипа / list", + "CLIPS_LIST": "Список доступных клипов", + "NO_CLIP": "Данный файл не существует", "ACTIVE_QUEUE": "Не могу воспроизвести клип, т.к. на сервере есть активная очередь", "ACTIVE_CLIP": "Уже воспроизводится какой-то файл", - "PLAYING": "Начато проигрывание клипа `{{clip}}`" + "PLAYING": "Начато проигрывание клипа **{{clip}}**" } \ No newline at end of file diff --git a/package.json b/package.json index ac9cd49f..a89135bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jaba", - "version": "4.1.12", + "version": "4.1.13", "description": "My Discord Bot", "main": "index.js", "private": true,