diff --git a/src/commands/config/index.ts b/src/commands/config/index.ts deleted file mode 100644 index 4110d5c..0000000 --- a/src/commands/config/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -// Dependencies -import { SlashCommandBuilder } from "@discordjs/builders"; -import { CommandInteraction } from "discord.js"; - -// Modules -import modules from "./modules"; - -// Handlers -import logger from "../../logger"; - -export const builder = new SlashCommandBuilder() - .setName("config") - .setDescription("Manage guild configurations.") - - .addSubcommand(modules.pterodactyl.builder) - .addSubcommand(modules.credits.builder) - .addSubcommand(modules.points.builder) - .addSubcommand(modules.welcome.builder) - .addSubcommand(modules.audits.builder) - .addSubcommand(modules.shop.builder) - .addSubcommand(modules.embeds.builder); - -export const moduleData = modules; - -// Function -export const execute = async (interaction: CommandInteraction) => { - switch (interaction.options?.getSubcommand()) { - case "pterodactyl": - return modules.pterodactyl.execute(interaction); - case "credits": - return modules.credits.execute(interaction); - case "points": - return modules.points.execute(interaction); - case "welcome": - return modules.welcome.execute(interaction); - case "audits": - return modules.audits.execute(interaction); - case "shop": - return modules.shop.execute(interaction); - case "embeds": - return modules.embeds.execute(interaction); - } -}; diff --git a/src/commands/config/modules/audits/index.ts b/src/commands/config/modules/audits/index.ts deleted file mode 100644 index 6d85a15..0000000 --- a/src/commands/config/modules/audits/index.ts +++ /dev/null @@ -1,95 +0,0 @@ -// Dependencies -import { CommandInteraction, Permissions } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; - -// Handlers -import logger from "../../../../logger"; - -// Models -import guildSchema from "../../../../models/guild"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import { ChannelType } from "discord-api-types/v10"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("audits") - .setDescription("Audits") - .addBooleanOption((option) => - option.setName("status").setDescription("Should audits be enabled?") - ) - .addChannelOption((option) => - option - .setName("channel") - .setDescription("Channel for audit messages.") - .addChannelTypes(ChannelType.GuildText) - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); - - const { guild, options } = interaction; - - // Get options - const status = options?.getBoolean("status"); - const channel = options?.getChannel("channel"); - - // Get guild object - const guildDB = await guildSchema?.findOne({ - guildId: guild?.id, - }); - - if (guildDB === null) { - return logger?.silly(`Guild not found in database.`); - } - - // Modify values - guildDB.audits.status = status !== null ? status : guildDB?.audits?.status; - guildDB.audits.channelId = - channel !== null ? channel.id : guildDB?.audits?.channelId; - - // Save guild - await guildDB?.save()?.then(async () => { - logger?.silly(`Guild audits updated.`); - - return interaction?.editReply({ - embeds: [ - { - title: ":hammer: Settings - Guild [Audits]", - description: `Audits settings updated.`, - color: successColor, - fields: [ - { - name: "🤖 Status", - value: `${guildDB?.audits?.status}`, - inline: true, - }, - { - name: "🌊 Channel", - value: `${guildDB?.audits?.channelId}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - }, -}; diff --git a/src/commands/config/modules/credits/index.ts b/src/commands/config/modules/credits/index.ts deleted file mode 100644 index 20df392..0000000 --- a/src/commands/config/modules/credits/index.ts +++ /dev/null @@ -1,144 +0,0 @@ -// Dependencies -import { CommandInteraction, Permissions } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; - -//Handlers -import logger from "../../../../logger"; - -// Models -import guildSchema from "../../../../models/guild"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("credits") - .setDescription(`Credits`) - .addBooleanOption((option) => - option.setName("status").setDescription("Should credits be enabled?") - ) - .addNumberOption((option) => - option.setName("rate").setDescription("Amount of credits per message.") - ) - .addNumberOption((option) => - option - .setName("minimum-length") - .setDescription("Minimum length of message to earn credits.") - ) - .addNumberOption((option) => - option - .setName("work-rate") - .setDescription("Maximum amount of credits on work.") - ) - .addNumberOption((option) => - option - .setName("work-timeout") - .setDescription("Timeout between work schedules (seconds).") - ) - .addNumberOption((option) => - option - .setName("timeout") - .setDescription("Timeout between earning credits (seconds).") - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); // Destructure member - const { guild, options } = interaction; - - if (guild == null) return; - - // Get options - const status = options?.getBoolean("status"); - const rate = options?.getNumber("rate"); - const timeout = options?.getNumber("timeout"); - const minimumLength = options?.getNumber("minimum-length"); - const workRate = options?.getNumber("work-rate"); - const workTimeout = options?.getNumber("work-timeout"); - - // Get guild object - const guildDB = await guildSchema?.findOne({ - guildId: guild?.id, - }); - - if (guildDB === null) { - return logger?.silly(`Guild is null`); - } - - // Modify values - guildDB.credits.status = - status !== null ? status : guildDB?.credits?.status; - guildDB.credits.rate = rate !== null ? rate : guildDB?.credits?.rate; - guildDB.credits.timeout = - timeout !== null ? timeout : guildDB?.credits?.timeout; - guildDB.credits.workRate = - workRate !== null ? workRate : guildDB?.credits?.workRate; - guildDB.credits.workTimeout = - workTimeout !== null ? workTimeout : guildDB?.credits?.workTimeout; - guildDB.credits.minimumLength = - minimumLength !== null ? minimumLength : guildDB?.credits?.minimumLength; - - // Save guild - await guildDB?.save()?.then(async () => { - logger?.silly(`Guild saved`); - - return interaction?.editReply({ - embeds: [ - { - title: ":tools: Settings - Guild [Credits]", - description: `Credits settings updated.`, - color: successColor, - fields: [ - { - name: "🤖 Status", - value: `${guildDB?.credits?.status}`, - inline: true, - }, - { - name: "📈 Rate", - value: `${guildDB?.credits?.rate}`, - inline: true, - }, - { - name: "📈 Work Rate", - value: `${guildDB?.credits?.workRate}`, - inline: true, - }, - { - name: "🔨 Minimum Length", - value: `${guildDB?.credits?.minimumLength}`, - inline: true, - }, - { - name: "⏰ Timeout", - value: `${guildDB?.credits?.timeout}`, - inline: true, - }, - { - name: "⏰ Work Timeout", - value: `${guildDB?.credits?.workTimeout}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - }, -}; diff --git a/src/commands/config/modules/embeds/components/getValues/index.ts b/src/commands/config/modules/embeds/components/getValues/index.ts deleted file mode 100644 index 38702e2..0000000 --- a/src/commands/config/modules/embeds/components/getValues/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ColorResolvable, CommandInteraction } from "discord.js"; -import guildSchema from "../../../../../../models/guild"; -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; - -export default async (interaction: CommandInteraction) => { - const { options, guild } = interaction; - - if (!guild) throw new Error("Guild not found"); - - const embedConfig = await getEmbedConfig(guild); - if (!embedConfig) throw new Error("Embed config not found"); - - // Get new values - const newSuccessColor = options.getString("success-color") as ColorResolvable; - const newWaitColor = options.getString("wait-color") as ColorResolvable; - const newErrorColor = options.getString("error-color") as ColorResolvable; - const newFooterIcon = options.getString("footer-icon"); - const newFooterText = options.getString("footer-text"); - - // Get guild values - const guildData = await guildSchema.findOne({ - guildId: guild.id, - }); - if (!guildData) throw new Error("Guild data not found"); - if (!guildData?.embeds) - throw new Error("Guild embed configuration not found"); - let { successColor, waitColor, errorColor, footerText, footerIcon } = - guildData.embeds; - - // Set new values - successColor = newSuccessColor || successColor; - waitColor = newWaitColor || waitColor; - errorColor = newErrorColor || errorColor; - footerIcon = newFooterIcon || footerIcon; - footerText = newFooterText || footerText; - - return { successColor, waitColor, errorColor, footerText, footerIcon }; -}; diff --git a/src/commands/config/modules/embeds/index.ts b/src/commands/config/modules/embeds/index.ts deleted file mode 100644 index 218bb26..0000000 --- a/src/commands/config/modules/embeds/index.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Dependencies -import { - ColorResolvable, - CommandInteraction, - MessageEmbed, - Permissions, -} from "discord.js"; - -//Handlers -import logger from "../../../../logger"; - -// Models -import guildSchema from "../../../../models/guild"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; -import getValues from "./components/getValues"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("embeds") - .setDescription(`Embeds`) - .addStringOption((option) => - option - .setName("success-color") - .setDescription("No provided description") - ) - .addStringOption((option) => - option.setName("wait-color").setDescription("No provided description") - ) - .addStringOption((option) => - option.setName("error-color").setDescription("No provided description") - ) - .addStringOption((option) => - option.setName("footer-icon").setDescription("No provided description") - ) - .addStringOption((option) => - option.setName("footer-text").setDescription("No provided description") - ); - }, - execute: async (interaction: CommandInteraction) => { - const { guild } = interaction; - if (!guild) throw new Error("Guild not found"); - - const { successColor, waitColor, errorColor, footerText, footerIcon } = - await getValues(interaction); - - // Initialize embed object - const embed = new MessageEmbed() - .setTitle("[:tools:] Embeds") - .setFooter({ text: footerText, iconURL: footerIcon }) - .setTimestamp(new Date()); - - // Get guild values - const guildData = await guildSchema.findOne({ - guildId: guild.id, - }); - if (!guildData) throw new Error("Guild data not found"); - - await guildData.save().then(async () => { - embed - .setDescription("Following embed configuration will be used.") - .setColor(successColor) - .addFields([ - { - name: "🟢 Success Color", - value: `${successColor}`, - inline: true, - }, - { - name: "🟡 Wait Color", - value: `${waitColor}`, - inline: true, - }, - { - name: "🔴 Error Color", - value: `${errorColor}`, - inline: true, - }, - { - name: "🖼️ Footer Icon", - value: `${footerIcon}`, - inline: true, - }, - { - name: "📄 Footer Text", - value: `${footerText}`, - inline: true, - }, - ]); - - return interaction.editReply({ - embeds: [embed], - }); - }); - }, -}; diff --git a/src/commands/config/modules/index.ts b/src/commands/config/modules/index.ts deleted file mode 100644 index 09a9f5a..0000000 --- a/src/commands/config/modules/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import audits from "./audits"; -import credits from "./credits"; -import points from "./points"; -import pterodactyl from "./pterodactyl"; -import shop from "./shop"; -import welcome from "./welcome"; -import embeds from "./embeds"; - -export default { audits, credits, points, pterodactyl, shop, welcome, embeds }; diff --git a/src/commands/config/modules/points/index.ts b/src/commands/config/modules/points/index.ts deleted file mode 100644 index 6683df7..0000000 --- a/src/commands/config/modules/points/index.ts +++ /dev/null @@ -1,117 +0,0 @@ -// Dependencies -import { CommandInteraction, Permissions } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; - -// Handlers -import logger from "../../../../logger"; - -// Models -import guildSchema from "../../../../models/guild"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("points") - .setDescription("Points") - .addBooleanOption((option) => - option.setName("status").setDescription("Should credits be enabled?") - ) - .addNumberOption((option) => - option.setName("rate").setDescription("Amount of credits per message.") - ) - .addNumberOption((option) => - option - .setName("minimum-length") - .setDescription("Minimum length of message to earn credits.") - ) - .addNumberOption((option) => - option - .setName("timeout") - .setDescription("Timeout between earning credits (milliseconds).") - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); - - // Destructure member - const { options, guild } = interaction; - - // Get options - const status = options?.getBoolean("status"); - const rate = options?.getNumber("rate"); - const timeout = options?.getNumber("timeout"); - const minimumLength = options?.getNumber("minimum-length"); - - // Get guild object - const guildDB = await guildSchema?.findOne({ - guildId: guild?.id, - }); - - if (guildDB === null) { - return logger?.silly(`Guild not found in database.`); - } - - // Modify values - guildDB.points.status = status !== null ? status : guildDB?.points?.status; - guildDB.points.rate = rate !== null ? rate : guildDB?.points?.rate; - guildDB.points.timeout = - timeout !== null ? timeout : guildDB?.points?.timeout; - guildDB.points.minimumLength = - minimumLength !== null ? minimumLength : guildDB?.points?.minimumLength; - - // Save guild - await guildDB?.save()?.then(async () => { - logger?.silly(`Guild points updated.`); - - return interaction?.editReply({ - embeds: [ - { - title: ":hammer: Settings - Guild [Points]", - description: `Points settings updated.`, - color: successColor, - fields: [ - { - name: "🤖 Status", - value: `${guildDB?.points?.status}`, - inline: true, - }, - { - name: "📈 Rate", - value: `${guildDB?.points?.rate}`, - inline: true, - }, - { - name: "🔨 Minimum Length", - value: `${guildDB?.points?.minimumLength}`, - inline: true, - }, - { - name: "⏰ Timeout", - value: `${guildDB?.points?.timeout}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - }, -}; diff --git a/src/commands/config/modules/pterodactyl/index.ts b/src/commands/config/modules/pterodactyl/index.ts deleted file mode 100644 index c73189a..0000000 --- a/src/commands/config/modules/pterodactyl/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Dependencies -import { CommandInteraction, Permissions } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; - -// Handlers -import logger from "../../../../logger"; - -// Models -import apiSchema from "../../../../models/api"; -import encryption from "../../../../handlers/encryption"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("pterodactyl") - .setDescription("Controlpanel.gg") - .addStringOption((option) => - option - .setName("url") - .setDescription(`Controlpanel.gg URL`) - .setRequired(true) - ) - .addStringOption((option) => - option - .setName("token") - .setDescription(`Controlpanel.gg Token`) - .setRequired(true) - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); // Destructure member - const { options, guild } = interaction; - - // Get options - const tokenData = options.getString("token"); - const url = options.getString("url"); - const token = tokenData && encryption.encrypt(tokenData); - - // Update API credentials - await apiSchema - ?.findOneAndUpdate( - { guildId: guild?.id }, - { url, token }, - { new: true, upsert: true } - ) - .then(async () => { - logger?.silly(`Updated API credentials.`); - - return interaction?.editReply({ - embeds: [ - { - title: ":hammer: Settings - Guild [Pterodactyl]", - color: successColor, - description: `Successfully updated API credentials.`, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - }, -}; diff --git a/src/commands/config/modules/shop/index.ts b/src/commands/config/modules/shop/index.ts deleted file mode 100644 index fc4f467..0000000 --- a/src/commands/config/modules/shop/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Dependencies -import { CommandInteraction, Permissions } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; - -// Handlers -import logger from "../../../../logger"; - -// Models -import guildSchema from "../../../../models/guild"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("shop") - .setDescription("Shop") - .addBooleanOption((option) => - option - .setName("roles-status") - .setDescription("Should roles be enabled?") - ) - .addNumberOption((option) => - option - .setName("roles-price-per-hour") - .setDescription("Price per hour for roles.") - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); // Destructure member - const { options, guild } = interaction; - - // Get options - const rolesStatus = options?.getBoolean("roles-status"); - const rolesPricePerHour = options?.getNumber("roles-price-per-hour"); - - // Get guild object - const guildDB = await guildSchema?.findOne({ - guildId: guild?.id, - }); - - if (guildDB === null) { - return logger?.silly(`Guild not found in database.`); - } - - // Modify values - guildDB.shop.roles.status = - rolesStatus !== null ? rolesStatus : guildDB?.shop?.roles?.status; - guildDB.shop.roles.pricePerHour = - rolesPricePerHour !== null - ? rolesPricePerHour - : guildDB?.shop?.roles?.pricePerHour; - - // Save guild - await guildDB?.save()?.then(async () => { - logger?.silly(`Guild shop updated.`); - - return interaction?.editReply({ - embeds: [ - { - title: ":hammer: Settings - Guild [Shop]", - description: `Shop settings updated.`, - color: successColor, - fields: [ - { - name: "🤖 Roles Status", - value: `${guildDB?.shop?.roles.status}`, - inline: true, - }, - { - name: "🌊 Roles Price Per Hour", - value: `${guildDB?.shop?.roles.pricePerHour}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - }, -}; diff --git a/src/commands/config/modules/welcome/index.ts b/src/commands/config/modules/welcome/index.ts deleted file mode 100644 index 8405aeb..0000000 --- a/src/commands/config/modules/welcome/index.ts +++ /dev/null @@ -1,142 +0,0 @@ -// Dependencies -import { CommandInteraction, Permissions } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; - -// Handlers -import logger from "../../../../logger"; - -// Models -import guildSchema from "../../../../models/guild"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import { ChannelType } from "discord-api-types/v10"; - -// Function -export default { - metadata: { - guildOnly: true, - ephemeral: true, - permissions: [Permissions.FLAGS.MANAGE_GUILD], - }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("welcome") - .setDescription("Welcome") - .addBooleanOption((option) => - option.setName("status").setDescription("Should welcome be enabled?") - ) - .addChannelOption((option) => - option - .setName("join-channel") - .setDescription("Channel for join messages.") - .addChannelTypes(ChannelType.GuildText) - ) - - .addChannelOption((option) => - option - .setName("leave-channel") - .setDescription("Channel for leave messages.") - .addChannelTypes(ChannelType.GuildText) - ) - - .addStringOption((option) => - option - .setName("leave-message") - .setDescription("Message for leave messages.") - ) - .addStringOption((option) => - option - .setName("join-message") - .setDescription("Message for join messages.") - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); // Destructure member - const { options, guild } = interaction; - - // Get options - const status = options?.getBoolean("status"); - const joinChannel = options?.getChannel("join-channel"); - const leaveChannel = options?.getChannel("leave-channel"); - const joinChannelMessage = options?.getString("join-message"); - const leaveChannelMessage = options?.getString("leave-message"); - - // Get guild object - const guildDB = await guildSchema?.findOne({ - guildId: guild?.id, - }); - - if (guildDB === null) { - return logger?.silly(`Guild not found in database.`); - } - - // Modify values - guildDB.welcome.status = - status !== null ? status : guildDB?.welcome?.status; - guildDB.welcome.joinChannel = - joinChannel !== null ? joinChannel.id : guildDB?.welcome?.joinChannel; - guildDB.welcome.leaveChannel = - leaveChannel !== null ? leaveChannel.id : guildDB?.welcome?.leaveChannel; - - guildDB.welcome.joinChannelMessage = - joinChannelMessage !== null - ? joinChannelMessage - : guildDB?.welcome?.joinChannelMessage; - guildDB.welcome.leaveChannelMessage = - leaveChannelMessage !== null - ? leaveChannelMessage - : guildDB?.welcome?.leaveChannelMessage; - - // Save guild - await guildDB?.save()?.then(async () => { - logger?.silly(`Guild welcome updated.`); - - if (!guildDB?.welcome?.status) { - return interaction?.editReply({ - embeds: [ - { - title: "[:tools:] Welcome", - description: `This module is currently disabled, please enable it to continue.`, - color: successColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - } - - return interaction?.editReply({ - embeds: [ - { - title: "[:tools:] Welcome", - description: `The following configuration will be used. - - [👋] **Welcome** - - ㅤ**Channel**: <#${guildDB?.welcome?.joinChannel}> - ㅤ**Message**: ${guildDB?.welcome?.joinChannelMessage} - - [🚪] **Leave** - - ㅤ**Channel**: <#${guildDB?.welcome?.leaveChannel}> - ㅤ**Message**: ${guildDB?.welcome?.leaveChannelMessage}`, - color: successColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - }, -}; diff --git a/src/commands/reputation/modules/give.ts b/src/commands/reputation/modules/give.ts deleted file mode 100644 index 8e7d980..0000000 --- a/src/commands/reputation/modules/give.ts +++ /dev/null @@ -1,165 +0,0 @@ -// Dependencies -import { CommandInteraction } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../helpers/getEmbedConfig"; - -import { timeout } from "../../../config/reputation"; - -// Handlers -import logger from "../../../logger"; - -// Models -import timeoutSchema from "../../../models/timeout"; -import fetchUser from "../../../helpers/fetchUser"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Function -export default { - metadata: { guildOnly: true, ephemeral: true }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("give") - .setDescription("Give reputation to a user") - .addUserOption((option) => - option - .setName("target") - .setDescription("The user you want to repute.") - .setRequired(true) - ) - .addStringOption((option) => - option - .setName("type") - .setDescription("What type of reputation you want to repute") - .setRequired(true) - .addChoices( - { name: "Positive", value: "positive" }, - { - name: "Negative", - value: "negative", - } - ) - ); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { errorColor, successColor, footerText, footerIcon } = - await getEmbedConfig(interaction.guild); // Destructure - const { options, user, guild } = interaction; - - // Target option - const optionTarget = options?.getUser("target"); - - // Type information - const optionType = options?.getString("type"); - - if (guild === null) { - return logger?.silly(`Guild is null`); - } - - // User information - const userObj = await fetchUser(user, guild); - - if (userObj === null) { - return logger?.silly(`User is null`); - } - - // Check if user has a timeout - const isTimeout = await timeoutSchema?.findOne({ - guildId: guild?.id, - userId: user?.id, - timeoutId: "2022-04-10-16-42", - }); - - // If user is not on timeout - if (isTimeout) { - logger?.silly(`User is on timeout`); - - return interaction?.editReply({ - embeds: [ - { - title: ":loudspeaker: Reputation [Give]", - description: `You cannot give reputation while on timeout, please wait ${timeout} seconds.`, - timestamp: new Date(), - color: errorColor, - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - } - - // Do not allow self reputation - if (optionTarget?.id === user?.id) { - logger?.silly(`User is trying to give reputation to self`); - - return interaction?.editReply({ - embeds: [ - { - title: ":loudspeaker: Reputation [Give]", - description: `You cannot give reputation to yourself.`, - timestamp: new Date(), - color: errorColor, - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - } - - // If type is positive - if (optionType === "positive") { - logger?.silly(`User is giving positive reputation`); - - userObj.reputation += 1; - } - - // If type is negative - else if (optionType === "negative") { - logger?.silly(`User is giving negative reputation`); - - userObj.reputation -= 1; - } - - // Save user - await userObj?.save()?.then(async () => { - logger?.silly(`User reputation has been updated`); - - await timeoutSchema?.create({ - guildId: guild?.id, - userId: user?.id, - timeoutId: "2022-04-10-16-42", - }); - - return interaction?.editReply({ - embeds: [ - { - title: ":loudspeaker: Reputation [Give]", - description: `You have given reputation to ${optionTarget}`, - timestamp: new Date(), - color: successColor, - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); - - setTimeout(async () => { - logger?.silly(`Removing timeout`); - - await timeoutSchema?.deleteOne({ - guildId: guild?.id, - userId: user?.id, - timeoutId: "2022-04-10-16-42", - }); - }, timeout); - }, -}; diff --git a/src/commands/shop/modules/index.ts b/src/commands/shop/modules/index.ts deleted file mode 100644 index d191ec6..0000000 --- a/src/commands/shop/modules/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import pterodactyl from "./pterodactyl"; -import * as roles from "./roles"; - -export default { pterodactyl, roles }; diff --git a/src/commands/utility/modules/about.ts b/src/commands/utility/modules/about.ts deleted file mode 100644 index 18eca4c..0000000 --- a/src/commands/utility/modules/about.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Dependencies -import { CommandInteraction } from "discord.js"; - -// Configurations -import getEmbedConfig from "../../../helpers/getEmbedConfig"; - -import { hosterName, hosterUrl } from "../../../config/other"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Function -export default { - metadata: { guildOnly: false, ephemeral: false }, - - builder: (command: SlashCommandSubcommandBuilder) => { - return command.setName("about").setDescription("About this bot!)"); - }, - execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { successColor, footerText, footerIcon } = await getEmbedConfig( - interaction.guild - ); - const interactionEmbed = { - title: ":hammer: Utilities [About]", - description: `This bot is hosted by ${ - hosterUrl ? `[${hosterName}](${hosterUrl})` : `${hosterName}` - }, the bot is developed by [Zyner](https://github.com/ZynerOrg)! - - If you are interested in contributing, then just [fork it](https://github.com/ZynerOrg/xyter) yourself, we :heart: Open Source.`, - color: successColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }; - interaction?.editReply({ embeds: [interactionEmbed] }); - }, -}; diff --git a/src/config_example/other.ts b/src/config_example/other.ts index 1695d42..eefd62c 100644 --- a/src/config_example/other.ts +++ b/src/config_example/other.ts @@ -8,7 +8,7 @@ export const guildId = ""; export const hosterName = "someone"; // Hoster Url -export const hosterUrl = "scheme://domain.tld"; +export const hosterUrl = "https://xyter.zyner.org/customization/change-hoster"; // Winston log level export const logLevel = "info"; diff --git a/src/events/interactionCreate/components/isCommand.ts b/src/events/interactionCreate/components/isCommand.ts deleted file mode 100644 index 9ed5868..0000000 --- a/src/events/interactionCreate/components/isCommand.ts +++ /dev/null @@ -1,178 +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 timeoutSchema from "../../../models/timeout"; -import addSeconds from "../../../helpers/addSeconds"; - -export default async (interaction: CommandInteraction) => { - if (!interaction.isCommand()) return; - if (interaction.guild == null) 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.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.cooldown) { - console.log("cooldown"); - // console.log(interaction); - - // Check if user has a timeout - const isTimeout = await timeoutSchema.findOne({ - guildId: guild.id, - userId: user.id, - cooldown: metadata.cooldown, - timeoutId: interaction.commandId, - }); - - // If user is not on timeout - if (isTimeout) { - logger?.silly(`User is on timeout`); - - const { guildId, userId, timeoutId, cooldown, createdAt } = isTimeout; - - const overDue = (await addSeconds(cooldown, createdAt)) < new Date(); - - if (overDue) { - timeoutSchema - .deleteOne({ - guildId, - userId, - timeoutId, - cooldown, - }) - .then(async () => { - logger.debug( - `Timeout document ${timeoutId} has been deleted from user ${userId}.` - ); - }); - } else { - const diff = Math.round( - ((new Date(isTimeout.createdAt).getTime() - new Date().getTime()) * - -1) / - 1000 - ); - - return interaction?.editReply({ - embeds: [ - { - title: `[:x:] ${capitalizeFirstLetter( - interaction.options.getSubcommand() - )}`, - description: ` - You are currently on timeout, please wait ${diff} seconds. - - If it still doesn't work, please wait for a maximum of **1 hour** before contacting bot owner. - `, - timestamp: new Date(), - color: errorColor, - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - } - } - - await timeoutSchema.create({ - guildId: guild.id, - userId: user.id, - cooldown: metadata.cooldown, - timeoutId: interaction.commandId, - }); - } - - if (metadata.guildOnly) { - if (!guild) { - logger.debug(`Guild is null`); - - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setDescription("This command is only available for guild") - .setColor(errorColor) - .setTimestamp(new Date()) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - } - } - - if (metadata.dmOnly) { - if (guild) { - logger.silly(`Guild exist`); - - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setDescription("This command is only available in DM.") - .setColor(errorColor) - .setTimestamp(new Date()) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - } - } - - await currentCommand - .execute(interaction) - .then(async () => { - return logger?.silly( - `Command: ${commandName} executed in guild: ${guild?.name} (${guild?.id}) by user: ${user?.tag} (${user?.id})` - ); - }) - .catch(async (error: string) => { - logger?.error(`${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/events/interactionCreate/index.ts b/src/events/interactionCreate/index.ts deleted file mode 100644 index 7e173dc..0000000 --- a/src/events/interactionCreate/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -// 3rd party dependencies -import { CommandInteraction } from "discord.js"; - -// Dependencies -import isCommand from "../../events/interactionCreate/components/isCommand"; -import logger from "../../logger"; -import audits from "./audits"; -import { IEventOptions } from "../../interfaces/EventOptions"; - -export const options: IEventOptions = { - type: "on", -}; - -export const execute = async (interaction: CommandInteraction) => { - const { guild, id } = interaction; - - logger?.silly( - `New interaction: ${id} in guild: ${guild?.name} (${guild?.id})` - ); - - await audits.execute(interaction); - await isCommand(interaction); -}; diff --git a/src/events/messageCreate/modules/credits/index.ts b/src/events/messageCreate/modules/credits/index.ts deleted file mode 100644 index ce96c4a..0000000 --- a/src/events/messageCreate/modules/credits/index.ts +++ /dev/null @@ -1,85 +0,0 @@ -import logger from "../../../../logger"; -import timeouts from "../../../../models/timeout"; -import { Message } from "discord.js"; - -import fetchUser from "../../../../helpers/fetchUser"; -import fetchGuild from "../../../../helpers/fetchGuild"; - -export default { - execute: async (message: Message) => { - const { guild, author, content, channel } = message; - - if (guild == null) return; - if (author.bot) return; - if (channel?.type !== "GUILD_TEXT") return; - - const { id: guildId } = guild; - const { id: userId } = author; - - const guildData = await fetchGuild(guild); - const userData = await fetchUser(author, guild); - - if (content.length < guildData.credits.minimumLength) return; - - const timeoutData = { - guildId, - userId, - timeoutId: "2022-04-14-13-51-00", - }; - - const timeout = await timeouts.findOne(timeoutData); - - if (timeout) { - logger.silly( - `User ${userId} in guild ${guildId} is on timeout 2022-04-14-13-51-00` - ); - return; - } - - userData.credits += guildData.credits.rate; - - await userData - .save() - .then(async () => { - logger.silly( - `User ${userId} in guild ${guildId} has ${userData.credits} credits` - ); - }) - .catch(async (err) => { - logger.error( - `Error saving credits for user ${userId} in guild ${guildId}`, - err - ); - }); - - await timeouts - .create(timeoutData) - .then(async () => { - logger.silly( - `Timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId} has been created` - ); - }) - .catch(async (err) => { - logger.error( - `Error creating timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId}`, - err - ); - }); - - setTimeout(async () => { - await timeouts - .deleteOne(timeoutData) - .then(async () => { - logger.silly( - `Timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId} has been deleted` - ); - }) - .catch(async (err) => { - logger.error( - `Error deleting timeout 2022-04-14-13-51-00 for user ${userId} in guild ${guildId}`, - err - ); - }); - }, guildData.credits.timeout); - }, -}; diff --git a/src/events/messageCreate/modules/points/index.ts b/src/events/messageCreate/modules/points/index.ts deleted file mode 100644 index 9a8acd7..0000000 --- a/src/events/messageCreate/modules/points/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -import logger from "../../../../logger"; -import timeouts from "../../../../models/timeout"; - -import fetchUser from "../../../../helpers/fetchUser"; -import fetchGuild from "../../../../helpers/fetchGuild"; - -import { Message } from "discord.js"; -export default { - execute: async (message: Message) => { - const { guild, author, content, channel } = message; - - if (guild == null) return; - if (author.bot) return; - if (channel?.type !== "GUILD_TEXT") return; - - const { id: guildId } = guild; - const { id: userId } = author; - - const guildData = await fetchGuild(guild); - const userData = await fetchUser(author, guild); - - if (content.length < guildData.credits.minimumLength) return; - - const timeoutData = { - guildId, - userId, - timeoutId: "2022-04-14-14-15-00", - }; - - const timeout = await timeouts.findOne(timeoutData); - - if (timeout) { - logger.silly( - `User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id} is on timeout 2022-04-14-14-15-00` - ); - return; - } - - userData.points += guildData.points.rate; - - await userData - .save() - .then(async () => { - logger.silly( - `Successfully saved user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})` - ); - }) - .catch(async (err) => { - logger.error( - `Error saving points for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`, - err - ); - }); - - logger.silly( - `User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id}) has ${userData.points} points` - ); - - await timeouts - .create(timeoutData) - .then(async () => { - logger.silly( - `Successfully created timeout for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})` - ); - }) - .catch(async (err) => { - logger.error( - `Error creating timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`, - err - ); - }); - - setTimeout(async () => { - await timeouts - .deleteOne(timeoutData) - .then(async () => { - logger.silly( - `Successfully deleted timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})` - ); - }) - .catch(async (err) => { - logger.error( - `Error deleting timeout 2022-04-14-14-15-00 for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`, - err - ); - }); - }, guildData.points.timeout); - }, -}; diff --git a/src/handlers/deployCommands.ts b/src/handlers/deployCommands.ts index 2123da7..4542377 100644 --- a/src/handlers/deployCommands.ts +++ b/src/handlers/deployCommands.ts @@ -12,6 +12,10 @@ import { ICommand } from "../interfaces/Command"; export default async (client: Client) => { const commandList: Array = []; + if (!client.commands) { + throw new Error("client.commands is not defined"); + } + logger.info("Gathering command list"); await Promise.all( @@ -25,7 +29,7 @@ export default async (client: Client) => { logger.info(`Finished gathering command list.`); }) .catch(async (error) => { - logger.error(`${error}`); + throw new Error(`Could not gather command list: ${error}`); }); const rest = new REST({ version: "9" }).setToken(token); diff --git a/src/helpers/addSeconds/index.ts b/src/helpers/addSeconds/index.ts index 6a89447..91bff4e 100644 --- a/src/helpers/addSeconds/index.ts +++ b/src/helpers/addSeconds/index.ts @@ -1,4 +1,4 @@ -export default async (numOfSeconds: number | undefined, date = new Date()) => { +export default async (numOfSeconds: number, date: Date) => { if (!numOfSeconds) throw new Error("numOfSeconds is required"); date.setSeconds(date.getSeconds() + numOfSeconds); diff --git a/src/helpers/cooldown/index.ts b/src/helpers/cooldown/index.ts new file mode 100644 index 0000000..2417d81 --- /dev/null +++ b/src/helpers/cooldown/index.ts @@ -0,0 +1,112 @@ +// Dependencies +import { CommandInteraction, Message } from "discord.js"; + +import logger from "../../logger"; + +import getEmbedConfig from "../../helpers/getEmbedConfig"; +import timeoutSchema from "../../models/timeout"; +import addSeconds from "../../helpers/addSeconds"; + +export const interaction = async (i: CommandInteraction, cooldown: number) => { + const { guild, user, commandId } = i; + + // Check if user has a timeout + const hasTimeout = await timeoutSchema.findOne({ + guildId: guild?.id || "0", + userId: user.id, + cooldown: cooldown, + timeoutId: commandId, + }); + + // 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: commandId, + }); +}; + +export const message = async ( + message: Message, + cooldown: number, + id: string +) => { + const { guild, member } = message; + if (!guild) throw new Error("Guild is undefined"); + if (!member) throw new Error("Member is undefined"); + + // Check if user has a timeout + const hasTimeout = await timeoutSchema.findOne({ + guildId: guild?.id || "0", + userId: member.id, + cooldown: cooldown, + timeoutId: id, + }); + + // 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( + `User: ${userId} on timeout-id: ${id} with cooldown: ${cooldown} secs with remaining: ${diff} secs.` + ); + } + + // Delete timeout + await timeoutSchema + .deleteOne({ + guildId, + userId: member.id, + timeoutId: id, + cooldown, + }) + .then(async () => { + logger.debug( + `Timeout document ${timeoutId} has been deleted from user ${userId}.` + ); + }); + } + // Create timeout + await timeoutSchema.create({ + guildId: guild?.id || "0", + userId: member.id, + cooldown: cooldown, + timeoutId: id, + }); +}; diff --git a/src/helpers/deferReply/index.ts b/src/helpers/deferReply/index.ts index 45d95ed..b5cfdc3 100644 --- a/src/helpers/deferReply/index.ts +++ b/src/helpers/deferReply/index.ts @@ -2,8 +2,6 @@ import { CommandInteraction, MessageEmbed } from "discord.js"; import getEmbedConfig from "../../helpers/getEmbedConfig"; export default async (interaction: CommandInteraction, ephemeral: boolean) => { - if (interaction.guild == null) return; - await interaction.deferReply({ ephemeral, }); diff --git a/src/helpers/listDir/index.ts b/src/helpers/listDir/index.ts index 247fbe0..95ed1da 100644 --- a/src/helpers/listDir/index.ts +++ b/src/helpers/listDir/index.ts @@ -2,9 +2,7 @@ import fs from "fs"; const fsPromises = fs.promises; export default async (path: string) => { - try { - return await fsPromises.readdir(path); - } catch (err) { - console.error("Error occurred while reading directory!", err); - } + return fsPromises.readdir(path).catch(async (e) => { + throw new Error(`Could not list directory: ${path}`, e); + }); }; diff --git a/src/index.ts b/src/index.ts index c9c82a1..ef3cab7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,10 +4,7 @@ import { token, intents } from "./config/discord"; import { Client } from "discord.js"; // discord.js -import * as databaseManager from "./managers/database"; -import * as scheduleManager from "./managers/schedule"; -import * as eventManager from "./managers/event"; -import * as commandManager from "./managers/command"; +import * as managers from "./managers"; // Main process that starts all other sub processes const main = async () => { @@ -16,11 +13,7 @@ const main = async () => { intents, }); - // Start managers - await databaseManager.start(); - await scheduleManager.start(client); - await commandManager.register(client); - await eventManager.register(client); + await managers.start(client); // Authorize with Discord's API await client.login(token); diff --git a/src/logger/index.ts b/src/logger/index.ts index 1ffbceb..0593ec9 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -3,7 +3,8 @@ import "winston-daily-rotate-file"; import { logLevel } from "../config/other"; -const { combine, timestamp, printf, colorize, align, json } = winston.format; +const { combine, timestamp, printf, errors, colorize, align, json } = + winston.format; export default winston.createLogger({ level: logLevel || "info", @@ -16,6 +17,7 @@ export default winston.createLogger({ }), new winston.transports.Console({ format: combine( + errors({ stack: true, trace: true }), // <-- use errors format colorize({ all: true }), timestamp({ format: "YYYY-MM-DD HH:MM:ss", diff --git a/src/managers/command/index.ts b/src/managers/command/index.ts index ece1712..c3c08f9 100644 --- a/src/managers/command/index.ts +++ b/src/managers/command/index.ts @@ -1,20 +1,25 @@ -import fs from "fs"; // fs -import { Collection, Client } from "discord.js"; // discord.js -import logger from "../../logger"; -import { ICommand } from "../../interfaces/Command"; +import { Collection, Client } from "discord.js"; import listDir from "../../helpers/listDir"; +import logger from "../../logger"; + +import { ICommand } from "../../interfaces/Command"; export const register = async (client: Client) => { client.commands = new Collection(); - const commandNames = await listDir("commands"); - if (!commandNames) return; + const commandNames = await listDir("plugins/commands"); + + if (!commandNames) throw new Error("Could not list commands"); logger.info(`Loading ${commandNames.length} commands`); await Promise.all( commandNames.map(async (commandName) => { - const command: ICommand = await import(`../../commands/${commandName}`); + const command: ICommand = await import( + `../../plugins/commands/${commandName}` + ).catch(async (e) => { + throw new Error(`Could not load command: ${commandName}`, e); + }); client.commands.set(command.builder.name, command); @@ -25,6 +30,6 @@ export const register = async (client: Client) => { logger.info(`Finished loading commands.`); }) .catch(async (err) => { - logger.error(`${err}`); + throw new Error(`Could not load commands: ${err}`); }); }; diff --git a/src/managers/database/index.ts b/src/managers/database/index.ts index c16eb73..0063c61 100644 --- a/src/managers/database/index.ts +++ b/src/managers/database/index.ts @@ -8,9 +8,14 @@ import logger from "../../logger"; import { url } from "../../config/database"; export const start = async () => { - await mongoose.connect(url).then(async (connection) => { - logger.info(`Connected to database: ${connection.connection.name}`); - }); + await mongoose + .connect(url) + .then(async (connection) => { + logger.info(`Connected to database: ${connection.connection.name}`); + }) + .catch(async (e) => { + logger.error("Could not connect to database", e); + }); mongoose.connection.on("error", async (error) => { logger.error(`${error}`); diff --git a/src/managers/event/index.ts b/src/managers/event/index.ts index 9718b12..348e138 100644 --- a/src/managers/event/index.ts +++ b/src/managers/event/index.ts @@ -2,15 +2,18 @@ import { Client } from "discord.js"; import listDir from "../../helpers/listDir"; import { IEvent } from "../../interfaces/Event"; +import logger from "../../logger"; export const register = async (client: Client) => { - const eventNames = await listDir("events"); + const eventNames = await listDir("plugins/events"); if (!eventNames) return; for await (const eventName of eventNames) { - const event: IEvent = await import(`../../events/${eventName}`); + const event: IEvent = await import(`../../plugins/events/${eventName}`); const eventExecutor = async (...args: Promise[]) => - event.execute(...args); + event.execute(...args).catch(async (err) => { + logger.error(`${err}`); + }); if (!event.options?.type) return; switch (event.options.type) { diff --git a/src/managers/index.ts b/src/managers/index.ts new file mode 100644 index 0000000..6981d3c --- /dev/null +++ b/src/managers/index.ts @@ -0,0 +1,13 @@ +import { Client } from "discord.js"; + +import * as database from "./database"; +import * as schedule from "./schedule"; +import * as event from "./event"; +import * as command from "./command"; + +export const start = async (client: Client) => { + await database.start(); + await schedule.start(client); + await command.register(client); + await event.register(client); +}; diff --git a/src/models/timeout.ts b/src/models/timeout.ts index 878f498..0e3554c 100644 --- a/src/models/timeout.ts +++ b/src/models/timeout.ts @@ -4,7 +4,7 @@ import { Schema, model } from "mongoose"; export interface ITimeout { userId: Snowflake; guildId: Snowflake; - cooldown?: number; + cooldown: number; timeoutId: string; createdAt: Date; updatedAt: Date; @@ -26,7 +26,7 @@ const timeoutSchema = new Schema( }, cooldown: { type: Number, - required: false, + required: true, unique: false, index: true, }, diff --git a/src/plugins/buttons/primary/index.ts b/src/plugins/buttons/primary/index.ts new file mode 100644 index 0000000..bd71158 --- /dev/null +++ b/src/plugins/buttons/primary/index.ts @@ -0,0 +1,7 @@ +import { CommandInteraction } from "discord.js"; + +export const metadata = { guildOnly: false, ephemeral: false }; + +export const execute = async (interaction: CommandInteraction) => { + console.log("primary button clicked!"); +}; diff --git a/src/commands/counters/index.ts b/src/plugins/commands/counters/index.ts similarity index 93% rename from src/commands/counters/index.ts rename to src/plugins/commands/counters/index.ts index da17e90..eca16dc 100644 --- a/src/commands/counters/index.ts +++ b/src/plugins/commands/counters/index.ts @@ -1,6 +1,6 @@ import { CommandInteraction } from "discord.js"; import { SlashCommandBuilder } from "@discordjs/builders"; -import logger from "../../logger"; +import logger from "../../../logger"; import modules from "../../commands/counters/modules"; diff --git a/src/commands/counters/modules/index.ts b/src/plugins/commands/counters/modules/index.ts similarity index 100% rename from src/commands/counters/modules/index.ts rename to src/plugins/commands/counters/modules/index.ts diff --git a/src/commands/counters/modules/view/index.ts b/src/plugins/commands/counters/modules/view/index.ts similarity index 91% rename from src/commands/counters/modules/view/index.ts rename to src/plugins/commands/counters/modules/view/index.ts index 6c3196f..77e1714 100644 --- a/src/commands/counters/modules/view/index.ts +++ b/src/plugins/commands/counters/modules/view/index.ts @@ -1,10 +1,10 @@ -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import { CommandInteraction, MessageEmbed } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { ChannelType } from "discord-api-types/v10"; -import counterSchema from "../../../../models/counter"; +import counterSchema from "../../../../../models/counter"; export default { metadata: { guildOnly: true, ephemeral: false }, @@ -25,7 +25,6 @@ export default { }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild } = interaction; diff --git a/src/commands/credits/index.ts b/src/plugins/commands/credits/index.ts similarity index 96% rename from src/commands/credits/index.ts rename to src/plugins/commands/credits/index.ts index 02c03a5..9d1a3b4 100644 --- a/src/commands/credits/index.ts +++ b/src/plugins/commands/credits/index.ts @@ -1,6 +1,6 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; -import logger from "../../logger"; +import logger from "../../../logger"; import modules from "./modules"; diff --git a/src/commands/credits/modules/balance/index.ts b/src/plugins/commands/credits/modules/balance/index.ts similarity index 91% rename from src/commands/credits/modules/balance/index.ts rename to src/plugins/commands/credits/modules/balance/index.ts index 2387d57..0e646c2 100644 --- a/src/commands/credits/modules/balance/index.ts +++ b/src/plugins/commands/credits/modules/balance/index.ts @@ -1,10 +1,10 @@ -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import { CommandInteraction, MessageEmbed } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import logger from "../../../../logger"; +import logger from "../../../../../logger"; -import fetchUser from "../../../../helpers/fetchUser"; +import fetchUser from "../../../../../helpers/fetchUser"; export default { metadata: { guildOnly: true, ephemeral: true }, @@ -19,7 +19,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, user, guild } = interaction; diff --git a/src/commands/credits/modules/gift/index.ts b/src/plugins/commands/credits/modules/gift/index.ts similarity index 96% rename from src/commands/credits/modules/gift/index.ts rename to src/plugins/commands/credits/modules/gift/index.ts index 9873c02..84b2350 100644 --- a/src/commands/credits/modules/gift/index.ts +++ b/src/plugins/commands/credits/modules/gift/index.ts @@ -2,15 +2,15 @@ import { CommandInteraction, MessageEmbed } from "discord.js"; // Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../logger"; +import logger from "../../../../../logger"; import mongoose from "mongoose"; // Models -import fetchUser from "../../../../helpers/fetchUser"; +import fetchUser from "../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -38,7 +38,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, user, guild, client } = interaction; diff --git a/src/commands/credits/modules/index.ts b/src/plugins/commands/credits/modules/index.ts similarity index 100% rename from src/commands/credits/modules/index.ts rename to src/plugins/commands/credits/modules/index.ts diff --git a/src/commands/credits/modules/top/index.ts b/src/plugins/commands/credits/modules/top/index.ts similarity index 88% rename from src/commands/credits/modules/top/index.ts rename to src/plugins/commands/credits/modules/top/index.ts index 78539c4..29aadda 100644 --- a/src/commands/credits/modules/top/index.ts +++ b/src/plugins/commands/credits/modules/top/index.ts @@ -1,10 +1,10 @@ -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import { CommandInteraction, MessageEmbed } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import logger from "../../../../logger"; +import logger from "../../../../../logger"; -import userSchema, { IUser } from "../../../../models/user"; +import userSchema, { IUser } from "../../../../../models/user"; export default { metadata: { guildOnly: true, ephemeral: false }, @@ -13,7 +13,6 @@ export default { return command.setName("top").setDescription(`View the top users`); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { guild } = interaction; diff --git a/src/commands/credits/modules/work/index.ts b/src/plugins/commands/credits/modules/work/index.ts similarity index 50% rename from src/commands/credits/modules/work/index.ts rename to src/plugins/commands/credits/modules/work/index.ts index ba1a08e..a84a696 100644 --- a/src/commands/credits/modules/work/index.ts +++ b/src/plugins/commands/credits/modules/work/index.ts @@ -4,17 +4,17 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import Chance from "chance"; // Configurations -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../logger"; +import logger from "../../../../../logger"; // Models -import timeoutSchema from "../../../../models/timeout"; +import * as cooldown from "../../../../../helpers/cooldown"; // Helpers -import fetchUser from "../../../../helpers/fetchUser"; -import fetchGuild from "../../../../helpers/fetchGuild"; +import fetchUser from "../../../../../helpers/fetchUser"; +import fetchGuild from "../../../../../helpers/fetchGuild"; export default { metadata: { guildOnly: true, ephemeral: true }, @@ -23,9 +23,9 @@ export default { return command.setName("work").setDescription(`Work to earn credits`); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { errorColor, successColor, footerText, footerIcon } = - await getEmbedConfig(interaction.guild); // Destructure member + const { successColor, footerText, footerIcon } = await getEmbedConfig( + interaction.guild + ); // Destructure member const { guild, user } = interaction; const embed = new MessageEmbed() @@ -39,33 +39,13 @@ export default { // Chance module const chance = new Chance(); - // Check if user has a timeout - const isTimeout = await timeoutSchema?.findOne({ - guildId: guild?.id, - userId: user?.id, - timeoutId: "2022-03-15-19-16", - }); - if (guild === null) { return logger?.silly(`Guild is null`); } const guildDB = await fetchGuild(guild); - // If user is not on timeout - if (isTimeout) { - logger?.silly(`User ${user?.id} is on timeout`); - - return interaction.editReply({ - embeds: [ - embed - .setDescription( - `You are on timeout, please wait ${guildDB?.credits.workTimeout} seconds.` - ) - .setColor(errorColor), - ], - }); - } + await cooldown.interaction(interaction, guildDB?.credits?.workTimeout); const creditsEarned = chance.integer({ min: 0, @@ -93,23 +73,5 @@ export default { ], }); }); - - // Create a timeout for the user - await timeoutSchema?.create({ - guildId: guild?.id, - userId: user?.id, - timeoutId: "2022-03-15-19-16", - }); - - setTimeout(async () => { - logger?.silly(`Removing timeout for user ${user?.id}`); - - // When timeout is out, remove it from the database - await timeoutSchema?.deleteOne({ - guildId: guild?.id, - userId: user?.id, - timeoutId: "2022-03-15-19-16", - }); - }, guildDB?.credits?.workTimeout); }, }; diff --git a/src/commands/fun/index.ts b/src/plugins/commands/fun/index.ts similarity index 94% rename from src/commands/fun/index.ts rename to src/plugins/commands/fun/index.ts index 5c71cd2..e0337a6 100644 --- a/src/commands/fun/index.ts +++ b/src/plugins/commands/fun/index.ts @@ -1,6 +1,6 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; -import logger from "../../logger"; +import logger from "../../../logger"; import modules from "../../commands/fun/modules"; diff --git a/src/commands/fun/modules/index.ts b/src/plugins/commands/fun/modules/index.ts similarity index 100% rename from src/commands/fun/modules/index.ts rename to src/plugins/commands/fun/modules/index.ts diff --git a/src/commands/fun/modules/meme/index.ts b/src/plugins/commands/fun/modules/meme/index.ts similarity index 92% rename from src/commands/fun/modules/meme/index.ts rename to src/plugins/commands/fun/modules/meme/index.ts index 249d2ee..88b2304 100644 --- a/src/commands/fun/modules/meme/index.ts +++ b/src/plugins/commands/fun/modules/meme/index.ts @@ -1,11 +1,11 @@ -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import axios from "axios"; import { CommandInteraction, MessageEmbed } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; export default { - metadata: { guildOnly: false, ephemeral: false, cooldown: 5 }, + metadata: { guildOnly: false, ephemeral: false, cooldown: 15 }, builder: (command: SlashCommandSubcommandBuilder) => { return command.setName("meme").setDescription("Get a meme from r/memes)"); diff --git a/src/commands/manage/index.ts b/src/plugins/commands/manage/index.ts similarity index 95% rename from src/commands/manage/index.ts rename to src/plugins/commands/manage/index.ts index 14b5f6c..1f914bb 100644 --- a/src/commands/manage/index.ts +++ b/src/plugins/commands/manage/index.ts @@ -4,7 +4,7 @@ import { CommandInteraction } from "discord.js"; // Groups import modules from "../../commands/manage/modules"; -import logger from "../../logger"; +import logger from "../../../logger"; export const moduleData = modules; diff --git a/src/commands/manage/modules/counters/index.ts b/src/plugins/commands/manage/modules/counters/index.ts similarity index 95% rename from src/commands/manage/modules/counters/index.ts rename to src/plugins/commands/manage/modules/counters/index.ts index 36d6cd9..dbafa9f 100644 --- a/src/commands/manage/modules/counters/index.ts +++ b/src/plugins/commands/manage/modules/counters/index.ts @@ -2,7 +2,7 @@ import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; -import logger from "../../../../logger"; +import logger from "../../../../../logger"; // Modules import modules from "./modules"; diff --git a/src/commands/manage/modules/counters/modules/add/index.ts b/src/plugins/commands/manage/modules/counters/modules/add/index.ts similarity index 91% rename from src/commands/manage/modules/counters/modules/add/index.ts rename to src/plugins/commands/manage/modules/counters/modules/add/index.ts index 5d7ea53..d00a319 100644 --- a/src/commands/manage/modules/counters/modules/add/index.ts +++ b/src/plugins/commands/manage/modules/counters/modules/add/index.ts @@ -4,12 +4,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { ChannelType } from "discord-api-types/v10"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; -import logger from "../../../../../../logger"; +import logger from "../../../../../../../logger"; // Models -import counterSchema from "../../../../../../models/counter"; +import counterSchema from "../../../../../../../models/counter"; // Function export default { @@ -43,7 +43,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild } = interaction; diff --git a/src/commands/manage/modules/counters/modules/index.ts b/src/plugins/commands/manage/modules/counters/modules/index.ts similarity index 100% rename from src/commands/manage/modules/counters/modules/index.ts rename to src/plugins/commands/manage/modules/counters/modules/index.ts diff --git a/src/commands/manage/modules/counters/modules/remove/index.ts b/src/plugins/commands/manage/modules/counters/modules/remove/index.ts similarity index 91% rename from src/commands/manage/modules/counters/modules/remove/index.ts rename to src/plugins/commands/manage/modules/counters/modules/remove/index.ts index c265f0b..563829b 100644 --- a/src/commands/manage/modules/counters/modules/remove/index.ts +++ b/src/plugins/commands/manage/modules/counters/modules/remove/index.ts @@ -2,13 +2,13 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../../../logger"; +import logger from "../../../../../../../logger"; // Models -import counterSchema from "../../../../../../models/counter"; +import counterSchema from "../../../../../../../models/counter"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { ChannelType } from "discord-api-types/v10"; @@ -33,7 +33,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild } = interaction; diff --git a/src/commands/manage/modules/credits/index.ts b/src/plugins/commands/manage/modules/credits/index.ts similarity index 84% rename from src/commands/manage/modules/credits/index.ts rename to src/plugins/commands/manage/modules/credits/index.ts index 5e215ec..b4673ff 100644 --- a/src/commands/manage/modules/credits/index.ts +++ b/src/plugins/commands/manage/modules/credits/index.ts @@ -1,6 +1,6 @@ import { CommandInteraction } from "discord.js"; import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; -import logger from "../../../../logger"; +import logger from "../../../../../logger"; import modules from "./modules"; @@ -14,7 +14,7 @@ export const builder = (group: SlashCommandSubcommandGroupBuilder) => { .addSubcommand(modules.set.builder) .addSubcommand(modules.take.builder) .addSubcommand(modules.transfer.builder) - .addSubcommand(modules.drop.builder); + .addSubcommand(modules.giveaway.builder); }; export const execute = async (interaction: CommandInteraction) => { @@ -27,7 +27,7 @@ export const execute = async (interaction: CommandInteraction) => { return modules.take.execute(interaction); case "transfer": return modules.transfer.execute(interaction); - case "drop": - return modules.drop.execute(interaction); + case "giveaway": + return modules.giveaway.execute(interaction); } }; diff --git a/src/commands/manage/modules/credits/modules/give/index.ts b/src/plugins/commands/manage/modules/credits/modules/give/index.ts similarity index 94% rename from src/commands/manage/modules/credits/modules/give/index.ts rename to src/plugins/commands/manage/modules/credits/modules/give/index.ts index ebbc762..9615f73 100644 --- a/src/commands/manage/modules/credits/modules/give/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/give/index.ts @@ -3,16 +3,16 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../../../logger"; +import logger from "../../../../../../../logger"; // Helpers -import pluralize from "../../../../../../helpers/pluralize"; +import pluralize from "../../../../../../../helpers/pluralize"; // Models -import fetchUser from "../../../../../../helpers/fetchUser"; +import fetchUser from "../../../../../../../helpers/fetchUser"; // Function export default { @@ -40,7 +40,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); // Destructure const { guild, options } = interaction; diff --git a/src/commands/manage/modules/credits/modules/drop/index.ts b/src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts similarity index 61% rename from src/commands/manage/modules/credits/modules/drop/index.ts rename to src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts index 7141c9a..9ee2f17 100644 --- a/src/commands/manage/modules/credits/modules/drop/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/giveaway/index.ts @@ -1,22 +1,21 @@ // Dependencies -import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; +import { + CommandInteraction, + MessageActionRow, + MessageButton, + MessageEmbed, + Permissions, +} from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { v4 as uuidv4 } from "uuid"; import axios from "axios"; -import apiSchema from "../../../../../../models/api"; -import encryption from "../../../../../../handlers/encryption"; +import apiSchema from "../../../../../../../models/api"; +import encryption from "../../../../../../../handlers/encryption"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; -// Handlers -import logger from "../../../../../../logger"; - -// Helpers -import pluralize from "../../../../../../helpers/pluralize"; - -// Models -import fetchUser from "../../../../../../helpers/fetchUser"; +import { ChannelType } from "discord-api-types/v10"; // Function export default { @@ -28,8 +27,8 @@ export default { builder: (command: SlashCommandSubcommandBuilder) => { return command - .setName("drop") - .setDescription("Drop some credits for specified amount of users.") + .setName("giveaway") + .setDescription("Giveaway some credits for specified amount of users.") .addIntegerOption((option) => option .setName("uses") @@ -47,12 +46,13 @@ export default { .setName("channel") .setDescription("The channel to send the message to.") .setRequired(true) + .addChannelTypes(ChannelType.GuildText) ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; - const { errorColor, successColor, footerText, footerIcon } = - await getEmbedConfig(interaction.guild); // Destructure + const { successColor, footerText, footerIcon } = await getEmbedConfig( + interaction.guild + ); // Destructure const { guild, options } = interaction; const uses = options?.getInteger("uses"); @@ -76,31 +76,39 @@ export default { if (!apiCredentials) return; const api = axios?.create({ - baseURL: apiCredentials.url, + baseURL: `${apiCredentials?.url}/api/`, headers: { Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`, }, }); - const shopUrl = apiCredentials?.url?.replace("/api", "/store"); + const shopUrl = `${apiCredentials?.url}/store`; await api .post("vouchers", { uses, code, credits: creditAmount, - memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`, + memo: `[GIVEAWAY] ${interaction?.createdTimestamp} - ${interaction?.user?.id}`, }) .then(async () => { await interaction.editReply({ embeds: [ embed .setColor(successColor) - .setDescription(`Successfully crated code: ${code}`), + .setDescription(`Successfully created code: ${code}`), ], }); - const discordChannel = guild.channels.cache.get(channel.id); + const buttons = new MessageActionRow().addComponents( + new MessageButton() + .setLabel("Redeem it here") + .setStyle("LINK") + .setEmoji("🏦") + .setURL(`${shopUrl}?voucher=${code}`) + ); + + const discordChannel = guild?.channels.cache.get(channel.id); if (!discordChannel) return; @@ -109,17 +117,20 @@ export default { discordChannel.send({ embeds: [ new MessageEmbed() - .setTitle("[:parachute:] Code Drop!") + .setTitle("[:parachute:] Credits!") .addFields([ - { name: "Code", value: `${code}`, inline: true }, - { name: "Amount", value: `${creditAmount}`, inline: true }, - { name: "Uses", value: `${uses}`, inline: true }, + { + name: "💶 Credits", + value: `${creditAmount}`, + inline: true, + }, ]) .setDescription( - `${interaction.user} dropped a voucher! You can use the code [here](${shopUrl})!` + `${interaction.user} dropped a voucher for a maximum **${uses}** members!` ) .setColor(successColor), ], + components: [buttons], }); }); }, diff --git a/src/commands/manage/modules/credits/modules/index.ts b/src/plugins/commands/manage/modules/credits/modules/index.ts similarity index 55% rename from src/commands/manage/modules/credits/modules/index.ts rename to src/plugins/commands/manage/modules/credits/modules/index.ts index 07b72e6..96acc29 100644 --- a/src/commands/manage/modules/credits/modules/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/index.ts @@ -2,6 +2,6 @@ import give from "./give"; import set from "./set"; import take from "./take"; import transfer from "./transfer"; -import drop from "./drop"; +import giveaway from "./giveaway"; -export default { give, set, take, transfer, drop }; +export default { give, set, take, transfer, giveaway }; diff --git a/src/commands/manage/modules/credits/modules/set/index.ts b/src/plugins/commands/manage/modules/credits/modules/set/index.ts similarity index 95% rename from src/commands/manage/modules/credits/modules/set/index.ts rename to src/plugins/commands/manage/modules/credits/modules/set/index.ts index a8e1771..a3be389 100644 --- a/src/commands/manage/modules/credits/modules/set/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/set/index.ts @@ -2,15 +2,15 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../../../logger"; +import logger from "../../../../../../../logger"; // Helpers // Models -import fetchUser from "../../../../../../helpers/fetchUser"; +import fetchUser from "../../../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -39,7 +39,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild } = interaction; diff --git a/src/commands/manage/modules/credits/modules/take/index.ts b/src/plugins/commands/manage/modules/credits/modules/take/index.ts similarity index 94% rename from src/commands/manage/modules/credits/modules/take/index.ts rename to src/plugins/commands/manage/modules/credits/modules/take/index.ts index 5cf794a..fd3076b 100644 --- a/src/commands/manage/modules/credits/modules/take/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/take/index.ts @@ -2,16 +2,16 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../../../logger"; +import logger from "../../../../../../../logger"; // Helpers -import pluralize from "../../../../../../helpers/pluralize"; +import pluralize from "../../../../../../../helpers/pluralize"; // Models -import fetchUser from "../../../../../../helpers/fetchUser"; +import fetchUser from "../../../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -40,7 +40,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); // Destructure const { guild, options } = interaction; diff --git a/src/commands/manage/modules/credits/modules/transfer/index.ts b/src/plugins/commands/manage/modules/credits/modules/transfer/index.ts similarity index 97% rename from src/commands/manage/modules/credits/modules/transfer/index.ts rename to src/plugins/commands/manage/modules/credits/modules/transfer/index.ts index 011e603..dca0091 100644 --- a/src/commands/manage/modules/credits/modules/transfer/index.ts +++ b/src/plugins/commands/manage/modules/credits/modules/transfer/index.ts @@ -4,13 +4,13 @@ import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; import mongoose from "mongoose"; // Configurations -import getEmbedConfig from "../../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Handlers -import logger from "../../../../../../logger"; +import logger from "../../../../../../../logger"; // Models -import fetchUser from "../../../../../../helpers/fetchUser"; +import fetchUser from "../../../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -45,7 +45,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); // Destructure member const { guild, options } = interaction; diff --git a/src/commands/manage/modules/index.ts b/src/plugins/commands/manage/modules/index.ts similarity index 100% rename from src/commands/manage/modules/index.ts rename to src/plugins/commands/manage/modules/index.ts diff --git a/src/commands/profile/index.ts b/src/plugins/commands/profile/index.ts similarity index 94% rename from src/commands/profile/index.ts rename to src/plugins/commands/profile/index.ts index 2039179..68cdc73 100644 --- a/src/commands/profile/index.ts +++ b/src/plugins/commands/profile/index.ts @@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js"; import modules from "../../commands/profile/modules"; // Handlers -import logger from "../../logger"; +import logger from "../../../logger"; export const moduleData = modules; diff --git a/src/commands/profile/modules/index.ts b/src/plugins/commands/profile/modules/index.ts similarity index 100% rename from src/commands/profile/modules/index.ts rename to src/plugins/commands/profile/modules/index.ts diff --git a/src/commands/profile/modules/view.ts b/src/plugins/commands/profile/modules/view/index.ts similarity index 92% rename from src/commands/profile/modules/view.ts rename to src/plugins/commands/profile/modules/view/index.ts index 5a59fd3..4fbd5c6 100644 --- a/src/commands/profile/modules/view.ts +++ b/src/plugins/commands/profile/modules/view/index.ts @@ -2,12 +2,12 @@ import { CommandInteraction } from "discord.js"; // Configurations -import getEmbedConfig from "../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; // Models -import fetchUser from "../../../helpers/fetchUser"; +import fetchUser from "../../../../../helpers/fetchUser"; -import logger from "../../../logger"; +import logger from "../../../../../logger"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -24,7 +24,6 @@ export default { }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { successColor, footerText, footerIcon } = await getEmbedConfig( interaction.guild ); // Destructure diff --git a/src/commands/reputation/index.ts b/src/plugins/commands/reputation/index.ts similarity index 71% rename from src/commands/reputation/index.ts rename to src/plugins/commands/reputation/index.ts index b0812d4..ae1876e 100644 --- a/src/commands/reputation/index.ts +++ b/src/plugins/commands/reputation/index.ts @@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js"; import modules from "./modules"; // Handlers -import logger from "../../logger"; +import logger from "../../../logger"; export const moduleData = modules; @@ -17,13 +17,7 @@ export const builder = new SlashCommandBuilder() .addSubcommand(modules.give.builder); export const execute = async (interaction: CommandInteraction) => { - const { options } = interaction; - - if (options?.getSubcommand() === "give") { - logger?.silly(`Executing give subcommand`); - + if (interaction.options.getSubcommand() === "give") { await modules.give.execute(interaction); } - - logger?.silly(`No subcommand found`); }; diff --git a/src/plugins/commands/reputation/modules/give/components/noSelfReputation.ts b/src/plugins/commands/reputation/modules/give/components/noSelfReputation.ts new file mode 100644 index 0000000..f6a6778 --- /dev/null +++ b/src/plugins/commands/reputation/modules/give/components/noSelfReputation.ts @@ -0,0 +1,6 @@ +import { User } from "discord.js"; +export default async (to: User | null, from: User | null) => { + if (from?.id === to?.id) { + throw new Error("You cannot give reputation to yourself."); + } +}; diff --git a/src/plugins/commands/reputation/modules/give/index.ts b/src/plugins/commands/reputation/modules/give/index.ts new file mode 100644 index 0000000..c899ecb --- /dev/null +++ b/src/plugins/commands/reputation/modules/give/index.ts @@ -0,0 +1,87 @@ +import { CommandInteraction } from "discord.js"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; +import { timeout } from "../../../../../config/reputation"; +import logger from "../../../../../logger"; +import fetchUser from "../../../../../helpers/fetchUser"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import * as cooldown from "../../../../../helpers/cooldown"; +import noSelfReputation from "./components/noSelfReputation"; + +export default { + metadata: { guildOnly: true, ephemeral: true }, + + builder: (command: SlashCommandSubcommandBuilder) => { + return command + .setName("give") + .setDescription("Give reputation to a user") + .addUserOption((option) => + option + .setName("target") + .setDescription("The user you want to repute.") + .setRequired(true) + ) + .addStringOption((option) => + option + .setName("type") + .setDescription("What type of reputation you want to repute") + .setRequired(true) + .addChoices( + { name: "Positive", value: "positive" }, + { + name: "Negative", + value: "negative", + } + ) + ); + }, + execute: async (interaction: CommandInteraction) => { + const { options, user, guild } = interaction; + + const { errorColor, successColor, footerText, footerIcon } = + await getEmbedConfig(guild); // Destructure + + const optionTarget = options?.getUser("target"); + const optionType = options?.getString("type"); + + if (!guild) throw new Error("Guild is undefined"); + + const userObj = await fetchUser(user, guild); + if (!userObj) throw new Error("User is undefined"); + + // Pre-checks + await noSelfReputation(optionTarget, user); + + // Check if user is on cooldown otherwise create one + await cooldown.interaction(interaction, timeout); + + switch (optionType) { + case "positive": + userObj.reputation += 1; + break; + case "negative": + userObj.reputation += 1; + break; + default: + throw new Error("Invalid reputation type"); + } + + await userObj.save().then(async () => { + logger.silly(`User reputation has been updated`); + + await interaction.editReply({ + embeds: [ + { + title: "[:loudspeaker:] Give", + description: `You have given a ${optionType} repute to ${optionTarget}`, + timestamp: new Date(), + color: successColor, + footer: { + iconURL: footerIcon, + text: footerText, + }, + }, + ], + }); + }); + }, +}; diff --git a/src/commands/reputation/modules/index.ts b/src/plugins/commands/reputation/modules/index.ts similarity index 100% rename from src/commands/reputation/modules/index.ts rename to src/plugins/commands/reputation/modules/index.ts diff --git a/src/commands/shop/index.ts b/src/plugins/commands/shop/index.ts similarity index 74% rename from src/commands/shop/index.ts rename to src/plugins/commands/shop/index.ts index 2ef6f65..697c94e 100644 --- a/src/commands/shop/index.ts +++ b/src/plugins/commands/shop/index.ts @@ -6,7 +6,7 @@ import { CommandInteraction } from "discord.js"; import modules from "./modules"; // Handlers -import logger from "../../logger"; +import logger from "../../../logger"; export const moduleData = modules; @@ -14,16 +14,16 @@ export const moduleData = modules; export const builder = new SlashCommandBuilder() .setName("shop") .setDescription("Shop for credits and custom roles.") - .addSubcommand(modules.pterodactyl.builder) + .addSubcommand(modules.cpgg.builder) .addSubcommandGroup(modules.roles.builder); export const execute = async (interaction: CommandInteraction) => { const { options } = interaction; - if (options?.getSubcommand() === "pterodactyl") { - logger.silly(`Executing pterodactyl subcommand`); + if (options?.getSubcommand() === "cpgg") { + logger.silly(`Executing cpgg subcommand`); - return modules.pterodactyl.execute(interaction); + return modules.cpgg.execute(interaction); } if (options?.getSubcommandGroup() === "roles") { diff --git a/src/commands/shop/modules/pterodactyl.ts b/src/plugins/commands/shop/modules/cpgg/index.ts similarity index 65% rename from src/commands/shop/modules/pterodactyl.ts rename to src/plugins/commands/shop/modules/cpgg/index.ts index 207774c..aeee472 100644 --- a/src/commands/shop/modules/pterodactyl.ts +++ b/src/plugins/commands/shop/modules/cpgg/index.ts @@ -1,25 +1,30 @@ -import { CommandInteraction } from "discord.js"; +import { + CommandInteraction, + MessageActionRow, + MessageButton, +} from "discord.js"; import { v4 as uuidv4 } from "uuid"; import axios from "axios"; -import getEmbedConfig from "../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; -import logger from "../../../logger"; -import encryption from "../../../handlers/encryption"; +import logger from "../../../../../logger"; +import encryption from "../../../../../handlers/encryption"; -import pluralize from "../../../helpers/pluralize"; +import pluralize from "../../../../../helpers/pluralize"; -import apiSchema from "../../../models/api"; -import fetchUser from "../../../helpers/fetchUser"; +import apiSchema from "../../../../../models/api"; +import fetchUser from "../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import { message } from "../../../../../helpers/cooldown/index"; export default { metadata: { guildOnly: true, ephemeral: true }, builder: (command: SlashCommandSubcommandBuilder) => { return command - .setName("pterodactyl") - .setDescription("Buy pterodactyl power.") + .setName("cpgg") + .setDescription("Buy cpgg power.") .addIntegerOption((option) => option .setName("amount") @@ -27,7 +32,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild, user, client } = interaction; @@ -71,8 +75,8 @@ export default { return interaction?.editReply({ embeds: [ { - title: ":shopping_cart: Shop [Pterodactyl]", - description: `You **can't** withdraw for __Pterodactyl__ below **100**.`, + title: "[:shopping_cart:] CPGG", + description: `You **can't** withdraw for __CPGG__ below **100**.`, color: errorColor, fields: [ { @@ -96,9 +100,9 @@ export default { return interaction?.editReply({ embeds: [ { - title: ":shopping_cart: Shop [Pterodactyl]", + title: "[:shopping_cart:] CPGG", description: - "You **can't** withdraw for __Pterodactyl__ above **1.000.000**.", + "You **can't** withdraw for __CPGG__ above **1.000.000**.", color: errorColor, fields: [ { @@ -122,7 +126,7 @@ export default { return interaction?.editReply({ embeds: [ { - title: ":shopping_cart: Shop [Pterodactyl]", + title: "[:shopping_cart:] CPGG", description: `You have **insufficient** credits.`, color: errorColor, fields: [ @@ -150,13 +154,21 @@ export default { if (!apiCredentials) return; const api = axios?.create({ - baseURL: apiCredentials.url, + baseURL: `${apiCredentials.url}/api/`, headers: { Authorization: `Bearer ${encryption.decrypt(apiCredentials.token)}`, }, }); - const shopUrl = apiCredentials?.url?.replace("/api", "/store"); + const shopUrl = `${apiCredentials?.url}/store`; + + const buttons = new MessageActionRow().addComponents( + new MessageButton() + .setLabel("Redeem it here") + .setStyle("LINK") + .setEmoji("🏦") + .setURL(`${shopUrl}?voucher=${code}`) + ); await api @@ -178,43 +190,47 @@ export default { ?.then(async () => { logger?.silly(`Successfully saved new credits.`); - await dmUser?.send({ - embeds: [ - { - title: ":shopping_cart: Shop [Pterodactyl]", - description: `Redeem this voucher [here](${shopUrl})!`, - fields: [ - { name: "Code", value: `${code}`, inline: true }, + if (!interaction.guild) throw new Error("Guild is undefined"); + + await dmUser + ?.send({ + embeds: [ + { + title: "[:shopping_cart:] CPGG", + description: `This voucher comes from **${interaction.guild.name}**.`, + fields: [ + { + name: "💶 Credits", + value: `${optionAmount || userDB?.credits}`, + inline: true, + }, + ], + color: successColor, + timestamp: new Date(), + footer: { + iconURL: footerIcon, + text: footerText, + }, + }, + ], + components: [buttons], + }) + .then(async (msg) => { + return interaction?.editReply({ + embeds: [ { - name: "Credits", - value: `${optionAmount || userDB?.credits}`, - inline: true, + title: "[:shopping_cart:] CPGG", + description: `I have sent you the code in [DM](${msg.url})!`, + color: successColor, + timestamp: new Date(), + footer: { + iconURL: footerIcon, + text: footerText, + }, }, ], - color: successColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - - return interaction?.editReply({ - embeds: [ - { - title: ":shopping_cart: Shop [Pterodactyl]", - description: "I have sent you the code in DM!", - color: successColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); + }); + }); }) .catch(async (error) => { @@ -223,7 +239,7 @@ export default { return interaction?.editReply({ embeds: [ { - title: ":shopping_cart: Shop [Pterodactyl]", + title: "[:shopping_cart:] CPGG", description: "Something went wrong.", color: errorColor, timestamp: new Date(), @@ -243,7 +259,7 @@ export default { return interaction?.editReply({ embeds: [ { - title: ":shopping_cart: Shop [Pterodactyl]", + title: "[:shopping_cart:] CPGG", description: "Something went wrong.", color: errorColor, timestamp: new Date(), diff --git a/src/plugins/commands/shop/modules/index.ts b/src/plugins/commands/shop/modules/index.ts new file mode 100644 index 0000000..c3df89d --- /dev/null +++ b/src/plugins/commands/shop/modules/index.ts @@ -0,0 +1,4 @@ +import cpgg from "./cpgg"; +import * as roles from "./roles"; + +export default { cpgg, roles }; diff --git a/src/commands/shop/modules/roles/index.ts b/src/plugins/commands/shop/modules/roles/index.ts similarity index 89% rename from src/commands/shop/modules/roles/index.ts rename to src/plugins/commands/shop/modules/roles/index.ts index 7d6cb90..f16a962 100644 --- a/src/commands/shop/modules/roles/index.ts +++ b/src/plugins/commands/shop/modules/roles/index.ts @@ -3,14 +3,14 @@ import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; // Handlers -import logger from "../../../../logger"; +import logger from "../../../../../logger"; -import getEmbedConfig from "../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; // Modules import modules from "./modules"; -import guildSchema from "../../../../models/guild"; +import guildSchema from "../../../../../models/guild"; export const moduleData = modules; diff --git a/src/commands/shop/modules/roles/modules/buy.ts b/src/plugins/commands/shop/modules/roles/modules/buy/index.ts similarity index 90% rename from src/commands/shop/modules/roles/modules/buy.ts rename to src/plugins/commands/shop/modules/roles/modules/buy/index.ts index 3d8ff54..23f91dc 100644 --- a/src/commands/shop/modules/roles/modules/buy.ts +++ b/src/plugins/commands/shop/modules/roles/modules/buy/index.ts @@ -6,17 +6,17 @@ import { } from "discord.js"; // Configurations -import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Models -import shopRolesSchema from "../../../../../models/shopRole"; -import guildSchema from "../../../../../models/guild"; +import shopRolesSchema from "../../../../../../../models/shopRole"; +import guildSchema from "../../../../../../../models/guild"; -import logger from "../../../../../logger"; +import logger from "../../../../../../../logger"; // Helpers -import pluralize from "../../../../../helpers/pluralize"; -import fetchUser from "../../../../../helpers/fetchUser"; +import pluralize from "../../../../../../../helpers/pluralize"; +import fetchUser from "../../../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -41,7 +41,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild, user, member } = interaction; diff --git a/src/commands/shop/modules/roles/modules/cancel.ts b/src/plugins/commands/shop/modules/roles/modules/cancel/index.ts similarity index 89% rename from src/commands/shop/modules/roles/modules/cancel.ts rename to src/plugins/commands/shop/modules/roles/modules/cancel/index.ts index d8b4314..5ff483e 100644 --- a/src/commands/shop/modules/roles/modules/cancel.ts +++ b/src/plugins/commands/shop/modules/roles/modules/cancel/index.ts @@ -2,16 +2,16 @@ import { CommandInteraction, GuildMemberRoleManager } from "discord.js"; // Configurations -import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../../../helpers/getEmbedConfig"; // Models -import shopRolesSchema from "../../../../../models/shopRole"; +import shopRolesSchema from "../../../../../../../models/shopRole"; -import logger from "../../../../../logger"; +import logger from "../../../../../../../logger"; // Helpers -import pluralize from "../../../../../helpers/pluralize"; -import fetchUser from "../../../../../helpers/fetchUser"; +import pluralize from "../../../../../../../helpers/pluralize"; +import fetchUser from "../../../../../../../helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function @@ -30,7 +30,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const { options, guild, user, member } = interaction; diff --git a/src/commands/shop/modules/roles/modules/index.ts b/src/plugins/commands/shop/modules/roles/modules/index.ts similarity index 100% rename from src/commands/shop/modules/roles/modules/index.ts rename to src/plugins/commands/shop/modules/roles/modules/index.ts diff --git a/src/commands/utility/index.ts b/src/plugins/commands/utility/index.ts similarity index 73% rename from src/commands/utility/index.ts rename to src/plugins/commands/utility/index.ts index 6fb52db..c97ef47 100644 --- a/src/commands/utility/index.ts +++ b/src/plugins/commands/utility/index.ts @@ -1,16 +1,9 @@ -// Dependencies import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; -// Modules -import modules from "../../commands/utility/modules"; - -// Handlers -import logger from "../../logger"; - +import modules from "./modules"; export const moduleData = modules; -// Function export const builder = new SlashCommandBuilder() .setName("utility") .setDescription("Common utility.") @@ -21,9 +14,7 @@ export const builder = new SlashCommandBuilder() .addSubcommand(modules.avatar.builder); export const execute = async (interaction: CommandInteraction) => { - const { options } = interaction; - - switch (options.getSubcommand()) { + switch (interaction.options.getSubcommand()) { case "lookup": return modules.lookup.execute(interaction); case "about": @@ -33,6 +24,8 @@ export const execute = async (interaction: CommandInteraction) => { case "avatar": return modules.avatar.execute(interaction); default: - logger.error(`Unknown subcommand ${options.getSubcommand()}`); + throw new Error( + `Unknown subcommand: ${interaction.options.getSubcommand()}` + ); } }; diff --git a/src/plugins/commands/utility/modules/about/index.ts b/src/plugins/commands/utility/modules/about/index.ts new file mode 100644 index 0000000..f89d74d --- /dev/null +++ b/src/plugins/commands/utility/modules/about/index.ts @@ -0,0 +1,71 @@ +// Dependencies +import { + CommandInteraction, + MessageActionRow, + MessageButton, +} from "discord.js"; + +// Configurations +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; + +import { hosterName, hosterUrl } from "../../../../../config/other"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; + +// Function +export default { + metadata: { guildOnly: false, ephemeral: false }, + + builder: (command: SlashCommandSubcommandBuilder) => { + return command.setName("about").setDescription("About this bot!)"); + }, + execute: async (interaction: CommandInteraction) => { + const { successColor, footerText, footerIcon } = await getEmbedConfig( + interaction.guild + ); + + const buttons = new MessageActionRow().addComponents( + new MessageButton() + .setLabel("Source Code") + .setStyle("LINK") + .setEmoji("📄") + .setURL("https://github.com/ZynerOrg/xyter"), + new MessageButton() + .setLabel("Website") + .setStyle("LINK") + .setEmoji("🌐") + .setURL("https://zyner.org"), + new MessageButton() + .setLabel("Get Help") + .setStyle("LINK") + .setEmoji("💬") + .setURL("https://discord.zyner.org"), + new MessageButton() + .setLabel(`Hosted by ${hosterName}`) + .setStyle("LINK") + .setEmoji("⚒️") + .setURL(`${hosterUrl}`) + ); + + const interactionEmbed = { + title: "[:tools:] About", + description: ` + **Xyter**'s goal is to provide a __privacy-friendly__ discord bot. + We created **Xyter** to **replace the mess** of having a dozen or so bots in __your__ community. + On top of this, you can also see our **source code** for **security** and **privacy** issues. + As well as making your own **fork** of the bot, you can also get **help** from our community. + + Developed with ❤️ by **Zyner**, a non-profit project by teens. + `, + color: successColor, + timestamp: new Date(), + footer: { + iconURL: footerIcon, + text: footerText, + }, + }; + await interaction.editReply({ + embeds: [interactionEmbed], + components: [buttons], + }); + }, +}; diff --git a/src/commands/utility/modules/avatar.ts b/src/plugins/commands/utility/modules/avatar/index.ts similarity index 91% rename from src/commands/utility/modules/avatar.ts rename to src/plugins/commands/utility/modules/avatar/index.ts index 57110cf..24ab6c1 100644 --- a/src/commands/utility/modules/avatar.ts +++ b/src/plugins/commands/utility/modules/avatar/index.ts @@ -1,4 +1,4 @@ -import getEmbedConfig from "../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import { CommandInteraction, MessageEmbed } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; @@ -17,7 +17,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { successColor, footerText, footerIcon } = await getEmbedConfig( interaction.guild ); diff --git a/src/commands/utility/modules/index.ts b/src/plugins/commands/utility/modules/index.ts similarity index 100% rename from src/commands/utility/modules/index.ts rename to src/plugins/commands/utility/modules/index.ts diff --git a/src/commands/utility/modules/lookup.ts b/src/plugins/commands/utility/modules/lookup/index.ts similarity index 95% rename from src/commands/utility/modules/lookup.ts rename to src/plugins/commands/utility/modules/lookup/index.ts index a5825f9..44abe14 100644 --- a/src/commands/utility/modules/lookup.ts +++ b/src/plugins/commands/utility/modules/lookup/index.ts @@ -1,11 +1,11 @@ import axios from "axios"; import { CommandInteraction } from "discord.js"; -import getEmbedConfig from "../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import embedBuilder from "../../../helpers/embedBuilder"; +import embedBuilder from "../../../../../helpers/embedBuilder"; export default { metadata: { guildOnly: false, ephemeral: false }, @@ -24,7 +24,6 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { errorColor, successColor, footerText, footerIcon } = await getEmbedConfig(interaction.guild); const embedTitle = "[:hammer:] Utility (Lookup)"; diff --git a/src/commands/utility/modules/stats.ts b/src/plugins/commands/utility/modules/stats/index.ts similarity index 95% rename from src/commands/utility/modules/stats.ts rename to src/plugins/commands/utility/modules/stats/index.ts index 805b948..a2a8282 100644 --- a/src/commands/utility/modules/stats.ts +++ b/src/plugins/commands/utility/modules/stats/index.ts @@ -1,4 +1,4 @@ -import getEmbedConfig from "../../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../../../helpers/getEmbedConfig"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; @@ -9,7 +9,6 @@ export default { return command.setName("stats").setDescription("Check bot statistics!)"); }, execute: async (interaction: CommandInteraction) => { - if (interaction.guild == null) return; const { successColor, footerText, footerIcon } = await getEmbedConfig( interaction.guild ); diff --git a/src/events/guildCreate/index.ts b/src/plugins/events/guildCreate/index.ts similarity index 60% rename from src/events/guildCreate/index.ts rename to src/plugins/events/guildCreate/index.ts index a309884..60d5a3c 100644 --- a/src/events/guildCreate/index.ts +++ b/src/plugins/events/guildCreate/index.ts @@ -1,8 +1,8 @@ import { Guild } from "discord.js"; -import updatePresence from "../../helpers/updatePresence"; -import fetchGuild from "../../helpers/fetchGuild"; -import logger from "../../logger"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import updatePresence from "../../../helpers/updatePresence"; +import fetchGuild from "../../../helpers/fetchGuild"; +import logger from "../../../logger"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", diff --git a/src/events/guildDelete/index.ts b/src/plugins/events/guildDelete/index.ts similarity index 64% rename from src/events/guildDelete/index.ts rename to src/plugins/events/guildDelete/index.ts index be090b8..146351f 100644 --- a/src/events/guildDelete/index.ts +++ b/src/plugins/events/guildDelete/index.ts @@ -2,10 +2,10 @@ import { Guild } from "discord.js"; // Dependencies -import updatePresence from "../../helpers/updatePresence"; -import dropGuild from "../../helpers/dropGuild"; -import logger from "../../logger"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import updatePresence from "../../../helpers/updatePresence"; +import dropGuild from "../../../helpers/dropGuild"; +import logger from "../../../logger"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", diff --git a/src/events/guildMemberAdd/audits.ts b/src/plugins/events/guildMemberAdd/audits.ts similarity index 91% rename from src/events/guildMemberAdd/audits.ts rename to src/plugins/events/guildMemberAdd/audits.ts index f2f3925..89c0384 100644 --- a/src/events/guildMemberAdd/audits.ts +++ b/src/plugins/events/guildMemberAdd/audits.ts @@ -1,9 +1,9 @@ -import logger from "../../logger"; +import logger from "../../../logger"; import { GuildMember, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (member: GuildMember) => { diff --git a/src/events/guildMemberAdd/index.ts b/src/plugins/events/guildMemberAdd/index.ts similarity index 72% rename from src/events/guildMemberAdd/index.ts rename to src/plugins/events/guildMemberAdd/index.ts index 156dfb7..8310579 100644 --- a/src/events/guildMemberAdd/index.ts +++ b/src/plugins/events/guildMemberAdd/index.ts @@ -2,14 +2,14 @@ import { GuildMember } from "discord.js"; // Dependencies -import updatePresence from "../../helpers/updatePresence"; -import fetchUser from "../../helpers/fetchUser"; -import logger from "../../logger"; +import updatePresence from "../../../helpers/updatePresence"; +import fetchUser from "../../../helpers/fetchUser"; +import logger from "../../../logger"; import joinMessage from "./joinMessage"; import audits from "./audits"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", diff --git a/src/events/guildMemberAdd/joinMessage.ts b/src/plugins/events/guildMemberAdd/joinMessage.ts similarity index 91% rename from src/events/guildMemberAdd/joinMessage.ts rename to src/plugins/events/guildMemberAdd/joinMessage.ts index 4ffc3d4..47f9043 100644 --- a/src/events/guildMemberAdd/joinMessage.ts +++ b/src/plugins/events/guildMemberAdd/joinMessage.ts @@ -1,8 +1,8 @@ import { GuildMember, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (member: GuildMember) => { diff --git a/src/events/guildMemberRemove/audits.ts b/src/plugins/events/guildMemberRemove/audits.ts similarity index 91% rename from src/events/guildMemberRemove/audits.ts rename to src/plugins/events/guildMemberRemove/audits.ts index 92c9df8..a940ca3 100644 --- a/src/events/guildMemberRemove/audits.ts +++ b/src/plugins/events/guildMemberRemove/audits.ts @@ -1,9 +1,9 @@ -import logger from "../../logger"; +import logger from "../../../logger"; import { GuildMember, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (member: GuildMember) => { diff --git a/src/events/guildMemberRemove/index.ts b/src/plugins/events/guildMemberRemove/index.ts similarity index 72% rename from src/events/guildMemberRemove/index.ts rename to src/plugins/events/guildMemberRemove/index.ts index 502aad4..b900a12 100644 --- a/src/events/guildMemberRemove/index.ts +++ b/src/plugins/events/guildMemberRemove/index.ts @@ -2,12 +2,12 @@ import { GuildMember } from "discord.js"; // Dependencies -import updatePresence from "../../helpers/updatePresence"; -import dropUser from "../../helpers/dropUser"; -import logger from "../../logger"; +import updatePresence from "../../../helpers/updatePresence"; +import dropUser from "../../../helpers/dropUser"; +import logger from "../../../logger"; import leaveMessage from "./leaveMessage"; import audits from "./audits"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", diff --git a/src/events/guildMemberRemove/leaveMessage.ts b/src/plugins/events/guildMemberRemove/leaveMessage.ts similarity index 91% rename from src/events/guildMemberRemove/leaveMessage.ts rename to src/plugins/events/guildMemberRemove/leaveMessage.ts index 5d3a36e..daae364 100644 --- a/src/events/guildMemberRemove/leaveMessage.ts +++ b/src/plugins/events/guildMemberRemove/leaveMessage.ts @@ -1,8 +1,8 @@ import { GuildMember, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (member: GuildMember) => { diff --git a/src/events/interactionCreate/audits.ts b/src/plugins/events/interactionCreate/audits.ts similarity index 84% rename from src/events/interactionCreate/audits.ts rename to src/plugins/events/interactionCreate/audits.ts index 3f68219..7c71f80 100644 --- a/src/events/interactionCreate/audits.ts +++ b/src/plugins/events/interactionCreate/audits.ts @@ -1,9 +1,9 @@ -import logger from "../../logger"; +import logger from "../../../logger"; import { Interaction, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (interaction: Interaction) => { @@ -38,10 +38,12 @@ export default { .setDescription( ` **Interaction created by** ${interaction.user.username} **in** ${interaction.channel} + ㅤ**Interaction ID**: ${interaction.id} + ㅤ**Type**: ${interaction.type} + ㅤ**User ID**: ${interaction.user.id} ` ) .setThumbnail(interaction.user.displayAvatarURL()) - .addFields([{ name: "Event", value: "interactionCreate" }]) .setTimestamp() .setFooter({ text: footerText, diff --git a/src/plugins/events/interactionCreate/components/checks.ts b/src/plugins/events/interactionCreate/components/checks.ts new file mode 100644 index 0000000..006a14f --- /dev/null +++ b/src/plugins/events/interactionCreate/components/checks.ts @@ -0,0 +1,51 @@ +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 new file mode 100644 index 0000000..eaf1605 --- /dev/null +++ b/src/plugins/events/interactionCreate/components/isButton.ts @@ -0,0 +1,123 @@ +// 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 new file mode 100644 index 0000000..3a14ad6 --- /dev/null +++ b/src/plugins/events/interactionCreate/components/isCommand.ts @@ -0,0 +1,48 @@ +// 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/index.ts b/src/plugins/events/interactionCreate/index.ts new file mode 100644 index 0000000..f7365ec --- /dev/null +++ b/src/plugins/events/interactionCreate/index.ts @@ -0,0 +1,51 @@ +// 3rd party dependencies +import { CommandInteraction, MessageEmbed } from "discord.js"; + +// Dependencies +import isCommand from "../../events/interactionCreate/components/isCommand"; +import isButton from "../../events/interactionCreate/components/isButton"; +import logger from "../../../logger"; +import audits from "./audits"; +import { IEventOptions } from "../../../interfaces/EventOptions"; +import capitalizeFirstLetter from "../../../helpers/capitalizeFirstLetter"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; + +export const options: IEventOptions = { + type: "on", +}; + +export const execute = async (interaction: CommandInteraction) => { + const { guild, id } = interaction; + + logger?.silly( + `New interaction: ${id} in guild: ${guild?.name} (${guild?.id})` + ); + + const { errorColor, footerText, footerIcon } = await getEmbedConfig( + interaction.guild + ); + + await audits.execute(interaction); + + try { + await isCommand(interaction); + await isButton(interaction); + } catch (error) { + logger.debug(`${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/events/messageCreate/index.ts b/src/plugins/events/messageCreate/index.ts similarity index 83% rename from src/events/messageCreate/index.ts rename to src/plugins/events/messageCreate/index.ts index f5beddf..06619f0 100644 --- a/src/events/messageCreate/index.ts +++ b/src/plugins/events/messageCreate/index.ts @@ -1,7 +1,7 @@ import { Message } from "discord.js"; import modules from "../../events/messageCreate/modules"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", diff --git a/src/events/messageCreate/modules/counters/index.ts b/src/plugins/events/messageCreate/modules/counters/index.ts similarity index 94% rename from src/events/messageCreate/modules/counters/index.ts rename to src/plugins/events/messageCreate/modules/counters/index.ts index 9dcfca5..1a3c7a3 100644 --- a/src/events/messageCreate/modules/counters/index.ts +++ b/src/plugins/events/messageCreate/modules/counters/index.ts @@ -1,7 +1,7 @@ import { Message } from "discord.js"; -import logger from "../../../../logger"; -import counterSchema from "../../../../models/counter"; +import logger from "../../../../../logger"; +import counterSchema from "../../../../../models/counter"; export default { execute: async (message: Message) => { diff --git a/src/plugins/events/messageCreate/modules/credits/index.ts b/src/plugins/events/messageCreate/modules/credits/index.ts new file mode 100644 index 0000000..24a5ce2 --- /dev/null +++ b/src/plugins/events/messageCreate/modules/credits/index.ts @@ -0,0 +1,46 @@ +import logger from "../../../../../logger"; +import { Message } from "discord.js"; + +import fetchUser from "../../../../../helpers/fetchUser"; +import fetchGuild from "../../../../../helpers/fetchGuild"; + +import * as cooldown from "../../../../../helpers/cooldown"; + +export default { + execute: async (message: Message) => { + const { guild, author, content, channel } = message; + + if (guild == null) return; + if (author.bot) return; + if (channel?.type !== "GUILD_TEXT") return; + + const { id: guildId } = guild; + const { id: userId } = author; + + const guildData = await fetchGuild(guild); + const userData = await fetchUser(author, guild); + + if (content.length < guildData.credits.minimumLength) return; + + await cooldown.message( + message, + guildData.credits.timeout, + "messageCreate-credits" + ); + + userData.credits += guildData.credits.rate; + + await userData + .save() + .then(async () => { + logger.silly( + `User ${userId} in guild ${guildId} has ${userData.credits} credits` + ); + }) + .catch(async (err) => { + logger.error( + `Error saving credits for user ${userId} in guild ${guildId} - ${err}` + ); + }); + }, +}; diff --git a/src/events/messageCreate/modules/index.ts b/src/plugins/events/messageCreate/modules/index.ts similarity index 100% rename from src/events/messageCreate/modules/index.ts rename to src/plugins/events/messageCreate/modules/index.ts diff --git a/src/plugins/events/messageCreate/modules/points/index.ts b/src/plugins/events/messageCreate/modules/points/index.ts new file mode 100644 index 0000000..4a4496b --- /dev/null +++ b/src/plugins/events/messageCreate/modules/points/index.ts @@ -0,0 +1,51 @@ +import logger from "../../../../../logger"; + +import * as cooldown from "../../../../../helpers/cooldown"; + +import fetchUser from "../../../../../helpers/fetchUser"; +import fetchGuild from "../../../../../helpers/fetchGuild"; + +import { Message } from "discord.js"; +export default { + execute: async (message: Message) => { + const { guild, author, content, channel } = message; + + if (guild == null) return; + if (author.bot) return; + if (channel?.type !== "GUILD_TEXT") return; + + const { id: guildId } = guild; + const { id: userId } = author; + + const guildData = await fetchGuild(guild); + const userData = await fetchUser(author, guild); + + if (content.length < guildData.credits.minimumLength) return; + + await cooldown.message( + message, + guildData.credits.timeout, + "messageCreate-points" + ); + + userData.points += guildData.points.rate; + + await userData + .save() + .then(async () => { + logger.silly( + `Successfully saved user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})` + ); + }) + .catch(async (err) => { + logger.error( + `Error saving points for user ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})`, + err + ); + }); + + logger.silly( + `User ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id}) has ${userData.points} points` + ); + }, +}; diff --git a/src/events/messageDelete/audits.ts b/src/plugins/events/messageDelete/audits.ts similarity index 91% rename from src/events/messageDelete/audits.ts rename to src/plugins/events/messageDelete/audits.ts index 0b8698e..4b5d5a2 100644 --- a/src/events/messageDelete/audits.ts +++ b/src/plugins/events/messageDelete/audits.ts @@ -1,9 +1,9 @@ -import logger from "../../logger"; +import logger from "../../../logger"; import { Message, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (message: Message) => { diff --git a/src/events/messageDelete/index.ts b/src/plugins/events/messageDelete/index.ts similarity index 82% rename from src/events/messageDelete/index.ts rename to src/plugins/events/messageDelete/index.ts index 7279add..3cb7f6e 100644 --- a/src/events/messageDelete/index.ts +++ b/src/plugins/events/messageDelete/index.ts @@ -1,7 +1,7 @@ import { Message } from "discord.js"; import audits from "../../events/messageDelete/audits"; import counter from "./modules/counter"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", diff --git a/src/events/messageDelete/modules/counter.ts b/src/plugins/events/messageDelete/modules/counter.ts similarity index 90% rename from src/events/messageDelete/modules/counter.ts rename to src/plugins/events/messageDelete/modules/counter.ts index 7fa9e46..5681da8 100644 --- a/src/events/messageDelete/modules/counter.ts +++ b/src/plugins/events/messageDelete/modules/counter.ts @@ -2,8 +2,8 @@ import { Message } from "discord.js"; // Models -import counterSchema from "../../../models/counter"; -import logger from "../../../logger"; +import counterSchema from "../../../../models/counter"; +import logger from "../../../../logger"; export default async (message: Message) => { const { guild, channel, author, content } = message; diff --git a/src/events/messageUpdate/audits.ts b/src/plugins/events/messageUpdate/audits.ts similarity index 92% rename from src/events/messageUpdate/audits.ts rename to src/plugins/events/messageUpdate/audits.ts index db75a84..515a090 100644 --- a/src/events/messageUpdate/audits.ts +++ b/src/plugins/events/messageUpdate/audits.ts @@ -1,10 +1,10 @@ /* eslint-disable no-loops/no-loops */ -import logger from "../../logger"; +import logger from "../../../logger"; import { Message, MessageEmbed, TextChannel } from "discord.js"; -import guildSchema from "../../models/guild"; +import guildSchema from "../../../models/guild"; -import getEmbedConfig from "../../helpers/getEmbedConfig"; +import getEmbedConfig from "../../../helpers/getEmbedConfig"; export default { execute: async (oldMessage: Message, newMessage: Message) => { diff --git a/src/events/messageUpdate/index.ts b/src/plugins/events/messageUpdate/index.ts similarity index 78% rename from src/events/messageUpdate/index.ts rename to src/plugins/events/messageUpdate/index.ts index 263d53e..23b5588 100644 --- a/src/events/messageUpdate/index.ts +++ b/src/plugins/events/messageUpdate/index.ts @@ -1,12 +1,12 @@ // Dependencies import { Message } from "discord.js"; -import logger from "../../logger"; +import logger from "../../../logger"; // Modules import counter from "./modules/counter"; import audits from "./audits"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "on", @@ -15,7 +15,7 @@ export const options: IEventOptions = { export const execute = async (oldMessage: Message, newMessage: Message) => { const { author, guild } = newMessage; - await audits.execute(oldMessage, newMessage); + // await audits.execute(oldMessage, newMessage); logger?.silly( `Message update event fired by ${author.tag} (${author.id}) in guild: ${guild?.name} (${guild?.id})` diff --git a/src/events/messageUpdate/modules/counter.ts b/src/plugins/events/messageUpdate/modules/counter.ts similarity index 89% rename from src/events/messageUpdate/modules/counter.ts rename to src/plugins/events/messageUpdate/modules/counter.ts index 9a79118..03c60b9 100644 --- a/src/events/messageUpdate/modules/counter.ts +++ b/src/plugins/events/messageUpdate/modules/counter.ts @@ -2,8 +2,8 @@ import { Message } from "discord.js"; // Models -import counterSchema from "../../../models/counter"; -import logger from "../../../logger"; +import counterSchema from "../../../../models/counter"; +import logger from "../../../../logger"; export default async (message: Message) => { const { guild, channel, author, content } = message; diff --git a/src/plugins/events/rateLimit/index.ts b/src/plugins/events/rateLimit/index.ts new file mode 100644 index 0000000..8886c1a --- /dev/null +++ b/src/plugins/events/rateLimit/index.ts @@ -0,0 +1,14 @@ +// Dependencies +import { Client } from "discord.js"; +import logger from "../../../logger"; + +// Helpers +import { IEventOptions } from "../../../interfaces/EventOptions"; + +export const options: IEventOptions = { + type: "on", +}; + +export const execute = async (client: Client) => { + logger.warn("Discord's API client is rate-limited!"); +}; diff --git a/src/events/ready/index.ts b/src/plugins/events/ready/index.ts similarity index 53% rename from src/events/ready/index.ts rename to src/plugins/events/ready/index.ts index bd1e060..125bdc8 100644 --- a/src/events/ready/index.ts +++ b/src/plugins/events/ready/index.ts @@ -1,12 +1,12 @@ // Dependencies import { Client } from "discord.js"; -import logger from "../../logger"; +import logger from "../../../logger"; // Helpers -import updatePresence from "../../helpers/updatePresence"; -import deployCommands from "../../handlers/deployCommands"; -import devMode from "../../handlers/devMode"; -import { IEventOptions } from "../../interfaces/EventOptions"; +import updatePresence from "../../../helpers/updatePresence"; +import deployCommands from "../../../handlers/deployCommands"; +import devMode from "../../../handlers/devMode"; +import { IEventOptions } from "../../../interfaces/EventOptions"; export const options: IEventOptions = { type: "once", diff --git a/tsconfig.json b/tsconfig.json index 0879e1c..83ed3fe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,21 +14,7 @@ "outDir": "./build", "resolveJsonModule": true, "baseUrl": "./src", - "typeRoots": ["/types/common", "./node_modules/@types"], - "paths": { - "@interface/*": ["interfaces/*"], - "@root/*": ["*"], - "@config/*": ["config/*"], - "@events/*": ["events/*"], - "@logger": ["logger"], - "@database": ["database"], - "@jobs/*": ["jobs/*"], - "@handlers/*": ["handlers/*"], - "@helpers/*": ["helpers/*"], - "@locale": ["locale"], - "@plugins/*": ["plugins/*"], - "@schemas/*": ["database/schemas/*"] - } + "typeRoots": ["/types/common", "./node_modules/@types"] }, "include": ["./src"], "exclude": ["./node_modules", "./test"]