diff options
author | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-11-28 09:27:41 -0500 |
---|---|---|
committer | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-11-28 09:27:41 -0500 |
commit | 453683b57b8ff013ff25e2aaa4aa1d2e047edcb7 (patch) | |
tree | 8b98d2f30dbb6a8448602446cfacf9091667cc33 /src | |
parent | de4c3dcaf172804d34ae708be1ed3e75af42f4d5 (diff) | |
download | tanzanite-453683b57b8ff013ff25e2aaa4aa1d2e047edcb7.tar.gz tanzanite-453683b57b8ff013ff25e2aaa4aa1d2e047edcb7.tar.bz2 tanzanite-453683b57b8ff013ff25e2aaa4aa1d2e047edcb7.zip |
a few small changes
Diffstat (limited to 'src')
90 files changed, 2150 insertions, 1955 deletions
diff --git a/src/arguments/messageLink.ts b/src/arguments/messageLink.ts new file mode 100644 index 0000000..d270abd --- /dev/null +++ b/src/arguments/messageLink.ts @@ -0,0 +1,19 @@ +import { type BushArgumentTypeCaster } from '../lib'; + +export const messageLink: BushArgumentTypeCaster = async (_, phrase) => { + const match = client.consts.regex.messageLink.exec(phrase); + if (!match || !match.groups) return null; + + const { guild_id, channel_id, message_id } = match.groups; + + if (!guild_id || !channel_id || message_id) return null; + + const guild = client.guilds.cache.get(guild_id); + if (!guild) return null; + + const channel = guild.channels.cache.get(channel_id); + if (!channel || (!channel.isText() && !channel.isThread())) return null; + + const message = await channel.messages.fetch(message_id).catch(() => null); + return message; +}; diff --git a/src/commands/_fake-command/ironmoon.ts b/src/commands/_fake-command/ironmoon.ts index b13d1d3..428b554 100644 --- a/src/commands/_fake-command/ironmoon.ts +++ b/src/commands/_fake-command/ironmoon.ts @@ -4,7 +4,9 @@ export default class IronmoonCommand extends BushCommand { public constructor() { super('ironmoon', { category: 'fake-commands', - description: { content: '', examples: [''], usage: [''] }, + description: '', + examples: [''], + usage: [''], pseudo: true, clientPermissions: [], userPermissions: [] diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts index befa3ea..b44ae21 100644 --- a/src/commands/admin/channelPermissions.ts +++ b/src/commands/admin/channelPermissions.ts @@ -7,79 +7,50 @@ export default class ChannelPermissionsCommand extends BushCommand { aliases: ['channel-perms', 'cperms', 'cperm', 'chanperms', 'chanperm', 'channel-permissions'], category: 'admin', typing: true, - description: { - content: 'Use to mass change the channel permissions.', - usage: ['channel-perms <role_id> <perm> <state>'], - examples: ['channel-perms 783794633129197589 read_messages deny'] - }, + description: 'Use to mass change the channel permissions.', + usage: ['channel-perms <role_id> <perm> <state>'], + examples: ['channel-perms 783794633129197589 read_messages deny'], args: [ { id: 'target', - customType: util.arg.union('member', 'member'), - prompt: { - start: 'What user/role would you like to change?', - retry: '{error} Choose a valid user/role to change.' - } + description: 'The user/role to change the permissions of.', + customType: util.arg.union('member', 'role'), + readableType: 'member|role', + prompt: 'What user/role would you like to change?', + retry: '{error} Choose a valid user/role to change.', + slashType: 'MENTIONABLE' }, { id: 'permission', + description: 'The permission to change for the target user/role.', type: 'permission', - prompt: { - start: 'What permission would you like to change?', - retry: '{error} Choose a valid permission.' - } + prompt: 'What permission would you like to change?', + retry: '{error} Choose a valid permission.', + slashType: 'STRING' }, { id: 'state', + description: 'The state that the permission should be set to for the target.', customType: [ ['true', '1', 'yes', 'enable', 'allow'], ['false', '0', 'no', 'disable', 'disallow', 'deny'], ['neutral', 'remove', 'none'] ], - prompt: { - start: 'What should that permission be set to?', - retry: '{error} Set the state to either `enable`, `disable`, or `remove`.' - } + readableType: "'enable'|'disable'|'remove'", + prompt: 'What should that permission be set to?', + retry: '{error} Set the state to either `enable`, `disable`, or `remove`.', + slashType: 'STRING', + choices: [ + { name: 'Enabled', value: 'true' }, + { name: 'Disabled', value: 'false' }, + { name: 'Neutral', value: 'neutral' } + ] } ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_CHANNELS']), userPermissions: ['ADMINISTRATOR'], channel: 'guild', - slash: true, - slashOptions: [ - { - name: 'target', - description: 'What user/role would you like to change?', - type: 'MENTIONABLE', - required: true - }, - { - name: 'permission', - description: 'What permission would you like to change?', - type: 'STRING', - required: true - }, - { - name: 'state', - description: 'What should that permission be set to?', - type: 'STRING', - choices: [ - { - name: 'Enabled', - value: 'true' - }, - { - name: 'Disabled', - value: 'false' - }, - { - name: 'Neutral', - value: 'neutral' - } - ], - required: true - } - ] + slash: true }); } diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts index 3c6b629..585e6cc 100644 --- a/src/commands/admin/roleAll.ts +++ b/src/commands/admin/roleAll.ts @@ -6,46 +6,34 @@ export default class RoleAllCommand extends BushCommand { super('roleAll', { aliases: ['role-all', 'rall'], category: 'admin', - description: { - content: 'Give a role to every member on the server.', - usage: ['role-all <role> [--bots]'], - examples: ['role-all 783794633129197589 --bots'] - }, + description: 'Give a role to every member on the server.', + usage: ['role-all <role> [--bots]'], + examples: ['role-all 783794633129197589 --bots'], args: [ { id: 'role', + description: 'The role to assigned to every member on the server.', type: 'role', - prompt: { - start: 'What role would you like to give to every member on the server?', - retry: '{error} Pick a valid role.' - } + prompt: 'What role would you like to give to every member on the server?', + retry: '{error} Pick a valid role.', + slashType: 'ROLE' }, { id: 'bots', + description: 'Also give the role to bots.', match: 'flag', + prompt: 'Would you like to also give roles to bots?', flag: '--bots', - default: false + default: false, + slashType: 'BOOLEAN', + optional: true } ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES']), userPermissions: ['ADMINISTRATOR'], typing: true, - slash: true, - slashOptions: [ - { - name: 'role', - description: 'What role would you like to give to every member on the server?', - type: 'ROLE', - required: true - }, - { - name: 'bots', - description: 'Would you like to also give roles to bots?', - type: 'BOOLEAN', - required: false - } - ] + slash: true }); } diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts index f0173a4..8bb778c 100644 --- a/src/commands/config/blacklist.ts +++ b/src/commands/config/blacklist.ts @@ -6,45 +6,43 @@ export default class BlacklistCommand extends BushCommand { super('blacklist', { aliases: ['blacklist', 'unblacklist'], category: 'config', - description: { - content: 'A command to blacklist users and channels.', - usage: ['blacklist|unblacklist <user|channel>'], - examples: ['blacklist @user', 'unblacklist #channel'] - }, + description: 'A command to blacklist users and channels.', + usage: ['blacklist|unblacklist <user|channel>'], + examples: ['blacklist @user', 'unblacklist #channel'], args: [ { + id: 'action', + description: 'Whether to blacklist or unblacklist the target.', + readableType: "'blacklist'|'unblacklist'", + prompt: 'Would you like to add or remove someone or something from/to the blacklist?', + slashType: 'STRING', + choices: [ + { name: 'blacklist', value: 'blacklist' }, + { name: 'unblacklist', value: 'unblacklist' } + ], + only: 'slash' + }, + { id: 'target', + description: 'The channel/user to blacklist.', customType: util.arg.union('channel', 'user'), - prompt: { - start: 'What channel or user that you would like to blacklist/unblacklist?', - retry: '{error} Pick a valid user or channel.' - } + readableType: 'channel|user', + prompt: 'What channel or user that you would like to blacklist/unblacklist?', + retry: '{error} Pick a valid user or channel.', + slashType: 'STRING' }, { id: 'global', + description: 'Blacklist the target globally.', match: 'flag', - flag: '--global' + flag: '--global', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'action', - description: 'Would you like to add or remove someone or something from/to the blacklist?', - type: 'STRING', - choices: [ - { name: 'blacklist', value: 'blacklist' }, - { name: 'unblacklist', value: 'unblacklist' } - ], - required: true - }, - { - name: 'target', - description: 'What channel or user that you would like to blacklist/unblacklist?', - type: 'STRING', - required: true - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: ['MANAGE_GUILD'] @@ -82,14 +80,14 @@ export default class BlacklistCommand extends BushCommand { .catch(() => false); if (!success) return await message.util.reply({ - content: `${util.emojis.error} There was an error globally ${action}ing ${util.format.bold( + content: `${util.emojis.error} There was an error globally ${action}ing ${util.format.input( target?.tag ?? target.name )}.`, allowedMentions: AllowedMentions.none() }); else return await message.util.reply({ - content: `${util.emojis.success} Successfully ${action}ed ${util.format.bold(target?.tag ?? target.name)} globally.`, + content: `${util.emojis.success} Successfully ${action}ed ${util.format.input(target?.tag ?? target.name)} globally.`, allowedMentions: AllowedMentions.none() }); // guild disable @@ -110,12 +108,12 @@ export default class BlacklistCommand extends BushCommand { .catch(() => false); if (!success) return await message.util.reply({ - content: `${util.emojis.error} There was an error ${action}ing ${util.format.bold(target?.tag ?? target.name)}.`, + content: `${util.emojis.error} There was an error ${action}ing ${util.format.input(target?.tag ?? target.name)}.`, allowedMentions: AllowedMentions.none() }); else return await message.util.reply({ - content: `${util.emojis.success} Successfully ${action}ed ${util.format.bold(target?.tag ?? target.name)}.`, + content: `${util.emojis.success} Successfully ${action}ed ${util.format.input(target?.tag ?? target.name)}.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts index 2ce4246..6af5895 100644 --- a/src/commands/config/config.ts +++ b/src/commands/config/config.ts @@ -22,15 +22,11 @@ export default class SettingsCommand extends BushCommand { super('config', { aliases: ['config', 'settings', 'setting', 'configure'], category: 'config', - description: { - content: 'Configure server settings.', - usage: [ - `settings (${settingsArr.map((s) => `\`${s}\``).join(', ')}) (${['view', 'set', 'add', 'remove'].map( - (s) => `\`${s}\`` - )})` - ], - examples: ['settings', 'config prefix set -'] - }, + description: 'Configure server settings.', + usage: [ + `settings (${settingsArr.map((s) => `\`${s}\``).join(', ')}) (${['view', 'set', 'add', 'remove'].map((s) => `\`${s}\``)})` + ], + examples: ['settings', 'config prefix set -'], slash: true, slashOptions: settingsArr.map((setting) => { return { diff --git a/src/commands/config/customAutomodPhrases.ts b/src/commands/config/customAutomodPhrases.ts index 9fbebf5..cf37595 100644 --- a/src/commands/config/customAutomodPhrases.ts +++ b/src/commands/config/customAutomodPhrases.ts @@ -5,11 +5,9 @@ // super('customAutomodPhrases', { // aliases: ['custom-automod-phrases'], // category: 'config', -// description: { -// content: 'Configure additional phrases to be used for automod.', -// usage: ['custom-automod-phrases <requiredArg> [optionalArg]'], -// examples: ['custom-automod-phrases 1 2'] -// }, +// description: 'Configure additional phrases to be used for automod.', +// usage: ['custom-automod-phrases <requiredArg> [optionalArg]'], +// examples: ['custom-automod-phrases 1 2'], // args: [ // { // id: 'required_argument', diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts index 83b4608..333ae19 100644 --- a/src/commands/config/disable.ts +++ b/src/commands/config/disable.ts @@ -5,52 +5,40 @@ export default class DisableCommand extends BushCommand { super('disable', { aliases: ['disable', 'enable'], category: 'config', - description: { - content: 'A command to disable and enable commands.', - usage: ['disable|enable <command>'], - examples: ['enable ban', 'disable kick'] - }, + description: 'A command to disable and enable commands.', + usage: ['disable|enable <command>'], + examples: ['enable ban', 'disable kick'], args: [ { + id: 'action', + description: 'Whether to disable or enable the command.', + readableType: "'blacklist'|'unblacklist", + prompt: 'Would you like to disable or enable a command?', + slashType: 'STRING', + choices: ['blacklist', 'unblacklist'].map((v) => ({ name: v, value: v })), + only: 'slash' + }, + { id: 'command', + description: 'The command to disable/enable.', customType: util.arg.union('commandAlias', 'command'), - prompt: { - start: 'What command would you like to enable/disable?', - retry: '{error} Pick a valid command.', - optional: false - } + readableType: 'command|commandAlias', + prompt: 'What command would you like to enable/disable?', + retry: '{error} Pick a valid command.', + slashType: 'STRING' }, { id: 'global', + description: 'Disable the command globally.', match: 'flag', - flag: '--global' + flag: '--global', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'action', - description: 'Would you like to disable or enable a command?', - type: 'STRING', - choices: [ - { - name: 'enable', - value: 'enable' - }, - { - name: 'disable', - value: 'disable' - } - ], - required: true - }, - { - name: 'command', - description: 'What command would you like to enable/disable?', - type: 'STRING', - required: true - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: ['MANAGE_GUILD'] @@ -67,57 +55,31 @@ export default class DisableCommand extends BushCommand { const global = args.global && message.author.isOwner(); const commandID = (args.command as BushCommand).id; - if (global) { - if (action === 'toggle') { - const disabledCommands = ( - (await Global.findByPk(client.config.environment)) ?? (await Global.create({ environment: client.config.environment })) - ).disabledCommands; - action = disabledCommands.includes(commandID) ? 'disable' : 'enable'; - } - const success = await util - .insertOrRemoveFromGlobal(action === 'disable' ? 'remove' : 'add', 'disabledCommands', commandID) - .catch(() => false); - if (!success) - return await message.util.reply({ - content: `${util.emojis.error} There was an error globally **${action.substring( - 0, - action.length - 2 - )}ing** the **${commandID}** command.`, - allowedMentions: AllowedMentions.none() - }); - else - return await message.util.reply({ - content: `${util.emojis.success} Successfully **${action.substring( - 0, - action.length - 2 - )}ed** the **${commandID}** command globally.`, - allowedMentions: AllowedMentions.none() - }); + const disabledCommands = global + ? ((await Global.findByPk(client.config.environment)) ?? (await Global.create({ environment: client.config.environment }))) + .disabledCommands + : await message.guild!.getSetting('disabledCommands'); - // guild disable - } else { - const disabledCommands = await message.guild!.getSetting('disabledCommands'); - if (action === 'toggle') { - action = disabledCommands.includes(commandID) ? 'disable' : 'enable'; - } - const newValue = util.addOrRemoveFromArray(action === 'disable' ? 'remove' : 'add', disabledCommands, commandID); - const success = await message.guild!.setSetting('disabledCommands', newValue, message.member!).catch(() => false); - if (!success) - return await message.util.reply({ - content: `${util.emojis.error} There was an error **${action.substr( - 0, - action.length - 2 - )}ing** the **${commandID}** command.`, - allowedMentions: AllowedMentions.none() - }); - else - return await message.util.reply({ - content: `${util.emojis.success} Successfully **${action.substr( - 0, - action.length - 2 - )}ed** the **${commandID}** command.`, - allowedMentions: AllowedMentions.none() - }); - } + if (action === 'toggle') action = disabledCommands.includes(commandID) ? 'disable' : 'enable'; + const newValue = util.addOrRemoveFromArray(action === 'disable' ? 'remove' : 'add', disabledCommands, commandID); + const success = global + ? await util.setGlobal('disabledCommands', newValue).catch(() => false) + : await message.guild!.setSetting('disabledCommands', newValue, message.member!).catch(() => false); + if (!success) + return await message.util.reply({ + content: `${util.emojis.error} There was an error${global ? ' globally' : ''} **${action.substring( + 0, + action.length - 2 + )}ing** the **${commandID}** command.`, + allowedMentions: AllowedMentions.none() + }); + else + return await message.util.reply({ + content: `${util.emojis.success} Successfully **${action.substring( + 0, + action.length - 2 + )}ed** the **${commandID}** command${global ? ' globally' : ''}.`, + allowedMentions: AllowedMentions.none() + }); } } diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts index 095d985..894c90a 100644 --- a/src/commands/config/features.ts +++ b/src/commands/config/features.ts @@ -13,11 +13,9 @@ export default class FeaturesCommand extends BushCommand { super('features', { aliases: ['features'], category: 'config', - description: { - content: 'Toggle features the server.', - usage: ['features'], - examples: ['features'] - }, + description: 'Toggle features the server.', + usage: ['features'], + examples: ['features'], slash: true, channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), diff --git a/src/commands/config/levelRoles.ts b/src/commands/config/levelRoles.ts index e7b4505..7f99580 100644 --- a/src/commands/config/levelRoles.ts +++ b/src/commands/config/levelRoles.ts @@ -5,11 +5,9 @@ // super('levelRole', { // aliases: ['level-role', 'level-roles', 'lr'], // category: 'config', -// description: { -// content: 'Configure roles to be assigned to users upon reaching certain levels.', -// usage: ['level-role add <level> <role>', 'level-role remove <level>'], -// examples: ['level-role 1 2'] -// }, +// description: 'Configure roles to be assigned to users upon reaching certain levels.', +// usage: ['level-role add <level> <role>', 'level-role remove <level>'], +// examples: ['level-role 1 2'], // args: [ // { // id: 'action', @@ -18,20 +16,16 @@ // { // id: 'role', // type: 'role', -// prompt: { -// start: 'What would you like to set your first argument to be?', -// retry: '{error} Pick a valid argument.', -// optional: false -// } +// prompt: 'What would you like to set your first argument to be?', +// retry: '{error} Pick a valid argument.', +// optional: false // }, // { // id: 'level', // type: 'integer', -// prompt: { -// start: 'What would you like to set your second argument to be?', -// retry: '{error} Pick a valid argument.', -// optional: false -// } +// prompt: 'What would you like to set your second argument to be?', +// retry: '{error} Pick a valid argument.', +// optional: false // } // ], // slash: true, diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts index cccef8c..6121ad7 100644 --- a/src/commands/config/log.ts +++ b/src/commands/config/log.ts @@ -7,25 +7,26 @@ export default class LogCommand extends BushCommand { super('log', { aliases: ['log', 'logging'], category: 'config', - description: { - content: 'Set or remove a log channel.', - usage: ['log <logType> [channel]'], - examples: ['log automod #automod-logs'] - }, + description: 'Set or remove a log channel.', + usage: ['log <logType> [channel]'], + examples: ['log automod #automod-logs'], slash: true, - slashOptions: [ + args: [ { - name: 'log_type', - description: 'What log type would you like to change?', - type: 'STRING', - required: true, - choices: guildLogsArr.map((log) => ({ name: log, value: log })) + id: 'log_type', + description: 'The log type to change.', + prompt: 'What log type would you like to change?', + slashType: 'STRING', + choices: guildLogsArr.map((log) => ({ name: log, value: log })), + only: 'slash' }, { - name: 'channel', - description: 'What channel would you like these logs to be sent in?', - type: 'CHANNEL', - required: false + id: 'channel', + description: 'The channel to have logs of the seleted type to be sent in.', + type: 'channel', + prompt: 'What channel would you like these logs to be sent in?', + slashType: 'CHANNEL', + channelTypes: ['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_NEWS_THREAD', 'GUILD_PUBLIC_THREAD', 'GUILD_PRIVATE_THREAD'] } ], channel: 'guild', @@ -36,7 +37,7 @@ export default class LogCommand extends BushCommand { override *args(): IterableIterator<ArgumentOptions | Flag> { const log_type = yield { - id: 'log', + id: 'log_type', type: guildLogsArr, prompt: { start: 'What log type would you like to change?', diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts index fe685ff..5bb29c8 100644 --- a/src/commands/dev/__template.ts +++ b/src/commands/dev/__template.ts @@ -5,46 +5,29 @@ export default class TemplateCommand extends BushCommand { super('template', { aliases: ['template'], category: 'template', - description: { - content: 'Command description.', - usage: ['template <requiredArg> [optionalArg]'], - examples: ['template 1 2'] - }, + description: 'Command description.', + usage: ['template <requiredArg> [optionalArg]'], + examples: ['template 1 2'], args: [ { id: 'required_argument', type: 'string', - prompt: { - start: 'What would you like to set your first argument to be?', - retry: '{error} Pick a valid argument.', - optional: false - } + description: 'This is the first argument.', + prompt: 'What would you like to set your first argument to be?', + retry: '{error} Pick a valid argument.', + slashType: 'STRING' }, { id: 'optional_argument', type: 'string', - prompt: { - start: 'What would you like to set your second argument to be?', - retry: '{error} Pick a valid argument.', - optional: true - } + description: 'This is the second argument.', + prompt: 'What would you like to set your second argument to be?', + retry: '{error} Pick a valid argument.', + optional: true, + slashType: 'STRING' } ], slash: false, //set this to true - slashOptions: [ - { - name: 'required_argument', - description: 'What would you like to set your first argument to be?', - type: 'STRING', - required: true - }, - { - name: 'optional_argument', - description: 'What would you like to set your second argument to be?', - type: 'STRING', - required: false - } - ], superUserOnly: true, ownerOnly: true, channel: 'guild', diff --git a/src/commands/dev/dm.ts b/src/commands/dev/dm.ts new file mode 100644 index 0000000..5031dfd --- /dev/null +++ b/src/commands/dev/dm.ts @@ -0,0 +1,46 @@ +import { BushCommand, BushUser, type BushMessage, type BushSlashMessage } from '#lib'; + +export default class DMCommand extends BushCommand { + public constructor() { + super('dm', { + aliases: ['dm'], + category: 'dev', + description: 'Send a direct message to a specified user from the bot.', + usage: ['dm <user> <content>'], + examples: ['dm IRONM00N hi'], + args: [ + { + id: 'user', + type: 'string', + description: 'The user to send the dm to.', + prompt: 'Who would you like to dm?', + retry: '{error} Pick a valid user to send a dm to.', + slashType: 'STRING' + }, + { + id: 'content', + type: 'string', + description: 'This is the second argument.', + prompt: 'What would you like to set your second argument to be?', + retry: '{error} Pick a valid argument.', + optional: true, + slashType: 'STRING' + } + ], + slash: false, + ownerOnly: true, + hidden: true, + clientPermissions: (m) => util.clientSendAndPermCheck(m), + userPermissions: [] + }); + } + public override async exec(message: BushMessage | BushSlashMessage, args: { user: BushUser; content: string }) { + try { + const u = await client.users.fetch(args.user.id); + await u.send(args.content); + } catch (e) { + return message.util.reply(`${util.emojis.error} There was an error sending ${util.format.input(args.user.tag)} a dm.`); + } + return message.util.reply(`${util.emojis.success} Successfully sent ${util.format.input(args.user.tag)} a dm.`); + } +} diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index b82534c..5b30d96 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -11,42 +11,95 @@ export default class EvalCommand extends BushCommand { super('eval', { aliases: ['eval', 'ev', 'evaluate'], category: 'dev', - description: { - content: 'Evaluate code.', - usage: ['eval <code> [--depth #] [--sudo] [--silent] [--delete] [--proto] [--hidden] [--ts]'], - examples: ['eval message.channel.delete()'] - }, + description: 'Evaluate code.', + usage: ['eval <code> [--depth #] [--sudo] [--silent] [--delete] [--proto] [--hidden] [--ts]'], + examples: ['eval message.channel.delete()'], args: [ - { id: 'sel_depth', match: 'option', type: 'integer', flag: '--depth', default: 0 }, - { id: 'sudo', match: 'flag', flag: '--sudo' }, - { id: 'delete_msg', match: 'flag', flag: '--delete' }, - { id: 'silent', match: 'flag', flag: '--silent' }, - { id: 'typescript', match: 'flag', flag: '--ts' }, - { id: 'hidden', match: 'flag', flag: '--hidden' }, - { id: 'show_proto', match: 'flag', flag: '--proto' }, + { + id: 'sel_depth', + description: 'How deep to inspect the output.', + match: 'option', + type: 'integer', + flag: '--depth', + default: 0, + prompt: 'How deep would you like to inspect the output?', + slashType: 'INTEGER', + optional: true + }, + { + id: 'sudo', + description: 'Whether or not to override checks.', + match: 'flag', + flag: '--sudo', + prompt: 'Would you like to override checks?', + slashType: 'BOOLEAN', + optional: true + }, + { + id: 'delete_msg', + description: 'Whether or not to delete the message that invoked the command.', + match: 'flag', + flag: '--delete', + prompt: 'Would you like to delete the message that invoked the command?', + slashType: false, + optional: true, + only: 'text' + }, + { + id: 'silent', + description: 'Whether or not to make the response silent', + match: 'flag', + flag: '--silent', + prompt: 'Would you like to make the response silent?', + slashType: 'BOOLEAN', + optional: true + }, + { + id: 'typescript', + description: 'Whether or not to treat the code as typescript and transpile it.', + match: 'flag', + flag: '--ts', + prompt: 'Is this code written in typescript?', + slashType: 'BOOLEAN', + optional: true + }, + { + id: 'hidden', + description: 'Whether or not to show hidden items.', + match: 'flag', + flag: '--hidden', + prompt: 'Would you like to show hidden items?', + slashType: 'BOOLEAN', + optional: true + }, + { + id: 'show_proto', + description: 'Whether or not to show the prototype of the output.', + match: 'flag', + flag: '--proto', + prompt: 'Would you like to show the prototype of the output?', + slashType: 'BOOLEAN', + optional: true + }, { id: 'show_methods', + description: 'Whether or not to inspect the prototype chain for methods.', match: 'flag', - flag: ['--func', '--function', '--functions', '--meth', '--method', '--methods'] + flag: ['--func', '--function', '--functions', '--meth', '--method', '--methods'], + prompt: 'Would you like to inspect the prototype chain to find methods?', + slashType: 'BOOLEAN', + optional: true }, { id: 'code', + description: 'The code you would like to evaluate.', match: 'rest', - type: 'string', - prompt: { start: 'What would you like to eval?', retry: '{error} Invalid code to eval.' } + prompt: 'What would you like to eval?', + retry: '{error} Invalid code to eval.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { name: 'code', description: 'The code you would like to evaluate.', type: 'STRING', required: true }, - { name: 'sel_depth', description: 'How deep to display the output.', type: 'INTEGER', required: false }, - { name: 'sudo', description: 'Whether or not to override checks.', type: 'BOOLEAN', required: false }, - { name: 'silent', description: 'Whether or not to make the response silent', type: 'BOOLEAN', required: false }, - { name: 'typescript', description: 'Whether or not the code is typescript.', type: 'BOOLEAN', required: false }, - { name: 'hidden', description: 'Whether or not to show hidden items.', type: 'BOOLEAN', required: false }, - { name: 'show_proto', description: 'Show prototype.', type: 'BOOLEAN', required: false }, - { name: 'show_methods', description: 'Show class functions.', type: 'BOOLEAN', required: false } - ], ownerOnly: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] diff --git a/src/commands/dev/javascript.ts b/src/commands/dev/javascript.ts index 8a47b23..5173cd1 100644 --- a/src/commands/dev/javascript.ts +++ b/src/commands/dev/javascript.ts @@ -7,25 +7,31 @@ export default class JavascriptCommand extends BushCommand { super('javascript', { aliases: ['javascript', 'js'], category: 'dev', - description: { - content: 'Evaluate code in a sand boxed environment.', - usage: ['javascript <code> [--depth #]'], - examples: ['javascript 9+10'] - }, + description: 'Evaluate code in a sand boxed environment.', + usage: ['javascript <code> [--depth #]'], + examples: ['javascript 9+10'], args: [ - { id: 'sel_depth', match: 'option', type: 'integer', flag: '--depth', default: 0 }, + { + id: 'sel_depth', + description: 'How deep to inspect the output.', + match: 'option', + type: 'integer', + flag: '--depth', + default: 0, + prompt: 'How deep would you like to inspect the output?', + slashType: 'INTEGER', + optional: true + }, { id: 'code', + description: 'The code you would like to run in a sand boxed environment.', match: 'rest', - type: 'string', - prompt: { start: 'What would you like to eval?', retry: '{error} Invalid code to eval.' } + prompt: 'What code would you like to run in a sand boxed environment?', + retry: '{error} Invalid code to run in a sand boxed environment.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { name: 'code', description: 'The code you would like to evaluate.', type: 'STRING', required: true }, - { name: 'sel_depth', description: 'How deep to display the output.', type: 'INTEGER', required: false } - ], superUserOnly: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts index f9b1867..6d030b7 100644 --- a/src/commands/dev/reload.ts +++ b/src/commands/dev/reload.ts @@ -5,29 +5,23 @@ export default class ReloadCommand extends BushCommand { super('reload', { aliases: ['reload'], category: 'dev', - description: { - content: 'Reloads the bot', - usage: ['reload'], - examples: ['reload'] - }, + description: 'Reloads the bot', + usage: ['reload'], + examples: ['reload'], // args: [ // { // id: 'fast', + // description: 'Whether or not to use esbuild for fast compiling.', // match: 'flag', - // flag: '--fast' + // flag: '--fast', + // prompt: 'Would you like to use esbuild for fast compiling?', + // optional: true, + // slashType:'BOOLEAN' // } // ], ownerOnly: true, typing: true, slash: true, - // slashOptions: [ - // { - // name: 'fast', - // description: 'Whether to use esbuild for fast compiling or not', - // type: 'BOOLEAN', - // required: false - // } - // ], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/commands/dev/say.ts b/src/commands/dev/say.ts index 400a132..80cbbe2 100644 --- a/src/commands/dev/say.ts +++ b/src/commands/dev/say.ts @@ -5,20 +5,20 @@ export default class SayCommand extends BushCommand { super('say', { aliases: ['say'], category: 'dev', - description: { - content: 'A command make the bot say something.', - usage: ['say <message>'], - examples: ['say hello'] - }, + description: 'A command make the bot say something.', + usage: ['say <content>'], + examples: ['say hello'], args: [ { id: 'content', + description: 'The content of the message to send.', type: 'string', match: 'rest', - prompt: { start: 'What would you like the bot to say?', retry: '{error} Choose something valid to say.' } + prompt: 'What would you like the bot to say?', + retry: '{error} Choose something for the bot to send.', + slashType: 'STRING' } ], - slashOptions: [{ name: 'content', description: 'What would you like the bot to say?', type: 'STRING' }], ownerOnly: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts index e8a2fe4..308d1db 100644 --- a/src/commands/dev/servers.ts +++ b/src/commands/dev/servers.ts @@ -6,11 +6,9 @@ export default class ServersCommand extends BushCommand { super('servers', { aliases: ['servers', 'guilds'], category: 'dev', - description: { - content: 'Displays all the severs the bot is in', - usage: ['servers'], - examples: ['servers'] - }, + description: 'Displays all the severs the bot is in', + usage: ['servers'], + examples: ['servers'], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], ownerOnly: true @@ -25,7 +23,7 @@ export default class ServersCommand extends BushCommand { title: `Server List [\`${guilds.length.toLocaleString()}\`]`, color: util.colors.default, fields: chunk.map((guild) => ({ - name: util.format.bold(guild.name), + name: util.format.input(guild.name), value: [ `**ID:** ${guild.id}`, `**Owner:** ${client.users.cache.has(guild.ownerId) ? client.users.cache.get(guild.ownerId)!.tag : guild.ownerId}`, diff --git a/src/commands/dev/sh.ts b/src/commands/dev/sh.ts index 83432f4..5ef72b1 100644 --- a/src/commands/dev/sh.ts +++ b/src/commands/dev/sh.ts @@ -17,20 +17,18 @@ export default class ShCommand extends BushCommand { super('sh', { aliases: ['sh', 'shell', 'cmd'], category: 'dev', - description: { - content: 'Run shell commands.', - usage: ['sh <command>'], - examples: ['sh git pull'] - }, + description: 'Run shell commands.', + usage: ['sh <command>'], + examples: ['sh git pull'], args: [ { id: 'command', + description: 'The content you would like to run as a shell command.', type: 'string', match: 'rest', - prompt: { - start: 'What would you like run', - retry: '{error} Invalid command to run.' - } + prompt: 'What would you like run', + retry: '{error} Invalid command to run.', + slashType: 'STRING' } ], ownerOnly: true, diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index 17d81ac..e0328db 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -7,14 +7,27 @@ export default class SuperUserCommand extends BushCommand { super('superuser', { aliases: ['superuser', 'su'], category: 'dev', - description: { - content: 'A command to manage superusers.', - usage: ['superuser <add/remove> <user>'], - examples: ['superuser add IRONM00N'] - }, + description: 'A command to manage superusers.', + usage: ['superuser <add/remove> <user>'], + examples: ['superuser add IRONM00N'], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], - ownerOnly: true + ownerOnly: true, + helpArgs: [ + { + id: 'action', + description: 'Whether to add or remove a user from the superuser list.', + readableType: 'add|remove', + slashType: false + }, + { + id: 'user', + description: 'The user to add/remove from the superuser list.', + type: 'user', + match: 'restContent', + slashType: false + } + ] }); } diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts index 83dd592..f3ac627 100644 --- a/src/commands/dev/test.ts +++ b/src/commands/dev/test.ts @@ -13,21 +13,18 @@ export default class TestCommand extends BushCommand { super('test', { aliases: ['test'], category: 'dev', - description: { - content: 'A command to stuff.', - usage: ['test [feature]'], - examples: ['test lots of buttons', 'test buttons'] - }, + description: 'A command to test stuff.', + usage: ['test [feature]'], + examples: ['test lots of buttons', 'test buttons'], args: [ { id: 'feature', - type: 'string', + description: 'The feature to test.', match: 'rest', - prompt: { - start: 'start prompt', - retry: 'retry prompt', - optional: true - } + prompt: 'start prompt', + retry: 'retry prompt', + optional: true, + slashType: false } ], superUserOnly: true, diff --git a/src/commands/fun/coinflip.ts b/src/commands/fun/coinflip.ts index 83de5ba..cd436f8 100644 --- a/src/commands/fun/coinflip.ts +++ b/src/commands/fun/coinflip.ts @@ -5,11 +5,9 @@ export default class CoinFlipCommand extends BushCommand { super('coinflip', { aliases: ['coinflip', 'cf'], category: 'fun', - description: { - content: 'Flip a virtual coin.', - usage: ['coinflip'], - examples: ['coinflip'] - }, + description: 'Flip a virtual coin.', + usage: ['coinflip'], + examples: ['coinflip'], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], slash: true diff --git a/src/commands/fun/dice.ts b/src/commands/fun/dice.ts index 74f387e..2f96e1c 100644 --- a/src/commands/fun/dice.ts +++ b/src/commands/fun/dice.ts @@ -5,11 +5,9 @@ export default class EightBallCommand extends BushCommand { super('dice', { aliases: ['dice', 'die'], category: 'fun', - description: { - content: 'Roll virtual dice.', - usage: ['dice'], - examples: ['dice'] - }, + description: 'Roll virtual dice.', + usage: ['dice'], + examples: ['dice'], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], slash: true diff --git a/src/commands/fun/eightBall.ts b/src/commands/fun/eightBall.ts index fc662ad..d6b0b8c 100644 --- a/src/commands/fun/eightBall.ts +++ b/src/commands/fun/eightBall.ts @@ -5,31 +5,21 @@ export default class EightBallCommand extends BushCommand { super('eightBall', { aliases: ['8ball', 'eightball'], category: 'fun', - description: { - content: 'Ask questions for a randomly generated response.', - usage: ['8Ball <question>'], - examples: ['8Ball does anyone love me?'] - }, + description: 'Ask questions for a randomly generated response.', + usage: ['8Ball <question>'], + examples: ['8Ball does anyone love me?'], args: [ { id: 'question', + description: 'The question to have answered.', type: 'string', match: 'rest', - prompt: { - start: 'What question would you like answered?', - retry: '{error} Invalid question.' - } + prompt: 'What question would you like answered?', + retry: '{error} Invalid question.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'question', - description: 'What question would you like answered?', - type: 'STRING', - required: true - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/commands/fun/minesweeper.ts b/src/commands/fun/minesweeper.ts index 9b511a3..7ef1de7 100644 --- a/src/commands/fun/minesweeper.ts +++ b/src/commands/fun/minesweeper.ts @@ -6,58 +6,60 @@ export default class MinesweeperCommand extends BushCommand { super('minesweeper', { aliases: ['minesweeper'], category: 'fun', - description: { - content: 'minesweeper command.', - usage: ['minesweeper <rows> <columns> <mines> [--spaces] [--revealFirstCell]'], - examples: ['minesweeper 10 10 2'] - }, + description: 'minesweeper command.', + usage: ['minesweeper <rows> <columns> <mines> [--spaces] [--revealFirstCell]'], + examples: ['minesweeper 10 10 2'], args: [ { id: 'rows', + description: 'The number of rows to generate.', type: 'integer', - prompt: { - start: 'How many rows would you like?', - retry: '{error} Choose a valid number of rows', - optional: true - }, - default: 9 + prompt: 'How many rows would you like?', + retry: '{error} Choose a valid number of rows', + optional: true, + default: 9, + slashType: 'INTEGER' }, { id: 'columns', + description: 'The number of columns to generate.', type: 'integer', - prompt: { - start: 'How many columns would you like?', - retry: '{error} Choose a valid number of columns', - optional: true - }, - default: 9 + prompt: 'How many columns would you like?', + retry: '{error} Choose a valid number of columns', + optional: true, + default: 9, + slashType: 'INTEGER' }, { id: 'mines', + description: 'The number of mines to generate.', type: 'integer', - prompt: { - start: 'How many mines would you like?', - retry: '{error} Choose a valid number of mines', - optional: true - }, - default: 10 + prompt: 'How many mines would you like?', + retry: '{error} Choose a valid number of mines', + optional: true, + default: 10, + slashType: 'INTEGER' }, - { id: 'spaces', match: 'flag', flag: '--spaces' }, - { id: 'reveal_first_cell', match: 'flag', flag: '--revealFirstCell' } - ], - slash: true, - slashOptions: [ - { name: 'rows', description: 'How many rows would you like?', type: 'INTEGER', required: false }, - { name: 'columns', description: 'How many rows would you like?', type: 'INTEGER', required: false }, - { name: 'mines', description: 'How many rows would you like?', type: 'INTEGER', required: false }, - { name: 'spaces', description: 'Would you like there to be spaces?', type: 'BOOLEAN', required: false }, { - name: 'reveal_first_cell', - description: 'Would you like to automatically reveal the first cell?', - type: 'BOOLEAN', - required: false + id: 'spaces', + description: 'Whether or not to put a space between cells.', + match: 'flag', + flag: '--spaces', + prompt: 'Would you like there to be spaces?', + slashType: 'BOOLEAN', + optional: true + }, + { + id: 'reveal_first_cell', + description: 'Whether or not to reveal the first cell automatically.', + match: 'flag', + flag: '--revealFirstCell', + prompt: 'Would you like to automatically reveal the first cell?', + slashType: 'BOOLEAN', + optional: true } ], + slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts index e8b213f..87ea0cc 100644 --- a/src/commands/info/avatar.ts +++ b/src/commands/info/avatar.ts @@ -6,33 +6,24 @@ export default class AvatarCommand extends BushCommand { super('avatar', { aliases: ['avatar', 'av'], category: 'info', - description: { - content: "A command to get a user's avatar", - usage: ['avatar [user]'], - examples: ['avatar', 'av IRONM00N'] - }, + description: "A command to get a user's avatar", + usage: ['avatar [user]'], + examples: ['avatar', 'av IRONM00N'], args: [ { id: 'user', + description: 'The user you would like to find the avatar of.', customType: util.arg.union('member', 'globalUser'), - prompt: { - start: 'Who would you like to see the avatar of?', - retry: '{error} Choose a valid user.', - optional: true - } + readableType: 'member|user', + prompt: 'Who would you like to see the avatar of?', + retry: '{error} Choose a valid user.', + optional: true, + slashType: 'USER' } ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], - slash: true, - slashOptions: [ - { - name: 'user', - description: 'The user you would like to find the avatar of.', - type: 'USER', - required: false - } - ] + slash: true }); } diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts index 833ed96..56885c5 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -8,11 +8,9 @@ export default class BotInfoCommand extends BushCommand { super('botInfo', { aliases: ['bot-info', 'stats'], category: 'info', - description: { - content: 'Shows information about the bot', - usage: ['bot-info'], - examples: ['bot-info'] - }, + description: 'Shows information about the bot', + usage: ['bot-info'], + examples: ['bot-info'], slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] diff --git a/src/commands/info/color.ts b/src/commands/info/color.ts index 8fed7da..cb612b5 100644 --- a/src/commands/info/color.ts +++ b/src/commands/info/color.ts @@ -14,20 +14,19 @@ export default class ColorCommand extends BushCommand { super('color', { aliases: ['color'], category: 'info', - description: { - content: 'Find the color of a hex code, user, or role.', - usage: ['color <color|role|user>'], - examples: ['color #0000FF'] - }, + description: 'Find the color of a hex code, user, or role.', + usage: ['color <color|role|user>'], + examples: ['color #0000FF'], args: [ { id: 'color', + description: 'The color string, role, or member to find the color of.', customType: Argument.union(isValidTinyColor, 'role', 'member'), + readableType: 'color|role|member', match: 'restContent', - prompt: { - start: 'What color code, role, or user would you like to find the color of?', - retry: '{error} Choose a valid color, role, or member.' - } + prompt: 'What color code, role, or user would you like to find the color of?', + retry: '{error} Choose a valid color, role, or member.', + slashType: 'STRING' } ], channel: 'guild', @@ -41,16 +40,23 @@ export default class ColorCommand extends BushCommand { } public override async exec(message: BushMessage | BushSlashMessage, args: { color: string | BushRole | BushGuildMember }) { + const _color = message.util.isSlashMessage(message) + ? ((await util.arg.cast(Argument.union(isValidTinyColor, 'role', 'member'), message, args.color as string)) as + | string + | BushRole + | BushGuildMember) + : args.color; + const color = - typeof args.color === 'string' - ? tinycolor(args.color) - : args.color instanceof Role - ? tinycolor(args.color.hexColor) - : tinycolor(args.color.displayHexColor); + typeof _color === 'string' + ? tinycolor(_color) + : _color instanceof Role + ? tinycolor(_color.hexColor) + : tinycolor(_color.displayHexColor); - if (args.color instanceof Role && args.color.hexColor === '#000000') { + if (_color instanceof Role && _color.hexColor === '#000000') { return await message.util.reply({ - content: `${util.emojis.error} <@&${args.color.id}> does not have a color.`, + content: `${util.emojis.error} <@&${_color.id}> does not have a color.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index 47a8281..a38a446 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -14,31 +14,22 @@ export default class GuildInfoCommand extends BushCommand { super('guildInfo', { aliases: ['guild-info', 'serverinfo', 'guild', 'server', 'g'], category: 'info', - description: { - content: 'Get info about a server.', - usage: ['guild-info [guild]'], - examples: ['guild-info 516977525906341928'] - }, + description: 'Get info about a server.', + usage: ['guild-info [guild]'], + examples: ['guild-info 516977525906341928'], args: [ { id: 'guild', + description: 'The guild to find information about.', customType: util.arg.union('guild', 'snowflake'), - prompt: { - start: 'What server would you like to find information about?', - retry: '{error} Choose a valid server to find information about.', - optional: true - } + readableType: 'guild|snowflake', + prompt: 'What server would you like to find information about?', + retry: '{error} Choose a valid server to find information about.', + optional: true, + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'guild', - description: 'The id of the guild you would like to find information about.', - type: 'STRING', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] }); @@ -67,23 +58,15 @@ export default class GuildInfoCommand extends BushCommand { const guildStats: string[] = []; const guildSecurity: string[] = []; const verifiedGuilds = Object.values(client.consts.mappings.guilds); - if (verifiedGuilds.includes(guild.id)) emojis.push(otherEmojis.BUSH_VERIFIED); + if (verifiedGuilds.includes(guild.id as typeof verifiedGuilds[number])) emojis.push(otherEmojis.BUSH_VERIFIED); if (!isPreview && guild instanceof Guild) { if (guild.premiumTier !== 'NONE') emojis.push(otherEmojis[`BOOST_${guild.premiumTier}`]); await guild.fetch(); const channels = guild.channels.cache; - type ChannelType = - | 'GUILD_TEXT' - | 'GUILD_NEWS' - | 'GUILD_VOICE' - | 'GUILD_STAGE_VOICE' - | 'GUILD_STORE' - | 'GUILD_CATEGORY' - | 'THREAD'; const channelTypes = ( - ['GUILD_TEXT', 'GUILD_VOICE', 'GUILD_STAGE_VOICE', 'GUILD_STORE', 'GUILD_CATEGORY', 'THREAD'] as ChannelType[] + ['GUILD_TEXT', 'GUILD_VOICE', 'GUILD_STAGE_VOICE', 'GUILD_STORE', 'GUILD_CATEGORY', 'THREAD'] as const ).map((type) => `${otherEmojis[type]} ${channels.filter((channel) => channel.type.includes(type)).size.toLocaleString()}`); const guildRegions = [ @@ -113,18 +96,18 @@ export default class GuildInfoCommand extends BushCommand { if (guild.banner) guildAbout.push(`**Banner:** [link](${guild.bannerURL({ size: 4096, format: 'png' })})`); if (guild.splash) guildAbout.push(`**Splash:** [link](${guild.splashURL({ size: 4096, format: 'png' })})`); - enum EmojiTierMap { - TIER_3 = 500, - TIER_2 = 300, - TIER_1 = 100, - NONE = 50 - } - enum StickerTierMap { - TIER_3 = 60, - TIER_2 = 30, - TIER_1 = 15, - NONE = 0 - } + const EmojiTierMap = { + TIER_3: 500, + TIER_2: 300, + TIER_1: 100, + NONE: 50 + } as const; + const StickerTierMap = { + TIER_3: 60, + TIER_2: 30, + TIER_1: 15, + NONE: 0 + } as const; guildStats.push( `**Channels:** ${guild.channels.cache.size.toLocaleString()} / 500 (${channelTypes.join(', ')})`, diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index 3bf8f5e..3b7883f 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -7,33 +7,32 @@ export default class HelpCommand extends BushCommand { super('help', { aliases: ['help'], category: 'info', - description: { - content: 'Displays a list of commands, or detailed information for a specific command.', - usage: ['help [command]'], - examples: ['help prefix'] - }, + description: 'Displays a list of commands, or detailed information for a specific command.', + usage: ['help [command]'], + examples: ['help prefix'], args: [ { id: 'command', + description: 'The command to show info about.', type: 'commandAlias', match: 'content', - prompt: { - start: 'What command do you need help with?', - retry: '{error} Choose a valid command to find help for.', - optional: true - } + prompt: 'What command do you need help with?', + retry: '{error} Choose a valid command to find help for.', + slashType: 'STRING', + optional: true }, - { id: 'showHidden', match: 'flag', flag: '--hidden' } - ], - slash: true, - slashOptions: [ { - name: 'command', - description: 'What command do you need help with?', - type: 'STRING', - required: false + id: 'showHidden', + description: 'Whether ot not to show hidden commands as well.', + match: 'flag', + flag: '--hidden', + slashType: 'BOOLEAN', + ownerOnly: true, + only: 'text', + optional: true } ], + slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] }); @@ -80,17 +79,17 @@ export default class HelpCommand extends BushCommand { const embed = new MessageEmbed() .setColor(util.colors.default) .setTitle(`${command.id} Command`) - .setDescription(`${command.description?.content ?? '*This command does not have a description.*'}`); - if (command.description?.usage?.length) { + .setDescription(`${command.description ?? '*This command does not have a description.*'}`); + if (command.usage?.length) { embed.addField( - `» Usage${command.description.usage.length > 1 ? 's' : ''}`, - command.description.usage.map((u) => `\`${u}\``).join('\n') + `» Usage${command.usage.length > 1 ? 's' : ''}`, + command.usage.map((u) => `\`${u}\``).join('\n') ); } - if (command.description?.examples?.length) { + if (command.examples?.length) { embed.addField( - `» Example${command.description.examples.length > 1 ? 's' : ''}`, - command.description.examples.map((u) => `\`${u}\``).join('\n') + `» Example${command.examples.length > 1 ? 's' : ''}`, + command.examples.map((u) => `\`${u}\``).join('\n') ); } if (command.aliases?.length > 1) embed.addField('» Aliases', `\`${command.aliases.join('` `')}\``); diff --git a/src/commands/info/icon.ts b/src/commands/info/icon.ts index 2a04a65..42b7fa4 100644 --- a/src/commands/info/icon.ts +++ b/src/commands/info/icon.ts @@ -6,11 +6,9 @@ export default class IconCommand extends BushCommand { super('icon', { aliases: ['icon', 'guildavatar', 'severicon', 'guildicon'], category: 'info', - description: { - content: "A command to get the server's icon", - usage: ['icon'], - examples: ['icon'] - }, + description: "A command to get the server's icon", + usage: ['icon'], + examples: ['icon'], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], channel: 'guild', diff --git a/src/commands/info/links.ts b/src/commands/info/links.ts index 935dca0..3f82245 100644 --- a/src/commands/info/links.ts +++ b/src/commands/info/links.ts @@ -7,11 +7,9 @@ export default class LinksCommand extends BushCommand { super('links', { aliases: ['links', 'invite', 'support'], category: 'info', - description: { - content: 'Sends bot links', - usage: ['links'], - examples: ['links'] - }, + description: 'Sends bot links', + usage: ['links'], + examples: ['links'], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], slash: true diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index 1d3f75a..619971f 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -6,11 +6,9 @@ export default class PingCommand extends BushCommand { super('ping', { aliases: ['ping'], category: 'info', - description: { - content: 'Gets the latency of the bot', - usage: ['ping'], - examples: ['ping'] - }, + description: 'Gets the latency of the bot', + usage: ['ping'], + examples: ['ping'], slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index 3cf27b6..29cd2ce 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -6,32 +6,22 @@ export default class PronounsCommand extends BushCommand { super('pronouns', { aliases: ['pronouns', 'pronoun'], category: 'info', - description: { - content: 'Finds the pronouns of a user using https://pronoundb.org.', - usage: ['pronouns <user>'], - examples: ['pronouns IRONM00N'] - }, + description: 'Finds the pronouns of a user using https://pronoundb.org.', + usage: ['pronouns <user>'], + examples: ['pronouns IRONM00N'], args: [ { id: 'user', + description: 'The user to get pronouns for.', type: 'globalUser', - prompt: { - start: 'Who would you like to view the pronouns of?', - retry: '{error} Choose a valid user to view the pronouns of.', - optional: true - } + prompt: 'Who would you like to view the pronouns of?', + retry: '{error} Choose a valid user to view the pronouns of.', + optional: true, + slashType: 'USER' } ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], - slashOptions: [ - { - name: 'user', - description: 'The user to get pronouns for', - type: 'USER', - required: false - } - ], slash: true }); } diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts index deb5692..bd0924f 100644 --- a/src/commands/info/snowflake.ts +++ b/src/commands/info/snowflake.ts @@ -21,33 +21,23 @@ export default class SnowflakeCommand extends BushCommand { super('snowflake', { aliases: ['snowflake', 'info', 'sf'], category: 'info', - description: { - content: 'Provides information about the specified Snowflake.', - usage: ['snowflake <snowflake>'], - examples: ['snowflake 322862723090219008'] - }, + description: 'Provides information about the specified Snowflake.', + usage: ['snowflake <snowflake>'], + examples: ['snowflake 322862723090219008'], args: [ { id: 'snowflake', + description: 'The snowflake you would like to get information about.', type: 'snowflake', - prompt: { - start: 'Enter the snowflake you would like to get information about.', - retry: '{error} Choose a valid snowflake.', - optional: false - } + prompt: 'What snowflake would you like to get information about?', + retry: '{error} Choose a valid snowflake.', + optional: false, + slashType: 'STRING' } ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], - slash: true, - slashOptions: [ - { - name: 'snowflake', - description: 'The snowflake you would like to get information about.', - type: 'STRING', - required: true - } - ] + slash: true }); } public override async exec(message: BushMessage | BushSlashMessage, args: { snowflake: Snowflake }) { @@ -58,24 +48,26 @@ export default class SnowflakeCommand extends BushCommand { if (client.channels.cache.has(snowflake)) { const channel: Channel = client.channels.cache.get(snowflake)!; const channelInfo = [`**Type:** ${channel.type}`]; - if (['dm', 'group'].includes(channel.type)) { + if ((['DM', 'GROUP_DM'] as const).includes(channel.type)) { const _channel = channel as DMChannel; channelInfo.push(`**Recipient:** ${util.discord.escapeMarkdown(_channel.recipient.tag)} (${_channel.recipient.id})`); snowflakeEmbed.setTitle( `:snowflake: DM with ${util.discord.escapeMarkdown((channel as DMChannel).recipient.tag)} \`[Channel]\`` ); } else if ( - [ - 'GUILD_CATEGORY', - 'GUILD_NEWS', - 'GUILD_TEXT', - 'GUILD_VOICE', - 'GUILD_STORE', - 'GUILD_STAGE_VOICE', - 'GUILD_NEWS_THREAD', - 'GUILD_PUBLIC_THREAD', - 'GUILD_PRIVATE_THREAD' - ].includes(channel.type) + ( + [ + 'GUILD_CATEGORY', + 'GUILD_NEWS', + 'GUILD_TEXT', + 'GUILD_VOICE', + 'GUILD_STORE', + 'GUILD_STAGE_VOICE', + 'GUILD_NEWS_THREAD', + 'GUILD_PUBLIC_THREAD', + 'GUILD_PRIVATE_THREAD' + ] as const + ).includes(channel.type) ) { const _channel = channel as TextChannel | VoiceChannel | NewsChannel | StageChannel | CategoryChannel | StageChannel; channelInfo.push( diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index 601e044..8ed6304 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -7,31 +7,22 @@ export default class UserInfoCommand extends BushCommand { super('userInfo', { aliases: ['user-info', 'user', 'u'], category: 'info', - description: { - content: 'Gives information about a specified user.', - usage: ['user-info [user]'], - examples: ['user-info 322862723090219008'] - }, + description: 'Gives information about a specified user.', + usage: ['user-info [user]'], + examples: ['user-info 322862723090219008'], args: [ { id: 'user', + description: 'The user you would like to find information about.', customType: util.arg.union('user', 'snowflake'), - prompt: { - start: 'What user would you like to find information about?', - retry: '{error} Choose a valid user to find information about.', - optional: true - } + readableType: 'user|snowflake', + prompt: 'What user would you like to find information about?', + retry: '{error} Choose a valid user to find information about.', + optional: true, + slashType: 'USER' } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'The user you would like to find information about.', - type: 'USER', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] }); @@ -108,7 +99,7 @@ export default class UserInfoCommand extends BushCommand { if (member?.displayHexColor) serverUserInfo.push(`**Display Color:** ${member.displayHexColor}`); if (user.id == '322862723090219008' && message.guild?.id == client.consts.mappings.guilds.bush) serverUserInfo.push(`**General Deletions:** 1⅓`); - if (['384620942577369088', '496409778822709251'].includes(user.id) && message.guild?.id == client.consts.mappings.guilds.bush) + if ((['384620942577369088', '496409778822709251'] as const).includes(user.id) && message.guild?.id == client.consts.mappings.guilds.bush) serverUserInfo.push(`**General Deletions:** ⅓`); if (member?.nickname) serverUserInfo.push(`**Nickname:** ${util.discord.escapeMarkdown(member?.nickname)}`); if (serverUserInfo.length) diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index 19387f9..7e6eefd 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -6,31 +6,21 @@ export default class LeaderboardCommand extends BushCommand { super('leaderboard', { aliases: ['leaderboard', 'lb'], category: 'leveling', - description: { - content: 'Allows you to see the users with the highest levels in the server.', - usage: ['leaderboard [page]'], - examples: ['leaderboard 5'] - }, + description: 'View the users with the highest levels in the server.', + usage: ['leaderboard [page]'], + examples: ['leaderboard 5'], args: [ { id: 'page', + description: 'The page of the leaderboard to view.', type: 'integer', - prompt: { - start: 'What would you like to set your first argument to be?', - retry: '{error} Pick a valid argument.', - optional: true - } + prompt: 'What page of the leaderboard would you like to view?', + retry: '{error} Pick a valid argument.', + optional: true, + slashType: 'INTEGER', } ], slash: true, - slashOptions: [ - { - name: 'page', - description: 'What would you like to set your first argument to be?', - type: 'INTEGER', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index 36fb153..61e5d0b 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -20,31 +20,21 @@ export default class LevelCommand extends BushCommand { super('level', { aliases: ['level', 'rank', 'lvl'], category: 'leveling', - description: { - content: 'Shows the level of a user', - usage: ['level [user]'], - examples: ['level', 'level @Tyman'] - }, + description: 'Shows the level of a user', + usage: ['level [user]'], + examples: ['level', 'level @Tyman'], args: [ { id: 'user', + description: 'The user to get the level of.', type: 'user', - prompt: { - start: 'What user would you like to see the level of?', - retry: '{error} Choose a valid user to see the level of.', - optional: true - } + prompt: 'What user would you like to see the level of?', + retry: '{error} Choose a valid user to see the level of.', + optional: true, + slashType: 'USER' } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'The user to get the level of', - type: 'USER', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts index 499463a..69f562d 100644 --- a/src/commands/leveling/setLevel.ts +++ b/src/commands/leveling/setLevel.ts @@ -6,41 +6,25 @@ export default class SetLevelCommand extends BushCommand { super('setLevel', { aliases: ['set-level'], category: 'leveling', - description: { - content: 'Sets the level of a user', - usage: ['set-level <user> <level>'], - examples: ['set-level @Moulberry 69'] //nice - }, + description: 'Sets the level of a user', + usage: ['set-level <user> <level>'], + examples: ['set-level @Moulberry 69'], //nice args: [ { id: 'user', + description: 'The user to set the level of.', type: 'user', - prompt: { - start: 'What user would you like to change the level of?', - retry: '{error} Choose a valid user to change the level of.' - } + prompt: 'What user would you like to change the level of?', + retry: '{error} Choose a valid user to change the level of.', + slashType: 'USER' }, { id: 'level', + description: 'The level to set the user to.', type: 'integer', - prompt: { - start: 'What level would you like to set the user to?', - retry: '{error} Choose a valid level to set the user to.' - } - } - ], - slashOptions: [ - { - name: 'user', - description: 'What user would you like to change the level of?', - type: 'USER', - required: true - }, - { - name: 'level', - description: 'What level would you like to set the user to?', - type: 'INTEGER', - required: true + prompt: 'What level would you like to set the user to?', + retry: '{error} Choose a valid level to set the user to.', + slashType: 'INTEGER' } ], slash: true, diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts index 2f2a6fa..68be528 100644 --- a/src/commands/leveling/setXp.ts +++ b/src/commands/leveling/setXp.ts @@ -6,44 +6,26 @@ export default class SetXpCommand extends BushCommand { super('setXp', { aliases: ['set-xp'], category: 'leveling', - description: { - content: 'Sets the xp of a user', - usage: ['set-xp <user> <xp>'], - examples: ['set-xp @Moulberry 69k'] //nice - }, + description: 'Sets the xp of a user', + usage: ['set-xp <user> <xp>'], + examples: ['set-xp @Moulberry 69k'], //nice args: [ { id: 'user', + description: 'The user to set the xp of.', type: 'user', - prompt: { - start: 'What user would you like to change the xp of?', - retry: '{error} Choose a valid user to change the xp of.', - required: true - } + prompt: 'What user would you like to change the xp of?', + retry: '{error} Choose a valid user to change the xp of.', + slashType: 'USER' }, { id: 'xp', + description: 'The xp to set the user to.', type: 'abbreviatedNumber', match: 'restContent', - prompt: { - start: 'How much xp should the user have?', - retry: "{error} Choose a valid number to set the user's xp to.", - required: true - } - } - ], - slashOptions: [ - { - name: 'user', - description: 'What user would you like to change the xp of?', - type: 'USER', - required: true - }, - { - name: 'xp', - description: 'How much xp should the user have?', - type: 'INTEGER', - required: true + prompt: 'How much xp should the user have?', + retry: "{error} Choose a valid number to set the user's xp to.", + slashType: 'INTEGER' } ], slash: true, diff --git a/src/commands/moderation/_lockdown.ts b/src/commands/moderation/_lockdown.ts index 029db29..08d4011 100644 --- a/src/commands/moderation/_lockdown.ts +++ b/src/commands/moderation/_lockdown.ts @@ -5,27 +5,21 @@ export default class LockdownCommand extends BushCommand { super('lockdown', { aliases: ['lockdown', 'unlockdown'], category: 'moderation', - description: { - content: 'Allows you to lockdown a channel or all configured channels..', - usage: ['lockdown [--all]'], - examples: ['lockdown', 'lockdown --all'] - }, + description: 'Allows you to lockdown a channel or all configured channels..', + usage: ['lockdown [--all]'], + examples: ['lockdown', 'lockdown --all'], args: [ { id: 'all', + description: 'Whether or not to lock all channels', match: 'flag', - flag: '--all' + flag: '--all', + prompt: 'Would you like to lockdown all channels?', + slashType: 'BOOLEAN', + optional: true } ], slash: true, - slashOptions: [ - { - name: 'all', - description: 'Would you like to lockdown all channels?', - type: 'BOOLEAN', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], @@ -34,6 +28,7 @@ export default class LockdownCommand extends BushCommand { } public override async exec(message: BushMessage | BushSlashMessage, args: { all: boolean }) { + // todo stop being lazy return await message.util.reply('Unfortunately my developer is too lazy to implement this command.'); if (!args.all) { if (!['GUILD_TEXT', 'GUILD_NEWS'].includes(message.channel!.type)) diff --git a/src/commands/moderation/activePunishments.ts b/src/commands/moderation/activePunishments.ts new file mode 100644 index 0000000..d40f2ba --- /dev/null +++ b/src/commands/moderation/activePunishments.ts @@ -0,0 +1,76 @@ +// import { BushCommand, ModLog, ModLogModel, type BushGuildMember, type BushMessage, type BushSlashMessage } from '#lib'; +// import { FindOptions, Op } from 'sequelize'; + +// const punishmentTypes = ['ban', 'kick', 'mute', 'warn', 'role'] as const; + +// export default class ActivePunishmentsCommand extends BushCommand { +// public constructor() { +// super('active-punishments', { +// aliases: ['active-punishments', 'ap'], +// category: 'moderation', +// description: 'Gets a list of all the active punishment in the server.', +// usage: [`active-punishments [--moderator <user>] [--type <${punishmentTypes.map((v) => `'${v}'`).join('|')}>]`], +// examples: ['active-punishments'], +// args: [ +// { +// id: 'moderator', +// description: 'Only show active punishments by this moderator.', +// type: 'user', +// match: 'option', +// prompt: 'Only show active punishments from what user?', +// optional: true, +// slashType: 'USER', +// slashResolve: 'member' +// }, +// { +// id: 'type', +// description: 'Only show active punishments of this type.', +// customType: [...punishmentTypes], +// readableType: punishmentTypes.map((v) => `'${v}'`).join('|'), +// match: 'option', +// optional: true, +// slashType: 'STRING', +// choices: punishmentTypes.map((v) => ({ name: v, value: v })) +// } +// ], +// slash: true, +// channel: 'guild', +// hidden: true, +// clientPermissions: (m) => util.clientSendAndPermCheck(m), +// userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) +// }); +// } +// public override async exec( +// message: BushMessage | BushSlashMessage, +// args: { moderator?: BushGuildMember; type: typeof punishmentTypes[number] } +// ) { +// const where: FindOptions<ModLogModel>['where'] = { guild: message.guild!.id }; +// if (args.moderator?.id) where.user = args.moderator.id; +// if (args.type) { +// switch (args.type) { +// case 'ban': +// where.type = { [Op.or]: ['PERM_BAN', 'TEMP_BAN', 'UNBAN'] }; +// break; +// case 'kick': +// where.type = { [Op.or]: ['KICK'] }; +// break; +// case 'mute': +// where.type = { [Op.or]: ['PERM_MUTE', 'TEMP_MUTE', 'UNMUTE'] }; +// break; +// case 'warn': +// where.type = { [Op.or]: ['WARN'] }; +// break; +// case 'role': +// where.type = { [Op.or]: ['PERM_PUNISHMENT_ROLE', 'TEMP_PUNISHMENT_ROLE', 'REMOVE_PUNISHMENT_ROLE'] }; +// break; +// default: +// return message.util.reply(`${util.emojis.error} You supplied an invalid case type to filter by.`); +// } +// } + +// const logs = await ModLog.findAll({ +// where, +// order: [['createdAt', 'ASC']] +// }); +// } +// } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 7c0d010..3d68a97 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -6,73 +6,52 @@ export default class BanCommand extends BushCommand { super('ban', { aliases: ['ban', 'force-ban', 'dban'], category: 'moderation', - description: { - content: 'Ban a member from the server.', - usage: ['ban <member> <reason> [--delete]'], - examples: ['ban ironm00n 1 day commands in #general --delete 7'] - }, + description: 'Ban a member from the server.', + usage: ['ban <member> <reason> [--delete]'], + examples: ['ban ironm00n 1 day commands in #general --delete 7'], args: [ { id: 'user', + description: 'The user that will be banned.', customType: util.arg.union('user', 'snowflake'), - prompt: { - start: 'What user would you like to ban?', - retry: '{error} Choose a valid user to ban.' - } + prompt: 'What user would you like to ban?', + retry: '{error} Choose a valid user to ban.', + slashType: 'USER' }, { id: 'reason', + description: 'The reason and duration of the ban.', type: 'contentWithDuration', match: 'restContent', - prompt: { - start: 'Why should this user be banned and for how long?', - retry: '{error} Choose a valid ban reason and duration.', - optional: true - } + prompt: 'Why should this user be banned and for how long?', + retry: '{error} Choose a valid ban reason and duration.', + slashType: 'STRING', + optional: true }, { id: 'days', + description: 'The number of days of messages to delete when the user is banned, defaults to 0.', flag: '--days', match: 'option', - customType: util.arg.range('integer', 0, 7, true) + prompt: "How many days of the user's messages would you like to delete?", + retry: '{error} Choose between 0 and 7 days to delete messages from the user for.', + customType: util.arg.range('integer', 0, 7, true), + optional: true, + slashType: 'INTEGER', + choices: [...Array(8).keys()].map((v) => ({ name: v.toString(), value: v })) }, { id: 'force', + description: 'Override permission checks.', flag: '--force', - match: 'flag' + match: 'flag', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'What user would you like to ban?', - type: 'USER', - required: true - }, - { - name: 'reason', - description: 'Why should this user be banned and for how long?', - type: 'STRING', - required: false - }, - { - name: 'days', - description: "How many days of the user's messages would you like to delete?", - type: 'INTEGER', - required: false, - choices: [ - { name: '0', value: 0 }, - { name: '1', value: 1 }, - { name: '2', value: 2 }, - { name: '3', value: 3 }, - { name: '4', value: 4 }, - { name: '5', value: 5 }, - { name: '6', value: 6 }, - { name: '7', value: 7 } - ] - } - ], channel: 'guild', clientPermissions: ['BAN_MEMBERS'], userPermissions: ['BAN_MEMBERS'] @@ -134,7 +113,7 @@ export default class BanCommand extends BushCommand { }); const responseMessage = () => { - const victim = util.format.bold(user.tag); + const victim = util.format.input(user.tag); switch (responseCode) { case 'missing permissions': return `${util.emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`; diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index 0204d84..714a2e5 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -6,26 +6,28 @@ export default class EvidenceCommand extends BushCommand { super('evidence', { aliases: ['evidence'], category: 'moderation', - description: { - content: 'Add evidence to a modlog case.', - usage: ['evidence <case_id> <evidence>'], - examples: ['evidence '] - }, - slash: true, - slashOptions: [ + description: 'Add evidence to a modlog case.', + usage: ['evidence <case_id> <evidence>'], + examples: ['evidence 9210b1ea-91f5-4ea2-801b-02b394469c77 was spamming in #general'], + args: [ { - name: 'case_id', - description: 'What case would you like to modify the evidence of?', - type: 'STRING', - required: true + id: 'case_id', + description: 'The case to modify the evidence of.', + type: 'string', + prompt: 'What case would you like to modify the evidence of?', + slashType: 'STRING', + only: 'slash' }, { - name: 'evidence', - description: 'What would you like to modify the evidence to?', - type: 'STRING', - required: true + id: 'evidence', + description: 'The value to set the evidence to.', + type: 'string', + prompt: 'What would you like to modify the evidence to?', + slashType: 'STRING', + only: 'slash' } ], + slash: true, channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) diff --git a/src/commands/moderation/hideCase.ts b/src/commands/moderation/hideCase.ts index 9c22e63..de7b310 100644 --- a/src/commands/moderation/hideCase.ts +++ b/src/commands/moderation/hideCase.ts @@ -5,30 +5,20 @@ export default class HideCaseCommand extends BushCommand { super('hideCase', { aliases: ['hide-case', 'hide_case', 'showcase', 'show_case', 'cover-up-mod-abuse', 'cover_up_mod_abuse'], category: 'moderation', - description: { - content: 'Hide a particular modlog case from the modlog command unless the `--hidden` flag is specified', - usage: ['hide-case <case_id>'], - examples: ['hide-case 9210b1ea-91f5-4ea2-801b-02b394469c77'] - }, + description: 'Hide a particular modlog case from the modlog command unless the `--hidden` flag is specified', + usage: ['hide-case <case_id>'], + examples: ['hide-case 9210b1ea-91f5-4ea2-801b-02b394469c77'], args: [ { id: 'case_id', + description: 'The id of the case to be hidden.', type: 'string', - prompt: { - start: 'What modlog case would you like to hide?', - retry: '{error} Choose a valid case id.' - } + prompt: 'What modlog case would you like to hide?', + retry: '{error} Choose a valid case id.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'case_id', - description: 'What modlog case would you like to hide?', - type: 'STRING', - required: true - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']), channel: 'guild' diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 1f1c2fb..9463154 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -5,51 +5,40 @@ export default class KickCommand extends BushCommand { super('kick', { aliases: ['kick'], category: 'moderation', - description: { - content: 'Kick a user.', - usage: ['kick <member> <reason>'], - examples: ['kick @user bad'] - }, + description: 'Kick a user.', + usage: ['kick <member> <reason>'], + examples: ['kick @user bad'], args: [ { id: 'user', + description: 'The user to kick.', type: 'user', - prompt: { - start: 'What user would you like to kick?', - retry: '{error} Choose a valid user to kick.' - } + prompt: 'What user would you like to kick?', + retry: '{error} Choose a valid user to kick.', + slashType: 'USER' }, { id: 'reason', + description: 'The reason for the kick.', type: 'string', match: 'rest', - prompt: { - start: 'Why should this user be kicked?', - retry: '{error} Choose a valid kick reason.', - optional: true - } + prompt: 'Why should this user be kicked?', + retry: '{error} Choose a valid kick reason.', + optional: true, + slashType: 'STRING' }, { id: 'force', + description: 'Override permission checks.', flag: '--force', - match: 'flag' + match: 'flag', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'What user would you like to kick?', - type: 'USER', - required: true - }, - { - name: 'reason', - description: 'Why should this user be kicked?', - type: 'STRING', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['KICK_MEMBERS']), userPermissions: ['KICK_MEMBERS'] }); @@ -77,7 +66,7 @@ export default class KickCommand extends BushCommand { }); const responseMessage = () => { - const victim = util.format.bold(member.user.tag); + const victim = util.format.input(member.user.tag); switch (responseCode) { case 'missing permissions': return `${util.emojis.error} Could not kick ${victim} because I am missing the \`Kick Members\` permission.`; diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 8bdee59..474eaa9 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -6,48 +6,36 @@ export default class ModlogCommand extends BushCommand { super('modlog', { aliases: ['modlog', 'modlogs'], category: 'moderation', - description: { - content: "View a user's modlogs, or view a specific case.", - usage: ['modlogs <search> [--hidden]'], - examples: ['modlogs @Tyman'] - }, + description: "View a user's modlogs, or view a specific case.", + usage: ['modlogs <search> [--hidden]'], + examples: ['modlogs @Tyman'], args: [ { id: 'search', + description: 'The case id or user to search for modlogs by.', customType: util.arg.union('user', 'string'), - prompt: { - start: 'What case id or user would you like to see?', - retry: '{error} Choose a valid case id or user.' - } + prompt: 'What case id or user would you like to see?', + retry: '{error} Choose a valid case id or user.', + slashType: 'STRING' }, { id: 'hidden', + description: 'Show hidden modlogs.', + prompt: 'Would you like to see hidden modlogs?', match: 'flag', flag: ['--hidden', '-h'], - default: false + default: false, + optional: true, + slashType: 'BOOLEAN' } ], - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']), slash: true, - slashOptions: [ - { - name: 'search', - description: 'What case id or user would you like to see?', - type: 'STRING', - required: true - }, - { - name: 'hidden', - description: 'Would you like to see hidden modlogs?', - type: 'BOOLEAN', - required: false - } - ] + clientPermissions: (m) => util.clientSendAndPermCheck(m), + userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) }); } - #generateModlogInfo(log: ModLog, showUser: boolean): string { + static generateModlogInfo(log: ModLog, showUser: boolean): string { const trim = (str: string): string => (str.endsWith('\n') ? str.substring(0, str.length - 1).trim() : str.trim()); const modLog = [`**Case ID**: ${util.discord.escapeMarkdown(log.id)}`, `**Type**: ${log.type.toLowerCase()}`]; if (showUser) modLog.push(`**User**: <@!${log.user}>`); @@ -75,7 +63,7 @@ export default class ModlogCommand extends BushCommand { const niceLogs = logs .filter((log) => !log.pseudo) .filter((log) => !(log.hidden && hidden)) - .map((log) => this.#generateModlogInfo(log, false)); + .map((log) => ModlogCommand.generateModlogInfo(log, false)); if (!logs.length || !niceLogs.length) return message.util.reply(`${util.emojis.error} **${foundUser.tag}** does not have any modlogs.`); const chunked: string[][] = util.chunk(niceLogs, 4); @@ -96,7 +84,7 @@ export default class ModlogCommand extends BushCommand { return message.util.reply(`${util.emojis.error} This modlog is from another server.`); const embed = { title: `Case ${entry.id}`, - description: this.#generateModlogInfo(entry, true), + description: ModlogCommand.generateModlogInfo(entry, true), color: util.colors.default }; return await ButtonPaginator.send(message, [embed]); diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index 44e1db4..0584d97 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -5,51 +5,40 @@ export default class MuteCommand extends BushCommand { super('mute', { aliases: ['mute'], category: 'moderation', - description: { - content: 'Mute a user.', - usage: ['mute <member> [reason] [duration]'], - examples: ['mute ironm00n 1 day commands in #general'] - }, + description: 'Mute a user.', + usage: ['mute <member> [reason] [duration]'], + examples: ['mute ironm00n 1 day commands in #general'], args: [ { id: 'user', + description: 'The user to mute.', type: 'user', - prompt: { - start: 'What user would you like to mute?', - retry: '{error} Choose a valid user to mute.' - } + prompt: 'What user would you like to mute?', + retry: '{error} Choose a valid user to mute.', + slashType: 'USER' }, { id: 'reason', + description: 'The reason for the mute.', type: 'contentWithDuration', match: 'rest', - prompt: { - start: 'Why should this user be muted and for how long?', - retry: '{error} Choose a valid mute reason and duration.', - optional: true - } + prompt: 'Why should this user be muted and for how long?', + retry: '{error} Choose a valid mute reason and duration.', + optional: true, + slashType:'STRING' }, { id: 'force', + description: 'Override permission checks.', flag: '--force', - match: 'flag' + match: 'flag', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'What user would you like to mute?', - type: 'USER', - required: true - }, - { - name: 'reason', - description: 'Why should this user be muted and for how long?', - type: 'STRING', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES']), userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) @@ -95,7 +84,7 @@ export default class MuteCommand extends BushCommand { const responseMessage = () => { const prefix = util.prefix(message); - const victim = util.format.bold(member.user.tag); + const victim = util.format.input(member.user.tag); switch (responseCode) { case 'missing permissions': return `${util.emojis.error} Could not mute ${victim} because I am missing the **Manage Roles** permission.`; diff --git a/src/commands/moderation/purge.ts b/src/commands/moderation/purge.ts index 4d8db08..6bc62de 100644 --- a/src/commands/moderation/purge.ts +++ b/src/commands/moderation/purge.ts @@ -1,4 +1,4 @@ -import { BushCommand, BushMessage } from '#lib'; +import { BushCommand, BushMessage, BushUser } from '#lib'; import { Collection, type Snowflake } from 'discord.js'; export default class PurgeCommand extends BushCommand { @@ -6,61 +6,56 @@ export default class PurgeCommand extends BushCommand { super('purge', { aliases: ['purge'], category: 'moderation', - description: { - content: 'A command to mass delete messages.', - usage: ['purge <amount>'], - examples: ['purge 20'] - }, + description: 'A command to mass delete messages.', + usage: ['purge <amount> [--bot] [--user <user>]'], + examples: ['purge 20'], args: [ { id: 'amount', + description: 'The amount of messages to purge.', customType: util.arg.range('integer', 1, 100, true), - prompt: { - start: 'How many messages would you like to purge?', - retry: '{error} Please pick a number between 1 and 100.' - } + readableType: 'integer', + prompt: 'How many messages would you like to purge?', + retry: '{error} Please pick a number between 1 and 100.', + slashType: 'INTEGER', + minValue: 1, + maxValue: 100 }, { id: 'bot', + description: 'Filter messages to only include those that are from bots.', match: 'flag', - flag: '--bot' + flag: '--bot', + prompt: 'Would you like to only delete messages that are from bots?', + slashType: 'BOOLEAN', + optional: true }, { id: 'user', + description: 'Filter messages to only include those that are from a specified user.', match: 'option', - flag: '--user' + type: 'user', + flag: '--user', + slashType: 'BOOLEAN', + optional: true } ], slash: true, - slashOptions: [ - { - name: 'amount', - description: 'How many messages would you like to purge?', - type: 'INTEGER', - required: true - }, - { - name: 'bot', - description: 'Would you like to only delete messages that are from bots?', - type: 'BOOLEAN', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_MESSAGES', 'EMBED_LINKS'], true), userPermissions: ['MANAGE_MESSAGES'], channel: 'guild' }); } - public override async exec(message: BushMessage, args: { amount: number; bot: boolean }) { + public override async exec(message: BushMessage, args: { amount: number; bot: boolean, user: BushUser }) { if (message.channel.type === 'DM') return message.util.reply(`${util.emojis.error} You cannot run this command in dms.`); if (args.amount > 100 || args.amount < 1) return message.util.reply(`${util.emojis.error} `); const messageFilter = (filterMessage: BushMessage): boolean => { - const shouldFilter = new Array<boolean>(); - if (args.bot) { - shouldFilter.push(filterMessage.author.bot); - } + const shouldFilter: boolean[] = []; + if (args.bot) shouldFilter.push(filterMessage.author.bot); + if (args.user) shouldFilter.push(filterMessage.author.id === args.user.id); + return shouldFilter.filter((bool) => bool === false).length === 0 && filterMessage.id !== message.id; }; const _messages = (await message.channel.messages.fetch({ limit: 100, before: message.id })) diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts index 80f2c01..e847aba 100644 --- a/src/commands/moderation/removeReactionEmoji.ts +++ b/src/commands/moderation/removeReactionEmoji.ts @@ -1,35 +1,35 @@ import { BushCommand, type BushMessage } from '#lib'; -import { type Emoji, type Snowflake } from 'discord.js'; +import { Message, type Emoji, type Snowflake } from 'discord.js'; export default class RemoveReactionEmojiCommand extends BushCommand { public constructor() { super('removeReactionEmoji', { aliases: ['remove-reaction-emoji', 'rre'], category: 'moderation', - description: { - content: 'Deleted all the reactions of a certain emoji from a message.', - usage: ['remove-reaction-emoji <message> <emoji>'], - examples: ['remove-reaction-emoji 791413052347252786 <:omegaclown:782630946435366942>'] - }, + description: 'Delete all the reactions of a certain emoji from a message.', + usage: ['remove-reaction-emoji <message> <emoji>'], + examples: ['remove-reaction-emoji 791413052347252786 <:omegaclown:782630946435366942>'], args: [ { - id: 'messageToRemoveFrom', + id: 'message', + description: 'The message to remove all the reactions of a certain emoji from.', type: 'guildMessage', - prompt: { - start: 'What message would you like to remove a reaction from?', - retry: '{error} Please pick a valid message.' - } + prompt: 'What message would you like to remove a reaction from?', + retry: '{error} Please pick a valid message.', + slashType: 'STRING' }, { id: 'emoji', + description: 'The emoji to remove all the reactions of from a message.', customType: util.arg.union('emoji', 'snowflake'), + readableType:'emoji|snowflake', match: 'restContent', - prompt: { - start: 'What emoji would you like to remove?', - retry: '{error} Please pick a valid emoji.' - } + prompt: 'What emoji would you like to remove?', + retry: '{error} Please pick a valid emoji.', + slashType: 'STRING' } ], + slash: true, channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_MESSAGES', 'EMBED_LINKS'], true), userPermissions: ['MANAGE_MESSAGES', 'MANAGE_EMOJIS_AND_STICKERS'] // Can't undo the removal of 1000s of reactions @@ -38,25 +38,27 @@ export default class RemoveReactionEmojiCommand extends BushCommand { public override async exec( message: BushMessage, - { messageToRemoveFrom, emoji }: { messageToRemoveFrom: BushMessage; emoji: Emoji | Snowflake } + args: { message: BushMessage|Snowflake; emoji: Emoji | Snowflake } ) { - const id = !['string'].includes(typeof emoji); - const emojiID = !id ? `${emoji}` : (emoji as Emoji).id; - const success = await messageToRemoveFrom.reactions.cache + const resolvedMessage = args.message instanceof Message ? args.message : await message.channel.messages.fetch(args.message ) + + const id = !(['string'] as const).includes(typeof args.emoji); + const emojiID = !id ? `${args.emoji}` : (args.emoji as Emoji).id; + const success = await resolvedMessage.reactions.cache ?.get(emojiID!) ?.remove() ?.catch(() => {}); if (success) { return await message.util.reply( - `${util.emojis.success} Removed all reactions of \`${id ? emojiID : emoji}\` from the message with the id of \`${ - messageToRemoveFrom.id + `${util.emojis.success} Removed all reactions of \`${id ? emojiID : args.emoji}\` from the message with the id of \`${ + resolvedMessage.id }\`.` ); } else { return await message.util.reply( `${util.emojis.error} There was an error removing all reactions of \`${ - id ? emojiID : emoji - }\` from the message with the id of \`${messageToRemoveFrom.id}\`.` + id ? emojiID : args.emoji + }\` from the message with the id of \`${resolvedMessage.id}\`.` ); } } diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index dd74bb4..7ca0a5d 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -1,47 +1,54 @@ import { AllowedMentions, BushCommand, type BushGuildMember, type BushMessage, type BushRole, type BushSlashMessage } from '#lib'; import { type ArgumentOptions, type Flag } from 'discord-akairo'; +import { Snowflake } from 'discord.js'; export default class RoleCommand extends BushCommand { public constructor() { super('role', { aliases: ['role', 'rr', 'ar', 'ra'], category: 'moderation', - description: { - content: "Manages users' roles.", - usage: ['role <add|remove> <user> <role> [duration]'], - examples: ['role add spammer nogiveaways 7days', 'ra tyman muted', 'rr tyman staff'] - }, - slash: true, - slashOptions: [ + description: "Manages users' roles.", + usage: ['role <add|remove> <member> <role> [duration]'], + examples: ['role add spammer nogiveaways 7days', 'ra tyman muted', 'rr tyman staff'], + args: [ { - name: 'action', - description: 'Would you like to add or remove a role?', - type: 'STRING', + id: 'action', + description: 'Whether to add or remove a role for the the user.', + prompt: 'Would you like to add or remove a role?', + slashType: 'STRING', choices: [ { name: 'add', value: 'add' }, { name: 'remove', value: 'remove' } ], - required: true + only: 'slash' }, { - name: 'user', - description: 'What user do you want to add/remove the role to/from?', - type: 'USER', - required: true + id: 'member', + description: 'The user to add/remove a role to/from.', + prompt: 'What user do you want to add/remove a role to/from?', + slashType: 'USER', + slashResolve: 'member', + optional: true, + only: 'slash' }, { - name: 'role', + id: 'role', description: 'The role you would like to add/remove from the to/from.', - type: 'ROLE', - required: true + prompt: 'What role would you like to add/remove from the user?', + slashType: 'ROLE', + optional: true, + only: 'slash' }, { - name: 'duration', - description: 'How long would you like to role to last?', - type: 'STRING', - required: false + id: 'duration', + description: 'The time before the role will be removed (ignored if removing a role).', + prompt: 'How long would you like to role to last?', + slashType: 'STRING', + optional: true, + only: 'slash' } ], + slash: true, channel: 'guild', typing: true, clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES', 'EMBED_LINKS'], true), @@ -49,10 +56,10 @@ export default class RoleCommand extends BushCommand { }); } - override *args(message: BushMessage): IterableIterator<ArgumentOptions | Flag> { - const action = ['rr'].includes(message.util.parsed?.alias ?? '') + override *args(message: BushMessage): Generator<ArgumentOptions | Flag> { + const action = (['rr'] as const).includes(message.util.parsed?.alias ?? '') ? 'remove' - : ['ar', 'ra'].includes(message.util.parsed?.alias ?? '') + : (['ar', 'ra'] as const).includes(message.util.parsed?.alias ?? '') ? 'add' : yield { id: 'action', @@ -63,7 +70,7 @@ export default class RoleCommand extends BushCommand { } }; - const user = yield { + const member = yield { id: 'user', type: 'member', prompt: { @@ -84,19 +91,21 @@ export default class RoleCommand extends BushCommand { } }; - return { action, user, role: (_role as any).role ?? _role, duration: (_role as any).duration }; + const force = yield { + id: 'force', + description: 'Override permission checks and ban the user anyway.', + flag: '--force', + match: 'flag' + }; + + return { action, member: member, role: (_role as any).role ?? _role, duration: (_role as any).duration, force }; } public override async exec( message: BushMessage | BushSlashMessage, - { - action, - user: member, - role, - duration - }: { action: 'add' | 'remove'; user: BushGuildMember; role: BushRole; duration?: number | null } + args: { action: 'add' | 'remove'; member: BushGuildMember; role: BushRole; duration?: number | null; force?: boolean } ) { - if (duration === null) duration = 0; + if (args.duration === null) args.duration = 0; if ( !message.member!.permissions.has('MANAGE_ROLES') && message.member!.id !== message.guild?.ownerId && @@ -106,11 +115,11 @@ export default class RoleCommand extends BushCommand { let mappedRole: { name: string; id: string }; for (let i = 0; i < mappings.roleMap.length; i++) { const a = mappings.roleMap[i]; - if (a.id == role.id) mappedRole = a; + if (a.id === args.role.id) mappedRole = a; } if (!mappedRole! || !Reflect.has(mappings.roleWhitelist, mappedRole.name)) { return await message.util.reply({ - content: `${util.emojis.error} <@&${role.id}> is not whitelisted, and you do not have manage roles permission.`, + content: `${util.emojis.error} <@&${args.role.id}> is not whitelisted, and you do not have manage roles permission.`, allowedMentions: AllowedMentions.none() }); } @@ -120,44 +129,54 @@ export default class RoleCommand extends BushCommand { } return; }); - if (!message.member!.roles.cache.some((role) => allowedRoles.includes(role.id))) { + if (!message.member!.roles.cache.some((role) => (allowedRoles as Snowflake[]).includes(role.id))) { return await message.util.reply({ - content: `${util.emojis.error} <@&${role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, + content: `${util.emojis.error} <@&${args.role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, allowedMentions: AllowedMentions.none() }); } } - const shouldLog = this.punishmentRoleNames.includes(role.name); + const shouldLog = this.punishmentRoleNames.includes(args.role.name); const responseCode = - action === 'add' - ? await member.addRole({ moderator: message.member!, addToModlog: shouldLog, role, duration }) - : await member.removeRole({ moderator: message.member!, addToModlog: shouldLog, role, duration }); + args.action === 'add' + ? await args.member.addRole({ + moderator: message.member!, + addToModlog: shouldLog, + role: args.role, + duration: args.duration + }) + : await args.member.removeRole({ + moderator: message.member!, + addToModlog: shouldLog, + role: args.role, + duration: args.duration + }); const responseMessage = () => { - const victim = util.format.bold(member.user.tag); + const victim = util.format.input(args.member.user.tag); switch (responseCode) { case 'user hierarchy': - return `${util.emojis.error} <@&${role.id}> is higher or equal to your highest role.`; + return `${util.emojis.error} <@&${args.role.id}> is higher or equal to your highest role.`; case 'role managed': - return `${util.emojis.error} <@&${role.id}> is managed by an integration and cannot be managed.`; + return `${util.emojis.error} <@&${args.role.id}> is managed by an integration and cannot be managed.`; case 'client hierarchy': - return `${util.emojis.error} <@&${role.id}> is higher or equal to my highest role.`; + return `${util.emojis.error} <@&${args.role.id}> is higher or equal to my highest role.`; case 'error creating modlog entry': return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case 'error creating role entry' || 'error removing role entry': return `${util.emojis.error} There was an error ${ - action === 'add' ? 'creating' : 'removing' + args.action === 'add' ? 'creating' : 'removing' } a punishment entry, please report this to my developers.`; case 'error adding role' || 'error removing role': - return `${util.emojis.error} An error occurred while trying to ${action} <@&${role.id}> ${ - action === 'add' ? 'to' : 'from' + return `${util.emojis.error} An error occurred while trying to ${args.action} <@&${args.role.id}> ${ + args.action === 'add' ? 'to' : 'from' } ${victim}.`; case 'success': - return `${util.emojis.success} Successfully ${action === 'add' ? 'added' : 'removed'} <@&${role.id}> ${ - action === 'add' ? 'to' : 'from' - } ${victim}${duration ? ` for ${util.humanizeDuration(duration)}` : ''}.`; + return `${util.emojis.success} Successfully ${args.action === 'add' ? 'added' : 'removed'} <@&${args.role.id}> ${ + args.action === 'add' ? 'to' : 'from' + } ${victim}${args.duration ? ` for ${util.humanizeDuration(args.duration)}` : ''}.`; } }; diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index e05a409..949038c 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -14,40 +14,32 @@ export default class SlowModeCommand extends BushCommand { super('slowmode', { aliases: ['slowmode', 'slow'], category: 'moderation', - description: { - content: 'A command to set the slowmode of a channel.', - usage: ['slowmode <length>'], - examples: ['slowmode 3'] - }, + description: 'A command to set the slowmode of a channel.', + usage: ['slowmode <length> [channel]'], + examples: ['slowmode 3'], args: [ { id: 'length', + description: 'The amount of time to set the slowmode of a channel to.', customType: Argument.union('duration', 'durationSeconds', 'off', 'none', 'disable'), - prompt: { - start: 'What would you like to set the slowmode to?', - retry: '{error} Please set the slowmode to a valid length.', - optional: true - } + readableType: "duration|durationSeconds|'off'|'none'|'disable'", + prompt: 'What would you like to set the slowmode to?', + retry: '{error} Please set the slowmode to a valid length.', + optional: true, + slashType: 'INTEGER' }, { id: 'channel', + description: 'The channel to change the slowmode of, defaults to the current channel.', type: 'channel', - prompt: { - start: 'What channel would you like to change?', - retry: '{error} Choose a valid channel.', - optional: true - } + prompt: 'What channel would you like to change?', + retry: '{error} Choose a valid channel.', + optional: true, + slashType: 'CHANNEL', + channelTypes: ['GUILD_TEXT', 'GUILD_PRIVATE_THREAD', 'GUILD_PUBLIC_THREAD'] } ], slash: true, - slashOptions: [ - { - name: 'channel', - description: 'What channel would you like to change the slowmode of?', - type: 'CHANNEL', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_CHANNELS', 'EMBED_LINKS'], true), userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) @@ -67,16 +59,16 @@ export default class SlowModeCommand extends BushCommand { if (message.channel!.type === 'DM') return await message.util.reply(`${util.emojis.error} This command cannot be run in dms.`); if (!channel) channel = message.channel as any; - if (!(channel instanceof TextChannel) && !(channel instanceof ThreadChannel)) + if (!(['GUILD_TEXT', 'GUILD_PRIVATE_THREAD', 'GUILD_PUBLIC_THREAD'] as const).includes(channel.type)) return await message.util.reply(`${util.emojis.error} <#${channel.id}> is not a text or thread channel.`); if (length) { length = - typeof length === 'string' && !['off', 'none', 'disable'].includes(length) + typeof length === 'string' && !(['off', 'none', 'disable'] as const).includes(length) ? await util.arg.cast('duration', message, length) : length; } - const length2: number = ['off', 'none', 'disable'].includes(length as string) ? 0 : (length as number); + const length2: number = (['off', 'none', 'disable'] as const).includes(length as string) ? 0 : (length as number); const setSlowmode = await (channel as ThreadChannel | TextChannel) .setRateLimitPerUser(length2 / 1000, `Changed by ${message.author.tag} (${message.author.id}).`) diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts index 1492b63..8444801 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/unban.ts @@ -5,46 +5,30 @@ export default class UnbanCommand extends BushCommand { super('unban', { aliases: ['unban'], category: 'moderation', - description: { - content: 'Unban a member from the server.', - usage: ['unban <member> <reason> [--delete]'], - examples: ['unban 322862723090219008 I changed my mind, commands are allowed in #general'] - }, + description: 'Unban a member from the server.', + usage: ['unban <member> <reason>'], + examples: ['unban 322862723090219008 I changed my mind, commands are allowed in #general'], args: [ { id: 'user', + description: 'The user to unban.', type: 'globalUser', - prompt: { - start: 'What user would you like to unban?', - retry: '{error} Choose a valid user to unban.' - } + prompt: 'What user would you like to unban?', + retry: '{error} Choose a valid user to unban.', + slashType: 'USER' }, { id: 'reason', + description: 'The reason for the unban', type: 'string', match: 'restContent', - prompt: { - start: 'Why should this user be unbanned?', - retry: '{error} Choose a valid unban reason.', - optional: true - } + prompt: 'Why should this user be unbanned?', + retry: '{error} Choose a valid unban reason.', + optional: true, + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'What user would you like to unban?', - type: 'USER', - required: true - }, - { - name: 'reason', - description: 'Why should this user be unbanned?', - type: 'STRING', - required: false - } - ], channel: 'guild', clientPermissions: ['BAN_MEMBERS'], userPermissions: ['BAN_MEMBERS'] @@ -58,7 +42,7 @@ export default class UnbanCommand extends BushCommand { }); const responseMessage = () => { - const victim = util.format.bold(user.tag); + const victim = util.format.input(user.tag); switch (responseCode) { case 'missing permissions': return `${util.emojis.error} Could not unban ${victim} because I am missing the **Ban Members** permission.`; diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index c31ce21..0c59b1f 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -13,51 +13,40 @@ export default class UnmuteCommand extends BushCommand { super('unmute', { aliases: ['unmute'], category: 'moderation', - description: { - content: 'unmute a user.', - usage: ['unmute <member> [reason]'], - examples: ['unmute 322862723090219008 1 day commands in #general'] - }, + description: 'unmute a user.', + usage: ['unmute <member> [reason]'], + examples: ['unmute 322862723090219008 1 day commands in #general'], args: [ { id: 'user', + description: 'The user to unmute.', type: 'user', - prompt: { - start: 'What user would you like to unmute?', - retry: '{error} Choose a valid user to unmute.' - } + prompt: 'What user would you like to unmute?', + retry: '{error} Choose a valid user to unmute.', + slashType: 'USER' }, { id: 'reason', + description: 'The reason for the unmute.', type: 'string', match: 'rest', - prompt: { - start: 'Why should this user be unmuted?', - retry: '{error} Choose a valid unmute reason.', - optional: true - } + prompt: 'Why should this user be unmuted?', + retry: '{error} Choose a valid unmute reason.', + optional: true, + slashType: 'STRING' }, { id: 'force', + description: 'Override permission checks.', flag: '--force', - match: 'flag' + match: 'flag', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'What user would you like to unmute?', - type: 'USER', - required: true - }, - { - name: 'reason', - description: 'Why should this user be unmuted?', - type: 'STRING', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES']), userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) @@ -87,7 +76,7 @@ export default class UnmuteCommand extends BushCommand { const responseMessage = () => { const prefix = util.prefix(message); - const victim = util.format.bold(member.user.tag); + const victim = util.format.input(member.user.tag); switch (responseCode) { case 'missing permissions': return `${error} Could not unmute ${victim} because I am missing the **Manage Roles** permission.`; diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 9b58cf6..2212548 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -13,50 +13,39 @@ export default class WarnCommand extends BushCommand { super('warn', { aliases: ['warn'], category: 'moderation', - description: { - content: 'Warn a user.', - usage: ['warn <member> [reason]'], - examples: ['warn @Tyman being cool'] - }, + description: 'Warn a user.', + usage: ['warn <member> [reason]'], + examples: ['warn @Tyman being cool'], args: [ { id: 'user', + description: 'The user to warn.', type: 'user', - prompt: { - start: 'What user would you like to warn?', - retry: '{error} Choose a valid user to warn.' - } + prompt: 'What user would you like to warn?', + retry: '{error} Choose a valid user to warn.', + slashType: 'USER' }, { id: 'reason', + description: 'The reason for the warn.', match: 'rest', - prompt: { - start: 'Why should this user be warned?', - retry: '{error} Choose a valid warn reason.', - optional: true - } + prompt: 'Why should this user be warned?', + retry: '{error} Choose a valid warn reason.', + slashType: 'STRING', + optional: true }, { id: 'force', + description: 'Override permission checks.', flag: '--force', - match: 'flag' + match: 'flag', + optional: true, + slashType: false, + only: 'text', + ownerOnly: true } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'What user would you like to warn?', - type: 'USER', - required: true - }, - { - name: 'reason', - description: 'Why should this user be warned?', - type: 'STRING', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']) @@ -83,7 +72,7 @@ export default class WarnCommand extends BushCommand { }); const responseMessage = () => { - const victim = util.format.bold(member.user.tag); + const victim = util.format.input(member.user.tag); switch (response) { case 'error creating modlog entry': return `${util.emojis.error} While warning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts index 237ca89..2749430 100644 --- a/src/commands/moulberry-bush/capePerms.ts +++ b/src/commands/moulberry-bush/capePerms.ts @@ -7,31 +7,20 @@ export default class CapePermissionsCommand extends BushCommand { super('capePermissions', { aliases: ['cape-perms', 'cape-perm', 'cape-permissions'], category: "Moulberry's Bush", - description: { - content: 'A command to see what capes someone has access to.', - usage: ['cape-perms <user>'], - examples: ['cape-perms IRONM00N'] - }, + description: 'A command to see what capes someone has access to.', + usage: ['cape-perms <user>'], + examples: ['cape-perms IRONM00N'], args: [ { id: 'ign', + description: 'The ign of the player you would like to view the capes permissions of.', type: 'string', - prompt: { - start: 'Who would you like to see the cape permissions of?', - retry: '{error} Choose someone to see the capes their available capes.', - optional: false - } + prompt: 'Who would you like to see the cape permissions of?', + retry: '{error} Choose someone to see the capes their available capes.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'ign', - description: 'The ign of the player you would like to view the capes permissions of.', - type: 'STRING', - required: true - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], channel: 'guild' @@ -51,7 +40,7 @@ export default class CapePermissionsCommand extends BushCommand { let capePerms: CapePerms | null, uuid: string; try { - uuid = await util.findUUID(args.ign); + uuid = await util.mcUUID(args.ign); } catch (e) { return await message.util.reply(`${util.emojis.error} \`${args.ign}\` doesn't appear to be a valid username.`); } diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts index 0af0125..1734568 100644 --- a/src/commands/moulberry-bush/capes.ts +++ b/src/commands/moulberry-bush/capes.ts @@ -7,32 +7,22 @@ export default class CapesCommand extends BushCommand { super('capes', { aliases: ['capes', 'cape'], category: "Moulberry's Bush", - description: { - content: 'A command to see what a cape looks like.', - usage: ['cape [cape]'], - examples: ['capes', 'cape space'] - }, + description: 'A command to see what a cape looks like.', + usage: ['cape [cape]'], + examples: ['capes', 'cape space'], args: [ { id: 'cape', + description: 'The cape to view.', type: 'string', - prompt: { - start: 'What cape would you like to see?', - retry: '{error} Choose a cape to see.', - optional: true - }, - default: null + prompt: 'What cape would you like to see?', + retry: '{error} Choose a cape to see.', + optional: true, + slashType: 'STRING' + // choices: client.consts.mappings.capes.map((v) => ({ name: v.name, value: v.name })) } ], slash: true, - slashOptions: [ - { - name: 'cape', - description: 'What cape would you like to see?', - type: 'STRING', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] }); diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts index d76964c..28f920a 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -5,11 +5,9 @@ export default class GiveawayPingCommand extends BushCommand { super('giveawayPing', { aliases: ['giveaway-ping', 'giveaway-pong'], category: "Moulberry's Bush", - description: { - content: 'Pings the giveaway role.', - usage: ['giveaway-ping'], - examples: ['giveaway-ping'] - }, + description: 'Pings the giveaway role.', + usage: ['giveaway-ping'], + examples: ['giveaway-ping'], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_MESSAGES'], true), userPermissions: ['MANAGE_GUILD', 'MANAGE_MESSAGES', 'BAN_MEMBERS', 'KICK_MEMBERS', 'VIEW_CHANNEL'], channel: 'guild', diff --git a/src/commands/moulberry-bush/moulHammer.ts b/src/commands/moulberry-bush/moulHammer.ts index 98ef78f..2811f80 100644 --- a/src/commands/moulberry-bush/moulHammer.ts +++ b/src/commands/moulberry-bush/moulHammer.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage } from '#lib'; +import { BushCommand, BushSlashMessage, type BushMessage } from '#lib'; import { MessageEmbed, type User } from 'discord.js'; export default class MoulHammerCommand extends BushCommand { @@ -6,28 +6,28 @@ export default class MoulHammerCommand extends BushCommand { super('moulHammer', { aliases: ['moul-hammer'], category: "Moulberry's Bush", - description: { - content: 'A command to moul hammer members.', - usage: ['moul-hammer <user>'], - examples: ['moul-hammer @IRONM00N'] - }, + description: 'A command to moul hammer members.', + usage: ['moul-hammer <user>'], + examples: ['moul-hammer @IRONM00N'], args: [ { id: 'user', + description: 'The user to moul hammer.', type: 'user', - prompt: { - start: 'What user would you like to moul hammer?', - retry: '{error} Choose a valid user to moul hammer' - } + prompt: 'What user would you like to moul hammer?', + retry: '{error} Choose a valid user to moul hammer', + slashType: 'USER' } ], + slash: true, + slashGuilds: ['516977525906341928'], restrictedGuilds: ['516977525906341928'], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] }); } - public override async exec(message: BushMessage, { user }: { user: User }) { + public override async exec(message: BushMessage | BushSlashMessage, { user }: { user: User }) { await message.delete(); const embed = new MessageEmbed() .setTitle('L') diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts index d5e9731..2a0d06f 100644 --- a/src/commands/moulberry-bush/report.ts +++ b/src/commands/moulberry-bush/report.ts @@ -7,47 +7,30 @@ export default class ReportCommand extends BushCommand { super('report', { aliases: ['report'], category: "Moulberry's Bush", - description: { - content: 'A command to report a user.', - usage: ['report <user> <reason/evidence>'], - examples: ['report IRONM00N commands in #general'] - }, + description: 'A command to report a user.', + usage: ['report <user> <reason/evidence>'], + examples: ['report IRONM00N commands in #general'], args: [ { id: 'member', + description: 'The member to report.', type: 'member', - prompt: { - start: 'Who would you like to report?', - retry: `{error} Choose a valid user to report.`, - optional: false - } + prompt: 'Who would you like to report?', + retry: '{error} Choose a valid user to report.', + slashType: 'USER' }, { id: 'evidence', + description: 'The evidence to report the user for.', type: 'string', match: 'rest', - prompt: { - start: 'What did the user do wrong?', - retry: `{error} Provide evidence.`, - optional: true - } + prompt: 'What did the user do wrong?', + retry: '{error} Provide evidence.', + optional: true, + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'user', - description: 'Who would you like to report?', - type: 'USER', - required: true - }, - { - name: 'evidence', - description: 'What did the user do wrong?', - type: 'STRING', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], channel: 'guild' diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index d4820fe..a88b323 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -55,46 +55,33 @@ export default class RuleCommand extends BushCommand { super('rule', { aliases: ['rule', 'rules'], category: "Moulberry's Bush", - description: { - content: 'A command to state a rule.', - usage: ['rule <rule> [user]'], - examples: ['rule 1 IRONM00N', 'rule 2', 'rules'] - }, + description: 'A command to state a rule.', + usage: ['rule <rule> [user]'], + examples: ['rule 1 IRONM00N', 'rule 2', 'rules'], args: [ { id: 'rule', + description: 'The rule to view.', customType: util.arg.range('integer', 1, rules.length, true), - prompt: { - start: 'What rule would you like to have cited?', - retry: '{error} Choose a valid rule.', - optional: true - } + readableType: 'integer', + prompt: 'What rule would you like to have cited?', + retry: '{error} Choose a valid rule.', + optional: true, + slashType: 'INTEGER', + minValue: 1, + maxValue: rules.length }, { id: 'user', + description: 'The user to mention.', type: 'user', - prompt: { - start: 'What user would you like to mention?', - retry: '{error} Choose a valid user to mention.', - optional: true - } + prompt: 'What user would you like to mention?', + retry: '{error} Choose a valid user to mention.', + optional: true, + slashType: 'USER' } ], slash: true, - slashOptions: [ - { - name: 'rule', - description: 'The rule you would you like to have cited', - type: 'INTEGER', - required: false - }, - { - name: 'user', - description: 'The user you would like to mention.', - type: 'USER', - required: false - } - ], slashGuilds: ['516977525906341928'], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), diff --git a/src/commands/moulberry-bush/serverStatus.ts b/src/commands/moulberry-bush/serverStatus.ts index ff0023e..ac37684 100644 --- a/src/commands/moulberry-bush/serverStatus.ts +++ b/src/commands/moulberry-bush/serverStatus.ts @@ -7,11 +7,9 @@ export default class ServerStatusCommand extends BushCommand { super('serverStatus', { aliases: ['server-status', 'ss'], category: "Moulberry's Bush", - description: { - content: "Gives the status of moulberry's server", - usage: ['server-status'], - examples: ['server-status', 'ss'] - }, + description: "Gives the status of moulberry's server", + usage: ['server-status'], + examples: ['server-status', 'ss'], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], slash: true diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts index 3fca2b6..6829757 100644 --- a/src/commands/utilities/activity.ts +++ b/src/commands/utilities/activity.ts @@ -2,29 +2,62 @@ import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { DiscordAPIError, Message, VoiceChannel } from 'discord.js'; const activityMap = { - 'Poker Night': '755827207812677713', - 'Betrayal.io': '773336526917861400', - 'Fishington.io': '814288819477020702', - 'YouTube Together': '755600276941176913', - 'Chess in the Park': '832012774040141894', - 'Watch Together': '880218394199220334', - 'Doodle Crew': '878067389634314250', - 'Wood Snacks': '879863976006127627', - 'Letter Tile': '879863686565621790' + 'Poker Night': { + id: '755827207812677713', + aliases: ['poker'] + }, + 'Betrayal.io': { + id: '773336526917861400', + aliases: ['betrayal'] + }, + 'Fishington.io': { + id: '814288819477020702', + aliases: ['fish', 'fishing', 'fishington'] + }, + 'YouTube Together': { + id: '755600276941176913', + aliases: ['youtube-together'] + }, + 'Chess In The Park': { + id: '832012774040141894', + aliases: ['chess'] + }, + 'Watch Together': { + id: '880218394199220334', + aliases: ['watch-together', 'yt', 'youtube'] + }, + 'Doodle Crew': { + id: '878067389634314250', + aliases: ['doodle-crew', 'doodle'] + }, + 'Wood Snacks': { + id: '879863976006127627', + aliases: ['wood-snacks', 'wood'] + }, + 'Letter Tile': { + id: '879863686565621790', + aliases: ['letter-tile', 'letter'] + }, + 'Spell Cast': { + id: '852509694341283871', + aliases: ['spell-cast', 'spell', 'cast'] + }, + 'Checkers In The Park': { + id: '832013003968348200', + aliases: ['checkers'] + } }; function map(phase: string) { if (client.consts.regex.snowflake.test(phase)) return phase; - else if (Reflect.has(activityMap, phase)) return activityMap[phase as keyof typeof activityMap]; - else if (['yt', 'youtube'].includes(phase)) return activityMap['Watch Together']; - else if (['chess', 'park'].includes(phase)) return activityMap['Chess in the Park']; - else if (['poker'].includes(phase)) return activityMap['Poker Night']; - else if (['fish', 'fishing', 'fishington'].includes(phase)) return activityMap['Fishington.io']; - else if (['betrayal'].includes(phase)) return activityMap['Betrayal.io']; - else if (['doodle-crew', 'doodle'].includes(phase)) return activityMap['Doodle Crew']; - else if (['wood-snacks', 'wood'].includes(phase)) return activityMap['Wood Snacks']; - else if (['letter-tile', 'letter'].includes(phase)) return activityMap['Letter Tile']; - else return null; + else if (phase in activityMap) return activityMap[phase as keyof typeof activityMap]; + + for (const activity in activityMap) { + if (activityMap[activity as keyof typeof activityMap].aliases.includes(phase.toLowerCase())) + return activityMap[activity as keyof typeof activityMap].id; + } + + return null; } const activityTypeCaster = (_message: Message | BushMessage | BushSlashMessage, phrase: string) => { @@ -56,53 +89,43 @@ export default class YouTubeCommand extends BushCommand { 'letter' ], category: 'utilities', - description: { - content: 'Allows you to play discord activities in voice channels.', - usage: [ - 'activity <channel> <`yt`|`youtube`|`chess`|`park`|`poker`|`fish`|`fishing`|`fishington`|`betrayal`>', - 'yt <channel>' // you do not need to specify the activity if you use its alias. - ], - examples: ['yt 785281831788216364', 'activity 785281831788216364 yt'] - }, + description: 'Allows you to play discord activities in voice channels.', + usage: [ + `activity <channel> <${Object.values(activityMap) + .flatMap((a) => a.aliases) + .map((a) => `'${a}'`) + .join('|')}>`, + 'yt <channel>' // you do not need to specify the activity if you use its alias. + ], + examples: ['yt 785281831788216364', 'activity 785281831788216364 yt'], args: [ { id: 'channel', + description: 'The channel to create the activity in.', type: 'voiceChannel', - prompt: { - start: 'What channel would you like to use?', - retry: '{error} Choose a valid voice channel' - } + prompt: 'What channel would you like to use?', + retry: '{error} Choose a valid voice channel', + slashType: 'CHANNEL', + channelTypes: ['GUILD_VOICE'] }, { id: 'activity', + description: 'The activity to create an invite for.', match: 'rest', customType: activityTypeCaster, - prompt: { - start: 'What activity would you like to play?', - retry: - '{error} You must choose one of the following options: `yt`, `youtube`, `chess`, `park`, `poker`, `fish`, `fishing`, `fishington`, or `betrayal`.' - } - } - ], - slash: true, - slashOptions: [ - { - name: 'channel', - description: 'What channel would you like to use?', - type: 'CHANNEL', - required: true - }, - { - name: 'activity', - description: 'What activity would you like to play?', - type: 'STRING', - required: true, + prompt: 'What activity would you like to play?', + retry: `{error} You must choose one of the following options: ${Object.values(activityMap) + .flatMap((a) => a.aliases) + .map((a) => `\`${a}\``) + .join(', ')}.`, + slashType: 'STRING', choices: Object.keys(activityMap).map((key) => ({ name: key, - value: activityMap[key as keyof typeof activityMap] + value: activityMap[key as keyof typeof activityMap].id })) } ], + slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/commands/utilities/calculator.ts b/src/commands/utilities/calculator.ts index 277947b..2c70dcc 100644 --- a/src/commands/utilities/calculator.ts +++ b/src/commands/utilities/calculator.ts @@ -7,32 +7,21 @@ export default class CalculatorCommand extends BushCommand { super('calculator', { aliases: ['calculator', 'calc', 'math'], category: 'utilities', - description: { - content: 'Calculates math expressions.', - usage: ['calculator <expression>'], - examples: ['calculator 9+10'] - }, + description: 'Calculates math expressions.', + usage: ['calculator <expression>'], + examples: ['calculator 9+10'], args: [ { id: 'expression', + description: 'The expression to calculate.', type: 'string', match: 'rest', - prompt: { - start: 'What would you like to evaluate?', - retry: '{error} Pick something to evaluate.', - optional: false - } + prompt: 'What would you like to calculate?', + retry: '{error} Pick something to calculate.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'expression', - description: 'What would you like to evaluate?', - type: 'STRING', - required: true - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/commands/utilities/decode.ts b/src/commands/utilities/decode.ts index 7f0c7d4..76f735b 100644 --- a/src/commands/utilities/decode.ts +++ b/src/commands/utilities/decode.ts @@ -10,61 +10,39 @@ export default class DecodeCommand extends BushCommand { super('decode', { aliases: ['decode', 'encode'], category: 'utilities', - description: { - content: 'Decode / encode.', - usage: ['decode <from> <to> <data>'], - examples: ['decode base64 ascii TmVyZApJbWFnaW5lIGRlY29kaW5nIHRoaXMgbG1hbw=='] - }, + description: 'Decode / encode.', + usage: ['decode <from> <to> <data>'], + examples: ['decode base64 ascii TmVyZApJbWFnaW5lIGRlY29kaW5nIHRoaXMgbG1hbw=='], args: [ { id: 'from', + description: 'The type of data you are inputting.', customType: encodingTypesArray, - prompt: { - start: 'What is the encoding of the original data?', - retry: `{error} Choose one of the following ${encodingTypesString} for the encoding of the original data.` - } + prompt: 'What is the encoding of the original data?', + retry: `{error} Choose one of the following ${encodingTypesString} for the encoding of the original data.`, + slashType: 'STRING', + choices: encodingTypesArray.map((e) => ({ name: e, value: e })) }, { id: 'to', + description: 'The type of data you want the output to be.', customType: encodingTypesArray, - prompt: { - start: 'What would you like the encoding of the resulting data to be?', - retry: `{error} Choose one of the following ${encodingTypesString} for the encoding of the resulting data.` - } + prompt: 'What would you like the encoding of the resulting data to be?', + retry: `{error} Choose one of the following ${encodingTypesString} for the encoding of the resulting data.`, + slashType: 'STRING', + choices: encodingTypesArray.map((e) => ({ name: e, value: e })) }, { id: 'data', + description: 'What you would like to decode.', type: 'string', match: 'restContent', - prompt: { - start: 'What would you to decode.', - retry: '{error} Choose a valid string to decode.' - } + prompt: 'What would you to decode.', + retry: '{error} Choose a valid string to decode.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'from', - description: 'The type of data you are inputting.', - type: 'STRING', - choices: encodingTypesArray.map((e) => ({ name: e, value: e })), - required: true - }, - { - name: 'to', - description: 'The type of data you want the output to be.', - type: 'STRING', - choices: encodingTypesArray.map((e) => ({ name: e, value: e })), - required: true - }, - { - name: 'data', - description: 'What you would like to decode.', - type: 'STRING', - required: true - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/commands/utilities/hash.ts b/src/commands/utilities/hash.ts index 62791c9..7a1bfa7 100644 --- a/src/commands/utilities/hash.ts +++ b/src/commands/utilities/hash.ts @@ -7,19 +7,17 @@ export default class HashCommand extends BushCommand { super('hash', { aliases: ['hash'], category: 'utilities', - description: { - content: 'Gets the file hash of the given discord link', - usage: ['hash <fileUrl>'], - examples: ['hash https://cdn.discordapp.com/emojis/782630946435366942.png?v=1'] //nice - }, + description: 'Gets the file hash of the given discord link', + usage: ['hash <fileUrl>'], + examples: ['hash https://cdn.discordapp.com/emojis/782630946435366942.png?v=1'], //nice args: [ { id: 'url', + description: 'The url of the discord link to find the hash of.', type: 'url', - prompt: { - start: 'What url would you like to find the hash of?', - retry: '{error} Enter a valid url.' - } + prompt: 'What url would you like to find the hash of?', + retry: '{error} Enter a valid url.', + slashType: 'STRING' } ], clientPermissions: (m) => util.clientSendAndPermCheck(m), diff --git a/src/commands/utilities/price.ts b/src/commands/utilities/price.ts index b852a53..d931dd2 100644 --- a/src/commands/utilities/price.ts +++ b/src/commands/utilities/price.ts @@ -8,43 +8,30 @@ export default class PriceCommand extends BushCommand { super('price', { aliases: ['price'], category: 'utilities', - description: { - content: 'Finds the price information of an item.', - usage: ['price <item id>'], - examples: ['price ASPECT_OF_THE_END'] - }, + description: 'Finds the price information of an item.', + usage: ['price <item id>'], + examples: ['price ASPECT_OF_THE_END'], args: [ { id: 'item', + description: 'The item that you would you like to find the price of.', type: 'string', match: 'content', - prompt: { - start: 'What item would you like to find the price of?', - retry: '{error} Choose a valid item.' - } + prompt: 'What item would you like to find the price of?', + retry: '{error} Choose a valid item.', + slashType: 'STRING' }, { id: 'strict', + description: 'Whether or not to bypass the fuzzy search.', match: 'flag', flag: '--strict', - default: false + prompt: 'Would you like to bypass the fuzzy search?', + optional: true, + slashType: 'BOOLEAN' } ], slash: true, - slashOptions: [ - { - name: 'item', - description: 'The item that you would you like to find the price of.', - type: 'STRING', - required: true - }, - { - name: 'strict', - description: 'Whether or not to bypass the fuzzy search.', - type: 'BOOLEAN', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [], typing: true diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts index 3d230c5..190277a 100644 --- a/src/commands/utilities/steal.ts +++ b/src/commands/utilities/steal.ts @@ -1,65 +1,100 @@ -import { BushCommand, type BushMessage } from '#lib'; +import { BushCommand, BushSlashMessage, type BushMessage } from '#lib'; +import { ArgumentOptions, Flag } from 'discord-akairo'; import { type Snowflake } from 'discord.js'; +import _ from 'lodash'; export default class StealCommand extends BushCommand { public constructor() { super('steal', { aliases: ['steal', 'copy-emoji'], category: 'utilities', - description: { - content: 'Steal an emoji from another server and add it to your own.', - usage: ['steal <emoji/emojiId/url> [name]'], - examples: ['steal <:omegaclown:782630946435366942> ironm00n'] - }, + description: 'Steal an emoji from another server and add it to your own.', + usage: ['steal <emoji/emojiId/url> [name]'], + examples: ['steal <:omegaclown:782630946435366942> ironm00n'], args: [ { - id: 'emojiOrName', + id: 'emoji', + description: 'The emoji to steal.', customType: util.arg.union('discordEmoji', 'snowflake', 'url'), - prompt: { - start: 'What emoji would you like to steal?', - retry: '{error} Pick a valid emoji, emoji id, or image url.', - optional: true - } + readableType: 'discordEmoji|snowflake|url', + prompt: 'What emoji would you like to steal?', + retry: '{error} Pick a valid emoji, emoji id, or image url.', + optional: true, + only: 'slash', + slashType: 'STRING' }, { - id: 'name2' + id: 'name', + description: 'The name to give the new emoji.', + prompt: 'What would you like to name the emoji?', + retry: '{error} Choose a valid name fore the emoji.', + optional: true, + only: 'slash', + slashType: 'STRING' } ], - slash: false, + slash: true, channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_EMOJIS_AND_STICKERS']), userPermissions: ['MANAGE_EMOJIS_AND_STICKERS'] }); } + + public override *args(message: BushMessage): Generator<ArgumentOptions | Flag> { + const hasImage = message.attachments.size && message.attachments.first()?.contentType?.includes('image/'); + + const emoji = hasImage + ? message.attachments.first()!.url + : yield { + id: 'emoji', + type: util.arg.union('discordEmoji', 'snowflake', 'url'), + prompt: { + start: 'What emoji would you like to steal?', + retry: '{error} Pick a valid emoji, emoji id, or image url.' + } + }; + + const name = yield { + id: 'name', + prompt: { + start: 'What would you like to name the emoji?', + retry: '{error} Choose a valid name fore the emoji.', + optional: true + }, + default: + hasImage && message.attachments.first()?.name + ? _.camelCase(message.attachments.first()!.name ?? 'stolen_emoji') + : 'stolen_emoji' + }; + + return { emoji, name }; + } + public override async exec( - message: BushMessage, - args?: { emojiOrName?: { name: string; id: Snowflake } | Snowflake | URL | string; name2: string } + message: BushMessage | BushSlashMessage, + args?: { emoji?: { name: string; id: Snowflake } | Snowflake | URL | string; name: string } ) { - if ((!args || !args.emojiOrName) && !message.attachments.size) - return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); + if (!args || !args.emoji) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); const image = - message.attachments.size && message.attachments.first()?.contentType?.includes('image/') - ? message.attachments.first()!.url - : args?.emojiOrName instanceof URL - ? args.emojiOrName.href - : typeof args?.emojiOrName === 'object' - ? `https://cdn.discordapp.com/emojis/${args.emojiOrName.id}` - : client.consts.regex.snowflake.test(args?.emojiOrName ?? '') - ? `https://cdn.discordapp.com/emojis/${args!.emojiOrName}` + args?.emoji instanceof URL + ? args.emoji.href + : typeof args?.emoji === 'object' + ? `https://cdn.discordapp.com/emojis/${args.emoji.id}` + : client.consts.regex.snowflake.test(args?.emoji ?? '') + ? `https://cdn.discordapp.com/emojis/${args!.emoji}` + : (args?.emoji ?? '').match(/https?:\/\//) + ? args?.emoji : undefined; if (image === undefined) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); - if (message.attachments.size && typeof args?.emojiOrName !== 'string') - return await message.util.reply(`${util.emojis.error} You cannot attach an image and provide an argument.`); - const emojiName = message.attachments.size - ? (args?.emojiOrName as string) ?? 'stolen_emoji' - : args?.emojiOrName instanceof URL - ? args?.name2 ?? 'stolen_emoji' - : typeof args?.emojiOrName === 'object' - ? args?.name2 ?? args.emojiOrName.name ?? 'stolen_emoji' - : 'stolen_emoji'; + const emojiName = + args.name ?? args?.emoji instanceof URL + ? args?.name ?? 'stolen_emoji' + : typeof args?.emoji === 'object' + ? args?.name ?? args.emoji.name ?? 'stolen_emoji' + : 'stolen_emoji'; const creationSuccess = await message .guild!.emojis.create(image, emojiName, { diff --git a/src/commands/utilities/suicide.ts b/src/commands/utilities/suicide.ts index 359f914..05f8d47 100644 --- a/src/commands/utilities/suicide.ts +++ b/src/commands/utilities/suicide.ts @@ -6,11 +6,9 @@ export default class TemplateCommand extends BushCommand { super('suicide', { aliases: ['suicide'], category: 'utilities', - description: { - content: 'Mental Health Resources. Credit to https://github.com/dexbiobot/Zeppelin.', - usage: ['suicide'], - examples: ['suicide'] - }, + description: 'Mental Health Resources. Credit to https://github.com/dexbiobot/Zeppelin.', + usage: ['suicide'], + examples: ['suicide'], slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], diff --git a/src/commands/utilities/uuid.ts b/src/commands/utilities/uuid.ts index 556a4de..e0f6b1c 100644 --- a/src/commands/utilities/uuid.ts +++ b/src/commands/utilities/uuid.ts @@ -5,41 +5,40 @@ export default class UuidCommand extends BushCommand { super('uuid', { aliases: ['uuid'], category: 'utilities', - description: { - content: "Find someone's minecraft uuid", - usage: ['uuid <ign>'], - examples: ['uuid ironm00n'] - }, + description: "Find someone's minecraft uuid", + usage: ['uuid <ign>'], + examples: ['uuid ironm00n'], args: [ { id: 'ign', + description: 'The ign to find the ign of.', customType: /\w{1,16}/im, - prompt: { - start: 'What ign would you like to find the uuid of?', - retry: '{error} Choose a valid ign.', - optional: false - } - } - ], - slash: true, - slashOptions: [ + readableType: 'ign', + prompt: 'What ign would you like to find the uuid of?', + retry: '{error} Choose a valid ign.', + slashType: 'STRING' + }, { - name: 'ign', - description: 'What ign would you like to find the uuid of?', - type: 'STRING', - required: false + id: 'dashed', + description: 'Include dashes in the uuid.', + match: 'flag', + flag: '--dashed', + prompt: 'Would you like to include dashes in the uuid?', + slashType: 'BOOLEAN', + optional: true } ], + slash: true, clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); } - public override async exec(message: BushMessage, { ign }: { ign: { match: any[]; matches: any[] } }) { + public override async exec(message: BushMessage, { ign, dashed }: { ign: { match: any[]; matches: any[] }; dashed: boolean }) { if (!ign) return await message.util.reply(`${util.emojis.error} Please enter a valid ign.`); const readableIGN = ign.match[0]; try { - const uuid = await util.findUUID(readableIGN); + const uuid = await util.mcUUID(readableIGN, dashed); return await message.util.reply(`The uuid for \`${readableIGN}\` is \`${uuid}\``); } catch (e) { return await message.util.reply(`${util.emojis.error} Could not find an uuid for \`${readableIGN}\`.`); diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index 4719da1..c934e2e 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.ts @@ -1,73 +1,54 @@ import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { MessageEmbed, type DMChannel, type NewsChannel, type Snowflake, type TextChannel } from 'discord.js'; +import { Message, MessageEmbed, type DMChannel, type NewsChannel, type Snowflake, type TextChannel } from 'discord.js'; export default class ViewRawCommand extends BushCommand { public constructor() { super('view-raw', { aliases: ['view-raw', 'vr'], category: 'utilities', - description: { - content: 'Shows raw information about a message.', - usage: ['viewraw <message id> <channel>'], - examples: ['viewraw 322862723090219008'] - }, + description: 'Shows raw information about a message.', + usage: ['viewraw <message id> <channel>'], + examples: ['viewraw 322862723090219008'], args: [ { id: 'message', - type: 'snowflake', - prompt: { - start: 'What message would you like to view?', - retry: '{error} Choose a valid message.', - optional: false - } + description: 'The message to view the raw content of.', + customType: util.arg.union('guildMessage', 'messageLink'), + readableType: 'guildMessage|messageLink', + prompt: 'What message would you like to view?', + retry: '{error} Choose a valid message.', + slashType: 'STRING' }, { id: 'channel', + description: 'The channel that the message is in.', type: 'channel', - prompt: { - start: 'What channel is the message in?', - retry: '{error} Choose a valid channel.', - optional: true - } + prompt: 'What channel is the message in?', + retry: '{error} Choose a valid channel.', + optional: true, + slashType: 'CHANNEL', + channelTypes: util.discordConstants.TextBasedChannelTypes }, { id: 'json', + description: 'Whether or not to view the raw JSON message data.', match: 'flag', - flag: '--json' + flag: '--json', + prompt: 'Would you like to view the raw JSON message data?', + slashType: 'BOOLEAN', + optional: true }, { id: 'js', + description: 'Whether or not to view the raw message data.', match: 'flag', - flag: '--js' + flag: '--js', + prompt: 'Would you like to view the raw message data?', + slashType: 'BOOLEAN', + optional: true } ], slash: true, - slashOptions: [ - { - name: 'message', - description: 'What message would you like to view?', - type: 'STRING', - required: true - }, - { - name: 'channel', - description: 'What channel is the message in?', - type: 'CHANNEL', - required: false - }, - { - name: 'json', - description: 'Would you like to view the raw JSON message data?', - type: 'BOOLEAN', - required: false - }, - { - name: 'js', - description: 'Would you like to view the raw message data?', - type: 'BOOLEAN', - required: false - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true), userPermissions: [] @@ -76,10 +57,13 @@ export default class ViewRawCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { message: Snowflake; channel: TextChannel | NewsChannel | DMChannel; json?: boolean; js: boolean } + args: { message: BushMessage | Snowflake; channel: TextChannel | NewsChannel | DMChannel; json?: boolean; js: boolean } ) { if (!args.channel) args.channel = (message.channel as TextChannel | NewsChannel | DMChannel)!; - const newMessage = await args.channel.messages.fetch(`${args.message}` as Snowflake).catch(() => null); + const newMessage = + args.message instanceof Message + ? args.message + : await args.channel.messages.fetch(`${args.message}` as Snowflake).catch(() => null); if (!newMessage) return await message.util.reply( `${util.emojis.error} There was an error fetching that message, make sure that is a valid id and if the message is not in this channel, please provide a channel.` diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index a457756..45cf77f 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -6,31 +6,21 @@ export default class WhoHasRoleCommand extends BushCommand { super('whoHasRole', { aliases: ['who-has-role', 'whr', 'dump'], category: 'utilities', - description: { - content: 'Allows you to view what users have a certain role.', - usage: ['who-has-role <role>'], - examples: ['who-has-role admin'] - }, + description: 'Allows you to view what users have a certain role.', + usage: ['who-has-role <role>'], + examples: ['who-has-role admin'], args: [ { id: 'role', + description: 'The role to find the users of.', type: 'role', - prompt: { - start: 'What role would you like to find the users of?', - retry: '{error} Pick a valid role.', - optional: false - } + prompt: 'What role would you like to find the users of?', + retry: '{error} Pick a valid role.', + optional: false, + slashType: 'ROLE' } ], slash: true, - slashOptions: [ - { - name: 'role', - description: 'What role would you like to find the users of?', - type: 'ROLE', - required: true - } - ], channel: 'guild', clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [], diff --git a/src/commands/utilities/wolframAlpha.ts b/src/commands/utilities/wolframAlpha.ts index de00620..34b2b90 100644 --- a/src/commands/utilities/wolframAlpha.ts +++ b/src/commands/utilities/wolframAlpha.ts @@ -7,43 +7,30 @@ export default class WolframAlphaCommand extends BushCommand { super('wolframAlpha', { aliases: ['wolfram-alpha', 'wolfram', 'alpha', 'wolf', 'wa'], category: 'utilities', - description: { - content: 'Queries Wolfram|Alpha for a result.', - usage: ['wolfram-alpha <expression>'], - examples: ['wolfram-alpha what is the population of france'] - }, + description: 'Queries Wolfram|Alpha for a result.', + usage: ['wolfram-alpha <expression>'], + examples: ['wolfram-alpha what is the population of france'], args: [ { id: 'image', + description: 'Whether to use the Simple API instead of the Short Answers API.', match: 'flag', - flag: '--image' + flag: '--image', + prompt: 'Would you like to use the Simple API instead of the Short Answers API?', + slashType: 'BOOLEAN', + optional: true }, { id: 'expression', + description: 'The expression to query the Wolfram|Alpha api for.', type: 'string', match: 'rest', - prompt: { - start: 'What would you like to look up?', - retry: '{error} Pick something to look up.', - optional: false - } + prompt: 'What would you like to look up?', + retry: '{error} Pick something to look up.', + slashType: 'STRING' } ], slash: true, - slashOptions: [ - { - name: 'expression', - description: 'What would you like to look up?', - type: 'STRING', - required: true - }, - { - name: 'image', - description: 'Would you like to use the Simple API instead of the Short Answers API?', - type: 'BOOLEAN', - required: false - } - ], clientPermissions: (m) => util.clientSendAndPermCheck(m), userPermissions: [] }); diff --git a/src/lib/common/Format.ts b/src/lib/common/Format.ts index b47be38..6cb6edc 100644 --- a/src/lib/common/Format.ts +++ b/src/lib/common/Format.ts @@ -1,5 +1,5 @@ import { type CodeBlockLang } from '#lib'; -import { Formatters, Util } from 'discord.js'; +import { EscapeMarkdownOptions, Formatters, Util } from 'discord.js'; /** * Formats and escapes content for formatting @@ -19,8 +19,8 @@ export class Format { public static codeBlock(language: CodeBlockLang, content: string): string; public static codeBlock(languageOrContent: string, content?: string): string { return typeof content === 'undefined' - ? Formatters.codeBlock(Util.escapeCodeBlock(languageOrContent)) - : Formatters.codeBlock(languageOrContent, Util.escapeCodeBlock(languageOrContent)); + ? Formatters.codeBlock(Util.escapeCodeBlock(`${languageOrContent}`)) + : Formatters.codeBlock(`${languageOrContent}`, Util.escapeCodeBlock(`${content}`)); } /** @@ -28,7 +28,7 @@ export class Format { * @param content The content to wrap. */ public static inlineCode(content: string): string { - return Formatters.inlineCode(Util.escapeInlineCode(content)); + return Formatters.inlineCode(Util.escapeInlineCode(`${content}`)); } /** @@ -36,7 +36,7 @@ export class Format { * @param content The content to wrap. */ public static italic(content: string): string { - return Formatters.italic(Util.escapeItalic(content)); + return Formatters.italic(Util.escapeItalic(`${content}`)); } /** @@ -44,7 +44,7 @@ export class Format { * @param content The content to wrap. */ public static bold(content: string): string { - return Formatters.bold(Util.escapeBold(content)); + return Formatters.bold(Util.escapeBold(`${content}`)); } /** @@ -52,7 +52,7 @@ export class Format { * @param content The content to wrap. */ public static underscore(content: string): string { - return Formatters.underscore(Util.escapeUnderline(content)); + return Formatters.underscore(Util.escapeUnderline(`${content}`)); } /** @@ -60,7 +60,7 @@ export class Format { * @param content The content to wrap. */ public static strikethrough(content: string): string { - return Formatters.strikethrough(Util.escapeStrikethrough(content)); + return Formatters.strikethrough(Util.escapeStrikethrough(`${content}`)); } /** @@ -68,6 +68,40 @@ export class Format { * @param content The content to wrap. */ public static spoiler(content: string): string { - return Formatters.spoiler(Util.escapeSpoiler(content)); + return Formatters.spoiler(Util.escapeSpoiler(`${content}`)); + } + + /** + * Escapes any Discord-flavour markdown in a string. + * @param text Content to escape + * @param options Options for escaping the markdown + */ + public static escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string { + return Util.escapeMarkdown(`${text}`, options); + } + + /** + * Formats input: makes it bold and escapes any other markdown + * @param text The input + */ + public static input(text: string): string { + return this.bold(this.escapeMarkdown(this.sanitizeWtlAndControl(`${text}`))); + } + + /** + * Formats input for logs: makes it highlighted + * @param text The input + */ + public static inputLog(text: string): string { + return `<<${this.sanitizeWtlAndControl(`${text}`)}>>`; + } + + /** + * Removes all characters in a string that are either control characters or change the direction of text etc. + * @param str The string you would like sanitized + */ + public static sanitizeWtlAndControl(str: string) { + // eslint-disable-next-line no-control-regex + return `${str}`.replace(/[\u0000-\u001F\u007F-\u009F\u200B]/g, ''); } } diff --git a/src/lib/common/util/Arg.ts b/src/lib/common/util/Arg.ts index ee9aee0..1982f4a 100644 --- a/src/lib/common/util/Arg.ts +++ b/src/lib/common/util/Arg.ts @@ -9,7 +9,11 @@ export class Arg { * @param message - Message that called the command. * @param phrase - Phrase to process. */ - public static cast(type: BushArgumentType, message: BushMessage | BushSlashMessage, phrase: string): Promise<any> { + public static cast( + type: BushArgumentType | ArgumentTypeCaster, + message: BushMessage | BushSlashMessage, + phrase: string + ): Promise<any> { return Argument.cast(type, client.commandHandler.resolver, message as Message, phrase); } diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index 3339a62..45ad7ca 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -41,6 +41,7 @@ import { discordEmoji } from '../../../arguments/discordEmoji.js'; import { duration } from '../../../arguments/duration.js'; import { durationSeconds } from '../../../arguments/durationSeconds.js'; import { globalUser } from '../../../arguments/globalUser.js'; +import { messageLink } from '../../../arguments/messageLink.js'; import { permission } from '../../../arguments/permission.js'; import { roleWithDuration } from '../../../arguments/roleWithDuration.js'; import { snowflake } from '../../../arguments/snowflake.js'; @@ -289,7 +290,8 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re roleWithDuration, abbreviatedNumber, durationSeconds, - globalUser + globalUser, + messageLink }); this.sentry = Sentry; diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index aa64562..889cd6e 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -18,9 +18,11 @@ import { } from '#lib'; import { humanizeDuration } from '@notenoughupdates/humanize-duration'; import { exec } from 'child_process'; +import deepLock from 'deep-lock'; import { ClientUtil, Util as AkairoUtil } from 'discord-akairo'; import { APIMessage } from 'discord-api-types'; import { + Constants as DiscordConstants, GuildMember, Message, MessageEmbed, @@ -37,7 +39,6 @@ import { } from 'discord.js'; import got from 'got'; import _ from 'lodash'; -import moment from 'moment'; import { inspect, promisify } from 'util'; import CommandErrorListener from '../../../listeners/commands/commandError.js'; import { Format } from '../../common/Format.js'; @@ -105,10 +106,7 @@ export class BushClientUtil extends ClientUtil { * @param content The text to post * @returns The url of the posted text */ - public async haste( - content: string, - substr = false - ): Promise<{ url?: string; error?: 'content too long' | 'substr' | 'unable to post' }> { + public async haste(content: string, substr = false): Promise<HasteResults> { let isSubstr = false; if (content.length > 400_000 && !substr) { void this.handleError('haste', new Error(`content over 400,000 characters (${content.length.toLocaleString()})`)); @@ -119,7 +117,7 @@ export class BushClientUtil extends ClientUtil { } for (const url of this.#hasteURLs) { try { - const res: hastebinRes = await got.post(`${url}/documents`, { body: content }).json(); + const res: HastebinRes = await got.post(`${url}/documents`, { body: content }).json(); return { url: `${url}/${res.key}`, error: isSubstr ? 'substr' : undefined }; } catch { void client.console.error('haste', `Unable to upload haste to ${url}`); @@ -197,7 +195,10 @@ export class BushClientUtil extends ClientUtil { } /** - * A simple utility to create and embed with the needed style for the bot + * A simple utility to create and embed with the needed style for the bot. + * @param color The color to set the embed to. + * @param author The author to set the embed to. + * @returns The generated embed. */ public createEmbed(color?: ColorResolvable, author?: User | GuildMember): MessageEmbed { if (author instanceof GuildMember) { @@ -214,15 +215,25 @@ export class BushClientUtil extends ClientUtil { return embed; } - public async mcUUID(username: string): Promise<string> { - const apiRes = (await got.get(`https://api.ashcon.app/mojang/v2/user/${username}`).json()) as uuidRes; - return apiRes.uuid.replace(/-/g, ''); + /** + * Fetches a user's uuid from the mojang api. + * @param username The username to get the uuid of. + * @returns The the uuid of the user. + */ + public async mcUUID(username: string, dashed = false): Promise<string> { + const apiRes = (await got.get(`https://api.ashcon.app/mojang/v2/user/${username}`).json()) as UuidRes; + return dashed ? apiRes.uuid : apiRes.uuid.replace(/-/g, ''); } /** * Surrounds text in a code block with the specified language and puts it in a hastebin if its too long. * * Embed Description Limit = 4096 characters * * Embed Field Limit = 1024 characters + * @param code The content of the code block. + * @param length The maximum length of the code block. + * @param language The language of the code. + * @param substr Whether or not to substring the code if it is too long. + * @returns The generated code block */ public async codeblock(code: string, length: number, language: CodeBlockLang | '' = '', substr = false): Promise<string> { let hasteOut = ''; @@ -250,15 +261,21 @@ export class BushClientUtil extends ClientUtil { } /** - * Uses {@link inspect} with custom defaults - * @param object - The object you would like to inspect - * @param options - The options you would like to use to inspect the object + * Uses {@link inspect} with custom defaults. + * @param object - The object you would like to inspect. + * @param options - The options you would like to use to inspect the object. + * @returns The inspected object. */ public inspect(object: any, options?: BushInspectOptions): string { const optionsWithDefaults = this.getDefaultInspectOptions(options); return inspect(object, optionsWithDefaults); } + /** + * Generate defaults for {@link inspect}. + * @param options The options to create defaults with. + * @returns The default options combined with the specified options. + */ private getDefaultInspectOptions(options?: BushInspectOptions): BushInspectOptions { const { showHidden = false, @@ -288,7 +305,12 @@ export class BushClientUtil extends ClientUtil { }; } - #mapCredential(old: string): string { + /** + * Maps the key of a credential with a readable version when redacting. + * @param key The key of the credential. + * @returns The readable version of the key or the original key if there isn't a mapping. + */ + #mapCredential(key: string): string { const mapping = { token: 'Main Token', devToken: 'Dev Token', @@ -297,12 +319,13 @@ export class BushClientUtil extends ClientUtil { wolframAlphaAppId: 'Wolfram|Alpha App ID', dbPassword: 'Database Password' }; - return mapping[old as keyof typeof mapping] || old; + return mapping[key as keyof typeof mapping] || key; } /** - * Redacts credentials from a string - * @param text - The text to redact credentials from + * Redacts credentials from a string. + * @param text The text to redact credentials from. + * @returns The redacted text. */ public redact(text: string) { for (const credentialName in { ...client.config.credentials, dbPassword: client.config.db.password }) { @@ -321,8 +344,13 @@ export class BushClientUtil extends ClientUtil { } /** - * Takes an any value, inspects it, redacts credentials and puts it in a codeblock - * (and uploads to hast if the content is too long) + * Takes an any value, inspects it, redacts credentials, and puts it in a codeblock + * (and uploads to hast if the content is too long). + * @param input The object to be inspect, redacted, and put into a codeblock. + * @param language The language to make the codeblock. + * @param inspectOptions The options for {@link BushClientUtil.inspect}. + * @param length The maximum length that the codeblock can be. + * @returns The generated codeblock. */ public async inspectCleanRedactCodeblock( input: any, @@ -338,17 +366,35 @@ export class BushClientUtil extends ClientUtil { return this.codeblock(input, length, language, true); } - public async inspectCleanRedactHaste(input: any, inspectOptions?: BushInspectOptions) { + /** + * Takes an any value, inspects it, redacts credentials, and uploads it to haste. + * @param input The object to be inspect, redacted, and upload. + * @param inspectOptions The options for {@link BushClientUtil.inspect}. + * @returns The {@link HasteResults}. + */ + public async inspectCleanRedactHaste(input: any, inspectOptions?: BushInspectOptions): Promise<HasteResults> { input = typeof input !== 'string' ? this.inspect(input, inspectOptions ?? undefined) : input; input = this.redact(input); return this.haste(input, true); } - public inspectAndRedact(input: any, inspectOptions?: BushInspectOptions) { + /** + * Takes an any value, inspects it and redacts credentials. + * @param input The object to be inspect and redacted. + * @param inspectOptions The options for {@link BushClientUtil.inspect}. + * @returns The redacted and inspected object. + */ + public inspectAndRedact(input: any, inspectOptions?: BushInspectOptions): string { input = typeof input !== 'string' ? this.inspect(input, inspectOptions ?? undefined) : input; return this.redact(input); } + /** + * Responds to a slash command interaction. + * @param interaction The interaction to respond to. + * @param responseOptions The options for the response. + * @returns The message sent. + */ public async slashRespond( interaction: CommandInteraction, responseOptions: BushSlashSendMessageType | BushSlashEditMessageType @@ -364,7 +410,8 @@ export class BushClientUtil extends ClientUtil { } /** - * Gets a a configured channel as a TextChannel + * Gets a a configured channel as a TextChannel. + * @channel The channel to retrieve. */ public async getConfigChannel(channel: keyof typeof client['config']['channels']): Promise<TextChannel> { return (await client.channels.fetch(client.config.channels[channel])) as unknown as TextChannel; @@ -375,7 +422,7 @@ export class BushClientUtil extends ClientUtil { * @param array The array to combine. * @param conjunction The conjunction to use. * @param ifEmpty What to return if the array is empty. - * @returns The combined elements or `ifEmpty` + * @returns The combined elements or `ifEmpty`. * * @example * const permissions = oxford(['ADMINISTRATOR', 'SEND_MESSAGES', 'MANAGE_MESSAGES'], 'and', 'none'); @@ -391,10 +438,16 @@ export class BushClientUtil extends ClientUtil { return array.join(', '); } - public async insertOrRemoveFromGlobal( + /** + * Add or remove an element from an array stored in the Globals database. + * @param action Either `add` or `remove` an element. + * @param key The key of the element in the global cache to update. + * @param value The value to add/remove from the array. + */ + public async insertOrRemoveFromGlobal<K extends keyof typeof client['cache']['global']>( action: 'add' | 'remove', - key: keyof typeof client['cache']['global'], - value: any + key: K, + value: typeof client['cache']['global'][K][0] ): Promise<Global | void> { const row = (await Global.findByPk(client.config.environment)) ?? (await Global.create({ environment: client.config.environment })); @@ -406,7 +459,26 @@ export class BushClientUtil extends ClientUtil { } /** + * Updates an element in the Globals database. + * @param key The key in the global cache to update. + * @param value The value to set the key to. + */ + public async setGlobal<K extends keyof typeof client['cache']['global']>( + key: K, + value: typeof client['cache']['global'][K] + ): Promise<Global | void> { + const row = + (await Global.findByPk(client.config.environment)) ?? (await Global.create({ environment: client.config.environment })); + row[key] = value; + client.cache.global[key] = value; + return await row.save().catch((e) => this.handleError('setGlobal', e)); + } + + /** * Add or remove an item from an array. All duplicates will be removed. + * @param action Either `add` or `remove` an element. + * @param array The array to add/remove an element from. + * @param value The element to add/remove from the array. */ public addOrRemoveFromArray<T>(action: 'add' | 'remove', array: T[], value: T): T[] { const set = new Set(array); @@ -416,15 +488,21 @@ export class BushClientUtil extends ClientUtil { /** * Surrounds a string to the begging an end of each element in an array. - * @param array - The array you want to surround. - * @param surroundChar1 - The character placed in the beginning of the element. - * @param surroundChar2 - The character placed in the end of the element. Defaults to `surroundChar1`. + * @param array The array you want to surround. + * @param surroundChar1 The character placed in the beginning of the element. + * @param surroundChar2 The character placed in the end of the element. Defaults to `surroundChar1`. */ public surroundArray(array: string[], surroundChar1: string, surroundChar2?: string): string[] { return array.map((a) => `${surroundChar1}${a}${surroundChar2 ?? surroundChar1}`); } - public parseDuration(content: string, remove = true): { duration: number | null; contentWithoutTime: string | null } { + /** + * Gets the duration from a specified string. + * @param content The string to look for a duration in. + * @param remove Whether or not to remove the duration from the original string. + * @returns The {@link ParsedDuration}. + */ + public parseDuration(content: string, remove = true): ParsedDuration { if (!content) return { duration: 0, contentWithoutTime: null }; // eslint-disable-next-line prefer-const @@ -434,10 +512,11 @@ export class BushClientUtil extends ClientUtil { let contentWithoutTime = ` ${content}`; for (const unit in BushConstants.TimeUnits) { - const regex = BushConstants.TimeUnits[unit].match; + const regex = BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].match; const match = regex.exec(contentWithoutTime); const value = Number(match?.groups?.[unit]); - if (!isNaN(value)) (duration as unknown as number) += value * BushConstants.TimeUnits[unit].value; + if (!isNaN(value)) + (duration as unknown as number) += value * BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].value; if (remove) contentWithoutTime = contentWithoutTime.replace(regex, ''); } @@ -446,16 +525,33 @@ export class BushClientUtil extends ClientUtil { return { duration, contentWithoutTime }; } + /** + * Converts a duration in milliseconds to a human readable form. + * @param duration The duration in milliseconds to convert. + * @param largest The maximum number of units to display for the duration. + * @returns A humanized string of the duration. + */ public humanizeDuration(duration: number, largest?: number): string { if (largest) return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2, largest })!; else return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2 })!; } + /** + * Creates a formatted relative timestamp from a duration in milliseconds. + * @param duration The duration in milliseconds. + * @returns The formatted relative timestamp. + */ public timestampDuration(duration: number): string { - return `<t:${Math.round(duration / 1000)}:R>`; + return `<t:${Math.round(new Date().getTime() / 1_000 + duration / 1_000)}:R>`; } /** + * Creates a timestamp from a date. + * @param date The date to create a timestamp from. + * @param style The style of the timestamp. + * @returns The formatted timestamp. + * + * @see * **Styles:** * - **t**: Short Time * - **T**: Long Time @@ -470,33 +566,24 @@ export class BushClientUtil extends ClientUtil { style: 't' | 'T' | 'd' | 'D' | 'f' | 'F' | 'R' = 'f' ): D extends Date ? string : undefined { if (!date) return date as unknown as D extends Date ? string : undefined; - return `<t:${Math.round(date.getTime() / 1000)}:${style}>` as unknown as D extends Date ? string : undefined; - } - - public dateDelta(date: Date, largest?: number) { - return this.humanizeDuration(moment(date).diff(moment()), largest ?? 3); + return `<t:${Math.round(date.getTime() / 1_000)}:${style}>` as unknown as D extends Date ? string : undefined; } - public async findUUID(player: string): Promise<string> { - try { - const raw = await got.get(`https://api.ashcon.app/mojang/v2/user/${player}`); - let profile: MojangProfile; - if (raw.statusCode == 200) { - profile = JSON.parse(raw.body); - } else { - throw new Error('invalid player'); - } - - if (raw.statusCode == 200 && profile && profile.uuid) { - return profile.uuid.replace(/-/g, ''); - } else { - throw new Error(`Could not fetch the uuid for ${player}.`); - } - } catch (e) { - throw new Error('An error has occurred.'); - } + /** + * Creates a human readable representation between a date and the current time. + * @param date The date to be compared with the current time. + * @param largest The maximum number of units to display for the duration. + * @returns A humanized string of the delta. + */ + public dateDelta(date: Date, largest?: number): string { + return this.humanizeDuration(new Date().getTime() - date.getTime(), largest ?? 3); } + /** + * Convert a hex code to an rbg value. + * @param hex The hex code to convert. + * @returns The rbg value. + */ public hexToRgb(hex: string): string { const arrBuff = new ArrayBuffer(4); const vw = new DataView(arrBuff); @@ -507,20 +594,34 @@ export class BushClientUtil extends ClientUtil { } /* eslint-disable @typescript-eslint/no-unused-vars */ - public async lockdownChannel(options: { channel: BushTextChannel | BushNewsChannel; moderator: BushUserResolvable }) {} + public async lockdownChannel(options: { channel: BushTextChannel | BushNewsChannel; moderator: BushUserResolvable }) { + // todo: implement lockdowns + } /* eslint-enable @typescript-eslint/no-unused-vars */ + /** + * Capitalize the first letter of a string. + * @param string The string to capitalize the first letter of. + * @returns The string with the first letter capitalized. + */ public capitalizeFirstLetter(string: string): string { return string.charAt(0)?.toUpperCase() + string.slice(1); } /** * Wait an amount in seconds. + * @param s The number of seconds to wait + * @returns A promise that resolves after the specified amount of seconds */ public async sleep(s: number) { return new Promise((resolve) => setTimeout(resolve, s * 1000)); } + /** + * Send a message in the error logging channel and console for an error. + * @param context + * @param error + */ public async handleError(context: string, error: Error) { await client.console.error(_.camelCase(context), `An error occurred:\n${error?.stack ?? (error as any)}`, false); await client.console.channelError({ @@ -553,24 +654,24 @@ export class BushClientUtil extends ClientUtil { if (!apiRes) return undefined; if (!apiRes.pronouns) throw new Error('apiRes.pronouns is undefined'); - return client.constants.pronounMapping[apiRes.pronouns]; + return client.constants.pronounMapping[apiRes.pronouns!]!; } - // modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class - // answer by Bruno Grieder - public getMethods(_obj: any): string { + public getMethods(obj: Record<string, any>): string { + // modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class + // answer by Bruno Grieder let props: string[] = []; - let obj: any = new Object(_obj); + let obj_: Record<string, any> = new Object(obj); do { - const l = Object.getOwnPropertyNames(obj) - .concat(Object.getOwnPropertySymbols(obj).map((s) => s.toString())) + const l = Object.getOwnPropertyNames(obj_) + .concat(Object.getOwnPropertySymbols(obj_).map((s) => s.toString())) .sort() .filter( (p, i, arr) => - typeof Object.getOwnPropertyDescriptor(obj, p)?.['get'] !== 'function' && // ignore getters - typeof Object.getOwnPropertyDescriptor(obj, p)?.['set'] !== 'function' && // ignore setters - typeof obj[p] === 'function' && //only the methods + typeof Object.getOwnPropertyDescriptor(obj_, p)?.['get'] !== 'function' && // ignore getters + typeof Object.getOwnPropertyDescriptor(obj_, p)?.['set'] !== 'function' && // ignore setters + typeof obj_[p] === 'function' && //only the methods p !== 'constructor' && //not the constructor (i == 0 || p !== arr[i - 1]) && //not overriding in this prototype props.indexOf(p) === -1 //not overridden in a child @@ -580,10 +681,10 @@ export class BushClientUtil extends ClientUtil { props = props.concat( l.map( (p) => - `${obj[p] && obj[p][Symbol.toStringTag] === 'AsyncFunction' ? 'async ' : ''}function ${p}(${ - reg.exec(obj[p].toString())?.[1] + `${obj_[p] && obj_[p][Symbol.toStringTag] === 'AsyncFunction' ? 'async ' : ''}function ${p}(${ + reg.exec(obj_[p].toString())?.[1] ? reg - .exec(obj[p].toString())?.[1] + .exec(obj_[p].toString())?.[1] .split(', ') .map((arg) => arg.split('=')[0].trim()) .join(', ') @@ -592,21 +693,13 @@ export class BushClientUtil extends ClientUtil { ) ); } while ( - (obj = Object.getPrototypeOf(obj)) && //walk-up the prototype chain - Object.getPrototypeOf(obj) //not the the Object prototype methods (hasOwnProperty, etc...) + (obj_ = Object.getPrototypeOf(obj_)) && //walk-up the prototype chain + Object.getPrototypeOf(obj_) //not the the Object prototype methods (hasOwnProperty, etc...) ); return props.join('\n'); } - /** - * Removes all characters in a string that are either control characters or change the direction of text etc. - */ - public sanitizeWtlAndControl(str: string) { - // eslint-disable-next-line no-control-regex - return str.replace(/[\u0000-\u001F\u007F-\u009F\u200B]/g, ''); - } - public async uploadImageToImgur(image: string) { const clientId = this.client.config.credentials.imgurClientId; @@ -667,6 +760,14 @@ export class BushClientUtil extends ClientUtil { : message.util.parsed?.prefix ?? client.config.prefix; } + public get deepFreeze() { + return deepLock; + } + + public static get deepFreeze() { + return deepLock; + } + public get arg() { return Arg; } @@ -686,6 +787,13 @@ export class BushClientUtil extends ClientUtil { } /** + * Discord.js's Util constants + */ + public get discordConstants() { + return DiscordConstants + } + + /** * discord-akairo's Util class */ public get akairo() { @@ -693,11 +801,11 @@ export class BushClientUtil extends ClientUtil { } } -interface hastebinRes { +interface HastebinRes { key: string; } -export interface uuidRes { +export interface UuidRes { uuid: string; username: string; username_history?: { username: string }[] | null; @@ -716,7 +824,12 @@ export interface uuidRes { created_at: string; } -interface MojangProfile { - username: string; - uuid: string; +export interface HasteResults { + url?: string; + error?: 'content too long' | 'substr' | 'unable to post'; +} + +export interface ParsedDuration { + duration: number | null; + contentWithoutTime: string | null; } diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 1494aee..8872831 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -1,13 +1,23 @@ import { type BushClient, type BushCommandHandler, type BushMessage, type BushSlashMessage } from '#lib'; import { + AkairoApplicationCommandAutocompleteOption, + AkairoApplicationCommandChannelOptionData, + AkairoApplicationCommandChoicesData, + AkairoApplicationCommandNonOptionsData, + AkairoApplicationCommandNumericOptionData, + AkairoApplicationCommandOptionData, + AkairoApplicationCommandSubCommandData, + AkairoApplicationCommandSubGroupData, Command, + MissingPermissionSupplier, + SlashOption, + SlashResolveTypes, type ArgumentOptions, - type ArgumentPromptOptions, type ArgumentTypeCaster, type CommandOptions } from 'discord-akairo'; import { BaseArgumentType } from 'discord-akairo/dist/src/struct/commands/arguments/Argument'; -import { type PermissionResolvable, type Snowflake } from 'discord.js'; +import { ApplicationCommandOptionChoice, type PermissionResolvable, type Snowflake } from 'discord.js'; export type BaseBushArgumentType = | BaseArgumentType @@ -18,14 +28,76 @@ export type BaseBushArgumentType = | 'discordEmoji' | 'roleWithDuration' | 'abbreviatedNumber' - | 'globalUser'; + | 'globalUser' + | 'messageLink' export type BushArgumentType = BaseBushArgumentType | RegExp; -interface BaseBushArgumentOptions extends Omit<ArgumentOptions, 'type'> { +interface BaseBushArgumentOptions extends Omit<ArgumentOptions, 'type' | 'prompt'> { id: string; - description?: string; - prompt?: ArgumentPromptOptions; + description: string; + + /** + * The message sent for the prompt and the slash command description. + */ + prompt?: string; + + /** + * The message set for the retry prompt. + */ + retry?: string; + + /** + * Whether or not the argument is optional. + */ + optional?: boolean; + + /** + * The type used for slash commands. Set to false to disable this argument for slash commands. + */ + slashType: AkairoApplicationCommandOptionData['type'] | false; + + /** + * Allows you to get a discord resolved object + * + * ex. get the resolved member object when the type is `USER` + */ + slashResolve?: SlashResolveTypes; + + /** + * The choices of the option for the user to pick from + */ + choices?: ApplicationCommandOptionChoice[]; + + /** + * Whether the option is an autocomplete option + */ + autocomplete?: boolean; + + /** + * When the option type is channel, the allowed types of channels that can be selected + */ + channelTypes?: AkairoApplicationCommandChannelOptionData['channelTypes']; + + /** + * The minimum value for an `INTEGER` or `NUMBER` option + */ + minValue?: number; + + /** + * The maximum value for an `INTEGER` or `NUMBER` option + */ + maxValue?: number; + + /** + * Restrict this argument to only slash or only text commands. + */ + only?: 'slash' | 'text'; + + /** + * Readable type for the help command. + */ + readableType?: string; } export interface BushArgumentOptions extends BaseBushArgumentOptions { @@ -93,7 +165,7 @@ export interface CustomBushArgumentOptions extends BaseBushArgumentOptions { export type BushMissingPermissionSupplier = (message: BushMessage | BushSlashMessage) => Promise<any> | any; -export interface BushCommandOptions extends Omit<CommandOptions, 'userPermissions' | 'clientPermissions'> { +export interface BaseBushCommandOptions extends Omit<CommandOptions, 'userPermissions' | 'clientPermissions' | 'args'> { /** * Whether the command is hidden from the help command. */ @@ -109,12 +181,24 @@ export interface BushCommandOptions extends Omit<CommandOptions, 'userPermission */ restrictedGuilds?: Snowflake[]; - description: { - content: string; - usage: string[]; - examples: string[]; - }; + /** + * The description of the command. + */ + description: string; + + /** + * Show how to use the command. + */ + usage: string[]; + /** + * Examples for how to use the command. + */ + examples: string[]; + + /** + * The arguments for the command. + */ args?: BushArgumentOptions[] & CustomBushArgumentOptions[]; category: string; @@ -138,6 +222,33 @@ export interface BushCommandOptions extends Omit<CommandOptions, 'userPermission * Permissions required by the user to run this command. */ userPermissions: PermissionResolvable | PermissionResolvable[] | BushMissingPermissionSupplier; + + /** + * Restrict this argument to owners + */ + ownerOnly?: boolean; + + /** + * Restrict this argument to super users. + */ + superUserOnly?: boolean; + + /** + * Use instead of {@link BaseBushCommandOptions.args} when using argument generators or custom slashOptions + */ + helpArgs?: BushArgumentOptions[]; +} + +export type BushCommandOptions = Omit<BaseBushCommandOptions, 'helpArgs'> | Omit<BaseBushCommandOptions, 'args'>; + +export interface ArgsInfo { + id: string; + description: string; + optional?: boolean; + slashType: AkairoApplicationCommandOptionData['type'] | false; + slashResolve?: SlashResolveTypes; + only?: 'slash' | 'text'; + type: string; } export class BushCommand extends Command { @@ -145,18 +256,29 @@ export class BushCommand extends Command { public declare handler: BushCommandHandler; - public declare description: { - content: string; - usage: string[]; - examples: string[]; - }; + public declare description: string; + + /** + * Show how to use the command. + */ + public usage: string[]; + + /** + * Examples for how to use the command. + */ + public examples: string[]; /** - * The command's options + * The options sent to the constructor */ public options: BushCommandOptions; /** + * The options sent to the super call + */ + public parsedOptions: CommandOptions; + + /** * The channels the command is limited to run in. */ public restrictedChannels: Snowflake[] | undefined; @@ -181,24 +303,138 @@ export class BushCommand extends Command { */ public bypassChannelBlacklist: boolean; + /** + * Info about the arguments for the help command. + */ + public argsInfo?: ArgsInfo[]; + public constructor(id: string, options: BushCommandOptions) { - if (options.args && typeof options.args !== 'function') { - options.args.forEach((_, index: number) => { - if ('customType' in options.args![index]) { - // @ts-expect-error: shut - if (!options.args[index]['type']) options.args[index]['type'] = options.args[index]['customType']; - delete options.args![index]['customType']; + const options_ = options as BaseBushCommandOptions; + + if (options_.args && typeof options_.args !== 'function') { + options_.args.forEach((_, index: number) => { + if ('customType' in (options_.args?.[index] ?? {})) { + if (!options_.args![index]['type']) options_.args![index]['type'] = options_.args![index]['customType']! as any; + delete options_.args![index]['customType']; } }); } - // incompatible options - super(id, options as any); - this.options = options; - this.hidden = Boolean(options.hidden); - this.restrictedChannels = options.restrictedChannels; - this.restrictedGuilds = options.restrictedGuilds; - this.pseudo = Boolean(options.pseudo); - this.bypassChannelBlacklist = Boolean(options.bypassChannelBlacklist); + + const newOptions: CommandOptions = {}; + if ('aliases' in options_) newOptions.aliases = options_.aliases; + if ('args' in options_ && typeof options_.args === 'object') { + const newTextArgs: ArgumentOptions[] = []; + const newSlashArgs: SlashOption[] = []; + for (const arg of options_.args) { + if (arg.only !== 'slash' && !options_.slashOnly) { + const newArg: ArgumentOptions = {}; + if ('default' in arg) newArg.default = arg.default; + if ('description' in arg) newArg.description = arg.description; + if ('flag' in arg) newArg.flag = arg.flag; + if ('id' in arg) newArg.id = arg.id; + if ('index' in arg) newArg.index = arg.index; + if ('limit' in arg) newArg.limit = arg.limit; + if ('match' in arg) newArg.match = arg.match; + if ('modifyOtherwise' in arg) newArg.modifyOtherwise = arg.modifyOtherwise; + if ('multipleFlags' in arg) newArg.multipleFlags = arg.multipleFlags; + if ('otherwise' in arg) newArg.otherwise = arg.otherwise; + if ('prompt' in arg || 'retry' in arg || 'optional' in arg) { + newArg.prompt = {}; + if ('prompt' in arg) newArg.prompt.start = arg.prompt; + if ('retry' in arg) newArg.prompt.retry = arg.retry; + if ('optional' in arg) newArg.prompt.optional = arg.optional; + } + if ('type' in arg) newArg.type = arg.type; + if ('unordered' in arg) newArg.unordered = arg.unordered; + newTextArgs.push(newArg); + } + if ( + arg.only !== 'text' && + !('slashOptions' in options_) && + (options_.slash || options_.slashOnly) && + arg.slashType !== false + ) { + const newArg: { + [key in SlashOptionKeys]?: any; + } = { + name: arg.id, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + description: arg.prompt || arg.description || 'No description provided.', + type: arg.slashType + }; + if ('slashResolve' in arg) newArg.resolve = arg.slashResolve; + if ('autocomplete' in arg) newArg.autocomplete = arg.autocomplete; + if ('channelTypes' in arg) newArg.channelTypes = arg.channelTypes; + if ('choices' in arg) newArg.choices = arg.choices; + if ('minValue' in arg) newArg.minValue = arg.minValue; + if ('maxValue' in arg) newArg.maxValue = arg.maxValue; + newArg.required = 'optional' in arg ? !arg.optional : true; + newSlashArgs.push(newArg as SlashOption); + } + } + newOptions.args = newTextArgs; + newOptions.slashOptions = options_.slashOptions ?? newSlashArgs; + } + type perm = PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier; + + if ('argumentDefaults' in options_) newOptions.argumentDefaults = options_.argumentDefaults; + if ('before' in options_) newOptions.before = options_.before; + if ('channel' in options_) newOptions.channel = options_.channel; + if ('clientPermissions' in options_) newOptions.clientPermissions = options_.clientPermissions as perm; + if ('condition' in options_) newOptions.condition = options_.condition; + if ('cooldown' in options_) newOptions.cooldown = options_.cooldown; + if ('description' in options_) newOptions.description = options_.description; + if ('editable' in options_) newOptions.editable = options_.editable; + if ('flags' in options_) newOptions.flags = options_.flags; + if ('ignoreCooldown' in options_) newOptions.ignoreCooldown = options_.ignoreCooldown; + if ('ignorePermissions' in options_) newOptions.ignorePermissions = options_.ignorePermissions; + if ('lock' in options_) newOptions.lock = options_.lock; + if ('onlyNsfw' in options_) newOptions.onlyNsfw = options_.onlyNsfw; + if ('optionFlags' in options_) newOptions.optionFlags = options_.optionFlags; + if ('ownerOnly' in options_) newOptions.ownerOnly = options_.ownerOnly; + if ('prefix' in options_) newOptions.prefix = options_.prefix; + if ('quoted' in options_) newOptions.quoted = options_.quoted; + if ('ratelimit' in options_) newOptions.ratelimit = options_.ratelimit; + if ('regex' in options_) newOptions.regex = options_.regex; + if ('separator' in options_) newOptions.separator = options_.separator; + if ('slash' in options_) newOptions.slash = options_.slash; + if ('slashEphemeral' in options_) newOptions.slashEphemeral = options_.slashEphemeral; + if ('slashGuilds' in options_) newOptions.slashGuilds = options_.slashGuilds; + if ('slashOptions' in options_) newOptions.slashOptions = options_.slashOptions; + if ('superUserOnly' in options_) newOptions.superUserOnly = options_.superUserOnly; + if ('typing' in options_) newOptions.typing = options_.typing; + if ('userPermissions' in options_) newOptions.userPermissions = options_.userPermissions as perm; + + super(id, newOptions); + + if (options_.args || options_.helpArgs) { + const argsInfo: ArgsInfo[] = []; + + for (const arg of (options_.args ?? options_.helpArgs)!) { + argsInfo.push({ + id: arg.id, + description: arg.description, + optional: arg.optional, + slashType: arg.slashType, + slashResolve: arg.slashResolve, + only: arg.only, + type: (arg.readableType ?? arg.type) as string + }); + } + + this.argsInfo = argsInfo; + } + + this.description = options_.description; + this.usage = options_.usage; + this.examples = options_.examples; + this.options = options_; + this.parsedOptions = newOptions; + this.hidden = !!options_.hidden; + this.restrictedChannels = options_.restrictedChannels; + this.restrictedGuilds = options_.restrictedGuilds; + this.pseudo = !!options_.pseudo; + this.bypassChannelBlacklist = !!options_.bypassChannelBlacklist; } } @@ -206,3 +442,12 @@ export interface BushCommand { exec(message: BushMessage, args: any): any; exec(message: BushMessage | BushSlashMessage, args: any): any; } + +type SlashOptionKeys = + | keyof AkairoApplicationCommandSubGroupData + | keyof AkairoApplicationCommandNonOptionsData + | keyof AkairoApplicationCommandChannelOptionData + | keyof AkairoApplicationCommandChoicesData + | keyof AkairoApplicationCommandAutocompleteOption + | keyof AkairoApplicationCommandNumericOptionData + | keyof AkairoApplicationCommandSubCommandData; diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index e3b39d3..9c272ff 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -9,8 +9,9 @@ import type { GuildLogType, GuildModel } from '#lib'; -import { Guild, type MessageOptions, type UserResolvable } from 'discord.js'; +import { Guild, MessagePayload, type MessageOptions, type UserResolvable } from 'discord.js'; import type { RawGuildData } from 'discord.js/typings/rawDataTypes'; +import _ from 'lodash'; import { Moderation } from '../../common/Moderation.js'; import { Guild as GuildDB } from '../../models/Guild.js'; import { ModLogType } from '../../models/ModLog.js'; @@ -24,29 +25,52 @@ export class BushGuild extends Guild { super(client, data); } + /** + * Checks if the guild has a certain custom feature. + * @param feature The feature to check for + */ public async hasFeature(feature: GuildFeatures): Promise<boolean> { const features = await this.getSetting('enabledFeatures'); return features.includes(feature); } + /** + * Adds a custom feature to the guild. + * @param feature The feature to add + * @param moderator The moderator responsible for adding a feature + */ public async addFeature(feature: GuildFeatures, moderator?: BushGuildMember): Promise<GuildModel['enabledFeatures']> { const features = await this.getSetting('enabledFeatures'); const newFeatures = util.addOrRemoveFromArray('add', features, feature); return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; } + /** + * Removes a custom feature from the guild. + * @param feature The feature to remove + * @param moderator The moderator responsible for removing a feature + */ public async removeFeature(feature: GuildFeatures, moderator?: BushGuildMember): Promise<GuildModel['enabledFeatures']> { const features = await this.getSetting('enabledFeatures'); const newFeatures = util.addOrRemoveFromArray('remove', features, feature); return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; } + /** + * Makes a custom feature the opposite of what it was before + * @param feature The feature to toggle + * @param moderator The moderator responsible for toggling a feature + */ public async toggleFeature(feature: GuildFeatures, moderator?: BushGuildMember): Promise<GuildModel['enabledFeatures']> { return (await this.hasFeature(feature)) ? await this.removeFeature(feature, moderator) : await this.addFeature(feature, moderator); } + /** + * Fetches a custom setting for the guild + * @param setting The setting to get + */ public async getSetting<K extends keyof GuildModel>(setting: K): Promise<GuildModel[K]> { return ( client.cache.guilds.get(this.id)?.[setting] ?? @@ -54,6 +78,12 @@ export class BushGuild extends Guild { ); } + /** + * Sets a custom setting for the guild + * @param setting The setting to change + * @param value The value to change the setting to + * @param moderator The moderator to responsible for changing the setting + */ public async setSetting<K extends Exclude<keyof GuildModel, 'id'>>( setting: K, value: GuildDB[K], @@ -208,13 +238,25 @@ export class BushGuild extends Guild { } /** - * Sends a message to the guild's specified logging channel. + * Sends a message to the guild's specified logging channel + * @param logType The corresponding channel that the message will be sent to + * @param message The parameters for {@link BushTextChannel.send} */ - public async sendLogChannel(logType: GuildLogType, message: MessageOptions) { + public async sendLogChannel(logType: GuildLogType, message: string | MessagePayload | MessageOptions) { const logChannel = await this.getLogChannel(logType); if (!logChannel || logChannel.type !== 'GUILD_TEXT') return; if (!logChannel.permissionsFor(this.me!.id)?.has(['VIEW_CHANNEL', 'SEND_MESSAGES', 'EMBED_LINKS'])) return; return await logChannel.send(message).catch(() => null); } + + /** + * Sends a formatted error message in a guild's error log channel + * @param title The title of the error embed + * @param message The description of the error embed + */ + public async error(title: string, message: string) { + void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); + void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: util.colors.error }] }); + } } diff --git a/src/lib/extensions/global.d.ts b/src/lib/extensions/global.d.ts index 1a30056..8427873 100644 --- a/src/lib/extensions/global.d.ts +++ b/src/lib/extensions/global.d.ts @@ -4,4 +4,13 @@ declare global { var client: BushClient; var util: BushClientUtil; var __rootdir__: string; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface ReadonlyArray<T> { + includes<S, R extends `${Extract<S, string>}`>( + this: ReadonlyArray<R>, + searchElement: S, + fromIndex?: number + ): searchElement is R & S; + } } diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts index 52b0f40..434a0a7 100644 --- a/src/lib/utils/BushConstants.ts +++ b/src/lib/utils/BushConstants.ts @@ -1,12 +1,28 @@ -import { Constants, type ConstantsColors } from 'discord.js'; +import { Constants } from 'discord.js'; +import { BushClientUtil } from '../extensions/discord-akairo/BushClientUtil.js'; const rawCapeUrl = 'https://raw.githubusercontent.com/NotEnoughUpdates/capes/master/'; export class BushConstants { - public static get emojis() { - return BushEmojis; - } + public static emojis = BushClientUtil.deepFreeze({ + success: '<:success:837109864101707807>', + warn: '<:warn:848726900876247050>', + error: '<:error:837123021016924261>', + successFull: '<:success_full:850118767576088646>', + warnFull: '<:warn_full:850118767391539312>', + errorFull: '<:error_full:850118767295201350>', + mad: '<:mad:783046135392239626>', + join: '<:join:850198029809614858>', + leave: '<:leave:850198048205307919>', + loading: '<a:Loading:853419254619963392>', + offlineCircle: '<:offline:787550565382750239>', + dndCircle: '<:dnd:787550487633330176>', + idleCircle: '<:idle:787550520956551218>', + onlineCircle: '<:online:787550449435803658>', + cross: '<:cross:878319362539421777>', + check: '<:check:878320135297961995>' + } as const); - public static colors: bushColors = { + public static colors = BushClientUtil.deepFreeze({ default: '#1FD8F1', error: '#EF4947', warn: '#FEBA12', @@ -29,11 +45,11 @@ export class BushConstants { darkGray: '#7a7a7a', black: '#000000', orange: '#E86100', - discord: Constants.Colors - }; + discord: Object.assign({}, Constants.Colors) + } as const); // Somewhat stolen from @Mzato0001 - public static TimeUnits: { [key: string]: { match: RegExp; value: number } } = { + public static TimeUnits = BushClientUtil.deepFreeze({ milliseconds: { match: / (?:(?<milliseconds>-?(?:\d+)?\.?\d+) *(?:milliseconds?|msecs?|ms))/im, value: 1 @@ -66,19 +82,43 @@ export class BushConstants { match: / (?:(?<years>-?(?:\d+)?\.?\d+) *(?:years?|y))/im, value: 1000 * 60 * 60 * 24 * 365.25 //leap years } - }; + } as const); - public static regex = { + public static regex = BushClientUtil.deepFreeze({ snowflake: /\d{15,21}/im, - discordEmoji: /<a?:(?<name>[a-zA-Z0-9_]+):(?<id>\d{15,21})>/im - }; + discordEmoji: /<a?:(?<name>[a-zA-Z0-9_]+):(?<id>\d{15,21})>/im, - public static get pronounMapping() { - return PronounMap; - } + //stolen from geek + messageLink: + /(?:ptb\.|canary\.|staging\.|lc\.)?(?:discord(?:app)?|inv)\.(?:com|wtf)?\/channels\/(?<guild_id>\d{15,21}|@me)\/(?<channel_id>\d{15,21})\/(?<message_id>\d{15,21})/im + } as const); + + public static pronounMapping = BushClientUtil.deepFreeze({ + unspecified: 'Unspecified', + hh: 'He/Him', + hi: 'He/It', + hs: 'He/She', + ht: 'He/They', + ih: 'It/Him', + ii: 'It/Its', + is: 'It/She', + it: 'It/They', + shh: 'She/He', + sh: 'She/Her', + si: 'She/It', + st: 'She/They', + th: 'They/He', + ti: 'They/It', + ts: 'They/She', + tt: 'They/Them', + any: 'Any pronouns', + other: 'Other pronouns', + ask: 'Ask me my pronouns', + avoid: 'Avoid pronouns, use my name' + } as const); /** A bunch of mappings */ - public static mappings = { + public static mappings = BushClientUtil.deepFreeze({ guilds: { bush: '516977525906341928', tree: '767448775450820639', @@ -326,25 +366,129 @@ export class BushConstants { 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'DJ': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] } - }; + } as const); - public static get ArgumentMatches() { - return ArgumentMatchesEnum; - } + public static ArgumentMatches = BushClientUtil.deepFreeze({ + PHRASE: 'phrase', + FLAG: 'flag', + OPTION: 'option', + REST: 'rest', + SEPARATE: 'separate', + TEXT: 'text', + CONTENT: 'content', + REST_CONTENT: 'restContent', + NONE: 'none' + } as const); - public static get ArgumentTypes() { - return BushArgumentTypesEnum; - } + public static ArgumentTypes = BushClientUtil.deepFreeze({ + STRING: 'string', + LOWERCASE: 'lowercase', + UPPERCASE: 'uppercase', + CHAR_CODES: 'charCodes', + NUMBER: 'number', + INTEGER: 'integer', + BIGINT: 'bigint', + EMOJINT: 'emojint', + URL: 'url', + DATE: 'date', + COLOR: 'color', + USER: 'user', + USERS: 'users', + MEMBER: 'member', + MEMBERS: 'members', + RELEVANT: 'relevant', + RELEVANTS: 'relevants', + CHANNEL: 'channel', + CHANNELS: 'channels', + TEXT_CHANNEL: 'textChannel', + TEXT_CHANNELS: 'textChannels', + VOICE_CHANNEL: 'voiceChannel', + VOICE_CHANNELS: 'voiceChannels', + CATEGORY_CHANNEL: 'categoryChannel', + CATEGORY_CHANNELS: 'categoryChannels', + NEWS_CHANNEL: 'newsChannel', + NEWS_CHANNELS: 'newsChannels', + STORE_CHANNEL: 'storeChannel', + STORE_CHANNELS: 'storeChannels', + STAGE_CHANNEL: 'stageChannel', + STAGE_CHANNELS: 'stageChannels', + THREAD_CHANNEL: 'threadChannel', + THREAD_CHANNELS: 'threadChannels', + ROLE: 'role', + ROLES: 'roles', + EMOJI: 'emoji', + EMOJIS: 'emojis', + GUILD: 'guild', + GUILDS: 'guilds', + MESSAGE: 'message', + GUILD_MESSAGE: 'guildMessage', + RELEVANT_MESSAGE: 'relevantMessage', + INVITE: 'invite', + USER_MENTION: 'userMention', + MEMBER_MENTION: 'memberMention', + CHANNEL_MENTION: 'channelMention', + ROLE_MENTION: 'roleMention', + EMOJI_MENTION: 'emojiMention', + COMMAND_ALIAS: 'commandAlias', + COMMAND: 'command', + INHIBITOR: 'inhibitor', + LISTENER: 'listener', + TASK: 'task', + CONTEXT_MENU_COMMAND: 'contextMenuCommand', + DURATION: 'duration', + CONTENT_WITH_DURATION: 'contentWithDuration', + PERMISSION: 'permission', + SNOWFLAKE: 'snowflake', + DISCORD_EMOJI: 'discordEmoji', + ROLE_WITH_DURATION: 'roleWithDuration', + ABBREVIATED_NUMBER: 'abbreviatedNumber', + GLOBAL_USER: 'globalUser' + } as const); - public static get BlockedReasons() { - return BushBlockedReasonsEnum; - } + public static BlockedReasons = BushClientUtil.deepFreeze({ + CLIENT: 'client', + BOT: 'bot', + OWNER: 'owner', + SUPER_USER: 'superUser', + GUILD: 'guild', + DM: 'dm', + AUTHOR_NOT_FOUND: 'authorNotFound', + NOT_NSFW: 'notNsfw', + DISABLED_GUILD: 'disabledGuild', + DISABLED_GLOBAL: 'disabledGlobal', + ROLE_BLACKLIST: 'roleBlacklist', + USER_GUILD_BLACKLIST: 'userGuildBlacklist', + USER_GLOBAL_BLACKLIST: 'userGlobalBlacklist', + RESTRICTED_GUILD: 'restrictedGuild', + CHANNEL_GUILD_BLACKLIST: 'channelGuildBlacklist', + CHANNEL_GLOBAL_BLACKLIST: 'channelGlobalBlacklist', + RESTRICTED_CHANNEL: 'restrictedChannel' + } as const); - public static get CommandHandlerEvents() { - return BushCommandHandlerEventsEnum; - } + public static CommandHandlerEvents = BushClientUtil.deepFreeze({ + COMMAND_BLOCKED: 'commandBlocked', + COMMAND_BREAKOUT: 'commandBreakout', + COMMAND_CANCELLED: 'commandCancelled', + COMMAND_FINISHED: 'commandFinished', + COMMAND_INVALID: 'commandInvalid', + COMMAND_LOCKED: 'commandLocked', + COMMAND_STARTED: 'commandStarted', + COOLDOWN: 'cooldown', + ERROR: 'error', + IN_PROMPT: 'inPrompt', + MESSAGE_BLOCKED: 'messageBlocked', + MESSAGE_INVALID: 'messageInvalid', + MISSING_PERMISSIONS: 'missingPermissions', + SLASH_BLOCKED: 'slashBlocked', + SLASH_ERROR: 'slashError', + SLASH_FINISHED: 'slashFinished', + SLASH_MISSING_PERMISSIONS: 'slashMissingPermissions', + SLASH_NOT_FOUND: 'slashNotFound', + SLASH_STARTED: 'slashStarted', + SLASH_ONLY: 'slashOnly' + } as const); - public static moulberryBushRoleMap = [ + public static moulberryBushRoleMap = BushClientUtil.deepFreeze([ { name: '*', id: '792453550768390194' }, { name: 'Admin Perms', id: '746541309853958186' }, { name: 'Sr. Moderator', id: '782803470205190164' }, @@ -370,197 +514,8 @@ export class BushConstants { { name: 'No VC', id: '788850482554208267' }, { name: 'No Giveaways', id: '808265422334984203' }, { name: 'No Support', id: '790247359824396319' } - ]; -} - -export enum PronounMap { - unspecified = 'Unspecified', - hh = 'He/Him', - hi = 'He/It', - hs = 'He/She', - ht = 'He/They', - ih = 'It/Him', - ii = 'It/Its', - is = 'It/She', - it = 'It/They', - shh = 'She/He', - sh = 'She/Her', - si = 'She/It', - st = 'She/They', - th = 'They/He', - ti = 'They/It', - ts = 'They/She', - tt = 'They/Them', - any = 'Any pronouns', - other = 'Other pronouns', - ask = 'Ask me my pronouns', - avoid = 'Avoid pronouns, use my name' -} - -export enum BushEmojis { - success = '<:success:837109864101707807>', - warn = '<:warn:848726900876247050>', - error = '<:error:837123021016924261>', - successFull = '<:success_full:850118767576088646>', - warnFull = '<:warn_full:850118767391539312>', - errorFull = '<:error_full:850118767295201350>', - mad = '<:mad:783046135392239626>', - join = '<:join:850198029809614858>', - leave = '<:leave:850198048205307919>', - loading = '<a:Loading:853419254619963392>', - offlineCircle = '<:offline:787550565382750239>', - dndCircle = '<:dnd:787550487633330176>', - idleCircle = '<:idle:787550520956551218>', - onlineCircle = '<:online:787550449435803658>', - cross = '<:cross:878319362539421777>', - check = '<:check:878320135297961995>' -} - -export enum ArgumentMatchesEnum { - PHRASE = 'phrase', - FLAG = 'flag', - OPTION = 'option', - REST = 'rest', - SEPARATE = 'separate', - TEXT = 'text', - CONTENT = 'content', - REST_CONTENT = 'restContent', - NONE = 'none' -} - -export enum BushArgumentTypesEnum { - STRING = 'string', - LOWERCASE = 'lowercase', - UPPERCASE = 'uppercase', - CHAR_CODES = 'charCodes', - NUMBER = 'number', - INTEGER = 'integer', - BIGINT = 'bigint', - EMOJINT = 'emojint', - URL = 'url', - DATE = 'date', - COLOR = 'color', - USER = 'user', - USERS = 'users', - MEMBER = 'member', - MEMBERS = 'members', - RELEVANT = 'relevant', - RELEVANTS = 'relevants', - CHANNEL = 'channel', - CHANNELS = 'channels', - TEXT_CHANNEL = 'textChannel', - TEXT_CHANNELS = 'textChannels', - VOICE_CHANNEL = 'voiceChannel', - VOICE_CHANNELS = 'voiceChannels', - CATEGORY_CHANNEL = 'categoryChannel', - CATEGORY_CHANNELS = 'categoryChannels', - NEWS_CHANNEL = 'newsChannel', - NEWS_CHANNELS = 'newsChannels', - STORE_CHANNEL = 'storeChannel', - STORE_CHANNELS = 'storeChannels', - STAGE_CHANNEL = 'stageChannel', - STAGE_CHANNELS = 'stageChannels', - THREAD_CHANNEL = 'threadChannel', - THREAD_CHANNELS = 'threadChannels', - ROLE = 'role', - ROLES = 'roles', - EMOJI = 'emoji', - EMOJIS = 'emojis', - GUILD = 'guild', - GUILDS = 'guilds', - MESSAGE = 'message', - GUILD_MESSAGE = 'guildMessage', - RELEVANT_MESSAGE = 'relevantMessage', - INVITE = 'invite', - USER_MENTION = 'userMention', - MEMBER_MENTION = 'memberMention', - CHANNEL_MENTION = 'channelMention', - ROLE_MENTION = 'roleMention', - EMOJI_MENTION = 'emojiMention', - COMMAND_ALIAS = 'commandAlias', - COMMAND = 'command', - INHIBITOR = 'inhibitor', - LISTENER = 'listener', - TASK = 'task', - CONTEXT_MENU_COMMAND = 'contextMenuCommand', - DURATION = 'duration', - CONTENT_WITH_DURATION = 'contentWithDuration', - PERMISSION = 'permission', - SNOWFLAKE = 'snowflake', - DISCORD_EMOJI = 'discordEmoji', - ROLE_WITH_DURATION = 'roleWithDuration', - ABBREVIATED_NUMBER = 'abbreviatedNumber', - GLOBAL_USER = 'globalUser' -} - -export enum BushBlockedReasonsEnum { - CLIENT = 'client', - BOT = 'bot', - OWNER = 'owner', - SUPER_USER = 'superUser', - GUILD = 'guild', - DM = 'dm', - AUTHOR_NOT_FOUND = 'authorNotFound', - NOT_NSFW = 'notNsfw', - DISABLED_GUILD = 'disabledGuild', - DISABLED_GLOBAL = 'disabledGlobal', - ROLE_BLACKLIST = 'roleBlacklist', - USER_GUILD_BLACKLIST = 'userGuildBlacklist', - USER_GLOBAL_BLACKLIST = 'userGlobalBlacklist', - RESTRICTED_GUILD = 'restrictedGuild', - CHANNEL_GUILD_BLACKLIST = 'channelGuildBlacklist', - CHANNEL_GLOBAL_BLACKLIST = 'channelGlobalBlacklist', - RESTRICTED_CHANNEL = 'restrictedChannel' -} - -export enum BushCommandHandlerEventsEnum { - COMMAND_BLOCKED = 'commandBlocked', - COMMAND_BREAKOUT = 'commandBreakout', - COMMAND_CANCELLED = 'commandCancelled', - COMMAND_FINISHED = 'commandFinished', - COMMAND_INVALID = 'commandInvalid', - COMMAND_LOCKED = 'commandLocked', - COMMAND_STARTED = 'commandStarted', - COOLDOWN = 'cooldown', - ERROR = 'error', - IN_PROMPT = 'inPrompt', - MESSAGE_BLOCKED = 'messageBlocked', - MESSAGE_INVALID = 'messageInvalid', - MISSING_PERMISSIONS = 'missingPermissions', - SLASH_BLOCKED = 'slashBlocked', - SLASH_ERROR = 'slashError', - SLASH_FINISHED = 'slashFinished', - SLASH_MISSING_PERMISSIONS = 'slashMissingPermissions', - SLASH_NOT_FOUND = 'slashNotFound', - SLASH_STARTED = 'slashStarted', - SLASH_ONLY = 'slashOnly' -} - -interface bushColors { - default: '#1FD8F1'; - error: '#EF4947'; - warn: '#FEBA12'; - success: '#3BB681'; - info: '#3B78FF'; - red: '#ff0000'; - blue: '#0055ff'; - aqua: '#00bbff'; - purple: '#8400ff'; - blurple: '#5440cd'; - newBlurple: '#5865f2'; - pink: '#ff00e6'; - green: '#00ff1e'; - darkGreen: '#008f11'; - gold: '#b59400'; - yellow: '#ffff00'; - white: '#ffffff'; - gray: '#a6a6a6'; - lightGray: '#cfcfcf'; - darkGray: '#7a7a7a'; - black: '#000000'; - orange: '#E86100'; - discord: ConstantsColors; + ] as const); } -export type PronounCode = keyof typeof PronounMap; -export type Pronoun = PronounMap; +export type PronounCode = keyof typeof BushConstants['pronounMapping']; +export type Pronoun = typeof BushConstants['pronounMapping'][PronounCode]; diff --git a/src/lib/utils/BushLogger.ts b/src/lib/utils/BushLogger.ts index 4b89622..3421985 100644 --- a/src/lib/utils/BushLogger.ts +++ b/src/lib/utils/BushLogger.ts @@ -1,5 +1,6 @@ import chalk from 'chalk'; -import { MessageEmbed, Util, type Message } from 'discord.js'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { MessageEmbed, Util, type Message, type PartialTextBasedChannelFields } from 'discord.js'; import { inspect } from 'util'; import { type BushSendMessageType } from '../extensions/discord-akairo/BushClient'; @@ -52,22 +53,27 @@ export class BushLogger { /** * Logs information. Highlight information by surrounding it in `<<>>`. - * @param header - The header displayed before the content, displayed in cyan. - * @param content - The content to log, highlights displayed in bright blue. - * @param sendChannel - Should this also be logged to discord? Defaults to false. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param header The header displayed before the content, displayed in cyan. + * @param content The content to log, highlights displayed in bright blue. + * @param sendChannel Should this also be logged to discord? Defaults to false. + * @param depth The depth the content will inspected. Defaults to 0. */ public static get log() { return BushLogger.info; } - /** Sends a message to the log channel */ + /** + * Sends a message to the log channel + * @param message The parameter to pass to {@link PartialTextBasedChannelFields.send} + */ public static async channelLog(message: BushSendMessageType) { const channel = await util.getConfigChannel('log'); await channel.send(message).catch(() => {}); } - /** Sends a message to the error channel */ + /** + * Sends a message to the error channel + */ public static async channelError(message: BushSendMessageType): Promise<Message> { const channel = await util.getConfigChannel('error'); return await channel.send(message); @@ -75,8 +81,8 @@ export class BushLogger { /** * Logs debug information. Only works in dev is enabled in the config. - * @param content - The content to log. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param content The content to log. + * @param depth The depth the content will inspected. Defaults to 0. */ public static debug(content: any, depth = 0): void { if (!client.config.isDevelopment) return; @@ -86,7 +92,7 @@ export class BushLogger { /** * Logs raw debug information. Only works in dev is enabled in the config. - * @param content - The content to log. + * @param content The content to log. */ public static debugRaw(...content: any): void { if (!client.config.isDevelopment) return; @@ -95,10 +101,10 @@ export class BushLogger { /** * Logs verbose information. Highlight information by surrounding it in `<<>>`. - * @param header - The header printed before the content, displayed in grey. - * @param content - The content to log, highlights displayed in bright black. - * @param sendChannel - Should this also be logged to discord? Defaults to false. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param header The header printed before the content, displayed in grey. + * @param content The content to log, highlights displayed in bright black. + * @param sendChannel Should this also be logged to discord? Defaults to false. + * @param depth The depth the content will inspected. Defaults to 0. */ public static async verbose(header: string, content: any, sendChannel = false, depth = 0) { if (!client.config.logging.verbose) return; @@ -116,10 +122,10 @@ export class BushLogger { /** * Logs information. Highlight information by surrounding it in `<<>>`. - * @param header - The header displayed before the content, displayed in cyan. - * @param content - The content to log, highlights displayed in bright blue. - * @param sendChannel - Should this also be logged to discord? Defaults to false. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param header The header displayed before the content, displayed in cyan. + * @param content The content to log, highlights displayed in bright blue. + * @param sendChannel Should this also be logged to discord? Defaults to false. + * @param depth The depth the content will inspected. Defaults to 0. */ public static async info(header: string, content: any, sendChannel = true, depth = 0) { if (!client.config.logging.info) return; @@ -137,10 +143,10 @@ export class BushLogger { /** * Logs warnings. Highlight information by surrounding it in `<<>>`. - * @param header - The header displayed before the content, displayed in yellow. - * @param content - The content to log, highlights displayed in bright yellow. - * @param sendChannel - Should this also be logged to discord? Defaults to false. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param header The header displayed before the content, displayed in yellow. + * @param content The content to log, highlights displayed in bright yellow. + * @param sendChannel Should this also be logged to discord? Defaults to false. + * @param depth The depth the content will inspected. Defaults to 0. */ public static async warn(header: string, content: any, sendChannel = true, depth = 0) { const newContent = this.#inspectContent(content, depth, true); @@ -161,10 +167,10 @@ export class BushLogger { /** * Logs errors. Highlight information by surrounding it in `<<>>`. - * @param header - The header displayed before the content, displayed in bright red. - * @param content - The content to log, highlights displayed in bright red. - * @param sendChannel - Should this also be logged to discord? Defaults to false. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param header The header displayed before the content, displayed in bright red. + * @param content The content to log, highlights displayed in bright red. + * @param sendChannel Should this also be logged to discord? Defaults to false. + * @param depth The depth the content will inspected. Defaults to 0. */ public static async error(header: string, content: any, sendChannel = true, depth = 0) { const newContent = this.#inspectContent(content, depth, true); @@ -185,10 +191,10 @@ export class BushLogger { /** * Logs successes. Highlight information by surrounding it in `<<>>`. - * @param header - The header displayed before the content, displayed in green. - * @param content - The content to log, highlights displayed in bright green. - * @param sendChannel - Should this also be logged to discord? Defaults to false. - * @param depth - The depth the content will inspected. Defaults to 0. + * @param header The header displayed before the content, displayed in green. + * @param content The content to log, highlights displayed in bright green. + * @param sendChannel Should this also be logged to discord? Defaults to false. + * @param depth The depth the content will inspected. Defaults to 0. */ public static async success(header: string, content: any, sendChannel = true, depth = 0) { const newContent = this.#inspectContent(content, depth, true); @@ -206,3 +212,5 @@ export class BushLogger { await this.channelLog({ embeds: [embed] }).catch(() => {}); } } + +/** @typedef {PartialTextBasedChannelFields} vscodeDontDeleteMyImportTy */
\ No newline at end of file diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts index 0b57156..1fb5c4a 100644 --- a/src/listeners/client/ready.ts +++ b/src/listeners/client/ready.ts @@ -1,4 +1,4 @@ -import { BushListener, Guild } from '#lib'; +import { BushClientEvents, BushListener, Guild } from '#lib'; import chalk from 'chalk'; export default class ReadyListener extends BushListener { @@ -10,7 +10,8 @@ export default class ReadyListener extends BushListener { }); } - public override async exec() { + // eslint-disable-next-line no-empty-pattern + public override async exec(...[]: BushClientEvents['ready']) { client.taskHandler.startAll(); process.emit('ready' as any); diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index 7c5bc67..2243abd 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -32,25 +32,25 @@ export default class CommandBlockedListener extends BushListener { switch (reason) { case reasons.OWNER: { return await respond({ - content: `${util.emojis.error} Only my developers can run the ${util.format.bold(command!.toString())} command.`, + content: `${util.emojis.error} Only my developers can run the ${util.format.input(command!.toString())} command.`, ephemeral: true }); } case reasons.SUPER_USER: { return await respond({ - content: `${util.emojis.error} You must be a superuser to run the ${util.format.bold(command!.toString())} command.`, + content: `${util.emojis.error} You must be a superuser to run the ${util.format.input(command!.toString())} command.`, ephemeral: true }); } case reasons.DISABLED_GLOBAL: { return await respond({ - content: `${util.emojis.error} My developers disabled the ${util.format.bold(command!.toString())} command.`, + content: `${util.emojis.error} My developers disabled the ${util.format.input(command!.toString())} command.`, ephemeral: true }); } case reasons.DISABLED_GUILD: { return await respond({ - content: `${util.emojis.error} The ${util.format.bold(command!.toString())} command is currently disabled in \`${ + content: `${util.emojis.error} The ${util.format.input(command!.toString())} command is currently disabled in \`${ message.guild?.name }\`.`, ephemeral: true @@ -89,7 +89,7 @@ export default class CommandBlockedListener extends BushListener { }); const pretty = util.oxford(names, 'and'); return await respond({ - content: `${util.emojis.error} ${util.format.bold(command!.toString())} can only be run in ${pretty}.`, + content: `${util.emojis.error} ${util.format.input(command!.toString())} can only be run in ${pretty}.`, ephemeral: true }); } @@ -102,7 +102,7 @@ export default class CommandBlockedListener extends BushListener { }); const pretty = util.oxford(names, 'and'); return await respond({ - content: `${util.emojis.error} ${util.format.bold(command!.toString())} can only be run in ${pretty}.`, + content: `${util.emojis.error} ${util.format.input(command!.toString())} can only be run in ${pretty}.`, ephemeral: true }); } diff --git a/src/listeners/commands/messageBlocked.ts b/src/listeners/commands/messageBlocked.ts index 6a0eaab..1b969ed 100644 --- a/src/listeners/commands/messageBlocked.ts +++ b/src/listeners/commands/messageBlocked.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushBlockedReasonsEnum, type BushCommandHandlerEvents } from '#lib'; +import { BushListener, type BushCommandHandlerEvents } from '#lib'; export default class MessageBlockedListener extends BushListener { public constructor() { @@ -9,8 +9,7 @@ export default class MessageBlockedListener extends BushListener { } public override async exec(...[message, reason]: BushCommandHandlerEvents['messageBlocked']) { - const reasons = client.consts.BlockedReasons; - if ([reasons.CLIENT, reasons.BOT].includes(reason as BushBlockedReasonsEnum)) return; + if (['client', 'bot'].includes(reason)) return; // return await CommandBlockedListener.handleBlocked(message as Message, null, reason); return void client.console.verbose(`messageBlocked`, `<<${message.author.tag}>>'s message was blocked because ${reason}`); } diff --git a/src/listeners/custom/bushLevelUpdate.ts b/src/listeners/custom/bushLevelUpdate.ts index 897def5..4504c45 100644 --- a/src/listeners/custom/bushLevelUpdate.ts +++ b/src/listeners/custom/bushLevelUpdate.ts @@ -1,5 +1,5 @@ import { BushListener, type BushClientEvents } from '#lib'; -import { Formatters, type TextChannel } from 'discord.js'; +import { type TextChannel } from 'discord.js'; export default class BushLevelUpdateListener extends BushListener { public constructor() { @@ -18,11 +18,7 @@ export default class BushLevelUpdateListener extends BushListener { .catch(() => null)) ?? message.channel) as TextChannel; const success = await channel - .send( - `${Formatters.bold(util.sanitizeWtlAndControl(member.user.tag))} leveled up to level ${Formatters.bold( - `${newLevel}` - )}.` - ) + .send(`${util.format.input(member.user.tag)} leveled up to level ${util.format.input(`${newLevel}`)}.`) .catch(() => null); if (!success) await client.console.warn('bushLevelUpdate', `Could not send level up message in ${message.guild}`); diff --git a/src/listeners/guild/guildMemberAdd.ts b/src/listeners/guild/guildMemberAdd.ts index f10255d..4c7d498 100644 --- a/src/listeners/guild/guildMemberAdd.ts +++ b/src/listeners/guild/guildMemberAdd.ts @@ -1,5 +1,5 @@ import { BushListener, StickyRole, type BushClientEvents, type BushGuildMember, type BushTextChannel } from '#lib'; -import { MessageEmbed, Util, type Snowflake } from 'discord.js'; +import { MessageEmbed, type Snowflake } from 'discord.js'; export default class GuildMemberAddListener extends BushListener { public constructor() { @@ -24,9 +24,9 @@ export default class GuildMemberAddListener extends BushListener { if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); const embed = new MessageEmbed() .setDescription( - `${util.emojis.join} **${util.sanitizeWtlAndControl( - Util.escapeMarkdown(member.user.tag) - )}** joined the server. There are now ${member.guild.memberCount.toLocaleString()} members.` + `${util.emojis.join} ${util.format.input( + member.user.tag + )} joined the server. There are now ${member.guild.memberCount.toLocaleString()} members.` ) .setColor(util.colors.green); await welcome @@ -34,13 +34,13 @@ export default class GuildMemberAddListener extends BushListener { .then(() => client.console.info( 'guildMemberAdd', - `Sent a message for <<${util.sanitizeWtlAndControl(member.user.tag)}>> in <<${member.guild.name}>>.` + `Sent a message for ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` ) ) .catch(() => client.console.warn( 'guildMemberAdd', - `Failed to send message for <<${util.sanitizeWtlAndControl(member.user.tag)}>> in <<${member.guild.name}>>.` + `Failed to send message for ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` ) ); } @@ -62,19 +62,21 @@ export default class GuildMemberAddListener extends BushListener { void member.setNickname(hadRoles.nickname).catch(() => {}); } if (rolesArray?.length) { - const addedRoles = await member.roles - .add(rolesArray, "Returning member's previous roles.") - .catch( - () => - void client.console.warn( - 'guildMemberAdd', - `There was an error returning <<${util.sanitizeWtlAndControl(member.user.tag)}>>'s roles.` - ) - ); + const addedRoles = await member.roles.add(rolesArray, "Returning member's previous roles.").catch(() => + member.guild.sendLogChannel('error', { + embeds: [ + { + title: 'Sticky Roles Error', + description: `There was an error returning ${util.format.input(member.user.tag)}'s roles.`, + color: util.colors.error + } + ] + }) + ); if (addedRoles) { void client.console.info( 'guildMemberAdd', - `Assigned sticky roles to <<${util.sanitizeWtlAndControl(member.user.tag)}>> in <<${member.guild.name}>>.` + `Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` ); } else if (!addedRoles) { const failedRoles: string[] = []; @@ -88,9 +90,9 @@ export default class GuildMemberAddListener extends BushListener { } else { void client.console.info( 'guildMemberAdd', - `[Fallback] Assigned sticky roles to <<${util.sanitizeWtlAndControl(member.user.tag)}>> in <<${ + `[Fallback] Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog( member.guild.name - }>>.` + )}.` ); } } @@ -103,15 +105,14 @@ export default class GuildMemberAddListener extends BushListener { .then(() => client.console.info( 'guildMemberAdd', - `Assigned join roles to <<${util.sanitizeWtlAndControl(member.user.tag)}>> in <<${member.guild.name}>>.` + `Assigned join roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` ) ) - .catch( - () => - void client.console.warn( - 'guildMemberAdd', - `Failed to assign join roles to <<${util.sanitizeWtlAndControl(member.user.tag)}>>, in <<${member.guild.name}>>.` - ) + .catch(() => + member.guild.error( + 'Join Roles Error', + `Failed to assign join roles to ${util.format.input(member.user.tag)}, in ${util.format.input(member.guild.name)}.` + ) ); } } diff --git a/src/listeners/guild/guildMemberRemove.ts b/src/listeners/guild/guildMemberRemove.ts index 393eea6..6181d52 100644 --- a/src/listeners/guild/guildMemberRemove.ts +++ b/src/listeners/guild/guildMemberRemove.ts @@ -6,7 +6,7 @@ import { type BushClientEvents, type PartialBushGuildMember } from '#lib'; -import { MessageEmbed, Util } from 'discord.js'; +import { MessageEmbed } from 'discord.js'; export default class GuildMemberRemoveListener extends BushListener { public constructor() { @@ -33,7 +33,7 @@ export default class GuildMemberRemoveListener extends BushListener { if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); const embed: MessageEmbed = new MessageEmbed() .setDescription( - `${util.emojis.leave} **${util.sanitizeWtlAndControl(Util.escapeBold(user.tag))}** ${ + `${util.emojis.leave} ${util.format.input(user.tag)} ${ isBan ? 'got banned from' : 'left' } the server. There are now ${welcome.guild.memberCount.toLocaleString()} members.` ) @@ -43,13 +43,13 @@ export default class GuildMemberRemoveListener extends BushListener { .then(() => client.console.info( 'guildMemberRemove', - `Sent a message for <<${util.sanitizeWtlAndControl(user.tag)}>> in <<${member.guild.name}>>.` + `Sent a message for ${util.format.inputLog(user.tag)} in ${util.format.inputLog(member.guild.name)}.` ) ) .catch(() => - client.console.warn( - 'guildMemberRemove', - `Failed to send message for <<${util.sanitizeWtlAndControl(user.tag)}>> in <<${member.guild.name}>>.` + member.guild.error( + 'Welcome Message Error', + `Failed to send message for ${util.format.input(user.tag)} in ${util.format.input(member.guild.name)}.` ) ); } @@ -81,7 +81,7 @@ export default class GuildMemberRemoveListener extends BushListener { .then(() => client.console.info( 'guildMemberRemove', - `${isNew ? 'Created' : 'Updated'} info for <<${util.sanitizeWtlAndControl(member.user.tag)}>>.` + `${isNew ? 'Created' : 'Updated'} info for ${util.format.inputLog(member.user.tag)}.` ) ); } diff --git a/src/listeners/other/promiseRejection.ts b/src/listeners/other/promiseRejection.ts index f7ca840..699b676 100644 --- a/src/listeners/other/promiseRejection.ts +++ b/src/listeners/other/promiseRejection.ts @@ -6,7 +6,8 @@ export default class PromiseRejectionListener extends BushListener { public constructor() { super('promiseRejection', { emitter: 'process', - event: 'unhandledRejection' + event: 'unhandledRejection', + type: 'prependListener' }); } diff --git a/src/listeners/other/uncaughtException.ts b/src/listeners/other/uncaughtException.ts index 096c1f0..34dcdd6 100644 --- a/src/listeners/other/uncaughtException.ts +++ b/src/listeners/other/uncaughtException.ts @@ -6,7 +6,8 @@ export default class UncaughtExceptionListener extends BushListener { public constructor() { super('uncaughtException', { emitter: 'process', - event: 'uncaughtException' + event: 'uncaughtException', + type: 'prependListener' }); } |