From 98f726ccf4a6ae4e0b54b386f07730b59398b0ce Mon Sep 17 00:00:00 2001 From: GamesTwoLife Date: Sat, 7 Sep 2024 20:31:03 +0300 Subject: [PATCH] chore: update to discord.js 14.16.1, remove Russian localization, replace deprecated methods - Updated discord.js from ^14.15.2 to ^14.16.1 - Removed Russian localization - Replaced setDMPermission with setContexts - Added tabulation for better code formatting - Bumped version to 1.2.0 --- .vscode/extensions.json | 8 + .vscode/settings.json | 19 + README.md | 4 +- commands/info/ping.js | 6 +- commands/sample/message sample.js | 6 +- commands/sample/sample.js | 409 ++++++++++-------- commands/sample/user sample.js | 6 +- events/Interaction/butonInteraction.js | 138 +++--- .../Interaction/chatInputInteractionCreate.js | 40 +- index.js | 9 +- locales/resources.js | 24 +- locales/ru/commands.json | 53 --- locales/ru/common.json | 16 - package.json | 84 ++-- 14 files changed, 393 insertions(+), 429 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json delete mode 100644 locales/ru/commands.json delete mode 100644 locales/ru/common.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b90895a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "github.vscode-pull-request-github", + "christian-kohler.npm-intellisense", + "christian-kohler.path-intellisense" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..692a2ec --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "eslint.experimental.useFlatConfig": true, + "eslint.workingDirectories": [ + { "directory": "${workspaceFolder}" }, + { "pattern": "./src/*/" } + ], + "editor.fontLigatures": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "never", + "source.fixAll.eslint": "explicit", + "source.fixAll": "explicit" + }, + "editor.trimAutoWhitespace": false, + "files.insertFinalNewline": true, + "files.eol": "\r\n", + "typescript.enablePromptUseWorkspaceTsdk": true +} diff --git a/README.md b/README.md index 7277c9a..4b188a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Ласкаво просимо до шаблону дискорд бота

