diff --git a/package.json b/package.json index dd1008a..19e6995 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "src/index.ts", "scripts": { "test": "jest", - "start": "nodemon | pino-pretty -i pid,hostname -t yyyy-mm-dd HH:MM:s", + "start": "node .", "prettier-format": "prettier \"src/**/*.ts\" --write", "lint": "eslint ./src --ext .ts", "prepare": "husky install" @@ -27,6 +27,7 @@ "email": "vermium@zyner.org" }, "dependencies": { + "@crowdin/ota-client": "^0.7.0", "@discordjs/builders": "^0.13.0", "@discordjs/rest": "^0.4.0", "axios": "^0.27.0", @@ -36,6 +37,9 @@ "discord-api-types": "^0.33.0", "discord.js": "^13.6.0", "i18next": "^21.6.13", + "i18next-async-backend": "^2.0.0", + "i18next-http-backend": "^1.4.0", + "i18next-resources-to-backend": "^1.0.0", "mongoose": "^6.2.3", "node-schedule": "^2.1.0", "ts-node": "^10.7.0", diff --git a/src/database/index.ts b/src/database/index.ts index 4fd3870..af3b524 100644 --- a/src/database/index.ts +++ b/src/database/index.ts @@ -8,13 +8,15 @@ import logger from "@logger"; import { url } from "@config/database"; export default async () => { - 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}`); }); - mongoose.connection.on("error", (error) => { - logger?.error(error); + + mongoose.connection.on("error", async (error) => { + logger.error(error); }); - mongoose.connection.on("warn", (warning) => { - logger?.warn(warning); + + mongoose.connection.on("warn", async (warning) => { + logger.warn(warning); }); }; diff --git a/src/events/guildCreate/index.ts b/src/events/guildCreate/index.ts index a55d9d5..31e87b2 100644 --- a/src/events/guildCreate/index.ts +++ b/src/events/guildCreate/index.ts @@ -7,7 +7,6 @@ import fetchGuild from "@helpers/fetchGuild"; import logger from "@logger"; export default { - name: "guildCreate", async execute(guild: Guild) { const { client } = guild; @@ -15,5 +14,7 @@ export default { await fetchGuild(guild); await updatePresence(client); + + logger.silly(`guildCreate: ${guild}`); }, }; diff --git a/src/events/guildDelete/index.ts b/src/events/guildDelete/index.ts index 9a0ceec..6da6f4a 100644 --- a/src/events/guildDelete/index.ts +++ b/src/events/guildDelete/index.ts @@ -7,7 +7,6 @@ import dropGuild from "@helpers/dropGuild"; import logger from "@logger"; export default { - name: "guildDelete", async execute(guild: Guild) { const { client } = guild; @@ -15,5 +14,7 @@ export default { await dropGuild(guild); await updatePresence(client); + + logger.silly(`guildDelete: ${guild}`); }, }; diff --git a/src/events/guildMemberAdd/audits.ts b/src/events/guildMemberAdd/audits.ts index 6d76893..af99a77 100644 --- a/src/events/guildMemberAdd/audits.ts +++ b/src/events/guildMemberAdd/audits.ts @@ -20,24 +20,35 @@ export default { if (channel === null) return; - (channel as TextChannel).send({ - embeds: [ - new MessageEmbed() - .setColor(successColor) - .setAuthor({ - name: "Member Joined", - iconURL: member.user.displayAvatarURL(), - }) - .setDescription(`${member.user} ${member.user.tag}`) - .addFields([ - { name: "Account Age", value: `${member.user.createdAt}` }, - ]) - .setTimestamp() - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), - ], - }); + (channel as TextChannel) + .send({ + embeds: [ + new MessageEmbed() + .setColor(successColor) + .setAuthor({ + name: "Member Joined", + iconURL: member.user.displayAvatarURL(), + }) + .setDescription(`${member.user} ${member.user.tag}`) + .addFields([ + { name: "Account Age", value: `${member.user.createdAt}` }, + ]) + .setTimestamp() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }), + ], + }) + .then(async () => { + logger.info( + `Audit log sent for event guildMemberAdd in guild ${member.guild.name} (${member.guild.id})` + ); + }) + .catch(async () => { + logger.error( + `Audit log failed to send for event guildMemberAdd in guild ${member.guild.name} (${member.guild.id})` + ); + }); }, }; diff --git a/src/events/guildMemberAdd/index.ts b/src/events/guildMemberAdd/index.ts index 4b2cbe0..b99d7ad 100644 --- a/src/events/guildMemberAdd/index.ts +++ b/src/events/guildMemberAdd/index.ts @@ -9,7 +9,6 @@ import joinMessage from "../guildMemberAdd/joinMessage"; import audits from "../guildMemberAdd/audits"; export default { - name: "guildMemberAdd", async execute(member: GuildMember) { const { client, user, guild } = member; diff --git a/src/events/guildMemberRemove/audits.ts b/src/events/guildMemberRemove/audits.ts index e096814..3bbd8aa 100644 --- a/src/events/guildMemberRemove/audits.ts +++ b/src/events/guildMemberRemove/audits.ts @@ -20,21 +20,32 @@ export default { if (channel === null) return; - (channel as TextChannel).send({ - embeds: [ - new MessageEmbed() - .setColor(errorColor) - .setAuthor({ - name: "Member Left", - iconURL: member.user.displayAvatarURL(), - }) - .setDescription(`${member.user} ${member.user.tag}`) - .setTimestamp() - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), - ], - }); + (channel as TextChannel) + .send({ + embeds: [ + new MessageEmbed() + .setColor(errorColor) + .setAuthor({ + name: "Member Left", + iconURL: member.user.displayAvatarURL(), + }) + .setDescription(`${member.user} ${member.user.tag}`) + .setTimestamp() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }), + ], + }) + .then(async () => { + logger.info( + `Audit log sent for event guildMemberRemove in guild ${member.guild.name} (${member.guild.id})` + ); + }) + .catch(async () => { + logger.error( + `Audit log failed to send for event guildMemberRemove in guild ${member.guild.name} (${member.guild.id})` + ); + }); }, }; diff --git a/src/events/guildMemberRemove/index.ts b/src/events/guildMemberRemove/index.ts index e2fe667..332f143 100644 --- a/src/events/guildMemberRemove/index.ts +++ b/src/events/guildMemberRemove/index.ts @@ -9,7 +9,6 @@ import leaveMessage from "./leaveMessage"; import audits from "./audits"; export default { - name: "guildMemberRemove", async execute(member: GuildMember) { const { client, user, guild } = member; diff --git a/src/events/interactionCreate/audits.ts b/src/events/interactionCreate/audits.ts index 5fe29e6..d91348e 100644 --- a/src/events/interactionCreate/audits.ts +++ b/src/events/interactionCreate/audits.ts @@ -26,23 +26,34 @@ export default { if (channel === null) return; - (channel as TextChannel).send({ - embeds: [ - new MessageEmbed() - .setColor(successColor) - .setDescription( - ` + (channel as TextChannel) + .send({ + embeds: [ + new MessageEmbed() + .setColor(successColor) + .setDescription( + ` **Interaction created by** ${interaction.user.username} **in** ${interaction.channel} ` - ) - .setThumbnail(interaction.user.displayAvatarURL()) - .addFields([{ name: "Event", value: "interactionCreate" }]) - .setTimestamp() - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), - ], - }); + ) + .setThumbnail(interaction.user.displayAvatarURL()) + .addFields([{ name: "Event", value: "interactionCreate" }]) + .setTimestamp() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }), + ], + }) + .then(async () => { + logger.info( + `Audit log sent for event interactionCreate in guild ${interaction?.guild?.name} (${interaction?.guild?.id})` + ); + }) + .catch(async () => { + logger.error( + `Audit log failed to send for event interactionCreate in guild ${interaction?.guild?.name} (${interaction?.guild?.id})` + ); + }); }, }; diff --git a/src/events/interactionCreate/components/isCommand.ts b/src/events/interactionCreate/components/isCommand.ts index ac1e19d..93902fe 100644 --- a/src/events/interactionCreate/components/isCommand.ts +++ b/src/events/interactionCreate/components/isCommand.ts @@ -4,16 +4,83 @@ import { CommandInteraction, MessageEmbed } from "discord.js"; import logger from "@logger"; import { errorColor, footerText, footerIcon } from "@config/embed"; +import i18next from "i18next"; +import deferReply from "@root/helpers/deferReply"; +import getCommandMeta from "@root/helpers/getCommandMeta"; export default async (interaction: CommandInteraction) => { if (!interaction.isCommand()) return; - const { client, guild, commandName, user } = interaction; + const { client, guild, commandName, user, memberPermissions } = interaction; const currentCommand = client.commands.get(commandName); - if (!currentCommand) return; - await interaction.deferReply({ ephemeral: true }); + if (currentCommand == null) { + logger.silly(`Command ${commandName} not found`); + } + + const meta = await getCommandMeta(interaction, currentCommand); + + await deferReply(interaction, meta.ephemeral || false); + + if ( + meta.permissions && + meta.guildOnly && + !memberPermissions?.has(meta.permissions) + ) { + return interaction?.editReply({ + embeds: [ + new MessageEmbed() + .setTitle("[:toolbox:] Manage") + .setDescription(`You do not have the permission to manage the bot.`) + .setTimestamp(new Date()) + .setColor(errorColor) + .setFooter({ text: footerText, iconURL: footerIcon }), + ], + }); + } + + if (meta.guildOnly) { + if (!guild) { + logger.verbose(`Guild is null`); + + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setDescription( + i18next.t("guildOnly", { + lng: interaction.locale, + ns: "errors", + }) + ) + .setColor(errorColor) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }), + ], + }); + } + } + + if (meta.dmOnly) { + if (guild) { + logger.verbose(`Guild exist`); + + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setDescription( + i18next.t("dmOnly", { + lng: interaction.locale, + ns: "errors", + }) + ) + .setColor(errorColor) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }), + ], + }); + } + } await currentCommand .execute(interaction) @@ -23,7 +90,7 @@ export default async (interaction: CommandInteraction) => { ); }) .catch(async (error: any) => { - logger?.error(error); + logger?.error(`${error}`); return interaction.editReply({ embeds: [ diff --git a/src/events/interactionCreate/index.ts b/src/events/interactionCreate/index.ts index fb35bc9..befd29f 100644 --- a/src/events/interactionCreate/index.ts +++ b/src/events/interactionCreate/index.ts @@ -7,7 +7,6 @@ import logger from "@logger"; import audits from "./audits"; export default { - name: "interactionCreate", async execute(interaction: CommandInteraction) { const { guild, id } = interaction; diff --git a/src/events/messageCreate/index.ts b/src/events/messageCreate/index.ts index 029568a..004ca71 100644 --- a/src/events/messageCreate/index.ts +++ b/src/events/messageCreate/index.ts @@ -2,7 +2,6 @@ import { Message } from "discord.js"; import modules from "@events/messageCreate/modules"; export default { - name: "messageCreate", async execute(message: Message) { await modules.credits.execute(message); await modules.points.execute(message); diff --git a/src/events/messageDelete/audits.ts b/src/events/messageDelete/audits.ts index f7f0ed1..d35cf37 100644 --- a/src/events/messageDelete/audits.ts +++ b/src/events/messageDelete/audits.ts @@ -26,26 +26,37 @@ export default { if (channel === null) return; - (channel as TextChannel).send({ - embeds: [ - new MessageEmbed() - .setColor(successColor) - .setAuthor({ - name: message.author.username, - iconURL: message.author.displayAvatarURL(), - }) - .setDescription( - ` + (channel as TextChannel) + .send({ + embeds: [ + new MessageEmbed() + .setColor(successColor) + .setAuthor({ + name: message.author.username, + iconURL: message.author.displayAvatarURL(), + }) + .setDescription( + ` **Message sent by** ${message.author} **deleted in** ${message.channel} ${message.content} ` - ) - .setTimestamp() - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), - ], - }); + ) + .setTimestamp() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }), + ], + }) + .then(async () => { + logger.info( + `Audit log sent for event messageDelete in guild ${message?.guild?.name} (${message?.guild?.id})` + ); + }) + .catch(async () => { + logger.error( + `Audit log failed to send for event messageDelete in guild ${message?.guild?.name} (${message?.guild?.id})` + ); + }); }, }; diff --git a/src/events/messageDelete/index.ts b/src/events/messageDelete/index.ts index 286c99a..77223d2 100644 --- a/src/events/messageDelete/index.ts +++ b/src/events/messageDelete/index.ts @@ -3,7 +3,6 @@ import audits from "@events/messageDelete/audits"; import counter from "./modules/counter"; export default { - name: "messageDelete", async execute(message: Message) { await audits.execute(message); await counter(message); diff --git a/src/events/messageUpdate/audits.ts b/src/events/messageUpdate/audits.ts index 50c76b5..b7f2726 100644 --- a/src/events/messageUpdate/audits.ts +++ b/src/events/messageUpdate/audits.ts @@ -29,25 +29,36 @@ export default { if (channel === null) return; - (channel as TextChannel).send({ - embeds: [ - new MessageEmbed() - .setColor(successColor) - .setAuthor({ - name: newMessage.author.username, - iconURL: newMessage.author.displayAvatarURL(), - }) - .setDescription( - ` + (channel as TextChannel) + .send({ + embeds: [ + new MessageEmbed() + .setColor(successColor) + .setAuthor({ + name: newMessage.author.username, + iconURL: newMessage.author.displayAvatarURL(), + }) + .setDescription( + ` **Message edited in** ${newMessage.channel} [jump to message](https://discord.com/channels/${newMessage.guild.id}/${newMessage.channel.id}/${newMessage.id}) ` - ) - .setTimestamp() - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), - ], - }); + ) + .setTimestamp() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }), + ], + }) + .then(async () => { + logger.info( + `Audit log sent for event messageUpdate in guild ${newMessage?.guild?.name} (${newMessage?.guild?.id})` + ); + }) + .catch(async () => { + logger.error( + `Audit log failed to send for event messageUpdate in guild ${newMessage?.guild?.name} (${newMessage?.guild?.id})` + ); + }); }, }; diff --git a/src/events/messageUpdate/index.ts b/src/events/messageUpdate/index.ts index 2ea1e42..0ff405e 100644 --- a/src/events/messageUpdate/index.ts +++ b/src/events/messageUpdate/index.ts @@ -8,7 +8,6 @@ import counter from "./modules/counter"; import audits from "./audits"; export default { - name: "messageUpdate", async execute(oldMessage: Message, newMessage: Message) { const { author, guild } = newMessage; diff --git a/src/events/ready/index.ts b/src/events/ready/index.ts index b840bb3..4e7684f 100644 --- a/src/events/ready/index.ts +++ b/src/events/ready/index.ts @@ -8,7 +8,6 @@ import deployCommands from "@handlers/deployCommands"; import devMode from "@handlers/devMode"; export default { - name: "ready", once: true, async execute(client: Client) { logger.info(`${client.user?.tag} (${client.user?.id}) is ready`); diff --git a/src/handlers/commands.ts b/src/handlers/commands.ts index dd4df30..f5cfed0 100644 --- a/src/handlers/commands.ts +++ b/src/handlers/commands.ts @@ -15,19 +15,18 @@ export default async (client: Client) => { plugins.map(async (pluginName) => { const plugin = await import(`../plugins/${pluginName}`); - await client?.commands?.set( - plugin?.default?.data?.name, - plugin?.default + await client.commands.set( + plugin.default.data.name, + plugin.default, + plugin.default.meta ); - - logger.verbose(`Loaded plugin: ${pluginName}`); }) ) .then(async () => { logger.debug("Successfully loaded plugins."); }) .catch(async (err) => { - logger.error(err); + logger.error(`${err}`); }); }); }; diff --git a/src/handlers/events.ts b/src/handlers/events.ts index 1428325..4c7d778 100644 --- a/src/handlers/events.ts +++ b/src/handlers/events.ts @@ -15,12 +15,12 @@ export default async (client: Client) => { logger.verbose(`Loaded event: ${eventName}`); if (event.once) { - return client.once(event.default.name, async (...args) => + return client.once(eventName, async (...args) => event.default.execute(...args) ); } - return client.on(event.default.name, async (...args) => + return client.on(eventName, async (...args) => event.default.execute(...args) ); }) diff --git a/src/helpers/deferReply.ts b/src/helpers/deferReply.ts new file mode 100644 index 0000000..378c62e --- /dev/null +++ b/src/helpers/deferReply.ts @@ -0,0 +1,22 @@ +import { CommandInteraction, MessageEmbed } from "discord.js"; +import { waitColor, footerText, footerIcon } from "@config/embed"; + +export default async (interaction: CommandInteraction, ephemeral: boolean) => { + await interaction.deferReply({ + ephemeral, + }); + + await interaction.editReply({ + embeds: [ + new MessageEmbed() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }) + .setTimestamp(new Date()) + .setTitle("Processing your request") + .setColor(waitColor) + .setDescription("Please wait..."), + ], + }); +}; diff --git a/src/helpers/embedBuilder.ts b/src/helpers/embedBuilder.ts new file mode 100644 index 0000000..8d15d90 --- /dev/null +++ b/src/helpers/embedBuilder.ts @@ -0,0 +1,9 @@ +import { footerText, footerIcon } from "@config/embed"; +import { MessageEmbed } from "discord.js"; + +export default new MessageEmbed() + .setFooter({ + text: footerText, + iconURL: footerIcon, + }) + .setTimestamp(new Date()); diff --git a/src/helpers/getCommandMeta.ts b/src/helpers/getCommandMeta.ts new file mode 100644 index 0000000..cba46e3 --- /dev/null +++ b/src/helpers/getCommandMeta.ts @@ -0,0 +1,12 @@ +import { CommandInteraction } from "discord.js"; + +export default async (interaction: CommandInteraction, currentCommand: any) => { + const subcommand = interaction.options.getSubcommand(); + const subcommandGroup = interaction.options.getSubcommandGroup(false); + + if (!subcommandGroup) { + return currentCommand.modules[subcommand].meta; + } + + return currentCommand.groups[subcommandGroup].modules[subcommand].meta; +}; diff --git a/src/index.ts b/src/index.ts index 270ab96..c083eae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,6 @@ import { token, intents } from "@config/discord"; import { Client } from "discord.js"; // discord.js -import locale from "@locale"; import database from "@database"; import schedules from "@schedules"; import events from "@handlers/events"; @@ -15,7 +14,6 @@ async function main() { intents, }); - await locale(); await database(); await schedules(client); diff --git a/src/locale/index.ts b/src/locale/index.ts deleted file mode 100644 index 536b91f..0000000 --- a/src/locale/index.ts +++ /dev/null @@ -1,205 +0,0 @@ -import i18next from "i18next"; -import logger from "@logger"; - -export default async () => { - await i18next - .init({ - lng: "en", // if you're using a language detector, do not define the lng option - // debug: true, - fallbackLng: "en", - resources: { - en: { - general: { not_available: "Not Available" }, - commands: { - credits: { - general: { - credits_one: "{{count}} credit", - credits_other: "{{count}} credits", - }, - addons: { - balance: { embed: { title: "Credits" } }, - gift: { embed: { title: "Gift" } }, - }, - }, - reputation: { - addons: { - give: { - version01: { - embed: { - title: ":medal: Reputation", - description: - "You have given reputation within the last day, you can not repute now!", - }, - }, - version02: { - embed: { - title: ":medal: Reputation", - description: - "You have given {{user}} a {{type}} reputation!", - }, - }, - version03: { - embed: { - title: ":medal: Reputation", - description: "You can not repute yourself.", - }, - }, - }, - }, - }, - profile: { - addons: { - view: { - embed: { - title: "Profile", - reputation: "Reputation (Global)", - level: "Level (Guild)", - points: "Points (Guild)", - credits: "Credits (Guild)", - language_code: "Language Code (Global)", - }, - }, - settings: { - embed: { - title: "Profile", - description: "Following settings is set", - fields: { language: "Language" }, - }, - }, - }, - }, - }, - }, - sv: { - general: { not_available: "Otillgänglig" }, - commands: { - credits: { - general: { - credits_one: "{{count}} krona", - credits_other: "{{count}} kronor", - }, - addons: { - balance: { embed: { title: "Krediter" } }, - gift: { embed: { title: "Gåva" } }, - }, - }, - reputation: { - addons: { - give: { - version01: { - embed: { - title: ":medal: Omdöme", - description: - "Du har redan gett omdöme inom den senaste dagen, du kan inte ge ett omdöme just nu!", - }, - }, - version02: { - embed: { - title: ":medal: Omdöme", - description: "Du har gett {{user}} ett {{type}} omdöme!", - }, - }, - version03: { - embed: { - title: ":medal: Omdöme", - description: "Du kan inte ge dig själv ett omdöme.", - }, - }, - }, - }, - }, - - profile: { - addons: { - view: { - embed: { - title: "Profil", - reputation: "Omdöme (Globalt)", - level: "Nivå (Server)", - points: "Poäng (Server)", - credits: "Krediter (Server)", - language_code: "Språkkod (Globalt)", - }, - }, - settings: { - embed: { - title: "Profil", - description: "Följande inställningar är satta", - fields: { language: "Språk" }, - }, - }, - }, - }, - }, - }, - de: { - general: { not_available: "Nicht verfügbar" }, - commands: { - credits: { - general: { - credits_one: "{{count}} Guthaben", - credits_other: "{{count}} Guthaben", - }, - addons: { - balance: { embed: { title: "Guthaben" } }, - gift: { embed: { title: "Geschenk" } }, - }, - }, - reputation: { - addons: { - give: { - version01: { - embed: { - title: ":medal: Ruf", - description: - "Du hast dir am letzten Tag einen Ruf verschafft, den du jetzt nicht rühmen kannst!", - }, - }, - version02: { - embed: { - title: ":medal: Ruf", - description: - "Du hast {{user}} einen {{type}} Ruf gegeben!", - }, - }, - version03: { - embed: { - title: ":medal: Ruf", - description: "Du kannst dich nicht selbst rühmen.", - }, - }, - }, - }, - }, - profile: { - addons: { - view: { - embed: { - title: "Profil", - reputation: "Ruf (Weltweit)", - level: "Level (Gilde)", - points: "Punkte (Gilde)", - credits: "Guthaben (Gilde)", - language_code: "Sprachcode (Weltweit)", - }, - }, - settings: { - embed: { - title: "Profile", - description: "Folgende Einstellungen werden vorgenommen", - fields: { language: "Sprache" }, - }, - }, - }, - }, - }, - }, - }, - }) - .then(async () => { - logger.debug(`i18next initialized`); - }) - .catch(async (error) => { - logger.error(`i18next failed to initialize: ${error}`); - }); -}; diff --git a/src/plugins/counters/index.ts b/src/plugins/counters/index.ts index 4da895c..1bd7c7b 100644 --- a/src/plugins/counters/index.ts +++ b/src/plugins/counters/index.ts @@ -1,17 +1,18 @@ -// Dependencies import { CommandInteraction } from "discord.js"; import { SlashCommandBuilder } from "@discordjs/builders"; - import logger from "@logger"; -import modules from "@root/plugins/counters/modules"; +import modules from "@plugins/counters/modules"; export default { - metadata: { author: "Zyner" }, + modules, + data: new SlashCommandBuilder() .setName("counters") .setDescription("View guild counters") + .addSubcommand(modules.view.data), + async execute(interaction: CommandInteraction) { const { options } = interaction; diff --git a/src/plugins/counters/modules/index.ts b/src/plugins/counters/modules/index.ts index dc539f8..8c5ea76 100644 --- a/src/plugins/counters/modules/index.ts +++ b/src/plugins/counters/modules/index.ts @@ -1,3 +1,3 @@ -import view from "./view"; +import view from "@plugins/counters/modules/view"; export default { view }; diff --git a/src/plugins/counters/modules/view/index.ts b/src/plugins/counters/modules/view/index.ts index e7635c2..7f4d1e0 100644 --- a/src/plugins/counters/modules/view/index.ts +++ b/src/plugins/counters/modules/view/index.ts @@ -1,11 +1,3 @@ -// Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import { ChannelType } from "discord-api-types/v10"; - -import counterSchema from "@schemas/counter"; - -// Configuration import { errorColor, successColor, @@ -13,7 +5,16 @@ import { footerIcon, } from "@config/embed"; +import { CommandInteraction, MessageEmbed } from "discord.js"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import { ChannelType } from "discord-api-types/v10"; + +import counterSchema from "@schemas/counter"; +import i18next from "i18next"; + export default { + meta: { guildOnly: true, ephemeral: false }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("view") @@ -25,14 +26,28 @@ export default { `The channel that contains the counter you want to view` ) .setRequired(true) - .addChannelType(ChannelType.GuildText as number) + .addChannelTypes(ChannelType.GuildText) ); }, + execute: async (interaction: CommandInteraction) => { - const { options, guild } = interaction; + const { options, guild, locale } = interaction; const discordChannel = options?.getChannel("channel"); + const embed = new MessageEmbed() + .setTitle( + i18next.t("counters:modules:view:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ + text: footerText, + iconURL: footerIcon, + }); + const counter = await counterSchema?.findOne({ guildId: guild?.id, channelId: discordChannel?.id, @@ -41,32 +56,31 @@ export default { if (counter === null) { return interaction?.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:1234:] Counters (View)") - .setDescription(`No counter found for channel ${discordChannel}!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), + embed + .setDescription( + i18next.t("counters:modules:view:error01:description", { + lng: locale, + ns: "plugins", + channel: discordChannel, + }) + ) + .setColor(errorColor), ], }); } return interaction?.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:1234:] Counters (View)") + embed .setDescription( - `Viewing counter for channel ${discordChannel} with count ${counter.counter}.` + i18next.t("counters:modules:view:success01:description", { + lng: locale, + ns: "plugins", + channel: discordChannel, + amount: counter.counter, + }) ) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ - text: footerText, - iconURL: footerIcon, - }), + .setColor(successColor), ], }); }, diff --git a/src/plugins/credits/index.ts b/src/plugins/credits/index.ts index 4566feb..2ec3c92 100644 --- a/src/plugins/credits/index.ts +++ b/src/plugins/credits/index.ts @@ -1,44 +1,39 @@ -// Dependencies import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; - import logger from "@logger"; -// Modules -import modules from "@root/plugins/credits/modules"; +import modules from "@plugins/credits/modules"; export default { - metadata: { author: "Zyner" }, + modules, + data: new SlashCommandBuilder() .setName("credits") .setDescription("Manage your credits.") + .addSubcommand(modules.balance.data) .addSubcommand(modules.gift.data) .addSubcommand(modules.top.data) .addSubcommand(modules.work.data), + async execute(interaction: CommandInteraction) { const { options } = interaction; - if (options?.getSubcommand() === "balance") { - logger?.verbose(`Executing balance subcommand`); - return modules.balance.execute(interaction); + switch (options.getSubcommand()) { + case "balance": + await modules.balance.execute(interaction); + break; + case "gift": + await modules.gift.execute(interaction); + break; + case "top": + await modules.top.execute(interaction); + break; + case "work": + await modules.work.execute(interaction); + break; + default: + logger.verbose(`Unknown subcommand ${options.getSubcommand()}`); } - - if (options?.getSubcommand() === "gift") { - logger?.verbose(`Executing gift subcommand`); - return modules.gift.execute(interaction); - } - - if (options?.getSubcommand() === "top") { - logger?.verbose(`Executing top command`); - return modules.top.execute(interaction); - } - - if (options?.getSubcommand() === "work") { - logger?.verbose(`Executing work command`); - return modules.work.execute(interaction); - } - - logger?.verbose(`Unknown subcommand ${options?.getSubcommand()}`); }, }; diff --git a/src/plugins/credits/modules/balance/index.ts b/src/plugins/credits/modules/balance/index.ts index 0c4f2c2..44ac45a 100644 --- a/src/plugins/credits/modules/balance/index.ts +++ b/src/plugins/credits/modules/balance/index.ts @@ -1,10 +1,3 @@ -// Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -import logger from "@logger"; - -// Configurations import { errorColor, successColor, @@ -12,41 +5,53 @@ import { footerIcon, } from "@config/embed"; -// Helpers -import pluralize from "@helpers/pluralize"; +import i18next from "i18next"; +import { CommandInteraction, MessageEmbed } from "discord.js"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import logger from "@logger"; + import fetchUser from "@helpers/fetchUser"; export default { + meta: { guildOnly: true, ephemeral: true }, data: (command: SlashCommandSubcommandBuilder) => { - return ( - command - .setName("balance") - .setDescription(`View a user's balance`) - - // User - .addUserOption((option) => - option - .setName("user") - .setDescription(`The user whose balance you want to view`) - ) - ); + return command + .setName("balance") + .setDescription(`View a user's balance`) + .addUserOption((option) => + option + .setName("user") + .setDescription(`The user whose balance you want to view`) + ); }, execute: async (interaction: CommandInteraction) => { - const { options, user, guild } = interaction; + const { options, user, guild, locale } = interaction; - const discordUser = options?.getUser("user"); + const discordUser = options.getUser("user"); + + const embed = new MessageEmbed() + .setTitle( + i18next.t("credits:modules:balance:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }); if (guild === null) { - logger?.verbose(`Guild is null`); + logger.verbose(`Guild is null`); - return interaction?.editReply({ + return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Balance)") - .setDescription(`You can only use this command in a guild!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("guildOnly", { + lng: locale, + ns: "errors", + }) + ) + .setColor(errorColor), ], }); } @@ -54,50 +59,55 @@ export default { const userObj = await fetchUser(discordUser || user, guild); if (userObj === null) { - logger?.verbose(`User not found`); + logger.verbose(`User not found`); - return interaction?.editReply({ + return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Balance)") - .setDescription(`Could not find user ${discordUser || user}`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("userNotFound", { + lng: locale, + ns: "errors", + user: discordUser || user, + }) + ) + .setColor(errorColor), ], }); } if (userObj.credits === null) { - logger?.verbose(`User has no credits`); + logger.verbose(`User has no credits`); - return interaction?.editReply({ + return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Balance)") - .setDescription(`${discordUser || user} has no credits!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("credits:modules:balance:error01:description", { + lng: locale, + ns: "plugins", + user: discordUser || user, + }) + ) + .setColor(errorColor), ], }); } - logger?.verbose(`Found user ${discordUser || user}`); + logger.verbose(`Found user ${discordUser || user}`); - return interaction?.editReply({ + return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Balance)") + embed .setDescription( - `${discordUser || user} has ${pluralize( - userObj.credits, - `credit` - )}!` + i18next.t("credits:modules:balance:success01:description", { + lng: locale, + ns: "plugins", + user: discordUser || user, + amount: userObj.credits, + }) ) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(successColor), ], }); }, diff --git a/src/plugins/credits/modules/gift/index.ts b/src/plugins/credits/modules/gift/index.ts index 2ce6a62..82b6a28 100644 --- a/src/plugins/credits/modules/gift/index.ts +++ b/src/plugins/credits/modules/gift/index.ts @@ -18,9 +18,12 @@ import saveUser from "@helpers/saveUser"; // Models import fetchUser from "@helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import i18next from "i18next"; // Function export default { + meta: { guildOnly: true, ephemeral: true }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("gift") @@ -42,38 +45,52 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - const { options, user, guild, client } = interaction; + const { options, user, guild, client, locale } = interaction; - const optionUser = options?.getUser("user"); - const optionAmount = options?.getInteger("amount"); - const optionReason = options?.getString("reason"); + const optionUser = options.getUser("user"); + const optionAmount = options.getInteger("amount"); + const optionReason = options.getString("reason"); + + const embed = new MessageEmbed() + .setTitle( + i18next.t("credits:modules:gift:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }); if (guild === null) { - logger?.verbose(`Guild is null`); + logger.verbose(`Guild is null`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") - .setDescription(`We can not find your guild!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("guildOnly", { + lng: locale, + ns: "errors", + }) + ) + .setColor(errorColor), ], }); } if (optionUser === null) { - logger?.verbose(`User not found`); + logger.verbose(`User not found`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") - .setDescription(`We can not find your requested user!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("userNotFound", { + lng: locale, + ns: "errors", + }) + ) + .setColor(errorColor), ], }); } @@ -85,119 +102,126 @@ export default { const toUserDB = await fetchUser(optionUser, guild); if (fromUserDB === null) { - logger?.verbose(`User not found`); + logger.verbose(`User not found`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") + embed .setDescription( - `We can not find your requested from user in our database!` + i18next.t("userNotFound", { + lng: locale, + ns: "errors", + }) ) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(errorColor), ], }); } if (toUserDB === null) { - logger?.verbose(`User not found`); + logger.verbose(`User not found`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") + embed .setDescription( - `We can not find your requested to user in our database!` + i18next.t("userNotFound", { + lng: locale, + ns: "errors", + }) ) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(errorColor), ], }); } // If receiver is same as sender - if (optionUser?.id === user?.id) { - logger?.verbose(`User is same as sender`); + if (optionUser.id === user.id) { + logger.verbose(`User is same as sender`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") - .setDescription(`You can not pay yourself!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("credits:modules:gift:error01:description", { + lng: locale, + ns: "plugins", + }) + ) + .setColor(errorColor), ], }); } // If amount is null if (optionAmount === null) { - logger?.verbose(`Amount is null`); + logger.verbose(`Amount is null`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") - .setDescription(`We could not read your requested amount!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("amountNotFound", { + lng: locale, + ns: "errors", + }) + ) + .setColor(errorColor), ], }); } // If amount is zero or below if (optionAmount <= 0) { - logger?.verbose(`Amount is zero or below`); + logger.verbose(`Amount is zero or below`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") - .setDescription(`You can't gift zero or below!`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("credits:modules:gift:error02:description", { + lng: locale, + ns: "plugins", + }) + ) + .setColor(errorColor), ], }); } // If user has below gifting amount - if (fromUserDB?.credits < optionAmount) { - logger?.verbose(`User has below gifting amount`); + if (fromUserDB.credits < optionAmount) { + logger.verbose(`User has below gifting amount`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") + embed .setDescription( - `You have insufficient credits. Your balance is ${fromUserDB?.credits}!` + i18next.t("credits:modules:gift:error03:description", { + lng: locale, + ns: "plugins", + amount: fromUserDB.credits, + }) ) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(errorColor), ], }); } // If toUserDB has no credits if (toUserDB === null) { - logger?.verbose(`User has no credits`); + logger.verbose(`User has no credits`); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") + embed .setDescription( - `We can not find your requested to user in our database!` + i18next.t("userNotFound", { + lng: locale, + ns: "errors", + }) ) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(errorColor), ], }); } @@ -209,46 +233,50 @@ export default { toUserDB.credits += optionAmount; // Save users - await saveUser(fromUserDB, toUserDB)?.then(async () => { + await saveUser(fromUserDB, toUserDB).then(async () => { // Get DM user object - const dmUser = client?.users?.cache?.get(optionUser?.id); + const dmUser = client.users.cache.get(optionUser.id); + + if (dmUser == null) return; // Send DM to user await dmUser - ?.send({ + .send({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") + embed .setDescription( - `You have received ${optionAmount} credits from ${ - user?.tag - } with reason ${ - optionReason ? ` with reason: ${optionReason}` : "" - }!` + i18next.t("credits:modules:gift:error03:description", { + lng: locale, + ns: "plugins", + user: user.tag, + amount: optionAmount, + reason: optionReason || "unspecified", + }) ) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(successColor), ], }) .catch(async (error) => - logger?.error(`[Gift] Error sending DM to user: ${error}`) + logger.error(`[Gift] Error sending DM to user: ${error}`) ); - logger?.verbose( - `[Gift] Successfully gifted ${optionAmount} credits to ${optionUser?.tag}` + logger.verbose( + `[Gift] Successfully gifted ${optionAmount} credits to ${optionUser.tag}` ); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Gift)") + embed .setDescription( - `Successfully gifted ${optionAmount} credits to ${optionUser?.tag}!` + i18next.t("credits:modules:gift:success02:description", { + lng: locale, + ns: "plugins", + user: user, + amount: optionAmount, + reason: optionReason || "unspecified", + }) ) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(successColor), ], }); }); diff --git a/src/plugins/credits/modules/index.ts b/src/plugins/credits/modules/index.ts index 20edcaf..9b144f2 100644 --- a/src/plugins/credits/modules/index.ts +++ b/src/plugins/credits/modules/index.ts @@ -1,6 +1,6 @@ -import balance from "./balance"; -import gift from "./gift"; -import top from "./top"; -import work from "./work"; +import balance from "@plugins/credits/modules/balance"; +import gift from "@plugins/credits/modules/gift"; +import top from "@plugins/credits/modules/top"; +import work from "@plugins/credits/modules/work"; export default { balance, gift, top, work }; diff --git a/src/plugins/credits/modules/top/index.ts b/src/plugins/credits/modules/top/index.ts index c27f4df..1656c59 100644 --- a/src/plugins/credits/modules/top/index.ts +++ b/src/plugins/credits/modules/top/index.ts @@ -1,48 +1,86 @@ -// Dependencies -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import { + successColor, + errorColor, + footerText, + footerIcon, +} from "@config/embed"; + +import i18next from "i18next"; import { CommandInteraction, MessageEmbed } from "discord.js"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import logger from "@logger"; -import userSchema from "@schemas/user"; - -// Configurations -import { successColor, footerText, footerIcon } from "@config/embed"; - -// Helpers -import pluralize from "@helpers/pluralize"; +import userSchema, { IUser } from "@schemas/user"; export default { + meta: { guildOnly: true, ephemeral: false }, + data: (command: SlashCommandSubcommandBuilder) => { return command.setName("top").setDescription(`View the top users`); }, execute: async (interaction: CommandInteraction) => { - // Get all users in the guild + const { locale, guild } = interaction; - const usersDB = await userSchema.find({ guildId: interaction?.guild?.id }); + const embed = new MessageEmbed() + .setTitle( + i18next.t("credits:modules:top:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }); + + if (guild === null) { + logger.verbose(`Guild is null`); + + return interaction.editReply({ + embeds: [ + embed + .setDescription( + i18next.t("guildOnly", { + lng: locale, + ns: "errors", + }) + ) + .setColor(errorColor), + ], + }); + } + + const usersDB = await userSchema.find({ guildId: guild.id }); const topTen = usersDB // Sort them after credits amount (ascending) - .sort((a, b) => (a?.credits > b?.credits ? -1 : 1)) + .sort((a, b) => (a.credits > b.credits ? -1 : 1)) // Return the top 10 .slice(0, 10); // Create entry object - const entry = (x: any, index: number) => - `${index + 1}. <@${x?.userId}> - ${pluralize(x?.credits, "credit")}`; + const entry = (x: IUser, index: number) => + i18next.t("credits:modules:top:entry", { + lng: locale, + ns: "plugins", + index: index + 1, + user: x.userId, + amount: x.credits, + }); return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Top)") + embed .setDescription( - `Top 10 users with the most credits. + ` ${i18next.t("credits:modules:top:success01:description", { + lng: locale, + ns: "plugins", + })} - ${topTen.map(entry).join("\n")}` + ${topTen.map(entry).join("\n")} + ` ) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(successColor), ], }); }, diff --git a/src/plugins/credits/modules/work/index.ts b/src/plugins/credits/modules/work/index.ts index 2ffb20d..37d743c 100644 --- a/src/plugins/credits/modules/work/index.ts +++ b/src/plugins/credits/modules/work/index.ts @@ -4,7 +4,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import Chance from "chance"; // Configurations -import { successColor, footerText, footerIcon } from "@config/embed"; +import { + successColor, + errorColor, + footerText, + footerIcon, +} from "@config/embed"; // Handlers import logger from "@logger"; @@ -15,14 +20,30 @@ import timeoutSchema from "@schemas/timeout"; // Helpers import fetchUser from "@helpers/fetchUser"; import fetchGuild from "@helpers/fetchGuild"; +import i18next from "i18next"; export default { + meta: { guildOnly: true, ephemeral: true }, + data: (command: SlashCommandSubcommandBuilder) => { return command.setName("work").setDescription(`Work to earn credits`); }, execute: async (interaction: CommandInteraction) => { // Destructure member - const { guild, user } = interaction; + const { guild, user, locale } = interaction; + + const embed = new MessageEmbed() + .setTitle( + i18next.t("credits:modules:work:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ + text: footerText, + iconURL: footerIcon, + }); // Chance module const chance = new Chance(); @@ -46,14 +67,15 @@ export default { return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Work)") + embed .setDescription( - `You can not work while on timeout, please wait ${guildDB?.credits.workTimeout} seconds.` + i18next.t("credits:modules:work:error01:description", { + lng: locale, + ns: "plugins", + time: guildDB?.credits.workTimeout, + }) ) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + .setColor(errorColor), ], }); } @@ -78,12 +100,16 @@ export default { return interaction.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:dollar:] Credits (Work)") - .setDescription(`You worked and earned ${creditsEarned} credits`) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t("credits:modules:work:success01:description", { + lng: locale, + ns: "plugins", + time: guildDB?.credits.workTimeout, + amount: creditsEarned, + }) + ) + .setColor(successColor), ], }); }); diff --git a/src/plugins/fun/index.ts b/src/plugins/fun/index.ts new file mode 100644 index 0000000..0d727dc --- /dev/null +++ b/src/plugins/fun/index.ts @@ -0,0 +1,27 @@ +import { SlashCommandBuilder } from "@discordjs/builders"; +import { CommandInteraction } from "discord.js"; +import logger from "@logger"; + +import modules from "@plugins/fun/modules"; + +export default { + modules, + + data: new SlashCommandBuilder() + .setName("fun") + .setDescription("Fun commands.") + + .addSubcommand(modules.meme.data), + + async execute(interaction: CommandInteraction) { + const { options } = interaction; + + switch (options.getSubcommand()) { + case "meme": + await modules.meme.execute(interaction); + break; + default: + logger.verbose(`Unknown subcommand ${options.getSubcommand()}`); + } + }, +}; diff --git a/src/plugins/fun/modules/index.ts b/src/plugins/fun/modules/index.ts new file mode 100644 index 0000000..2b59097 --- /dev/null +++ b/src/plugins/fun/modules/index.ts @@ -0,0 +1,5 @@ +import meme from "@plugins/fun/modules/meme"; + +export default { + meme, +}; diff --git a/src/plugins/fun/modules/meme.ts b/src/plugins/fun/modules/meme.ts new file mode 100644 index 0000000..40d5cdb --- /dev/null +++ b/src/plugins/fun/modules/meme.ts @@ -0,0 +1,37 @@ +import { successColor, footerText, footerIcon } from "@config/embed"; + +import axios from "axios"; +import { CommandInteraction, MessageEmbed } from "discord.js"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import logger from "@logger"; + +export default { + meta: { guildOnly: false, ephemeral: false }, + + data: (command: SlashCommandSubcommandBuilder) => { + return command.setName("meme").setDescription("Get a meme from r/memes)"); + }, + execute: async (interaction: CommandInteraction) => { + await axios + .get("https://www.reddit.com/r/memes/random/.json") + .then(async (res) => { + const response = res.data[0].data.children; + const content = response[0].data; + + const embed = new MessageEmbed() + .setTitle(content.title) + .setTimestamp(new Date()) + .setImage(content.url) + .setFooter({ + text: `👍 ${content.ups}︱👎 ${content.downs}\n${footerText}`, + iconURL: footerIcon, + }) + .setColor(successColor); + + return interaction.editReply({ embeds: [embed] }); + }) + .catch((error) => { + logger.error(`${error}`); + }); + }, +}; diff --git a/src/plugins/manage/groups/counters/index.ts b/src/plugins/manage/groups/counters/index.ts index dc3c255..2a16b04 100644 --- a/src/plugins/manage/groups/counters/index.ts +++ b/src/plugins/manage/groups/counters/index.ts @@ -5,31 +5,33 @@ import { CommandInteraction } from "discord.js"; import logger from "@logger"; // Modules -import moduleCreate from "./modules/create"; -import moduleDelete from "./modules/delete"; +import modules from "./modules"; // Function export default { + modules, + data: (group: SlashCommandSubcommandGroupBuilder) => { return group .setName("counters") .setDescription("Manage guild counters.") - .addSubcommand(moduleCreate.data) - .addSubcommand(moduleDelete.data); + .addSubcommand(modules.add.data) + .addSubcommand(modules.remove.data); }, + execute: async (interaction: CommandInteraction) => { const { options } = interaction; - if (options?.getSubcommand() === "create") { + if (options?.getSubcommand() === "add") { logger?.verbose(`Executing create subcommand`); - return moduleCreate.execute(interaction); + return modules.add.execute(interaction); } - if (options?.getSubcommand() === "delete") { + if (options?.getSubcommand() === "remove") { logger?.verbose(`Executing delete subcommand`); - return moduleDelete.execute(interaction); + return modules.remove.execute(interaction); } logger?.verbose(`Unknown subcommand ${options?.getSubcommand()}`); diff --git a/src/plugins/manage/groups/counters/modules/create/index.ts b/src/plugins/manage/groups/counters/modules/add/index.ts similarity index 57% rename from src/plugins/manage/groups/counters/modules/create/index.ts rename to src/plugins/manage/groups/counters/modules/add/index.ts index 32c83fb..5320937 100644 --- a/src/plugins/manage/groups/counters/modules/create/index.ts +++ b/src/plugins/manage/groups/counters/modules/add/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { MessageEmbed, CommandInteraction } from "discord.js"; +import { MessageEmbed, CommandInteraction, Permissions } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { ChannelType } from "discord-api-types/v10"; @@ -16,19 +16,26 @@ import logger from "@logger"; // Models import counterSchema from "@schemas/counter"; +import i18next from "i18next"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command - .setName("create") + .setName("add") .setDescription("Add a counter to your guild.") .addChannelOption((option) => option .setName("channel") .setDescription("The channel to send the counter to.") .setRequired(true) - .addChannelType(ChannelType.GuildText as number) + .addChannelTypes(ChannelType.GuildText) ) .addStringOption((option) => option @@ -43,12 +50,22 @@ export default { ); }, execute: async (interaction: CommandInteraction) => { - const { options, guild } = interaction; + const { options, guild, locale } = interaction; const discordChannel = options?.getChannel("channel"); const countingWord = options?.getString("word"); const startValue = options?.getNumber("start"); + const embed = new MessageEmbed() + .setTitle( + i18next.t("manage:groups:counters:modules:add:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }); + const counter = await counterSchema?.findOne({ guildId: guild?.id, channelId: discordChannel?.id, @@ -57,12 +74,18 @@ export default { if (counter) { return interaction?.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:toolbox:] Manage - Counters (Create)") - .setDescription(`A counter already exists for this channel.`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t( + "manage:groups:counters:modules:add:error01:description", + { + lng: locale, + ns: "plugins", + channel: discordChannel, + } + ) + ) + .setColor(errorColor), ], }); } @@ -79,12 +102,18 @@ export default { return interaction?.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:toolbox:] Manage - Counters (Create)") - .setDescription(`Created counter for ${discordChannel}`) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t( + "manage:groups:counters:modules:create:success01:description", + { + lng: locale, + ns: "plugins", + channel: discordChannel, + } + ) + ) + .setColor(successColor), ], }); }); diff --git a/src/plugins/manage/groups/counters/modules/index.ts b/src/plugins/manage/groups/counters/modules/index.ts new file mode 100644 index 0000000..e623f48 --- /dev/null +++ b/src/plugins/manage/groups/counters/modules/index.ts @@ -0,0 +1,4 @@ +import add from "@plugins/manage/groups/counters/modules/add"; +import remove from "@plugins/manage/groups/counters/modules/remove"; + +export default { add, remove }; diff --git a/src/plugins/manage/groups/counters/modules/delete/index.ts b/src/plugins/manage/groups/counters/modules/remove/index.ts similarity index 53% rename from src/plugins/manage/groups/counters/modules/delete/index.ts rename to src/plugins/manage/groups/counters/modules/remove/index.ts index 67bd222..b707b45 100644 --- a/src/plugins/manage/groups/counters/modules/delete/index.ts +++ b/src/plugins/manage/groups/counters/modules/remove/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; +import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations import { @@ -16,26 +16,43 @@ import logger from "@logger"; import counterSchema from "@schemas/counter"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { ChannelType } from "discord-api-types/v10"; +import i18next from "i18next"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command - .setName("delete") + .setName("remove") .setDescription(`Delete a counter from your guild.`) .addChannelOption((option) => option .setName("channel") .setDescription("The channel to delete the counter from.") .setRequired(true) - .addChannelType(ChannelType.GuildText as number) + .addChannelTypes(ChannelType.GuildText) ); }, execute: async (interaction: CommandInteraction) => { - const { options, guild } = interaction; + const { options, guild, locale } = interaction; const discordChannel = options?.getChannel("channel"); + const embed = new MessageEmbed() + .setTitle( + i18next.t("manage:groups:counters:modules:remove:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }); + const counter = await counterSchema?.findOne({ guildId: guild?.id, channelId: discordChannel?.id, @@ -46,12 +63,17 @@ export default { return interaction?.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:toolbox:] Manage - Counters (Delete)") - .setDescription(`The counter for this channel does not exist.`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t( + "manage:groups:counters:modules:remove:error01:description", + { + lng: locale, + ns: "plugins", + } + ) + ) + .setColor(errorColor), ], }); } @@ -66,12 +88,17 @@ export default { return interaction?.editReply({ embeds: [ - new MessageEmbed() - .setTitle("[:toolbox:] Manage - Counters (Delete)") - .setDescription(`The counter for this channel has been deleted.`) - .setTimestamp(new Date()) - .setColor(successColor) - .setFooter({ text: footerText, iconURL: footerIcon }), + embed + .setDescription( + i18next.t( + "manage:groups:counters:modules:remove:success01:description", + { + lng: locale, + ns: "plugins", + } + ) + ) + .setColor(successColor), ], }); }) diff --git a/src/plugins/manage/groups/credits/index.ts b/src/plugins/manage/groups/credits/index.ts index 9209f8a..fc0839c 100644 --- a/src/plugins/manage/groups/credits/index.ts +++ b/src/plugins/manage/groups/credits/index.ts @@ -1,53 +1,43 @@ -// Dependencies import { CommandInteraction } from "discord.js"; import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; - import logger from "@logger"; -// Modules -import moduleGive from "./modules/give"; -import moduleSet from "./modules/set"; -import moduleTake from "./modules/take"; -import moduleTransfer from "./modules/transfer"; +import modules from "./modules"; -// Function export default { + modules, + data: (group: SlashCommandSubcommandGroupBuilder) => { return group .setName("credits") .setDescription("Manage the credits of a user.") - .addSubcommand(moduleGive.data) - .addSubcommand(moduleSet.data) - .addSubcommand(moduleTake.data) - .addSubcommand(moduleTransfer.data); + .addSubcommand(modules.give.data) + .addSubcommand(modules.set.data) + .addSubcommand(modules.take.data) + .addSubcommand(modules.transfer.data); }, execute: async (interaction: CommandInteraction) => { const { options } = interaction; - if (options?.getSubcommand() === "give") { - logger?.verbose(`Executing give subcommand`); + switch (options.getSubcommand()) { + case "give": + logger.verbose(`Executing give subcommand`); - return moduleGive.execute(interaction); + return modules.give.execute(interaction); + case "set": + logger.verbose(`Executing set subcommand`); + + return modules.set.execute(interaction); + case "take": + logger.verbose(`Executing take subcommand`); + + return modules.take.execute(interaction); + case "transfer": + logger.verbose(`Executing transfer subcommand`); + + return modules.transfer.execute(interaction); + default: + logger.verbose(`Unknown subcommand ${options.getSubcommand()}`); } - - if (options?.getSubcommand() === "set") { - logger?.verbose(`Executing set subcommand`); - - return moduleSet.execute(interaction); - } - - if (options?.getSubcommand() === "take") { - logger?.verbose(`Executing take subcommand`); - - return moduleTake.execute(interaction); - } - - if (options?.getSubcommand() === "transfer") { - logger?.verbose(`Executing transfer subcommand`); - - return moduleTransfer.execute(interaction); - } - - logger?.verbose(`No subcommand found`); }, }; diff --git a/src/plugins/manage/groups/credits/modules/give/index.ts b/src/plugins/manage/groups/credits/modules/give/index.ts index 2345852..38e272c 100644 --- a/src/plugins/manage/groups/credits/modules/give/index.ts +++ b/src/plugins/manage/groups/credits/modules/give/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; +import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Configurations @@ -21,6 +21,12 @@ import fetchUser from "@helpers/fetchUser"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("give") diff --git a/src/plugins/manage/groups/credits/modules/index.ts b/src/plugins/manage/groups/credits/modules/index.ts new file mode 100644 index 0000000..4065a60 --- /dev/null +++ b/src/plugins/manage/groups/credits/modules/index.ts @@ -0,0 +1,6 @@ +import give from "@plugins/manage/groups/credits/modules/give"; +import set from "@plugins/manage/groups/credits/modules/set"; +import take from "@plugins/manage/groups/credits/modules/take"; +import transfer from "@plugins/manage/groups/credits/modules/transfer"; + +export default { give, set, take, transfer }; diff --git a/src/plugins/manage/groups/credits/modules/set/index.ts b/src/plugins/manage/groups/credits/modules/set/index.ts index 53103a4..a2b8e18 100644 --- a/src/plugins/manage/groups/credits/modules/set/index.ts +++ b/src/plugins/manage/groups/credits/modules/set/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; +import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations import { @@ -20,6 +20,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("set") diff --git a/src/plugins/manage/groups/credits/modules/take/index.ts b/src/plugins/manage/groups/credits/modules/take/index.ts index 40e74bb..53b2864 100644 --- a/src/plugins/manage/groups/credits/modules/take/index.ts +++ b/src/plugins/manage/groups/credits/modules/take/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; +import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations import { @@ -21,6 +21,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("take") diff --git a/src/plugins/manage/groups/credits/modules/transfer/index.ts b/src/plugins/manage/groups/credits/modules/transfer/index.ts index 0dbd07d..4423ca5 100644 --- a/src/plugins/manage/groups/credits/modules/transfer/index.ts +++ b/src/plugins/manage/groups/credits/modules/transfer/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction, MessageEmbed } from "discord.js"; +import { CommandInteraction, MessageEmbed, Permissions } from "discord.js"; // Configurations import { @@ -21,6 +21,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("transfer") diff --git a/src/plugins/manage/groups/index.ts b/src/plugins/manage/groups/index.ts new file mode 100644 index 0000000..6b928fe --- /dev/null +++ b/src/plugins/manage/groups/index.ts @@ -0,0 +1,4 @@ +import counters from "@plugins/manage/groups/counters"; +import credits from "@plugins/manage/groups/credits"; + +export default { counters, credits }; diff --git a/src/plugins/manage/index.ts b/src/plugins/manage/index.ts index 32d066d..4fb4165 100644 --- a/src/plugins/manage/index.ts +++ b/src/plugins/manage/index.ts @@ -1,52 +1,35 @@ //Dependencies import { SlashCommandBuilder } from "@discordjs/builders"; -import { CommandInteraction, Permissions, MessageEmbed } from "discord.js"; - -// Configurations -import { errorColor, footerText, footerIcon } from "@config/embed"; +import { CommandInteraction } from "discord.js"; // Groups -import credits from "./groups/credits"; -import counters from "./groups/counters"; +import groups from "@plugins/manage/groups"; import logger from "@logger"; // Function export default { - metadata: { author: "Zyner" }, + groups, + data: new SlashCommandBuilder() .setName("manage") .setDescription("Manage the bot.") - .addSubcommandGroup(counters.data) - .addSubcommandGroup(credits.data), + .addSubcommandGroup(groups.counters.data) + .addSubcommandGroup(groups.credits.data), async execute(interaction: CommandInteraction) { // Destructure - const { memberPermissions, options } = interaction; - - // Check permission - if (!memberPermissions?.has(Permissions?.FLAGS?.MANAGE_GUILD)) { - return interaction?.editReply({ - embeds: [ - new MessageEmbed() - .setTitle("[:toolbox:] Manage") - .setDescription(`You do not have the permission to manage the bot.`) - .setTimestamp(new Date()) - .setColor(errorColor) - .setFooter({ text: footerText, iconURL: footerIcon }), - ], - }); - } + const { options } = interaction; if (options?.getSubcommandGroup() === "credits") { logger?.verbose(`Subcommand group is credits`); - return credits.execute(interaction); + return groups.credits.execute(interaction); } if (options?.getSubcommandGroup() === "counters") { logger?.verbose(`Subcommand group is counters`); - return counters.execute(interaction); + return groups.counters.execute(interaction); } logger?.verbose(`Subcommand group is not credits or counters`); diff --git a/src/plugins/profile/index.ts b/src/plugins/profile/index.ts index a255a7a..1908543 100644 --- a/src/plugins/profile/index.ts +++ b/src/plugins/profile/index.ts @@ -3,34 +3,26 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; // Modules -import view from "./modules/view"; +import modules from "@plugins/profile/modules"; // Handlers import logger from "@logger"; // Function export default { - metadata: { author: "Zyner" }, + modules, + data: new SlashCommandBuilder() .setName("profile") .setDescription("Check a profile.") - .addSubcommand((subcommand) => - subcommand - .setName("view") - .setDescription("View a profile.") - .addUserOption((option) => - option - .setName("target") - .setDescription("The profile you wish to view") - ) - ), + .addSubcommand(modules.view.data), async execute(interaction: CommandInteraction) { const { options } = interaction; if (options?.getSubcommand() === "view") { logger?.verbose(`Executing view subcommand`); - return view(interaction); + return modules.view.execute(interaction); } logger?.verbose(`No subcommand found`); diff --git a/src/plugins/profile/modules/index.ts b/src/plugins/profile/modules/index.ts new file mode 100644 index 0000000..1dc8e1b --- /dev/null +++ b/src/plugins/profile/modules/index.ts @@ -0,0 +1,3 @@ +import view from "@plugins/profile/modules/view"; + +export default { view }; diff --git a/src/plugins/profile/modules/view.ts b/src/plugins/profile/modules/view.ts index 6e05df5..81bfc52 100644 --- a/src/plugins/profile/modules/view.ts +++ b/src/plugins/profile/modules/view.ts @@ -8,68 +8,82 @@ import { successColor, footerText, footerIcon } from "@config/embed"; import fetchUser from "@helpers/fetchUser"; import logger from "@logger"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function -export default async (interaction: CommandInteraction) => { - // Destructure - const { client, options, user, guild } = interaction; +export default { + meta: { guildOnly: true, ephemeral: false }, - // Target information - const target = options?.getUser("target"); + data: (command: SlashCommandSubcommandBuilder) => { + return command + .setName("view") + .setDescription("View a profile.") + .addUserOption((option) => + option.setName("target").setDescription("The profile you wish to view") + ); + }, - // Discord User Information - const discordUser = await client?.users?.fetch( - `${target ? target?.id : user?.id}` - ); + execute: async (interaction: CommandInteraction) => { + // Destructure + const { client, options, user, guild } = interaction; - if (guild === null) { - return logger?.verbose(`Guild is null`); - } + // Target information + const target = options?.getUser("target"); - // User Information - const userObj = await fetchUser(discordUser, guild); + // Discord User Information + const discordUser = await client?.users?.fetch( + `${target ? target?.id : user?.id}` + ); - // Embed object - const embed = { - author: { - name: `${discordUser?.username}#${discordUser?.discriminator}`, - icon_url: discordUser?.displayAvatarURL(), - }, - color: successColor, - fields: [ - { - name: `:dollar: Credits`, - value: `${userObj?.credits || "Not found"}`, - inline: true, + if (guild === null) { + return logger?.verbose(`Guild is null`); + } + + // User Information + const userObj = await fetchUser(discordUser, guild); + + // Embed object + const embed = { + author: { + name: `${discordUser?.username}#${discordUser?.discriminator}`, + icon_url: discordUser?.displayAvatarURL(), }, - { - name: `:squeeze_bottle: Level`, - value: `${userObj?.level || "Not found"}`, - inline: true, + color: successColor, + fields: [ + { + name: `:dollar: Credits`, + value: `${userObj?.credits || "Not found"}`, + inline: true, + }, + { + name: `:squeeze_bottle: Level`, + value: `${userObj?.level || "Not found"}`, + inline: true, + }, + { + name: `:squeeze_bottle: Points`, + value: `${userObj?.points || "Not found"}`, + inline: true, + }, + { + name: `:loudspeaker: Reputation`, + value: `${userObj?.reputation || "Not found"}`, + inline: true, + }, + { + name: `:rainbow_flag: Language`, + value: `${userObj?.language || "Not found"}`, + inline: true, + }, + ], + timestamp: new Date(), + footer: { + iconURL: footerIcon, + text: footerText, }, - { - name: `:squeeze_bottle: Points`, - value: `${userObj?.points || "Not found"}`, - inline: true, - }, - { - name: `:loudspeaker: Reputation`, - value: `${userObj?.reputation || "Not found"}`, - inline: true, - }, - { - name: `:rainbow_flag: Language`, - value: `${userObj?.language || "Not found"}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }; + }; - // Return interaction reply - return interaction?.editReply({ embeds: [embed] }); + // Return interaction reply + return interaction?.editReply({ embeds: [embed] }); + }, }; diff --git a/src/plugins/reputation/index.ts b/src/plugins/reputation/index.ts index 4908641..7fd5218 100644 --- a/src/plugins/reputation/index.ts +++ b/src/plugins/reputation/index.ts @@ -3,25 +3,25 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; // Modules -import give from "./modules/give"; +import modules from "./modules"; // Handlers import logger from "@logger"; // Function export default { - metadata: { author: "Zyner" }, + modules, data: new SlashCommandBuilder() .setName("reputation") .setDescription("Manage reputation.") - .addSubcommand(give.data), + .addSubcommand(modules.give.data), async execute(interaction: CommandInteraction) { const { options } = interaction; if (options?.getSubcommand() === "give") { logger?.verbose(`Executing give subcommand`); - await give.execute(interaction); + await modules.give.execute(interaction); } logger?.verbose(`No subcommand found`); diff --git a/src/plugins/reputation/modules/give.ts b/src/plugins/reputation/modules/give.ts index 4da08db..c3cdf3c 100644 --- a/src/plugins/reputation/modules/give.ts +++ b/src/plugins/reputation/modules/give.ts @@ -21,6 +21,8 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { guildOnly: true, ephemeral: true }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("give") @@ -36,8 +38,13 @@ export default { .setName("type") .setDescription("What type of reputation you want to repute") .setRequired(true) - .addChoice("Positive", "positive") - .addChoice("Negative", "negative") + .addChoices( + { name: "Positive", value: "positive" }, + { + name: "Negative", + value: "negative", + } + ) ); }, execute: async (interaction: CommandInteraction) => { diff --git a/src/plugins/reputation/modules/index.ts b/src/plugins/reputation/modules/index.ts new file mode 100644 index 0000000..f6746fd --- /dev/null +++ b/src/plugins/reputation/modules/index.ts @@ -0,0 +1,3 @@ +import give from "@plugins/reputation/modules/give"; + +export default { give }; diff --git a/src/plugins/settings/groups/guild/index.ts b/src/plugins/settings/groups/guild/index.ts new file mode 100644 index 0000000..b1344e1 --- /dev/null +++ b/src/plugins/settings/groups/guild/index.ts @@ -0,0 +1,60 @@ +// Dependencies +import { CommandInteraction } from "discord.js"; + +// Handlers +import logger from "@logger"; + +// Modules +import modules from "./modules"; + +import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; + +// Function +export default { + modules, + + data: (group: SlashCommandSubcommandGroupBuilder) => { + return group + .setName("guild") + .setDescription("Guild settings.") + .addSubcommand(modules.pterodactyl.data) + .addSubcommand(modules.credits.data) + .addSubcommand(modules.points.data) + .addSubcommand(modules.welcome.data) + .addSubcommand(modules.audits.data) + .addSubcommand(modules.shop.data); + }, + execute: async (interaction: CommandInteraction) => { + // Destructure member + const { options } = interaction; + + switch (options?.getSubcommand()) { + case "pterodactyl": + logger?.verbose(`Subcommand is pterodactyl`); + + return modules.pterodactyl.execute(interaction); + case "credits": + logger?.verbose(`Subcommand is credits`); + + return modules.credits.execute(interaction); + case "points": + logger?.verbose(`Subcommand is points`); + + return modules.points.execute(interaction); + case "welcome": + logger?.verbose(`Subcommand is welcome`); + + return modules.welcome.execute(interaction); + case "audits": + logger?.verbose(`Subcommand is audits`); + + return modules.audits.execute(interaction); + case "shop": + logger?.verbose(`Subcommand is shop`); + + return modules.shop.execute(interaction); + default: + logger?.verbose(`Subcommand is not found`); + } + }, +}; diff --git a/src/plugins/settings/guild/modules/audits.ts b/src/plugins/settings/groups/guild/modules/audits.ts similarity index 91% rename from src/plugins/settings/guild/modules/audits.ts rename to src/plugins/settings/groups/guild/modules/audits.ts index f04ff97..c54b4c2 100644 --- a/src/plugins/settings/guild/modules/audits.ts +++ b/src/plugins/settings/groups/guild/modules/audits.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction } from "discord.js"; +import { CommandInteraction, Permissions } from "discord.js"; // Configurations import { successColor, footerText, footerIcon } from "@config/embed"; @@ -14,6 +14,12 @@ import { ChannelType } from "discord-api-types/v10"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("audits") @@ -25,7 +31,7 @@ export default { option .setName("channel") .setDescription("Channel for audit messages.") - .addChannelType(ChannelType.GuildText as number) + .addChannelTypes(ChannelType.GuildText) ); }, execute: async (interaction: CommandInteraction) => { diff --git a/src/plugins/settings/guild/modules/credits.ts b/src/plugins/settings/groups/guild/modules/credits.ts similarity index 95% rename from src/plugins/settings/guild/modules/credits.ts rename to src/plugins/settings/groups/guild/modules/credits.ts index f7bd893..11589ab 100644 --- a/src/plugins/settings/guild/modules/credits.ts +++ b/src/plugins/settings/groups/guild/modules/credits.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction } from "discord.js"; +import { CommandInteraction, Permissions } from "discord.js"; // Configurations import { successColor, footerText, footerIcon } from "@config/embed"; @@ -13,6 +13,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("credits") diff --git a/src/plugins/settings/groups/guild/modules/index.ts b/src/plugins/settings/groups/guild/modules/index.ts new file mode 100644 index 0000000..9a5fcd6 --- /dev/null +++ b/src/plugins/settings/groups/guild/modules/index.ts @@ -0,0 +1,8 @@ +import audits from "@plugins/settings/groups/guild/modules/audits"; +import credits from "@plugins/settings/groups/guild/modules/credits"; +import points from "@plugins/settings/groups/guild/modules/points"; +import pterodactyl from "@plugins/settings/groups/guild/modules/pterodactyl"; +import shop from "@plugins/settings/groups/guild/modules/shop"; +import welcome from "@plugins/settings/groups/guild/modules/welcome"; + +export default { audits, credits, points, pterodactyl, shop, welcome }; diff --git a/src/plugins/settings/guild/modules/points.ts b/src/plugins/settings/groups/guild/modules/points.ts similarity index 94% rename from src/plugins/settings/guild/modules/points.ts rename to src/plugins/settings/groups/guild/modules/points.ts index f484f22..75ea6a7 100644 --- a/src/plugins/settings/guild/modules/points.ts +++ b/src/plugins/settings/groups/guild/modules/points.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction } from "discord.js"; +import { CommandInteraction, Permissions } from "discord.js"; // Configurations import { successColor, footerText, footerIcon } from "@config/embed"; @@ -13,6 +13,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("points") diff --git a/src/plugins/settings/guild/modules/pterodactyl.ts b/src/plugins/settings/groups/guild/modules/pterodactyl.ts similarity index 91% rename from src/plugins/settings/guild/modules/pterodactyl.ts rename to src/plugins/settings/groups/guild/modules/pterodactyl.ts index 14b0bf4..b595b80 100644 --- a/src/plugins/settings/guild/modules/pterodactyl.ts +++ b/src/plugins/settings/groups/guild/modules/pterodactyl.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction } from "discord.js"; +import { CommandInteraction, Permissions } from "discord.js"; // Configurations import { successColor, footerText, footerIcon } from "@config/embed"; @@ -14,6 +14,12 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("pterodactyl") diff --git a/src/plugins/settings/guild/modules/shop.ts b/src/plugins/settings/groups/guild/modules/shop.ts similarity index 93% rename from src/plugins/settings/guild/modules/shop.ts rename to src/plugins/settings/groups/guild/modules/shop.ts index 5c633a0..785c161 100644 --- a/src/plugins/settings/guild/modules/shop.ts +++ b/src/plugins/settings/groups/guild/modules/shop.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction } from "discord.js"; +import { CommandInteraction, Permissions } from "discord.js"; // Configurations import { successColor, footerText, footerIcon } from "@config/embed"; @@ -10,10 +10,15 @@ import logger from "@logger"; // Models import guildSchema from "@schemas/guild"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import { ChannelType } from "discord-api-types/v10"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("shop") diff --git a/src/plugins/settings/guild/modules/welcome.ts b/src/plugins/settings/groups/guild/modules/welcome.ts similarity index 93% rename from src/plugins/settings/guild/modules/welcome.ts rename to src/plugins/settings/groups/guild/modules/welcome.ts index d45b642..ad0c05b 100644 --- a/src/plugins/settings/guild/modules/welcome.ts +++ b/src/plugins/settings/groups/guild/modules/welcome.ts @@ -1,5 +1,5 @@ // Dependencies -import { CommandInteraction } from "discord.js"; +import { CommandInteraction, Permissions } from "discord.js"; // Configurations import { successColor, footerText, footerIcon } from "@config/embed"; @@ -14,6 +14,12 @@ import { ChannelType } from "discord-api-types/v10"; // Function export default { + meta: { + guildOnly: true, + ephemeral: true, + permissions: [Permissions.FLAGS.MANAGE_GUILD], + }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("welcome") @@ -25,14 +31,16 @@ export default { option .setName("join-channel") .setDescription("Channel for join messages.") - .addChannelType(ChannelType.GuildText as number) + .addChannelTypes(ChannelType.GuildText) ) + .addChannelOption((option) => option .setName("leave-channel") .setDescription("Channel for leave messages.") - .addChannelType(ChannelType.GuildText as number) + .addChannelTypes(ChannelType.GuildText) ) + .addStringOption((option) => option .setName("leave-message") diff --git a/src/plugins/settings/groups/index.ts b/src/plugins/settings/groups/index.ts new file mode 100644 index 0000000..3271a99 --- /dev/null +++ b/src/plugins/settings/groups/index.ts @@ -0,0 +1,3 @@ +import guild from "@plugins/settings/groups/guild"; + +export default { guild }; diff --git a/src/plugins/settings/guild/index.ts b/src/plugins/settings/guild/index.ts deleted file mode 100644 index 37218bd..0000000 --- a/src/plugins/settings/guild/index.ts +++ /dev/null @@ -1,94 +0,0 @@ -// Dependencies -import { Permissions, CommandInteraction } from "discord.js"; - -// Configurations -import { errorColor, footerText, footerIcon } from "@config/embed"; - -// Handlers -import logger from "@logger"; - -// Modules -import pterodactyl from "./modules/pterodactyl"; -import credits from "./modules/credits"; -import points from "./modules/points"; -import welcome from "./modules/welcome"; -import audits from "./modules/audits"; -import shop from "./modules/shop"; -import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; - -// Function -export default { - data: (group: SlashCommandSubcommandGroupBuilder) => { - return group - .setName("guild") - .setDescription("Guild settings.") - .addSubcommand(pterodactyl.data) - .addSubcommand(credits.data) - .addSubcommand(points.data) - .addSubcommand(welcome.data) - .addSubcommand(audits.data) - .addSubcommand(shop.data); - }, - execute: async (interaction: CommandInteraction) => { - // Destructure member - const { memberPermissions, options } = interaction; - - // Check permission - if (!memberPermissions?.has(Permissions?.FLAGS?.MANAGE_GUILD)) { - logger?.verbose(`User does not have permission to execute command.`); - - return interaction?.editReply({ - embeds: [ - { - title: ":tools: Settings - Guild", - color: errorColor, - description: "You do not have permission to use this command.", - timestamp: new Date(), - footer: { - iconURL: footerIcon as string, - text: footerText as string, - }, - }, - ], - }); - } - - if (options?.getSubcommand() === "pterodactyl") { - logger?.verbose(`Executing pterodactyl subcommand`); - - return pterodactyl.execute(interaction); - } - - if (options?.getSubcommand() === "credits") { - logger?.verbose(`Executing credits subcommand`); - - return credits.execute(interaction); - } - - if (options?.getSubcommand() === "points") { - logger?.verbose(`Executing points subcommand`); - - return points.execute(interaction); - } - - if (options?.getSubcommand() === "welcome") { - logger?.verbose(`Executing welcome subcommand`); - - return welcome.execute(interaction); - } - - if (options?.getSubcommand() === "audits") { - logger?.verbose(`Executing audit subcommand`); - - return audits.execute(interaction); - } - - if (options?.getSubcommand() === "shop") { - logger?.verbose(`Executing shop subcommand`); - - return shop.execute(interaction); - } - - logger?.verbose(`No subcommand found`); - }, -}; diff --git a/src/plugins/settings/index.ts b/src/plugins/settings/index.ts index 682fc30..c657e43 100644 --- a/src/plugins/settings/index.ts +++ b/src/plugins/settings/index.ts @@ -3,20 +3,20 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; // Groups -import guildGroup from "./guild"; -import userGroup from "./user"; +import groups from "./groups"; // Handlers import logger from "@logger"; // Function export default { - metadata: { author: "Zyner" }, + groups, + data: new SlashCommandBuilder() .setName("settings") .setDescription("Manage settings.") - .addSubcommandGroup(guildGroup.data) - .addSubcommandGroup(userGroup.data), + + .addSubcommandGroup(groups.guild.data), async execute(interaction: CommandInteraction) { const { options } = interaction; @@ -24,13 +24,7 @@ export default { if (options.getSubcommandGroup() === "guild") { logger.verbose(`Executing guild subcommand`); - return guildGroup.execute(interaction); - } - - if (options.getSubcommandGroup() === "user") { - logger.verbose(`Executing user subcommand`); - - return userGroup.execute(interaction); + return groups.guild.execute(interaction); } logger.verbose(`No subcommand group found`); diff --git a/src/plugins/settings/user/index.ts b/src/plugins/settings/user/index.ts deleted file mode 100644 index e3c572f..0000000 --- a/src/plugins/settings/user/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Dependencies -import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; -import { CommandInteraction } from "discord.js"; - -// Handlers -import logger from "@logger"; - -// Modules -import appearance from "./modules/appearance"; - -// Function -export default { - data: (group: SlashCommandSubcommandGroupBuilder) => { - return group - .setName("user") - .setDescription("User settings.") - .addSubcommand((command) => - command - .setName("appearance") - .setDescription("User appearance settings.") - .addStringOption((option) => - option - .setName("language") - .setDescription("Set the language.") - .addChoice("English", "en") - .addChoice("Swedish", "sv") - ) - ); - }, - execute: async (interaction: CommandInteraction) => { - const { options } = interaction; - - if (options?.getSubcommand() === "appearance") { - logger?.verbose(`Executing appearance subcommand`); - - await appearance(interaction); - } - - logger?.verbose(`No subcommand found`); - }, -}; diff --git a/src/plugins/settings/user/modules/appearance.ts b/src/plugins/settings/user/modules/appearance.ts deleted file mode 100644 index bf0a4eb..0000000 --- a/src/plugins/settings/user/modules/appearance.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Dependencies -import { CommandInteraction } from "discord.js"; - -// Configurations -import { successColor, footerText, footerIcon } from "@config/embed"; - -// Handlers -import logger from "@logger"; - -// Models -import fetchUser from "@helpers/fetchUser"; - -// Function -export default async (interaction: CommandInteraction) => { - // Destructure member - const { options, user, guild } = interaction; - - // Get options - const language = options?.getString("language"); - - if (guild === null) { - return logger?.verbose(`Guild is null`); - } - - // Get user object - const userDB = await fetchUser(user, guild); - - if (userDB === null) { - return logger?.verbose(`User is null`); - } - - // Modify values - userDB.language = language !== null ? language : userDB?.language; - - // Save guild - await userDB?.save()?.then(async () => { - logger?.verbose(`Updated user language.`); - - return interaction?.editReply({ - embeds: [ - { - title: ":hammer: Settings - User [Appearance]", - description: "Successfully updated user settings.", - color: successColor, - fields: [ - { - name: "🏳️‍🌈 Language", - value: `${userDB?.language}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }, - ], - }); - }); -}; diff --git a/src/plugins/shop/groups/index.ts b/src/plugins/shop/groups/index.ts new file mode 100644 index 0000000..c1c25c9 --- /dev/null +++ b/src/plugins/shop/groups/index.ts @@ -0,0 +1,3 @@ +import roles from "./roles"; + +export default { roles }; diff --git a/src/plugins/shop/roles/index.ts b/src/plugins/shop/groups/roles/index.ts similarity index 84% rename from src/plugins/shop/roles/index.ts rename to src/plugins/shop/groups/roles/index.ts index 96ae1ca..26135e6 100644 --- a/src/plugins/shop/roles/index.ts +++ b/src/plugins/shop/groups/roles/index.ts @@ -3,24 +3,25 @@ import { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; // Handlers -import logger from "../../../logger"; +import logger from "@logger"; import { errorColor, footerText, footerIcon } from "@config/embed"; // Modules -import buy from "./modules/buy"; -import cancel from "./modules/cancel"; +import modules from "./modules"; import guildSchema from "@schemas/guild"; // Function export default { + modules, + data: (group: SlashCommandSubcommandGroupBuilder) => { return group .setName("roles") .setDescription("Shop for custom roles.") - .addSubcommand(buy.data) - .addSubcommand(cancel.data); + .addSubcommand(modules.buy.data) + .addSubcommand(modules.cancel.data); }, execute: async (interaction: CommandInteraction) => { const { options, guild } = interaction; @@ -53,13 +54,13 @@ export default { if (options?.getSubcommand() === "buy") { logger.verbose(`Executing buy subcommand`); - await buy.execute(interaction); + await modules.buy.execute(interaction); } if (options?.getSubcommand() === "cancel") { logger.verbose(`Executing cancel subcommand`); - await cancel.execute(interaction); + await modules.cancel.execute(interaction); } }, }; diff --git a/src/plugins/shop/roles/modules/buy.ts b/src/plugins/shop/groups/roles/modules/buy.ts similarity index 97% rename from src/plugins/shop/roles/modules/buy.ts rename to src/plugins/shop/groups/roles/modules/buy.ts index 0baf7ed..1f8c54b 100644 --- a/src/plugins/shop/roles/modules/buy.ts +++ b/src/plugins/shop/groups/roles/modules/buy.ts @@ -25,6 +25,8 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { guildOnly: true, ephemeral: true }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("buy") @@ -33,11 +35,13 @@ export default { option .setName("name") .setDescription("Name of the role you wish to buy.") + .setRequired(true) ) .addStringOption((option) => option .setName("color") .setDescription("Color of the role you wish to buy.") + .setRequired(true) ); }, execute: async (interaction: CommandInteraction) => { diff --git a/src/plugins/shop/roles/modules/cancel.ts b/src/plugins/shop/groups/roles/modules/cancel.ts similarity index 94% rename from src/plugins/shop/roles/modules/cancel.ts rename to src/plugins/shop/groups/roles/modules/cancel.ts index 359d79c..b35ef16 100644 --- a/src/plugins/shop/roles/modules/cancel.ts +++ b/src/plugins/shop/groups/roles/modules/cancel.ts @@ -20,12 +20,17 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { guildOnly: true, ephemeral: true }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("cancel") .setDescription("Cancel a purchase.") .addRoleOption((option) => - option.setName("role").setDescription("Role you wish to cancel.") + option + .setName("role") + .setDescription("Role you wish to cancel.") + .setRequired(true) ); }, execute: async (interaction: CommandInteraction) => { diff --git a/src/plugins/shop/groups/roles/modules/index.ts b/src/plugins/shop/groups/roles/modules/index.ts new file mode 100644 index 0000000..b9e1626 --- /dev/null +++ b/src/plugins/shop/groups/roles/modules/index.ts @@ -0,0 +1,7 @@ +import buy from "./buy"; +import cancel from "./cancel"; + +export default { + buy, + cancel, +}; diff --git a/src/plugins/shop/index.ts b/src/plugins/shop/index.ts index 7d222a8..f9af6d4 100644 --- a/src/plugins/shop/index.ts +++ b/src/plugins/shop/index.ts @@ -3,35 +3,37 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; // Modules -import pterodactyl from "./modules/pterodactyl"; +import modules from "./modules"; // Groups -import roles from "./roles"; +import groups from "./groups"; // Handlers import logger from "../../logger"; // Function export default { - metadata: { author: "Zyner" }, + modules, + groups, + data: new SlashCommandBuilder() .setName("shop") .setDescription("Shop for credits and custom roles.") - .addSubcommand(pterodactyl.data) - .addSubcommandGroup(roles.data), + .addSubcommand(modules.pterodactyl.data) + .addSubcommandGroup(groups.roles.data), async execute(interaction: CommandInteraction) { const { options } = interaction; if (options?.getSubcommand() === "pterodactyl") { logger.verbose(`Executing pterodactyl subcommand`); - return pterodactyl.execute(interaction); + return modules.pterodactyl.execute(interaction); } if (options?.getSubcommandGroup() === "roles") { logger?.verbose(`Subcommand group is roles`); - return roles.execute(interaction); + return groups.roles.execute(interaction); } logger?.verbose(`No subcommand found.`); diff --git a/src/plugins/shop/modules/index.ts b/src/plugins/shop/modules/index.ts new file mode 100644 index 0000000..bc33df3 --- /dev/null +++ b/src/plugins/shop/modules/index.ts @@ -0,0 +1,3 @@ +import pterodactyl from "@plugins/shop/modules/pterodactyl"; + +export default { pterodactyl }; diff --git a/src/plugins/shop/modules/pterodactyl.ts b/src/plugins/shop/modules/pterodactyl.ts index 01cd120..6aeed22 100644 --- a/src/plugins/shop/modules/pterodactyl.ts +++ b/src/plugins/shop/modules/pterodactyl.ts @@ -1,9 +1,7 @@ -// Dependencies import { CommandInteraction } from "discord.js"; import { v4 as uuidv4 } from "uuid"; import axios from "axios"; -// Configurations import { successColor, errorColor, @@ -11,20 +9,18 @@ import { footerIcon, } from "@config/embed"; -// Handlers import logger from "@logger"; import encryption from "@handlers/encryption"; -// Helpers import pluralize from "@helpers/pluralize"; -// Models import apiSchema from "@schemas/api"; import fetchUser from "@helpers/fetchUser"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -// Function export default { + meta: { guildOnly: true, ephemeral: true }, + data: (command: SlashCommandSubcommandBuilder) => { return command .setName("pterodactyl") @@ -38,10 +34,8 @@ export default { execute: async (interaction: CommandInteraction) => { const { options, guild, user, client } = interaction; - // Get options const optionAmount = options?.getInteger("amount"); - // If amount is null if (optionAmount === null) { logger?.verbose(`Amount is null.`); @@ -65,17 +59,14 @@ export default { return logger?.verbose(`Guild is null`); } - // Get user object const userDB = await fetchUser(user, guild); if (userDB === null) { return logger?.verbose(`User is null`); } - // Get DM user object const dmUser = client?.users?.cache?.get(user?.id); - // Stop if amount or user credits is below 100 if ((optionAmount || userDB?.credits) < 100) { logger?.verbose(`Amount or user credits is below 100.`); @@ -101,7 +92,6 @@ export default { }); } - // Stop if amount or user credits is above 1.000.000 if ((optionAmount || userDB?.credits) > 1000000) { logger?.verbose(`Amount or user credits is above 1.000.000.`); @@ -128,7 +118,6 @@ export default { }); } - // Stop if user credits is below amount if (userDB?.credits < optionAmount) { logger?.verbose(`User credits is below amount.`); @@ -154,15 +143,12 @@ export default { }); } - // Generate a unique voucher for the user const code = uuidv4(); - // Get api object const apiCredentials = await apiSchema?.findOne({ guildId: guild?.id, }); - // Create a api instance const api = axios?.create({ baseURL: apiCredentials?.url, headers: { @@ -170,13 +156,10 @@ export default { }, }); - // Get shop URL const shopUrl = apiCredentials?.url?.replace("/api", "/store"); - // Make API request await api - // Make a post request to the API ?.post("vouchers", { uses: 1, code, @@ -184,17 +167,14 @@ export default { memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`, }) - // If successful ?.then(async () => { logger?.verbose(`Successfully created voucher.`); - // Withdraw amount from user credits userDB.credits -= optionAmount || userDB?.credits; - // Save new credits await userDB ?.save() - // If successful + ?.then(async () => { logger?.verbose(`Successfully saved new credits.`); @@ -237,7 +217,6 @@ export default { }); }) - // If error occurs .catch(async (error) => { logger?.verbose(`Error saving new credits. - ${error}`); @@ -258,7 +237,6 @@ export default { }); }) - // If error occurs .catch(async (error: any) => { logger?.verbose(`Error creating voucher. - ${error}`); diff --git a/src/plugins/utilities/index.ts b/src/plugins/utilities/index.ts deleted file mode 100644 index 23c43c3..0000000 --- a/src/plugins/utilities/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Dependencies -import { SlashCommandBuilder } from "@discordjs/builders"; -import { CommandInteraction } from "discord.js"; - -// Modules -import lookup from "./modules/lookup"; -import about from "./modules/about"; -import stats from "./modules/stats"; - -// Handlers -import logger from "../../logger"; - -// Function -export default { - metadata: { author: "Zyner" }, - data: new SlashCommandBuilder() - .setName("utilities") - .setDescription("Common utilities.") - .addSubcommand(lookup.data) - .addSubcommand(about.data) - .addSubcommand(stats.data), - async execute(interaction: CommandInteraction) { - const { options } = interaction; - - if (options?.getSubcommand() === "lookup") { - logger.verbose(`Executing lookup subcommand`); - - return lookup.execute(interaction); - } - - if (options?.getSubcommand() === "about") { - logger.verbose(`Executing about subcommand`); - - return about.execute(interaction); - } - - if (options?.getSubcommand() === "stats") { - logger.verbose(`Executing stats subcommand`); - - return stats.execute(interaction); - } - - logger.verbose(`No subcommand found.`); - }, -}; diff --git a/src/plugins/utilities/modules/lookup.ts b/src/plugins/utilities/modules/lookup.ts deleted file mode 100644 index 6175418..0000000 --- a/src/plugins/utilities/modules/lookup.ts +++ /dev/null @@ -1,134 +0,0 @@ -// Dependencies -import axios from "axios"; -import { CommandInteraction } from "discord.js"; - -// Configurations -import { - successColor, - errorColor, - footerText, - footerIcon, -} from "@config/embed"; - -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; - -// Handlers -import logger from "@logger"; - -// Function -export default { - data: (command: SlashCommandSubcommandBuilder) => { - return command - .setName("lookup") - .setDescription( - "Lookup a domain or ip. (Request sent over HTTP, proceed with caution!)" - ) - .addStringOption((option) => - option - .setName("query") - .setDescription("The query you want to look up.") - .setRequired(true) - ); - }, - execute: async (interaction: CommandInteraction) => { - const { options } = interaction; - // Get lookup query - const query = options?.getString("query"); - - // Make API request - await axios - // Make a get request - ?.get(`http://ip-api.com/json/${query}`) - - // If successful - ?.then(async (res) => { - // If query failed - if (res?.data?.status === "fail") { - // Create embed object - const embed = { - title: ":hammer: Utilities - Lookup", - description: `${res?.data?.message}: ${res?.data?.query}`, - color: errorColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }; - - // Send interaction reply - await interaction?.editReply({ embeds: [embed] }); - } - - // If query is successful - else if (res?.data?.status === "success") { - // Create embed object - const embed = { - title: ":hammer: Utilities - Lookup", - fields: [ - { - name: "AS", - value: `${res?.data?.as || "Not available"}`, - }, - { - name: "Country", - value: `${res?.data?.country || "Not available"}`, - }, - { - name: "Country Code", - value: `${res?.data?.countryCode || "Not available"}`, - }, - { - name: "Region", - value: `${res?.data?.region || "Not available"}`, - }, - { - name: "Region Name", - value: `${res?.data?.regionName || "Not available"}`, - }, - { - name: "City", - value: `${res?.data?.city || "Not available"}`, - }, - { - name: "ZIP Code", - value: `${res?.data?.zip || "Not available"}`, - }, - { - name: "Latitude", - value: `${res?.data?.lat || "Not available"}`, - }, - { - name: "Longitude", - value: `${res?.data?.lon || "Not available"}`, - }, - { - name: "Timezone", - value: `${res?.data?.timezone || "Not available"}`, - }, - { - name: "ISP", - value: `${res?.data?.isp || "Not available"}`, - }, - { - name: "Organization", - value: `${res?.data?.org || "Not available"}`, - }, - ], - color: successColor, - timestamp: new Date(), - footer: { - iconURL: footerIcon, - text: footerText, - }, - }; - - // Send interaction reply - await interaction?.editReply({ embeds: [embed] }); - } - }) - .catch(async (e) => { - logger?.error(e); - }); - }, -}; diff --git a/src/plugins/utility/index.ts b/src/plugins/utility/index.ts new file mode 100644 index 0000000..69e6268 --- /dev/null +++ b/src/plugins/utility/index.ts @@ -0,0 +1,40 @@ +// Dependencies +import { SlashCommandBuilder } from "@discordjs/builders"; +import { CommandInteraction } from "discord.js"; + +// Modules +import modules from "@plugins/utility/modules"; + +// Handlers +import logger from "../../logger"; + +// Function +export default { + modules, + + data: new SlashCommandBuilder() + .setName("utility") + .setDescription("Common utility.") + + .addSubcommand(modules.lookup.data) + .addSubcommand(modules.about.data) + .addSubcommand(modules.stats.data) + .addSubcommand(modules.avatar.data), + + async execute(interaction: CommandInteraction) { + const { options } = interaction; + + switch (options.getSubcommand()) { + case "lookup": + return modules.lookup.execute(interaction); + case "about": + return modules.about.execute(interaction); + case "stats": + return modules.stats.execute(interaction); + case "avatar": + return modules.avatar.execute(interaction); + default: + logger.error(`Unknown subcommand ${options.getSubcommand()}`); + } + }, +}; diff --git a/src/plugins/utilities/modules/about.ts b/src/plugins/utility/modules/about.ts similarity index 95% rename from src/plugins/utilities/modules/about.ts rename to src/plugins/utility/modules/about.ts index f618149..cfeb44e 100644 --- a/src/plugins/utilities/modules/about.ts +++ b/src/plugins/utility/modules/about.ts @@ -9,6 +9,8 @@ import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; // Function export default { + meta: { guildOnly: false, ephemeral: false }, + data: (command: SlashCommandSubcommandBuilder) => { return command.setName("about").setDescription("About this bot!)"); }, diff --git a/src/plugins/utility/modules/avatar.ts b/src/plugins/utility/modules/avatar.ts new file mode 100644 index 0000000..9f57fb3 --- /dev/null +++ b/src/plugins/utility/modules/avatar.ts @@ -0,0 +1,52 @@ +import { successColor, footerText, footerIcon } from "@config/embed"; + +import i18next from "i18next"; +import { CommandInteraction, MessageEmbed } from "discord.js"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; + +export default { + meta: { guildOnly: false, ephemeral: false }, + + data: (command: SlashCommandSubcommandBuilder) => { + return command + .setName("avatar") + .setDescription("Check someones avatar!)") + .addUserOption((option) => + option + .setName("user") + .setDescription("The user whose avatar you want to check") + ); + }, + execute: async (interaction: CommandInteraction) => { + const { locale } = interaction; + + const userOption = interaction.options.getUser("user"); + + const targetUser = userOption || interaction.user; + + const embed = new MessageEmbed() + .setTitle( + i18next.t("utility:modules:avatar:general:title", { + lng: locale, + ns: "plugins", + }) + ) + .setTimestamp(new Date()) + .setFooter({ text: footerText, iconURL: footerIcon }); + + return interaction.editReply({ + embeds: [ + embed + .setDescription( + i18next.t("utility:modules:avatar:success01:description", { + lng: locale, + ns: "plugins", + user: targetUser, + }) + ) + .setThumbnail(targetUser.displayAvatarURL()) + .setColor(successColor), + ], + }); + }, +}; diff --git a/src/plugins/utility/modules/index.ts b/src/plugins/utility/modules/index.ts new file mode 100644 index 0000000..80c5a64 --- /dev/null +++ b/src/plugins/utility/modules/index.ts @@ -0,0 +1,11 @@ +import avatar from "@plugins/utility/modules/avatar"; +import about from "@plugins/utility/modules/about"; +import lookup from "@plugins/utility/modules/lookup"; +import stats from "@plugins/utility/modules/stats"; + +export default { + avatar, + about, + lookup, + stats, +}; diff --git a/src/plugins/utility/modules/lookup.ts b/src/plugins/utility/modules/lookup.ts new file mode 100644 index 0000000..ac48a4d --- /dev/null +++ b/src/plugins/utility/modules/lookup.ts @@ -0,0 +1,124 @@ +import axios from "axios"; +import { CommandInteraction, MessageEmbed } from "discord.js"; + +import { + successColor, + errorColor, + footerText, + footerIcon, +} from "@config/embed"; + +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; + +import logger from "@logger"; +import embedBuilder from "@root/helpers/embedBuilder"; + +export default { + meta: { guildOnly: false, ephemeral: false }, + + data: (command: SlashCommandSubcommandBuilder) => { + return command + .setName("lookup") + .setDescription( + "Lookup a domain or ip. (Request sent over HTTP, proceed with caution!)" + ) + .addStringOption((option) => + option + .setName("query") + .setDescription("The query you want to look up.") + .setRequired(true) + ); + }, + execute: async (interaction: CommandInteraction) => { + const embedTitle = "[:hammer:] Utility (Lookup)"; + + embedBuilder.setTitle(embedTitle); + + const { options } = interaction; + const query = options.getString("query"); + + await axios + .get(`http://ip-api.com/json/${query}`) + .then(async (response) => { + if (response.data.status !== "success") { + await interaction.editReply({ + embeds: [ + embedBuilder + .setColor(errorColor) + .setDescription( + `${response?.data?.message}: ${response?.data?.query}` + ), + ], + }); + return; + } + + await interaction.editReply({ + embeds: [ + embedBuilder.setColor(successColor).setFields([ + { + name: ":classical_building: AS", + value: `${response.data.as || "Unknown"}`, + inline: true, + }, + { + name: ":classical_building: ISP", + value: `${response.data.isp || "Unknown"}`, + inline: true, + }, + { + name: ":classical_building: Organization", + value: `${response.data.org || "Unknown"}`, + inline: true, + }, + { + name: ":compass: Latitude", + value: `${response.data.lat || "Unknown"}`, + inline: true, + }, + { + name: ":compass: Longitude", + value: `${response.data.lon || "Unknown"}`, + inline: true, + }, + { + name: ":clock4: Timezone", + value: `${response.data.timezone || "Unknown"}`, + inline: true, + }, + { + name: ":globe_with_meridians: Country", + value: `${response.data.country || "Unknown"}`, + inline: true, + }, + { + name: ":globe_with_meridians: Region", + value: `${response.data.regionName || "Unknown"}`, + inline: true, + }, + { + name: ":globe_with_meridians: City", + value: `${response.data.city || "Unknown"}`, + inline: true, + }, + { + name: ":globe_with_meridians: Country Code", + value: `${response.data.countryCode || "Unknown"}`, + inline: true, + }, + { + name: ":globe_with_meridians: Region Code", + value: `${response.data.region || "Unknown"}`, + inline: true, + }, + { + name: ":globe_with_meridians: ZIP", + value: `${response.data.zip || "Unknown"}`, + inline: true, + }, + ]), + ], + }); + }); + }, +}; diff --git a/src/plugins/utilities/modules/stats.ts b/src/plugins/utility/modules/stats.ts similarity index 97% rename from src/plugins/utilities/modules/stats.ts rename to src/plugins/utility/modules/stats.ts index 277f670..3b02684 100644 --- a/src/plugins/utilities/modules/stats.ts +++ b/src/plugins/utility/modules/stats.ts @@ -2,6 +2,8 @@ import { successColor, footerText, footerIcon } from "@config/embed"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; export default { + meta: { guildOnly: false, ephemeral: false }, + data: (command: SlashCommandSubcommandBuilder) => { return command.setName("stats").setDescription("Check bot statistics!)"); }, diff --git a/src/schedules/jobs/shopRoles.ts b/src/schedules/jobs/shopRoles.ts index 877e3f1..be1767b 100644 --- a/src/schedules/jobs/shopRoles.ts +++ b/src/schedules/jobs/shopRoles.ts @@ -55,13 +55,25 @@ export default async (client: Client) => { const rRole = rMember.roles.cache.get(roleId); - if (!rRole) { - logger.error(`Role ${roleId} not found for shop role ${roleId}.`); - return; - } - - if (!rMember) { + if (!rMember || !rRole) { logger.error(`Member ${userId} not found for shop role ${roleId}.`); + await shopRoleSchema + .deleteOne({ + userId, + roleId, + guildId, + }) + .then(async () => { + logger.verbose( + `Shop role document ${roleId} has been deleted from user ${userId}.` + ); + }) + .catch(async (error) => { + logger.error( + `Error deleting shop role document ${roleId} from user ${userId}.`, + error + ); + }); return; } @@ -79,23 +91,6 @@ export default async (client: Client) => { if (!rMember) { logger.error(`Member ${userId} not found for shop role ${roleId}.`); - await shopRoleSchema - .deleteOne({ - userId, - roleId, - guildId, - }) - .then(async () => { - logger.verbose( - `Shop role document ${roleId} has been deleted from user ${userId}.` - ); - }) - .catch(async (error) => { - logger.error( - `Error deleting shop role document ${roleId} from user ${userId}.`, - error - ); - }); return; } diff --git a/tsconfig.json b/tsconfig.json index 93449a5..6fda632 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "moduleResolution": "node", "isolatedModules": true, "outDir": "./build", + "resolveJsonModule": true, "baseUrl": "./src", "typeRoots": ["/types/common", "./node_modules/@types"], "paths": {