diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/commands/moderation/removeReactionEmoji.ts | 3 | ||||
-rw-r--r-- | src/commands/utilities/viewRaw.ts | 6 | ||||
-rw-r--r-- | src/context-menu-commands/user/userInfo.ts | 9 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClient.ts | 2 | ||||
-rw-r--r-- | src/listeners/commands/commandBlocked.ts | 15 | ||||
-rw-r--r-- | src/listeners/commands/commandError.ts | 11 | ||||
-rw-r--r-- | src/listeners/commands/commandStarted.ts | 8 | ||||
-rw-r--r-- | src/listeners/commands/messageBlocked.ts | 3 | ||||
-rw-r--r-- | src/listeners/contextCommands/contextCommandBlocked.ts | 41 | ||||
-rw-r--r-- | src/listeners/contextCommands/contextCommandError.ts | 123 | ||||
-rw-r--r-- | src/listeners/contextCommands/contextCommandNotFound.ts | 16 | ||||
-rw-r--r-- | src/listeners/contextCommands/contextCommandStarted.ts | 27 |
12 files changed, 240 insertions, 24 deletions
diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts index b059bff..bc24db0 100644 --- a/src/commands/moderation/removeReactionEmoji.ts +++ b/src/commands/moderation/removeReactionEmoji.ts @@ -14,7 +14,8 @@ export default class RemoveReactionEmojiCommand extends BushCommand { { id: 'message', description: 'The message to remove all the reactions of a certain emoji from.', - type: 'guildMessage', + type: util.arg.union('message', 'messageLink'), + readableType: 'message|messageLink', prompt: 'What message would you like to remove a reaction from?', retry: '{error} Please pick a valid message.', slashType: ApplicationCommandOptionType.String diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index 1da26f9..20e7272 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.ts @@ -14,8 +14,8 @@ export default class ViewRawCommand extends BushCommand { { id: 'message', description: 'The message to view the raw content of.', - type: util.arg.union('guildMessage', 'messageLink'), - readableType: 'guildMessage|messageLink', + type: util.arg.union('message', 'messageLink'), + readableType: 'message|messageLink', prompt: 'What message would you like to view?', retry: '{error} Choose a valid message.', slashType: ApplicationCommandOptionType.String @@ -66,7 +66,7 @@ export default class ViewRawCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - message: ArgType<'guildMessage'> | ArgType<'messageLink'>; + message: ArgType<'message'> | ArgType<'messageLink'>; channel: OptionalArgType<'textChannel'> | OptionalArgType<'newsChannel'> | OptionalArgType<'threadChannel'>; json: boolean; js: boolean; diff --git a/src/context-menu-commands/user/userInfo.ts b/src/context-menu-commands/user/userInfo.ts index 5f59a30..8ecde92 100644 --- a/src/context-menu-commands/user/userInfo.ts +++ b/src/context-menu-commands/user/userInfo.ts @@ -15,9 +15,14 @@ export default class UserInfoContextMenuCommand extends ContextMenuCommand { public override async exec(interaction: ContextMenuCommandInteraction) { await interaction.deferReply({ ephemeral: true }); - const user = await client.users.fetch(interaction.targetId); + const user = await client.users.fetch(interaction.targetId).catch(() => null); + if (!user) return interaction.reply(`⁉ I couldn't find that user`); + const guild = interaction.guild as BushGuild; - const member = await guild.members.fetch(interaction.targetId); + + const member = await guild.members.fetch(interaction.targetId).catch(() => null); + if (!member) return interaction.reply(`${util.format.input(user.tag)} doesn't appear to be a member of this server anymore.`); + const userEmbed = await UserInfoCommand.makeUserInfoEmbed(user, member, guild); return await interaction.editReply({ embeds: [userEmbed] }); diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index 5618903..5ccc283 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -347,7 +347,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re * Initializes the bot. */ public async init() { - if (!process.version.startsWith('v17.')) { + if (parseInt(process.versions.node.split('.')[0]) <= 17) { void (await this.console.error('version', `Please use node <<v17.x.x>>, not <<${process.version}>>.`, false)); process.exit(2); } diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index 5724d33..0d3c1d9 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -5,7 +5,8 @@ export default class CommandBlockedListener extends BushListener { public constructor() { super('commandBlocked', { emitter: 'commandHandler', - event: 'commandBlocked' + event: 'commandBlocked', + category: 'commands' }); } @@ -32,26 +33,26 @@ 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.input(command!.toString())} command.`, + content: `${util.emojis.error} Only my developers can run the ${util.format.input(command!.id)} command.`, ephemeral: true }); } case reasons.SUPER_USER: { return await respond({ - content: `${util.emojis.error} You must be a superuser to run the ${util.format.input(command!.toString())} command.`, + content: `${util.emojis.error} You must be a superuser to run the ${util.format.input(command!.id)} command.`, ephemeral: true }); } case reasons.DISABLED_GLOBAL: { return await respond({ - content: `${util.emojis.error} My developers disabled the ${util.format.input(command!.toString())} command.`, + content: `${util.emojis.error} My developers disabled the ${util.format.input(command!.id)} command.`, ephemeral: true }); } case reasons.DISABLED_GUILD: { return await respond({ content: `${util.emojis.error} The ${util.format.input( - command!.toString() + command!.id )} command is currently disabled in ${util.format.input(message.guild!.name)}.`, ephemeral: true }); @@ -89,7 +90,7 @@ export default class CommandBlockedListener extends BushListener { }); const pretty = util.oxford(names, 'and'); return await respond({ - content: `${util.emojis.error} ${util.format.input(command!.toString())} can only be run in ${pretty}.`, + content: `${util.emojis.error} ${util.format.input(command!.id)} can only be run in ${pretty}.`, ephemeral: true }); } @@ -99,7 +100,7 @@ export default class CommandBlockedListener extends BushListener { const names = guilds!.map((g) => util.format.input(client.guilds.cache.get(g)?.name ?? g)); const pretty = util.oxford(names, 'and'); return await respond({ - content: `${util.emojis.error} ${util.format.input(command!.toString())} can only be run in ${pretty}.`, + content: `${util.emojis.error} ${util.format.input(command!.id)} can only be run in ${pretty}.`, ephemeral: true }); } diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index e1acb00..d590e06 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -8,7 +8,8 @@ export default class CommandErrorListener extends BushListener { public constructor() { super('commandError', { emitter: 'commandHandler', - event: 'error' + event: 'error', + category: 'commands' }); } @@ -33,10 +34,10 @@ export default class CommandErrorListener extends BushListener { 'message.id': message.id, 'message.type': message.util.isSlash ? 'slash' : 'normal', 'message.parsed.content': message.util.parsed?.content, - 'channel.id': (message.channel?.isDM() ? message.channel.recipient?.id : message.channel?.id) ?? '¯_(ツ)_/¯', + 'channel.id': (message.channel?.isDM() ? message.channel.recipient?.id : message.channel?.id) ?? '¯\\_(ツ)_/¯', 'channel.name': channel, - 'guild.id': message.guild?.id ?? '¯_(ツ)_/¯', - 'guild.name': message.guild?.name ?? '¯_(ツ)_/¯', + 'guild.id': message.guild?.id ?? '¯\\_(ツ)_/¯', + 'guild.name': message.guild?.name ?? '¯\\_(ツ)_/¯', 'environment': client.config.environment } }); @@ -232,7 +233,7 @@ export default class CommandErrorListener extends BushListener { } } -class IFuckedUpError extends Error { +export class IFuckedUpError extends Error { public declare original: Error | any; public declare newError: Error | any; diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts index 4d2f0ad..63b906b 100644 --- a/src/listeners/commands/commandStarted.ts +++ b/src/listeners/commands/commandStarted.ts @@ -21,11 +21,11 @@ export default class CommandStartedListener extends BushListener { 'message.id': message.id, 'message.type': message.util.isSlash ? 'slash' : 'normal', 'message.parsed.content': message.util.parsed?.content, - 'channel.id': (message.channel.isDMBased() ? message.channel.recipient?.id : message.channel?.id) ?? '¯_(ツ)_/¯', + 'channel.id': (message.channel.isDMBased() ? message.channel.recipient?.id : message.channel?.id) ?? '¯\\_(ツ)_/¯', 'channel.name': - (message.channel.isDMBased() ? message.channel.recipient?.tag : (<any>message.channel)?.name) ?? '¯_(ツ)_/¯', - 'guild.id': message.guild?.id ?? '¯_(ツ)_/¯', - 'guild.name': message.guild?.name ?? '¯_(ツ)_/¯', + (message.channel.isDMBased() ? message.channel.recipient?.tag : (<any>message.channel)?.name) ?? '¯\\_(ツ)_/¯', + 'guild.id': message.guild?.id ?? '¯\\_(ツ)_/¯', + 'guild.name': message.guild?.name ?? '¯\\_(ツ)_/¯', 'environment': client.config.environment } }); diff --git a/src/listeners/commands/messageBlocked.ts b/src/listeners/commands/messageBlocked.ts index 1b969ed..5a2b10d 100644 --- a/src/listeners/commands/messageBlocked.ts +++ b/src/listeners/commands/messageBlocked.ts @@ -4,7 +4,8 @@ export default class MessageBlockedListener extends BushListener { public constructor() { super('messageBlocked', { emitter: 'commandHandler', - event: 'messageBlocked' + event: 'messageBlocked', + category: 'commands' }); } diff --git a/src/listeners/contextCommands/contextCommandBlocked.ts b/src/listeners/contextCommands/contextCommandBlocked.ts new file mode 100644 index 0000000..7fe381e --- /dev/null +++ b/src/listeners/contextCommands/contextCommandBlocked.ts @@ -0,0 +1,41 @@ +import { BushListener } from '#lib'; +import { ContextMenuCommandHandlerEvents } from 'discord-akairo'; + +export default class ContextCommandBlockedListener extends BushListener { + public constructor() { + super('contextCommandBlocked', { + emitter: 'contextMenuCommandHandler', + event: 'blocked', + category: 'contextCommands' + }); + } + + public override async exec(...[interaction, command, reason]: ContextMenuCommandHandlerEvents['blocked']) { + void client.console.info( + `ContextCommandBlocked`, + `<<${interaction.user.tag}>> tried to run <<${command}>> but was blocked because <<${reason}>>.`, + true + ); + + switch (reason) { + case client.consts.BlockedReasons.OWNER: { + return await interaction.reply({ + content: `${util.emojis.error} Only my developers can run the ${util.format.input(command!.id)} command.`, + ephemeral: true + }); + } + case client.consts.BlockedReasons.SUPER_USER: { + return await interaction.reply({ + content: `${util.emojis.error} You must be a superuser to run the ${util.format.input(command!.id)} command.`, + ephemeral: true + }); + } + default: { + return await interaction.reply({ + content: `${util.emojis.error} Command blocked with reason ${util.format.input(reason ?? 'unknown')}.`, + ephemeral: true + }); + } + } + } +} diff --git a/src/listeners/contextCommands/contextCommandError.ts b/src/listeners/contextCommands/contextCommandError.ts new file mode 100644 index 0000000..04a97ad --- /dev/null +++ b/src/listeners/contextCommands/contextCommandError.ts @@ -0,0 +1,123 @@ +import { Severity } from '@sentry/types'; +import { ContextMenuCommand, ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; +import { BushListener } from '../../lib/extensions/discord-akairo/BushListener.js'; +import CommandErrorListener, { IFuckedUpError } from '../commands/commandError.js'; + +export default class ContextCommandErrorListener extends BushListener { + public constructor() { + super('contextCommandError', { + emitter: 'contextMenuCommandHandler', + event: 'error', + category: 'contextCommands' + }); + } + + public override exec(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { + return ContextCommandErrorListener.handleError(error, interaction, command); + } + + public static async handleError(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { + try { + const errorNum = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number + const channel = interaction.channel?.isDM() + ? interaction.channel.recipient?.tag + : (<GuildTextBasedChannel>interaction.channel)?.name; + + client.sentry.captureException(error, { + level: Severity.Error, + user: { id: interaction.user.id, username: interaction.user.tag }, + extra: { + 'command.name': command?.id, + 'message.id': interaction.id, + 'message.type': 'context command', + 'channel.id': + (interaction.channel?.isDM() ? interaction.channel.recipient?.id : interaction.channel?.id) ?? '¯\\_(ツ)_/¯', + 'channel.name': channel, + 'guild.id': interaction.guild?.id ?? '¯\\_(ツ)_/¯', + 'guild.name': interaction.guild?.name ?? '¯\\_(ツ)_/¯', + 'environment': client.config.environment + } + }); + + void client.console.error( + `contextCommandError`, + `an error occurred with the <<${command}>> context command in <<${channel}>> triggered by <<${ + interaction?.user?.tag + }>>:\n${error?.stack ?? <any>error}`, + false + ); + + const _haste = CommandErrorListener.getErrorHaste(error); + const _stack = CommandErrorListener.getErrorStack(error); + const [haste, stack] = await Promise.all([_haste, _stack]); + const options = { interaction, error, errorNum, command, channel, haste, stack }; + + const errorEmbed = this._generateErrorEmbed({ + ...options, + type: 'command-log' + }); + + void client.logger.channelError({ embeds: [errorEmbed] }); + + if (interaction) { + if (!client.config.owners.includes(interaction.user.id)) { + const errorUserEmbed = this._generateErrorEmbed({ + ...options, + type: 'command-user' + }); + void interaction?.reply({ embeds: [errorUserEmbed] }).catch(() => null); + } else { + const errorDevEmbed = this._generateErrorEmbed({ + ...options, + type: 'command-dev' + }); + + void interaction?.reply({ embeds: [errorDevEmbed] }).catch(() => null); + } + } + } catch (e) { + throw new IFuckedUpError('An error occurred while handling a command error.', error, e); + } + } + + private static _generateErrorEmbed(options: { + interaction: ContextMenuCommandInteraction; + error: Error | any; + type: 'command-log' | 'command-dev' | 'command-user'; + errorNum: number; + command?: ContextMenuCommand; + channel?: string; + haste: string[]; + stack: string; + }): EmbedBuilder { + const embed = new EmbedBuilder().setColor(util.colors.error).setTimestamp(); + if (options.type === 'command-user') { + return embed + .setTitle('An Error Occurred') + .setDescription( + `Oh no! ${ + options.command ? `While running the command ${util.format.input(options.command.id)}, a` : 'A' + }n error occurred. Please give the developers code ${util.format.input(`${options.errorNum}`)}.` + ); + } + const description = new Array<string>(); + + if (options.type === 'command-log') { + description.push( + `**User:** ${options.interaction.user} (${options.interaction.user.tag})`, + `**Command:** ${options.command ?? 'N/A'}`, + `**Channel:** <#${options.interaction.channel?.id}> (${options.channel})` + ); + } + + description.push(...options.haste); + + embed.addFields([{ name: 'Stack Trace', value: options.stack.substring(0, 1024) }]); + if (description.length) embed.setDescription(description.join('\n').substring(0, 4000)); + + if (options.type === 'command-dev' || options.type === 'command-log') + embed.setTitle(`ContextCommandError #${util.format.input(`${options.errorNum}`)}`); + return embed; + } +} diff --git a/src/listeners/contextCommands/contextCommandNotFound.ts b/src/listeners/contextCommands/contextCommandNotFound.ts new file mode 100644 index 0000000..b0a8f62 --- /dev/null +++ b/src/listeners/contextCommands/contextCommandNotFound.ts @@ -0,0 +1,16 @@ +import { BushListener } from '#lib'; +import { ContextMenuCommandHandlerEvents } from 'discord-akairo'; + +export default class ContextCommandNotFoundListener extends BushListener { + public constructor() { + super('contextCommandNotFound', { + emitter: 'contextMenuCommandHandler', + event: 'notFound', + category: 'contextCommands' + }); + } + + public override async exec(...[interaction]: ContextMenuCommandHandlerEvents['notFound']) { + void client.console.info('contextCommandNotFound', `<<${interaction?.commandName}>> could not be found.`); + } +} diff --git a/src/listeners/contextCommands/contextCommandStarted.ts b/src/listeners/contextCommands/contextCommandStarted.ts new file mode 100644 index 0000000..91eceaa --- /dev/null +++ b/src/listeners/contextCommands/contextCommandStarted.ts @@ -0,0 +1,27 @@ +import { BushListener } from '#lib'; +import { ContextMenuCommandHandlerEvents } from 'discord-akairo'; +import { ChannelType } from 'discord.js'; + +export default class ContextCommandStartedListener extends BushListener { + public constructor() { + super('contextCommandStarted', { + emitter: 'contextMenuCommandHandler', + event: 'started', + category: 'contextCommands' + }); + } + + public override async exec(...[interaction, command]: ContextMenuCommandHandlerEvents['started']) { + return void client.logger.info( + 'contextCommandStarted', + `The <<${command.id}>> command was used by <<${interaction.user.tag}>> in ${ + interaction.channel + ? interaction.channel.type === ChannelType.DM + ? `their <<DMs>>` + : `<<#${interaction.channel.name}>> in <<${interaction.guild?.name}>>` + : 'unknown' + }.`, + true + ); + } +} |