- Version + Version Документація @@ -30,7 +30,7 @@ Discord Bot Template — це шаблон бота з відкритим вих #### • **Динамічний обробник команд (як слеш так і контекстного меню):** - Мій шаблон постачається з динамічним обробником команд, який дуже легко налаштовувати та створювати команди. -- Команди в папці commands отримують об’єкт [`ChatInputCommandInteraction`](https://discord.js.org/docs/packages/discord.js/14.15.2/ChatInputCommandInteraction) або [`ContextMenuCommandInteraction`](https://discord.js.org/docs/packages/discord.js/14.15.2/ContextMenuCommandInteraction) в залежності від типу команди (слеш або контекстна). +- Команди в папці commands отримують об’єкт [`ChatInputCommandInteraction`](https://discord.js.org/docs/packages/discord.js/14.16.1/ChatInputCommandInteraction:Class) або [`ContextMenuCommandInteraction`](https://discord.js.org/docs/packages/discord.js/14.16.1/ContextMenuCommandInteraction:Class) в залежності від типу команди (слеш або контекстна). - **ВАЖЛИВО:** У шаблоні ми надсилаємо команди в Discord для реєстрації лише в 1 гільдії. Це тому, що є 2 типи команд, гільдійські та глобальні. Команди гільдії обмежені 1 гільдією, але щоразу, коли ви їх оновлюєте, вони набувають чинності негайно, тоді як для глобальних команд потрібно до 1 години. Тому використовуйте команди гільди у розробці та глобальні команди для виробництва. (змінити це можна використавши параметр в кожному файлі команди `devGuildOnly`: `true` - команда в 1 гільдії, `false` - глобальна команда) #### • **Динамічний обробник взаємодії кнопок:** diff --git a/commands/info/ping.js b/commands/info/ping.js index 0283abf..0119fbf 100644 --- a/commands/info/ping.js +++ b/commands/info/ping.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder } = require("discord.js"); +const { SlashCommandBuilder, InteractionContextType } = require("discord.js"); const { t } = require("i18next"); /** @@ -12,12 +12,12 @@ module.exports = { uk: t('commands:info.ping.description', { lng: "uk" }), ru: t('commands:info.ping.description', { lng: "ru" }) }) - .setDMPermission(false), + .setContexts([InteractionContextType.Guild]), options: { cooldown: 30, ownerOnly: false, devGuildOnly: true, - bot_permissions: ["ViewChannel", "SendMessages"], + bot_permissions: [], }, async execute(interaction) { diff --git a/commands/sample/message sample.js b/commands/sample/message sample.js index ab98c34..5bd98da 100644 --- a/commands/sample/message sample.js +++ b/commands/sample/message sample.js @@ -1,4 +1,4 @@ -const { ContextMenuCommandBuilder, ApplicationCommandType } = require("discord.js"); +const { ContextMenuCommandBuilder, ApplicationCommandType, InteractionContextType } = require("discord.js"); const { t } = require("i18next"); /** @@ -12,12 +12,12 @@ module.exports = { ru: t('commands:sample.message_sample.description', { lng: "ru" }).slice(0, 32) }) .setType(ApplicationCommandType.Message) - .setDMPermission(false), + .setContexts([InteractionContextType.Guild]), options: { cooldown: 10, ownerOnly: false, devGuildOnly: true, - bot_permissions: ["ViewChannel", "SendMessages"], + bot_permissions: [], }, async execute(interaction) { diff --git a/commands/sample/sample.js b/commands/sample/sample.js index 80db7eb..4735f87 100644 --- a/commands/sample/sample.js +++ b/commands/sample/sample.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, EmbedBuilder, UserSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, RoleSelectMenuBuilder } = require("discord.js"); +const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, EmbedBuilder, UserSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, RoleSelectMenuBuilder, PollLayoutType, InteractionContextType } = require("discord.js"); const { t } = require("i18next"); const buttonPagination = require("../../utils/buttonPagination"); const buttonWrapper = require("../../utils/buttonWrapper"); @@ -7,194 +7,219 @@ const buttonWrapper = require("../../utils/buttonWrapper"); * @type {import('../../typings').Command} */ module.exports = { - data: new SlashCommandBuilder() - .setName("sample") - .setDescription(t('commands:sample.sample.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.description', { lng: "uk" }), - ru: t('commands:sample.sample.description', { lng: "ru" }) - }) - .addSubcommand(subcommand => - subcommand - .setName("autocomplete") - .setDescription(t('commands:sample.sample.autocomplete.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.autocomplete.description', { lng: "uk" }), - ru: t('commands:sample.sample.autocomplete.description', { lng: "ru" }) - }) - .addStringOption(option => - option - .setName("input") - .setDescription(t('commands:sample.sample.autocomplete.options.input', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.autocomplete.options.input', { lng: "uk" }), - ru: t('commands:sample.sample.autocomplete.options.input', { lng: "ru" }) - }) - .setAutocomplete(true) - .setRequired(true) - ) - ) - .addSubcommand(subcommand => - subcommand - .setName("button") - .setDescription(t('commands:sample.sample.button.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.button.description', { lng: "uk" }), - ru: t('commands:sample.sample.button.description', { lng: "ru" }) - }) - ) - .addSubcommand(subcommand => - subcommand - .setName("menu") - .setDescription("Sample Menu") - .setDescription(t('commands:sample.sample.menu.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.menu.description', { lng: "uk" }), - ru: t('commands:sample.sample.menu.description', { lng: "ru" }) - }) - ) - .addSubcommand(subcommand => - subcommand - .setName("modal") - .setDescription(t('commands:sample.sample.modal.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.modal.description', { lng: "uk" }), - ru: t('commands:sample.sample.modal.description', { lng: "ru" }) - }) - ) - .addSubcommand(subcommand => - subcommand - .setName("pagination") - .setDescription(t('commands:sample.sample.pagination.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.pagination.description', { lng: "uk" }), - ru: t('commands:sample.sample.pagination.description', { lng: "ru" }) - }) - ) - .addSubcommand(subcommand => - subcommand - .setName("buttonwrapper") - .setDescription(t('commands:sample.sample.buttonwrapper.description', { lng: "en" })) - .setDescriptionLocalizations({ - uk: t('commands:sample.sample.buttonwrapper.description', { lng: "uk" }), - ru: t('commands:sample.sample.buttonwrapper.description', { lng: "ru" }) - }) - ) - .setDMPermission(false), - options: { - cooldown: 10, - ownerOnly: false, - devGuildOnly: true, - bot_permissions: ["ViewChannel", "SendMessages"], - }, - - async execute(interaction) { - const { options } = interaction; - - switch(options.getSubcommand()) { - case "autocomplete": { - const Input = options.getString("input", true); - - await interaction.reply({ - content: `${Input}` - }); - } break; - - case "button": { - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId("sample") - .setStyle(ButtonStyle.Secondary) - .setLabel("sample button") - ); - - await interaction.reply({ - components: [row] - }); - } break; - - case "menu": { - const row = new ActionRowBuilder().addComponents( - new StringSelectMenuBuilder() - .setCustomId("string_sample") - .setOptions( - { - label: "sample option", - value: "sample_option" - }, - { - label: "sample option_two", - value: "sample_option_two" - } - ) - ); - - const row2 = new ActionRowBuilder().addComponents( - new UserSelectMenuBuilder().setCustomId("user_sample") - ); - - const row3 = new ActionRowBuilder().addComponents( - new MentionableSelectMenuBuilder().setCustomId("mentionable_sample") - ); - - const row4 = new ActionRowBuilder().addComponents( - new ChannelSelectMenuBuilder().setCustomId("channel_sample") - ); - - const row5 = new ActionRowBuilder().addComponents( - new RoleSelectMenuBuilder().setCustomId("role_sample") - ); - - await interaction.reply({ - components: [row, row2, row3, row4, row5] - }); - } break; - - case "modal": { - const modal = new ModalBuilder() - .setCustomId("sample") - .setTitle("Sample"); - - const input = new TextInputBuilder() - .setCustomId("input") - .setStyle(TextInputStyle.Short) - .setLabel("Input Text") - .setRequired(true); - - const row = new ActionRowBuilder().addComponents([input]); - - modal.setComponents(row); - - await interaction.showModal(modal) - } break; - - case "pagination": { - let embeds = []; - - for (let i = 0; i < 5; i++) { - embeds.push(new EmbedBuilder().setColor(0x2b2d31).setDescription(`${t(`commands:sample.sample.pagination.pageStrings.${[i]}`, { lng: interaction.locale })}`)); - } - - await buttonPagination(interaction, embeds); - } break; - - case "buttonwrapper": { - const buttons = [ - new ButtonBuilder() - .setCustomId("say_hello") - .setStyle(ButtonStyle.Secondary) - .setLabel(t('commands:sample.sample.buttonwrapper.say_hello', { lng: interaction.locale })), - new ButtonBuilder() - .setLabel(t('commands:sample.sample.buttonwrapper.cool_template', { lng: interaction.locale })) - .setStyle(ButtonStyle.Link) - .setURL("https://github.com/GamesTwoLife/DiscordBot-Template"), - ]; - - await interaction.reply({ - content: t('commands:sample.sample.buttonwrapper.content', { lng: interaction.locale }), - components: buttonWrapper(buttons) - }); - } break; - } - }, -}; \ No newline at end of file + data: new SlashCommandBuilder() + .setName("sample") + .setDescription(t('commands:sample.sample.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.description', { lng: "uk" }), + ru: t('commands:sample.sample.description', { lng: "ru" }) + }) + .addSubcommand(subcommand => + subcommand + .setName("autocomplete") + .setDescription(t('commands:sample.sample.autocomplete.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.autocomplete.description', { lng: "uk" }), + ru: t('commands:sample.sample.autocomplete.description', { lng: "ru" }) + }) + .addStringOption(option => + option + .setName("input") + .setDescription(t('commands:sample.sample.autocomplete.options.input', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.autocomplete.options.input', { lng: "uk" }), + ru: t('commands:sample.sample.autocomplete.options.input', { lng: "ru" }) + }) + .setAutocomplete(true) + .setRequired(true) + ) + ) + .addSubcommand(subcommand => + subcommand + .setName("button") + .setDescription(t('commands:sample.sample.button.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.button.description', { lng: "uk" }), + ru: t('commands:sample.sample.button.description', { lng: "ru" }) + }) + ) + .addSubcommand(subcommand => + subcommand + .setName("menu") + .setDescription("Sample Menu") + .setDescription(t('commands:sample.sample.menu.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.menu.description', { lng: "uk" }), + ru: t('commands:sample.sample.menu.description', { lng: "ru" }) + }) + ) + .addSubcommand(subcommand => + subcommand + .setName("modal") + .setDescription(t('commands:sample.sample.modal.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.modal.description', { lng: "uk" }), + ru: t('commands:sample.sample.modal.description', { lng: "ru" }) + }) + ) + .addSubcommand(subcommand => + subcommand + .setName("pagination") + .setDescription(t('commands:sample.sample.pagination.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.pagination.description', { lng: "uk" }), + ru: t('commands:sample.sample.pagination.description', { lng: "ru" }) + }) + ) + .addSubcommand(subcommand => + subcommand + .setName("buttonwrapper") + .setDescription(t('commands:sample.sample.buttonwrapper.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.buttonwrapper.description', { lng: "uk" }), + ru: t('commands:sample.sample.buttonwrapper.description', { lng: "ru" }) + }) + ) + .addSubcommand(subcommand => + subcommand + .setName("create-poll") + .setDescription(t('commands:sample.sample.create-poll.description', { lng: "en" })) + .setDescriptionLocalizations({ + uk: t('commands:sample.sample.create-poll.description', { lng: "uk" }), + ru: t('commands:sample.sample.create-poll.description', { lng: "ru" }) + }) + ) + .setContexts([InteractionContextType.Guild]), + options: { + cooldown: 10, + ownerOnly: false, + devGuildOnly: true, + bot_permissions: [], + }, + + async execute(interaction) { + const { options } = interaction; + + switch(options.getSubcommand()) { + case "autocomplete": { + const Input = options.getString("input", true); + + await interaction.reply({ + content: `${Input}` + }); + } break; + + case "button": { + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId("sample") + .setStyle(ButtonStyle.Secondary) + .setLabel("sample button") + ); + + await interaction.reply({ + components: [row] + }); + } break; + + case "menu": { + const row = new ActionRowBuilder().addComponents( + new StringSelectMenuBuilder() + .setCustomId("string_sample") + .setOptions( + { + label: "sample option", + value: "sample_option" + }, + { + label: "sample option_two", + value: "sample_option_two" + } + ) + ); + + const row2 = new ActionRowBuilder().addComponents( + new UserSelectMenuBuilder().setCustomId("user_sample") + ); + + const row3 = new ActionRowBuilder().addComponents( + new MentionableSelectMenuBuilder().setCustomId("mentionable_sample") + ); + + const row4 = new ActionRowBuilder().addComponents( + new ChannelSelectMenuBuilder().setCustomId("channel_sample") + ); + + const row5 = new ActionRowBuilder().addComponents( + new RoleSelectMenuBuilder().setCustomId("role_sample") + ); + + await interaction.reply({ + components: [row, row2, row3, row4, row5] + }); + } break; + + case "modal": { + const modal = new ModalBuilder() + .setCustomId("sample") + .setTitle("Sample"); + + const input = new TextInputBuilder() + .setCustomId("input") + .setStyle(TextInputStyle.Short) + .setLabel("Input Text") + .setRequired(true); + + const row = new ActionRowBuilder().addComponents([input]); + + modal.setComponents(row); + + await interaction.showModal(modal) + } break; + + case "pagination": { + let embeds = []; + + for (let i = 0; i < 5; i++) { + embeds.push(new EmbedBuilder().setColor(0x2b2d31).setDescription(`${t(`commands:sample.sample.pagination.pageStrings.${[i]}`, { lng: interaction.locale })}`)); + } + + await buttonPagination(interaction, embeds); + } break; + + case "buttonwrapper": { + const buttons = [ + new ButtonBuilder() + .setCustomId("say_hello") + .setStyle(ButtonStyle.Secondary) + .setLabel(t('commands:sample.sample.buttonwrapper.say_hello', { lng: interaction.locale })), + new ButtonBuilder() + .setLabel(t('commands:sample.sample.buttonwrapper.cool_template', { lng: interaction.locale })) + .setStyle(ButtonStyle.Link) + .setURL("https://github.com/GamesTwoLife/DiscordBot-Template"), + ]; + + await interaction.reply({ + content: t('commands:sample.sample.buttonwrapper.content', { lng: interaction.locale }), + components: buttonWrapper(buttons) + }); + } break; + + case "create-poll": { + await interaction.reply({ + poll: { + question: { text: "Цей шаблон крутий?" }, + answers: [ + { text: "Так, безумовно" }, + { text: "Ні, беззаперечно крутий" }, + { text: "Можливо" }, + ], + allowMultiselect: false, + duration: 2, + layoutType: PollLayoutType.Default + } + }) + } break; + } + }, +}; diff --git a/commands/sample/user sample.js b/commands/sample/user sample.js index 894f9b9..3927027 100644 --- a/commands/sample/user sample.js +++ b/commands/sample/user sample.js @@ -1,4 +1,4 @@ -const { ContextMenuCommandBuilder, ApplicationCommandType } = require("discord.js"); +const { ContextMenuCommandBuilder, ApplicationCommandType, InteractionContextType } = require("discord.js"); const { t } = require("i18next"); /** @@ -12,12 +12,12 @@ module.exports = { ru: t('commands:sample.user_sample.description', { lng: "ru" }).slice(0, 32) }) .setType(ApplicationCommandType.User) - .setDMPermission(false), + .setContexts([InteractionContextType.Guild]), options: { cooldown: 10, ownerOnly: false, devGuildOnly: true, - bot_permissions: ["ViewChannel", "SendMessages"], + bot_permissions: [], }, async execute(interaction) { diff --git a/events/Interaction/butonInteraction.js b/events/Interaction/butonInteraction.js index abdbbe1..8f7a9ad 100644 --- a/events/Interaction/butonInteraction.js +++ b/events/Interaction/butonInteraction.js @@ -3,87 +3,79 @@ const { developers, guildId } = require("../../config.json"); const { t } = require("i18next"); module.exports = { - name: Events.InteractionCreate, - /** - * - * @param {import('discord.js').ButtonInteraction & { client: import('../../typings').MainClient }} interaction - */ - async execute(interaction) { - const { client, guild, user } = interaction; - const { cooldowns } = client; + name: Events.InteractionCreate, + /** + * + * @param {import('discord.js').ButtonInteraction & { client: import('../../typings').MainClient }} interaction + */ + async execute(interaction) { + const { client, guild, user } = interaction; + const { cooldowns } = client; - if (!interaction.isButton()) return; + if (!interaction.isButton()) return; - try { - const buttons = client.components.get(interaction.customId)?.filter(component => component.type === "button"); + try { + const buttons = client.components.get(interaction.customId)?.filter(component => component.type === "button"); - if (!buttons) return; + if (!buttons) return; - for (const button of buttons) { - if (button.options && button.options?.ownerOnly && !developers.includes(user.id)) { - return interaction.reply({ content: t('common:events.Interaction.only_developer', { lng: interaction.locale, member: user.toString() }), ephemeral: true }); - } - - if (button.options && button.options?.bot_permissions && !guild.members.me.permissions.has(button.options?.bot_permissions)) { - const permsBot = button.options?.bot_permissions?.map(x => x).join(', '); - - return interaction.reply({ content: t('common:events.Interaction.missing_permissions', { lng: interaction.locale, member: user.toString(), permissions: permsBot }), ephemeral: true }); - } - - if (!cooldowns.has(interaction.customId)) { - cooldowns.set(interaction.customId, new Collection()); - } - - const now = Date.now(); - const timestamps = cooldowns.get(interaction.customId); - const cooldownAmount = (button.options?.cooldown ?? 5) * 1000; - - if (timestamps.has(user.id)) { - const expirationTime = timestamps.get(user.id) + cooldownAmount; - - if (now < expirationTime) { - const expiredTimestamp = Math.round(expirationTime / 1000); - - if (interaction.deferred) { - return interaction.editReply({ - content: t('common:events.Interaction.cooldown_button', { lng: interaction.locale, member: user.toString(), buttonId: interaction.customId, expiredTimestamp }), - }); - } else if (interaction.replied) { - return interaction.followUp({ - content: t('common:events.Interaction.cooldown_button', { lng: interaction.locale, member: user.toString(), buttonId: interaction.customId, expiredTimestamp }), - ephemeral: true - }); - } else { - return interaction.reply({ - content: t('common:events.Interaction.cooldown_button', { lng: interaction.locale, member: user.toString(), buttonId: interaction.customId, expiredTimestamp }), - ephemeral: true - }); - } - } - } - - timestamps.set(user.id, now); - setTimeout(() => timestamps.delete(user.id), cooldownAmount); - - return button.execute(interaction); - } - } catch (error) { - console.log(error); - if (interaction.deferred) { - return interaction.editReply({ - content: t('common:events.Interaction.error_occured', { lng: interaction.locale, member: user.toString() }) - }); - } else if (interaction.replied) { - return interaction.followUp({ + for (const button of buttons) { + if (button.options && button.options?.ownerOnly && !developers.includes(user.id)) { + return interaction.reply({ content: t('common:events.Interaction.only_developer', { lng: interaction.locale, member: user.toString() }), ephemeral: true }); + } + + if (button.options && button.options?.bot_permissions && !guild.members.me.permissions.has(button.options?.bot_permissions)) { + const permsBot = button.options?.bot_permissions?.map(x => x).join(', '); + + return interaction.reply({ content: t('common:events.Interaction.missing_permissions', { lng: interaction.locale, member: user.toString(), permissions: permsBot }), ephemeral: true }); + } + + if (!cooldowns.has(interaction.customId)) { + cooldowns.set(interaction.customId, new Collection()); + } + + const now = Date.now(); + const timestamps = cooldowns.get(interaction.customId); + const cooldownAmount = (button.options?.cooldown ?? 5) * 1000; + + if (timestamps.has(user.id)) { + const expirationTime = timestamps.get(user.id) + cooldownAmount; + + if (now < expirationTime) { + const expiredTimestamp = Math.round(expirationTime / 1000); + + if (interaction.deferred || interaction.replied) { + return interaction.followUp({ + content: t('common:events.Interaction.cooldown_button', { lng: interaction.locale, member: user.toString(), buttonId: interaction.customId, expiredTimestamp }), + ephemeral: true + }); + } else { + return interaction.reply({ + content: t('common:events.Interaction.cooldown_button', { lng: interaction.locale, member: user.toString(), buttonId: interaction.customId, expiredTimestamp }), + ephemeral: true + }); + } + } + } + + timestamps.set(user.id, now); + setTimeout(() => timestamps.delete(user.id), cooldownAmount); + + return button.execute(interaction); + } + } catch (error) { + console.log(error); + if (interaction.deferred || interaction.replied) { + return interaction.followUp({ content: t('common:events.Interaction.error_occured', { lng: interaction.locale, member: user.toString() }), ephemeral: true }); } else { - return interaction.reply({ + return interaction.reply({ content: t('common:events.Interaction.error_occured', { lng: interaction.locale, member: user.toString() }), ephemeral: true }); - } - } - }, -}; \ No newline at end of file + } + } + }, +}; diff --git a/events/Interaction/chatInputInteractionCreate.js b/events/Interaction/chatInputInteractionCreate.js index b366b91..45ccc8f 100644 --- a/events/Interaction/chatInputInteractionCreate.js +++ b/events/Interaction/chatInputInteractionCreate.js @@ -3,15 +3,15 @@ const { developers } = require("../../config.json"); const { t } = require("i18next"); module.exports = { - name: Events.InteractionCreate, - /** - * - * @param {import('discord.js').CommandInteraction & { client: import('../../typings').MainClient }} interaction - */ - async execute(interaction) { - const { client, guild, user } = interaction; + name: Events.InteractionCreate, + /** + * + * @param {import('discord.js').CommandInteraction & { client: import('../../typings').MainClient }} interaction + */ + async execute(interaction) { + const { client, guild, user } = interaction; - if (!interaction.isChatInputCommand()) return; + if (!interaction.isChatInputCommand()) return; try { const command = client.commands.get(interaction.commandName); @@ -70,24 +70,20 @@ module.exports = { timestamps.set(user.id, now); setTimeout(() => timestamps.delete(user.id), cooldownAmount); - return command.execute(interaction); - } catch (error) { - console.log(error); - if (interaction.deferred) { - return interaction.editReply({ - content: t('common:events.Interaction.error_occured', { lng: interaction.locale, member: user.toString() }) - }); - } else if (interaction.replied) { - return interaction.followUp({ + return command.execute(interaction); + } catch (error) { + console.log(error); + if (interaction.deferred || interaction.replied) { + return interaction.editReply({ content: t('common:events.Interaction.error_occured', { lng: interaction.locale, member: user.toString() }), ephemeral: true }); } else { - return interaction.reply({ + return interaction.reply({ content: t('common:events.Interaction.error_occured', { lng: interaction.locale, member: user.toString() }), ephemeral: true }); - } - } - }, -}; \ No newline at end of file + } + } + }, +}; diff --git a/index.js b/index.js index bc14038..3bd1dd5 100644 --- a/index.js +++ b/index.js @@ -45,8 +45,7 @@ client.i18n = i18next.init({ }, resources: { en: resources.en, - uk: resources.uk, - ru: resources.ru + uk: resources.uk } }); @@ -215,9 +214,9 @@ process.on('warning', async (warning) => { (async () => { await Event(client); - await Command(client); + await Command(client); await Component(client); - await SlashUpdate(client); + await SlashUpdate(client); })() -client.login(token); \ No newline at end of file +client.login(token); diff --git a/locales/resources.js b/locales/resources.js index c769417..744c878 100644 --- a/locales/resources.js +++ b/locales/resources.js @@ -1,21 +1,15 @@ const common_en = require("./en/common.json"); const common_uk = require("./uk/common.json"); -const common_ru = require("./ru/common.json"); const commands_en = require("./en/commands.json"); const commands_uk = require("./uk/commands.json"); -const commands_ru = require("./ru/commands.json"); module.exports = { - en: { - common: common_en, - commands: commands_en - }, - uk: { - common: common_uk, - commands: commands_uk - }, - ru: { - common: common_ru, - commands: commands_ru - } -} \ No newline at end of file + en: { + common: common_en, + commands: commands_en + }, + uk: { + common: common_uk, + commands: commands_uk + } +} diff --git a/locales/ru/commands.json b/locales/ru/commands.json deleted file mode 100644 index 9178974..0000000 --- a/locales/ru/commands.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "info": { - "ping": { - "description": "Пинг и Задержка бота", - "content": "Discord Websocket Пинг **{{ping}}**мс\nОтвет на команди: **{{latency}}**мс" - } - }, - "sample": { - "message_sample": { - "description": "образец сообщения", - "content": "Это ответ на команду контекстного меню сообщения\nID сообщения: {{id}}\nКонтент сообщения: {{message}}" - }, - "sample": { - "description": "Образец автозаполнения/кнопки/меню/модальной отправки.", - "autocomplete": { - "description": "Образец автозаполнения.", - "options": { - "input": "Ввести" - } - }, - "button": { - "description": "Образец кнопки." - }, - "menu": { - "description": "Образец меню." - }, - "modal": { - "description": "Образец модальной отправки." - }, - "pagination": { - "description": "Образец пагинации.", - "pageStrings": [ - "Котики самые лучшие :)", - "Собачки самые лучшие :)", - "Украина превыше всего!", - "Слава Украине! Героям Слава!", - "Крутой шаблон бота" - ], - "footer": "Страница {{currentPage}}/{{totalPages}}" - }, - "buttonwrapper": { - "description": "Образец обертки для кнопок.", - "content": "Нажимайте на кнопки внизу:", - "say_hello": "Нажми на меня", - "cool_template": "Самый лучший шаблон Discord бота" - } - }, - "user_sample": { - "description": "образец пользователя", - "content": "Это ответ на команду контекстного меню пользователя\nID пользователя: {{id}}\nУчастник/Пользователь: {{member}} / {{user}}}" - } - } -} \ No newline at end of file diff --git a/locales/ru/common.json b/locales/ru/common.json deleted file mode 100644 index d8107bd..0000000 --- a/locales/ru/common.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "events": { - "Interaction": { - "no_guild": "{{member}}, Это взаимодействие может быть выполнено только на сервере", - "no_command": "{{member}}, Команда не существует или что-то странное произошло", - "only_developer": "{{member}}, Это взаимодействие предназначено только для разработчиков бота", - "missing_permissions": "{{member}}, Мне нужны следующие разрешения для выполнения этого взаимодействия: {{permissions}}", - "cooldown_command": "{{member}}, Достигнут лимит использования команды `{{commandName}}`, попробуйте еще раз ", - "cooldown_button": "{{member}}, Достигнут лимит использования кнопки `{{buttonId}}`, попробуйте еще раз ", - "cooldown_menu": "{{member}}, Достигнут лимит использования меню `{{menuId}}`, попробуйте еще раз ", - "cooldown_modal": "{{member}}, Достигнут лимит использования модального представления `{{modalId}}`, попробуйте еще раз ", - "error_occured": "{{member}}, Ой друг, похоже, произошла непредсказуемая ошибка, мы уже отследили ее и скоро исправим", - "something_strange": "{{member}}, Что-то странное происходит в контекстном меню. Получено контекстное меню неизвестного типа." - } - } -} \ No newline at end of file diff --git a/package.json b/package.json index 38146b1..eab1ffc 100644 --- a/package.json +++ b/package.json @@ -1,44 +1,44 @@ { - "name": "discordbot-template", - "version": "1.1.1", - "description": "Шаблон для створення власного бота на основі discord-js!", - "main": "index.js", - "scripts": { - "start": "node ." - }, - "repository": { - "type": "git", - "url": "git+https://github.com/GamesTwoLife/DiscordBot-Template.git" - }, - "keywords": [ - "mongodb", - "mongoose", - "discord", - "discordjs", - "discord-bot", - "discord-js", - "discord-bot-template", - "bot-template", - "discord-handler", - "discordjs-v14", - "discord-js-handler-v14", - "discord-v14" - ], - "author": { - "name": "GamesTwoLife", - "url": "https://github.com/GamesTwoLife" - }, - "contributors": [], - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/GamesTwoLife/DiscordBot-Template/issues" - }, - "homepage": "https://github.com/GamesTwoLife/DiscordBot-Template#readme", - "dependencies": { - "discord.js": "^14.15.2", - "fs": "^0.0.1-security", - "i18next": "^23.10.0", - "mongoose": "^7.0.2", - "ms": "^2.1.3" - } + "name": "discordbot-template", + "version": "1.2.0", + "description": "Шаблон для створення власного бота на основі discord-js!", + "main": "index.js", + "scripts": { + "start": "node ." + }, + "repository": { + "type": "git", + "url": "git+https://github.com/GamesTwoLife/DiscordBot-Template.git" + }, + "keywords": [ + "mongodb", + "mongoose", + "discord", + "discordjs", + "discord-bot", + "discord-js", + "discord-bot-template", + "bot-template", + "discord-handler", + "discordjs-v14", + "discord-js-handler-v14", + "discord-v14" + ], + "author": { + "name": "GamesTwoLife", + "url": "https://github.com/GamesTwoLife" + }, + "contributors": [], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/GamesTwoLife/DiscordBot-Template/issues" + }, + "homepage": "https://github.com/GamesTwoLife/DiscordBot-Template#readme", + "dependencies": { + "discord.js": "^14.16.1", + "fs": "^0.0.1-security", + "i18next": "^23.10.0", + "mongoose": "^7.0.2", + "ms": "^2.1.3" + } }