diff options
147 files changed, 1167 insertions, 1023 deletions
diff --git a/src/arguments/globalUser.ts b/src/arguments/globalUser.ts index df4f5d9..4324aa9 100644 --- a/src/arguments/globalUser.ts +++ b/src/arguments/globalUser.ts @@ -2,6 +2,6 @@ import type { BushArgumentTypeCaster } from '#lib'; import type { User } from 'discord.js'; // resolve non-cached users -export const globalUser: BushArgumentTypeCaster<Promise<User | null>> = async (_, phrase) => { - return client.users.resolve(phrase) ?? (await client.users.fetch(`${phrase}`).catch(() => null)); +export const globalUser: BushArgumentTypeCaster<Promise<User | null>> = async (message, phrase) => { + return message.client.users.resolve(phrase) ?? (await message.client.users.fetch(`${phrase}`).catch(() => null)); }; diff --git a/src/arguments/messageLink.ts b/src/arguments/messageLink.ts index a473485..c95e42d 100644 --- a/src/arguments/messageLink.ts +++ b/src/arguments/messageLink.ts @@ -1,7 +1,7 @@ import { BushArgumentTypeCaster, regex } from '#lib'; import type { Message } from 'discord.js'; -export const messageLink: BushArgumentTypeCaster<Promise<Message | null>> = async (_, phrase) => { +export const messageLink: BushArgumentTypeCaster<Promise<Message | null>> = async (message, phrase) => { const match = new RegExp(regex.messageLink).exec(phrase); if (!match || !match.groups) return null; @@ -9,12 +9,12 @@ export const messageLink: BushArgumentTypeCaster<Promise<Message | null>> = asyn if (!guild_id || !channel_id || message_id) return null; - const guild = client.guilds.cache.get(guild_id); + const guild = message.client.guilds.cache.get(guild_id); if (!guild) return null; const channel = guild.channels.cache.get(channel_id); if (!channel || (!channel.isTextBased() && !channel.isThread())) return null; - const message = await channel.messages.fetch(message_id).catch(() => null); - return message; + const msg = await channel.messages.fetch(message_id).catch(() => null); + return msg; }; @@ -13,7 +13,6 @@ const isDry = process.argv.includes('dry'); if (!isDry) new Sentry(dirname(fileURLToPath(import.meta.url)) || process.cwd()); BushClient.extendStructures(); const client = new BushClient(config); -global.client = client; if (!isDry) await client.dbPreInit(); await client.init(); if (isDry) { diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts index 12245a9..15a1128 100644 --- a/src/commands/admin/channelPermissions.ts +++ b/src/commands/admin/channelPermissions.ts @@ -89,7 +89,7 @@ export default class ChannelPermissionsCommand extends BushCommand { { reason: 'Changing overwrites for mass channel perms command' } ); } catch (e) { - void client.console.error('channelPermissions', formatError(e, false)); + void this.client.console.error('channelPermissions', formatError(e, false)); failedChannels.push(channel); } } diff --git a/src/commands/config/_customAutomodPhrases.ts b/src/commands/config/_customAutomodPhrases.ts index d60688c..0b571e5 100644 --- a/src/commands/config/_customAutomodPhrases.ts +++ b/src/commands/config/_customAutomodPhrases.ts @@ -1,4 +1,4 @@ -// import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +// import { BushCommand, clientSendAndPermCheck, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; // import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; // export default class CustomAutomodPhrasesCommand extends BushCommand { diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts index 80acd0b..6768a1c 100644 --- a/src/commands/config/blacklist.ts +++ b/src/commands/config/blacklist.ts @@ -6,8 +6,6 @@ import { clientSendAndPermCheck, emojis, format, - getGlobal, - setGlobal, type ArgType, type CommandMessage, type SlashMessage @@ -83,10 +81,10 @@ export default class BlacklistCommand extends BushCommand { if (!global) assert(message.inGuild()); const blacklistedUsers = global - ? getGlobal('blacklistedUsers') + ? this.client.utils.getGlobal('blacklistedUsers') : (await message.guild!.getSetting('blacklistedChannels')) ?? []; const blacklistedChannels = global - ? getGlobal('blacklistedChannels') + ? this.client.utils.getGlobal('blacklistedChannels') : (await message.guild!.getSetting('blacklistedUsers')) ?? []; if (action === 'toggle') { action = blacklistedUsers.includes(targetID) || blacklistedChannels.includes(targetID) ? 'unblacklist' : 'blacklist'; @@ -100,7 +98,7 @@ export default class BlacklistCommand extends BushCommand { const key = target instanceof User ? 'blacklistedUsers' : 'blacklistedChannels'; const success = await (global - ? setGlobal(key, newValue) + ? this.client.utils.setGlobal(key, newValue) : message.guild!.setSetting(key, newValue, message.member as GuildMember) ).catch(() => false); diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts index f0db467..66e10b6 100644 --- a/src/commands/config/config.ts +++ b/src/commands/config/config.ts @@ -6,9 +6,7 @@ import { emojis, GuildNoArraySetting, guildSettingsObj, - inspectAndRedact, oxford, - prefix, settingsArr, type ArgType, type CommandMessage, @@ -284,7 +282,7 @@ export default class ConfigCommand extends BushCommand { }); collector.on('collect', async (interaction: MessageComponentInteraction) => { - if (interaction.user.id === message.author.id || client.config.owners.includes(interaction.user.id)) { + if (interaction.user.id === message.author.id || this.client.config.owners.includes(interaction.user.id)) { assert(message.inGuild()); switch (interaction.customId) { @@ -346,7 +344,7 @@ export default class ConfigCommand extends BushCommand { const func = ((): ((v: string | any) => string) => { switch (type.replace('-array', '') as BaseSettingTypes) { case 'string': - return (v) => inspectAndRedact(v); + return (v) => this.client.utils.inspectAndRedact(v); case 'channel': return (v) => `<#${v}>`; case 'role': @@ -354,7 +352,7 @@ export default class ConfigCommand extends BushCommand { case 'user': return (v) => `<@${v}>`; case 'custom': - return inspectAndRedact; + return this.client.utils.inspectAndRedact; default: return (v) => v; } @@ -377,7 +375,7 @@ export default class ConfigCommand extends BushCommand { ); settingsEmbed.setFooter({ - text: `Run "${prefix(message)}${message.util.parsed?.alias ?? 'config'} ${ + text: `Run "${this.client.utils.prefix(message)}${message.util.parsed?.alias ?? 'config'} ${ message.util.isSlash ? snakeCase(setting) : setting } ${guildSettingsObj[setting].type.includes('-array') ? 'add/remove' : 'set'} <value>" to set this setting.` }); diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts index 4f52b7c..e9866d5 100644 --- a/src/commands/config/disable.ts +++ b/src/commands/config/disable.ts @@ -5,8 +5,6 @@ import { BushCommand, clientSendAndPermCheck, emojis, - getGlobal, - setGlobal, type ArgType, type CommandMessage, type SlashMessage @@ -81,12 +79,14 @@ export default class DisableCommand extends BushCommand { if (DisableCommand.blacklistedCommands.includes(commandID)) return message.util.send(`${emojis.error} the ${commandID} command cannot be disabled.`); - const disabledCommands = global ? getGlobal('disabledCommands') : await message.guild.getSetting('disabledCommands'); + const disabledCommands = global + ? this.client.utils.getGlobal('disabledCommands') + : await message.guild.getSetting('disabledCommands'); if (action === 'toggle') action = disabledCommands.includes(commandID) ? 'disable' : 'enable'; const newValue = addOrRemoveFromArray(action === 'disable' ? 'add' : 'remove', disabledCommands, commandID); const success = global - ? await setGlobal('disabledCommands', newValue).catch(() => false) + ? await this.client.utils.setGlobal('disabledCommands', newValue).catch(() => false) : await message.guild.setSetting('disabledCommands', newValue, message.member!).catch(() => false); if (!success) return await message.util.reply({ diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts index e88f4b7..affcde3 100644 --- a/src/commands/config/features.ts +++ b/src/commands/config/features.ts @@ -51,7 +51,7 @@ export default class FeaturesCommand extends BushCommand { }); collector.on('collect', async (interaction: SelectMenuInteraction) => { - if (interaction.user.id === message.author.id || client.config.owners.includes(interaction.user.id)) { + if (interaction.user.id === message.author.id || this.client.config.owners.includes(interaction.user.id)) { assert(message.inGuild()); const [selected]: GuildFeatures[] = interaction.values as GuildFeatures[]; diff --git a/src/commands/dev/dm.ts b/src/commands/dev/dm.ts index f1e2bce..c1340b1 100644 --- a/src/commands/dev/dm.ts +++ b/src/commands/dev/dm.ts @@ -41,7 +41,7 @@ export default class DMCommand extends BushCommand { args: { user: ArgType<'user'>; content: ArgType<'string'> } ) { try { - await client.users.send(args.user.id, args.content); + await this.client.users.send(args.user.id, args.content); } catch (e) { return message.util.reply(`${emojis.error} There was an error sending ${format.input(args.user.tag)} a dm.`); } diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index 239a06a..2be2963 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -11,7 +11,6 @@ import { getMethods, Global, Guild, - inspectCleanRedactCodeblock, Level, ModLog, Shared, @@ -243,10 +242,11 @@ export default class EvalCommand extends BushCommand { /* eslint-disable @typescript-eslint/no-unused-vars */ const me = message.member, member = message.member, - bot = client, + bot = this.client, + client = this.client, guild = message.guild, channel = message.channel, - config = client.config, + config = this.client.config, members = message.guild?.members, roles = message.guild?.roles; /* eslint-enable @typescript-eslint/no-unused-vars */ @@ -315,7 +315,7 @@ export default class EvalCommand extends BushCommand { options.depth ??= 1; options.getters ??= true; - return inspectCleanRedactCodeblock(obj, language, options); + return this.client.utils.inspectCleanRedactCodeblock(obj, language, options); } } diff --git a/src/commands/dev/javascript.ts b/src/commands/dev/javascript.ts index 7c47f2f..e472a5a 100644 --- a/src/commands/dev/javascript.ts +++ b/src/commands/dev/javascript.ts @@ -3,7 +3,6 @@ import { clientSendAndPermCheck, colors, emojis, - inspectCleanRedactCodeblock, type ArgType, type CommandMessage, type OptArgType, @@ -60,13 +59,13 @@ export default class JavascriptCommand extends BushCommand { } const code = args.code.replace(/[“”]/g, '"').replace(/```*(?:js)?/g, ''); const embed = new EmbedBuilder(); - const input = await inspectCleanRedactCodeblock(code, 'js'); + const input = await this.client.utils.inspectCleanRedactCodeblock(code, 'js'); try { const rawOutput = /^(9\s*?\+\s*?10)|(10\s*?\+\s*?9)$/.test(code) ? '21' : new VM({ eval: true, wasm: true, timeout: 1_000, fixAsync: true }).run(`${code}`); - const output = await inspectCleanRedactCodeblock(rawOutput, 'js', { + const output = await this.client.utils.inspectCleanRedactCodeblock(rawOutput, 'js', { depth: args.sel_depth ?? 0, getters: true, inspectStrings: true, @@ -82,7 +81,7 @@ export default class JavascriptCommand extends BushCommand { embed.setTitle(`${emojis.errorFull} Unable to Evaluate Expression`).setColor(colors.error); embed.addFields([ { name: '📥 Input', value: input }, - { name: '📤 Error', value: await inspectCleanRedactCodeblock(e, 'js', { colors: false }) } + { name: '📤 Error', value: await this.client.utils.inspectCleanRedactCodeblock(e, 'js', { colors: false }) } ]); } diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts index 8c2000f..40d53eb 100644 --- a/src/commands/dev/reload.ts +++ b/src/commands/dev/reload.ts @@ -1,13 +1,4 @@ -import { - BushCommand, - clientSendAndPermCheck, - codeblock, - emojis, - formatError, - shell, - type CommandMessage, - type SlashMessage -} from '#lib'; +import { BushCommand, clientSendAndPermCheck, emojis, formatError, shell, type CommandMessage, type SlashMessage } from '#lib'; export default class ReloadCommand extends BushCommand { public constructor() { @@ -44,17 +35,19 @@ export default class ReloadCommand extends BushCommand { const s = new Date(); output = await shell(`yarn build:${/* args.fast ? 'esbuild' : */ 'tsc'}`); await Promise.all([ - client.commandHandler.reloadAll(), - client.listenerHandler.reloadAll(), - client.inhibitorHandler.reloadAll(), - client.contextMenuCommandHandler.reloadAll(), - client.taskHandler.reloadAll() + this.client.commandHandler.reloadAll(), + this.client.listenerHandler.reloadAll(), + this.client.inhibitorHandler.reloadAll(), + this.client.contextMenuCommandHandler.reloadAll(), + this.client.taskHandler.reloadAll() ]); return message.util.send(`🔁 Successfully reloaded! (${new Date().getTime() - s.getTime()}ms)`); } catch (e) { - if (output!) void client.logger.error('reloadCommand', output); - return message.util.send(`An error occurred while reloading:\n${await codeblock(formatError(e), 2048 - 34, 'js', true)}`); + if (output!) void this.client.logger.error('reloadCommand', output); + return message.util.send( + `An error occurred while reloading:\n${await this.client.utils.codeblock(formatError(e), 2048 - 34, 'js', true)}` + ); } } } diff --git a/src/commands/dev/say.ts b/src/commands/dev/say.ts index 6ec52a1..2246588 100644 --- a/src/commands/dev/say.ts +++ b/src/commands/dev/say.ts @@ -43,7 +43,7 @@ export default class SayCommand extends BushCommand { } public override async execSlash(message: SlashMessage, args: { content: string }) { - if (!client.config.owners.includes(message.author.id)) { + if (!this.client.config.owners.includes(message.author.id)) { return await message.interaction.reply({ content: `${emojis.error} Only my developers can run this command.`, ephemeral: true diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts index 28a4e5d..ab66f1c 100644 --- a/src/commands/dev/servers.ts +++ b/src/commands/dev/servers.ts @@ -26,7 +26,7 @@ export default class ServersCommand extends BushCommand { } public override async exec(message: CommandMessage | SlashMessage) { - const guilds = [...client.guilds.cache.sort((a, b) => (a.memberCount < b.memberCount ? 1 : -1)).values()]; + const guilds = [...this.client.guilds.cache.sort((a, b) => (a.memberCount < b.memberCount ? 1 : -1)).values()]; const chunkedGuilds: Guild[][] = chunk(guilds, 10); const embeds: APIEmbed[] = chunkedGuilds.map((chunk) => { return { @@ -36,7 +36,7 @@ export default class ServersCommand extends BushCommand { name: format.input(guild.name), value: stripIndent` **ID:** ${guild.id} - **Owner:** ${client.users.cache.has(guild.ownerId) ? client.users.cache.get(guild.ownerId)!.tag : guild.ownerId} + **Owner:** ${this.client.users.cache.has(guild.ownerId) ? this.client.users.cache.get(guild.ownerId)!.tag : guild.ownerId} **Members:** ${guild.memberCount.toLocaleString()}` })) } as APIEmbed; diff --git a/src/commands/dev/sh.ts b/src/commands/dev/sh.ts index f7c17bd..7ffdf27 100644 --- a/src/commands/dev/sh.ts +++ b/src/commands/dev/sh.ts @@ -2,7 +2,6 @@ import { ArgType, BushCommand, clientSendAndPermCheck, - codeblock, colors, emojis, formatError, @@ -51,7 +50,7 @@ export default class ShCommand extends BushCommand { } public override async exec(message: CommandMessage | SlashMessage, args: { command: ArgType<'string'> }) { - if (!client.config.owners.includes(message.author.id)) + if (!this.client.config.owners.includes(message.author.id)) return await message.util.reply(`${emojis.error} Only my developers can run this command.`); const input = clean(args.command); @@ -61,7 +60,7 @@ export default class ShCommand extends BushCommand { .setTimestamp() .setTitle('Shell Command') .addFields([ - { name: '📥 Input', value: await codeblock(input, 1024, 'sh', true) }, + { name: '📥 Input', value: await this.client.utils.codeblock(input, 1024, 'sh', true) }, { name: 'Running', value: emojis.loading } ]); @@ -81,12 +80,14 @@ export default class ShCommand extends BushCommand { embed.setTitle(`${emojis.successFull} Executed command successfully.`).setColor(colors.success).spliceFields(1, 1); - if (stdout) embed.addFields([{ name: '📤 stdout', value: await codeblock(stdout, 1024, 'ansi', true) }]); - if (stderr) embed.addFields([{ name: '📤 stderr', value: await codeblock(stderr, 1024, 'ansi', true) }]); + if (stdout) embed.addFields([{ name: '📤 stdout', value: await this.client.utils.codeblock(stdout, 1024, 'ansi', true) }]); + if (stderr) embed.addFields([{ name: '📤 stderr', value: await this.client.utils.codeblock(stderr, 1024, 'ansi', true) }]); } catch (e) { embed.setTitle(`${emojis.errorFull} An error occurred while executing.`).setColor(colors.error).spliceFields(1, 1); - embed.addFields([{ name: '📤 Output', value: await codeblock(formatError(e, true), 1024, 'ansi', true) }]); + embed.addFields([ + { name: '📤 Output', value: await this.client.utils.codeblock(formatError(e, true), 1024, 'ansi', true) } + ]); } await message.util.edit({ embeds: [embed] }); } diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index 3de04bf..24e8c9a 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,13 +1,4 @@ -import { - BushCommand, - clientSendAndPermCheck, - emojis, - format, - getShared, - insertOrRemoveFromShared, - type ArgType, - type CommandMessage -} from '#lib'; +import { BushCommand, clientSendAndPermCheck, emojis, format, type ArgType, type CommandMessage } from '#lib'; import { type ArgumentGeneratorReturn, type ArgumentTypeCasterReturn } from 'discord-akairo'; export default class SuperUserCommand extends BushCommand { @@ -65,14 +56,14 @@ export default class SuperUserCommand extends BushCommand { public override async exec(message: CommandMessage, args: { action: 'add' | 'remove'; user: ArgType<'user'> }) { if (!message.author.isOwner()) return await message.util.reply(`${emojis.error} Only my developers can run this command.`); - const superUsers: string[] = getShared('superUsers'); + const superUsers: string[] = this.client.utils.getShared('superUsers'); if (args.action === 'add' ? superUsers.includes(args.user.id) : !superUsers.includes(args.user.id)) return message.util.reply( `${emojis.warn} ${format.input(args.user.tag)} is ${args.action === 'add' ? 'already' : 'not'} a superuser.` ); - const success = await insertOrRemoveFromShared(args.action, 'superUsers', args.user.id).catch(() => false); + const success = await this.client.utils.insertOrRemoveFromShared(args.action, 'superUsers', args.user.id).catch(() => false); if (success) { return await message.util.reply( diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts index 9491d19..600aeac 100644 --- a/src/commands/dev/test.ts +++ b/src/commands/dev/test.ts @@ -135,15 +135,15 @@ export default class TestCommand extends BushCommand { return await message.util.reply({ content: 'this is content', components: ButtonRows, embeds }); } else if (['delete slash commands'].includes(args.feature?.toLowerCase())) { if (!message.guild) return await message.util.reply(`${emojis.error} This test can only be run in a guild.`); - await client.guilds.fetch(); + await this.client.guilds.fetch(); const promises: Promise<Collection<string, ApplicationCommand>>[] = []; - client.guilds.cache.each((guild) => { + this.client.guilds.cache.each((guild) => { promises.push(guild.commands.set([])); }); await Promise.all(promises); - await client.application!.commands.fetch(); - await client.application!.commands.set([]); + await this.client.application!.commands.fetch(); + await this.client.application!.commands.set([]); return await message.util.reply(`${emojis.success} Removed guild commands and global commands.`); } else if (['drop down', 'drop downs', 'select menu', 'select menus'].includes(args.feature?.toLowerCase())) { @@ -166,7 +166,7 @@ export default class TestCommand extends BushCommand { }); // eslint-disable-next-line @typescript-eslint/no-misused-promises - client.ws.on(GatewayDispatchEvents.InteractionCreate, async (i: any) => { + this.client.ws.on(GatewayDispatchEvents.InteractionCreate, async (i: any) => { if (i?.data?.custom_id !== 'test;modal' || i?.data?.component_type !== 2) return; if (i?.message?.id !== m.id) return; diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts index decbe04..25b860c 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -3,7 +3,6 @@ import { clientSendAndPermCheck, colors, humanizeDuration, - mapIDs, shell, type CommandMessage, type SlashMessage @@ -44,14 +43,14 @@ export default class BotInfoCommand extends BushCommand { haiku = 'Haiku' } - const developers = (await mapIDs(client.config.owners)).map((u) => u?.tag).join('\n'); + const developers = (await this.client.utils.mapIDs(this.client.config.owners)).map((u) => u?.tag).join('\n'); const currentCommit = (await shell('git rev-parse HEAD')).stdout.replace('\n', ''); let repoUrl = (await shell('git remote get-url origin')).stdout.replace('\n', ''); if (repoUrl.includes('.git')) repoUrl = repoUrl.substring(0, repoUrl.length - 4); const embed = new EmbedBuilder() .setTitle('Bot Info:') .addFields([ - { name: '**Uptime**', value: humanizeDuration(client.uptime!, 2), inline: true }, + { name: '**Uptime**', value: humanizeDuration(this.client.uptime!, 2), inline: true }, { name: '**Memory Usage**', value: `System: ${prettyBytes(os.totalmem() - os.freemem(), { binary: true })}/${prettyBytes(os.totalmem(), { @@ -62,18 +61,18 @@ export default class BotInfoCommand extends BushCommand { )}`, inline: true }, - { name: '**CPU Usage**', value: `${client.stats.cpu}%`, inline: true }, + { name: '**CPU Usage**', value: `${this.client.stats.cpu}%`, inline: true }, { name: '**Platform**', value: Platform[process.platform], inline: true }, - { name: '**Commands Used**', value: `${client.stats.commandsUsed.toLocaleString()}`, inline: true }, - { name: '**Slash Commands Used**', value: `${client.stats.slashCommandsUsed.toLocaleString()}`, inline: true }, - { name: '**Servers**', value: client.guilds.cache.size.toLocaleString(), inline: true }, - { name: '**Users**', value: client.users.cache.size.toLocaleString(), inline: true }, + { name: '**Commands Used**', value: `${this.client.stats.commandsUsed.toLocaleString()}`, inline: true }, + { name: '**Slash Commands Used**', value: `${this.client.stats.slashCommandsUsed.toLocaleString()}`, inline: true }, + { name: '**Servers**', value: this.client.guilds.cache.size.toLocaleString(), inline: true }, + { name: '**Users**', value: this.client.users.cache.size.toLocaleString(), inline: true }, { name: '**Discord.js Version**', value: discordJSVersion, inline: true }, { name: '**Node.js Version**', value: process.version.slice(1), inline: true }, - { name: '**Commands**', value: client.commandHandler.modules.size.toLocaleString(), inline: true }, - { name: '**Listeners**', value: client.listenerHandler.modules.size.toLocaleString(), inline: true }, - { name: '**Inhibitors**', value: client.inhibitorHandler.modules.size.toLocaleString(), inline: true }, - { name: '**Tasks**', value: client.taskHandler.modules.size.toLocaleString(), inline: true }, + { name: '**Commands**', value: this.client.commandHandler.modules.size.toLocaleString(), inline: true }, + { name: '**Listeners**', value: this.client.listenerHandler.modules.size.toLocaleString(), inline: true }, + { name: '**Inhibitors**', value: this.client.inhibitorHandler.modules.size.toLocaleString(), inline: true }, + { name: '**Tasks**', value: this.client.taskHandler.modules.size.toLocaleString(), inline: true }, { name: '**Current Commit**', value: `[${currentCommit.substring(0, 7)}](${repoUrl}/commit/${currentCommit})`, diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index 92999a5..060a439 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -66,7 +66,7 @@ export default class GuildInfoCommand extends BushCommand { let guild: ArgType<'guild' | 'snowflake'> | GuildPreview = args.guild ?? message.guild!; if (typeof guild === 'string') { - const preview = await client.fetchGuildPreview(`${args.guild}` as Snowflake).catch(() => undefined); + const preview = await this.client.fetchGuildPreview(`${args.guild}` as Snowflake).catch(() => undefined); if (preview) guild = preview; else return await message.util.reply(`${emojis.error} That guild is not discoverable or does not exist.`); } diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index 051fce5..1a19969 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -4,7 +4,6 @@ import { colors, format, invite, - prefix, type ArgType, type CommandMessage, type OptArgType, @@ -68,7 +67,7 @@ export default class HelpCommand extends BushCommand { const row = this.addLinks(message); const command = args.command ? typeof args.command === 'string' - ? client.commandHandler.findCommand(args.command) ?? null + ? this.client.commandHandler.findCommand(args.command) ?? null : args.command : null; @@ -82,7 +81,7 @@ export default class HelpCommand extends BushCommand { } private helpAll(message: CommandMessage | SlashMessage, args: HelpArgs, row: ActionRowBuilder<ButtonBuilder>) { - const prefix_ = prefix(message); + const prefix_ = this.client.utils.prefix(message); const embed = new EmbedBuilder() .setColor(colors.default) .setTimestamp() @@ -211,7 +210,7 @@ export default class HelpCommand extends BushCommand { if (command.restrictedGuilds?.length) restrictions.push( `__Restricted Servers__: ${command.restrictedGuilds - .map((g) => format.inlineCode(client.guilds.cache.find((g1) => g1.id === g)?.name ?? 'Unknown')) + .map((g) => format.inlineCode(this.client.guilds.cache.find((g1) => g1.id === g)?.name ?? 'Unknown')) .join(' ')}` ); if (restrictions.length) embed.addFields([{ name: '» Restrictions', value: restrictions.join('\n') }]); @@ -221,12 +220,12 @@ export default class HelpCommand extends BushCommand { private addLinks(message: CommandMessage | SlashMessage) { const row = new ActionRowBuilder<ButtonBuilder>(); - if (!client.config.isDevelopment && !client.guilds.cache.some((guild) => guild.ownerId === message.author.id)) { + if (!this.client.config.isDevelopment && !this.client.guilds.cache.some((guild) => guild.ownerId === message.author.id)) { row.addComponents([new ButtonBuilder({ style: ButtonStyle.Link, label: 'Invite Me', url: invite(this.client) })]); } - if (!client.guilds.cache.get(client.config.supportGuild.id)?.members.cache.has(message.author.id)) { + if (!this.client.guilds.cache.get(this.client.config.supportGuild.id)?.members.cache.has(message.author.id)) { row.addComponents([ - new ButtonBuilder({ style: ButtonStyle.Link, label: 'Support Server', url: client.config.supportGuild.invite }) + new ButtonBuilder({ style: ButtonStyle.Link, label: 'Support Server', url: this.client.config.supportGuild.invite }) ]); } if (packageDotJSON?.repository) diff --git a/src/commands/info/links.ts b/src/commands/info/links.ts index a7ff30e..41e5c4d 100644 --- a/src/commands/info/links.ts +++ b/src/commands/info/links.ts @@ -21,11 +21,11 @@ export default class LinksCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage) { const buttonRow = new ActionRowBuilder<ButtonBuilder>(); - if (!client.config.isDevelopment || message.author.isOwner()) { + if (!this.client.config.isDevelopment || message.author.isOwner()) { buttonRow.addComponents([new ButtonBuilder({ style: ButtonStyle.Link, label: 'Invite Me', url: invite(this.client) })]); } buttonRow.addComponents([ - new ButtonBuilder({ style: ButtonStyle.Link, label: 'Support Server', url: client.config.supportGuild.invite }), + new ButtonBuilder({ style: ButtonStyle.Link, label: 'Support Server', url: this.client.config.supportGuild.invite }), new ButtonBuilder({ style: ButtonStyle.Link, label: 'GitHub', url: packageDotJSON.repository }) ]); return await message.util.reply({ content: 'Here are some useful links:', components: [buttonRow] }); diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index e87ca1f..0063f4c 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -2,7 +2,6 @@ import { AllowedMentions, BushCommand, clientSendAndPermCheck, - getPronounsOf, type CommandMessage, type OptArgType, type SlashMessage @@ -40,7 +39,7 @@ export default class PronounsCommand extends BushCommand { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); - const pronouns = await getPronounsOf(user); + const pronouns = await this.client.utils.getPronounsOf(user); if (!pronouns) { return await message.util.reply({ content: `${author ? 'You do' : `${escapeMarkdown(user.tag)} does`} not appear to have any pronouns set. Please${ diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts index a28f4c5..dba1378 100644 --- a/src/commands/info/snowflake.ts +++ b/src/commands/info/snowflake.ts @@ -16,10 +16,7 @@ import { PermissionFlagsBits, SnowflakeUtil, type DeconstructedSnowflake, - type Guild, - type Role, - type Snowflake, - type User + type Snowflake } from 'discord.js'; export default class SnowflakeCommand extends BushCommand { @@ -51,8 +48,8 @@ export default class SnowflakeCommand extends BushCommand { const snowflakeEmbed = new EmbedBuilder().setTitle('Unknown :snowflake:').setColor(colors.default); // Channel - if (client.channels.cache.has(snowflake)) { - const channel = client.channels.resolve(snowflake)!; + if (this.client.channels.cache.has(snowflake)) { + const channel = this.client.channels.resolve(snowflake)!; const channelInfo = [`**Type:** ${BushChannelType[channel.type] ?? ChannelType[channel.type]}`]; if (channel.type === ChannelType.DM) { channelInfo.push( @@ -77,11 +74,11 @@ export default class SnowflakeCommand extends BushCommand { } // Guild - if (client.guilds.cache.has(snowflake)) { - const guild: Guild = client.guilds.cache.get(snowflake)!; + if (this.client.guilds.cache.has(snowflake)) { + const guild = this.client.guilds.cache.get(snowflake)!; const guildInfo = stripIndent` **Name:** ${escapeMarkdown(guild.name)} - **Owner:** ${escapeMarkdown(client.users.cache.get(guild.ownerId)?.tag ?? '¯\\_(ツ)_/¯')} (${guild.ownerId}) + **Owner:** ${escapeMarkdown(this.client.users.cache.get(guild.ownerId)?.tag ?? '¯\\_(ツ)_/¯')} (${guild.ownerId}) **Members:** ${guild.memberCount?.toLocaleString()}`; if (guild.icon) snowflakeEmbed.setThumbnail(guild.iconURL({ size: 2048 })!); snowflakeEmbed.addFields([{ name: '» Server Info', value: guildInfo }]); @@ -89,9 +86,9 @@ export default class SnowflakeCommand extends BushCommand { } // User - const fetchedUser = await client.users.fetch(`${snowflake}`).catch(() => undefined); - if (client.users.cache.has(snowflake) || fetchedUser) { - const user: User = (client.users.cache.get(snowflake) ?? fetchedUser)!; + const fetchedUser = await this.client.users.fetch(`${snowflake}`).catch(() => undefined); + if (this.client.users.cache.has(snowflake) || fetchedUser) { + const user = (this.client.users.cache.get(snowflake) ?? fetchedUser)!; const userInfo = stripIndent` **Name:** <@${user.id}> (${escapeMarkdown(user.tag)})`; if (user.avatar) snowflakeEmbed.setThumbnail(user.avatarURL({ size: 2048 })!); @@ -100,8 +97,8 @@ export default class SnowflakeCommand extends BushCommand { } // Emoji - if (client.emojis.cache.has(snowflake)) { - const emoji = client.emojis.cache.get(snowflake)!; + if (this.client.emojis.cache.has(snowflake)) { + const emoji = this.client.emojis.cache.get(snowflake)!; const emojiInfo = stripIndent` **Name:** ${escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} **Animated:** ${emoji.animated}`; @@ -112,7 +109,7 @@ export default class SnowflakeCommand extends BushCommand { // Role if (message.guild && message.guild.roles.cache.has(snowflake)) { - const role: Role = message.guild.roles.cache.get(snowflake)!; + const role = message.guild.roles.cache.get(snowflake)!; const roleInfo = stripIndent` **Name:** <@&${role.id}> (${escapeMarkdown(role.name)}) **Members:** ${role.members.size} diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index a39e28a..73a0422 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -4,8 +4,6 @@ import { clientSendAndPermCheck, colors, emojis, - getPronounsOf, - getShared, mappings, oxford, sleep, @@ -63,7 +61,7 @@ export default class UserInfoCommand extends BushCommand { ? message.author : typeof args.user === 'object' ? args.user - : await client.users.fetch(`${args.user}`).catch(() => undefined); + : await this.client.users.fetch(`${args.user}`).catch(() => undefined); if (user === undefined) return message.util.reply(`${emojis.error} Invalid user.`); const member = message.guild ? await message.guild.members.fetch(user.id).catch(() => undefined) : undefined; await user.fetch(true); // gets banner info and accent color @@ -75,7 +73,7 @@ export default class UserInfoCommand extends BushCommand { public static async makeUserInfoEmbed(user: User, member?: GuildMember, guild?: Guild | null) { const emojis = []; - const superUsers = getShared('superUsers'); + const superUsers = user.client.utils.getShared('superUsers'); const userEmbed = new EmbedBuilder() .setTitle(escapeMarkdown(user.tag)) @@ -85,7 +83,7 @@ export default class UserInfoCommand extends BushCommand { .setColor(member?.displayColor ?? colors.default); // Flags - if (client.config.owners.includes(user.id)) emojis.push(mappings.otherEmojis.Developer); + if (user.client.config.owners.includes(user.id)) emojis.push(mappings.otherEmojis.Developer); if (superUsers.includes(user.id)) emojis.push(mappings.otherEmojis.Superuser); const flags = user.flags?.toArray(); if (flags) { @@ -143,7 +141,7 @@ export default class UserInfoCommand extends BushCommand { if (user.accentColor !== null) generalInfo.push(`**Accent Color:** ${user.hexAccentColor}`); if (user.banner) generalInfo.push(`**Banner:** [link](${user.bannerURL({ extension: 'png', size: 4096 })})`); - const pronouns = await Promise.race([getPronounsOf(user), sleep(2 * Time.Second)]); // cut off request after 2 seconds + const pronouns = await Promise.race([user.client.utils.getPronounsOf(user), sleep(2 * Time.Second)]); // cut off request after 2 seconds if (pronouns && typeof pronouns === 'string' && pronouns !== 'Unspecified') generalInfo.push(`**Pronouns:** ${pronouns}`); @@ -211,7 +209,7 @@ export default class UserInfoCommand extends BushCommand { } embed.setFooter({ text: member.user.tag, - iconURL: client.emojis.cache.get(statusEmojis[member?.presence.status])?.url ?? undefined + iconURL: member.client.emojis.cache.get(statusEmojis[member?.presence.status])?.url ?? undefined }); } @@ -258,7 +256,9 @@ export default class UserInfoCommand extends BushCommand { public static async generateBotField(embed: EmbedBuilder, user: User, title = '» Bot Information') { if (!user.bot) return; - const applicationInfo = (await client.rest.get(`/applications/${user.id}/rpc`).catch(() => null)) as APIApplication | null; + const applicationInfo = (await user.client.rest + .get(`/applications/${user.id}/rpc`) + .catch(() => null)) as APIApplication | null; if (!applicationInfo) return; const flags = new ApplicationFlagsBitField(applicationInfo.flags); diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index d81d88b..7eef990 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -5,7 +5,6 @@ import { clientSendAndPermCheck, emojis, Level, - prefix, type CommandMessage, type OptArgType, type SlashMessage @@ -46,7 +45,7 @@ export default class LeaderboardCommand extends BushCommand { return await message.util.reply( `${emojis.error} This command can only be run in servers with the leveling feature enabled.${ message.member?.permissions.has(PermissionFlagsBits.ManageGuild) - ? ` You can toggle features using the \`${prefix(message)}features\` command.` + ? ` You can toggle features using the \`${this.client.utils.prefix(message)}features\` command.` : '' }` ); diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index df3e5b2..7888695 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -5,7 +5,6 @@ import { clientSendAndPermCheck, emojis, Level, - prefix, type CommandMessage, type OptArgType, type SlashMessage @@ -54,7 +53,7 @@ export default class LevelCommand extends BushCommand { return await message.util.reply( `${emojis.error} This command can only be run in servers with the leveling feature enabled.${ message.member?.permissions.has(PermissionFlagsBits.ManageGuild) - ? ` You can toggle features using the \`${prefix(message)}features\` command.` + ? ` You can toggle features using the \`${this.client.utils.prefix(message)}features\` command.` : '' }` ); diff --git a/src/commands/moderation/_activePunishments.ts b/src/commands/moderation/_activePunishments.ts index 80cecf2..06e33f2 100644 --- a/src/commands/moderation/_activePunishments.ts +++ b/src/commands/moderation/_activePunishments.ts @@ -1,4 +1,14 @@ -// import { BushCommand, ModLog, ModLogModel, OptArgType, type CommandMessage, type SlashMessage } from '#lib'; +// import { +// BushCommand, +// clientSendAndPermCheck, +// emojis, +// ModLog, +// ModLogModel, +// OptArgType, +// userGuildPermCheck, +// type CommandMessage, +// type SlashMessage +// } from '#lib'; // import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; // import { FindOptions, Op } from 'sequelize'; diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index e301fb2..598fcaa 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -7,7 +7,6 @@ import { emojis, format, Moderation, - resolveNonCachedUser, type ArgType, type CommandMessage, type OptArgType, @@ -91,7 +90,8 @@ export default class BanCommand extends BushCommand { args.days ??= message.util.parsed?.alias === 'dban' ? 1 : 0; const member = message.guild.members.cache.get(typeof args.user === 'string' ? args.user : args.user.id); - const user = member?.user ?? (await resolveNonCachedUser(typeof args.user === 'string' ? args.user : args.user.id)); + const user = + member?.user ?? (await this.client.utils.resolveNonCachedUser(typeof args.user === 'string' ? args.user : args.user.id)); if (!user) return message.util.reply(`${emojis.error} Invalid user.`); const useForce = args.force && message.author.isOwner(); diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index 3c9e726..f480844 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -122,7 +122,7 @@ export default class EvidenceCommand extends BushCommand { entry.evidence = _evidence.trim(); await entry.save(); - client.emit('bushUpdateModlog', message.member!, entry.id, 'evidence', oldEntry, entry.evidence); + this.client.emit('bushUpdateModlog', message.member!, entry.id, 'evidence', oldEntry, entry.evidence); return message.util.reply(`${emojis.success} Successfully updated the evidence for case ${format.input(entry.id)}.`); } diff --git a/src/commands/moderation/hideCase.ts b/src/commands/moderation/hideCase.ts index 0cc3de4..6724d3c 100644 --- a/src/commands/moderation/hideCase.ts +++ b/src/commands/moderation/hideCase.ts @@ -47,7 +47,7 @@ export default class HideCaseCommand extends BushCommand { entry.hidden = !entry.hidden; await entry.save(); - client.emit('bushUpdateModlog', message.member!, entry.id, 'hidden', oldEntry, entry.hidden); + this.client.emit('bushUpdateModlog', message.member!, entry.id, 'hidden', oldEntry, entry.hidden); return await message.util.reply(`${emojis.success} CaseID ${format.input(caseID)} is ${action}.`); } diff --git a/src/commands/moderation/massBan.ts b/src/commands/moderation/massBan.ts index ab9fbc8..0db5263 100644 --- a/src/commands/moderation/massBan.ts +++ b/src/commands/moderation/massBan.ts @@ -96,7 +96,7 @@ export default class MassBanCommand extends BushCommand { const res = await Promise.all(promises); const map = new Collection(res.map((r, i) => [ids[i], r])); - client.emit('massBan', message.member!, message.guild!, args.reason ? args.reason.trim() : 'No reason provided.', map); + this.client.emit('massBan', message.member!, message.guild!, args.reason ? args.reason.trim() : 'No reason provided.', map); const success = (res: BanResponse): boolean => [banResponse.SUCCESS, banResponse.DM_ERROR].includes(res as any); diff --git a/src/commands/moderation/massEvidence.ts b/src/commands/moderation/massEvidence.ts index ffe85d2..6547203 100644 --- a/src/commands/moderation/massEvidence.ts +++ b/src/commands/moderation/massEvidence.ts @@ -93,7 +93,7 @@ export default class MassEvidenceCommand extends BushCommand { return `${emojis.success} ${id} - ${case_.id}`; }); - client.emit('massEvidence', message.member!, message.guild, evidence, lines); + this.client.emit('massEvidence', message.member!, message.guild, evidence, lines); const embeds = overflowEmbed( { diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 2c0e33a..2bcc54d 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -8,7 +8,6 @@ import { emojis, humanizeDuration, ModLog, - resolveUserAsync, timestamp, userGuildPermCheck, type ArgType, @@ -61,7 +60,7 @@ export default class ModlogCommand extends BushCommand { ) { assert(message.inGuild()); - const foundUser = search instanceof User ? search : await resolveUserAsync(search); + const foundUser = search instanceof User ? search : await this.client.utils.resolveUserAsync(search); if (foundUser) { const logs = await ModLog.findAll({ where: { diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index 7ad023a..12b94d6 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -7,7 +7,6 @@ import { format, Moderation, muteResponse, - prefix, userGuildPermCheck, type ArgType, type CommandMessage, @@ -93,7 +92,7 @@ export default class MuteCommand extends BushCommand { }); const responseMessage = (): string => { - const prefix_ = prefix(message); + const prefix_ = this.client.utils.prefix(message); const victim = format.input(member.user.tag); switch (responseCode) { case muteResponse.MISSING_PERMISSIONS: diff --git a/src/commands/moderation/purge.ts b/src/commands/moderation/purge.ts index acf3897..ed5d49d 100644 --- a/src/commands/moderation/purge.ts +++ b/src/commands/moderation/purge.ts @@ -83,7 +83,7 @@ export default class PurgeCommand extends BushCommand { const purged = await message.channel!.bulkDelete(messages, true).catch(() => null); if (!purged) return message.util.reply(`${emojis.error} Failed to purge messages.`).catch(() => null); else { - client.emit('bushPurge', message.author, message.guild, message.channel!, messages); + this.client.emit('bushPurge', message.author, message.guild, message.channel!, messages); await message.util.send(`${emojis.success} Successfully purged **${purged.size}** messages.`); /* .then(async (purgeMessage) => { if (!message.util.isSlashMessage(message)) { diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index 9ac13ce..08497c7 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -5,7 +5,6 @@ import { emojis, format, Moderation, - prefix, unmuteResponse, userGuildPermCheck, type ArgType, @@ -84,7 +83,7 @@ export default class UnmuteCommand extends BushCommand { }); const responseMessage = (): string => { - const prefix_ = prefix(message); + const prefix_ = this.client.utils.prefix(message); const victim = format.input(member.user.tag); switch (responseCode) { case unmuteResponse.MISSING_PERMISSIONS: diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts index 0cadd6a..d93e0c9 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -1,4 +1,5 @@ import { AllowedMentions, BushCommand, clientSendAndPermCheck, emojis, type CommandMessage } from '#lib'; +import assert from 'assert'; import { PermissionFlagsBits } from 'discord.js'; export default class GiveawayPingCommand extends BushCommand { @@ -29,6 +30,8 @@ export default class GiveawayPingCommand extends BushCommand { } public override async exec(message: CommandMessage) { + assert(message.inGuild()); + if (!message.member!.permissions.has(PermissionFlagsBits.ManageGuild) && !message.member!.user.isOwner()) await message.util.reply(`${emojis.error} You are missing the **ManageGuild** permission.`); @@ -41,21 +44,21 @@ export default class GiveawayPingCommand extends BushCommand { }); //! Broken - /* const webhooks = await (message.channel as TextChannel | NewsChannel).fetchWebhooks(); - let webhookClient: WebhookClient; - if (webhooks.size < 1) { - const webhook = await (message.channel as TextChannel | NewsChannel).createWebhook('Giveaway ping webhook'); - webhookClient = new WebhookClient(webhook.id, webhook.token); - } else { - const webhook = webhooks.first(); - webhookClient = new WebhookClient(webhook.id, webhook.token); - } - return await webhookClient.send({ - content: - '🎉 <@&767782793261875210> Giveaway.\n\n<:mad:783046135392239626> Spamming, line breaking, gibberish etc. disqualifies you from winning. We can and will ban you from giveaways. Winners will all be checked and rerolled if needed.', - username: `${message.member.nickname || message.author.username}`, - avatarURL: message.author.avatarURL(), - allowedMentions: AllowedMentions.roles() - }); */ + // const webhooks = await message.channel.fetchWebhooks(); + // let webhookClient: WebhookClient; + // if (webhooks.size < 1) { + // const webhook = await message.channel.createWebhook('Giveaway ping webhook'); + // webhookClient = new WebhookClient(webhook.id, webhook.token); + // } else { + // const webhook = webhooks.first(); + // webhookClient = new WebhookClient(webhook.id, webhook.token); + // } + // return await webhookClient.send({ + // content: + // '🎉 <@&767782793261875210> Giveaway.\n\n<:mad:783046135392239626> Spamming, line breaking, gibberish etc. disqualifies you from winning. We can and will ban you from giveaways. Winners will all be checked and rerolled if needed.', + // username: `${message.member?.nickname ?? message.author.username}`, + // avatarURL: message.author.avatarURL(), + // allowedMentions: AllowedMentions.roles() + // }); } } diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts index 06c1ad2..47e45f9 100644 --- a/src/commands/moulberry-bush/report.ts +++ b/src/commands/moulberry-bush/report.ts @@ -112,7 +112,7 @@ export default class ReportCommand extends BushCommand { await ReportMessage.react(emojis.check); await ReportMessage.react(emojis.cross); } catch { - void client.console.warn('ReportCommand', 'Could not react to report message.'); + void this.client.console.warn('ReportCommand', 'Could not react to report message.'); } }); return await message.util.reply('Successfully made a report.'); diff --git a/src/commands/utilities/_poll.ts b/src/commands/utilities/_poll.ts index fdf6381..1ceb13a 100644 --- a/src/commands/utilities/_poll.ts +++ b/src/commands/utilities/_poll.ts @@ -1,4 +1,4 @@ -// import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +// import { BushCommand, clientSendAndPermCheck, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; // import { ApplicationCommandOptionType, ComponentType } from 'discord.js'; // export default class PollCommand extends BushCommand { diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts index dfbccfb..e5c2cdd 100644 --- a/src/commands/utilities/activity.ts +++ b/src/commands/utilities/activity.ts @@ -171,7 +171,7 @@ export default class ActivityCommand extends BushCommand { : activityTypeCaster(message, args.activity); let response: string; - const invite: any = await client.rest + const invite: any = await this.client.rest .post(`/channels/${channel.id}/invites`, { body: { validate: null, diff --git a/src/commands/utilities/calculator.ts b/src/commands/utilities/calculator.ts index 75d63a1..2204fe6 100644 --- a/src/commands/utilities/calculator.ts +++ b/src/commands/utilities/calculator.ts @@ -4,7 +4,6 @@ import { clientSendAndPermCheck, colors, emojis, - inspectCleanRedactCodeblock, type CommandMessage, type SlashMessage } from '#lib'; @@ -43,7 +42,7 @@ export default class CalculatorCommand extends BushCommand { const decodedEmbed = new EmbedBuilder().addFields([ { name: '📥 Input', - value: await inspectCleanRedactCodeblock(args.expression, 'mma') + value: await this.client.utils.inspectCleanRedactCodeblock(args.expression, 'mma') } ]); try { @@ -51,12 +50,19 @@ export default class CalculatorCommand extends BushCommand { decodedEmbed .setTitle(`${emojis.successFull} Successfully Calculated Expression`) .setColor(colors.success) - .addFields([{ name: '📤 Output', value: await inspectCleanRedactCodeblock(calculated.toString(), 'mma') }]); + .addFields([ + { name: '📤 Output', value: await this.client.utils.inspectCleanRedactCodeblock(calculated.toString(), 'mma') } + ]); } catch (error) { decodedEmbed .setTitle(`${emojis.errorFull} Unable to Calculate Expression`) .setColor(colors.error) - .addFields([{ name: `📤 Error`, value: await inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js') }]); + .addFields([ + { + name: `📤 Error`, + value: await this.client.utils.inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js') + } + ]); } return await message.util.reply({ embeds: [decodedEmbed], allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/utilities/decode.ts b/src/commands/utilities/decode.ts index cc742c8..6aabeac 100644 --- a/src/commands/utilities/decode.ts +++ b/src/commands/utilities/decode.ts @@ -5,7 +5,6 @@ import { clientSendAndPermCheck, colors, formatError, - inspectCleanRedactCodeblock, type CommandMessage, type SlashMessage } from '#lib'; @@ -64,15 +63,17 @@ export default class DecodeCommand extends BushCommand { const encodeOrDecode = capitalize(message?.util?.parsed?.alias ?? 'decoded'); const decodedEmbed = new EmbedBuilder() .setTitle(`${encodeOrDecode} Information`) - .addFields([{ name: '📥 Input', value: await inspectCleanRedactCodeblock(data) }]); + .addFields([{ name: '📥 Input', value: await this.client.utils.inspectCleanRedactCodeblock(data) }]); try { const decoded = Buffer.from(data, from).toString(to); - decodedEmbed.setColor(colors.success).addFields([{ name: '📤 Output', value: await inspectCleanRedactCodeblock(decoded) }]); + decodedEmbed + .setColor(colors.success) + .addFields([{ name: '📤 Output', value: await this.client.utils.inspectCleanRedactCodeblock(decoded) }]); } catch (error) { decodedEmbed.setColor(colors.error).addFields([ { name: `📤 Error ${encodeOrDecode.slice(1)}ing`, - value: await inspectCleanRedactCodeblock(formatError(error)) + value: await this.client.utils.inspectCleanRedactCodeblock(formatError(error)) } ]); } diff --git a/src/commands/utilities/highlight-add.ts b/src/commands/utilities/highlight-add.ts index 9624a7e..3547c90 100644 --- a/src/commands/utilities/highlight-add.ts +++ b/src/commands/utilities/highlight-add.ts @@ -59,7 +59,7 @@ export default class HighlightAddCommand extends BushCommand { } } - const res = await client.highlightManager.addHighlight(message.guild.id, message.author.id, { + const res = await this.client.highlightManager.addHighlight(message.guild.id, message.author.id, { word: args.word, regex: args.regex }); diff --git a/src/commands/utilities/highlight-clear.ts b/src/commands/utilities/highlight-clear.ts index df9f387..9e1ed62 100644 --- a/src/commands/utilities/highlight-clear.ts +++ b/src/commands/utilities/highlight-clear.ts @@ -23,7 +23,7 @@ export default class HighlightClearCommand extends BushCommand { const confirm = await ConfirmationPrompt.send(message, { content: `Are you sure you want to clear your highlight list?` }); if (!confirm) return await message.util.reply(`${emojis.warn} You decided not to clear your highlight list.`); - const success = await client.highlightManager.removeAllHighlights(message.guild.id, message.author.id); + const success = await this.client.highlightManager.removeAllHighlights(message.guild.id, message.author.id); if (!success) return await message.util.reply(`${emojis.error} There was an error clearing your highlight list.`); return await message.util.reply(`${emojis.success} Successfully cleared your highlight list.`); diff --git a/src/commands/utilities/highlight-matches.ts b/src/commands/utilities/highlight-matches.ts index b458550..7bf94fd 100644 --- a/src/commands/utilities/highlight-matches.ts +++ b/src/commands/utilities/highlight-matches.ts @@ -34,7 +34,7 @@ export default class HighlightMatchesCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { phrase: ArgType<'string'> }) { assert(message.inGuild()); - const res = await client.highlightManager.checkPhrase(message.guild.id, message.author.id, args.phrase); + const res = await this.client.highlightManager.checkPhrase(message.guild.id, message.author.id, args.phrase); if (!res.size) return await message.util.reply(`${emojis.error} You are not highlighting any words`); diff --git a/src/commands/utilities/highlight-remove.ts b/src/commands/utilities/highlight-remove.ts index bb1300a..4dddff6 100644 --- a/src/commands/utilities/highlight-remove.ts +++ b/src/commands/utilities/highlight-remove.ts @@ -31,7 +31,7 @@ export default class HighlightRemoveCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { word: ArgType<'string'> }) { assert(message.inGuild()); - const res = await client.highlightManager.removeHighlight(message.guild.id, message.author.id, args.word); + const res = await this.client.highlightManager.removeHighlight(message.guild.id, message.author.id, args.word); if (typeof res === 'string') return await message.util.reply({ content: `${emojis.error} ${res}`, allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/utilities/highlight-show.ts b/src/commands/utilities/highlight-show.ts index d966f3a..0558005 100644 --- a/src/commands/utilities/highlight-show.ts +++ b/src/commands/utilities/highlight-show.ts @@ -23,7 +23,7 @@ export default class HighlightShowCommand extends BushCommand { where: { guild: message.guild.id, user: message.author.id } }); - void client.highlightManager.syncCache(); + void this.client.highlightManager.syncCache(); if (!highlight.words.length) return message.util.reply(`${emojis.error} You are not highlighting any words.`); diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index 8114cef..057d86d 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.ts @@ -2,7 +2,6 @@ import { Arg, BushCommand, clientSendAndPermCheck, - codeblock, colors, emojis, inspect, @@ -114,6 +113,6 @@ export default class ViewRawCommand extends BushCommand { .setTimestamp(message.createdTimestamp) .setColor(message.member?.roles?.color?.color ?? colors.default) .setTitle('Raw Message Information') - .setDescription(await codeblock(content, 2048, lang)); + .setDescription(await message.client.utils.codeblock(content, 2048, lang)); } } diff --git a/src/commands/utilities/wolframAlpha.ts b/src/commands/utilities/wolframAlpha.ts index b682c85..baf764f 100644 --- a/src/commands/utilities/wolframAlpha.ts +++ b/src/commands/utilities/wolframAlpha.ts @@ -4,8 +4,6 @@ import { clientSendAndPermCheck, colors, emojis, - inspectCleanRedactCodeblock, - uploadImageToImgur, type ArgType, type CommandMessage, type SlashMessage @@ -57,12 +55,12 @@ export default class WolframAlphaCommand extends BushCommand { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); args.image && void message.util.reply({ content: `${emojis.loading} Loading...`, embeds: [] }); - const waApi = WolframAlphaAPI(client.config.credentials.wolframAlphaAppId); + const waApi = WolframAlphaAPI(this.client.config.credentials.wolframAlphaAppId); const decodedEmbed = new EmbedBuilder().addFields([ { name: '📥 Input', - value: await inspectCleanRedactCodeblock(args.expression) + value: await this.client.utils.inspectCleanRedactCodeblock(args.expression) } ]); const sendOptions: MessageOptions = { content: null, allowedMentions: AllowedMentions.none() }; @@ -73,16 +71,23 @@ export default class WolframAlphaCommand extends BushCommand { decodedEmbed.setTitle(`${emojis.successFull} Successfully Queried Expression`).setColor(colors.success); if (args.image) { - decodedEmbed.setImage(await uploadImageToImgur(calculated.split(',')[1])); + decodedEmbed.setImage(await this.client.utils.uploadImageToImgur(calculated.split(',')[1])); decodedEmbed.addFields([{ name: '📤 Output', value: '' }]); } else { - decodedEmbed.addFields([{ name: '📤 Output', value: await inspectCleanRedactCodeblock(calculated.toString()) }]); + decodedEmbed.addFields([ + { name: '📤 Output', value: await this.client.utils.inspectCleanRedactCodeblock(calculated.toString()) } + ]); } } catch (error) { decodedEmbed .setTitle(`${emojis.errorFull} Unable to Query Expression`) .setColor(colors.error) - .addFields([{ name: `📤 Error`, value: await inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js') }]); + .addFields([ + { + name: `📤 Error`, + value: await this.client.utils.inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js') + } + ]); } sendOptions.embeds = [decodedEmbed]; diff --git a/src/context-menu-commands/user/modlog.ts b/src/context-menu-commands/user/modlog.ts index 7f3103d..c78396e 100644 --- a/src/context-menu-commands/user/modlog.ts +++ b/src/context-menu-commands/user/modlog.ts @@ -25,8 +25,8 @@ export default class ModlogContextMenuCommand extends ContextMenuCommand { }); await interaction.deferReply({ ephemeral: true }); - const pseudoMessage = new SlashMessage(client, interaction as any); - pseudoMessage.util = new CommandUtil(client.commandHandler, pseudoMessage); + const pseudoMessage = new SlashMessage(this.client, interaction as any); + pseudoMessage.util = new CommandUtil(this.client.commandHandler, pseudoMessage); void new ModlogCommand().exec(pseudoMessage, { search: interaction.targetId, hidden: false }); } diff --git a/src/context-menu-commands/user/userInfo.ts b/src/context-menu-commands/user/userInfo.ts index 075b681..6d7f3b6 100644 --- a/src/context-menu-commands/user/userInfo.ts +++ b/src/context-menu-commands/user/userInfo.ts @@ -15,7 +15,7 @@ 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).catch(() => null); + const user = await this.client.users.fetch(interaction.targetId).catch(() => null); if (!user) return interaction.reply(`⁉ I couldn't find that user`); const guild = interaction.guild as Guild; diff --git a/src/inhibitors/blacklist/channelGlobalBlacklist.ts b/src/inhibitors/blacklist/channelGlobalBlacklist.ts index 86ab21f..7f23604 100644 --- a/src/inhibitors/blacklist/channelGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/channelGlobalBlacklist.ts @@ -13,9 +13,9 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { public exec(message: CommandMessage | SlashMessage, command: BushCommand): boolean { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() - if (client.isOwner(message.author) || client.user!.id === message.author.id) return false; - if (client.cache.global.blacklistedChannels.includes(message.channel!.id) && !command.bypassChannelBlacklist) { - void client.console.verbose( + if (this.client.isOwner(message.author) || this.client.user!.id === message.author.id) return false; + if (this.client.cache.global.blacklistedChannels.includes(message.channel!.id) && !command.bypassChannelBlacklist) { + void this.client.console.verbose( 'channelGlobalBlacklist', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/blacklist/channelGuildBlacklist.ts b/src/inhibitors/blacklist/channelGuildBlacklist.ts index 8cf35ec..ae087bd 100644 --- a/src/inhibitors/blacklist/channelGuildBlacklist.ts +++ b/src/inhibitors/blacklist/channelGuildBlacklist.ts @@ -13,7 +13,7 @@ export default class ChannelGuildBlacklistInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() - if (client.isOwner(message.author) || client.user!.id === message.author.id) return false; + if (this.client.isOwner(message.author) || this.client.user!.id === message.author.id) return false; if ( (await message.guild.getSetting('bypassChannelBlacklist'))?.includes(message.author.id) && !command.bypassChannelBlacklist @@ -24,7 +24,7 @@ export default class ChannelGuildBlacklistInhibitor extends BushInhibitor { (await message.guild.getSetting('blacklistedChannels'))?.includes(message.channel!.id) && !command.bypassChannelBlacklist ) { - void client.console.verbose( + void this.client.console.verbose( 'channelGuildBlacklist', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/blacklist/guildBlacklist.ts b/src/inhibitors/blacklist/guildBlacklist.ts index 60c5755..b7df41a 100644 --- a/src/inhibitors/blacklist/guildBlacklist.ts +++ b/src/inhibitors/blacklist/guildBlacklist.ts @@ -13,10 +13,14 @@ export default class GuildBlacklistInhibitor extends BushInhibitor { public exec(message: CommandMessage | SlashMessage): boolean { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() - if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user!.id === message.author.id) + if ( + this.client.isOwner(message.author) || + this.client.isSuperUser(message.author) || + this.client.user!.id === message.author.id + ) return false; - if (client.cache.global.blacklistedGuilds.includes(message.guild.id)) { - void client.console.verbose( + if (this.client.cache.global.blacklistedGuilds.includes(message.guild.id)) { + void this.client.console.verbose( 'guildBlacklist', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/blacklist/userGlobalBlacklist.ts b/src/inhibitors/blacklist/userGlobalBlacklist.ts index bf0d4bd..e8a1306 100644 --- a/src/inhibitors/blacklist/userGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/userGlobalBlacklist.ts @@ -13,9 +13,9 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { public exec(message: CommandMessage | SlashMessage): boolean { if (!message.author) return false; // do not change to message.author.isOwner() - if (client.isOwner(message.author) || client.user!.id === message.author.id) return false; - if (client.cache.global.blacklistedUsers.includes(message.author.id)) { - void client.console.verbose( + if (this.client.isOwner(message.author) || this.client.user!.id === message.author.id) return false; + if (this.client.cache.global.blacklistedUsers.includes(message.author.id)) { + void this.client.console.verbose( 'userGlobalBlacklist', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${ message.inGuild() ? message.guild?.name : message.author.tag diff --git a/src/inhibitors/blacklist/userGuildBlacklist.ts b/src/inhibitors/blacklist/userGuildBlacklist.ts index a2ac1aa..a661606 100644 --- a/src/inhibitors/blacklist/userGuildBlacklist.ts +++ b/src/inhibitors/blacklist/userGuildBlacklist.ts @@ -13,10 +13,14 @@ export default class UserGuildBlacklistInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage): Promise<boolean> { if (!message.author || !message.inGuild()) return false; // do not change to message.author.isOwner() - if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user!.id === message.author.id) + if ( + this.client.isOwner(message.author) || + this.client.isSuperUser(message.author) || + this.client.user!.id === message.author.id + ) return false; if ((await message.guild.getSetting('blacklistedUsers'))?.includes(message.author.id)) { - void client.console.verbose( + void this.client.console.verbose( 'userGuildBlacklist', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/checks/fatal.ts b/src/inhibitors/checks/fatal.ts index a3601b5..9fda504 100644 --- a/src/inhibitors/checks/fatal.ts +++ b/src/inhibitors/checks/fatal.ts @@ -12,10 +12,11 @@ export default class FatalInhibitor extends BushInhibitor { } public async exec(message: Message | SlashMessage): Promise<boolean> { - if (client.isOwner(message.author)) return false; - for (const property in client.cache.global) { - if (!client.cache.global[property as keyof typeof client.cache.global]) { - void client.console.verbose( + if (this.client.isOwner(message.author)) return false; + const globalCache = this.client.cache.global; + for (const property in globalCache) { + if (!globalCache[property as keyof typeof globalCache]) { + void this.client.console.verbose( 'fatal', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/inhibitors/checks/guildUnavailable.ts b/src/inhibitors/checks/guildUnavailable.ts index 6c741a1..f5b62f4 100644 --- a/src/inhibitors/checks/guildUnavailable.ts +++ b/src/inhibitors/checks/guildUnavailable.ts @@ -13,7 +13,7 @@ export default class GuildUnavailableInhibitor extends BushInhibitor { public async exec(message: Message | SlashMessage): Promise<boolean> { if (message.inGuild() && !message.guild.available) { - void client.console.verbose( + void this.client.console.verbose( 'guildUnavailable', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/command/dm.ts b/src/inhibitors/command/dm.ts index 1fc43c6..5516c81 100644 --- a/src/inhibitors/command/dm.ts +++ b/src/inhibitors/command/dm.ts @@ -12,7 +12,7 @@ export default class DMInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.channel === 'dm' && message.guild) { - void client.console.verbose( + void this.client.console.verbose( 'dm', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/command/globalDisabledCommand.ts b/src/inhibitors/command/globalDisabledCommand.ts index 7e855ae..f013183 100644 --- a/src/inhibitors/command/globalDisabledCommand.ts +++ b/src/inhibitors/command/globalDisabledCommand.ts @@ -12,8 +12,8 @@ export default class DisabledGuildCommandInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (message.author.isOwner()) return false; - if (client.cache.global.disabledCommands.includes(command?.id)) { - void client.console.verbose( + if (this.client.cache.global.disabledCommands.includes(command?.id)) { + void this.client.console.verbose( 'disabledGlobalCommand', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/inhibitors/command/guild.ts b/src/inhibitors/command/guild.ts index 6ea4e6a..ea52d99 100644 --- a/src/inhibitors/command/guild.ts +++ b/src/inhibitors/command/guild.ts @@ -12,7 +12,7 @@ export default class GuildInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.channel === 'guild' && !message.guild) { - void client.console.verbose( + void this.client.console.verbose( 'guild', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.author.tag}>>.` ); diff --git a/src/inhibitors/command/guildDisabledCommand.ts b/src/inhibitors/command/guildDisabledCommand.ts index 7d63093..7fef78a 100644 --- a/src/inhibitors/command/guildDisabledCommand.ts +++ b/src/inhibitors/command/guildDisabledCommand.ts @@ -15,7 +15,7 @@ export default class DisabledGuildCommandInhibitor extends BushInhibitor { if (message.author.isOwner() || message.author.isSuperUser()) return false; // super users bypass guild disabled commands if ((await message.guild.getSetting('disabledCommands'))?.includes(command?.id)) { - void client.console.verbose( + void this.client.console.verbose( 'disabledGuildCommand', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild.name}>>.` ); diff --git a/src/inhibitors/command/nsfw.ts b/src/inhibitors/command/nsfw.ts index f8f2180..ed55b00 100644 --- a/src/inhibitors/command/nsfw.ts +++ b/src/inhibitors/command/nsfw.ts @@ -13,7 +13,7 @@ export default class NsfwInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.onlyNsfw && !(message.channel as TextChannel).nsfw) { - void client.console.verbose( + void this.client.console.verbose( 'notNsfw', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/inhibitors/command/owner.ts b/src/inhibitors/command/owner.ts index 3769d84..7a39063 100644 --- a/src/inhibitors/command/owner.ts +++ b/src/inhibitors/command/owner.ts @@ -12,8 +12,8 @@ export default class OwnerInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.ownerOnly) { - if (!client.isOwner(message.author)) { - void client.console.verbose( + if (!this.client.isOwner(message.author)) { + void this.client.console.verbose( 'owner', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/inhibitors/command/restrictedChannel.ts b/src/inhibitors/command/restrictedChannel.ts index 867756d..849166a 100644 --- a/src/inhibitors/command/restrictedChannel.ts +++ b/src/inhibitors/command/restrictedChannel.ts @@ -13,7 +13,7 @@ export default class RestrictedChannelInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.restrictedChannels?.length && message.channel) { if (!command.restrictedChannels.includes(message.channel.id)) { - void client.console.verbose( + void this.client.console.verbose( 'restrictedChannel', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/inhibitors/command/restrictedGuild.ts b/src/inhibitors/command/restrictedGuild.ts index 4d43c21..1e2d1b2 100644 --- a/src/inhibitors/command/restrictedGuild.ts +++ b/src/inhibitors/command/restrictedGuild.ts @@ -13,7 +13,7 @@ export default class RestrictedGuildInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.restrictedChannels?.length && message.channel) { if (!command.restrictedChannels.includes(message.channel.id)) { - void client.console.verbose( + void this.client.console.verbose( 'restrictedGuild', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/inhibitors/command/superUser.ts b/src/inhibitors/command/superUser.ts index 2e44b67..69e95a2 100644 --- a/src/inhibitors/command/superUser.ts +++ b/src/inhibitors/command/superUser.ts @@ -12,8 +12,8 @@ export default class SuperUserInhibitor extends BushInhibitor { public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.superUserOnly) { - if (!client.isSuperUser(message.author)) { - void client.console.verbose( + if (!this.client.isSuperUser(message.author)) { + void this.client.console.verbose( 'superUser', `Blocked message with id <<${message.id}>> from <<${message.author.tag}>> in <<${message.guild?.name}>>.` ); diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts index 7f19e63..0910352 100644 --- a/src/lib/common/AutoMod.ts +++ b/src/lib/common/AutoMod.ts @@ -1,4 +1,4 @@ -import { banResponse, codeblock, colors, emojis, format, formatError, getShared, Moderation, resolveNonCachedUser } from '#lib'; +import { banResponse, colors, emojis, format, formatError, Moderation } from '#lib'; import assert from 'assert'; import chalk from 'chalk'; import { @@ -31,7 +31,7 @@ export class AutoMod { */ private message: Message ) { - if (message.author.id === client.user?.id) return; + if (message.author.id === message.client.user?.id) return; void this.handle(); } @@ -56,9 +56,9 @@ export class AutoMod { traditional: { if (this.isImmune) break traditional; - const badLinksArray = getShared('badLinks'); - const badLinksSecretArray = getShared('badLinksSecret'); - const badWordsRaw = getShared('badWords'); + const badLinksArray = this.message.client.utils.getShared('badLinks'); + const badLinksSecretArray = this.message.client.utils.getShared('badLinksSecret'); + const badWordsRaw = this.message.client.utils.getShared('badWords'); const customAutomodPhrases = (await this.message.guild.getSetting('autoModPhases')) ?? []; const uniqueLinks = [...new Set([...badLinksArray, ...badLinksSecretArray])]; @@ -167,7 +167,9 @@ export class AutoMod { .setDescription( `**User:** ${this.message.author} (${this.message.author.tag})\n**Sent From:** <#${this.message.channel.id}> [Jump to context](${this.message.url})` ) - .addFields([{ name: 'Message Content', value: `${await codeblock(this.message.content, 1024)}` }]) + .addFields([ + { name: 'Message Content', value: `${await this.message.client.utils.codeblock(this.message.content, 1024)}` } + ]) .setColor(color) .setTimestamp() ], @@ -186,12 +188,12 @@ export class AutoMod { private async checkPerspectiveApi() { return; - if (!client.config.isDevelopment) return; + if (!this.message.client.config.isDevelopment) return; if (!this.message.content) return; - client.perspective.comments.analyze( + this.message.client.perspective.comments.analyze( { - key: client.config.credentials.perspectiveApiKey, + key: this.message.client.config.credentials.perspectiveApiKey, resource: { comment: { text: this.message.content @@ -301,7 +303,7 @@ export class AutoMod { { title: 'AutoMod Error', description: `Unable to delete triggered message.`, - fields: [{ name: 'Error', value: await codeblock(`${formatError(e)}`, 1024, 'js', true) }], + fields: [{ name: 'Error', value: await this.message.client.utils.codeblock(`${formatError(e)}`, 1024, 'js', true) }], color: colors.error } ] @@ -316,7 +318,7 @@ export class AutoMod { * @param offences The other offences that were also matched in the message */ private async log(highestOffence: BadWordDetails, color: number, offences: BadWordDetails[]) { - void client.console.info( + void this.message.client.console.info( 'autoMod', `Severity <<${highestOffence.severity}>> action performed on <<${this.message.author.tag}>> (<<${ this.message.author.id @@ -332,7 +334,9 @@ export class AutoMod { this.message.channel.id }> [Jump to context](${this.message.url})\n**Blacklisted Words:** ${offences.map((o) => `\`${o.match}\``).join(', ')}` ) - .addFields([{ name: 'Message Content', value: `${await codeblock(this.message.content, 1024)}` }]) + .addFields([ + { name: 'Message Content', value: `${await this.message.client.utils.codeblock(this.message.content, 1024)}` } + ]) .setColor(color) .setTimestamp() .setAuthor({ name: this.message.author.tag, url: this.message.author.displayAvatarURL() }) @@ -386,7 +390,7 @@ export class AutoMod { evidence: (interaction.message as Message).url ?? undefined }); - const victimUserFormatted = (await resolveNonCachedUser(userId))?.tag ?? userId; + const victimUserFormatted = (await interaction.client.utils.resolveNonCachedUser(userId))?.tag ?? userId; if (result === banResponse.SUCCESS) return interaction.reply({ content: `${emojis.success} Successfully banned **${victimUserFormatted}**.`, diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts index 9560247..708b374 100644 --- a/src/lib/common/ButtonPaginator.ts +++ b/src/lib/common/ButtonPaginator.ts @@ -97,7 +97,7 @@ export class ButtonPaginator { * @param interaction The interaction received */ protected async collect(interaction: MessageComponentInteraction) { - if (interaction.user.id !== this.message.author.id && !client.config.owners.includes(interaction.user.id)) + if (interaction.user.id !== this.message.author.id && !this.message.client.config.owners.includes(interaction.user.id)) return await interaction?.deferUpdate().catch(() => null); switch (interaction.customId) { diff --git a/src/lib/common/ConfirmationPrompt.ts b/src/lib/common/ConfirmationPrompt.ts index 4593d24..38d078a 100644 --- a/src/lib/common/ConfirmationPrompt.ts +++ b/src/lib/common/ConfirmationPrompt.ts @@ -43,7 +43,7 @@ export class ConfirmationPrompt { collector.on('collect', async (interaction: MessageComponentInteraction) => { await interaction.deferUpdate().catch(() => undefined); - if (interaction.user.id == this.message.author.id || client.config.owners.includes(interaction.user.id)) { + if (interaction.user.id == this.message.author.id || this.message.client.config.owners.includes(interaction.user.id)) { if (interaction.customId === 'confirmationPrompt_confirm') { responded = true; collector.stop(); diff --git a/src/lib/common/DeleteButton.ts b/src/lib/common/DeleteButton.ts index b561d94..bc0da17 100644 --- a/src/lib/common/DeleteButton.ts +++ b/src/lib/common/DeleteButton.ts @@ -45,7 +45,7 @@ export class DeleteButton { collector.on('collect', async (interaction: MessageComponentInteraction) => { await interaction.deferUpdate().catch(() => undefined); - if (interaction.user.id == this.message.author.id || client.config.owners.includes(interaction.user.id)) { + if (interaction.user.id == this.message.author.id || this.message.client.config.owners.includes(interaction.user.id)) { if (msg.deletable && !CommandUtil.deletedMessages.has(msg.id)) await msg.delete(); } }); diff --git a/src/lib/common/HighlightManager.ts b/src/lib/common/HighlightManager.ts index caaa6a5..cd89c89 100644 --- a/src/lib/common/HighlightManager.ts +++ b/src/lib/common/HighlightManager.ts @@ -232,10 +232,10 @@ export class HighlightManager { const lastDM = this.lastedDMedUserCooldown.get(user); if (!lastDM) break dmCooldown; - const cooldown = client.ownerID.includes(user) ? OWNER_NOTIFY_COOLDOWN : NOTIFY_COOLDOWN; + const cooldown = message.client.ownerID.includes(user) ? OWNER_NOTIFY_COOLDOWN : NOTIFY_COOLDOWN; if (new Date().getTime() - lastDM.getTime() < cooldown) { - void client.console.verbose('Highlight', `User <<${user}>> has been dmed recently.`); + void message.client.console.verbose('Highlight', `User <<${user}>> has been dmed recently.`); return false; } } @@ -248,7 +248,7 @@ export class HighlightManager { const talked = lastTalked.getTime(); if (now - talked < LAST_MESSAGE_COOLDOWN) { - void client.console.verbose('Highlight', `User <<${user}>> has talked too recently.`); + void message.client.console.verbose('Highlight', `User <<${user}>> has talked too recently.`); setTimeout(() => { const newTalked = this.userLastTalkedCooldown.get(message.guildId)?.get(user)?.getTime(); @@ -268,7 +268,7 @@ export class HighlightManager { .first(4) .reverse(); - return client.users + return message.client.users .send(user, { // eslint-disable-next-line @typescript-eslint/no-base-to-string content: `In ${format.input(message.guild.name)} ${message.channel}, your highlight "${hl.word}" was matched:`, diff --git a/src/lib/common/util/Arg.ts b/src/lib/common/util/Arg.ts index a7795b1..325f821 100644 --- a/src/lib/common/util/Arg.ts +++ b/src/lib/common/util/Arg.ts @@ -18,7 +18,7 @@ export async function cast<T extends ATC>(type: T, message: CommandMessage | Sla export async function cast<T extends KBAT>(type: T, message: CommandMessage | SlashMessage, phrase: string): Promise<BAT[T]>; export async function cast(type: AT | ATC, message: CommandMessage | SlashMessage, phrase: string): Promise<any>; export async function cast(type: ATC | AT, message: CommandMessage | SlashMessage, phrase: string): Promise<any> { - return Argument.cast(type as any, client.commandHandler.resolver, message as Message, phrase); + return Argument.cast(type as any, message.client.commandHandler.resolver, message as Message, phrase); } /** diff --git a/src/lib/common/util/Moderation.ts b/src/lib/common/util/Moderation.ts index a08dfa4..cb6b4db 100644 --- a/src/lib/common/util/Moderation.ts +++ b/src/lib/common/util/Moderation.ts @@ -5,10 +5,8 @@ import { emojis, format, Guild as GuildDB, - handleError, humanizeDuration, ModLog, - resolveNonCachedUser, type ModLogType } from '#lib'; import assert from 'assert'; @@ -16,6 +14,7 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, + Client, EmbedBuilder, PermissionFlagsBits, type Guild, @@ -129,9 +128,9 @@ export async function createModLogEntry( options: CreateModLogEntryOptions, getCaseNumber = false ): Promise<{ log: ModLog | null; caseNum: number | null }> { - const user = (await resolveNonCachedUser(options.user))!.id; - const moderator = (await resolveNonCachedUser(options.moderator))!.id; - const guild = client.guilds.resolveId(options.guild)!; + const user = (await options.client.utils.resolveNonCachedUser(options.user))!.id; + const moderator = (await options.client.utils.resolveNonCachedUser(options.moderator))!.id; + const guild = options.client.guilds.resolveId(options.guild)!; return createModLogEntrySimple( { @@ -172,7 +171,7 @@ export async function createModLogEntrySimple( hidden: options.hidden ?? false }); const saveResult: ModLog | null = await modLogEntry.save().catch(async (e) => { - await handleError('createModLogEntry', e); + await options.client.utils.handleError('createModLogEntry', e); return null; }); @@ -191,8 +190,8 @@ export async function createModLogEntrySimple( */ export async function createPunishmentEntry(options: CreatePunishmentEntryOptions): Promise<ActivePunishment | null> { const expires = options.duration ? new Date(+new Date() + options.duration ?? 0) : undefined; - const user = (await resolveNonCachedUser(options.user))!.id; - const guild = client.guilds.resolveId(options.guild)!; + const user = (await options.client.utils.resolveNonCachedUser(options.user))!.id; + const guild = options.client.guilds.resolveId(options.guild)!; const type = findTypeEnum(options.type)!; const entry = ActivePunishment.build( @@ -201,7 +200,7 @@ export async function createPunishmentEntry(options: CreatePunishmentEntryOption : { user, type, guild, expires, modlog: options.modlog } ); return await entry.save().catch(async (e) => { - await handleError('createPunishmentEntry', e); + await options.client.utils.handleError('createPunishmentEntry', e); return null; }); } @@ -212,8 +211,8 @@ export async function createPunishmentEntry(options: CreatePunishmentEntryOption * @returns Whether or not the entry was destroyed. */ export async function removePunishmentEntry(options: RemovePunishmentEntryOptions): Promise<boolean> { - const user = await resolveNonCachedUser(options.user); - const guild = client.guilds.resolveId(options.guild); + const user = await options.client.utils.resolveNonCachedUser(options.user); + const guild = options.client.guilds.resolveId(options.guild); const type = findTypeEnum(options.type); if (!user || !guild) return false; @@ -226,13 +225,13 @@ export async function removePunishmentEntry(options: RemovePunishmentEntryOption ? { user: user.id, guild: guild, type, extraInfo: options.extraInfo } : { user: user.id, guild: guild, type } }).catch(async (e) => { - await handleError('removePunishmentEntry', e); + await options.client.utils.handleError('removePunishmentEntry', e); success = false; }); if (entries) { const promises = entries.map(async (entry) => entry.destroy().catch(async (e) => { - await handleError('removePunishmentEntry', e); + await options.client.utils.handleError('removePunishmentEntry', e); success = false; }) ); @@ -298,9 +297,9 @@ export async function punishDM(options: PunishDMOptions): Promise<boolean> { new ActionRowBuilder<ButtonBuilder>({ components: [ new ButtonBuilder({ - customId: `appeal;${punishmentToPresentTense(options.punishment)};${options.guild.id};${client.users.resolveId( - options.user - )};${options.modlog}`, + customId: `appeal;${punishmentToPresentTense(options.punishment)};${ + options.guild.id + };${options.client.users.resolveId(options.user)};${options.modlog}`, style: ButtonStyle.Primary, label: 'Appeal' }).toJSON() @@ -308,7 +307,7 @@ export async function punishDM(options: PunishDMOptions): Promise<boolean> { }) ]; - const dmSuccess = await client.users + const dmSuccess = await options.client.users .send(options.user, { content, embeds: dmEmbed ? [dmEmbed] : undefined, @@ -318,7 +317,7 @@ export async function punishDM(options: PunishDMOptions): Promise<boolean> { return !!dmSuccess; } -interface BaseCreateModLogEntryOptions { +interface BaseCreateModLogEntryOptions extends BaseOptions { /** * The type of modlog entry. */ @@ -355,6 +354,11 @@ interface BaseCreateModLogEntryOptions { */ export interface CreateModLogEntryOptions extends BaseCreateModLogEntryOptions { /** + * The client. + */ + client: Client; + + /** * The user that a modlog entry is created for. */ user: GuildMemberResolvable; @@ -393,7 +397,7 @@ export interface SimpleCreateModLogEntryOptions extends BaseCreateModLogEntryOpt /** * Options for creating a punishment entry. */ -export interface CreatePunishmentEntryOptions { +export interface CreatePunishmentEntryOptions extends BaseOptions { /** * The type of punishment. */ @@ -428,7 +432,7 @@ export interface CreatePunishmentEntryOptions { /** * Options for removing a punishment entry. */ -export interface RemovePunishmentEntryOptions { +export interface RemovePunishmentEntryOptions extends BaseOptions { /** * The type of punishment. */ @@ -453,7 +457,7 @@ export interface RemovePunishmentEntryOptions { /** * Options for sending a user a punishment dm. */ -export interface PunishDMOptions { +export interface PunishDMOptions extends BaseOptions { /** * The modlog case id so the user can make an appeal. */ @@ -496,6 +500,13 @@ export interface PunishDMOptions { channel?: Snowflake; } +interface BaseOptions { + /** + * The client. + */ + client: Client; +} + export type PunishmentTypeDM = | 'warned' | 'muted' diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index b382121..68b2599 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -63,7 +63,8 @@ import { Shared } from '../../models/shared/Shared.js'; import { Stat } from '../../models/shared/Stat.js'; import { AllowedMentions } from '../../utils/AllowedMentions.js'; import { BushCache } from '../../utils/BushCache.js'; -import BushLogger from '../../utils/BushLogger.js'; +import { BushClientUtils } from '../../utils/BushClientUtils.js'; +import { BushLogger } from '../../utils/BushLogger.js'; import { ExtendedGuild } from '../discord.js/ExtendedGuild.js'; import { ExtendedGuildMember } from '../discord.js/ExtendedGuildMember.js'; import { ExtendedMessage } from '../discord.js/ExtendedMessage.js'; @@ -76,14 +77,44 @@ const { Sequelize } = (await import('sequelize')).default; declare module 'discord.js' { export interface Client extends EventEmitter { - /** - * The ID of the owner(s). - */ + /** The ID of the owner(s). */ ownerID: Snowflake | Snowflake[]; - /** - * The ID of the superUser(s). - */ + /** The ID of the superUser(s). */ superUserID: Snowflake | Snowflake[]; + /** Whether or not the client is ready. */ + customReady: boolean; + /** The configuration for the client. */ + config: Config; + /** Stats for the client. */ + stats: BushStats; + /** The handler for the bot's listeners. */ + listenerHandler: BushListenerHandler; + /** The handler for the bot's command inhibitors. */ + inhibitorHandler: BushInhibitorHandler; + /** The handler for the bot's commands. */ + commandHandler: BushCommandHandler; + /** The handler for the bot's tasks. */ + taskHandler: BushTaskHandler; + /** The handler for the bot's context menu commands. */ + contextMenuCommandHandler: ContextMenuCommandHandler; + /** The database connection for this instance of the bot (production, beta, or development). */ + instanceDB: SequelizeType; + /** The database connection that is shared between all instances of the bot. */ + sharedDB: SequelizeType; + /** A custom logging system for the bot. */ + logger: BushLogger; + /** Cached global and guild database data. */ + cache: BushCache; + /** Sentry error reporting for the bot. */ + sentry: typeof Sentry; + /** Manages most aspects of the highlight command */ + highlightManager: HighlightManager; + /** The perspective api */ + perspective: any; + /** Client utilities. */ + utils: BushClientUtils; + /** A custom logging system for the bot. */ + get console(): BushLogger; on<K extends keyof BushClientEvents>(event: K, listener: (...args: BushClientEvents[K]) => Awaitable<void>): this; once<K extends keyof BushClientEvents>(event: K, listener: (...args: BushClientEvents[K]) => Awaitable<void>): this; emit<K extends keyof BushClientEvents>(event: K, ...args: BushClientEvents[K]): boolean; @@ -126,72 +157,77 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re /** * Whether or not the client is ready. */ - public customReady = false; + public override customReady = false; /** * Stats for the client. */ - public stats: BushStats = { cpu: undefined, commandsUsed: 0n, slashCommandsUsed: 0n }; + public override stats: BushStats = { cpu: undefined, commandsUsed: 0n, slashCommandsUsed: 0n }; /** * The handler for the bot's listeners. */ - public listenerHandler: BushListenerHandler; + public override listenerHandler: BushListenerHandler; /** * The handler for the bot's command inhibitors. */ - public inhibitorHandler: BushInhibitorHandler; + public override inhibitorHandler: BushInhibitorHandler; /** * The handler for the bot's commands. */ - public commandHandler: BushCommandHandler; + public override commandHandler: BushCommandHandler; /** * The handler for the bot's tasks. */ - public taskHandler: BushTaskHandler; + public override taskHandler: BushTaskHandler; /** * The handler for the bot's context menu commands. */ - public contextMenuCommandHandler: ContextMenuCommandHandler; + public override contextMenuCommandHandler: ContextMenuCommandHandler; /** * The database connection for this instance of the bot (production, beta, or development). */ - public instanceDB: SequelizeType; + public override instanceDB: SequelizeType; /** * The database connection that is shared between all instances of the bot. */ - public sharedDB: SequelizeType; + public override sharedDB: SequelizeType; /** * A custom logging system for the bot. */ - public logger = BushLogger; + public override logger: BushLogger; /** * Cached global and guild database data. */ - public cache = new BushCache(); + public override cache = new BushCache(); /** * Sentry error reporting for the bot. */ - public sentry!: typeof Sentry; + public override sentry!: typeof Sentry; /** * Manages most aspects of the highlight command */ - public highlightManager = new HighlightManager(); + public override highlightManager = new HighlightManager(); /** * The perspective api */ - public perspective: any; + public override perspective: any; + + /** + * Client utilities. + */ + public override utils: BushClientUtils; /** * @param config The configuration for the bot. @@ -200,7 +236,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re /** * The configuration for the client. */ - public config: Config + public override config: Config ) { super({ ownerID: config.owners, @@ -220,7 +256,8 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re patch(this); this.token = config.token as If<Ready, string, string | null>; - this.config = config; + this.logger = new BushLogger(this); + this.utils = new BushClientUtils(this); /* =-=-= handlers =-=-= */ this.listenerHandler = new BushListenerHandler(this, { @@ -320,7 +357,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re /** * A custom logging system for the bot. */ - public get console(): typeof BushLogger { + public override get console(): BushLogger { return this.logger; } @@ -474,7 +511,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re await this.highlightManager.syncCache(); await UpdateCacheTask.init(this); void this.console.success('startup', `Successfully created <<cache>>.`, false); - const stats = await UpdateStatsTask.init(); + const stats = await UpdateStatsTask.init(this); this.stats.commandsUsed = stats.commandsUsed; this.stats.slashCommandsUsed = stats.slashCommandsUsed; await this.login(this.token!); @@ -500,7 +537,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re public override isSuperUser(user: UserResolvable): boolean { const userID = this.users.resolveId(user)!; - return client.cache.shared.superUsers.includes(userID) || this.config.owners.includes(userID); + return this.cache.shared.superUsers.includes(userID) || this.config.owners.includes(userID); } } diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 5fb4e06..414da09 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -34,6 +34,8 @@ import { Message, User, type ApplicationCommandOptionChoiceData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type ApplicationCommandOptionType, type PermissionResolvable, type PermissionsString, type Snowflake @@ -93,7 +95,7 @@ interface BaseBushArgumentOptions extends Omit<ArgumentOptions, 'type' | 'prompt /** * Allows you to get a discord resolved object * - * ex. get the resolved member object when the type is `USER` + * ex. get the resolved member object when the type is {@link ApplicationCommandOptionType.User User} */ slashResolve?: SlashResolveType; @@ -113,12 +115,12 @@ interface BaseBushArgumentOptions extends Omit<ArgumentOptions, 'type' | 'prompt channelTypes?: AkairoApplicationCommandChannelOptionData['channelTypes']; /** - * The minimum value for an `INTEGER` or `NUMBER` option + * The minimum value for an {@link ApplicationCommandOptionType.Integer Integer} or {@link ApplicationCommandOptionType.Number Number} option */ minValue?: number; /** - * The maximum value for an `INTEGER` or `NUMBER` option + * The maximum value for an {@link ApplicationCommandOptionType.Integer Integer} or {@link ApplicationCommandOptionType.Number Number} option */ maxValue?: number; } diff --git a/src/lib/extensions/discord-akairo/BushCommandHandler.ts b/src/lib/extensions/discord-akairo/BushCommandHandler.ts index f095356..da49af9 100644 --- a/src/lib/extensions/discord-akairo/BushCommandHandler.ts +++ b/src/lib/extensions/discord-akairo/BushCommandHandler.ts @@ -1,4 +1,4 @@ -import { type BushClient, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { CommandHandler, type Category, type CommandHandlerEvents, type CommandHandlerOptions } from 'discord-akairo'; import { type Collection, type Message, type PermissionsString } from 'discord.js'; @@ -28,13 +28,8 @@ export interface BushCommandHandlerEvents extends CommandHandlerEvents { } export class BushCommandHandler extends CommandHandler { - public declare client: BushClient; public declare modules: Collection<string, BushCommand>; public declare categories: Collection<string, Category<string, BushCommand>>; - - public constructor(client: BushClient, options: CommandHandlerOptions) { - super(client, options); - } } export interface BushCommandHandler extends CommandHandler { diff --git a/src/lib/extensions/discord-akairo/BushInhibitor.ts b/src/lib/extensions/discord-akairo/BushInhibitor.ts index b4e6797..be396cf 100644 --- a/src/lib/extensions/discord-akairo/BushInhibitor.ts +++ b/src/lib/extensions/discord-akairo/BushInhibitor.ts @@ -1,15 +1,15 @@ -import { type BushClient, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { Inhibitor } from 'discord-akairo'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Message } from 'discord.js'; export abstract class BushInhibitor extends Inhibitor { - public declare client: BushClient; - /** * Checks if message should be blocked. * A return value of true will block the message. * If returning a Promise, a resolved value of true will block the message. * - * **Note:** `all` type inhibitors do not have `message.util` defined. + * **Note:** `'all'` type inhibitors do not have {@link Message.util} defined. * * @param message - Message being handled. * @param command - Command to check. diff --git a/src/lib/extensions/discord.js/ExtendedGuild.ts b/src/lib/extensions/discord.js/ExtendedGuild.ts index c199899..c58916c 100644 --- a/src/lib/extensions/discord.js/ExtendedGuild.ts +++ b/src/lib/extensions/discord.js/ExtendedGuild.ts @@ -41,7 +41,7 @@ import _ from 'lodash'; import * as Moderation from '../../common/util/Moderation.js'; import { Guild as GuildDB } from '../../models/instance/Guild.js'; import { ModLogType } from '../../models/instance/ModLog.js'; -import { addOrRemoveFromArray, resolveNonCachedUser } from '../../utils/BushUtils.js'; +import { addOrRemoveFromArray } from '../../utils/BushUtils.js'; declare module 'discord.js' { export interface Guild { @@ -187,7 +187,7 @@ export class ExtendedGuild extends Guild { */ public override async getSetting<K extends keyof GuildModel>(setting: K): Promise<GuildModel[K]> { return ( - client.cache.guilds.get(this.id)?.[setting] ?? + this.client.cache.guilds.get(this.id)?.[setting] ?? ((await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }))[setting] ); } @@ -206,8 +206,8 @@ export class ExtendedGuild extends Guild { const row = (await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }); const oldValue = row[setting] as GuildDB[K]; row[setting] = value; - client.cache.guilds.set(this.id, row.toJSON() as GuildDB); - client.emit('bushUpdateSettings', setting, this, oldValue, row[setting], moderator); + this.client.cache.guilds.set(this.id, row.toJSON() as GuildDB); + this.client.emit('bushUpdateSettings', setting, this, oldValue, row[setting], moderator); return await row.save(); } @@ -253,7 +253,7 @@ export class ExtendedGuild extends Guild { * @param message The description of the error embed */ public override async error(title: string, message: string): Promise<void> { - void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); + void this.client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: colors.error }] }); } @@ -268,8 +268,8 @@ export class ExtendedGuild extends Guild { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const user = await resolveNonCachedUser(options.user); - const moderator = client.users.resolve(options.moderator ?? client.user!); + const user = await this.client.utils.resolveNonCachedUser(options.user); + const moderator = this.client.users.resolve(options.moderator ?? this.client.user!); if (!user || !moderator) return banResponse.CANNOT_RESOLVE_USER; if ((await this.bans.fetch()).has(user.id)) return banResponse.ALREADY_BANNED; @@ -277,6 +277,7 @@ export class ExtendedGuild extends Guild { const ret = await (async () => { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, user: user, moderator: moderator.id, @@ -290,6 +291,7 @@ export class ExtendedGuild extends Guild { // dm user dmSuccessEvent = await Moderation.punishDM({ + client: this.client, modlog: modlog.id, guild: this, user: user, @@ -310,6 +312,7 @@ export class ExtendedGuild extends Guild { // add punishment entry so they can be unbanned later const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + client: this.client, type: 'ban', user: user, guild: this, @@ -323,7 +326,7 @@ export class ExtendedGuild extends Guild { })(); if (!([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret)) - client.emit( + this.client.emit( 'bushBan', user, moderator, @@ -352,6 +355,7 @@ export class ExtendedGuild extends Guild { const ret = await (async () => { // add modlog entry const { log: modlog } = await Moderation.createModLogEntrySimple({ + client: this.client, type: ModLogType.PERM_BAN, user: options.user, moderator: options.moderator, @@ -365,6 +369,7 @@ export class ExtendedGuild extends Guild { // dm user if (this.members.cache.has(options.user)) { dmSuccessEvent = await Moderation.punishDM({ + client: this.client, modlog: modlog.id, guild: this, user: options.user, @@ -386,6 +391,7 @@ export class ExtendedGuild extends Guild { // add punishment entry so they can be unbanned later const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + client: this.client, type: 'ban', user: options.user, guild: this, @@ -411,8 +417,8 @@ export class ExtendedGuild extends Guild { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const user = await resolveNonCachedUser(options.user); - const moderator = client.users.resolve(options.moderator ?? client.user!); + const user = await this.client.utils.resolveNonCachedUser(options.user); + const moderator = this.client.users.resolve(options.moderator ?? this.client.user!); if (!user || !moderator) return unbanResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -435,6 +441,7 @@ export class ExtendedGuild extends Guild { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.UNBAN, user: user.id, moderator: moderator.id, @@ -447,6 +454,7 @@ export class ExtendedGuild extends Guild { // remove punishment entry const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({ + client: this.client, type: 'ban', user: user.id, guild: this @@ -455,6 +463,7 @@ export class ExtendedGuild extends Guild { // dm user dmSuccessEvent = await Moderation.punishDM({ + client: this.client, guild: this, user: user, punishment: 'unbanned', @@ -470,7 +479,16 @@ export class ExtendedGuild extends Guild { ret ) ) - client.emit('bushUnban', user, moderator, this, options.reason ?? undefined, caseID!, dmSuccessEvent!, options.evidence); + this.client.emit( + 'bushUnban', + user, + moderator, + this, + options.reason ?? undefined, + caseID!, + dmSuccessEvent!, + options.evidence + ); return ret; } @@ -549,7 +567,7 @@ export class ExtendedGuild extends Guild { else return `success: ${success.filter((c) => c === true).size}`; })(); - client.emit(options.unlock ? 'bushUnlockdown' : 'bushLockdown', moderator, options.reason, success, options.all); + this.client.emit(options.unlock ? 'bushUnlockdown' : 'bushLockdown', moderator, options.reason, success, options.all); return ret; } @@ -557,7 +575,7 @@ export class ExtendedGuild extends Guild { if (!channel.isTextBased() || channel.isDMBased() || channel.guildId !== this.id || !this.members.me) return null; if (!channel.permissionsFor(this.members.me).has('ManageWebhooks')) return null; - const quote = new Message(client, rawQuote); + const quote = new Message(this.client, rawQuote); const target = channel instanceof ThreadChannel ? channel.parent : channel; if (!target) return null; @@ -570,8 +588,8 @@ export class ExtendedGuild extends Guild { if (!webhook) webhook = await target .createWebhook({ - name: `${client.user!.username} Quotes #${target.name}`, - avatar: client.user!.displayAvatarURL({ size: 2048 }), + name: `${this.client.user!.username} Quotes #${target.name}`, + avatar: this.client.user!.displayAvatarURL({ size: 2048 }), reason: 'Creating a webhook for quoting' }) .catch(() => null); diff --git a/src/lib/extensions/discord.js/ExtendedGuildMember.ts b/src/lib/extensions/discord.js/ExtendedGuildMember.ts index ad29236..947f9cd 100644 --- a/src/lib/extensions/discord.js/ExtendedGuildMember.ts +++ b/src/lib/extensions/discord.js/ExtendedGuildMember.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { BushClientEvents, formatError, Moderation, ModLogType, PunishmentTypeDM, resolveNonCachedUser, Time } from '#lib'; +import { BushClientEvents, formatError, Moderation, ModLogType, PunishmentTypeDM, Time } from '#lib'; import { ChannelType, GuildChannelResolvable, @@ -129,6 +129,7 @@ export class ExtendedGuildMember extends GuildMember { sendFooter = true ): Promise<boolean> { return Moderation.punishDM({ + client: this.client, modlog, guild: this.guild, user: this, @@ -148,13 +149,14 @@ export class ExtendedGuildMember extends GuildMember { public override async bushWarn(options: BushPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }> { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return { result: warnResponse.CANNOT_RESOLVE_USER, caseNum: null }; const ret = await (async (): Promise<{ result: WarnResponse; caseNum: number | null }> => { // add modlog entry const result = await Moderation.createModLogEntry( { + client: this.client, type: ModLogType.WARN, user: this, moderator: moderator.id, @@ -178,7 +180,7 @@ export class ExtendedGuildMember extends GuildMember { return { result: warnResponse.SUCCESS, caseNum: result.caseNum }; })(); if (!([warnResponse.MODLOG_ERROR] as const).includes(ret.result) && !options.silent) - client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); + this.client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); return ret; } @@ -195,12 +197,13 @@ export class ExtendedGuildMember extends GuildMember { if (ifShouldAddRole !== true) return ifShouldAddRole; let caseID: string | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return addRoleResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { if (options.addToModlog || options.duration) { const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE, guild: this.guild, moderator: moderator.id, @@ -216,6 +219,7 @@ export class ExtendedGuildMember extends GuildMember { if (options.addToModlog || options.duration) { const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + client: this.client, type: 'role', user: this, guild: this.guild, @@ -239,7 +243,7 @@ export class ExtendedGuildMember extends GuildMember { options.addToModlog && !options.silent ) - client.emit( + this.client.emit( 'bushPunishRole', this, moderator, @@ -266,12 +270,13 @@ export class ExtendedGuildMember extends GuildMember { if (ifShouldAddRole !== true) return ifShouldAddRole; let caseID: string | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return removeRoleResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { if (options.addToModlog) { const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.REMOVE_PUNISHMENT_ROLE, guild: this.guild, moderator: moderator.id, @@ -285,6 +290,7 @@ export class ExtendedGuildMember extends GuildMember { caseID = modlog.id; const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ + client: this.client, type: 'role', user: this, guild: this.guild, @@ -311,7 +317,7 @@ export class ExtendedGuildMember extends GuildMember { options.addToModlog && !options.silent ) - client.emit( + this.client.emit( 'bushPunishRoleRemove', this, moderator, @@ -362,7 +368,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return muteResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -370,14 +376,15 @@ export class ExtendedGuildMember extends GuildMember { const muteSuccess = await this.roles .add(muteRole, `[Mute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) .catch(async (e) => { - await client.console.warn('muteRoleAddError', e); - client.console.debug(e); + await this.client.console.warn('muteRoleAddError', e); + this.client.console.debug(e); return false; }); if (!muteSuccess) return muteResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE, user: this, moderator: moderator.id, @@ -393,6 +400,7 @@ export class ExtendedGuildMember extends GuildMember { // add punishment entry so they can be unmuted later const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + client: this.client, type: 'mute', user: this, guild: this.guild, @@ -416,7 +424,7 @@ export class ExtendedGuildMember extends GuildMember { !([muteResponse.ACTION_ERROR, muteResponse.MODLOG_ERROR, muteResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) && !options.silent ) - client.emit( + this.client.emit( 'bushMute', this, moderator, @@ -448,7 +456,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return unmuteResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -456,13 +464,14 @@ export class ExtendedGuildMember extends GuildMember { const muteSuccess = await this.roles .remove(muteRole, `[Unmute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) .catch(async (e) => { - await client.console.warn('muteRoleAddError', formatError(e, true)); + await this.client.console.warn('muteRoleAddError', formatError(e, true)); return false; }); if (!muteSuccess) return unmuteResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.UNMUTE, user: this, moderator: moderator.id, @@ -477,6 +486,7 @@ export class ExtendedGuildMember extends GuildMember { // remove mute entry const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({ + client: this.client, type: 'mute', user: this, guild: this.guild @@ -500,7 +510,7 @@ export class ExtendedGuildMember extends GuildMember { ).includes(ret) && !options.silent ) - client.emit( + this.client.emit( 'bushUnmute', this, moderator, @@ -526,11 +536,12 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return kickResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.KICK, user: this, moderator: moderator.id, @@ -554,7 +565,7 @@ export class ExtendedGuildMember extends GuildMember { return kickResponse.SUCCESS; })(); if (!([kickResponse.ACTION_ERROR, kickResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) - client.emit( + this.client.emit( 'bushKick', this, moderator, @@ -580,7 +591,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return banResponse.CANNOT_RESOLVE_USER; // ignore result, they should still be banned even if their mute cannot be removed @@ -593,6 +604,7 @@ export class ExtendedGuildMember extends GuildMember { const ret = await (async () => { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, user: this, moderator: moderator.id, @@ -620,6 +632,7 @@ export class ExtendedGuildMember extends GuildMember { // add punishment entry so they can be unbanned later const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + client: this.client, type: 'ban', user: this, guild: this.guild, @@ -635,7 +648,7 @@ export class ExtendedGuildMember extends GuildMember { !([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) && !options.silent ) - client.emit( + this.client.emit( 'bushBan', this, moderator, @@ -663,7 +676,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return blockResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -677,6 +690,7 @@ export class ExtendedGuildMember extends GuildMember { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: options.duration ? ModLogType.TEMP_CHANNEL_BLOCK : ModLogType.PERM_CHANNEL_BLOCK, user: this, moderator: moderator.id, @@ -690,6 +704,7 @@ export class ExtendedGuildMember extends GuildMember { // add punishment entry so they can be unblocked later const punishmentEntrySuccess = await Moderation.createPunishmentEntry({ + client: this.client, type: 'block', user: this, guild: this.guild, @@ -703,6 +718,7 @@ export class ExtendedGuildMember extends GuildMember { const dmSuccess = options.silent ? null : await Moderation.punishDM({ + client: this.client, punishment: 'blocked', reason: options.reason ?? undefined, duration: options.duration ?? 0, @@ -724,7 +740,7 @@ export class ExtendedGuildMember extends GuildMember { ) && !options.silent ) - client.emit( + this.client.emit( 'bushBlock', this, moderator, @@ -754,7 +770,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return unblockResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -768,6 +784,7 @@ export class ExtendedGuildMember extends GuildMember { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.CHANNEL_UNBLOCK, user: this, moderator: moderator.id, @@ -781,6 +798,7 @@ export class ExtendedGuildMember extends GuildMember { // remove punishment entry const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ + client: this.client, type: 'block', user: this, guild: this.guild, @@ -792,6 +810,7 @@ export class ExtendedGuildMember extends GuildMember { const dmSuccess = options.silent ? null : await Moderation.punishDM({ + client: this.client, punishment: 'unblocked', reason: options.reason ?? undefined, guild: this.guild, @@ -812,7 +831,7 @@ export class ExtendedGuildMember extends GuildMember { !([unblockResponse.ACTION_ERROR, unblockResponse.MODLOG_ERROR, unblockResponse.ACTION_ERROR] as const).includes(ret) && !options.silent ) - client.emit( + this.client.emit( 'bushUnblock', this, moderator, @@ -839,7 +858,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return timeoutResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -852,6 +871,7 @@ export class ExtendedGuildMember extends GuildMember { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.TIMEOUT, user: this, moderator: moderator.id, @@ -876,7 +896,7 @@ export class ExtendedGuildMember extends GuildMember { })(); if (!([timeoutResponse.ACTION_ERROR, timeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) - client.emit( + this.client.emit( 'bushTimeout', this, moderator, @@ -901,7 +921,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return removeTimeoutResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -913,6 +933,7 @@ export class ExtendedGuildMember extends GuildMember { // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.REMOVE_TIMEOUT, user: this, moderator: moderator.id, @@ -936,7 +957,7 @@ export class ExtendedGuildMember extends GuildMember { })(); if (!([removeTimeoutResponse.ACTION_ERROR, removeTimeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent) - client.emit( + this.client.emit( 'bushRemoveTimeout', this, moderator, @@ -953,14 +974,14 @@ export class ExtendedGuildMember extends GuildMember { * Whether or not the user is an owner of the bot. */ public override isOwner(): boolean { - return client.isOwner(this); + return this.client.isOwner(this); } /** * Whether or not the user is a super user of the bot. */ public override isSuperUser(): boolean { - return client.isSuperUser(this); + return this.client.isSuperUser(this); } } diff --git a/src/lib/extensions/discord.js/ExtendedMessage.ts b/src/lib/extensions/discord.js/ExtendedMessage.ts index 4431077..0d8ce37 100644 --- a/src/lib/extensions/discord.js/ExtendedMessage.ts +++ b/src/lib/extensions/discord.js/ExtendedMessage.ts @@ -7,6 +7,6 @@ export class ExtendedMessage<Cached extends boolean = boolean> extends Message<C public constructor(client_: Client, data: RawMessageData) { super(client_, data); - this.util = new CommandUtil(client.commandHandler, this); + this.util = new CommandUtil(this.client.commandHandler, this); } } diff --git a/src/lib/extensions/discord.js/ExtendedUser.ts b/src/lib/extensions/discord.js/ExtendedUser.ts index 556ab85..23de523 100644 --- a/src/lib/extensions/discord.js/ExtendedUser.ts +++ b/src/lib/extensions/discord.js/ExtendedUser.ts @@ -23,13 +23,13 @@ export class ExtendedUser extends User { * Indicates whether the user is an owner of the bot. */ public override isOwner(): boolean { - return client.isOwner(this); + return this.client.isOwner(this); } /** * Indicates whether the user is a superuser of the bot. */ public override isSuperUser(): boolean { - return client.isSuperUser(this); + return this.client.isSuperUser(this); } } diff --git a/src/lib/extensions/global.ts b/src/lib/extensions/global.ts index d9cfaec..a9020d7 100644 --- a/src/lib/extensions/global.ts +++ b/src/lib/extensions/global.ts @@ -1,11 +1,5 @@ /* eslint-disable no-var */ -import type { BushClient } from '#lib'; declare global { - /** - * The bushbot client. - */ - var client: BushClient; - // eslint-disable-next-line @typescript-eslint/no-unused-vars interface ReadonlyArray<T> { includes<S, R extends `${Extract<S, string>}`>( @@ -15,3 +9,5 @@ declare global { ): searchElement is R & S; } } + +export {}; diff --git a/src/lib/models/instance/Guild.ts b/src/lib/models/instance/Guild.ts index 49bf822..7a19b72 100644 --- a/src/lib/models/instance/Guild.ts +++ b/src/lib/models/instance/Guild.ts @@ -199,12 +199,14 @@ const asGuildSetting = <T>(et: { [K in keyof T]: PartialBy<GuildSetting, 'config return et as { [K in keyof T]: GuildSetting }; }; +const { default: config } = await import('../../../../config/options.js'); + export const guildSettingsObj = asGuildSetting({ prefix: { name: 'Prefix', description: 'The phrase required to trigger text commands in this server.', type: 'string', - replaceNullWith: () => client.config.prefix + replaceNullWith: () => config.prefix }, autoPublishChannels: { name: 'Auto Publish Channels', diff --git a/src/lib/utils/BushClientUtils.ts b/src/lib/utils/BushClientUtils.ts new file mode 100644 index 0000000..44a08ef --- /dev/null +++ b/src/lib/utils/BushClientUtils.ts @@ -0,0 +1,480 @@ +import assert from 'assert'; +import { + cleanCodeBlockContent, + escapeCodeBlock, + GuildMember, + Message, + Routes, + TextChannel, + ThreadMember, + User, + type APIMessage, + type Client, + type Snowflake, + type UserResolvable +} from 'discord.js'; +import got from 'got'; +import _ from 'lodash'; +import CommandErrorListener from '../../listeners/commands/commandError.js'; +import { BushInspectOptions } from '../common/typings/BushInspectOptions.js'; +import { CodeBlockLang } from '../common/typings/CodeBlockLang.js'; +import { CommandMessage } from '../extensions/discord-akairo/BushCommand.js'; +import { SlashMessage } from '../extensions/discord-akairo/SlashMessage.js'; +import { Global } from '../models/shared/Global.js'; +import { Shared } from '../models/shared/Shared.js'; +import { GlobalCache, SharedCache } from './BushCache.js'; +import { emojis, Pronoun, PronounCode, pronounMapping, regex } from './BushConstants.js'; +import { addOrRemoveFromArray, formatError, inspect } from './BushUtils.js'; + +/** + * Utilities that require access to the client. + */ +export class BushClientUtils { + /** + * The hastebin urls used to post to hastebin, attempts to post in order + */ + #hasteURLs: string[] = [ + 'https://hst.sh', + // 'https://hasteb.in', + 'https://hastebin.com', + 'https://mystb.in', + 'https://haste.clicksminuteper.net', + 'https://paste.pythondiscord.com', + 'https://haste.unbelievaboat.com' + // 'https://haste.tyman.tech' + ]; + + public constructor(private readonly client: Client) {} + + /** + * Maps an array of user ids to user objects. + * @param ids The list of IDs to map + * @returns The list of users mapped + */ + public async mapIDs(ids: Snowflake[]): Promise<User[]> { + return await Promise.all(ids.map((id) => this.client.users.fetch(id))); + } + + /** + * Posts text to hastebin + * @param content The text to post + * @returns The url of the posted text + */ + 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()})`)); + return { error: 'content too long' }; + } else if (content.length > 400_000) { + content = content.substring(0, 400_000); + isSubstr = true; + } + for (const url of this.#hasteURLs) { + try { + const res: HastebinRes = await got.post(`${url}/documents`, { body: content }).json(); + return { url: `${url}/${res.key}`, error: isSubstr ? 'substr' : undefined }; + } catch { + void this.client.console.error('haste', `Unable to upload haste to ${url}`); + } + } + return { error: 'unable to post' }; + } + + /** + * Resolves a user-provided string into a user object, if possible + * @param text The text to try and resolve + * @returns The user resolved or null + */ + public async resolveUserAsync(text: string): Promise<User | null> { + const idReg = /\d{17,19}/; + const idMatch = text.match(idReg); + if (idMatch) { + try { + return await this.client.users.fetch(text as Snowflake); + } catch {} + } + const mentionReg = /<@!?(?<id>\d{17,19})>/; + const mentionMatch = text.match(mentionReg); + if (mentionMatch) { + try { + return await this.client.users.fetch(mentionMatch.groups!.id as Snowflake); + } catch {} + } + const user = this.client.users.cache.find((u) => u.username === text); + if (user) return user; + return null; + } + + /** + * 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 = ''; + code = escapeCodeBlock(code); + const prefix = `\`\`\`${language}\n`; + const suffix = '\n```'; + if (code.length + (prefix + suffix).length >= length) { + const haste_ = await this.haste(code, substr); + hasteOut = `Too large to display. ${ + haste_.url + ? `Hastebin: ${haste_.url}${language ? `.${language}` : ''}${haste_.error ? ` - ${haste_.error}` : ''}` + : `${emojis.error} Hastebin: ${haste_.error}` + }`; + } + + const FormattedHaste = hasteOut.length ? `\n${hasteOut}` : ''; + const shortenedCode = hasteOut ? code.substring(0, length - (prefix + FormattedHaste + suffix).length) : code; + const code3 = code.length ? prefix + shortenedCode + suffix + FormattedHaste : prefix + suffix; + if (code3.length > length) { + void this.client.console.warn(`codeblockError`, `Required Length: ${length}. Actual Length: ${code3.length}`, true); + void this.client.console.warn(`codeblockError`, code3, true); + throw new Error('code too long'); + } + return code3; + } + + /** + * 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', + betaToken: 'Beta Token', + hypixelApiKey: 'Hypixel Api Key', + wolframAlphaAppId: 'Wolfram|Alpha App ID', + dbPassword: 'Database Password' + }; + return mapping[key as keyof typeof mapping] || key; + } + + /** + * 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 { ...this.client.config.credentials, dbPassword: this.client.config.db.password }) { + const credential = { ...this.client.config.credentials, dbPassword: this.client.config.db.password }[ + credentialName as keyof typeof this.client.config.credentials + ]; + const replacement = this.#mapCredential(credentialName); + const escapeRegex = /[.*+?^${}()|[\]\\]/g; + text = text.replace(new RegExp(credential.toString().replace(escapeRegex, '\\$&'), 'g'), `[${replacement} Omitted]`); + text = text.replace( + new RegExp([...credential.toString()].reverse().join('').replace(escapeRegex, '\\$&'), 'g'), + `[${replacement} Omitted]` + ); + } + return text; + } + + /** + * 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, + language?: CodeBlockLang | '', + inspectOptions?: BushInspectOptions, + length = 1024 + ) { + input = inspect(input, inspectOptions ?? undefined); + if (inspectOptions) inspectOptions.inspectStrings = undefined; + input = cleanCodeBlockContent(input); + input = this.redact(input); + return this.codeblock(input, length, language, true); + } + + /** + * 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 = inspect(input, inspectOptions ?? undefined); + input = this.redact(input); + return this.haste(input, true); + } + + /** + * 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 = inspect(input, inspectOptions ?? undefined); + return this.redact(input); + } + + /** + * Get the global cache. + */ + public getGlobal(): GlobalCache; + /** + * Get a key from the global cache. + * @param key The key to get in the global cache. + */ + public getGlobal<K extends keyof GlobalCache>(key: K): GlobalCache[K]; + public getGlobal(key?: keyof GlobalCache) { + return key ? this.client.cache.global[key] : this.client.cache.global; + } + + /** + * Get the shared cache. + */ + public getShared(): SharedCache; + /** + * Get a key from the shared cache. + * @param key The key to get in the shared cache. + */ + public getShared<K extends keyof SharedCache>(key: K): SharedCache[K]; + public getShared(key?: keyof SharedCache) { + return key ? this.client.cache.shared[key] : this.client.cache.shared; + } + + /** + * 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 Client['cache']['global']>( + action: 'add' | 'remove', + key: K, + value: Client['cache']['global'][K][0] + ): Promise<Global | void> { + const row = + (await Global.findByPk(this.client.config.environment)) ?? + (await Global.create({ environment: this.client.config.environment })); + const oldValue: any[] = row[key]; + const newValue = addOrRemoveFromArray(action, oldValue, value); + row[key] = newValue; + this.client.cache.global[key] = newValue; + return await row.save().catch((e) => this.handleError('insertOrRemoveFromGlobal', e)); + } + + /** + * Add or remove an element from an array stored in the Shared database. + * @param action Either `add` or `remove` an element. + * @param key The key of the element in the shared cache to update. + * @param value The value to add/remove from the array. + */ + public async insertOrRemoveFromShared<K extends Exclude<keyof Client['cache']['shared'], 'badWords' | 'autoBanCode'>>( + action: 'add' | 'remove', + key: K, + value: Client['cache']['shared'][K][0] + ): Promise<Shared | void> { + const row = (await Shared.findByPk(0)) ?? (await Shared.create()); + const oldValue: any[] = row[key]; + const newValue = addOrRemoveFromArray(action, oldValue, value); + row[key] = newValue; + this.client.cache.shared[key] = newValue; + return await row.save().catch((e) => this.handleError('insertOrRemoveFromShared', e)); + } + + /** + * 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 Client['cache']['global']>( + key: K, + value: Client['cache']['global'][K] + ): Promise<Global | void> { + const row = + (await Global.findByPk(this.client.config.environment)) ?? + (await Global.create({ environment: this.client.config.environment })); + row[key] = value; + this.client.cache.global[key] = value; + return await row.save().catch((e) => this.handleError('setGlobal', e)); + } + + /** + * Updates an element in the Shared database. + * @param key The key in the shared cache to update. + * @param value The value to set the key to. + */ + public async setShared<K extends Exclude<keyof Client['cache']['shared'], 'badWords' | 'autoBanCode'>>( + key: K, + value: Client['cache']['shared'][K] + ): Promise<Shared | void> { + const row = (await Shared.findByPk(0)) ?? (await Shared.create()); + row[key] = value; + this.client.cache.shared[key] = value; + return await row.save().catch((e) => this.handleError('setShared', e)); + } + + /** + * 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 this.client.console.error(_.camelCase(context), `An error occurred:\n${formatError(error, false)}`, false); + await this.client.console.channelError({ + embeds: await CommandErrorListener.generateErrorEmbed(this.client, { type: 'unhandledRejection', error: error, context }) + }); + } + + /** + * Fetches a user from discord. + * @param user The user to fetch + * @returns Undefined if the user is not found, otherwise the user. + */ + public async resolveNonCachedUser(user: UserResolvable | undefined | null): Promise<User | undefined> { + if (user == null) return undefined; + const resolvedUser = + user instanceof User + ? user + : user instanceof GuildMember + ? user.user + : user instanceof ThreadMember + ? user.user + : user instanceof Message + ? user.author + : undefined; + + return resolvedUser ?? (await this.client.users.fetch(user as Snowflake).catch(() => undefined)); + } + + /** + * Get the pronouns of a discord user from pronoundb.org + * @param user The user to retrieve the promises of. + * @returns The human readable pronouns of the user, or undefined if they do not have any. + */ + public async getPronounsOf(user: User | Snowflake): Promise<Pronoun | undefined> { + const _user = await this.resolveNonCachedUser(user); + if (!_user) throw new Error(`Cannot find user ${user}`); + const apiRes = (await got + .get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${_user.id}`) + .json() + .catch(() => undefined)) as { pronouns: PronounCode } | undefined; + + if (!apiRes) return undefined; + assert(apiRes.pronouns); + + return pronounMapping[apiRes.pronouns!]!; + } + + /** + * Uploads an image to imgur. + * @param image The image to upload. + * @returns The url of the imgur. + */ + public async uploadImageToImgur(image: string) { + const clientId = this.client.config.credentials.imgurClientId; + + const resp = (await got + .post('https://api.imgur.com/3/upload', { + headers: { + Authorization: `Client-ID ${clientId}`, + Accept: 'application/json' + }, + form: { + image: image, + type: 'base64' + }, + followRedirect: true + }) + .json()) as { data: { link: string } }; + + return resp.data.link; + } + + /** + * Gets the prefix based off of the message. + * @param message The message to get the prefix from. + * @returns The prefix. + */ + public prefix(message: CommandMessage | SlashMessage): string { + return message.util.isSlash + ? '/' + : this.client.config.isDevelopment + ? 'dev ' + : message.util.parsed?.prefix ?? this.client.config.prefix; + } + + public async resolveMessageLinks(content: string | null): Promise<MessageLinkParts[]> { + const res: MessageLinkParts[] = []; + + if (!content) return res; + + const regex_ = new RegExp(regex.messageLink); + let match: RegExpExecArray | null; + while (((match = regex_.exec(content)), match !== null)) { + const input = match.input; + if (!match.groups || !input) continue; + if (input.startsWith('<') && input.endsWith('>')) continue; + + const { guild_id, channel_id, message_id } = match.groups; + if (!guild_id || !channel_id || !message_id) continue; + + res.push({ guild_id, channel_id, message_id }); + } + + return res; + } + + public async resolveMessagesFromLinks(content: string): Promise<APIMessage[]> { + const res: APIMessage[] = []; + + const links = await this.resolveMessageLinks(content); + if (!links.length) return []; + + for (const { guild_id, channel_id, message_id } of links) { + const guild = this.client.guilds.cache.get(guild_id); + if (!guild) continue; + const channel = guild.channels.cache.get(channel_id); + if (!channel || (!channel.isTextBased() && !channel.isThread())) continue; + + const message = (await this.client.rest + .get(Routes.channelMessage(channel_id, message_id)) + .catch(() => null)) as APIMessage | null; + if (!message) continue; + + res.push(message); + } + + return res; + } + + /** + * Gets a a configured channel as a TextChannel. + * @channel The channel to retrieve. + */ + public async getConfigChannel(channel: keyof Client['config']['channels']): Promise<TextChannel> { + return (await this.client.channels.fetch(this.client.config.channels[channel])) as unknown as TextChannel; + } +} + +interface HastebinRes { + key: string; +} + +export interface HasteResults { + url?: string; + error?: 'content too long' | 'substr' | 'unable to post'; +} + +export interface MessageLinkParts { + guild_id: Snowflake; + channel_id: Snowflake; + message_id: Snowflake; +} diff --git a/src/lib/utils/BushLogger.ts b/src/lib/utils/BushLogger.ts index 7d42574..1be58a4 100644 --- a/src/lib/utils/BushLogger.ts +++ b/src/lib/utils/BushLogger.ts @@ -1,11 +1,11 @@ import chalk from 'chalk'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { EmbedBuilder, escapeMarkdown, PartialTextBasedChannelFields, type Message } from 'discord.js'; +import { Client, EmbedBuilder, escapeMarkdown, PartialTextBasedChannelFields, type Message } from 'discord.js'; import repl, { REPLServer, REPL_MODE_STRICT } from 'repl'; import { WriteStream } from 'tty'; import { type SendMessageType } from '../extensions/discord-akairo/BushClient.js'; import { colors } from './BushConstants.js'; -import { getConfigChannel, inspect } from './BushUtils.js'; +import { inspect } from './BushUtils.js'; let REPL: REPLServer; let replGone = false; @@ -131,7 +131,14 @@ function getTimeStamp(): string { /** * Custom logging utility for the bot. */ -export default { +export class BushLogger { + public constructor( + /** + * The client. + */ + public client: Client + ) {} + /** * Logs information. Highlight information by surrounding it in `<<>>`. * @param header The header displayed before the content, displayed in cyan. @@ -139,27 +146,27 @@ export default { * @param sendChannel Should this also be logged to discord? Defaults to false. * @param depth The depth the content will inspected. Defaults to 0. */ - get log() { + public get log() { return this.info; - }, + } /** * Sends a message to the log channel. * @param message The parameter to pass to {@link PartialTextBasedChannelFields.send}. * @returns The message sent. */ - async channelLog(message: SendMessageType): Promise<Message | null> { - const channel = await getConfigChannel('log'); + public async channelLog(message: SendMessageType): Promise<Message | null> { + const channel = await this.client.utils.getConfigChannel('log'); return await channel.send(message).catch(() => null); - }, + } /** * Sends a message to the error channel. * @param message The parameter to pass to {@link PartialTextBasedChannelFields.send}. * @returns The message sent. */ - async channelError(message: SendMessageType): Promise<Message | null> { - const channel = await getConfigChannel('error'); + public async channelError(message: SendMessageType): Promise<Message | null> { + const channel = await this.client.utils.getConfigChannel('error'); if (!channel) { void this.error( 'BushLogger', @@ -171,27 +178,27 @@ export default { return null; } return await channel.send(message); - }, + } /** * 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`. */ - debug(content: any, depth = 0): void { - if (!client.config.isDevelopment) return; + public debug(content: any, depth = 0): void { + if (!this.client.config.isDevelopment) return; const newContent = inspectContent(content, depth, true); console.log(`${chalk.bgMagenta(getTimeStamp())} ${chalk.magenta('[Debug]')} ${newContent}`); - }, + } /** * Logs raw debug information. Only works in dev is enabled in the config. * @param content The content to log. */ - debugRaw(...content: any): void { - if (!client.config.isDevelopment) return; + public debugRaw(...content: any): void { + if (!this.client.config.isDevelopment) return; console.log(`${chalk.bgMagenta(getTimeStamp())} ${chalk.magenta('[Debug]')}`, ...content); - }, + } /** * Logs verbose information. Highlight information by surrounding it in `<<>>`. @@ -200,8 +207,8 @@ export default { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - async verbose(header: string, content: any, sendChannel = false, depth = 0): Promise<void> { - if (!client.config.logging.verbose) return; + public async verbose(header: string, content: any, sendChannel = false, depth = 0): Promise<void> { + if (!this.client.config.logging.verbose) return; const newContent = inspectContent(content, depth, true); console.log(`${chalk.bgGrey(getTimeStamp())} ${chalk.grey(`[${header}]`)} ${parseFormatting(newContent, 'blackBright')}`); if (!sendChannel) return; @@ -210,7 +217,7 @@ export default { .setColor(colors.gray) .setTimestamp(); await this.channelLog({ embeds: [embed] }); - }, + } /** * Logs very verbose information. Highlight information by surrounding it in `<<>>`. @@ -218,23 +225,23 @@ export default { * @param content The content to log, highlights displayed in bright black. * @param depth The depth the content will inspected. Defaults to `0`. */ - async superVerbose(header: string, content: any, depth = 0): Promise<void> { - if (!client.config.logging.verbose) return; + public async superVerbose(header: string, content: any, depth = 0): Promise<void> { + if (!this.client.config.logging.verbose) return; const newContent = inspectContent(content, depth, true); console.log( `${chalk.bgHex('#949494')(getTimeStamp())} ${chalk.hex('#949494')(`[${header}]`)} ${chalk.hex('#b3b3b3')(newContent)}` ); - }, + } /** * Logs raw very verbose information. * @param header The header printed before the content, displayed in purple. * @param content The content to log. */ - async superVerboseRaw(header: string, ...content: any[]): Promise<void> { - if (!client.config.logging.verbose) return; + public async superVerboseRaw(header: string, ...content: any[]): Promise<void> { + if (!this.client.config.logging.verbose) return; console.log(`${chalk.bgHex('#a3a3a3')(getTimeStamp())} ${chalk.hex('#a3a3a3')(`[${header}]`)}`, ...content); - }, + } /** * Logs information. Highlight information by surrounding it in `<<>>`. @@ -243,8 +250,8 @@ export default { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - async info(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { - if (!client.config.logging.info) return; + public async info(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + if (!this.client.config.logging.info) return; const newContent = inspectContent(content, depth, true); console.log(`${chalk.bgCyan(getTimeStamp())} ${chalk.cyan(`[${header}]`)} ${parseFormatting(newContent, 'blueBright')}`); if (!sendChannel) return; @@ -253,7 +260,7 @@ export default { .setColor(colors.info) .setTimestamp(); await this.channelLog({ embeds: [embed] }); - }, + } /** * Logs warnings. Highlight information by surrounding it in `<<>>`. @@ -262,7 +269,7 @@ export default { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - async warn(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + public async warn(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { const newContent = inspectContent(content, depth, true); console.warn( `${chalk.bgYellow(getTimeStamp())} ${chalk.yellow(`[${header}]`)} ${parseFormatting(newContent, 'yellowBright')}` @@ -274,7 +281,7 @@ export default { .setColor(colors.warn) .setTimestamp(); await this.channelError({ embeds: [embed] }); - }, + } /** * Logs errors. Highlight information by surrounding it in `<<>>`. @@ -283,7 +290,7 @@ export default { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - async error(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + public async error(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { const newContent = inspectContent(content, depth, true); console.warn( `${chalk.bgRedBright(getTimeStamp())} ${chalk.redBright(`[${header}]`)} ${parseFormatting(newContent, 'redBright')}` @@ -295,7 +302,7 @@ export default { .setTimestamp(); await this.channelError({ embeds: [embed] }); return; - }, + } /** * Logs successes. Highlight information by surrounding it in `<<>>`. @@ -304,7 +311,7 @@ export default { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - async success(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + public async success(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { const newContent = inspectContent(content, depth, true); console.log( `${chalk.bgGreen(getTimeStamp())} ${chalk.greenBright(`[${header}]`)} ${parseFormatting(newContent, 'greenBright')}` @@ -316,6 +323,6 @@ export default { .setTimestamp(); await this.channelLog({ embeds: [embed] }).catch(() => {}); } -}; +} /** @typedef {PartialTextBasedChannelFields} vscodeDontDeleteMyImportTy */ diff --git a/src/lib/utils/BushUtils.ts b/src/lib/utils/BushUtils.ts index 8a84d80..a6463cf 100644 --- a/src/lib/utils/BushUtils.ts +++ b/src/lib/utils/BushUtils.ts @@ -1,22 +1,12 @@ import { Arg, BushClient, - CodeBlockLang, CommandMessage, - emojis, - Global, - Pronoun, - pronounMapping, - regex, - Shared, SlashEditMessageType, SlashSendMessageType, timeUnits, type BaseBushArgumentType, type BushInspectOptions, - type GlobalCache, - type PronounCode, - type SharedCache, type SlashMessage } from '#lib'; import { humanizeDuration as humanizeDurationMod } from '@notenoughupdates/humanize-duration'; @@ -25,59 +15,25 @@ import { exec } from 'child_process'; import deepLock from 'deep-lock'; import { Util as AkairoUtil } from 'discord-akairo'; import { - cleanCodeBlockContent, Constants as DiscordConstants, EmbedBuilder, - escapeCodeBlock, - GuildMember, Message, OAuth2Scopes, PermissionFlagsBits, PermissionsBitField, - Routes, - ThreadMember, - User, - UserResolvable, type APIEmbed, type APIMessage, type CommandInteraction, type InteractionReplyOptions, - type PermissionsString, - type Snowflake, - type TextChannel + type PermissionsString } from 'discord.js'; import got from 'got'; -import _ from 'lodash'; import { inspect as inspectUtil, promisify } from 'util'; -import CommandErrorListener from '../../listeners/commands/commandError.js'; import * as Format from '../common/util/Format.js'; export type StripPrivate<T> = { [K in keyof T]: T[K] extends Record<string, any> ? StripPrivate<T[K]> : T[K] }; /** - * The hastebin urls used to post to hastebin, attempts to post in order - */ -const hasteURLs: string[] = [ - 'https://hst.sh', - // 'https://hasteb.in', - 'https://hastebin.com', - 'https://mystb.in', - 'https://haste.clicksminuteper.net', - 'https://paste.pythondiscord.com', - 'https://haste.unbelievaboat.com' - // 'https://haste.tyman.tech' -]; - -/** - * Maps an array of user ids to user objects. - * @param ids The list of IDs to map - * @returns The list of users mapped - */ -export async function mapIDs(ids: Snowflake[]): Promise<User[]> { - return await Promise.all(ids.map((id) => client.users.fetch(id))); -} - -/** * Capitalizes the first letter of the given text * @param text The text to capitalize * @returns The capitalized text @@ -96,60 +52,6 @@ export async function shell(command: string): Promise<{ stdout: string; stderr: } /** - * Posts text to hastebin - * @param content The text to post - * @returns The url of the posted text - */ -export async function haste(content: string, substr = false): Promise<HasteResults> { - let isSubstr = false; - if (content.length > 400_000 && !substr) { - void handleError('haste', new Error(`content over 400,000 characters (${content.length.toLocaleString()})`)); - return { error: 'content too long' }; - } else if (content.length > 400_000) { - content = content.substring(0, 400_000); - isSubstr = true; - } - for (const url of hasteURLs) { - try { - 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}`); - } - } - return { error: 'unable to post' }; -} - -/** - * Resolves a user-provided string into a user object, if possible - * @param text The text to try and resolve - * @returns The user resolved or null - */ -export async function resolveUserAsync(text: string): Promise<User | null> { - const idReg = /\d{17,19}/; - const idMatch = text.match(idReg); - if (idMatch) { - try { - return await client.users.fetch(text as Snowflake); - } catch { - // pass - } - } - const mentionReg = /<@!?(?<id>\d{17,19})>/; - const mentionMatch = text.match(mentionReg); - if (mentionMatch) { - try { - return await client.users.fetch(mentionMatch.groups!.id as Snowflake); - } catch { - // pass - } - } - const user = client.users.cache.find((u) => u.username === text); - if (user) return user; - return null; -} - -/** * Appends the correct ordinal to the given number * @param n The number to append an ordinal to * @returns The number with the ordinal @@ -185,46 +87,6 @@ export async function mcUUID(username: string, dashed = false): Promise<string> } /** - * 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 - */ -export async function codeblock( - code: string, - length: number, - language: CodeBlockLang | '' = '', - substr = false -): Promise<string> { - let hasteOut = ''; - code = escapeCodeBlock(code); - const prefix = `\`\`\`${language}\n`; - const suffix = '\n```'; - if (code.length + (prefix + suffix).length >= length) { - const haste_ = await haste(code, substr); - hasteOut = `Too large to display. ${ - haste_.url - ? `Hastebin: ${haste_.url}${language ? `.${language}` : ''}${haste_.error ? ` - ${haste_.error}` : ''}` - : `${emojis.error} Hastebin: ${haste_.error}` - }`; - } - - const FormattedHaste = hasteOut.length ? `\n${hasteOut}` : ''; - const shortenedCode = hasteOut ? code.substring(0, length - (prefix + FormattedHaste + suffix).length) : code; - const code3 = code.length ? prefix + shortenedCode + suffix + FormattedHaste : prefix + suffix; - if (code3.length > length) { - void client.console.warn(`codeblockError`, `Required Length: ${length}. Actual Length: ${code3.length}`, true); - void client.console.warn(`codeblockError`, code3, true); - throw new Error('code too long'); - } - return code3; -} - -/** * Generate defaults for {@link inspect}. * @param options The options to create defaults with. * @returns The default options combined with the specified options. @@ -246,44 +108,6 @@ function getDefaultInspectOptions(options?: BushInspectOptions): BushInspectOpti } /** - * 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. - */ -function mapCredential(key: string): string { - const mapping = { - token: 'Main Token', - devToken: 'Dev Token', - betaToken: 'Beta Token', - hypixelApiKey: 'Hypixel Api Key', - wolframAlphaAppId: 'Wolfram|Alpha App ID', - dbPassword: 'Database Password' - }; - return mapping[key as keyof typeof mapping] || key; -} - -/** - * Redacts credentials from a string. - * @param text The text to redact credentials from. - * @returns The redacted text. - */ -export function redact(text: string) { - for (const credentialName in { ...client.config.credentials, dbPassword: client.config.db.password }) { - const credential = { ...client.config.credentials, dbPassword: client.config.db.password }[ - credentialName as keyof typeof client.config.credentials - ]; - const replacement = mapCredential(credentialName); - const escapeRegex = /[.*+?^${}()|[\]\\]/g; - text = text.replace(new RegExp(credential.toString().replace(escapeRegex, '\\$&'), 'g'), `[${replacement} Omitted]`); - text = text.replace( - new RegExp([...credential.toString()].reverse().join('').replace(escapeRegex, '\\$&'), 'g'), - `[${replacement} Omitted]` - ); - } - return text; -} - -/** * 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. @@ -298,51 +122,6 @@ export function inspect(object: any, options?: BushInspectOptions): string { } /** - * 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. - */ -export async function inspectCleanRedactCodeblock( - input: any, - language?: CodeBlockLang | '', - inspectOptions?: BushInspectOptions, - length = 1024 -) { - input = inspect(input, inspectOptions ?? undefined); - if (inspectOptions) inspectOptions.inspectStrings = undefined; - input = cleanCodeBlockContent(input); - input = redact(input); - return codeblock(input, length, language, true); -} - -/** - * 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}. - */ -export async function inspectCleanRedactHaste(input: any, inspectOptions?: BushInspectOptions): Promise<HasteResults> { - input = inspect(input, inspectOptions ?? undefined); - input = redact(input); - return haste(input, true); -} - -/** - * 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. - */ -export function inspectAndRedact(input: any, inspectOptions?: BushInspectOptions): string { - input = inspect(input, inspectOptions ?? undefined); - return redact(input); -} - -/** * Responds to a slash command interaction. * @param interaction The interaction to respond to. * @param responseOptions The options for the response. @@ -363,14 +142,6 @@ export async function slashRespond( } /** - * Gets a a configured channel as a TextChannel. - * @channel The channel to retrieve. - */ -export async function getConfigChannel(channel: keyof typeof client['config']['channels']): Promise<TextChannel> { - return (await client.channels.fetch(client.config.channels[channel])) as unknown as TextChannel; -} - -/** * Takes an array and combines the elements using the supplied conjunction. * @param array The array to combine. * @param conjunction The conjunction to use. @@ -392,93 +163,6 @@ export function oxford(array: string[], conjunction: string, ifEmpty?: string): } /** - * Get the global cache. - */ -export function getGlobal(): GlobalCache; -/** - * Get a key from the global cache. - * @param key The key to get in the global cache. - */ -export function getGlobal<K extends keyof GlobalCache>(key: K): GlobalCache[K]; -export function getGlobal(key?: keyof GlobalCache) { - return key ? client.cache.global[key] : client.cache.global; -} - -export function getShared(): SharedCache; -export function getShared<K extends keyof SharedCache>(key: K): SharedCache[K]; -export function getShared(key?: keyof SharedCache) { - return key ? client.cache.shared[key] : client.cache.shared; -} - -/** - * 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. - */ -export async function insertOrRemoveFromGlobal<K extends keyof typeof client['cache']['global']>( - action: 'add' | 'remove', - 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 })); - const oldValue: any[] = row[key]; - const newValue = addOrRemoveFromArray(action, oldValue, value); - row[key] = newValue; - client.cache.global[key] = newValue; - return await row.save().catch((e) => handleError('insertOrRemoveFromGlobal', e)); -} - -/** - * Add or remove an element from an array stored in the Shared database. - * @param action Either `add` or `remove` an element. - * @param key The key of the element in the shared cache to update. - * @param value The value to add/remove from the array. - */ -export async function insertOrRemoveFromShared< - K extends Exclude<keyof typeof client['cache']['shared'], 'badWords' | 'autoBanCode'> ->(action: 'add' | 'remove', key: K, value: typeof client['cache']['shared'][K][0]): Promise<Shared | void> { - const row = (await Shared.findByPk(0)) ?? (await Shared.create()); - const oldValue: any[] = row[key]; - const newValue = addOrRemoveFromArray(action, oldValue, value); - row[key] = newValue; - client.cache.shared[key] = newValue; - return await row.save().catch((e) => handleError('insertOrRemoveFromShared', e)); -} - -/** - * 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. - */ -export async function 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) => handleError('setGlobal', e)); -} - -/** - * Updates an element in the Shared database. - * @param key The key in the shared cache to update. - * @param value The value to set the key to. - */ -export async function setShared<K extends Exclude<keyof typeof client['cache']['shared'], 'badWords' | 'autoBanCode'>>( - key: K, - value: typeof client['cache']['shared'][K] -): Promise<Shared | void> { - const row = (await Shared.findByPk(0)) ?? (await Shared.create()); - row[key] = value; - client.cache.shared[key] = value; - return await row.save().catch((e) => handleError('setShared', 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. @@ -643,58 +327,6 @@ export function hexToRgb(hex: string): string { export const sleep = promisify(setTimeout); /** - * Send a message in the error logging channel and console for an error. - * @param context - * @param error - */ -export async function handleError(context: string, error: Error) { - await client.console.error(_.camelCase(context), `An error occurred:\n${formatError(error, false)}`, false); - await client.console.channelError({ - embeds: await CommandErrorListener.generateErrorEmbed({ type: 'unhandledRejection', error: error, context }) - }); -} - -/** - * Fetches a user from discord. - * @param user The user to fetch - * @returns Undefined if the user is not found, otherwise the user. - */ -export async function resolveNonCachedUser(user: UserResolvable | undefined | null): Promise<User | undefined> { - if (user == null) return undefined; - const resolvedUser = - user instanceof User - ? user - : user instanceof GuildMember - ? user.user - : user instanceof ThreadMember - ? user.user - : user instanceof Message - ? user.author - : undefined; - - return resolvedUser ?? (await client.users.fetch(user as Snowflake).catch(() => undefined)); -} - -/** - * Get the pronouns of a discord user from pronoundb.org - * @param user The user to retrieve the promises of. - * @returns The human readable pronouns of the user, or undefined if they do not have any. - */ -export async function getPronounsOf(user: User | Snowflake): Promise<Pronoun | undefined> { - const _user = await resolveNonCachedUser(user); - if (!_user) throw new Error(`Cannot find user ${user}`); - const apiRes = (await got - .get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${_user.id}`) - .json() - .catch(() => undefined)) as { pronouns: PronounCode } | undefined; - - if (!apiRes) return undefined; - assert(apiRes.pronouns); - - return pronounMapping[apiRes.pronouns!]!; -} - -/** * List the methods of an object. * @param obj The object to get the methods of. * @returns A string with each method on a new line. @@ -763,31 +395,6 @@ export function getSymbols(obj: Record<string, any>): symbol[] { } /** - * Uploads an image to imgur. - * @param image The image to upload. - * @returns The url of the imgur. - */ -export async function uploadImageToImgur(image: string) { - const clientId = client.config.credentials.imgurClientId; - - const resp = (await got - .post('https://api.imgur.com/3/upload', { - headers: { - Authorization: `Client-ID ${clientId}`, - Accept: 'application/json' - }, - form: { - image: image, - type: 'base64' - }, - followRedirect: true - }) - .json()) as { data: { link: string } }; - - return resp.data.link; -} - -/** * Checks if a user has a certain guild permission (doesn't check channel permissions). * @param message The message to check the user from. * @param permissions The permissions to check for. @@ -843,15 +450,6 @@ export function clientSendAndPermCheck( return missing.length ? missing : null; } -/** - * Gets the prefix based off of the message. - * @param message The message to get the prefix from. - * @returns The prefix. - */ -export function prefix(message: CommandMessage | SlashMessage): string { - return message.util.isSlash ? '/' : client.config.isDevelopment ? 'dev ' : message.util.parsed?.prefix ?? client.config.prefix; -} - export { deepLock as deepFreeze }; export { Arg as arg }; export { Format as format }; @@ -950,48 +548,6 @@ export function overflowEmbed(embed: Omit<APIEmbed, 'description'>, lines: strin return embeds; } -export async function resolveMessageLinks(content: string | null): Promise<MessageLinkParts[]> { - const res: MessageLinkParts[] = []; - - if (!content) return res; - - const regex_ = new RegExp(regex.messageLink); - let match: RegExpExecArray | null; - while (((match = regex_.exec(content)), match !== null)) { - const input = match.input; - if (!match.groups || !input) continue; - if (input.startsWith('<') && input.endsWith('>')) continue; - - const { guild_id, channel_id, message_id } = match.groups; - if (!guild_id || !channel_id || !message_id) continue; - - res.push({ guild_id, channel_id, message_id }); - } - - return res; -} - -export async function resolveMessagesFromLinks(content: string): Promise<APIMessage[]> { - const res: APIMessage[] = []; - - const links = await resolveMessageLinks(content); - if (!links.length) return []; - - for (const { guild_id, channel_id, message_id } of links) { - const guild = client.guilds.cache.get(guild_id); - if (!guild) continue; - const channel = guild.channels.cache.get(channel_id); - if (!channel || (!channel.isTextBased() && !channel.isThread())) continue; - - const message = (await client.rest.get(Routes.channelMessage(channel_id, message_id)).catch(() => null)) as APIMessage | null; - if (!message) continue; - - res.push(message); - } - - return res; -} - /** * Formats an error into a string. * @param error The error to format. @@ -1011,10 +567,6 @@ export function formatError(error: Error | any, colors = false): string { return error.stack; } -interface HastebinRes { - key: string; -} - export interface UuidRes { uuid: string; username: string; @@ -1034,11 +586,6 @@ export interface UuidRes { created_at: string; } -export interface HasteResults { - url?: string; - error?: 'content too long' | 'substr' | 'unable to post'; -} - export interface ParsedDuration { duration: number | null; content: string | null; @@ -1050,9 +597,3 @@ export interface ParsedDurationRes { } export type TimestampStyle = 't' | 'T' | 'd' | 'D' | 'f' | 'F' | 'R'; - -export interface MessageLinkParts { - guild_id: Snowflake; - channel_id: Snowflake; - message_id: Snowflake; -} diff --git a/src/listeners/bush/appealListener.ts b/src/listeners/bush/appealListener.ts index 06eb245..b62031d 100644 --- a/src/listeners/bush/appealListener.ts +++ b/src/listeners/bush/appealListener.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-control-regex */ import { BushListener, colors, mappings, ModLog, type BushClientEvents } from '#lib'; import assert from 'assert'; import { EmbedBuilder } from 'discord.js'; @@ -15,7 +14,7 @@ export default class AppealListener extends BushListener { } public async exec(...[message]: BushClientEvents['messageCreate']): Promise<any> { - if (!client.config.isProduction || !message.inGuild() || message.guildId !== mappings.guilds.bush) return; + if (!this.client.config.isProduction || !message.inGuild() || message.guildId !== mappings.guilds.bush) return; if (message.author.id !== '855446927688335370' || message.embeds.length < 1) return; const userId = message.embeds[0].fields?.find?.((f) => f.name === 'What is your discord ID?')?.value; @@ -26,7 +25,7 @@ export default class AppealListener extends BushListener { name: `${message.embeds[0].fields.find((f) => f.name === 'What type of punishment are you appealing?')?.value} appeal` }); - const user = await client.users.fetch(userId, { force: true }).catch(() => null); + const user = await this.client.users.fetch(userId, { force: true }).catch(() => null); if (!user) return await thread.send({ embeds: [ diff --git a/src/listeners/bush/joinAutoBan.ts b/src/listeners/bush/joinAutoBan.ts index eae18d3..082a925 100644 --- a/src/listeners/bush/joinAutoBan.ts +++ b/src/listeners/bush/joinAutoBan.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushListener, colors, emojis, format, getShared, mappings, type BushClientEvents } from '#lib'; +import { AllowedMentions, BushListener, colors, emojis, format, mappings, type BushClientEvents } from '#lib'; import { TextChannel } from 'discord.js'; export default class JoinAutoBanListener extends BushListener { @@ -11,13 +11,13 @@ export default class JoinAutoBanListener extends BushListener { } public async exec(...[member]: BushClientEvents['guildMemberAdd']): Promise<void> { - if (!client.config.isProduction) return; + if (!this.client.config.isProduction) return; if (member.guild.id !== mappings.guilds.bush) return; const guild = member.guild; // eslint-disable-next-line @typescript-eslint/no-unused-vars const user = member.user; - const code = getShared('autoBanCode'); + const code = this.client.utils.getShared('autoBanCode'); if (!code) return; if (eval(code)) { const res = await member.bushBan({ diff --git a/src/listeners/bush/supportThread.ts b/src/listeners/bush/supportThread.ts index 3651409..1dadecb 100644 --- a/src/listeners/bush/supportThread.ts +++ b/src/listeners/bush/supportThread.ts @@ -13,7 +13,7 @@ export default class SupportThreadListener extends BushListener { } public async exec(...[message]: BushClientEvents['messageCreate']): Promise<void | undefined> { - if (!client.config.isProduction || !message.inGuild()) return; + if (!this.client.config.isProduction || !message.inGuild()) return; if (![MessageType.Default, MessageType.Reply].includes(message.type)) return; if (message.thread) return; if (message.author.bot && (message.author.id !== '444871677176709141' || !message.content.includes('uploaded a log,'))) @@ -23,10 +23,10 @@ export default class SupportThreadListener extends BushListener { if (message.channel.id !== '714332750156660756') return; // neu-support if ( - [await message.guild.getSetting('prefix'), `<@!${client.user!.id}>`, `<@${client.user!.id}>`].some((v) => + [await message.guild.getSetting('prefix'), `<@!${this.client.user!.id}>`, `<@${this.client.user!.id}>`].some((v) => message.content.trim().startsWith(v) ) && - client.commandHandler.aliases.some((alias) => message.content.includes(alias)) + this.client.commandHandler.aliases.some((alias) => message.content.includes(alias)) ) return; @@ -54,7 +54,7 @@ export default class SupportThreadListener extends BushListener { void thread .send({ embeds: [embed] }) .then(() => - client.console.info( + this.client.console.info( 'supportThread', `opened a support thread for <<${message.author.tag}>> in <<${message.channel.name}>> in <<${message.guild!.name}>>.` ) diff --git a/src/listeners/bush/userUpdateAutoBan.ts b/src/listeners/bush/userUpdateAutoBan.ts index 07381e0..54051b2 100644 --- a/src/listeners/bush/userUpdateAutoBan.ts +++ b/src/listeners/bush/userUpdateAutoBan.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushListener, colors, emojis, format, getShared, mappings, type BushClientEvents } from '#lib'; +import { AllowedMentions, BushListener, colors, emojis, format, mappings, type BushClientEvents } from '#lib'; import { GuildMember, type TextChannel } from 'discord.js'; export default class UserUpdateAutoBanListener extends BushListener { @@ -11,14 +11,14 @@ export default class UserUpdateAutoBanListener extends BushListener { } public async exec(...[_oldUser, newUser]: BushClientEvents['userUpdate']): Promise<void> { - if (!client.config.isProduction) return; + if (!this.client.config.isProduction) return; // eslint-disable-next-line @typescript-eslint/no-unused-vars const user = newUser; - const code = getShared('autoBanCode'); + const code = this.client.utils.getShared('autoBanCode'); if (!code) return; if (eval(code)) { - const member = await client.guilds.cache + const member = await this.client.guilds.cache .get(mappings.guilds.bush) ?.members.fetch(newUser.id) .catch(() => undefined); diff --git a/src/listeners/client/akairoDebug.ts b/src/listeners/client/akairoDebug.ts index 208e289..3fa7977 100644 --- a/src/listeners/client/akairoDebug.ts +++ b/src/listeners/client/akairoDebug.ts @@ -11,7 +11,7 @@ export default class DiscordJsDebugListener extends BushListener { public async exec(...[message, ...other]: BushClientEvents['debug']): Promise<void> { if (other.length && !message.includes('[registerInteractionCommands]')) - void client.console.superVerboseRaw('akairoDebug', message, ...other); - else void client.console.superVerbose('akairoDebug', message); + void this.client.console.superVerboseRaw('akairoDebug', message, ...other); + else void this.client.console.superVerbose('akairoDebug', message); } } diff --git a/src/listeners/client/dcjsDebug.ts b/src/listeners/client/dcjsDebug.ts index 56a9eab..4b80c65 100644 --- a/src/listeners/client/dcjsDebug.ts +++ b/src/listeners/client/dcjsDebug.ts @@ -10,6 +10,6 @@ export default class DiscordJsDebugListener extends BushListener { } public async exec(...[message]: BushClientEvents['debug']): Promise<void> { - void client.console.superVerbose('dc.js-debug', message); + void this.client.console.superVerbose('dc.js-debug', message); } } diff --git a/src/listeners/client/dcjsError.ts b/src/listeners/client/dcjsError.ts index c3cf8f0..a39a92d 100644 --- a/src/listeners/client/dcjsError.ts +++ b/src/listeners/client/dcjsError.ts @@ -10,6 +10,6 @@ export default class DiscordJsErrorListener extends BushListener { } public async exec(...[error]: BushClientEvents['error']): Promise<void> { - void client.console.superVerbose('dc.js-error', error); + void this.client.console.superVerbose('dc.js-error', error); } } diff --git a/src/listeners/client/dcjsWarn.ts b/src/listeners/client/dcjsWarn.ts index 6c13630..b187f0c 100644 --- a/src/listeners/client/dcjsWarn.ts +++ b/src/listeners/client/dcjsWarn.ts @@ -10,6 +10,6 @@ export default class DiscordJsWarnListener extends BushListener { } public async exec(...[message]: BushClientEvents['warn']): Promise<void> { - void client.console.superVerbose('dc.js-warn', message); + void this.client.console.superVerbose('dc.js-warn', message); } } diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts index ae9919c..a6a289c 100644 --- a/src/listeners/client/ready.ts +++ b/src/listeners/client/ready.ts @@ -14,22 +14,22 @@ export default class ReadyListener extends BushListener { public async exec(...[]: BushClientEvents['ready']) { process.emit('ready' as any); - const tag = `<<${client.user?.tag}>>`, - guildCount = `<<${client.guilds.cache.size.toLocaleString()}>>`, - userCount = `<<${client.users.cache.size.toLocaleString()}>>`; + const tag = `<<${this.client.user?.tag}>>`, + guildCount = `<<${this.client.guilds.cache.size.toLocaleString()}>>`, + userCount = `<<${this.client.users.cache.size.toLocaleString()}>>`; - void client.logger.success('ready', `Logged in to ${tag} serving ${guildCount} guilds and ${userCount} users.`); + void this.client.logger.success('ready', `Logged in to ${tag} serving ${guildCount} guilds and ${userCount} users.`); console.log( chalk.blue( `------------------------------------------------------------------------------${ - client.config.isDevelopment ? '---' : client.config.isBeta ? '----' : '' + this.client.config.isDevelopment ? '---' : this.client.config.isBeta ? '----' : '' }` ) ); const guilds = await Guild.findAll(); const needToCreate = []; - for (const [, guild] of client.guilds.cache) { + for (const [, guild] of this.client.guilds.cache) { const find = guilds.find((g) => guild.id === g.id); if (!find) needToCreate.push(guild.id); } diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index 8a05d39..e9d1394 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -9,7 +9,7 @@ import { type CommandMessage, type SlashMessage } from '#lib'; -import { type InteractionReplyOptions, type MessagePayload, type ReplyMessageOptions } from 'discord.js'; +import { type Client, type InteractionReplyOptions, type MessagePayload, type ReplyMessageOptions } from 'discord.js'; export default class CommandBlockedListener extends BushListener { public constructor() { @@ -21,10 +21,15 @@ export default class CommandBlockedListener extends BushListener { } public async exec(...[message, command, reason]: BushCommandHandlerEvents['commandBlocked']) { - return await CommandBlockedListener.handleBlocked(message, command, reason); + return await CommandBlockedListener.handleBlocked(this.client, message, command, reason); } - public static async handleBlocked(message: CommandMessage | SlashMessage, command: BushCommand | null, reason?: string) { + public static async handleBlocked( + client: Client, + message: CommandMessage | SlashMessage, + command: BushCommand | null, + reason?: string + ) { const isSlash = !!command && !!message.util?.isSlash; void client.console.info( diff --git a/src/listeners/commands/commandCooldown.ts b/src/listeners/commands/commandCooldown.ts index 74dd4eb..5f4d70e 100644 --- a/src/listeners/commands/commandCooldown.ts +++ b/src/listeners/commands/commandCooldown.ts @@ -10,7 +10,7 @@ export default class CommandCooldownListener extends BushListener { } public async exec(...[message, command, remaining]: BushCommandHandlerEvents['cooldown']) { - void client.console.info( + void this.client.console.info( 'commandCooldown', `<<${message.author.tag}>> tried to run <<${ command ?? message.util!.parsed?.command diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index ca6565e..04ea517 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -1,16 +1,6 @@ -import { - capitalize, - colors, - format, - formatError, - inspectAndRedact, - inspectCleanRedactCodeblock, - inspectCleanRedactHaste, - SlashMessage, - type BushCommandHandlerEvents -} from '#lib'; +import { capitalize, colors, format, formatError, SlashMessage, type BushCommandHandlerEvents } from '#lib'; import { type AkairoMessage, type Command } from 'discord-akairo'; -import { ChannelType, EmbedBuilder, escapeInlineCode, Formatters, GuildTextBasedChannel, type Message } from 'discord.js'; +import { ChannelType, Client, EmbedBuilder, escapeInlineCode, Formatters, GuildTextBasedChannel, type Message } from 'discord.js'; import { BushListener } from '../../lib/extensions/discord-akairo/BushListener.js'; export default class CommandErrorListener extends BushListener { @@ -23,10 +13,11 @@ export default class CommandErrorListener extends BushListener { } public exec(...[error, message, command]: BushCommandHandlerEvents['error']) { - return CommandErrorListener.handleError(error, message, command); + return CommandErrorListener.handleError(this.client, error, message, command); } public static async handleError( + client: Client, ...[error, message, _command]: BushCommandHandlerEvents['error'] | BushCommandHandlerEvents['slashError'] ) { try { @@ -63,8 +54,8 @@ export default class CommandErrorListener extends BushListener { false ); - const _haste = CommandErrorListener.getErrorHaste(error); - const _stack = CommandErrorListener.getErrorStack(error); + const _haste = CommandErrorListener.getErrorHaste(client, error); + const _stack = CommandErrorListener.getErrorStack(client, error); const [haste, stack] = await Promise.all([_haste, _stack]); const options = { message, error, isSlash, errorNum, command, channel, haste, stack }; @@ -97,6 +88,7 @@ export default class CommandErrorListener extends BushListener { } public static async generateErrorEmbed( + client: Client, options: | { message: Message | AkairoMessage; @@ -109,8 +101,8 @@ export default class CommandErrorListener extends BushListener { } | { error: Error | any; type: 'uncaughtException' | 'unhandledRejection'; context?: string } ): Promise<EmbedBuilder[]> { - const _haste = CommandErrorListener.getErrorHaste(options.error); - const _stack = CommandErrorListener.getErrorStack(options.error); + const _haste = CommandErrorListener.getErrorHaste(client, options.error); + const _stack = CommandErrorListener.getErrorStack(client, options.error); const [haste, stack] = await Promise.all([_haste, _stack]); return CommandErrorListener._generateErrorEmbed({ ...options, haste, stack }); @@ -179,7 +171,7 @@ export default class CommandErrorListener extends BushListener { return embeds; } - public static async getErrorHaste(error: Error | any): Promise<string[]> { + public static async getErrorHaste(client: Client, error: Error | any): Promise<string[]> { const inspectOptions = { showHidden: false, depth: 9, @@ -209,7 +201,7 @@ export default class CommandErrorListener extends BushListener { for (const element in error) { if (['stack', 'name', 'message'].includes(element)) continue; else if (typeof (error as any)[element] === 'object') { - promises.push(inspectCleanRedactHaste((error as any)[element], inspectOptions)); + promises.push(client.utils.inspectCleanRedactHaste((error as any)[element], inspectOptions)); } } @@ -235,7 +227,7 @@ export default class CommandErrorListener extends BushListener { ? `[haste](${pair[element].url})${pair[element].error ? ` - ${pair[element].error}` : ''}` : pair[element].error }` - : `\`${escapeInlineCode(inspectAndRedact((error as any)[element], inspectOptions))}\`` + : `\`${escapeInlineCode(client.utils.inspectAndRedact((error as any)[element], inspectOptions))}\`` }` ); } @@ -243,8 +235,8 @@ export default class CommandErrorListener extends BushListener { return ret; } - public static async getErrorStack(error: Error | any): Promise<string> { - return await inspectCleanRedactCodeblock(error, 'js', { colors: false }, 4000); + public static async getErrorStack(client: Client, error: Error | any): Promise<string> { + return await client.utils.inspectCleanRedactCodeblock(error, 'js', { colors: false }, 4000); } } diff --git a/src/listeners/commands/commandMissingPermissions.ts b/src/listeners/commands/commandMissingPermissions.ts index 352899d..4d39264 100644 --- a/src/listeners/commands/commandMissingPermissions.ts +++ b/src/listeners/commands/commandMissingPermissions.ts @@ -1,5 +1,5 @@ import { BushListener, emojis, format, mappings, oxford, surroundArray, type BushCommandHandlerEvents } from '#lib'; -import { type PermissionsString } from 'discord.js'; +import { Client, type PermissionsString } from 'discord.js'; export default class CommandMissingPermissionsListener extends BushListener { public constructor() { @@ -11,10 +11,11 @@ export default class CommandMissingPermissionsListener extends BushListener { } public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['missingPermissions']) { - return await CommandMissingPermissionsListener.handleMissing(message, command, type, missing); + return await CommandMissingPermissionsListener.handleMissing(this.client, message, command, type, missing); } public static async handleMissing( + client: Client, ...[message, command, type, missing]: | BushCommandHandlerEvents['missingPermissions'] | BushCommandHandlerEvents['slashMissingPermissions'] diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts index 89fea28..9d0e4cb 100644 --- a/src/listeners/commands/commandStarted.ts +++ b/src/listeners/commands/commandStarted.ts @@ -11,7 +11,7 @@ export default class CommandStartedListener extends BushListener { } public exec(...[message, command]: BushCommandHandlerEvents['commandStarted']): void { - client.sentry.addBreadcrumb({ + this.client.sentry.addBreadcrumb({ message: `[commandStarted] The ${command.id} was started by ${message.author.tag}.`, level: 'info', timestamp: Date.now(), @@ -25,11 +25,11 @@ export default class CommandStartedListener extends BushListener { (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 + 'environment': this.client.config.environment } }); - void client.logger.info( + void this.client.logger.info( 'commandStarted', `The <<${command.id}>> command was used by <<${message.author.tag}>> in ${ message.channel.type === ChannelType.DM ? `their <<DMs>>` : `<<#${message.channel.name}>> in <<${message.guild?.name}>>` @@ -37,6 +37,6 @@ export default class CommandStartedListener extends BushListener { true ); - client.stats.commandsUsed = client.stats.commandsUsed + 1n; + this.client.stats.commandsUsed = this.client.stats.commandsUsed + 1n; } } diff --git a/src/listeners/commands/messageBlocked.ts b/src/listeners/commands/messageBlocked.ts index 997579c..d73cff1 100644 --- a/src/listeners/commands/messageBlocked.ts +++ b/src/listeners/commands/messageBlocked.ts @@ -12,6 +12,9 @@ export default class MessageBlockedListener extends BushListener { public async exec(...[message, reason]: BushCommandHandlerEvents['messageBlocked']) { 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}`); + return void this.client.console.verbose( + `messageBlocked`, + `<<${message.author.tag}>>'s message was blocked because ${reason}` + ); } } diff --git a/src/listeners/commands/slashBlocked.ts b/src/listeners/commands/slashBlocked.ts index 915b953..c877708 100644 --- a/src/listeners/commands/slashBlocked.ts +++ b/src/listeners/commands/slashBlocked.ts @@ -11,6 +11,6 @@ export default class SlashBlockedListener extends BushListener { } public async exec(...[message, command, reason]: BushCommandHandlerEvents['slashBlocked']) { - return await CommandBlockedListener.handleBlocked(message, command, reason); + return await CommandBlockedListener.handleBlocked(this.client, message, command, reason); } } diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts index f851f4f..7c0d52c 100644 --- a/src/listeners/commands/slashCommandError.ts +++ b/src/listeners/commands/slashCommandError.ts @@ -11,6 +11,6 @@ export default class SlashCommandErrorListener extends BushListener { } public async exec(...[error, message, command]: BushCommandHandlerEvents['slashError']) { - return await CommandErrorListener.handleError(error, message, command); + return await CommandErrorListener.handleError(this.client, error, message, command); } } diff --git a/src/listeners/commands/slashMissingPermissions.ts b/src/listeners/commands/slashMissingPermissions.ts index 3ae03e3..0a1383b 100644 --- a/src/listeners/commands/slashMissingPermissions.ts +++ b/src/listeners/commands/slashMissingPermissions.ts @@ -11,6 +11,6 @@ export default class SlashMissingPermissionsListener extends BushListener { } public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['slashMissingPermissions']) { - return await CommandMissingPermissionsListener.handleMissing(message, command, type, missing); + return await CommandMissingPermissionsListener.handleMissing(this.client, message, command, type, missing); } } diff --git a/src/listeners/commands/slashNotFound.ts b/src/listeners/commands/slashNotFound.ts index 8c6d2d4..cc14969 100644 --- a/src/listeners/commands/slashNotFound.ts +++ b/src/listeners/commands/slashNotFound.ts @@ -10,6 +10,6 @@ export default class SlashNotFoundListener extends BushListener { } public async exec(...[interaction]: BushCommandHandlerEvents['slashNotFound']) { - void client.console.info('slashNotFound', `<<${interaction?.commandName}>> could not be found.`); + void this.client.console.info('slashNotFound', `<<${interaction?.commandName}>> could not be found.`); } } diff --git a/src/listeners/commands/slashStarted.ts b/src/listeners/commands/slashStarted.ts index e2c8a47..400920f 100644 --- a/src/listeners/commands/slashStarted.ts +++ b/src/listeners/commands/slashStarted.ts @@ -11,7 +11,7 @@ export default class SlashStartedListener extends BushListener { } public async exec(...[message, command]: BushCommandHandlerEvents['slashStarted']) { - client.sentry.addBreadcrumb({ + this.client.sentry.addBreadcrumb({ message: `[slashStarted] The ${command.id} was started by ${message.author.tag}.`, level: 'info', timestamp: Date.now(), @@ -25,11 +25,11 @@ export default class SlashStartedListener extends BushListener { (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 + 'environment': this.client.config.environment } }); - void client.logger.info( + void this.client.logger.info( 'slashStarted', `The <<${command.id}>> command was used by <<${message.author.tag}>> in ${ message.channel @@ -41,6 +41,6 @@ export default class SlashStartedListener extends BushListener { true ); - client.stats.commandsUsed = client.stats.commandsUsed + 1n; + this.client.stats.commandsUsed = this.client.stats.commandsUsed + 1n; } } diff --git a/src/listeners/contextCommands/contextCommandBlocked.ts b/src/listeners/contextCommands/contextCommandBlocked.ts index a9397b6..bb237a0 100644 --- a/src/listeners/contextCommands/contextCommandBlocked.ts +++ b/src/listeners/contextCommands/contextCommandBlocked.ts @@ -11,7 +11,7 @@ export default class ContextCommandBlockedListener extends BushListener { } public async exec(...[interaction, command, reason]: ContextMenuCommandHandlerEvents['blocked']) { - void client.console.info( + void this.client.console.info( `ContextCommandBlocked`, `<<${interaction.user.tag}>> tried to run <<${command}>> but was blocked because <<${reason}>>.`, true diff --git a/src/listeners/contextCommands/contextCommandError.ts b/src/listeners/contextCommands/contextCommandError.ts index e69ed4e..4b1ea52 100644 --- a/src/listeners/contextCommands/contextCommandError.ts +++ b/src/listeners/contextCommands/contextCommandError.ts @@ -1,6 +1,6 @@ import { BushListener, colors, format, formatError } from '#lib'; import { type ContextMenuCommand, type ContextMenuCommandHandlerEvents } from 'discord-akairo'; -import { ChannelType, ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; +import { ChannelType, Client, ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; import CommandErrorListener, { IFuckedUpError } from '../commands/commandError.js'; export default class ContextCommandErrorListener extends BushListener { @@ -13,10 +13,10 @@ export default class ContextCommandErrorListener extends BushListener { } public exec(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { - return ContextCommandErrorListener.handleError(error, interaction, command); + return ContextCommandErrorListener.handleError(this.client, error, interaction, command); } - public static async handleError(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { + public static async handleError(client: Client, ...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { try { const errorNum = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number const channel = @@ -49,8 +49,8 @@ export default class ContextCommandErrorListener extends BushListener { false ); - const _haste = CommandErrorListener.getErrorHaste(error); - const _stack = CommandErrorListener.getErrorStack(error); + const _haste = CommandErrorListener.getErrorHaste(client, error); + const _stack = CommandErrorListener.getErrorStack(client, error); const [haste, stack] = await Promise.all([_haste, _stack]); const options = { interaction, error, errorNum, command, channel, haste, stack }; diff --git a/src/listeners/contextCommands/contextCommandNotFound.ts b/src/listeners/contextCommands/contextCommandNotFound.ts index 65954a2..f5097f3 100644 --- a/src/listeners/contextCommands/contextCommandNotFound.ts +++ b/src/listeners/contextCommands/contextCommandNotFound.ts @@ -11,6 +11,6 @@ export default class ContextCommandNotFoundListener extends BushListener { } public async exec(...[interaction]: ContextMenuCommandHandlerEvents['notFound']) { - void client.console.info('contextCommandNotFound', `<<${interaction?.commandName}>> could not be found.`); + void this.client.console.info('contextCommandNotFound', `<<${interaction?.commandName}>> could not be found.`); } } diff --git a/src/listeners/contextCommands/contextCommandStarted.ts b/src/listeners/contextCommands/contextCommandStarted.ts index 3aab07a..2d1e9ef 100644 --- a/src/listeners/contextCommands/contextCommandStarted.ts +++ b/src/listeners/contextCommands/contextCommandStarted.ts @@ -12,7 +12,7 @@ export default class ContextCommandStartedListener extends BushListener { } public async exec(...[interaction, command]: ContextMenuCommandHandlerEvents['started']) { - client.sentry.addBreadcrumb({ + this.client.sentry.addBreadcrumb({ message: `[contextCommandStarted] The ${command.id} was started by ${interaction.user.tag}.`, level: 'info', timestamp: Date.now(), @@ -28,11 +28,11 @@ export default class ContextCommandStartedListener extends BushListener { '¯\\_(ツ)_/¯', 'guild.id': interaction.guild?.id ?? '¯\\_(ツ)_/¯', 'guild.name': interaction.guild?.name ?? '¯\\_(ツ)_/¯', - 'environment': client.config.environment + 'environment': this.client.config.environment } }); - return void client.logger.info( + return void this.client.logger.info( 'contextCommandStarted', `The <<${command.id}>> command was used by <<${interaction.user.tag}>> in ${ interaction.channel diff --git a/src/listeners/guild/guildCreate.ts b/src/listeners/guild/guildCreate.ts index fe23979..9f5f56c 100644 --- a/src/listeners/guild/guildCreate.ts +++ b/src/listeners/guild/guildCreate.ts @@ -1,4 +1,4 @@ -import { BushListener, colors, emojis, format, getConfigChannel, Guild, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, format, Guild, type BushClientEvents } from '#lib'; export default class GuildCreateListener extends BushListener { public constructor() { @@ -10,11 +10,14 @@ export default class GuildCreateListener extends BushListener { } public async exec(...[guild]: BushClientEvents['guildCreate']) { - void client.console.info('guildCreate', `Joined <<${guild.name}>> with <<${guild.memberCount?.toLocaleString()}>> members.`); + void this.client.console.info( + 'guildCreate', + `Joined <<${guild.name}>> with <<${guild.memberCount?.toLocaleString()}>> members.` + ); const g = await Guild.findByPk(guild.id); if (!g) void Guild.create({ id: guild.id }); - const channel = await getConfigChannel('servers'); + const channel = await this.client.utils.getConfigChannel('servers'); if (!channel) return; return await channel.send({ embeds: [ @@ -22,7 +25,7 @@ export default class GuildCreateListener extends BushListener { color: colors.Green, description: `${emojis.join} Joined ${format.input( guild.name - )} with **${guild.memberCount?.toLocaleString()}** members. I am now in **${client.guilds.cache.size}** guilds.`, + )} with **${guild.memberCount?.toLocaleString()}** members. I am now in **${this.client.guilds.cache.size}** guilds.`, timestamp: new Date().toISOString(), footer: { text: `${guild.id}`, icon_url: guild.iconURL() ?? undefined } } diff --git a/src/listeners/guild/guildDelete.ts b/src/listeners/guild/guildDelete.ts index 5efbe9b..62d98e3 100644 --- a/src/listeners/guild/guildDelete.ts +++ b/src/listeners/guild/guildDelete.ts @@ -1,4 +1,4 @@ -import { BushListener, colors, emojis, format, getConfigChannel, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, format, type BushClientEvents } from '#lib'; export default class GuildDeleteListener extends BushListener { public constructor() { @@ -10,9 +10,12 @@ export default class GuildDeleteListener extends BushListener { } public async exec(...[guild]: BushClientEvents['guildDelete']) { - void client.console.info('guildDelete', `Left <<${guild.name}>> with <<${guild.memberCount?.toLocaleString()}>> members.`); + void this.client.console.info( + 'guildDelete', + `Left <<${guild.name}>> with <<${guild.memberCount?.toLocaleString()}>> members.` + ); - const channel = await getConfigChannel('servers'); + const channel = await this.client.utils.getConfigChannel('servers'); if (!channel) return; return await channel.send({ embeds: [ @@ -20,7 +23,7 @@ export default class GuildDeleteListener extends BushListener { color: colors.Red, description: `${emojis.leave} Left ${format.input( guild.name - )} with **${guild.memberCount?.toLocaleString()}** members. I am now in **${client.guilds.cache.size}** guilds.`, + )} with **${guild.memberCount?.toLocaleString()}** members. I am now in **${this.client.guilds.cache.size}** guilds.`, timestamp: new Date().toISOString(), footer: { text: `${guild.id}`, icon_url: guild.iconURL() ?? undefined } } diff --git a/src/listeners/guild/guildMemberAdd.ts b/src/listeners/guild/guildMemberAdd.ts index 004a2c2..f1f90af 100644 --- a/src/listeners/guild/guildMemberAdd.ts +++ b/src/listeners/guild/guildMemberAdd.ts @@ -15,10 +15,10 @@ export default class GuildMemberAddListener extends BushListener { } private async sendWelcomeMessage(member: GuildMember) { - if (client.config.isDevelopment) return; + if (this.client.config.isDevelopment) return; const welcomeChannel = await member.guild.getSetting('welcomeChannel'); if (!welcomeChannel) return; - const welcome = client.channels.cache.get(welcomeChannel) as TextChannel | undefined; + const welcome = this.client.channels.cache.get(welcomeChannel) as TextChannel | undefined; if (!welcome) return; if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); @@ -39,7 +39,7 @@ export default class GuildMemberAddListener extends BushListener { await welcome .send({ embeds: [embed] }) .then(() => - client.console.info( + this.client.console.info( 'guildMemberAdd', `Sent a message for ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ) diff --git a/src/listeners/guild/guildMemberRemove.ts b/src/listeners/guild/guildMemberRemove.ts index 80e9ee8..39bab24 100644 --- a/src/listeners/guild/guildMemberRemove.ts +++ b/src/listeners/guild/guildMemberRemove.ts @@ -16,13 +16,13 @@ export default class GuildMemberRemoveListener extends BushListener { } private async sendWelcomeMessage(member: GuildMember | PartialGuildMember) { - if (client.config.isDevelopment) return; - const user = member.partial ? await client.users.fetch(member.id) : member.user; + if (this.client.config.isDevelopment) return; + const user = member.partial ? await this.client.users.fetch(member.id) : member.user; await sleep(50 * Time.Millisecond); // ban usually triggers after member leave const isBan = member.guild.bans.cache.has(member.id); const welcomeChannel = await member.guild.getSetting('welcomeChannel'); if (!welcomeChannel) return; - const welcome = client.channels.cache.get(welcomeChannel) as TextChannel | undefined; + const welcome = this.client.channels.cache.get(welcomeChannel) as TextChannel | undefined; if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); const embed: EmbedBuilder = new EmbedBuilder() .setDescription( @@ -34,7 +34,7 @@ export default class GuildMemberRemoveListener extends BushListener { welcome .send({ embeds: [embed] }) .then(() => - client.console.info( + this.client.console.info( 'guildMemberRemove', `Sent a message for ${format.inputLog(user.tag)} in ${format.inputLog(member.guild.name)}.` ) @@ -72,7 +72,7 @@ export default class GuildMemberRemoveListener extends BushListener { await row .save() .then(() => - client.console.info( + this.client.console.info( 'guildMemberRemove', `${isNew ? 'Created' : 'Updated'} info for ${format.inputLog(member.user.tag)}.` ) diff --git a/src/listeners/guild/joinRoles.ts b/src/listeners/guild/joinRoles.ts index f2922c8..539fa71 100644 --- a/src/listeners/guild/joinRoles.ts +++ b/src/listeners/guild/joinRoles.ts @@ -11,7 +11,7 @@ export default class JoinRolesListener extends BushListener { } public async exec(...[oldMember, newMember]: BushClientEvents['guildMemberUpdate']) { - if (client.config.isDevelopment) return; + if (this.client.config.isDevelopment) return; if (oldMember.pending && !newMember.pending) { const feat = { stickyRoles: await newMember.guild.hasFeature('stickyRoles'), @@ -66,7 +66,7 @@ export default class JoinRolesListener extends BushListener { return false as const; }); if (addedRoles) { - void client.console.info( + void this.client.console.info( 'guildMemberAdd', `Assigned sticky roles to ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ); @@ -78,9 +78,9 @@ export default class JoinRolesListener extends BushListener { .catch(() => failedRoles.push(rolesArray[i])); } if (failedRoles.length) { - void client.console.warn('guildMemberAdd', `Failed assigning the following roles on Fallback: ${failedRoles}`); + void this.client.console.warn('guildMemberAdd', `Failed assigning the following roles on Fallback: ${failedRoles}`); } else { - void client.console.info( + void this.client.console.info( 'guildMemberAdd', `[Fallback] Assigned sticky roles to ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ); @@ -102,7 +102,7 @@ export default class JoinRolesListener extends BushListener { await member.roles .add(joinRoles, 'Join roles.') .then(() => - client.console.info( + this.client.console.info( 'guildMemberAdd', `Assigned join roles to ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ) diff --git a/src/listeners/interaction/interactionCreate.ts b/src/listeners/interaction/interactionCreate.ts index 6c421d1..91bcae6 100644 --- a/src/listeners/interaction/interactionCreate.ts +++ b/src/listeners/interaction/interactionCreate.ts @@ -13,7 +13,7 @@ export default class InteractionCreateListener extends BushListener { public async exec(...[interaction]: BushClientEvents['interactionCreate']) { if (!interaction) return; if ('customId' in interaction && (interaction as any)['customId'].startsWith('test')) return; - void client.console.verbose( + void this.client.console.verbose( 'interactionVerbose', `An interaction of type <<${InteractionType[interaction.type]}>> was received from <<${interaction.user.tag}>>.` ); diff --git a/src/listeners/member-custom/bushPurge.ts b/src/listeners/member-custom/bushPurge.ts index ea30bc5..61a65e1 100644 --- a/src/listeners/member-custom/bushPurge.ts +++ b/src/listeners/member-custom/bushPurge.ts @@ -1,4 +1,4 @@ -import { BushListener, colors, emojis, inspectCleanRedactHaste, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushPurgeListener extends BushListener { @@ -21,7 +21,7 @@ export default class BushPurgeListener extends BushListener { embeds: m.embeds, attachments: [...m.attachments.values()] })); - const haste = await inspectCleanRedactHaste(mappedMessages); + const haste = await this.client.utils.inspectCleanRedactHaste(mappedMessages); const logEmbed = new EmbedBuilder() .setColor(colors.DarkPurple) diff --git a/src/listeners/member-custom/bushUpdateModlog.ts b/src/listeners/member-custom/bushUpdateModlog.ts index 5e29b9a..893bec6 100644 --- a/src/listeners/member-custom/bushUpdateModlog.ts +++ b/src/listeners/member-custom/bushUpdateModlog.ts @@ -1,4 +1,4 @@ -import { BushListener, colors, inspectCleanRedactCodeblock, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushUpdateModlogListener extends BushListener { @@ -26,8 +26,14 @@ export default class BushUpdateModlogListener extends BushListener { { name: '**Moderator**', value: `${moderator} (${moderator.user.tag})` }, { name: '**ModLog Changed**', value: modlogID }, { name: '**Value Changed**', value: key }, - { name: '**Old Value**', value: await inspectCleanRedactCodeblock(oldModlog, undefined, undefined, 1024) }, - { name: '**New Value**', value: await inspectCleanRedactCodeblock(newModlog, undefined, undefined, 1024) } + { + name: '**Old Value**', + value: await this.client.utils.inspectCleanRedactCodeblock(oldModlog, undefined, undefined, 1024) + }, + { + name: '**New Value**', + value: await this.client.utils.inspectCleanRedactCodeblock(newModlog, undefined, undefined, 1024) + } ]); return await logChannel.send({ embeds: [logEmbed] }); diff --git a/src/listeners/member-custom/bushUpdateSettings.ts b/src/listeners/member-custom/bushUpdateSettings.ts index e367c4e..3781a8b 100644 --- a/src/listeners/member-custom/bushUpdateSettings.ts +++ b/src/listeners/member-custom/bushUpdateSettings.ts @@ -1,4 +1,4 @@ -import { BushListener, colors, inspectCleanRedactCodeblock, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushUpdateSettingsListener extends BushListener { @@ -25,8 +25,8 @@ export default class BushUpdateSettingsListener extends BushListener { if (moderator) logEmbed.addFields([{ name: '**Moderator**', value: `${moderator} (${moderator.user.tag})` }]); logEmbed.addFields([ { name: '**Setting Changed**', value: setting }, - { name: '**Old Value**', value: await inspectCleanRedactCodeblock(oldSettings, 'js', undefined, 1024) }, - { name: '**New Value**', value: await inspectCleanRedactCodeblock(newSettings, 'js', undefined, 1024) } + { name: '**Old Value**', value: await this.client.utils.inspectCleanRedactCodeblock(oldSettings, 'js', undefined, 1024) }, + { name: '**New Value**', value: await this.client.utils.inspectCleanRedactCodeblock(newSettings, 'js', undefined, 1024) } ]); return await logChannel.send({ embeds: [logEmbed] }); diff --git a/src/listeners/message/autoPublisher.ts b/src/listeners/message/autoPublisher.ts index c669930..a6fb7e0 100644 --- a/src/listeners/message/autoPublisher.ts +++ b/src/listeners/message/autoPublisher.ts @@ -18,10 +18,11 @@ export default class autoPublisherListener extends BushListener { await message .crosspost() .then( - () => void client.logger.log('autoPublisher', `Published message <<${message.id}>> in <<${message.guild!.name}>>.`) + () => + void this.client.logger.log('autoPublisher', `Published message <<${message.id}>> in <<${message.guild!.name}>>.`) ) .catch(() => { - void client.console.log('autoPublisher', `Failed to publish <<${message.id}>> in <<${message.guild!.name}>>.`); + void this.client.console.log('autoPublisher', `Failed to publish <<${message.id}>> in <<${message.guild!.name}>>.`); void message.guild?.error('autoPublisher', `Unable to publish message id ${message.id} in <#${message.channel.id}>.`); }); } diff --git a/src/listeners/message/blacklistedFile.ts b/src/listeners/message/blacklistedFile.ts index 15d97e3..3d66f38 100644 --- a/src/listeners/message/blacklistedFile.ts +++ b/src/listeners/message/blacklistedFile.ts @@ -122,7 +122,7 @@ export default class BlacklistedFileListener extends BushListener { if (foundFiles.length > 0) { try { for (let i = 0; i < foundFiles.length; i++) { - if (foundFiles[i].name === 'Discord crash video' && !client.ownerID.includes(message.author.id)) { + if (foundFiles[i].name === 'Discord crash video' && !this.client.ownerID.includes(message.author.id)) { await message.member?.roles.add('748912426581229690'); } } @@ -132,7 +132,7 @@ export default class BlacklistedFileListener extends BushListener { `<@!${message.author.id}>, please do not send ${foundFiles.map((f) => f.description).join(' or ')}.` ); if (message.channel.type === ChannelType.DM) return; - void client.console.info( + void this.client.console.info( 'blacklistedFile', `Deleted <<${foundFiles.map((f) => f.description).join(' and ')}>> sent by <<${message.author.tag}>> in ${ message.channel.name @@ -142,7 +142,7 @@ export default class BlacklistedFileListener extends BushListener { void message.util!.send( `<@!${message.author.id}>, please do not send ${foundFiles.map((f) => f.description).join(' or ')}.` ); - void client.console.warn( + void this.client.console.warn( 'blacklistedFile', `Failed to delete <<${foundFiles.map((f) => f.description).join(' and ')}>> sent by <<${message.author.tag}>> in <<${ message.channel.type === ChannelType.DM ? `${message.channel.recipient?.tag}'s DMs` : message.channel.name diff --git a/src/listeners/message/boosterMessage.ts b/src/listeners/message/boosterMessage.ts index e33abfb..1a4d3c3 100644 --- a/src/listeners/message/boosterMessage.ts +++ b/src/listeners/message/boosterMessage.ts @@ -14,7 +14,7 @@ export default class BoosterMessageListener extends BushListener { if (!message.guild || !(await message.guild?.hasFeature('boosterMessageReact'))) return; if (message.type === MessageType.UserPremiumGuildSubscription) { return await message.react('<:nitroboost:785160348885975062>').catch(() => { - void client.console.warn('boosterMessage', `Failed to react to <<${message.id}>>.`); + void this.client.console.warn('boosterMessage', `Failed to react to <<${message.id}>>.`); }); } } diff --git a/src/listeners/message/directMessage.ts b/src/listeners/message/directMessage.ts index 463e4d8..7b599d9 100644 --- a/src/listeners/message/directMessage.ts +++ b/src/listeners/message/directMessage.ts @@ -1,4 +1,4 @@ -import { BushListener, colors, getConfigChannel, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { ChannelType, EmbedBuilder } from 'discord.js'; export default class DirectMessageListener extends BushListener { @@ -12,11 +12,11 @@ export default class DirectMessageListener extends BushListener { public async exec(...[message]: BushClientEvents['messageCreate']) { if (message.channel.type === ChannelType.DM) { - if (!(message.author.id == client.user!.id) && message.author.bot) return; - if (client.cache.global.blacklistedUsers.includes(message.author.id)) return; + if (!(message.author.id == this.client.user!.id) && message.author.bot) return; + if (this.client.cache.global.blacklistedUsers.includes(message.author.id)) return; const dmLogEmbed = new EmbedBuilder().setTimestamp().setFooter({ text: `User ID • ${message.channel.recipientId}` }); - if (message.author.id != client.user!.id) { + if (message.author.id != this.client.user!.id) { dmLogEmbed .setAuthor({ name: `From: ${message.author.username}`, @@ -39,7 +39,7 @@ export default class DirectMessageListener extends BushListener { } else if (message.attachments.size > 0) { dmLogEmbed.addFields([{ name: 'Attachments', value: message.attachments.map((a) => a.proxyURL).join('\n') }]); } - const dmChannel = await getConfigChannel('dm'); + const dmChannel = await this.client.utils.getConfigChannel('dm'); await dmChannel.send({ embeds: [dmLogEmbed] }); } } diff --git a/src/listeners/message/highlight.ts b/src/listeners/message/highlight.ts index d68337c..d9d3c0b 100644 --- a/src/listeners/message/highlight.ts +++ b/src/listeners/message/highlight.ts @@ -14,12 +14,12 @@ export default class HighlightListener extends BushListener { if (message.author.bot || message.system) return; if (!(await message.guild.hasFeature('highlight'))) return; // allows highlighting to be disabled on a guild-by-guild basis - client.highlightManager.updateLastTalked(message); - const res = client.highlightManager.checkMessage(message); + this.client.highlightManager.updateLastTalked(message); + const res = this.client.highlightManager.checkMessage(message); for (const [user, hl] of res.entries()) { if (message.author.id === user) continue; - void client.highlightManager.notify(message, user, hl); + void this.client.highlightManager.notify(message, user, hl); } } } diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts index a72ae02..526dac9 100644 --- a/src/listeners/message/level.ts +++ b/src/listeners/message/level.ts @@ -1,4 +1,4 @@ -import { BushListener, handleError, Level, type BushCommandHandlerEvents } from '#lib'; +import { BushListener, Level, type BushCommandHandlerEvents } from '#lib'; import { MessageType } from 'discord.js'; export default class LevelListener extends BushListener { @@ -33,13 +33,14 @@ export default class LevelListener extends BushListener { const xpToGive = Level.genRandomizedXp(); user.xp = user.xp + xpToGive; const success = await user.save().catch((e) => { - void handleError('level', e); + void this.client.utils.handleError('level', e); return false; }); const newLevel = Level.convertXpToLevel(user.xp); - if (previousLevel !== newLevel) client.emit('bushLevelUpdate', message.member!, previousLevel, newLevel, user.xp, message); + if (previousLevel !== newLevel) + this.client.emit('bushLevelUpdate', message.member!, previousLevel, newLevel, user.xp, message); if (success) - void client.logger.verbose(`level`, `Gave <<${xpToGive}>> XP to <<${message.author.tag}>> in <<${message.guild}>>.`); + void this.client.logger.verbose(`level`, `Gave <<${xpToGive}>> XP to <<${message.author.tag}>> in <<${message.guild}>>.`); this.#levelCooldowns.add(`${message.guildId}-${message.author.id}`); setTimeout(() => this.#levelCooldowns.delete(`${message.guildId}-${message.author.id}`), 60_000); } diff --git a/src/listeners/message/quoteCreate.ts b/src/listeners/message/quoteCreate.ts index d43eef7..0478bcf 100644 --- a/src/listeners/message/quoteCreate.ts +++ b/src/listeners/message/quoteCreate.ts @@ -1,4 +1,4 @@ -import { BushListener, resolveMessagesFromLinks, type BushClientEvents } from '#lib'; +import { BushListener, type BushClientEvents } from '#lib'; export default class QuoteCreateListener extends BushListener { public constructor() { @@ -10,10 +10,10 @@ export default class QuoteCreateListener extends BushListener { } public async exec(...[message]: BushClientEvents['messageCreate']) { - if (message.author.id !== '322862723090219008' || !client.config.isProduction) return; + if (message.author.id !== '322862723090219008' || !this.client.config.isProduction) return; if (!message.inGuild()) return; - const messages = await resolveMessagesFromLinks(message.content); + const messages = await this.client.utils.resolveMessagesFromLinks(message.content); if (!messages.length) return; for (const msg of messages) { diff --git a/src/listeners/message/verbose.ts b/src/listeners/message/verbose.ts index e3ef04f..f5e94de 100644 --- a/src/listeners/message/verbose.ts +++ b/src/listeners/message/verbose.ts @@ -11,9 +11,9 @@ export default class MessageVerboseListener extends BushListener { } public exec(...[message]: BushClientEvents['messageCreate']): void { - if (client.customReady) { + if (this.client.customReady) { if (message.channel?.type === ChannelType.DM) return; - void client.console.verbose( + void this.client.console.verbose( 'messageVerbose', `A message was sent by <<${message.author.tag}>> in <<${message.channel.name}>> in <<${message.guild!.name}>>.` ); diff --git a/src/listeners/other/consoleListener.ts b/src/listeners/other/consoleListener.ts index 543f459..3e72e9e 100644 --- a/src/listeners/other/consoleListener.ts +++ b/src/listeners/other/consoleListener.ts @@ -15,8 +15,9 @@ import { promisify } from 'util'; if (line.startsWith('eval ') || line.startsWith('ev ')) { /* eslint-disable @typescript-eslint/no-unused-vars */ const sh = promisify(exec), - bot = client, - config = client.config, + bot = this.client, + client = this.client, + config = this.client.config, { ActivePunishment, Global, Guild, Level, ModLog, StickyRole } = await import('#lib'), { ButtonInteraction, diff --git a/src/listeners/other/exit.ts b/src/listeners/other/exit.ts index e16aeb7..ac074df 100644 --- a/src/listeners/other/exit.ts +++ b/src/listeners/other/exit.ts @@ -9,6 +9,6 @@ export default class ExitListener extends BushListener { } public async exec(code: number) { - await client.console.error('processExit', `Process ended with code <<${code}>>.`); + await this.client.console.error('processExit', `Process ended with code <<${code}>>.`); } } diff --git a/src/listeners/other/promiseRejection.ts b/src/listeners/other/promiseRejection.ts index 79bf5de..ab67ad4 100644 --- a/src/listeners/other/promiseRejection.ts +++ b/src/listeners/other/promiseRejection.ts @@ -16,14 +16,18 @@ export default class PromiseRejectionListener extends BushListener { process.removeListener('unhandledRejection', listener); }); - client.sentry.captureException(error, { + this.client.sentry.captureException(error, { level: 'error' }); - void client.console.error('promiseRejection', `An unhanded promise rejection occurred:\n${formatError(error, true)}`, false); + void this.client.console.error( + 'promiseRejection', + `An unhanded promise rejection occurred:\n${formatError(error, true)}`, + false + ); if (!error.message.includes('reason: getaddrinfo ENOTFOUND canary.discord.com')) - void client.console.channelError({ - embeds: await CommandErrorListener.generateErrorEmbed({ type: 'unhandledRejection', error: error }) + void this.client.console.channelError({ + embeds: await CommandErrorListener.generateErrorEmbed(this.client, { type: 'unhandledRejection', error: error }) }); } } diff --git a/src/listeners/other/uncaughtException.ts b/src/listeners/other/uncaughtException.ts index 3e80feb..e06a3b8 100644 --- a/src/listeners/other/uncaughtException.ts +++ b/src/listeners/other/uncaughtException.ts @@ -15,13 +15,13 @@ export default class UncaughtExceptionListener extends BushListener { if (listener.toString() === this.exec.toString()) return; process.removeListener('uncaughtException', listener); }); - client.sentry.captureException(error, { + this.client.sentry.captureException(error, { level: 'error' }); - void client.console.error('uncaughtException', `An uncaught exception occurred:\n${formatError(error, true)}`, false); - void client.console.channelError({ - embeds: await CommandErrorListener.generateErrorEmbed({ type: 'uncaughtException', error: error }) + void this.client.console.error('uncaughtException', `An uncaught exception occurred:\n${formatError(error, true)}`, false); + void this.client.console.channelError({ + embeds: await CommandErrorListener.generateErrorEmbed(this.client, { type: 'uncaughtException', error: error }) }); } } diff --git a/src/listeners/other/warning.ts b/src/listeners/other/warning.ts index ba38f5f..b427e4c 100644 --- a/src/listeners/other/warning.ts +++ b/src/listeners/other/warning.ts @@ -12,15 +12,15 @@ export default class WarningListener extends BushListener { public async exec(error: Error) { if (error.name === 'ExperimentalWarning') return; - client.sentry.captureException(error, { + this.client.sentry.captureException(error, { level: 'warning' }); - void client.console.warn('warning', `A warning occurred:\n${formatError(error, true)}`, false); + void this.client.console.warn('warning', `A warning occurred:\n${formatError(error, true)}`, false); - const embeds = await CommandErrorListener.generateErrorEmbed({ type: 'unhandledRejection', error: error }); + const embeds = await CommandErrorListener.generateErrorEmbed(this.client, { type: 'unhandledRejection', error: error }); embeds[0].setColor(colors.warn).setTitle('A Warning Occurred'); - void client.console.channelError({ embeds }); + void this.client.console.channelError({ embeds }); } } diff --git a/src/listeners/rest/rateLimit.ts b/src/listeners/rest/rateLimit.ts index 5878d81..9071416 100644 --- a/src/listeners/rest/rateLimit.ts +++ b/src/listeners/rest/rateLimit.ts @@ -11,6 +11,6 @@ export default class RateLimitedListener extends BushListener { } public async exec(...[message]: RestEvents['rateLimited']): Promise<void> { - void client.console.superVerboseRaw('rateLimited', message); + void this.client.console.superVerboseRaw('rateLimited', message); } } diff --git a/src/listeners/track-manual-punishments/modlogSyncBan.ts b/src/listeners/track-manual-punishments/modlogSyncBan.ts index 45d6f51..a387897 100644 --- a/src/listeners/track-manual-punishments/modlogSyncBan.ts +++ b/src/listeners/track-manual-punishments/modlogSyncBan.ts @@ -37,6 +37,7 @@ export default class ModlogSyncBanListener extends BushListener { } const { log } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.PERM_BAN, user: ban.user, moderator: first.executor, diff --git a/src/listeners/track-manual-punishments/modlogSyncKick.ts b/src/listeners/track-manual-punishments/modlogSyncKick.ts index d901f5f..51d3dd8 100644 --- a/src/listeners/track-manual-punishments/modlogSyncKick.ts +++ b/src/listeners/track-manual-punishments/modlogSyncKick.ts @@ -37,6 +37,7 @@ export default class ModlogSyncKickListener extends BushListener { } const { log } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.KICK, user: member.user, moderator: first.executor, diff --git a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts index 4619edd..2ce3db2 100644 --- a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts +++ b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts @@ -41,6 +41,7 @@ export default class ModlogSyncTimeoutListener extends BushListener { const newTime = <string | null>timeOut.new ? new Date(<string>timeOut.new) : null; const { log } = await Moderation.createModLogEntry({ + client: this.client, type: newTime ? ModLogType.TIMEOUT : ModLogType.REMOVE_TIMEOUT, user: newMember.user, moderator: first.executor, diff --git a/src/listeners/track-manual-punishments/modlogSyncUnban.ts b/src/listeners/track-manual-punishments/modlogSyncUnban.ts index 34979a5..2207e78 100644 --- a/src/listeners/track-manual-punishments/modlogSyncUnban.ts +++ b/src/listeners/track-manual-punishments/modlogSyncUnban.ts @@ -36,6 +36,7 @@ export default class ModlogSyncUnbanListener extends BushListener { } const { log } = await Moderation.createModLogEntry({ + client: this.client, type: ModLogType.UNBAN, user: ban.user, moderator: first.executor, diff --git a/src/listeners/ws/INTERACTION_CREATE.ts b/src/listeners/ws/INTERACTION_CREATE.ts index 5f479ad..0e8ee7c 100644 --- a/src/listeners/ws/INTERACTION_CREATE.ts +++ b/src/listeners/ws/INTERACTION_CREATE.ts @@ -62,7 +62,7 @@ export default class WsInteractionCreateListener extends BushListener { string ]; - const guild = client.guilds.resolve(guildId); + const guild = this.client.guilds.resolve(guildId); if (!guild) return respond({ type: InteractionResponseType.ChannelMessageWithSource, @@ -137,8 +137,8 @@ export default class WsInteractionCreateListener extends BushListener { ]; if (action === 'appeal_deny') { - await client.users - .send(userId, `Your ${punishment} appeal has been denied in ${client.guilds.resolve(guildId)!}.`) + await this.client.users + .send(userId, `Your ${punishment} appeal has been denied in ${this.client.guilds.resolve(guildId)!}.`) .catch(() => {}); void respond({ @@ -172,7 +172,7 @@ export default class WsInteractionCreateListener extends BushListener { string ]; - const guild = client.guilds.resolve(guildId); + const guild = this.client.guilds.resolve(guildId); if (!guild) return respond({ type: InteractionResponseType.ChannelMessageWithSource, @@ -191,7 +191,7 @@ export default class WsInteractionCreateListener extends BushListener { }); assert(interaction.user); - const user = new User(client, interaction.user); + const user = new User(this.client, interaction.user); assert(user); // const caseId = await ModLog.findOne({ where: { user: userId, guild: guildId, id: modlogCase } }); diff --git a/src/tasks/cpuUsage.ts b/src/tasks/cpuUsage.ts index 6ce6671..f456c31 100644 --- a/src/tasks/cpuUsage.ts +++ b/src/tasks/cpuUsage.ts @@ -1,5 +1,5 @@ import { BushTask, Time } from '#lib'; -import { cpu } from 'node-os-utils'; +import osu from 'node-os-utils'; export default class CpuUsageTask extends BushTask { public constructor() { @@ -10,7 +10,7 @@ export default class CpuUsageTask extends BushTask { } public async exec() { - const cpuStats = await cpu.usage(client.stats.cpu === undefined ? 100 * Time.Millisecond : Time.Minute); - client.stats.cpu = cpuStats; + const cpuStats = await osu.cpu.usage(this.client.stats.cpu === undefined ? 100 * Time.Millisecond : Time.Minute); + this.client.stats.cpu = cpuStats; } } diff --git a/src/tasks/handleReminders.ts b/src/tasks/handleReminders.ts index 79693f5..7863c9a 100644 --- a/src/tasks/handleReminders.ts +++ b/src/tasks/handleReminders.ts @@ -19,11 +19,14 @@ export default class HandlerRemindersTask extends BushTask { } }); - void client.logger.verbose(`handlerReminders`, `Queried reminders, found <<${expiredEntries.length}>> expired reminders.`); + void this.client.logger.verbose( + `handlerReminders`, + `Queried reminders, found <<${expiredEntries.length}>> expired reminders.` + ); for (const entry of expiredEntries) { setTimeout(() => { - void client.users + void this.client.users .send( entry.user, `The reminder you set ${dateDelta(entry.created)} ago has expired: ${format.bold(entry.content)}\n${entry.messageUrl}` diff --git a/src/tasks/memberCount.ts b/src/tasks/memberCount.ts index fa51080..ea422fa 100644 --- a/src/tasks/memberCount.ts +++ b/src/tasks/memberCount.ts @@ -10,10 +10,10 @@ export default class MemberCountTask extends BushTask { } public override async exec() { - if (!client.config.isProduction) return; + if (!this.client.config.isProduction) return; const res = await Promise.allSettled( - client.guilds.cache + this.client.guilds.cache .filter((g) => g.memberCount >= 100) .map((g) => MemberCount.create({ guildId: g.id, memberCount: g.memberCount })) ); @@ -22,7 +22,7 @@ export default class MemberCountTask extends BushTask { .filter((r) => r.status === 'rejected') .forEach((r) => { assert(r.status === 'rejected'); - void client.console.error('memberCount', r.status); + void this.client.console.error('memberCount', r.status); }); } } diff --git a/src/tasks/removeExpiredPunishements.ts b/src/tasks/removeExpiredPunishements.ts index 3f6f6dd..0b20a27 100644 --- a/src/tasks/removeExpiredPunishements.ts +++ b/src/tasks/removeExpiredPunishements.ts @@ -1,4 +1,4 @@ -import { ActivePunishment, ActivePunishmentType, BushTask, resolveNonCachedUser, Time } from '#lib'; +import { ActivePunishment, ActivePunishmentType, BushTask, Time } from '#lib'; import assert from 'assert'; const { Op } = (await import('sequelize')).default; @@ -19,19 +19,19 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { } }); - void client.logger.verbose( + void this.client.logger.verbose( `removeExpiredPunishments`, `Queried punishments, found <<${expiredEntries.length}>> expired punishments.` ); for (const entry of expiredEntries) { - const guild = client.guilds.cache.get(entry.guild); + const guild = this.client.guilds.cache.get(entry.guild); if (!guild) continue; // eslint-disable-next-line @typescript-eslint/no-misused-promises setTimeout(async () => { const member = guild.members.cache.get(entry.user); - const user = await resolveNonCachedUser(entry.user); + const user = await this.client.utils.resolveNonCachedUser(entry.user); assert(guild); switch (entry.type) { @@ -40,7 +40,7 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { const result = await guild.bushUnban({ user: user, reason: 'Punishment expired' }); if (['success', 'user not banned', 'cannot resolve user'].includes(result)) await entry.destroy(); else throw new Error(result); - void client.logger.verbose(`removeExpiredPunishments`, `Unbanned ${entry.user}.`); + void this.client.logger.verbose(`removeExpiredPunishments`, `Unbanned ${entry.user}.`); break; } case ActivePunishmentType.BLOCK: { @@ -51,7 +51,7 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { const result = await member.bushUnblock({ reason: 'Punishment expired', channel: entry.extraInfo }); if (['success', 'user not blocked'].includes(result)) await entry.destroy(); else throw new Error(result); - void client.logger.verbose(`removeExpiredPunishments`, `Unblocked ${entry.user}.`); + void this.client.logger.verbose(`removeExpiredPunishments`, `Unblocked ${entry.user}.`); break; } case ActivePunishmentType.MUTE: { @@ -59,7 +59,7 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { const result = await member.bushUnmute({ reason: 'Punishment expired' }); if (['success', 'failed to dm'].includes(result)) await entry.destroy(); else throw new Error(result); - void client.logger.verbose(`removeExpiredPunishments`, `Unmuted ${entry.user}.`); + void this.client.logger.verbose(`removeExpiredPunishments`, `Unmuted ${entry.user}.`); break; } case ActivePunishmentType.ROLE: { @@ -74,7 +74,7 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { if (['success', 'failed to dm'].includes(result)) await entry.destroy(); else throw new Error(result); - void client.logger.verbose(`removeExpiredPunishments`, `Removed a punishment role from ${entry.user}.`); + void this.client.logger.verbose(`removeExpiredPunishments`, `Removed a punishment role from ${entry.user}.`); break; } } diff --git a/src/tasks/updateCache.ts b/src/tasks/updateCache.ts index 487b9bc..0dc49df 100644 --- a/src/tasks/updateCache.ts +++ b/src/tasks/updateCache.ts @@ -1,5 +1,6 @@ import { Time } from '#constants'; import { Global, Guild, Shared, type BushClient } from '#lib'; +import { Client } from 'discord.js'; import config from '../../config/options.js'; import { BushTask } from '../lib/extensions/discord-akairo/BushTask.js'; @@ -13,11 +14,11 @@ export default class UpdateCacheTask extends BushTask { public async exec() { await Promise.all([ - UpdateCacheTask.#updateGlobalCache(client), - UpdateCacheTask.#updateSharedCache(client), - UpdateCacheTask.#updateGuildCache(client) + UpdateCacheTask.#updateGlobalCache(this.client), + UpdateCacheTask.#updateSharedCache(this.client), + UpdateCacheTask.#updateGuildCache(this.client) ]); - void client.logger.verbose(`UpdateCache`, `Updated cache.`); + void this.client.logger.verbose(`UpdateCache`, `Updated cache.`); } public static async init(client: BushClient) { @@ -28,7 +29,7 @@ export default class UpdateCacheTask extends BushTask { ]); } - static async #updateGlobalCache(client: BushClient) { + static async #updateGlobalCache(client: Client) { const environment = config.environment; const row: { [x: string]: any } = ((await Global.findByPk(environment)) ?? (await Global.create({ environment }))).toJSON(); @@ -39,7 +40,7 @@ export default class UpdateCacheTask extends BushTask { } } - static async #updateSharedCache(client: BushClient) { + static async #updateSharedCache(client: Client) { const row: { [x: string]: any } = ((await Shared.findByPk(0)) ?? (await Shared.create())).toJSON(); for (const option in row) { @@ -50,7 +51,7 @@ export default class UpdateCacheTask extends BushTask { } } - static async #updateGuildCache(client: BushClient) { + static async #updateGuildCache(client: Client) { const rows = await Guild.findAll(); for (const row of rows) { client.cache.guilds.set(row.id, row.toJSON() as Guild); diff --git a/src/tasks/updateHighlightCache.ts b/src/tasks/updateHighlightCache.ts index d4fca71..4677ea7 100644 --- a/src/tasks/updateHighlightCache.ts +++ b/src/tasks/updateHighlightCache.ts @@ -10,6 +10,6 @@ export default class UpdateHighlightCacheTask extends BushTask { } public async exec() { - return client.highlightManager.syncCache(); + return this.client.highlightManager.syncCache(); } } diff --git a/src/tasks/updateStats.ts b/src/tasks/updateStats.ts index 0e50c23..0d0e661 100644 --- a/src/tasks/updateStats.ts +++ b/src/tasks/updateStats.ts @@ -1,4 +1,5 @@ import { BushTask, Stat, Time } from '#lib'; +import { Client } from 'discord.js'; export default class UpdateStatsTask extends BushTask { public constructor() { @@ -10,13 +11,14 @@ export default class UpdateStatsTask extends BushTask { public async exec() { const row = - (await Stat.findByPk(client.config.environment)) ?? (await Stat.create({ environment: client.config.environment })); - row.commandsUsed = client.stats.commandsUsed; - row.slashCommandsUsed = client.stats.slashCommandsUsed; + (await Stat.findByPk(this.client.config.environment)) ?? + (await Stat.create({ environment: this.client.config.environment })); + row.commandsUsed = this.client.stats.commandsUsed; + row.slashCommandsUsed = this.client.stats.slashCommandsUsed; await row.save(); } - public static async init(): Promise<{ commandsUsed: bigint; slashCommandsUsed: bigint }> { + public static async init(client: Client): Promise<{ commandsUsed: bigint; slashCommandsUsed: bigint }> { const temp = (await Stat.findByPk(client.config.environment)) ?? (await Stat.create({ environment: client.config.environment })); return { commandsUsed: temp.commandsUsed, slashCommandsUsed: temp.slashCommandsUsed }; |