From 1b0b9fb6bb563e5175e5e82333c4f859866eb651 Mon Sep 17 00:00:00 2001 From: Vermium Sifell Date: Sun, 10 Apr 2022 17:54:36 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20typescripted=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/counter/addons/view.ts | 47 ---- src/commands/{counter => counters}/index.ts | 71 +++--- src/commands/counters/modules/view.ts | 54 ++++ src/commands/credits/addons/balance.ts | 68 ----- src/commands/credits/addons/gift.ts | 140 ----------- src/commands/credits/addons/index.ts | 6 - src/commands/credits/addons/top.ts | 35 --- src/commands/credits/addons/work.ts | 96 ------- src/commands/credits/index.ts | 57 +++-- src/commands/credits/modules/balance.ts | 83 +++++++ src/commands/credits/modules/gift.ts | 187 ++++++++++++++ src/commands/credits/modules/top.ts | 47 ++++ src/commands/credits/modules/work.ts | 113 +++++++++ src/commands/profile/index.ts | 17 +- src/commands/profile/modules/view.ts | 54 ++-- src/commands/reputation/index.ts | 14 +- src/commands/reputation/modules/give.ts | 116 +++++---- src/commands/settings/guild/addons/credits.ts | 108 ++++---- src/commands/settings/guild/addons/index.ts | 5 - src/commands/settings/guild/addons/points.ts | 98 ++++---- .../settings/guild/addons/pterodactyl.ts | 70 +++--- src/commands/settings/guild/index.ts | 65 ++--- src/commands/settings/index.ts | 39 ++- .../settings/user/addons/appearance.ts | 49 ---- src/commands/settings/user/addons/index.ts | 3 - src/commands/settings/user/index.ts | 31 +-- .../settings/user/modules/appearance.ts | 59 +++++ src/commands/shop/addons/pterodactyl.ts | 177 ------------- src/commands/shop/addons/roles.ts | 48 ---- src/commands/shop/index.ts | 40 ++- src/commands/shop/modules/pterodactyl.ts | 235 ++++++++++++++++++ src/commands/shop/roles/addons/buy.ts | 66 ----- src/commands/shop/roles/addons/cancel.ts | 63 ----- src/commands/shop/roles/addons/index.ts | 4 - src/commands/shop/roles/index.ts | 37 +-- src/commands/shop/roles/modules/buy.ts | 99 ++++++++ src/commands/shop/roles/modules/cancel.ts | 87 +++++++ src/commands/utilities/addons/about.ts | 18 -- src/commands/utilities/addons/index.ts | 5 - src/commands/utilities/addons/lookup.ts | 87 ------- src/commands/utilities/addons/stats.ts | 53 ---- src/commands/utilities/index.ts | 48 ++-- src/commands/utilities/modules/about.ts | 26 ++ src/commands/utilities/modules/lookup.ts | 112 +++++++++ src/commands/utilities/modules/stats.ts | 58 +++++ 45 files changed, 1663 insertions(+), 1332 deletions(-) delete mode 100644 src/commands/counter/addons/view.ts rename src/commands/{counter => counters}/index.ts (51%) create mode 100644 src/commands/counters/modules/view.ts delete mode 100644 src/commands/credits/addons/balance.ts delete mode 100644 src/commands/credits/addons/gift.ts delete mode 100644 src/commands/credits/addons/index.ts delete mode 100644 src/commands/credits/addons/top.ts delete mode 100644 src/commands/credits/addons/work.ts create mode 100644 src/commands/credits/modules/balance.ts create mode 100644 src/commands/credits/modules/gift.ts create mode 100644 src/commands/credits/modules/top.ts create mode 100644 src/commands/credits/modules/work.ts delete mode 100644 src/commands/settings/guild/addons/index.ts delete mode 100644 src/commands/settings/user/addons/appearance.ts delete mode 100644 src/commands/settings/user/addons/index.ts create mode 100644 src/commands/settings/user/modules/appearance.ts delete mode 100644 src/commands/shop/addons/pterodactyl.ts delete mode 100644 src/commands/shop/addons/roles.ts create mode 100644 src/commands/shop/modules/pterodactyl.ts delete mode 100644 src/commands/shop/roles/addons/buy.ts delete mode 100644 src/commands/shop/roles/addons/cancel.ts delete mode 100644 src/commands/shop/roles/addons/index.ts create mode 100644 src/commands/shop/roles/modules/buy.ts create mode 100644 src/commands/shop/roles/modules/cancel.ts delete mode 100644 src/commands/utilities/addons/about.ts delete mode 100644 src/commands/utilities/addons/index.ts delete mode 100644 src/commands/utilities/addons/lookup.ts delete mode 100644 src/commands/utilities/addons/stats.ts create mode 100644 src/commands/utilities/modules/about.ts create mode 100644 src/commands/utilities/modules/lookup.ts create mode 100644 src/commands/utilities/modules/stats.ts diff --git a/src/commands/counter/addons/view.ts b/src/commands/counter/addons/view.ts deleted file mode 100644 index 78223d9..0000000 --- a/src/commands/counter/addons/view.ts +++ /dev/null @@ -1,47 +0,0 @@ -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import counters from '../../../helpers/database/models/counterSchema'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - try { - // Destructure member - const { member } = await interaction; - - // Get options - const channel = await interaction.options.getChannel('channel'); - - const counter = await counters.findOne({ - guildId: interaction?.guild?.id, - channelId: channel?.id, - }); - - if (!counter) { - // Create embed object - const embed = { - title: 'Counter - View', - description: `${channel} is not a counting channel.`, - timestamp: new Date(), - color: config.colors.error as any, - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return await interaction.editReply({ embeds: [embed] }); - } - - // Create embed object - const embed = { - title: 'Counter - View', - color: config.colors.success as any, - description: `${channel} is currently at number ${counter.counter}.`, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return await interaction.editReply({ embeds: [embed] }); - } catch (e) { - // Send debug message - await logger.error(e); - } -}; diff --git a/src/commands/counter/index.ts b/src/commands/counters/index.ts similarity index 51% rename from src/commands/counter/index.ts rename to src/commands/counters/index.ts index 3c4f328..1f9e35b 100644 --- a/src/commands/counter/index.ts +++ b/src/commands/counters/index.ts @@ -1,28 +1,43 @@ -import { SlashCommandBuilder } from '@discordjs/builders'; -import view from './addons/view'; - -import { CommandInteraction } from 'discord.js'; - -export default { - data: new SlashCommandBuilder() - .setName('counter') - .setDescription('Manage counters.') - .addSubcommand((subcommand) => - subcommand - .setName('view') - .setDescription('View a counter.') - .addChannelOption((option) => - option - .setName('channel') - .setDescription('The counter channel you want to view') - .setRequired(true) - ) - ), - async execute(interaction: CommandInteraction) { - // If subcommand is view - if (interaction.options.getSubcommand() === 'view') { - // Execute view addon - await view(interaction); - } - }, -}; +// Dependencies +import { CommandInteraction } from 'discord.js'; +import { SlashCommandBuilder } from '@discordjs/builders'; + +// Modules +import view from './modules/view'; + +// Handlers +import logger from '../../handlers/logger'; + +// Function +export default { + data: new SlashCommandBuilder() + .setName('counters') + .setDescription('Manage counters.') + .addSubcommand((subcommand) => + subcommand + .setName('view') + .setDescription('View a counter.') + .addChannelOption((option) => + option + .setName('channel') + .setDescription('The counter channel you want to view') + .setRequired(true) + ) + ), + async execute(interaction: CommandInteraction) { + const { options, guild, user, commandName } = interaction; + + // Module - View + if (options?.getSubcommand() === 'view') { + // Execute Module - View + return await view(interaction); + } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); + }, +}; diff --git a/src/commands/counters/modules/view.ts b/src/commands/counters/modules/view.ts new file mode 100644 index 0000000..9110a3c --- /dev/null +++ b/src/commands/counters/modules/view.ts @@ -0,0 +1,54 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Models +import counterSchema from '../../../helpers/database/models/counterSchema'; + +// Function +export default async (interaction: CommandInteraction) => { + // Destructure member + const { options, guild } = interaction; + + // Get options + const optionChannel = options?.getChannel('channel'); + + const counter = await counterSchema?.findOne({ + guildId: guild?.id, + channelId: optionChannel?.id, + }); + + if (!counter) { + // Create embed object + const embed = { + title: ':1234: Counters [View]' as string, + description: `${optionChannel} is not a counting channel.` as string, + timestamp: new Date() as Date, + color: config?.colors?.error as ColorResolvable, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // Embed object + const embed = { + title: ':1234: Counters [View]' as string, + color: config.colors.success as ColorResolvable, + description: `${optionChannel} is currently at number ${counter?.counter}.`, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); +}; diff --git a/src/commands/credits/addons/balance.ts b/src/commands/credits/addons/balance.ts deleted file mode 100644 index 17c69cb..0000000 --- a/src/commands/credits/addons/balance.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { CommandInteraction } from 'discord.js'; -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import users from '../../../helpers/database/models/userSchema'; -import creditNoun from '../../../helpers/creditNoun'; - -export default async (interaction: CommandInteraction) => { - // Get options - const user = await interaction.options.getUser('user'); - - // Get credit object - const userDB = await users.findOne({ - userId: user ? user.id : interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - // Destructure balance - const { credits } = userDB; - - // If !userDB - if (!userDB) { - // Create embed object - const embed = { - title: ':dollar: Credits - Balance', - description: `${ - user ? `${user} is` : 'You are' - } not found in the database.`, - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } - - // If !credits - if (!credits) { - // Create embed object - const embed = { - title: ':dollar: Credits - Balance', - description: `${user ? `${user} has` : 'You have'} no credits.`, - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } - - // If credits - if (credits) { - // Create embed object - const embed = { - title: ':dollar: Credits - Balance', - description: `${user ? `${user} has` : 'You have'} ${creditNoun( - credits - )}.`, - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } -}; diff --git a/src/commands/credits/addons/gift.ts b/src/commands/credits/addons/gift.ts deleted file mode 100644 index 92690b8..0000000 --- a/src/commands/credits/addons/gift.ts +++ /dev/null @@ -1,140 +0,0 @@ -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import users from '../../../helpers/database/models/userSchema'; -import saveUser from '../../../helpers/saveUser'; -import creditNoun from '../../../helpers/creditNoun'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - // Get options - const user = await interaction.options.getUser('user'); - const amount = await interaction.options.getInteger('amount'); - const reason = await interaction.options.getString('reason'); - - const { member } = interaction; - - // Get fromUserDB object - const fromUserDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - // Get toUserDB object - const toUserDB = await users.findOne({ - userId: user?.id, - guildId: interaction?.guild?.id, - }); - - // If receiver is same as sender - if (user?.id === interaction?.user?.id) { - // Create embed object - const embed = { - title: ':dollar: Credits - Gift', - description: "You can't pay yourself.", - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } - - if (amount === null) return; - - // If amount is zero or below - if (amount <= 0) { - // Create embed object - const embed = { - title: ':dollar: Credits - Gift', - description: "You can't pay zero or below.", - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } - - // If user has below gifting amount - if (fromUserDB.credits < amount) { - // Create embed - const embed = { - title: ':dollar: Credits - Gift', - description: `You have insufficient credits. Your credits is ${fromUserDB.credits}`, - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } - - // If toUserDB has no credits - if (!toUserDB) { - // Create embed object - const embed = { - title: ':dollar: Credits - Gift', - description: - 'That user has no credits, I can not gift credits to the user', - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } - - // Withdraw amount from fromUserDB - fromUserDB.credits -= amount; - - // Deposit amount to toUserDB - toUserDB.credits += amount; - - // Save users - await saveUser(fromUserDB, toUserDB).then(async () => { - // Create interaction embed object - const interactionEmbed = { - title: ':dollar: Credits - Gift', - description: `You sent ${creditNoun(amount)} to ${user}${ - reason ? ` with reason: ${reason}` : '' - }. Your new credits is ${creditNoun(fromUserDB.credits)}.`, - color: 0x22bb33, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Create DM embed object - const dmEmbed = { - title: ':dollar: Credits - Gift', - description: `You received ${creditNoun(amount)} from ${ - interaction.user - }${ - reason ? ` with reason: ${reason}` : '' - }. Your new credits is ${creditNoun(toUserDB.credits)}.`, - color: 0x22bb33, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Get DM user object - const dmUser = await interaction.client.users.cache.get( - interaction?.user?.id - ); - - // Send DM to user - await dmUser?.send({ embeds: [dmEmbed] }); - - // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${interaction?.user?.id} gift sent from: ${interaction?.user?.id} to: ${user?.id}` - ); - - // Send interaction reply - return interaction.editReply({ - embeds: [interactionEmbed], - }); - }); -}; diff --git a/src/commands/credits/addons/index.ts b/src/commands/credits/addons/index.ts deleted file mode 100644 index 9a61a6c..0000000 --- a/src/commands/credits/addons/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import balance from './balance'; -import gift from './gift'; -import top from './top'; -import work from './work'; - -export default { balance, gift, top, work }; diff --git a/src/commands/credits/addons/top.ts b/src/commands/credits/addons/top.ts deleted file mode 100644 index 175754f..0000000 --- a/src/commands/credits/addons/top.ts +++ /dev/null @@ -1,35 +0,0 @@ -import config from '../../../../config.json'; -import users from '../../../helpers/database/models/userSchema'; -import creditNoun from '../../../helpers/creditNoun'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - // Get all users in the guild - - const usersDB = await users.find({ guildId: interaction?.guild?.id }); - - const topTen = usersDB - - // Sort them after credits amount (ascending) - .sort((a, b) => (a.credits > b.credits ? -1 : 1)) - - // Return the top 10 - .slice(0, 10); - - // Create entry object - const entry = (x: any, index: any) => - `**Top ${index + 1}** - <@${x.userId}> ${creditNoun(x.credits)}`; - - // Create embed object - const embed = { - title: ':dollar: Credits - Top', - description: `Below are the top ten.\n${topTen - .map((x, index) => entry(x, index)) - .join('\n')}`, - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); -}; diff --git a/src/commands/credits/addons/work.ts b/src/commands/credits/addons/work.ts deleted file mode 100644 index 77a4ebd..0000000 --- a/src/commands/credits/addons/work.ts +++ /dev/null @@ -1,96 +0,0 @@ -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import guilds from '../../../helpers/database/models/guildSchema'; -import users from '../../../helpers/database/models/userSchema'; -import timeouts from '../../../helpers/database/models/timeoutSchema'; -import creditNoun from '../../../helpers/creditNoun'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - // Destructure member - const { member } = interaction; - - // Check if user has a timeout - const isTimeout = await timeouts.findOne({ - guildId: interaction?.guild?.id, - userId: interaction?.user?.id, - timeoutId: '2022-03-15-19-16', - }); - - const guildDB = await guilds.findOne({ - guildId: interaction?.guild?.id, - }); - - // If user is not on timeout - if (!isTimeout) { - // Make a variable of how much credits user will earn based on random multiplied with work rate - const creditsEarned = Math.floor(Math.random() * guildDB.credits.workRate); - - const userDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - userDB.credits += creditsEarned; - - await userDB.save().then(async () => { - // Send debug message - await logger.debug(`Credits added to user: ${interaction?.user?.id}`); - - // Create embed object - const embed = { - title: ':dollar: Credits - Work', - description: `You have earned ${creditNoun(creditsEarned)}`, - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - }); - - // Create a timeout for the user - await timeouts.create({ - guildId: interaction?.guild?.id, - userId: interaction?.user?.id, - timeoutId: '2022-03-15-19-16', - }); - - setTimeout(async () => { - // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${ - interaction?.user?.id - } has not worked within the last ${ - guildDB.work.timeout / 1000 - } seconds, work can be done` - ); - - // When timeout is out, remove it from the database - await timeouts.deleteOne({ - guildId: interaction?.guild?.id, - userId: interaction?.user?.id, - timeoutId: '2022-03-15-19-16', - }); - }, guildDB.credits.workTimeout); - } else { - // Create embed object - const embed = { - title: ':dollar: Credits - Work', - description: `You have worked within the last ${ - guildDB.credits.workTimeout / 1000 - } seconds, you can not work now!`, - timestamp: new Date(), - color: config.colors.error as any, - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - - // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${interaction?.user?.id} has worked within last day, no work can be done` - ); - } -}; diff --git a/src/commands/credits/index.ts b/src/commands/credits/index.ts index d5fcc4a..5d729a0 100644 --- a/src/commands/credits/index.ts +++ b/src/commands/credits/index.ts @@ -1,9 +1,17 @@ +// Dependencies import { SlashCommandBuilder } from '@discordjs/builders'; -import balance from './addons/balance'; -import gift from './addons/gift'; -import top from './addons/top'; -import work from './addons/work'; import { CommandInteraction } from 'discord.js'; + +// Handlers +import logger from '../../handlers/logger'; + +// Modules +import balance from './modules/balance'; +import gift from './modules/gift'; +import top from './modules/top'; +import work from './modules/work'; + +// Function export default { data: new SlashCommandBuilder() .setName('credits') @@ -46,28 +54,37 @@ export default { subcommand.setName('work').setDescription('Work for credits.') ), async execute(interaction: CommandInteraction) { - // If subcommand is balance - if (interaction.options.getSubcommand() === 'balance') { - // Execute balance addon - await balance(interaction); + const { options, user, guild, commandName } = interaction; + + // Module - Balance + if (options?.getSubcommand() === 'balance') { + // Execute Module - Balance + return await balance(interaction); } - // If subcommand is gift - else if (interaction.options.getSubcommand() === 'gift') { - // Execute gift addon - await gift(interaction); + // Module - Gift + else if (options?.getSubcommand() === 'gift') { + // Execute Module - Gift + return await gift(interaction); } - // If subcommand is top - else if (interaction.options.getSubcommand() === 'top') { - // Execute top addon - await top(interaction); + // Module - Top + else if (options?.getSubcommand() === 'top') { + // Execute Module - Top + return await top(interaction); } - // If subcommand is work - else if (interaction.options.getSubcommand() === 'work') { - // Execute work addon - await work(interaction); + // Module - Work + else if (options?.getSubcommand() === 'work') { + // Execute Module - Work + return await work(interaction); } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); }, }; diff --git a/src/commands/credits/modules/balance.ts b/src/commands/credits/modules/balance.ts new file mode 100644 index 0000000..724e260 --- /dev/null +++ b/src/commands/credits/modules/balance.ts @@ -0,0 +1,83 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Helpers +import creditNoun from '../../../helpers/creditNoun'; + +// Models +import userSchema from '../../../helpers/database/models/userSchema'; + +// Function +export default async (interaction: CommandInteraction) => { + // Destructure + const { options, user, guild } = interaction; + + // User option + const optionUser = options?.getUser('user'); + + // Get credit object + const userDB = await userSchema?.findOne({ + userId: optionUser ? optionUser?.id : user?.id, + guildId: guild?.id, + }); + + // If userDB does not exist + if (!userDB) { + // Embed object + const embed = { + title: ':dollar: Credits [Balance]' as string, + description: `We can not find ${ + optionUser || 'you' + } in our database.` as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return interaction?.editReply({ embeds: [embed] }); + } + + // If userDB.credits does not exist + if (!userDB.credits) { + // Embed object + const embed = { + title: ':dollar: Credits [Balance]' as string, + description: `We can not find credits for ${ + optionUser || 'you' + } in our database.` as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return interaction?.editReply({ embeds: [embed] }); + } else { + // Embed object + const embed = { + title: ':dollar: Credits [Balance]' as string, + description: `${ + optionUser ? `${optionUser} has` : 'You have' + } ${creditNoun(userDB.credits)}.` as string, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return interaction?.editReply({ embeds: [embed] }); + } +}; diff --git a/src/commands/credits/modules/gift.ts b/src/commands/credits/modules/gift.ts new file mode 100644 index 0000000..2135777 --- /dev/null +++ b/src/commands/credits/modules/gift.ts @@ -0,0 +1,187 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Handlers +import logger from '../../../handlers/logger'; + +// Helpers +import saveUser from '../../../helpers/saveUser'; +import creditNoun from '../../../helpers/creditNoun'; + +// Models +import userSchema from '../../../helpers/database/models/userSchema'; + +// Function +export default async (interaction: CommandInteraction) => { + // Destructure + const { options, user, guild, client } = interaction; + + // User option + const optionUser = options?.getUser('user'); + + // Amount option + const optionAmount = options?.getInteger('amount'); + + // Reason option + const optionReason = options?.getString('reason'); + + // Get fromUserDB object + const fromUserDB = await userSchema?.findOne({ + userId: user?.id, + guildId: guild?.id, + }); + + // Get toUserDB object + const toUserDB = await userSchema?.findOne({ + userId: optionUser?.id, + guildId: guild?.id, + }); + + // If receiver is same as sender + if (optionUser?.id === user?.id) { + // Create embed object + const embed = { + title: ':dollar: Credits [Gift]' as string, + description: "You can't pay yourself." as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // If amount is null + if (optionAmount === null) { + // Embed object + const embed = { + title: ':dollar: Credits [Gift]' as string, + description: 'We could not read your requested amount.' as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // If amount is zero or below + if (optionAmount <= 0) { + // Embed object + const embed = { + title: ':dollar: Credits [Gift]' as string, + description: "You can't pay zero or below." as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // If user has below gifting amount + if (fromUserDB?.credits < optionAmount) { + // Embed object + const embed = { + title: ':dollar: Credits [Gift]' as string, + description: + `You have insufficient credits. Your credits is ${fromUserDB?.credits}` as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // If toUserDB has no credits + if (!toUserDB) { + // Embed object + const embed = { + title: ':dollar: Credits [Gift]' as string, + description: + `That user has no credits, I can not gift credits to ${optionUser}` as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // Withdraw amount from fromUserDB + fromUserDB.credits -= optionAmount; + + // Deposit amount to toUserDB + toUserDB.credits += optionAmount; + + // Save users + await saveUser(fromUserDB, toUserDB)?.then(async () => { + // Interaction embed object + const interactionEmbed = { + title: ':dollar: Credits [Gift]', + description: `You sent ${creditNoun(optionAmount)} to ${optionUser}${ + optionReason ? ` with reason: ${optionReason}` : '' + }. Your new credits is ${creditNoun(fromUserDB?.credits)}.`, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // DM embed object + const dmEmbed = { + title: ':dollar: Credits [Gift]' as string, + description: `You received ${creditNoun(optionAmount)} from ${user}${ + optionReason ? ` with reason: ${optionReason}` : '' + }. Your new credits is ${creditNoun(toUserDB?.credits)}.` as string, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Get DM user object + const dmUser = client?.users?.cache?.get(interaction?.user?.id); + + // Send DM to user + await dmUser?.send({ embeds: [dmEmbed] }); + + // Send debug message + logger.debug( + `Guild: ${guild?.id} User: ${user?.id} gift sent from: ${user?.id} to: ${optionUser?.id}` + ); + + // Send interaction reply + return await interaction.editReply({ + embeds: [interactionEmbed], + }); + }); +}; diff --git a/src/commands/credits/modules/top.ts b/src/commands/credits/modules/top.ts new file mode 100644 index 0000000..7194963 --- /dev/null +++ b/src/commands/credits/modules/top.ts @@ -0,0 +1,47 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Models +import userSchema from '../../../helpers/database/models/userSchema'; + +// helpers +import creditNoun from '../../../helpers/creditNoun'; + +// Function +export default async (interaction: CommandInteraction) => { + // Get all users in the guild + + const usersDB = await userSchema.find({ guildId: interaction?.guild?.id }); + + const topTen = usersDB + + // Sort them after credits amount (ascending) + .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) => + `**Top ${index + 1}** - <@${x?.userId}> ${creditNoun(x?.credits)}`; + + // Create embed object + const embed = { + title: ':dollar: Credits [Top]' as string, + description: `Below are the top ten.\n${topTen + ?.map((x, index) => entry(x, index)) + ?.join('\n')}` as string, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); +}; diff --git a/src/commands/credits/modules/work.ts b/src/commands/credits/modules/work.ts new file mode 100644 index 0000000..ae3847c --- /dev/null +++ b/src/commands/credits/modules/work.ts @@ -0,0 +1,113 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Handlers +import logger from '../../../handlers/logger'; + +// Models +import guildSchema from '../../../helpers/database/models/guildSchema'; +import userSchema from '../../../helpers/database/models/userSchema'; +import timeouts from '../../../helpers/database/models/timeoutSchema'; + +// Helpers +import creditNoun from '../../../helpers/creditNoun'; + +// Function +export default async (interaction: CommandInteraction) => { + // Destructure member + const { guild, user } = interaction; + + // Check if user has a timeout + const isTimeout = await timeouts?.findOne({ + guildId: guild?.id, + userId: user?.id, + timeoutId: '2022-03-15-19-16', + }); + + const guildDB = await guildSchema?.findOne({ + guildId: guild?.id, + }); + + // If user is not on timeout + if (!isTimeout) { + // Make a variable of how much credits user will earn based on random multiplied with work rate + const creditsEarned = Math?.floor( + Math?.random() * guildDB?.credits?.workRate + ); + + const userDB = await userSchema?.findOne({ + userId: user?.id, + guildId: guild?.id, + }); + + userDB.credits += creditsEarned; + + await userDB?.save()?.then(async () => { + // Send debug message + logger?.debug(`Credits added to user: ${user?.id}`); + + // Create embed object + const embed = { + title: ':dollar: Credits [Work]' as string, + description: `You have earned ${creditNoun(creditsEarned)}` as string, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + }); + + // Create a timeout for the user + await timeouts?.create({ + guildId: guild?.id, + userId: user?.id, + timeoutId: '2022-03-15-19-16', + }); + + setTimeout(async () => { + // Send debug message + logger?.debug( + `Guild: ${guild?.id} User: ${user?.id} has not worked within the last ${ + guildDB?.work?.timeout / 1000 + } seconds, work can be done` + ); + + // When timeout is out, remove it from the database + await timeouts?.deleteOne({ + guildId: guild?.id, + userId: user?.id, + timeoutId: '2022-03-15-19-16', + }); + }, guildDB?.credits?.workTimeout); + } else { + // Create embed object + const embed = { + title: ':dollar: Credits [Work]' as string, + description: `You have worked within the last ${ + guildDB?.credits?.workTimeout / 1000 + } seconds, you can not work now!` as string, + timestamp: new Date() as Date, + color: config?.colors?.error as ColorResolvable, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send debug message + logger?.debug( + `Guild: ${guild?.id} User: ${user?.id} has worked within last day, no work can be done` + ); + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } +}; diff --git a/src/commands/profile/index.ts b/src/commands/profile/index.ts index 7dfe9f9..6c57b28 100644 --- a/src/commands/profile/index.ts +++ b/src/commands/profile/index.ts @@ -5,6 +5,9 @@ import { CommandInteraction } from 'discord.js'; // Modules import view from './modules/view'; +// Handlers +import logger from '../../handlers/logger'; + // Function export default { data: new SlashCommandBuilder() @@ -21,10 +24,20 @@ export default { ) ), async execute(interaction: CommandInteraction) { + // Destructure + const { options, guild, user, commandName } = interaction; + // Module - View - if (interaction.options.getSubcommand() === 'view') { + if (options?.getSubcommand() === 'view') { // Execute Module - View - await view(interaction); + return await view(interaction); } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); }, }; diff --git a/src/commands/profile/modules/view.ts b/src/commands/profile/modules/view.ts index f4faa89..734c6f2 100644 --- a/src/commands/profile/modules/view.ts +++ b/src/commands/profile/modules/view.ts @@ -1,8 +1,13 @@ -import i18next from 'i18next'; +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import users from '../../../helpers/database/models/userSchema'; -import { CommandInteraction } from 'discord.js'; + +// Models +import userSchema from '../../../helpers/database/models/userSchema'; + +// Function export default async (interaction: CommandInteraction) => { // Destructure const { client, options, user, guild } = interaction; @@ -11,12 +16,12 @@ export default async (interaction: CommandInteraction) => { const target = options?.getUser('target'); // Discord User Information - const discordUser = await client.users.fetch( + const discordUser = await client?.users?.fetch( `${target ? target?.id : user?.id}` ); // User Information - const userObj = await users.findOne({ + const userObj = await userSchema?.findOne({ userId: discordUser?.id, guildId: guild?.id, }); @@ -24,41 +29,44 @@ export default async (interaction: CommandInteraction) => { // Embed object const embed = { author: { - name: `${discordUser.username}#${discordUser.discriminator}`, - icon_url: discordUser.displayAvatarURL(), + name: `${discordUser?.username}#${discordUser?.discriminator}` as string, + icon_url: discordUser?.displayAvatarURL() as string, }, - color: config.colors.success as any, + color: config?.colors?.success as ColorResolvable, fields: [ { - name: `:dollar: Credits`, - value: `${userObj.credits || 'Not found'}`, + name: `:dollar: Credits` as string, + value: `${userObj?.credits || 'Not found'}` as string, inline: true, }, { - name: `:squeeze_bottle: Level`, - value: `${userObj.level || 'Not found'}`, + name: `:squeeze_bottle: Level` as string, + value: `${userObj?.level || 'Not found'}` as string, inline: true, }, { - name: `:squeeze_bottle: Points`, - value: `${userObj.points || 'Not found'}`, + name: `:squeeze_bottle: Points` as string, + value: `${userObj?.points || 'Not found'}` as string, inline: true, }, { - name: `:loudspeaker: Reputation`, - value: `${userObj.reputation || 'Not found'}`, + name: `:loudspeaker: Reputation` as string, + value: `${userObj?.reputation || 'Not found'}` as string, inline: true, }, { - name: `:rainbow_flag: Language`, - value: `${userObj.language || 'Not found'}`, + name: `:rainbow_flag: Language` as string, + value: `${userObj?.language || 'Not found'}` as string, inline: true, }, ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send interaction reply - return await interaction.editReply({ embeds: [embed] }); + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); }; diff --git a/src/commands/reputation/index.ts b/src/commands/reputation/index.ts index 8ec7572..021adee 100644 --- a/src/commands/reputation/index.ts +++ b/src/commands/reputation/index.ts @@ -5,6 +5,9 @@ import { CommandInteraction } from 'discord.js'; // Modules import give from './modules/give'; +// Handlers +import logger from '../../handlers/logger'; + // Function export default { data: new SlashCommandBuilder() @@ -31,12 +34,19 @@ export default { ), async execute(interaction: CommandInteraction) { // Destructure - const { options } = interaction; + const { options, guild, user, commandName } = interaction; // Module - Give - if (options.getSubcommand() === 'give') { + if (options?.getSubcommand() === 'give') { // Execute Module - Give await give(interaction); } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); }, }; diff --git a/src/commands/reputation/modules/give.ts b/src/commands/reputation/modules/give.ts index 501e79d..cdeb950 100644 --- a/src/commands/reputation/modules/give.ts +++ b/src/commands/reputation/modules/give.ts @@ -1,122 +1,138 @@ -import i18next from 'i18next'; -import { CommandInteraction } from 'discord.js'; -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import users from '../../../helpers/database/models/userSchema'; -import timeouts from '../../../helpers/database/models/timeoutSchema'; +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; +// Configurations +import config from '../../../../config.json'; + +// Handlers +import logger from '../../../handlers/logger'; + +// Models +import userSchema from '../../../helpers/database/models/userSchema'; +import timeoutSchema from '../../../helpers/database/models/timeoutSchema'; + +// Function export default async (interaction: CommandInteraction) => { // Destructure const { options, user, guild } = interaction; - // Target information - const target = options.getUser('target'); + // Target option + const optionTarget = options?.getUser('target'); // Type information - const type = options.getString('type'); + const optionType = options?.getString('type'); // User information - const userObj = await users.findOne({ + const userObj = await userSchema?.findOne({ userId: user?.id, guildId: guild?.id, }); // Check if user has a timeout - const isTimeout = await timeouts.findOne({ + const isTimeout = await timeoutSchema?.findOne({ guildId: guild?.id, userId: user?.id, - timeoutId: 2, + timeoutId: '2022-04-10-16-42', }); // If user is not on timeout if (!isTimeout) { // Do not allow self reputation - if (target?.id === user?.id) { + if (optionTarget?.id === user?.id) { // Embed object const embed = { - title: ':loudspeaker: Reputation - Give', - description: 'You can not repute yourself.', - timestamp: new Date(), - color: config.colors.error as any, - footer: { iconURL: config.footer.icon, text: config.footer.text }, + title: ':loudspeaker: Reputation [Give]' as string, + description: 'You can not repute yourself.' as string, + timestamp: new Date() as Date, + color: config?.colors?.error as ColorResolvable, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; // Return interaction reply - return interaction.editReply({ embeds: [embed] }); + return await interaction?.editReply({ embeds: [embed] }); } // If type is positive - if (type === 'positive') { + if (optionType === 'positive') { userObj.reputation += 1; } // If type is negative - if (type === 'negative') { + else if (optionType === 'negative') { userObj.reputation -= 1; } // Save user - await userObj.save().then(async () => { + await userObj?.save()?.then(async () => { // Embed object const embed = { - title: ':loudspeaker: Reputation - Give', - description: `You have given ${target} a ${type} reputation!`, - timestamp: new Date(), - color: config.colors.success as any, - footer: { iconURL: config.footer.icon, text: config.footer.text }, + title: ':loudspeaker: Reputation [Give]' as string, + description: + `You have given ${optionTarget} a ${optionType} reputation!` as string, + timestamp: new Date() as Date, + color: config?.colors?.success as ColorResolvable, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - // Log debug message - logger.debug( - `Guild: ${guild?.id} User: ${user?.id} has given ${target?.id} a ${type} reputation.` + logger?.debug( + `Guild: ${guild?.id} User: ${user?.id} has given ${optionTarget?.id} a ${optionType} reputation.` ); // Create a timeout for the user - await timeouts.create({ + await timeoutSchema?.create({ guildId: guild?.id, userId: user?.id, - timeoutId: 2, + timeoutId: '2022-04-10-16-42', }); + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); }); setTimeout(async () => { // send debug message - logger.debug( + logger?.debug( `Guild: ${guild?.id} User: ${user?.id} has not repute within last ${ - config.reputation.timeout / 1000 + config?.reputation?.timeout / 1000 } seconds, reputation can be given` ); // When timeout is out, remove it from the database - await timeouts.deleteOne({ + await timeoutSchema?.deleteOne({ guildId: guild?.id, userId: user?.id, - timeoutId: 2, + timeoutId: '2022-04-10-16-42', }); - }, config.reputation.timeout); + }, config?.reputation?.timeout); } else { // Create embed object const embed = { - title: ':loudspeaker: Reputation - Give', + title: ':loudspeaker: Reputation [Give]' as string, description: `You have given reputation within the last ${ - config.reputation.timeout / 1000 - } seconds, you can not repute now!`, - timestamp: new Date(), - color: config.colors.error as any, - footer: { iconURL: config.footer.icon, text: config.footer.text }, + config?.reputation?.timeout / 1000 + } seconds, you can not repute now!` as string, + timestamp: new Date() as Date, + color: config.colors.error as ColorResolvable, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - // Log debug message - logger.debug( + logger?.debug( `Guild: ${guild?.id} User: ${user?.id} has repute within last ${ - config.reputation.timeout / 1000 + config?.reputation?.timeout / 1000 } seconds, no reputation can be given` ); + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); } }; diff --git a/src/commands/settings/guild/addons/credits.ts b/src/commands/settings/guild/addons/credits.ts index cb5ebef..04998f3 100644 --- a/src/commands/settings/guild/addons/credits.ts +++ b/src/commands/settings/guild/addons/credits.ts @@ -1,95 +1,97 @@ -import { Permissions, CommandInteraction } from 'discord.js'; +// Dependencies +import { ColorResolvable, CommandInteraction } from 'discord.js'; + +// Configurations import config from '../../../../../config.json'; + +//Handlers import logger from '../../../../handlers/logger'; -// Database models -import guilds from '../../../../helpers/database/models/guildSchema'; +// Models +import guildSchema from '../../../../helpers/database/models/guildSchema'; +// Function export default async (interaction: CommandInteraction) => { // Destructure member - const { guild, user } = interaction; - - // Check permission - if (!interaction?.memberPermissions?.has(Permissions.FLAGS.MANAGE_GUILD)) { - // Create embed object - const embed = { - title: ':hammer: Settings - Guild [Credits]', - color: config.colors.error as any, - description: `You don't have permission to manage this!`, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } + const { guild, user, options } = interaction; // Get options - const status = await interaction.options.getBoolean('status'); - const rate = await interaction.options.getNumber('rate'); - const timeout = await interaction.options.getNumber('timeout'); - const minimumLength = await interaction.options.getNumber('minimum-length'); - const workRate = await interaction.options.getNumber('work-rate'); - const workTimeout = await interaction.options.getNumber('work-timeout'); + const status = options?.getBoolean('status'); + const rate = options?.getNumber('rate'); + const timeout = options?.getNumber('timeout'); + const minimumLength = options?.getNumber('minimum-length'); + const workRate = options?.getNumber('work-rate'); + const workTimeout = options?.getNumber('work-timeout'); // Get guild object - const guildDB = await guilds.findOne({ + const guildDB = await guildSchema?.findOne({ guildId: guild?.id, }); // Modify values - guildDB.credits.status = status !== null ? status : guildDB.credits.status; - guildDB.credits.rate = rate !== null ? rate : guildDB.credits.rate; + guildDB.credits.status = status !== null ? status : guildDB?.credits?.status; + guildDB.credits.rate = rate !== null ? rate : guildDB?.credits?.rate; guildDB.credits.timeout = - timeout !== null ? timeout : guildDB.credits.timeout; + timeout !== null ? timeout : guildDB?.credits?.timeout; guildDB.credits.workRate = - workRate !== null ? workRate : guildDB.credits.workRate; + workRate !== null ? workRate : guildDB?.credits?.workRate; guildDB.credits.workTimeout = - workTimeout !== null ? workTimeout : guildDB.credits.workTimeout; + workTimeout !== null ? workTimeout : guildDB?.credits?.workTimeout; guildDB.credits.minimumLength = - minimumLength !== null ? minimumLength : guildDB.credits.minimumLength; + minimumLength !== null ? minimumLength : guildDB?.credits?.minimumLength; // Save guild - await guildDB.save().then(async () => { - // Create embed object + await guildDB?.save()?.then(async () => { + // Embed object const embed = { - title: ':hammer: Settings - Guild [Credits]', - description: 'Following settings is set!', - color: config.colors.success as any, + title: ':tools: Settings - Guild [Credits]' as string, + description: 'Following settings is set!' as string, + color: config?.colors?.success as ColorResolvable, fields: [ - { name: '🤖 Status', value: `${guildDB.credits.status}`, inline: true }, - { name: '📈 Rate', value: `${guildDB.credits.rate}`, inline: true }, { - name: '📈 Work Rate', - value: `${guildDB.credits.workRate}`, + name: '🤖 Status' as string, + value: `${guildDB?.credits?.status}` as string, inline: true, }, { - name: '🔨 Minimum Length', - value: `${guildDB.credits.minimumLength}`, + name: '📈 Rate' as string, + value: `${guildDB?.credits?.rate}` as string, inline: true, }, { - name: '⏰ Timeout', - value: `${guildDB.credits.timeout}`, + name: '📈 Work Rate' as string, + value: `${guildDB?.credits?.workRate}` as string, inline: true, }, { - name: '⏰ Work Timeout', - value: `${guildDB.credits.workTimeout}`, + name: '🔨 Minimum Length' as string, + value: `${guildDB?.credits?.minimumLength}` as string, + inline: true, + }, + { + name: '⏰ Timeout' as string, + value: `${guildDB?.credits?.timeout}` as string, + inline: true, + }, + { + name: '⏰ Work Timeout' as string, + value: `${guildDB?.credits?.workTimeout}` as string, inline: true, }, ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - // Send debug message - await logger.debug( + logger?.debug( `Guild: ${guild?.id} User: ${user.id} has changed credit details.` ); + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); }); }; diff --git a/src/commands/settings/guild/addons/index.ts b/src/commands/settings/guild/addons/index.ts deleted file mode 100644 index 52501a3..0000000 --- a/src/commands/settings/guild/addons/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import pterodactyl from './pterodactyl'; -import credits from './credits'; -import points from './points'; - -export default { pterodactyl, credits, points }; diff --git a/src/commands/settings/guild/addons/points.ts b/src/commands/settings/guild/addons/points.ts index 2fdfc43..aa3370e 100644 --- a/src/commands/settings/guild/addons/points.ts +++ b/src/commands/settings/guild/addons/points.ts @@ -1,79 +1,81 @@ -import { Permissions, CommandInteraction } from 'discord.js'; +// Dependencies +import { ColorResolvable, CommandInteraction } from 'discord.js'; + +// Configurations import config from '../../../../../config.json'; + +// Handlers import logger from '../../../../handlers/logger'; -// Database models -import guilds from '../../../../helpers/database/models/guildSchema'; +// Models +import guildSchema from '../../../../helpers/database/models/guildSchema'; +// Function export default async (interaction: CommandInteraction) => { // Destructure member - const { member } = interaction; - - // Check permission - if (!interaction?.memberPermissions?.has(Permissions.FLAGS.MANAGE_GUILD)) { - // Create embed object - const embed = { - title: ':hammer: Settings - Guild [Points]', - color: config.colors.error as any, - description: `You don't have permission to manage this!`, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } + const { options, guild, user } = interaction; // Get options - const status = await interaction.options.getBoolean('status'); - const rate = await interaction.options.getNumber('rate'); - const timeout = await interaction.options.getNumber('timeout'); - const minimumLength = await interaction.options.getNumber('minimum-length'); + const status = options?.getBoolean('status'); + const rate = options?.getNumber('rate'); + const timeout = options?.getNumber('timeout'); + const minimumLength = options?.getNumber('minimum-length'); // Get guild object - const guildDB = await guilds.findOne({ - guildId: interaction?.guild?.id, + const guildDB = await guildSchema?.findOne({ + guildId: guild?.id, }); // Modify values - guildDB.credits.status = status !== null ? status : guildDB.credits.status; - guildDB.credits.rate = rate !== null ? rate : guildDB.credits.rate; - guildDB.credits.timeout = - timeout !== null ? timeout : guildDB.credits.timeout; - guildDB.credits.minimumLength = - minimumLength !== null ? minimumLength : guildDB.credits.minimumLength; + guildDB.points.status = status !== null ? status : guildDB?.points?.status; + guildDB.points.rate = rate !== null ? rate : guildDB?.points?.rate; + guildDB.points.timeout = + timeout !== null ? timeout : guildDB?.points?.timeout; + guildDB.points.minimumLength = + minimumLength !== null ? minimumLength : guildDB?.points?.minimumLength; // Save guild - await guildDB.save().then(async () => { + await guildDB?.save()?.then(async () => { // Create embed object const embed = { - title: ':hammer: Settings - Guild [Points]', - description: 'Following settings is set!', - color: config.colors.success as any, + title: ':hammer: Settings - Guild [Points]' as string, + description: 'Following settings is set!' as string, + color: config.colors.success as ColorResolvable, fields: [ - { name: '🤖 Status', value: `${guildDB.credits.status}`, inline: true }, - { name: '📈 Rate', value: `${guildDB.credits.rate}`, inline: true }, { - name: '🔨 Minimum Length', - value: `${guildDB.credits.minimumLength}`, + name: '🤖 Status' as string, + value: `${guildDB?.points?.status}` as string, inline: true, }, { - name: '⏰ Timeout', - value: `${guildDB.credits.timeout}`, + name: '📈 Rate' as string, + value: `${guildDB?.points?.rate}` as string, + inline: true, + }, + { + name: '🔨 Minimum Length' as string, + value: `${guildDB?.points?.minimumLength}` as string, + inline: true, + }, + { + name: '⏰ Timeout' as string, + value: `${guildDB?.points?.timeout}` as string, inline: true, }, ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${interaction?.user?.id} has changed credit details.` + logger?.debug( + `Guild: ${guild?.id} User: ${user?.id} has changed credit details.` ); + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); }); }; diff --git a/src/commands/settings/guild/addons/pterodactyl.ts b/src/commands/settings/guild/addons/pterodactyl.ts index ffa564f..6824062 100644 --- a/src/commands/settings/guild/addons/pterodactyl.ts +++ b/src/commands/settings/guild/addons/pterodactyl.ts @@ -1,62 +1,50 @@ -import { Permissions, CommandInteraction } from 'discord.js'; +// Dependencies +import { ColorResolvable, CommandInteraction } from 'discord.js'; + +// Configurations import config from '../../../../../config.json'; + +// Handlers import logger from '../../../../handlers/logger'; -// Database models - -import apis from '../../../../helpers/database/models/apiSchema'; +// Models +import apiSchema from '../../../../helpers/database/models/apiSchema'; +// Function export default async (interaction: CommandInteraction) => { // Destructure member - const { member } = interaction; - - // Check permission - if (!interaction?.memberPermissions?.has(Permissions.FLAGS.MANAGE_GUILD)) { - // Create embed object - const embed = { - title: ':hammer: Settings - Guild [Pterodactyl]', - color: config.colors.error as any, - description: 'You do not have permission to manage this!', - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - return interaction.editReply({ embeds: [embed] }); - } + const { options, guild, user } = interaction; // Get options - - const url = await interaction.options.getString('url'); - const token = await interaction.options.getString('token'); + const url = options?.getString('url'); + const token = options?.getString('token'); // Update API credentials - - await apis - .findOneAndUpdate( - { guildId: interaction?.guild?.id }, + await apiSchema + ?.findOneAndUpdate( + { guildId: guild?.id }, { url, token }, { new: true, upsert: true } ) .then(async () => { - // Build embed - + // Embed object const embed = { - title: ':hammer: Settings - Guild [Pterodactyl]', - color: config.colors.success as any, - description: 'Pterodactyl settings is saved!', - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, + title: ':hammer: Settings - Guild [Pterodactyl]' as string, + color: config?.colors?.success as ColorResolvable, + description: 'Pterodactyl settings is saved!' as string, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send reply - - await interaction.editReply({ embeds: [embed] }); - // Send debug message - - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${interaction?.user?.id} has changed api credentials.` + logger?.debug( + `Guild: ${guild?.id} User: ${user?.id} has changed api credentials.` ); + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); }); }; diff --git a/src/commands/settings/guild/index.ts b/src/commands/settings/guild/index.ts index 876a49f..ba09961 100644 --- a/src/commands/settings/guild/index.ts +++ b/src/commands/settings/guild/index.ts @@ -1,53 +1,62 @@ -import { Permissions, CommandInteraction } from 'discord.js'; +// Dependencies +import { Permissions, ColorResolvable, CommandInteraction } from 'discord.js'; + +// Configurations import config from '../../../../config.json'; + +// Handlers import logger from '../../../handlers/logger'; + +// Modules import pterodactyl from './addons/pterodactyl'; import credits from './addons/credits'; import points from './addons/points'; +// Function export default async (interaction: CommandInteraction) => { // Destructure member - const { member } = interaction; + const { memberPermissions, options, commandName, user, guild } = interaction; // Check permission - if (!interaction?.memberPermissions?.has(Permissions.FLAGS.MANAGE_GUILD)) { + if (!memberPermissions?.has(Permissions?.FLAGS?.MANAGE_GUILD)) { // Create embed object const embed = { - title: 'Settings - Guild', - color: config.colors.error as any, - description: 'You do not have permission to manage this!', - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, + title: ':tools: Settings - Guild' as string, + color: config?.colors?.error as ColorResolvable, + description: 'You do not have permission to manage this!' as string, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, }; - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); } - // If subcommand is pterodactyl - if (interaction.options.getSubcommand() === 'pterodactyl') { - // Execute pterodactyl addon - await pterodactyl(interaction); + // Module - Pterodactyl + if (options?.getSubcommand() === 'pterodactyl') { + // Execute Module - Pterodactyl + return await pterodactyl(interaction); } - // If subcommand is credits - else if (interaction.options.getSubcommand() === 'credits') { - // Execute credits addon - await credits(interaction); + // Module - Credits + else if (options?.getSubcommand() === 'credits') { + // Execute Module - Credits + return await credits(interaction); } - // If subcommand is points - else if (interaction.options.getSubcommand() === 'points') { - // Execute points addon - await points(interaction); + // Module - Points + else if (options?.getSubcommand() === 'points') { + // Execute Module - Points + return await points(interaction); } // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${ - interaction?.user?.id - } executed /${ - interaction.commandName - } ${interaction.options.getSubcommandGroup()} ${interaction.options.getSubcommand()}` + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` ); }; diff --git a/src/commands/settings/index.ts b/src/commands/settings/index.ts index e2a2c17..113b9bb 100644 --- a/src/commands/settings/index.ts +++ b/src/commands/settings/index.ts @@ -1,8 +1,15 @@ +// Dependencies import { SlashCommandBuilder } from '@discordjs/builders'; -import { Permissions, CommandInteraction } from 'discord.js'; -import guild from './guild'; -import user from './user'; +import { CommandInteraction } from 'discord.js'; +// Groups +import guildGroup from './guild'; +import userGroup from './user'; + +// Handlers +import logger from '../../handlers/logger'; + +// Function export default { data: new SlashCommandBuilder() .setName('settings') @@ -113,15 +120,25 @@ export default { ) ), async execute(interaction: CommandInteraction) { - // If subcommand group is guild - if (interaction.options.getSubcommandGroup() === 'guild') { - // Execute guild group - await guild(interaction); + // Destructure + const { options, commandName, user, guild } = interaction; + + // Group - Guild + if (options.getSubcommandGroup() === 'guild') { + // Execute Group - Guild + await guildGroup(interaction); } - // If subcommand group is user - else if (interaction.options.getSubcommandGroup() === 'user') { - // Execute user group - await user(interaction); + // Group - User + else if (options.getSubcommandGroup() === 'user') { + // Execute Group - User + await userGroup(interaction); } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); }, }; diff --git a/src/commands/settings/user/addons/appearance.ts b/src/commands/settings/user/addons/appearance.ts deleted file mode 100644 index de400c0..0000000 --- a/src/commands/settings/user/addons/appearance.ts +++ /dev/null @@ -1,49 +0,0 @@ -import config from '../../../../../config.json'; -import logger from '../../../../handlers/logger'; -import { CommandInteraction } from 'discord.js'; -// Database models -import users from '../../../../helpers/database/models/userSchema'; - -export default async (interaction: CommandInteraction) => { - // Destructure member - const { member } = interaction; - - // Get options - const language = await interaction.options.getString('language'); - - // Get user object - const userDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - // Modify values - userDB.language = language !== null ? language : userDB.language; - - // Save guild - await userDB.save().then(async () => { - // Create embed object - const embed = { - title: ':hammer: Settings - User [Appearance]', - description: 'Following settings is set!', - color: config.colors.success as any, - fields: [ - { - name: '🏳️‍🌈 Language', - value: `${userDB.language}`, - inline: true, - }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - - // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${interaction?.user?.id} has changed appearance settings.` - ); - }); -}; diff --git a/src/commands/settings/user/addons/index.ts b/src/commands/settings/user/addons/index.ts deleted file mode 100644 index 5362aba..0000000 --- a/src/commands/settings/user/addons/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import appearance from './appearance'; - -export default { appearance }; diff --git a/src/commands/settings/user/index.ts b/src/commands/settings/user/index.ts index ba69eae..3953c5f 100644 --- a/src/commands/settings/user/index.ts +++ b/src/commands/settings/user/index.ts @@ -1,24 +1,27 @@ -import { Permissions, CommandInteraction } from 'discord.js'; -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import appearance from './addons/appearance'; +// Dependencies +import { CommandInteraction } from 'discord.js'; +// Handlers +import logger from '../../../handlers/logger'; + +// Modules +import appearance from './modules/appearance'; + +// Function export default async (interaction: CommandInteraction) => { // Destructure member - const { member } = interaction; + const { guild, user, options, commandName } = interaction; - // If subcommand is appearance - if (interaction.options.getSubcommand() === 'appearance') { - // Execute appearance addon + // Module - Appearance + if (options?.getSubcommand() === 'appearance') { + // Execute Module - Appearance await appearance(interaction); } // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${ - interaction?.user?.id - } executed /${ - interaction.commandName - } ${interaction.options.getSubcommandGroup()} ${interaction.options.getSubcommand()}` + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` ); }; diff --git a/src/commands/settings/user/modules/appearance.ts b/src/commands/settings/user/modules/appearance.ts new file mode 100644 index 0000000..e00cfda --- /dev/null +++ b/src/commands/settings/user/modules/appearance.ts @@ -0,0 +1,59 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../../config.json'; + +// Handlers +import logger from '../../../../handlers/logger'; + +// Models +import userSchema from '../../../../helpers/database/models/userSchema'; + +// Function +export default async (interaction: CommandInteraction) => { + // Destructure member + const { options, user, guild } = interaction; + + // Get options + const language = options?.getString('language'); + + // Get user object + const userDB = await userSchema?.findOne({ + userId: user?.id, + guildId: guild?.id, + }); + + // Modify values + userDB.language = language !== null ? language : userDB?.language; + + // Save guild + await userDB?.save()?.then(async () => { + // Embed object + const embed = { + title: ':hammer: Settings - User [Appearance]' as string, + description: 'Following settings is set!' as string, + color: config?.colors?.success as ColorResolvable, + fields: [ + { + name: '🏳️‍🌈 Language' as string, + value: `${userDB?.language}` as string, + inline: true, + }, + ], + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send debug message + logger?.debug( + `Guild: ${guild?.id} User: ${user?.id} has changed appearance settings.` + ); + + // Return interaction reply + return await interaction?.editReply({ embeds: [embed] }); + }); +}; diff --git a/src/commands/shop/addons/pterodactyl.ts b/src/commands/shop/addons/pterodactyl.ts deleted file mode 100644 index 7d0c7ea..0000000 --- a/src/commands/shop/addons/pterodactyl.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import axios from 'axios'; -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import apis from '../../../helpers/database/models/apiSchema'; -import users from '../../../helpers/database/models/userSchema'; -import creditNoun from '../../../helpers/creditNoun'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - const { member } = interaction; - - // Get options - const amount = await interaction.options.getInteger('amount'); - - if (amount === null) return; - - // Get user object - const userDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - // Get DM user object - const dmUser = interaction.client.users.cache.get(interaction?.user?.id); - - // Stop if amount or user credits is below 100 - if ((amount || userDB.credits) < 100) { - const embed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: `You **can't** withdraw for __Pterodactyl__ below **100**.`, - color: config.colors.error as any, - fields: [ - { name: 'Your balance', value: `${creditNoun(userDB.credits)}` }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ embeds: [embed] }); - } - - // Stop if amount or user credits is above 1.000.000 - if ((amount || userDB.credits) > 1000000) { - const embed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: `You **can't** withdraw for __Pterodactyl__ above **1.000.000**.`, - color: config.colors.error as any, - fields: [ - { name: 'Your balance', value: `${creditNoun(userDB.credits)}` }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ embeds: [embed] }); - } - - // Stop if user credits is below amount - if (userDB.credits < amount) { - const embed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: `You have **insufficient** credits.`, - color: config.colors.error as any, - fields: [ - { name: 'Your balance', value: `${creditNoun(userDB.credits)}` }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ embeds: [embed] }); - } - - // Generate a unique voucher for the user - const code = uuidv4(); - - // Get api object - const apiCredentials = await apis.findOne({ - guildId: interaction?.guild?.id, - }); - - // Create a api instance - const api = axios.create({ - baseURL: apiCredentials.url, - headers: { Authorization: `Bearer ${apiCredentials.token}` }, - }); - - // 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, - credits: amount || userDB.credits, - memo: `${interaction.createdTimestamp} - ${interaction?.user?.id}`, - }) - - // If successful - .then(async () => { - // Create DM embed object - const dmEmbed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: `Redeem this voucher [here](${shopUrl})!`, - fields: [ - { name: 'Code', value: `${code}`, inline: true }, - { - name: 'Credits', - value: `${amount || userDB.credits}`, - inline: true, - }, - ], - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Create interaction embed object - const interactionEmbed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: 'I have sent you the code in DM!', - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Withdraw amount from user credits - userDB.credits -= amount || userDB.credits; - - // Save new credits - await userDB - .save() - // If successful - .then(async () => { - // Send debug message - await logger.debug( - `User: ${interaction?.user?.username} redeemed: ${creditNoun( - amount - )}` - ); - - // Send DM message - await dmUser?.send({ embeds: [dmEmbed] }); - - // Send interaction reply - await interaction.editReply({ - embeds: [interactionEmbed], - }); - }) - - // If error occurs - .catch(async (e: any) => { - await logger.error(e); - const embed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: 'Something went wrong, please try again later.', - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ embeds: [embed] }); - }); - }) - - // If error occurs - .catch(async (e) => { - await logger.error(e); - const embed = { - title: ':shopping_cart: Shop - Pterodactyl', - description: 'Something went wrong, please try again later.', - color: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ embeds: [embed] }); - }); -}; diff --git a/src/commands/shop/addons/roles.ts b/src/commands/shop/addons/roles.ts deleted file mode 100644 index 08d1873..0000000 --- a/src/commands/shop/addons/roles.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import axios from 'axios'; -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import guilds from '../../../helpers/database/models/guildSchema'; -import users from '../../../helpers/database/models/userSchema'; -import creditNoun from '../../../helpers/creditNoun'; -import { CommandInteraction, RoleManager } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - const name = interaction.options.getString('name'); - - const { member } = interaction; - - const guildDB = await guilds.findOne({ guildId: interaction?.guild?.id }); - const userDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - if (name === null) return; - - (interaction?.guild?.roles as RoleManager) - .create({ - name, - color: 'BLUE', - reason: `${interaction?.user?.id} bought from shop`, - }) - .then(async (role) => { - console.log(role); - userDB.credits -= guildDB.shop.roles.pricePerHour; - await userDB.save().then(async () => { - const embed = { - title: ':shopping_cart: Shop - Roles', - description: `You have bought ${role.name} for ${guildDB.shop.roles.pricePerHour} per hour.`, - color: config.colors.error as any, - fields: [ - { name: 'Your balance', value: `${creditNoun(userDB.credits)}` }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ - embeds: [embed], - }); - }); - }) - .catch(console.error); -}; diff --git a/src/commands/shop/index.ts b/src/commands/shop/index.ts index 74a870e..ce5211a 100644 --- a/src/commands/shop/index.ts +++ b/src/commands/shop/index.ts @@ -1,9 +1,17 @@ +// Dependencies import { SlashCommandBuilder } from '@discordjs/builders'; -import { Permissions, CommandInteraction } from 'discord.js'; -import guilds from '../../helpers/database/models/guildSchema'; -import pterodactyl from './addons/pterodactyl'; +import { CommandInteraction } from 'discord.js'; + +// Modules +import pterodactyl from './modules/pterodactyl'; + +// Groups import roles from './roles'; +// Handlers +import logger from '../../handlers/logger'; + +// Function export default { data: new SlashCommandBuilder() .setName('shop') @@ -44,16 +52,26 @@ export default { ) ), async execute(interaction: CommandInteraction) { - // If subcommand is pterodactyl - if (interaction.options.getSubcommand() === 'pterodactyl') { - // Execute pterodactyl addon - await pterodactyl(interaction); + // Destructure + const { options, commandName, user, guild } = interaction; + + // Module - Pterodactyl + if (options?.getSubcommand() === 'pterodactyl') { + // Execute Module - Pterodactyl + return await pterodactyl(interaction); } - // If subcommand group is roles - else if (interaction.options.getSubcommandGroup() === 'roles') { - // Execute roles addon - await roles(interaction); + // Group - Roles + else if (options?.getSubcommandGroup() === 'roles') { + // Execute Group - Roles + return await roles(interaction); } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); }, }; diff --git a/src/commands/shop/modules/pterodactyl.ts b/src/commands/shop/modules/pterodactyl.ts new file mode 100644 index 0000000..bbd6974 --- /dev/null +++ b/src/commands/shop/modules/pterodactyl.ts @@ -0,0 +1,235 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; +import { v4 as uuidv4 } from 'uuid'; +import axios from 'axios'; + +// Configurations +import config from '../../../../config.json'; + +// Handlers +import logger from '../../../handlers/logger'; + +// Helpers +import creditNoun from '../../../helpers/creditNoun'; + +// Models +import apiSchema from '../../../helpers/database/models/apiSchema'; +import userSchema from '../../../helpers/database/models/userSchema'; + +// Function +export default async (interaction: CommandInteraction) => { + const { options, guild, user, client } = interaction; + + // Get options + const optionAmount = options?.getInteger('amount'); + + // If amount is null + if (optionAmount === null) { + // Embed object + const embed = { + title: ':dollar: Credits [Gift]' as string, + description: 'We could not read your requested amount.' as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + // Get user object + const userDB = await userSchema?.findOne({ + userId: user?.id, + guildId: guild?.id, + }); + + // 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) { + const embed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: + `You **can't** withdraw for __Pterodactyl__ below **100**.` as string, + color: config?.colors?.error as ColorResolvable, + fields: [ + { + name: 'Your balance' as string, + value: `${creditNoun(userDB?.credits)}` as string, + }, + ], + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ embeds: [embed] }); + } + + // Stop if amount or user credits is above 1.000.000 + if ((optionAmount || userDB?.credits) > 1000000) { + const embed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: + `You **can't** withdraw for __Pterodactyl__ above **1.000.000**.` as string, + color: config?.colors?.error as ColorResolvable, + fields: [ + { + name: 'Your balance' as string, + value: `${creditNoun(userDB?.credits)}` as string, + }, + ], + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ embeds: [embed] }); + } + + // Stop if user credits is below amount + if (userDB?.credits < optionAmount) { + const embed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: `You have **insufficient** credits.` as string, + color: config.colors.error as ColorResolvable, + fields: [ + { + name: 'Your balance' as string, + value: `${creditNoun(userDB?.credits)}` as string, + }, + ], + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ embeds: [embed] }); + } + + // 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: { Authorization: `Bearer ${apiCredentials?.token}` }, + }); + + // 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, + credits: optionAmount || userDB?.credits, + memo: `${interaction?.createdTimestamp} - ${interaction?.user?.id}`, + }) + + // If successful + ?.then(async () => { + // Create DM embed object + const dmEmbed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: `Redeem this voucher [here](${shopUrl})!` as string, + fields: [ + { name: 'Code' as string, value: `${code}` as string, inline: true }, + { + name: 'Credits' as string, + value: `${optionAmount || userDB?.credits}` as string, + inline: true, + }, + ], + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Create interaction embed object + const interactionEmbed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: 'I have sent you the code in DM!' as string, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Withdraw amount from user credits + userDB.credits -= optionAmount || userDB?.credits; + + // Save new credits + await userDB + ?.save() + // If successful + ?.then(async () => { + // Send debug message + logger?.debug( + `User: ${user?.username} redeemed: ${creditNoun(optionAmount)}` + ); + + // Send DM message + await dmUser?.send({ embeds: [dmEmbed] }); + + // Send interaction reply + await interaction?.editReply({ + embeds: [interactionEmbed], + }); + }) + + // If error occurs + .catch(async (e: any) => { + logger?.error(e); + const embed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: + 'Something went wrong, please try again later.' as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ embeds: [embed] }); + }); + }) + + // If error occurs + .catch(async (e) => { + logger?.error(e); + const embed = { + title: ':shopping_cart: Shop [Pterodactyl]' as string, + description: 'Something went wrong, please try again later.' as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ embeds: [embed] }); + }); +}; diff --git a/src/commands/shop/roles/addons/buy.ts b/src/commands/shop/roles/addons/buy.ts deleted file mode 100644 index eeca6c4..0000000 --- a/src/commands/shop/roles/addons/buy.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import axios from 'axios'; -import config from '../../../../../config.json'; -import logger from '../../../../handlers/logger'; -import users from '../../../../helpers/database/models/userSchema'; -import shopRoles from '../../../../helpers/database/models/shopRolesSchema'; -import guilds from '../../../../helpers/database/models/guildSchema'; -import creditNoun from '../../../../helpers/creditNoun'; -import { CommandInteraction, GuildMemberRoleManager } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - const { member } = interaction; - - const name = await interaction.options.getString('name'); - - if (name === null) return; - - await interaction?.guild?.roles - .create({ - name, - color: 'RED', - reason: `${interaction?.user?.id} bought from shop`, - }) - .then(async (role) => { - // Get guild object - const guildDB = await guilds.findOne({ - guildId: interaction?.guild?.id, - }); - const userDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - const { pricePerHour } = guildDB.shop.roles; - - userDB.credits -= pricePerHour; - - await userDB.save(); - - await shopRoles.create({ - roleId: role?.id, - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - pricePerHour, - lastPayed: new Date(), - }); - - await (interaction?.member?.roles as GuildMemberRoleManager)?.add( - role?.id - ); - await shopRoles.find().then((role: any) => console.log(role)); - - const embed = { - title: ':shopping_cart: Shop - Roles [Buy]', - description: `You have bought ${role.name} for ${guildDB.shop.roles.pricePerHour} per hour.`, - color: config.colors.success as any, - fields: [ - { name: 'Your balance', value: `${creditNoun(userDB.credits)}` }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ - embeds: [embed], - }); - }) - .catch(console.error); -}; diff --git a/src/commands/shop/roles/addons/cancel.ts b/src/commands/shop/roles/addons/cancel.ts deleted file mode 100644 index 5b9d90c..0000000 --- a/src/commands/shop/roles/addons/cancel.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import axios from 'axios'; -import config from '../../../../../config.json'; -import logger from '../../../../handlers/logger'; -import users from '../../../../helpers/database/models/userSchema'; -import shopRoles from '../../../../helpers/database/models/shopRolesSchema'; -import guilds from '../../../../helpers/database/models/guildSchema'; -import creditNoun from '../../../../helpers/creditNoun'; -import { CommandInteraction, GuildMemberRoleManager } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - const { member } = interaction; - - const role = await interaction.options.getRole('role'); - - if (role === null) return; - - const roleExist = await shopRoles.find({ - guildId: interaction?.guild?.id, - userId: interaction?.user?.id, - roleId: role?.id, - }); - - if (roleExist) { - await (interaction?.member?.roles as GuildMemberRoleManager).remove( - role?.id - ); - - await interaction?.guild?.roles - .delete(role?.id, `${interaction?.user?.id} canceled from shop`) - .then(async () => { - // Get guild object - const guildDB = await guilds.findOne({ - guildId: interaction?.guild?.id, - }); - - const userDB = await users.findOne({ - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - await shopRoles.deleteOne({ - roleId: role?.id, - userId: interaction?.user?.id, - guildId: interaction?.guild?.id, - }); - - const embed = { - title: ':shopping_cart: Shop - Roles [Buy]', - description: `You have canceled ${role.name}.`, - color: config.colors.success as any, - fields: [ - { name: 'Your balance', value: `${creditNoun(userDB.credits)}` }, - ], - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - return interaction.editReply({ - embeds: [embed], - }); - }) - .catch(console.error); - } -}; diff --git a/src/commands/shop/roles/addons/index.ts b/src/commands/shop/roles/addons/index.ts deleted file mode 100644 index 12e3413..0000000 --- a/src/commands/shop/roles/addons/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import buy from './buy'; -import cancel from './cancel'; - -export default { buy, cancel }; diff --git a/src/commands/shop/roles/index.ts b/src/commands/shop/roles/index.ts index 67f136d..fc51aff 100644 --- a/src/commands/shop/roles/index.ts +++ b/src/commands/shop/roles/index.ts @@ -1,29 +1,34 @@ -import logger from '../../../handlers/logger'; -import buy from './addons/buy'; -import cancel from './addons/cancel'; +// Dependencies import { CommandInteraction } from 'discord.js'; + +// Handlers +import logger from '../../../handlers/logger'; + +// Modules +import buy from './modules/buy'; +import cancel from './modules/cancel'; + +// Function export default async (interaction: CommandInteraction) => { // Destructure member - const { member } = interaction; + const { options, commandName, guild, user } = interaction; - // If subcommand is buy - if (interaction.options.getSubcommand() === 'buy') { - // Execute buy addon + // Module - Buy + if (options?.getSubcommand() === 'buy') { + // Execute Module - Buy await buy(interaction); } - // If subcommand is cancel - if (interaction.options.getSubcommand() === 'cancel') { - // Execute cancel addon + // Module - Cancel + if (options?.getSubcommand() === 'cancel') { + // Execute Module - Cancel await cancel(interaction); } // Send debug message - await logger.debug( - `Guild: ${interaction?.guild?.id} User: ${ - interaction?.user?.id - } executed /${ - interaction.commandName - } ${interaction.options.getSubcommandGroup()} ${interaction.options.getSubcommand()}` + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` ); }; diff --git a/src/commands/shop/roles/modules/buy.ts b/src/commands/shop/roles/modules/buy.ts new file mode 100644 index 0000000..0ffb36d --- /dev/null +++ b/src/commands/shop/roles/modules/buy.ts @@ -0,0 +1,99 @@ +// Dependencies +import { + CommandInteraction, + ColorResolvable, + GuildMemberRoleManager, +} from 'discord.js'; + +// Configurations +import config from '../../../../../config.json'; + +// Models +import userSchema from '../../../../helpers/database/models/userSchema'; +import shopRolesSchema from '../../../../helpers/database/models/shopRolesSchema'; +import guildSchema from '../../../../helpers/database/models/guildSchema'; + +// Helpers +import creditNoun from '../../../../helpers/creditNoun'; + +// Function +export default async (interaction: CommandInteraction) => { + const { options, guild, user, member } = interaction; + + const optionName = options?.getString('name'); + + // If amount is null + if (optionName === null) { + // Embed object + const embed = { + title: ':dollar: Shop - Roles [Buy]' as string, + description: 'We could not read your requested name.' as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + await guild?.roles + .create({ + name: optionName, + color: 'RED', + reason: `${user?.id} bought from shop`, + }) + .then(async (role) => { + // Get guild object + const guildDB = await guildSchema?.findOne({ + guildId: guild?.id, + }); + + const userDB = await userSchema?.findOne({ + userId: user?.id, + guildId: guild?.id, + }); + + const { pricePerHour } = guildDB?.shop?.roles; + + userDB.credits -= pricePerHour; + + await userDB?.save(); + + await shopRolesSchema?.create({ + roleId: role?.id, + userId: user?.id, + guildId: guild?.id, + pricePerHour, + lastPayed: new Date(), + }); + + await (member?.roles as GuildMemberRoleManager)?.add(role?.id); + await shopRolesSchema?.find()?.then((role: any) => console.log(role)); + + const embed = { + title: ':shopping_cart: Shop - Roles [Buy]' as string, + description: + `You have bought ${role?.name} for ${guildDB?.shop?.roles?.pricePerHour} per hour.` as string, + color: config?.colors?.success as ColorResolvable, + fields: [ + { + name: 'Your balance' as string, + value: `${creditNoun(userDB?.credits)}` as string, + }, + ], + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ + embeds: [embed], + }); + }) + .catch(console.error); +}; diff --git a/src/commands/shop/roles/modules/cancel.ts b/src/commands/shop/roles/modules/cancel.ts new file mode 100644 index 0000000..6d7ce8d --- /dev/null +++ b/src/commands/shop/roles/modules/cancel.ts @@ -0,0 +1,87 @@ +// Dependencies +import { + CommandInteraction, + ColorResolvable, + GuildMemberRoleManager, +} from 'discord.js'; + +// Configurations +import config from '../../../../../config.json'; + +// Models +import userSchema from '../../../../helpers/database/models/userSchema'; +import shopRolesSchema from '../../../../helpers/database/models/shopRolesSchema'; + +// Helpers +import creditNoun from '../../../../helpers/creditNoun'; + +// Function +export default async (interaction: CommandInteraction) => { + const { options, guild, user, member } = interaction; + + const optionRole = options.getRole('role'); + + // If amount is null + if (optionRole === null) { + // Embed object + const embed = { + title: ':dollar: Shop - Roles [Cancel]' as string, + description: 'We could not read your requested role.' as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + return await interaction?.editReply({ embeds: [embed] }); + } + + const roleExist = await shopRolesSchema?.find({ + guildId: guild?.id, + userId: user?.id, + roleId: optionRole?.id, + }); + + if (roleExist) { + await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id); + + await guild?.roles + .delete(optionRole?.id, `${user?.id} canceled from shop`) + .then(async () => { + const userDB = await userSchema?.findOne({ + userId: user?.id, + guildId: guild?.id, + }); + + await shopRolesSchema?.deleteOne({ + roleId: optionRole?.id, + userId: user?.id, + guildId: guild?.id, + }); + + const embed = { + title: ':shopping_cart: Shop - Roles [Cancel]' as string, + description: `You have canceled ${optionRole.name}.` as string, + color: config?.colors?.success as ColorResolvable, + fields: [ + { + name: 'Your balance' as string, + value: `${creditNoun(userDB?.credits)}` as string, + }, + ], + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + return interaction?.editReply({ + embeds: [embed], + }); + }) + .catch(console.error); + } +}; diff --git a/src/commands/utilities/addons/about.ts b/src/commands/utilities/addons/about.ts deleted file mode 100644 index d0d4d48..0000000 --- a/src/commands/utilities/addons/about.ts +++ /dev/null @@ -1,18 +0,0 @@ -import config from '../../../../config.json'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - const interactionEmbed = { - title: ':hammer: Utilities - About', - description: `This bot is hosted by ${ - config.hoster.url - ? `[${config.hoster.name}](${config.hoster.url})` - : `${config.hoster.name}` - }, the bot is developed by [Zyner](https://github.com/ZynerOrg)! - - If you are interested in contributing, then just [fork it](https://github.com/ZynerOrg/xyter) yourself, we :heart: Open Source.`, - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - interaction.editReply({ embeds: [interactionEmbed] }); -}; diff --git a/src/commands/utilities/addons/index.ts b/src/commands/utilities/addons/index.ts deleted file mode 100644 index 1eb5b45..0000000 --- a/src/commands/utilities/addons/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import lookup from './lookup'; -import about from './about'; -import stats from './stats'; - -export default { lookup, about, stats }; diff --git a/src/commands/utilities/addons/lookup.ts b/src/commands/utilities/addons/lookup.ts deleted file mode 100644 index 4183509..0000000 --- a/src/commands/utilities/addons/lookup.ts +++ /dev/null @@ -1,87 +0,0 @@ -import axios from 'axios'; -import config from '../../../../config.json'; -import logger from '../../../handlers/logger'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - try { - // Get lookup query - const query = await interaction.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: config.colors.error as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // 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: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - - // Send interaction reply - await interaction.editReply({ embeds: [embed] }); - } - }) - .catch(async (e) => { - await logger.error(e); - }); - } catch (e) { - await logger.error(e); - } -}; diff --git a/src/commands/utilities/addons/stats.ts b/src/commands/utilities/addons/stats.ts deleted file mode 100644 index d73139f..0000000 --- a/src/commands/utilities/addons/stats.ts +++ /dev/null @@ -1,53 +0,0 @@ -import config from '../../../../config.json'; -import { CommandInteraction } from 'discord.js'; -export default async (interaction: CommandInteraction) => { - if (interaction?.client?.uptime === null) return; - let totalSeconds = interaction?.client?.uptime / 1000; - const days = Math.floor(totalSeconds / 86400); - totalSeconds %= 86400; - const hours = Math.floor(totalSeconds / 3600); - totalSeconds %= 3600; - const minutes = Math.floor(totalSeconds / 60); - const seconds = Math.floor(totalSeconds % 60); - - const uptime = `${days} days, ${hours} hours, ${minutes} minutes and ${seconds} seconds`; - - const interactionEmbed = { - title: ':hammer: Utilities - Stats', - description: 'Below you can see a list of statistics about the bot.', - fields: [ - { - name: '⏰ Latency', - value: `${Date.now() - interaction.createdTimestamp} ms`, - inline: true, - }, - { - name: '⏰ API Latency', - value: `${Math.round(interaction.client.ws.ping)} ms`, - inline: true, - }, - { - name: '⏰ Uptime', - value: `${uptime}`, - inline: false, - }, - { - name: '📈 Guilds', - value: `${interaction.client.guilds.cache.size}`, - inline: true, - }, - { - name: '📈 Users (non-unique)', - value: `${interaction.client.guilds.cache.reduce( - (acc, guild) => acc + guild.memberCount, - 0 - )}`, - inline: true, - }, - ], - color: config.colors.success as any, - timestamp: new Date(), - footer: { iconURL: config.footer.icon, text: config.footer.text }, - }; - interaction.editReply({ embeds: [interactionEmbed] }); -}; diff --git a/src/commands/utilities/index.ts b/src/commands/utilities/index.ts index cbb3723..096a60f 100644 --- a/src/commands/utilities/index.ts +++ b/src/commands/utilities/index.ts @@ -1,8 +1,16 @@ +// Dependencies import { SlashCommandBuilder } from '@discordjs/builders'; -import lookup from './addons/lookup'; -import about from './addons/about'; -import stats from './addons/stats'; 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 '../../handlers/logger'; + +// Function export default { data: new SlashCommandBuilder() .setName('utilities') @@ -27,20 +35,30 @@ export default { subcommand.setName('stats').setDescription('Check bot statistics!)') ), async execute(interaction: CommandInteraction) { - // If subcommand is lookup - if (interaction.options.getSubcommand() === 'lookup') { - // Execute lookup addon - await lookup(interaction); + // Destructure + const { options, guild, user, commandName } = interaction; + + // Module - Lookup + if (options?.getSubcommand() === 'lookup') { + // Execute Module - Lookup + return await lookup(interaction); } - // If subcommand is about - else if (interaction.options.getSubcommand() === 'about') { - // Execute about addon - await about(interaction); + // Module - About + else if (options?.getSubcommand() === 'about') { + // Execute Module - About + return await about(interaction); } - // If subcommand is stats - else if (interaction.options.getSubcommand() === 'stats') { - // Execute stats addon - await stats(interaction); + // Module - Stats + else if (options?.getSubcommand() === 'stats') { + // Execute Module - Stats + return await stats(interaction); } + + // Send debug message + return logger?.debug( + `Guild: ${guild?.id} User: ${ + user?.id + } executed /${commandName} ${options?.getSubcommandGroup()} ${options?.getSubcommand()}` + ); }, }; diff --git a/src/commands/utilities/modules/about.ts b/src/commands/utilities/modules/about.ts new file mode 100644 index 0000000..93e863e --- /dev/null +++ b/src/commands/utilities/modules/about.ts @@ -0,0 +1,26 @@ +// Dependencies +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Function +export default async (interaction: CommandInteraction) => { + const interactionEmbed = { + title: ':hammer: Utilities [About]' as string, + description: `This bot is hosted by ${ + config?.hoster?.url + ? `[${config?.hoster?.name}](${config?.hoster?.url})` + : `${config?.hoster?.name}` + }, the bot is developed by [Zyner](https://github.com/ZynerOrg)! + + If you are interested in contributing, then just [fork it](https://github.com/ZynerOrg/xyter) yourself, we :heart: Open Source.` as string, + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + interaction?.editReply({ embeds: [interactionEmbed] }); +}; diff --git a/src/commands/utilities/modules/lookup.ts b/src/commands/utilities/modules/lookup.ts new file mode 100644 index 0000000..102192a --- /dev/null +++ b/src/commands/utilities/modules/lookup.ts @@ -0,0 +1,112 @@ +// Dependencies +import axios from 'axios'; +import { CommandInteraction, ColorResolvable } from 'discord.js'; + +// Configurations +import config from '../../../../config.json'; + +// Handlers +import logger from '../../../handlers/logger'; + +// Function +export default 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' as string, + description: `${res?.data?.message}: ${res?.data?.query}` as string, + color: config?.colors?.error as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // 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' as string, + fields: [ + { + name: 'AS' as string, + value: `${res?.data?.as || 'Not available'}` as string, + }, + { + name: 'Country' as string, + value: `${res?.data?.country || 'Not available'}` as string, + }, + { + name: 'Country Code' as string, + value: `${res?.data?.countryCode || 'Not available'}` as string, + }, + { + name: 'Region' as string, + value: `${res?.data?.region || 'Not available'}` as string, + }, + { + name: 'Region Name' as string, + value: `${res?.data?.regionName || 'Not available'}` as string, + }, + { + name: 'City' as string, + value: `${res?.data?.city || 'Not available'}` as string, + }, + { + name: 'ZIP Code' as string, + value: `${res?.data?.zip || 'Not available'}` as string, + }, + { + name: 'Latitude' as string, + value: `${res?.data?.lat || 'Not available'}` as string, + }, + { + name: 'Longitude' as string, + value: `${res?.data?.lon || 'Not available'}` as string, + }, + { + name: 'Timezone' as string, + value: `${res?.data?.timezone || 'Not available'}` as string, + }, + { + name: 'ISP' as string, + value: `${res?.data?.isp || 'Not available'}` as string, + }, + { + name: 'Organization' as string, + value: `${res?.data?.org || 'Not available'}` as string, + }, + ], + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + + // Send interaction reply + await interaction?.editReply({ embeds: [embed] }); + } + }) + .catch(async (e) => { + logger?.error(e); + }); +}; diff --git a/src/commands/utilities/modules/stats.ts b/src/commands/utilities/modules/stats.ts new file mode 100644 index 0000000..1490b4e --- /dev/null +++ b/src/commands/utilities/modules/stats.ts @@ -0,0 +1,58 @@ +import config from '../../../../config.json'; +import { CommandInteraction, ColorResolvable } from 'discord.js'; +export default async (interaction: CommandInteraction) => { + const { client } = interaction; + if (client?.uptime === null) return; + let totalSeconds = client?.uptime / 1000; + const days = Math?.floor(totalSeconds / 86400); + totalSeconds %= 86400; + const hours = Math?.floor(totalSeconds / 3600); + totalSeconds %= 3600; + const minutes = Math?.floor(totalSeconds / 60); + const seconds = Math?.floor(totalSeconds % 60); + + const uptime = `${days} days, ${hours} hours, ${minutes} minutes and ${seconds} seconds`; + + const interactionEmbed = { + title: ':hammer: Utilities - Stats' as string, + description: + 'Below you can see a list of statistics about the bot.' as string, + fields: [ + { + name: '⏰ Latency' as string, + value: `${Date?.now() - interaction?.createdTimestamp} ms` as string, + inline: true, + }, + { + name: '⏰ API Latency' as string, + value: `${Math?.round(client?.ws?.ping)} ms` as string, + inline: true, + }, + { + name: '⏰ Uptime' as string, + value: `${uptime}` as string, + inline: false, + }, + { + name: '📈 Guilds' as string, + value: `${client?.guilds?.cache?.size}` as string, + inline: true, + }, + { + name: '📈 Users (non-unique)' as string, + value: `${client?.guilds?.cache?.reduce( + (acc, guild) => acc + guild?.memberCount, + 0 + )}` as string, + inline: true, + }, + ], + color: config?.colors?.success as ColorResolvable, + timestamp: new Date() as Date, + footer: { + iconURL: config?.footer?.icon as string, + text: config?.footer?.text as string, + }, + }; + interaction?.editReply({ embeds: [interactionEmbed] }); +};