From 24e44f723517a0134bb8a8ece16bc2893df3cee2 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Sat, 11 Jun 2022 21:59:47 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20button=20cooldowns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/helpers/cooldown/index.ts | 61 +++++++-- src/helpers/deferReply/index.ts | 17 +-- src/jobs/shop/index.ts | 2 +- src/models/api.ts | 21 +-- .../commands/credits/modules/top/index.ts | 2 +- .../commands/credits/modules/work/index.ts | 2 +- .../modules/credits/modules/giveaway/index.ts | 6 +- .../commands/reputation/modules/give/index.ts | 2 +- .../commands/shop/modules/cpgg/index.ts | 5 +- .../interactionCreate/components/checks.ts | 51 -------- .../interactionCreate/components/isButton.ts | 123 ------------------ .../interactionCreate/components/isCommand.ts | 48 ------- .../handlers/button/index.ts | 36 +++++ .../handlers/command/index.ts | 34 +++++ .../interactionCreate/handlers/index.ts | 11 ++ src/plugins/events/interactionCreate/index.ts | 15 +-- 16 files changed, 173 insertions(+), 263 deletions(-) delete mode 100644 src/plugins/events/interactionCreate/components/checks.ts delete mode 100644 src/plugins/events/interactionCreate/components/isButton.ts delete mode 100644 src/plugins/events/interactionCreate/components/isCommand.ts create mode 100644 src/plugins/events/interactionCreate/handlers/button/index.ts create mode 100644 src/plugins/events/interactionCreate/handlers/command/index.ts create mode 100644 src/plugins/events/interactionCreate/handlers/index.ts diff --git a/src/helpers/cooldown/index.ts b/src/helpers/cooldown/index.ts index 2417d81..45d2729 100644 --- a/src/helpers/cooldown/index.ts +++ b/src/helpers/cooldown/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction, Message } from "discord.js"; +import { CommandInteraction, ButtonInteraction, Message } from "discord.js"; import logger from "../../logger"; @@ -7,7 +7,7 @@ import getEmbedConfig from "../../helpers/getEmbedConfig"; import timeoutSchema from "../../models/timeout"; import addSeconds from "../../helpers/addSeconds"; -export const interaction = async (i: CommandInteraction, cooldown: number) => { +export const command = async (i: CommandInteraction, cooldown: number) => { const { guild, user, commandId } = i; // Check if user has a timeout @@ -56,12 +56,57 @@ export const interaction = async (i: CommandInteraction, cooldown: number) => { }); }; -export const message = async ( - message: Message, - cooldown: number, - id: string -) => { - const { guild, member } = message; +export const button = async (i: ButtonInteraction, cooldown: number) => { + const { guild, user, customId } = i; + + // Check if user has a timeout + const hasTimeout = await timeoutSchema.findOne({ + guildId: guild?.id || "0", + userId: user.id, + cooldown: cooldown, + timeoutId: customId, + }); + + // If user is not on timeout + if (hasTimeout) { + const { guildId, userId, timeoutId, createdAt } = hasTimeout; + const overDue = (await addSeconds(cooldown, createdAt)) < new Date(); + + if (!overDue) { + const diff = Math.round( + (new Date(hasTimeout.createdAt).getTime() - new Date().getTime()) / 1000 + ); + + throw new Error( + `You must wait ${diff} seconds before using this command.` + ); + } + + // Delete timeout + await timeoutSchema + .deleteOne({ + guildId, + userId, + timeoutId, + cooldown, + }) + .then(async () => { + logger.debug( + `Timeout document ${timeoutId} has been deleted from user ${userId}.` + ); + }); + } + // Create timeout + await timeoutSchema.create({ + guildId: guild?.id || "0", + userId: user.id, + cooldown: cooldown, + timeoutId: customId, + }); +}; + +export const message = async (msg: Message, cooldown: number, id: string) => { + const { guild, member } = msg; if (!guild) throw new Error("Guild is undefined"); if (!member) throw new Error("Member is undefined"); diff --git a/src/helpers/deferReply/index.ts b/src/helpers/deferReply/index.ts index b5cfdc3..02cc413 100644 --- a/src/helpers/deferReply/index.ts +++ b/src/helpers/deferReply/index.ts @@ -1,25 +1,26 @@ -import { CommandInteraction, MessageEmbed } from "discord.js"; +import { Interaction, MessageEmbed } from "discord.js"; import getEmbedConfig from "../../helpers/getEmbedConfig"; -export default async (interaction: CommandInteraction, ephemeral: boolean) => { +export default async (interaction: Interaction, ephemeral: boolean) => { + if (!interaction.isRepliable()) + throw new Error(`Cannot reply to an interaction that is not repliable`); + await interaction.deferReply({ ephemeral, }); - const { waitColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); + const embedConfig = await getEmbedConfig(interaction.guild); await interaction.editReply({ embeds: [ new MessageEmbed() .setFooter({ - text: footerText, - iconURL: footerIcon, + text: embedConfig.footerText, + iconURL: embedConfig.footerIcon, }) .setTimestamp(new Date()) .setTitle("Processing your request") - .setColor(waitColor) + .setColor(embedConfig.waitColor) .setDescription("Please wait..."), ], }); diff --git a/src/jobs/shop/index.ts b/src/jobs/shop/index.ts index d2f76d1..b2ef366 100644 --- a/src/jobs/shop/index.ts +++ b/src/jobs/shop/index.ts @@ -4,7 +4,7 @@ import { Client } from "discord.js"; import * as roles from "./modules/roles"; export const options = { - schedule: "*/5 * * * * *", // https://crontab.guru/ + schedule: "*/5 * * * *", // https://crontab.guru/ }; export const execute = async (client: Client) => { diff --git a/src/models/api.ts b/src/models/api.ts index d3690df..fd5cbd7 100644 --- a/src/models/api.ts +++ b/src/models/api.ts @@ -4,7 +4,7 @@ import { IEncryptionData } from "../interfaces/EncryptionData"; export interface IApi { guildId: Snowflake; - url: string; + url: IEncryptionData; token: IEncryptionData; } @@ -17,11 +17,18 @@ const apiSchema = new Schema( index: true, }, url: { - type: String, - required: true, - unique: false, - index: true, - default: "https://localhost/api/", + iv: { + type: String, + required: true, + unique: false, + index: true, + }, + content: { + type: String, + required: true, + unique: false, + index: true, + }, }, token: { iv: { @@ -29,14 +36,12 @@ const apiSchema = new Schema( required: true, unique: false, index: true, - default: "token", }, content: { type: String, required: true, unique: false, index: true, - default: "token", }, }, }, diff --git a/src/plugins/commands/credits/modules/top/index.ts b/src/plugins/commands/credits/modules/top/index.ts index 29aadda..54478aa 100644 --- a/src/plugins/commands/credits/modules/top/index.ts +++ b/src/plugins/commands/credits/modules/top/index.ts @@ -54,7 +54,7 @@ export default { embeds: [ embed .setDescription( - `Below are the top 10 users in this guild. + `Below are the top ten members in this guild. ${topTen.map(entry).join("\n")} ` diff --git a/src/plugins/commands/credits/modules/work/index.ts b/src/plugins/commands/credits/modules/work/index.ts index a84a696..ef0a550 100644 --- a/src/plugins/commands/credits/modules/work/index.ts +++ b/src/plugins/commands/credits/modules/work/index.ts @@ -45,7 +45,7 @@ export default { const guildDB = await fetchGuild(guild); - await cooldown.interaction(interaction, guildDB?.credits?.workTimeout); + await cooldown.command(interaction, guildDB?.credits?.workTimeout); const creditsEarned = chance.integer({ min: 0, diff --git a/src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts b/src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts index 9ee2f17..acb73cb 100644 --- a/src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts @@ -75,14 +75,16 @@ export default { if (!apiCredentials) return; + const url = encryption.decrypt(apiCredentials?.url); + const api = axios?.create({ - baseURL: `${apiCredentials?.url}/api/`, + baseURL: `${url}/api/`, headers: { Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`, }, }); - const shopUrl = `${apiCredentials?.url}/store`; + const shopUrl = `${url}/store`; await api .post("vouchers", { diff --git a/src/plugins/commands/reputation/modules/give/index.ts b/src/plugins/commands/reputation/modules/give/index.ts index c899ecb..eb58abe 100644 --- a/src/plugins/commands/reputation/modules/give/index.ts +++ b/src/plugins/commands/reputation/modules/give/index.ts @@ -52,7 +52,7 @@ export default { await noSelfReputation(optionTarget, user); // Check if user is on cooldown otherwise create one - await cooldown.interaction(interaction, timeout); + await cooldown.command(interaction, timeout); switch (optionType) { case "positive": diff --git a/src/plugins/commands/shop/modules/cpgg/index.ts b/src/plugins/commands/shop/modules/cpgg/index.ts index 82ee508..90dbe70 100644 --- a/src/plugins/commands/shop/modules/cpgg/index.ts +++ b/src/plugins/commands/shop/modules/cpgg/index.ts @@ -153,15 +153,16 @@ export default { }); if (!apiCredentials) return; + const url = encryption.decrypt(apiCredentials?.url); const api = axios?.create({ - baseURL: `${apiCredentials.url}/api/`, + baseURL: `${url}/api/`, headers: { Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`, }, }); - const shopUrl = `${apiCredentials?.url}/store`; + const shopUrl = `${url}/store`; const buttons = new MessageActionRow().addComponents( new MessageButton() diff --git a/src/plugins/events/interactionCreate/components/checks.ts b/src/plugins/events/interactionCreate/components/checks.ts deleted file mode 100644 index 006a14f..0000000 --- a/src/plugins/events/interactionCreate/components/checks.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CommandInteraction, MessageEmbed } from "discord.js"; -import * as cooldown from "../../../../helpers/cooldown"; -import logger from "../../../../logger"; - -export default async ( - interaction: CommandInteraction, - metadata: any, - embedConfig: any -) => { - if ( - metadata.permissions && - metadata.guildOnly && - !interaction.memberPermissions?.has(metadata.permissions) - ) { - return interaction?.editReply({ - embeds: [ - new MessageEmbed() - .setTitle("[:x:] Permission") - .setDescription(`You do not have the permission to manage the bot.`) - .setTimestamp(new Date()) - .setColor(embedConfig.errorColor) - .setFooter({ - text: embedConfig.footerText, - iconURL: embedConfig.footerIcon, - }), - ], - }); - } - - logger.info(metadata); - - if (metadata.cooldown) { - await cooldown - .interaction(interaction, metadata.cooldown) - .catch(async (error) => { - throw new Error("Cooldown error: " + error); - }); - } - - if (metadata.guildOnly) { - if (!interaction.guild) { - throw new Error("This command is guild only."); - } - } - - if (metadata.dmOnly) { - if (interaction.guild) { - throw new Error("This command is DM only."); - } - } -}; diff --git a/src/plugins/events/interactionCreate/components/isButton.ts b/src/plugins/events/interactionCreate/components/isButton.ts deleted file mode 100644 index eaf1605..0000000 --- a/src/plugins/events/interactionCreate/components/isButton.ts +++ /dev/null @@ -1,123 +0,0 @@ -// Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; - -import logger from "../../../../logger"; - -import deferReply from "../../../../helpers/deferReply"; -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; -import capitalizeFirstLetter from "../../../../helpers/capitalizeFirstLetter"; -import * as cooldown from "../../../../helpers/cooldown"; - -export default async (interaction: CommandInteraction) => { - if (!interaction.isButton()) return; - - const { errorColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); - - const { guild, customId, user, memberPermissions } = interaction; - - const currentButton = await import(`../../../buttons/${customId}`); - - if (currentButton == null) { - logger.silly(`Button ${customId} not found`); - } - - const metadata = currentButton.metadata; - - await deferReply(interaction, metadata.ephemeral || false); - - if (metadata.guildOnly) { - if (!guild) { - logger.debug(`Guild is null`); - - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setTitle("[:x:] Permission") - .setDescription("This command is only available for guild") - .setColor(errorColor) - .setTimestamp(new Date()) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - } - } - - if ( - metadata.permissions && - metadata.guildOnly && - !memberPermissions?.has(metadata.permissions) - ) { - return interaction?.editReply({ - embeds: [ - new MessageEmbed() - .setTitle("[:x:] Permission") - .setDescription(`You do not have the permission to manage the bot.`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - } - - if (metadata.dmOnly) { - if (guild) { - logger.silly(`Guild exist`); - - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setTitle("[:x:] Permission") - .setDescription("This command is only available in DM.") - .setColor(errorColor) - .setTimestamp(new Date()) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - } - } - - if (metadata.cooldown) { - await cooldown - .interaction(interaction, metadata.cooldown) - .catch(async (error) => { - return interaction?.editReply({ - embeds: [ - new MessageEmbed() - .setTitle("[:x:] Permission") - .setDescription(`${error}`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - }); - } - - await currentButton - .execute(interaction) - .then(async () => { - return logger?.silly( - `Button: ${customId} executed in guild: ${guild?.name} (${guild?.id}) by user: ${user?.tag} (${user?.id})` - ); - }) - .catch(async (error: string) => { - logger?.debug(`INTERACTION BUTTON CATCH: ${error}`); - - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setTitle( - `[:x:] ${capitalizeFirstLetter( - interaction.options.getSubcommand() - )}` - ) - .setDescription(`${"``"}${error}${"``"}`) - .setColor(errorColor) - .setTimestamp(new Date()) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - }); -}; diff --git a/src/plugins/events/interactionCreate/components/isCommand.ts b/src/plugins/events/interactionCreate/components/isCommand.ts deleted file mode 100644 index 3a14ad6..0000000 --- a/src/plugins/events/interactionCreate/components/isCommand.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; - -import logger from "../../../../logger"; - -import deferReply from "../../../../helpers/deferReply"; -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; -import getCommandMetadata from "../../../../helpers/getCommandMetadata"; -import capitalizeFirstLetter from "../../../../helpers/capitalizeFirstLetter"; -import * as cooldown from "../../../../helpers/cooldown"; - -export default async (interaction: CommandInteraction) => { - if (!interaction.isCommand()) return; - - const { errorColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); - - const { client, guild, commandName, user, memberPermissions } = interaction; - - const currentCommand = client.commands.get(commandName); - - if (currentCommand == null) { - logger.silly(`Command ${commandName} not found`); - } - - const metadata = await getCommandMetadata(interaction, currentCommand); - - await deferReply(interaction, metadata.ephemeral || false); - - if (metadata.guildOnly && !guild) - throw new Error("This command is guild only."); - - if ( - metadata.permissions && - metadata.guildOnly && - !memberPermissions?.has(metadata.permissions) - ) - throw new Error("You don't have the required permissions"); - - if (metadata.dmOnly && guild) - throw new Error("This command is only available in DM"); - - if (metadata.cooldown) - await cooldown.interaction(interaction, metadata.cooldown); - - await currentCommand.execute(interaction); -}; diff --git a/src/plugins/events/interactionCreate/handlers/button/index.ts b/src/plugins/events/interactionCreate/handlers/button/index.ts new file mode 100644 index 0000000..3373f09 --- /dev/null +++ b/src/plugins/events/interactionCreate/handlers/button/index.ts @@ -0,0 +1,36 @@ +// Dependencies +import { Interaction } from "discord.js"; + +import deferReply from "../../../../../helpers/deferReply"; +import * as cooldown from "../../../../../helpers/cooldown"; + +export default async (interaction: Interaction) => { + if (!interaction.isButton()) return; + + const { guild, customId, memberPermissions } = interaction; + + const currentButton = await import(`../../../buttons/${customId}`); + + if (!currentButton) throw new Error(`Unknown button ${customId}`); + + const metadata = currentButton.metadata; + + await deferReply(interaction, metadata.ephemeral || false); + + if (metadata.guildOnly && !guild) + throw new Error("This command is guild only."); + + if ( + metadata.permissions && + metadata.guildOnly && + !memberPermissions?.has(metadata.permissions) + ) + throw new Error("You don't have the required permissions"); + + if (metadata.dmOnly && guild) + throw new Error("This command is only available in DM"); + + if (metadata.cooldown) await cooldown.button(interaction, metadata.cooldown); + + await currentButton.execute(interaction); +}; diff --git a/src/plugins/events/interactionCreate/handlers/command/index.ts b/src/plugins/events/interactionCreate/handlers/command/index.ts new file mode 100644 index 0000000..b6b30ec --- /dev/null +++ b/src/plugins/events/interactionCreate/handlers/command/index.ts @@ -0,0 +1,34 @@ +// Dependencies +import { Interaction } from "discord.js"; + +import deferReply from "../../../../../helpers/deferReply"; +import getCommandMetadata from "../../../../../helpers/getCommandMetadata"; +import * as cooldown from "../../../../../helpers/cooldown"; + +export default async (interaction: Interaction) => { + if (!interaction.isCommand()) return; + const { client, commandName } = interaction; + + const currentCommand = client.commands.get(commandName); + if (!currentCommand) throw new Error(`Unknown command ${commandName}`); + + const metadata = await getCommandMetadata(interaction, currentCommand); + await deferReply(interaction, metadata.ephemeral || false); + + if (metadata.guildOnly && !interaction.guild) + throw new Error("This command is guild only."); + + if ( + metadata.permissions && + metadata.guildOnly && + !interaction.memberPermissions?.has(metadata.permissions) + ) + throw new Error("You don't have the required permissions"); + + if (metadata.dmOnly && interaction.guild) + throw new Error("This command is only available in DM"); + + if (metadata.cooldown) await cooldown.command(interaction, metadata.cooldown); + + await currentCommand.execute(interaction); +}; diff --git a/src/plugins/events/interactionCreate/handlers/index.ts b/src/plugins/events/interactionCreate/handlers/index.ts new file mode 100644 index 0000000..c29bfc3 --- /dev/null +++ b/src/plugins/events/interactionCreate/handlers/index.ts @@ -0,0 +1,11 @@ +import { Interaction } from "discord.js"; + +import button from "./button"; +import command from "./command"; + +import logger from "../../../../logger"; + +export const execute = async (interaction: Interaction) => { + await button(interaction); + await command(interaction); +}; diff --git a/src/plugins/events/interactionCreate/index.ts b/src/plugins/events/interactionCreate/index.ts index f7365ec..aa6e763 100644 --- a/src/plugins/events/interactionCreate/index.ts +++ b/src/plugins/events/interactionCreate/index.ts @@ -2,8 +2,8 @@ import { CommandInteraction, MessageEmbed } from "discord.js"; // Dependencies -import isCommand from "../../events/interactionCreate/components/isCommand"; -import isButton from "../../events/interactionCreate/components/isButton"; +import * as handlers from "./handlers"; + import logger from "../../../logger"; import audits from "./audits"; import { IEventOptions } from "../../../interfaces/EventOptions"; @@ -27,11 +27,8 @@ export const execute = async (interaction: CommandInteraction) => { await audits.execute(interaction); - try { - await isCommand(interaction); - await isButton(interaction); - } catch (error) { - logger.debug(`${error}`); + await handlers.execute(interaction).catch(async (err) => { + logger.debug(`${err}`); return interaction.editReply({ embeds: [ @@ -41,11 +38,11 @@ export const execute = async (interaction: CommandInteraction) => { interaction.options.getSubcommand() )}` ) - .setDescription(`${"``"}${error}${"``"}`) + .setDescription(`${"``"}${err}${"``"}`) .setColor(errorColor) .setTimestamp(new Date()) .setFooter({ text: footerText, iconURL: footerIcon }), ], }); - } + }); };