diff --git a/commands/economy/deposit.js b/commands/economy/deposit.js index 73e8322..f15c478 100644 --- a/commands/economy/deposit.js +++ b/commands/economy/deposit.js @@ -1,5 +1,5 @@ const { ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } = require("discord.js"); -const { embedReplyFailureColor, embedReplySuccessColor } = require("../../helpers/embeds/embed-reply"); +const { embedReplyFailureColor, embedReplySuccessColor, embedReplyWarningColor } = require("../../helpers/embeds/embed-reply"); const { logToFileAndDatabase } = require("../../helpers/logger"); const db = require("../../helpers/db"); @@ -10,7 +10,7 @@ const feePercentage = 0.3; module.exports = { data: new SlashCommandBuilder() .setName("deposit") - .setDescription("Desposits a specified amount of money to your bank account.") + .setDescription("Deposits a specified amount of money to your bank account.") .addIntegerOption(option => option .setName("amount") @@ -76,7 +76,7 @@ module.exports = { isCommandReplied = true; const filter = i => i.user.id === interaction.user.id; - const collector = message.createMessageComponentCollector({ filter, time: 30000 }); + const collector = message.createMessageComponentCollector({ filter, time: 15000 }); collector.on("collect", async i => { if (i.customId === "confirm") { @@ -91,11 +91,12 @@ module.exports = { var embedReply = embedReplySuccessColor( "Deposit successful.", - `You've successfully deposited \`$${amount}\` for a fee of \`$${fee}\`.\Your balance decreased by total of \`$${totalAmount}\`.`, + `You've successfully deposited \`$${amount}\` for a fee of \`$${fee}\`.\nYour balance decreased by total of \`$${totalAmount}\`.`, interaction ); await i.update({ embeds: [embedReply], components: [] }); + await logToFileAndDatabase(interaction, JSON.stringify(embedReply.toJSON())); } else if (i.customId === "cancel") { var embedReply = embedReplyFailureColor( @@ -140,10 +141,7 @@ module.exports = { if (!isCommandReplied) { await interaction.reply({ embeds: [embedReply] }); - } - - //logging - const response = JSON.stringify(embedReply.toJSON()); - await logToFileAndDatabase(interaction, response); + await logToFileAndDatabase(interaction, JSON.stringify(embedReply.toJSON())); + } } } \ No newline at end of file diff --git a/commands/economy/pay.js b/commands/economy/pay.js index 0130aac..f8b1576 100644 --- a/commands/economy/pay.js +++ b/commands/economy/pay.js @@ -28,14 +28,25 @@ module.exports = { } else { const amount = interaction.options.getInteger("amount"); - const targetUserId = interaction.options.getUser("target").id; + + const targetUser = interaction.options.getUser("target"); + const targetUserId = targetUser.id; + const targetUserName = targetUser.username; const interactionUserId = interaction.user.id; + if (targetUserId === interactionUserId) { + var embedReply = embedReplyFailureColor( + "Payment - Error", + "You cannot issue a payment to yourself.", + interaction + ); + } + const interactionUserBalanceQuery = await db.query("SELECT balance FROM economy WHERE userId = ?", [interactionUserId]); - const userBalance = interactionUserBalanceQuery[0]?.balance || null; + const userBalance = interactionUserBalanceQuery[0]?.balance || 0; - const targetUserBalanceQuery = await db.query("SELECT balance FROM economy WHERE userId = ?", [targetUserId]); - const targetUserBalance = targetUserBalanceQuery[0]?.balance || null; + const targetUserQuery = await db.query("SELECT * FROM economy WHERE userId = ?", [targetUserId]); + const targetUserExists = targetUserQuery.length > 0; if (amount > userBalance) { var embedReply = embedReplyFailureColor( @@ -53,26 +64,17 @@ module.exports = { } else { await db.query("UPDATE economy SET balance = balance - ? WHERE userId = ?", - [ - amount, - interactionUserId - ] + [amount, interactionUserId] ); - if (!targetUserBalance) { - await db.query("INSERT INTO economy (userId, balance) VALUES (?, ?)", - [ - targetUserId, - amount - ] + if (!targetUserExists) { + await db.query("INSERT INTO economy (userName, userId, balance) VALUES (?, ?, ?)", + [targetUserName, targetUserId, amount] ); } else { await db.query("UPDATE economy SET balance = balance + ? WHERE userId = ?", - [ - amount, - targetUserId - ] + [amount, targetUserId] ); } @@ -85,9 +87,6 @@ module.exports = { } await interaction.reply({ embeds: [embedReply] }); - - //logging - const response = JSON.stringify(embedReply.toJSON()); - await logToFileAndDatabase(interaction, response); + await logToFileAndDatabase(interaction, JSON.stringify(embedReply.toJSON())); } } \ No newline at end of file diff --git a/commands/utility/help.js b/commands/utility/help.js index 66b85ed..65e94f0 100755 --- a/commands/utility/help.js +++ b/commands/utility/help.js @@ -1,6 +1,7 @@ const { SlashCommandBuilder } = require("discord.js"); -const { embedReplyPrimaryColorWithFields, embedReplyErrorColorWithFields } = require("../../helpers/embeds/embed-reply"); +const { embedReplyPrimaryColorWithFields, embedReplyErrorColorWithFields, embedReplyFailureColor } = require("../../helpers/embeds/embed-reply"); const { logToFileAndDatabase } = require("../../helpers/logger"); +const db = require("../../helpers/db"); module.exports = { data: new SlashCommandBuilder() @@ -18,137 +19,101 @@ module.exports = { { name: "Administration", value: "administration" }, ) .setRequired(false)) + .addStringOption(option => + option + .setName("command") + .setDescription("Specify a command's name you would like to know more information about.") + .setRequired(false)) .setDMPermission(true), async execute(interaction) { const commandCategory = interaction.options.getString("category"); - - const utilityCommands = - { name: "Utility", value: - "`/help` - Displays this message.\n" + - "`/ping` - Displays the discord API's current latency.\n" + - "`/ping-db` - Displays the current latency between the bot and it's database.\n" + - "`/server` - Provides information about the current server.\n" + - "`/user` - Provides information about a specified user.\n" + - "`/translate` - Translates a message from any language to any language.\n" + - "`/say` - Makes the bot say a specified message." - }; - - const funCommands = - { name: "Fun", value: - "`/coinflip` - Flips a coin that has a 50/50 chance landing on heads or tails.\n" + - "`/randompic` - Send a random picture using the [picsum.photos](https://picsum.photos/) API.\n" + - "`/randomfeet` - I have nothing to say about my greatest shame...\n" + - "`/911-countdown` - Displays huw much time is left until Spetember 11th." - }; - - const economyCommands = - { name: "Economy", value: - "`/work` - Lets you work for a random amount of money.\n" + - "`/beg` - Lets you beg for a random (or no) amount of money.\n" + - "`/rob` - Steals a random amount of money from the target user, and adds it to your balance.\n" + - "`/balance` - Displays the user's current bank and handheld balance.\n" + - "`/deposit` - Deposits money into the user's bank account. It has a daily limit for free deposits, but the users can choose to pay a small deposit fee if they exceed the limit.\n" + - "`/withdraw` - Withdraws money from the user's bank account.\n" + - "`/pay` - Transfers money from one user to another.\n" + - "`/roulette` - Lets you pick a color, then gives you a great price if you guess the color right.\n" + - "`/leaderboard` - Displays users with the most money on the server." - }; - - const moderationCommands = - { name: "Moderation", value: - "**__NOTE__**: The following commands require relevant **moderation permissions** for both the bot and the command's executor to work.\n" + - "`/warn` - Warns a specified member on the server.\n" + - "`/timeout` - Times out a specified member for a specified time.\n" + - "`/kick` - Kicks a specified member from the server.\n" + - "`/ban` - Bans a specified member from the server.\n" + - "`/purge` - Purges (mass deletes) a specified amount of messages from the current channel." - }; - - const administrationCommands = - { name: "Administration", value: - "**__NOTE__**: The following commands require relevant **administration permissions** for both the bot and the command's executor to work.\n" + - "`/autorole-configure` - Sets / modifies the autorole feature. When a new member joins the server, a specified role will get assigned to them automatically.\n" + - "`/autorole-disable` - Disables the autorole feature. New members won't get the specified role automatically on join anymore.\n" + - "`/welcome-configure` - Sets / modifies the welcome messages feature. When a new member joins the server, the bot send a specified welcome message.\n" + - "`/welcome-disable` - Disables the welcome messages feature. The bot won't send a welcome message on join anymore.\n" + - "`/logging-configure` - Sets / modifies the channel where event on the server will get logged.\n" + - "`/logging-disable` - Disables the logging feature. The bot won't log the events on the server anymore.\n" + - "`/bridge-configure` - Bridges all messages from one channel to another.\n" + - "`/bridge-disable` - Disables briding for a target channel.\n" + - "`/rename` - Renames a specified user to a specified nickname in the current server." - }; - - const tipField = - { name: "Tip.", value: - "**Friendly reminder**: You can use `/help ` to get a list of commands from a specific category. :grin:" + const commandName = interaction.options.getString("command"); + + if (commandCategory && commandName) { + var embedReply = embedReplyFailureColor( + "Help: Error", + "Please only provide one parameter at a time for the command.", + interaction + ); } + else if (commandCategory) { + const commandsQuery = await db.query("SELECT name, category FROM commandData WHERE category = ? ORDER BY name", [commandCategory]); + + if (commandsQuery.length === 0) { + var embedReply = embedReplyFailureColor( + "Help: Error", + "Invalid command category provided as parameter.\nPlease choose a valid category: `utility`, `fun`, `economy`, `moderation`, `administration`.", + interaction + ); + } + else { + const commandsList = commandsQuery + .map(cmd => `\`/${cmd.name}\``) + .join(", "); - const errorField = - { name: "Error", value: - "Invalid command category provided as parameter.\n" + - "Please choose a valid category: `utility`, `fun`, `economy`, `moderation`, `administration`." + var embedReply = embedReplyPrimaryColorWithFields( + `Help - ${commandCategory.charAt(0).toUpperCase() + commandCategory.slice(1)} Commands`, + `Here is a list of the bot's currently available **${commandCategory}** commands:`, + [{ name: "Commands", value: commandsList }], + interaction + ); + } + } + else if (commandName) { + const commandQuery = await db.query("SELECT category, description FROM commandData WHERE name = ?", [commandName]); + const category = commandQuery[0]?.category || null; + const description = commandQuery[0]?.description || null; + + if (!description) { + var embedReply = embedReplyFailureColor( + "Help - Error", + `There is no such command as \`/${commandName}\`.\nPlease provide a valid command name.`, + interaction + ); + } + else { + var embedReply = embedReplyPrimaryColorWithFields( + `Help - /${commandName}`, + "You can check out all commands with descriptions on [the bot's wiki](https://github.com/vb2007/discordbot/wiki/Commands)", + [ + { name: "Category", value: category.charAt(0).toUpperCase() + category.slice(1) }, + { name: "Description", value: description } + ], + interaction + ); + } } + else { + const allCommandsQuery = await db.query("SELECT name, category FROM commandData ORDER BY category, name"); - let embedReply; - if (!commandCategory) { - embedReply = embedReplyPrimaryColorWithFields( - "Help - All Commands.", - "Here is a list of the bot's currently avaliable commands:", - [ utilityCommands, funCommands, economyCommands, moderationCommands, administrationCommands, tipField ], + //grouping commands by category + const commandsByCategory = allCommandsQuery.reduce((acc, cmd) => { + if (!acc[cmd.category]) { + acc[cmd.category] = []; + } + acc[cmd.category].push(cmd.name); + return acc; + }, {}); + + //fields for each category + const categoryFields = Object.entries(commandsByCategory).map(([category, commands]) => ({ + name: category.charAt(0).toUpperCase() + category.slice(1), + value: commands.map(cmd => `\`/${cmd}\``).join(", ") + })); + + var embedReply = embedReplyPrimaryColorWithFields( + "Help - All Commands", + "You can check out all commands with descriptions on [the bot's wiki](https://github.com/vb2007/discordbot/wiki/Commands)", + [ + ...categoryFields, + { + name: "Tip.", + value: "You can use `/help ` to get a list of commands from a specific category." + } + ], interaction ); } - else { - switch (commandCategory) { - case "utility": - embedReply = embedReplyPrimaryColorWithFields( - "Help - Utility Commands.", - "Here is a list of the bot's currently avaliable **utility** commands:", - [ utilityCommands ], - interaction - ); - break; - case "fun": - embedReply = embedReplyPrimaryColorWithFields( - "Help - Fun Commands.", - "Here is a list of the bot's currently avaliable **fun** commands:", - [ funCommands ], - interaction - ); - break; - case "economy": - embedReply = embedReplyPrimaryColorWithFields( - "Help - Economy Commands.", - "Here is a list of the bot's currently avaliable **economy** commands:", - [ economyCommands ], - interaction - ); - break; - case "moderation": - embedReply = embedReplyPrimaryColorWithFields( - "Help - Moderation Commands.", - "Here is a list of the bot's currently avaliable **moderation** commands:", - [ moderationCommands ], - interaction - ); - break; - case "administration": - embedReply = embedReplyPrimaryColorWithFields( - "Help - Administration Commands.", - "Here is a list of the bot's currently avaliable **administration** commands:", - [ administrationCommands ], - interaction - ); - break; - default: - embedReply = embedReplyErrorColorWithFields( - "Help - Utility Commands.", - "Here is a list of the bot's currently avaliable **utility** commands:", - [ errorField ], - interaction - ); - } - } await interaction.reply({ embeds: [embedReply] }); diff --git a/package-lock.json b/package-lock.json index fee1074..b3e825e 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "discordbot", - "version": "2.2", + "version": "2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "discordbot", - "version": "2.2", + "version": "2.3", "license": "AGPL-3.0-only", "dependencies": { "ajv": "^8.17.1", @@ -1463,9 +1463,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "dev": true, "funding": [ { @@ -1482,9 +1482,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -1528,9 +1528,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001688", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", - "integrity": "sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==", + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "dev": true, "funding": [ { @@ -2008,9 +2008,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.73", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz", - "integrity": "sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==", + "version": "1.5.75", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz", + "integrity": "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==", "dev": true }, "node_modules/emittery": { @@ -2410,9 +2410,9 @@ } }, "node_modules/google-translate-api-x": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/google-translate-api-x/-/google-translate-api-x-10.7.1.tgz", - "integrity": "sha512-OdZDS6jRWzn1woOk62aOKQ5OyVaJSA+eyc6CktOWxo36IWfstOjwG/dkvnGl3Z2Sbpmk1A+jc2WwrBiRjqaY2A==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/google-translate-api-x/-/google-translate-api-x-10.7.2.tgz", + "integrity": "sha512-GSmbvGMcnULaih2NFgD4Y6840DLAMot90mLWgwoB+FG/QpetyZkFrZkxop8ZxXgOAQXGskFOhGJady8nA6ZJ2g==", "engines": { "node": ">=14.0.0" }, @@ -2622,9 +2622,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -3683,9 +3683,9 @@ "dev": true }, "node_modules/nodemon": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", - "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -4178,18 +4178,21 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } diff --git a/sql/commandData/table.sql b/sql/commandData/table.sql new file mode 100644 index 0000000..67f938e --- /dev/null +++ b/sql/commandData/table.sql @@ -0,0 +1,49 @@ +-- discordbot.commandData definition + +CREATE TABLE IF NOT EXISTS `commandData` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `name` text NOT NULL, + `category` text DEFAULT NULL, + `description` text DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_name` (`name`(255)) +); + +INSERT IGNORE INTO commandData (name,category,description) VALUES + ('help','utility','Displays a message with the currently available commands.'), + ('ping','utility','Displays the discord API''s current latency.'), + ('ping-db','utility','Displays the current latency between the bot and it''s database.'), + ('server','utility','Provides information about the current server.'), + ('user','utility','Provides information about a specified user.'), + ('translate','utility','Translates a message from any language to any language.'), + ('say','utility','Makes the bot say a specified message.'), + ('coinflip','fun','Flips a coin that has a 50/50 chance landing on heads or tails.'), + ('randompic','fun','Send a random picture using the [picsum.photos](https://picsum.photos/) API.'), + ('randomfeet','fun','I have nothing to say about my greatest shame...'); +INSERT IGNORE INTO commandData (name,category,description) VALUES + ('911-countdown','fun','Displays huw much time is left until Spetember 11th.'), + ('work','economy','Lets you work for a random amount of money.'), + ('beg','economy','Lets you beg for a random (or no) amount of money.'), + ('rob','economy','Steals a random amount of money from the target user, and adds it to your balance.'), + ('balance','economy','Displays the user''s current bank and handheld balance.'), + ('deposit','economy','Deposits money into the user''s bank account. It has a daily limit for free deposits, but the users can choose to pay a small deposit fee if they exceed the limit.'), + ('withdraw','economy','Withdraws money from the user''s bank account.'), + ('pay','economy','Transfers money from one user to another.'), + ('roulette','economy','Lets you pick a color, then gives you a great price if you guess the color right.'), + ('leaderboard','economy','Displays users with the most money on the server.'); +INSERT IGNORE INTO commandData (name,category,description) VALUES + ('warn','moderation','Warns a specified member on the server.'), + ('timeout','moderation','Times out a specified member for a specified time.'), + ('kick','moderation','Kicks a specified member from the server.'), + ('ban','moderation','Bans a specified member from the server.'), + ('purge','moderation','Purges (mass deletes) a specified amount of messages from the current channel.'), + ('autorole-configure','administration','Sets / modifies the autorole feature. When a new member joins the server, a specified role will get assigned to them automatically.'), + ('autorole-disable','administration','Disables the autorole feature. New members won''t get the specified role automatically on join anymore.'), + ('welcome-configure','administration','Sets / modifies the welcome messages feature. When a new member joins the server, the bot send a specified welcome message.'), + ('welcome-disable','administration','Disables the welcome messages feature. The bot won''t send a welcome message on join anymore.'), + ('logging-configure','administration','Sets / modifies the channel where event on the server will get logged.'); +INSERT IGNORE INTO commandData(name,category,description) VALUES + ('logging-disable','administration','Disables the logging feature. The bot won''t log the events on the server anymore.'), + ('bridge-configure','administration','Bridges all messages from one channel to another.'), + ('bridge-disable','administration','Disables briding for a target channel.'), + ('rename','administration','Renames a specified user to a specified nickname in the current server.'); \ No newline at end of file diff --git a/sql/commandUsageLog/table.sql b/sql/commandUsageLog/table.sql index deb23e0..46296d2 100644 --- a/sql/commandUsageLog/table.sql +++ b/sql/commandUsageLog/table.sql @@ -1,4 +1,4 @@ --- discordbotTest.commandUsageLog definition +-- discordbot.commandUsageLog definition CREATE TABLE IF NOT EXISTS `commandUsageLog` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, diff --git a/sql/configLogging/table.sql b/sql/configLogging/table.sql index b997d61..898c7af 100644 --- a/sql/configLogging/table.sql +++ b/sql/configLogging/table.sql @@ -1,3 +1,5 @@ +-- discordbot.configLogging definition + CREATE TABLE IF NOT EXISTS `configLogging` ( `guildId` varchar(50) NOT NULL, `logChannelId` text NOT NULL, diff --git a/sql/economy/table.sql b/sql/economy/table.sql index e66eef5..95c158d 100644 --- a/sql/economy/table.sql +++ b/sql/economy/table.sql @@ -14,7 +14,8 @@ CREATE TABLE IF NOT EXISTS `economy` ( `lastDepositTime` datetime DEFAULT NULL, `dailyDeposits` int DEFAULT 0 NOT NULL, `robSuccessChance` float DEFAULT 1, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + UNIQUE KEY `userId` (`userId`) ); CREATE INDEX IF NOT EXISTS idx_userId ON economy(userId); \ No newline at end of file diff --git a/unit-tests/help-commands.test.js b/unit-tests/help-commands.test.js index e3d9ac1..0b7c48a 100644 --- a/unit-tests/help-commands.test.js +++ b/unit-tests/help-commands.test.js @@ -1,31 +1,79 @@ -const fs = require("fs"); -const path = require("path"); +const fs = require('fs'); +const path = require('path'); -describe("/help command test", () => { +function parseCommandsFromSQL(sqlPath) { + const sql = fs.readFileSync(sqlPath, 'utf8'); + const commands = []; + + //splitting by lines that contain insert statements + const chunks = sql.split('INSERT IGNORE INTO commandData'); + chunks.shift(); //skipping the create table part + + chunks.forEach(chunk => { + const lines = chunk + .split('\n') + .filter(line => line.includes('(\'')) + .map(line => { + const match = line.match(/\((.*?)\)/); + if (!match) return null; + const [name, category, description] = match[1] + .split(',') + .map(str => str.trim().replace(/^'|'$/g, '')); + return { name, category, description }; + }) + .filter(Boolean); + + commands.push(...lines); + }); + + return commands; +} - const helpFilePath = path.join(__dirname, "..", "commands", "utility", "help.js"); - const helpFileContent = fs.readFileSync(helpFilePath, "utf-8"); - - const commandCategories = { - utility: "utilityCommands", - fun: "funCommands", - economy: "economyCommands", - moderation: "moderationCommands", - administration: "administrationCommands" +function getCommandsFromFS(commandsDir) { + const commands = []; + + const categories = fs.readdirSync(commandsDir); + categories.forEach(category => { + const categoryPath = path.join(commandsDir, category); + if (fs.statSync(categoryPath).isDirectory()) { + const files = fs.readdirSync(categoryPath); + files.forEach(file => { + if (file.endsWith('.js')) { + commands.push({ + name: file.replace('.js', ''), + category + }); + } + }); } + }); + + return commands; +} - Object.keys(commandCategories).forEach(category => { - test("All commmand in the filesystem should be listed in the /help command's relevant objects.", () => { - const commandsPath = path.join(__dirname, "..", "commands", category); - const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith(".js")); +describe('Command Data Tests', () => { + const sqlCommands = parseCommandsFromSQL(path.join(__dirname, '../sql/commandData/table.sql')); + const fsCommands = getCommandsFromFS(path.join(__dirname, '../commands')); + + test('All filesystem commands exist in SQL', () => { + fsCommands.forEach(fsCommand => { + const sqlCommand = sqlCommands.find(cmd => cmd.name === fsCommand.name); + expect(sqlCommand).toBeTruthy(); + }); + }); - commandFiles.forEach(file => { - const commandName = `/${file.replace(".js", "")}`; - const commandListObject = commandCategories[category]; + test('All commands have valid categories in SQL', () => { + fsCommands.forEach(fsCommand => { + const sqlCommand = sqlCommands.find(cmd => cmd.name === fsCommand.name); + expect(sqlCommand.category).toBe(fsCommand.category); + }); + }); - expect(helpFileContent).toContain(commandListObject); - expect(helpFileContent).toContain(commandName); - }); - }); + test('All commands have descriptions in SQL', () => { + fsCommands.forEach(fsCommand => { + const sqlCommand = sqlCommands.find(cmd => cmd.name === fsCommand.name); + expect(sqlCommand.description).toBeTruthy(); + expect(sqlCommand.description.length).toBeGreaterThan(0); }); + }); }); \ No newline at end of file