From 2e9570b6be5397f1d3713645fb55a30ffc26057e Mon Sep 17 00:00:00 2001 From: janderedev Date: Wed, 5 Jan 2022 20:20:55 +0100 Subject: [PATCH] fix help command --- src/bot/commands/ban.ts | 3 +- src/bot/commands/bot_managers.ts | 1 + src/bot/commands/botctl.ts | 1 + src/bot/commands/debug.ts | 3 +- src/bot/commands/eval.ts | 1 + src/bot/commands/help.ts | 106 ++++++++++++++++++++++++++++- src/bot/commands/kick.ts | 1 + src/bot/commands/moderator.ts | 1 + src/bot/commands/ping.ts | 1 + src/bot/commands/prefix.ts | 3 +- src/bot/commands/purge.ts | 3 +- src/bot/commands/settings.ts | 5 +- src/bot/commands/shell_eval.ts | 1 + src/bot/commands/test.ts | 5 +- src/bot/commands/unban.ts | 1 + src/bot/commands/warn.ts | 1 + src/bot/commands/warns.ts | 1 + src/bot/commands/whitelist.ts | 3 +- src/bot/modules/command_handler.ts | 12 ++-- src/struct/Command.ts | 1 + src/struct/CommandCategory.ts | 7 ++ 21 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 src/struct/CommandCategory.ts diff --git a/src/bot/commands/ban.ts b/src/bot/commands/ban.ts index 5780d99..8be40d2 100644 --- a/src/bot/commands/ban.ts +++ b/src/bot/commands/ban.ts @@ -17,8 +17,9 @@ export default { name: 'ban', aliases: null, description: 'Ban a member from the server', - syntax: '/ban @username [10m?] [reason?]', + syntax: '/ban @username [10m|1h|...?] [reason?]', removeEmptyArgs: true, + category: 'moderation', run: async (message: MessageCommandContext, args: string[]) => { if (!await isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); diff --git a/src/bot/commands/bot_managers.ts b/src/bot/commands/bot_managers.ts index 770debf..15f88c0 100644 --- a/src/bot/commands/bot_managers.ts +++ b/src/bot/commands/bot_managers.ts @@ -12,6 +12,7 @@ export default { aliases: [ 'admins', 'manager', 'managers' ], description: 'Allow users to control the bot\'s configuration', syntax: SYNTAX, + category: 'configuration', run: async (message: MessageCommandContext, args: string[]) => { if (!hasPerm(message.member!, 'ManageServer')) return message.reply('You need **ManageServer** permission to use this command.'); diff --git a/src/bot/commands/botctl.ts b/src/bot/commands/botctl.ts index a8846b8..dc7023d 100644 --- a/src/bot/commands/botctl.ts +++ b/src/bot/commands/botctl.ts @@ -12,6 +12,7 @@ export default { name: 'botctl', aliases: null, description: 'Perform administrative actions', + category: 'configuration', run: async (message: MessageCommandContext, args: string[]) => { if (!isBotManager(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); diff --git a/src/bot/commands/debug.ts b/src/bot/commands/debug.ts index bb5c599..ea3e784 100644 --- a/src/bot/commands/debug.ts +++ b/src/bot/commands/debug.ts @@ -4,7 +4,8 @@ import MessageCommandContext from "../../struct/MessageCommandContext"; export default { name: 'debug', aliases: null, - description: 'give info helpful for development and debugging', + description: 'Gives info helpful for development and debugging', + category: 'misc', run: (message: MessageCommandContext, args: string[]) => { message.reply(`Server ID: ${message.channel?.server_id || 'None'}\n` + `Server context: ${message.serverContext._id} ` diff --git a/src/bot/commands/eval.ts b/src/bot/commands/eval.ts index ee306e0..0d975d1 100644 --- a/src/bot/commands/eval.ts +++ b/src/bot/commands/eval.ts @@ -9,6 +9,7 @@ export default { description: 'Evaluate JS code', restrict: 'BOTOWNER', removeEmptyArgs: false, + category: 'owner', run: async (message: Message, args: string[]) => { let cmd = `let { client } = require("../..");` + `let axios = require("axios").default;` diff --git a/src/bot/commands/help.ts b/src/bot/commands/help.ts index 1c73589..b1626dc 100644 --- a/src/bot/commands/help.ts +++ b/src/bot/commands/help.ts @@ -1,11 +1,111 @@ import Command from "../../struct/Command"; import { Message } from "revolt.js/dist/maps/Messages"; +import { commands, DEFAULT_PREFIX, ownerIDs } from "../modules/command_handler"; +import CommandCategory from "../../struct/CommandCategory"; + +const categories: { [key: string]: CommandCategory } = { + 'moderation': { + friendlyName: 'Moderation', + description: 'Moderation-focused commands', + aliases: [ 'mod', 'mods' ], + }, + 'configuration': { + friendlyName: 'Configuration', + description: 'Configure AutoMod', + aliases: [ 'conf', 'config' ], + }, + 'misc': { + friendlyName: 'Misc', + description: 'Random stuff :yed:', + aliases: [ 'miscellaneous', 'weirdwordicantspell' ], + }, + 'owner': { + friendlyName: 'Owner', + description: 'Owner-only commands for managing AutoMod', + aliases: [], + }, + 'uncategorized': { + friendlyName: 'Uncategorized', + description: 'Uncategorized commands', + aliases: [], + }, +}; export default { name: 'help', aliases: null, - description: 'help command i guess', - run: (message: Message, args: string[]) => { - message.reply(`command list can be found here kthxbay https://github.com/janderedev/revolt-automod/wiki/Bot-usage`); + description: 'Help command.', + removeEmptyArgs: true, + category: 'misc', + run: async (message: Message, args: string[]) => { + const isBotOwner = ownerIDs.includes(message.author_id); + const prefix = DEFAULT_PREFIX; // TODO: fetch prefix from server config + + let searchInput = args.shift()?.toLowerCase(); + if (!searchInput) { + let msg = `## AutoMod help\n` + + `Type **${prefix}help [category]** to view see all commands or **${prefix}help [command]** to learn more about a command.\n\n`; + + let total = 0; + + for (const categoryName in categories) { + let cmdCount = commands.filter( + cmd => ((cmd.category || 'uncategorized') == categoryName) && + (cmd.restrict == 'BOTOWNER' ? isBotOwner : true) // Ensure owner commands are only shown to bot owner + ).length; + + if (cmdCount > 0) { + total++; + const category = categories[categoryName]; + msg += `**${category.friendlyName}**\n` + + ` \u200b \u200b ↳ ${(category.description)} \u200b $\\big |$ \u200b **${cmdCount}** command${cmdCount == 1 ? '' : 's'}\n`; + } + } + + msg += `\n##### Categories: ${total}`; + + await message.reply(msg); + } else { + let [ categoryName, category ] = Object.entries(categories).find( + c => c[1].friendlyName.toLowerCase() == searchInput + || c[0].toLowerCase() == searchInput + ) || Object.entries(categories).find( + c => c[1].aliases.find(k => k.toLowerCase() == searchInput) + ) || []; + if (category && !searchInput.startsWith(prefix)) { + let msg = `**AutoMod help** - Category: ${category.friendlyName}\n` + + `${category.description}\n\n` + + `Type **${prefix}help [command]** to learn more about a command.\n\n`; + + let cmdList = commands.filter(c => (c.category || 'uncategorized') == categoryName); + if (cmdList.length > 0) { + for (const cmd of cmdList) { + msg += `**${prefix}${cmd.name}** \u200b $\\big |$ \u200b ${cmd.description}\n`; + + msg += '\n'; + } + + msg += `##### Total: ${cmdList.length}`; + } else msg += `### This category is empty.`; + + await message.reply(msg); + } else { + if (searchInput.startsWith(prefix)) searchInput = searchInput.substring(prefix.length); + let cmd = commands.find(c => c.name.toLowerCase() == searchInput) + || commands.find(c => c.aliases && c.aliases.find(k => k.toLowerCase() == searchInput)); + + if (!cmd) { + return message.reply(`I can't find any command or category matching \`${searchInput}\`.`); + } else { + let msg = `**AutoMod help** - Command: ${cmd.name}\n` + + `${cmd.description}\n\n`; + + if (cmd.syntax) msg += `Syntax: \`${cmd.syntax}\`\n`; + msg += 'Aliases: ' + (cmd.aliases ? `\`${cmd.aliases.join(`\`, \``)}\`` : 'None') + '\n'; + + message.reply(msg); + } + } + } } } as Command; diff --git a/src/bot/commands/kick.ts b/src/bot/commands/kick.ts index f2b8391..965a389 100644 --- a/src/bot/commands/kick.ts +++ b/src/bot/commands/kick.ts @@ -14,6 +14,7 @@ export default { description: 'Eject a member from the server', syntax: '/kick @username [reason?]', removeEmptyArgs: true, + category: 'moderation', run: async (message: MessageCommandContext, args: string[]) => { if (!await isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); diff --git a/src/bot/commands/moderator.ts b/src/bot/commands/moderator.ts index 75c178a..820ad29 100644 --- a/src/bot/commands/moderator.ts +++ b/src/bot/commands/moderator.ts @@ -15,6 +15,7 @@ export default { aliases: [ 'moderators', 'mod', 'mods' ], description: 'Allow users to moderate other users', syntax: SYNTAX, + category: 'configuration', run: async (message: MessageCommandContext, args: string[]) => { if (!await isBotManager(message.member!, message.channel?.server!)) return message.reply(NO_MANAGER_MSG); diff --git a/src/bot/commands/ping.ts b/src/bot/commands/ping.ts index 30e2c8a..a6285d1 100644 --- a/src/bot/commands/ping.ts +++ b/src/bot/commands/ping.ts @@ -6,6 +6,7 @@ export default { name: 'ping', aliases: null, description: 'ping pong', + category: 'misc', run: async (message: Message, args: string[]) => { let now = Date.now(); message.reply(`Measuring...`) diff --git a/src/bot/commands/prefix.ts b/src/bot/commands/prefix.ts index 23f153b..df86f1e 100644 --- a/src/bot/commands/prefix.ts +++ b/src/bot/commands/prefix.ts @@ -11,8 +11,9 @@ const MENTION_TEXT = 'You can also @mention me instead of using the prefix.'; export default { name: 'prefix', aliases: null, - description: 'modify prefix', + description: 'Configure AutoMod\'s prefix', syntax: SYNTAX, + category: 'configuration', run: async (message: Message, args: string[]) => { let config: ServerConfig = (await client.db.get('servers').findOne({ id: message.channel?.server_id })) ?? {}; diff --git a/src/bot/commands/purge.ts b/src/bot/commands/purge.ts index 37a264f..4439f4b 100644 --- a/src/bot/commands/purge.ts +++ b/src/bot/commands/purge.ts @@ -9,8 +9,9 @@ const MAX_PURGE_AMOUNT = 100; export default { name: 'purge', aliases: [ 'clear' ], - description: 'delete multiple messages at once', + description: 'Mass delete messages', syntax: SYNTAX, + category: 'moderation', run: async (message: Message, args: string[]) => { try { if (!message.member || !await isModerator(message.member!, message.channel?.server!)) return message.reply('🔒 Access denied'); diff --git a/src/bot/commands/settings.ts b/src/bot/commands/settings.ts index ba43447..451baf1 100644 --- a/src/bot/commands/settings.ts +++ b/src/bot/commands/settings.ts @@ -11,11 +11,12 @@ import MessageCommandContext from "../../struct/MessageCommandContext"; export default { name: 'settings', aliases: [ 'setting' ], - description: 'change antispam settings', + description: 'Manage AutoMod\'s configuration', + category: 'configuration', run: async (message: MessageCommandContext, args: string[]) => { if (!isBotManager(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); - return 'command is disabled for now'; + return 'This feature is currently disabled'; let settings = { spam: [ diff --git a/src/bot/commands/shell_eval.ts b/src/bot/commands/shell_eval.ts index 9a1f722..fe61ddc 100644 --- a/src/bot/commands/shell_eval.ts +++ b/src/bot/commands/shell_eval.ts @@ -8,6 +8,7 @@ export default { description: 'Run code in a shell', restrict: 'BOTOWNER', removeEmptyArgs: false, + category: 'owner', run: async (message: Message, args: string[]) => { let cmd = args.join(' '); diff --git a/src/bot/commands/test.ts b/src/bot/commands/test.ts index 9a3ed92..c763cbf 100644 --- a/src/bot/commands/test.ts +++ b/src/bot/commands/test.ts @@ -4,8 +4,9 @@ import { Message } from "revolt.js/dist/maps/Messages"; export default { name: 'test', aliases: [ 'testalias' ], - description: 'epic test command', + description: 'Test command', + category: 'misc', run: (message: Message, args: string[]) => { - message.reply('I am here'); + message.reply('Beep boop.'); } } as Command; diff --git a/src/bot/commands/unban.ts b/src/bot/commands/unban.ts index ec5ffba..81dfab6 100644 --- a/src/bot/commands/unban.ts +++ b/src/bot/commands/unban.ts @@ -11,6 +11,7 @@ export default { aliases: [ 'pardon' ], description: 'Unbans a user', syntax: '/unban [@user or ID]', + category: 'moderation', run: async (message: MessageCommandContext, args: string[]) => { if (!isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); diff --git a/src/bot/commands/warn.ts b/src/bot/commands/warn.ts index 4732994..5d35e48 100644 --- a/src/bot/commands/warn.ts +++ b/src/bot/commands/warn.ts @@ -11,6 +11,7 @@ export default { aliases: null, removeEmptyArgs: false, description: 'add an infraction to an user\'s record', + category: 'moderation', run: async (message: MessageCommandContext, args: string[]) => { if (!await isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); let user = await parseUserOrId(args.shift() ?? ''); diff --git a/src/bot/commands/warns.ts b/src/bot/commands/warns.ts index 452cb7d..82aa3ed 100644 --- a/src/bot/commands/warns.ts +++ b/src/bot/commands/warns.ts @@ -16,6 +16,7 @@ export default { aliases: [ 'warnings', 'infractions', 'infraction' ], description: 'Show all user infractions', syntax: '/warns; /warns @username ["export-csv"]; /warns rm [ID]', + category: 'moderation', run: async (message: MessageCommandContext, args: string[]) => { if (!await isModerator(message.member!, message.serverContext)) return message.reply(NO_MANAGER_MSG); diff --git a/src/bot/commands/whitelist.ts b/src/bot/commands/whitelist.ts index a6a6e97..f315e25 100644 --- a/src/bot/commands/whitelist.ts +++ b/src/bot/commands/whitelist.ts @@ -6,13 +6,14 @@ import MessageCommandContext from "../../struct/MessageCommandContext"; import ServerConfig from "../../struct/ServerConfig"; import { isBotManager, NO_MANAGER_MSG, parseUser } from "../util"; -const SYNTAX = ''; +const SYNTAX = '/whitelist add @user; /whitelist remove @user; /whitelist list'; export default { name: 'whitelist', aliases: [], description: 'Allow users or roles to bypass moderation rules', syntax: SYNTAX, + category: 'configuration', run: async (message: MessageCommandContext, args: string[]) => { let config: ServerConfig = await client.db.get('servers').findOne({ id: message.serverContext._id }) || {} if (!config.whitelist) config.whitelist = { users: [], roles: [], managers: true } diff --git a/src/bot/modules/command_handler.ts b/src/bot/modules/command_handler.ts index 3e5ba86..8cdc573 100644 --- a/src/bot/modules/command_handler.ts +++ b/src/bot/modules/command_handler.ts @@ -13,13 +13,16 @@ import { fileURLToPath } from 'url'; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); +const ownerIDs = process.env['BOT_OWNERS'] ? process.env['BOT_OWNERS'].split(',') : []; const DEFAULT_PREFIX = process.env['PREFIX'] ?? process.env['BOT_PREFIX'] ?? process.env['COMMAND_PREFIX'] ?? '/'; +let commands: Command[]; + (async () => { - let commands: Command[] = (await Promise.all( + commands = (await Promise.all( fs.readdirSync(path.join(dirname, '..', 'commands')) .filter(file => file.endsWith('.js')) .map(async file => await import(path.join(dirname, '..', 'commands', file)) as Command) @@ -43,10 +46,10 @@ const DEFAULT_PREFIX = process.env['PREFIX'] let guildPrefix = config.prefix ?? DEFAULT_PREFIX; if (cmdName.startsWith(`<@${client.user?._id}>`)) { - cmdName = cmdName.substr(`<@${client.user?._id}>`.length); + cmdName = cmdName.substring(`<@${client.user?._id}>`.length); if (!cmdName) cmdName = args.shift() ?? ''; // Space between mention and command name } else if (cmdName.startsWith(guildPrefix)) { - cmdName = cmdName.substr(guildPrefix.length); + cmdName = cmdName.substring(guildPrefix.length); if (config.spaceAfterPrefix && !cmdName) cmdName = args.shift() ?? ''; } else return; @@ -55,7 +58,6 @@ const DEFAULT_PREFIX = process.env['PREFIX'] let cmd = commands.find(c => c.name == cmdName || (c.aliases?.indexOf(cmdName!) ?? -1) > -1); if (!cmd) return; - let ownerIDs = process.env['BOT_OWNERS'] ? process.env['BOT_OWNERS'].split(',') : []; if (cmd.restrict == 'BOTOWNER' && ownerIDs.indexOf(msg.author_id) == -1) { logger.warn(`User ${msg.author?.username} tried to run owner-only command: ${cmdName}`); msg.reply('🔒 Access denied'); @@ -95,4 +97,4 @@ const DEFAULT_PREFIX = process.env['PREFIX'] }); })(); -export { DEFAULT_PREFIX } +export { DEFAULT_PREFIX, commands, ownerIDs } diff --git a/src/struct/Command.ts b/src/struct/Command.ts index a33b855..80e394c 100644 --- a/src/struct/Command.ts +++ b/src/struct/Command.ts @@ -6,6 +6,7 @@ class Command { restrict?: 'BOTOWNER' | null; removeEmptyArgs?: boolean | null; run: Function; + category?: string; } export default Command; diff --git a/src/struct/CommandCategory.ts b/src/struct/CommandCategory.ts new file mode 100644 index 0000000..5bacab4 --- /dev/null +++ b/src/struct/CommandCategory.ts @@ -0,0 +1,7 @@ +class CommandCategory { + friendlyName: string; + description: string; + aliases: string[]; +} + +export default CommandCategory;