diff options
225 files changed, 4325 insertions, 3534 deletions
@@ -27,5 +27,6 @@ dist # Options and credentials for the bot src/config/options.ts +config/options.ts src/lib/badlinks-secret.ts diff --git a/src/lib/utils/Config.ts b/config/Config.ts index ce5ec06..ce5ec06 100644 --- a/src/lib/utils/Config.ts +++ b/config/Config.ts diff --git a/src/config/example-options.ts b/config/example-options.ts index 1b384c4..024b043 100644 --- a/src/config/example-options.ts +++ b/config/example-options.ts @@ -1,4 +1,4 @@ -import { Config } from '#lib'; +import { Config } from './Config.js'; export default new Config({ credentials: { diff --git a/package.json b/package.json index ea08576..dad7620 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,8 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-deprecation": "^1.3.2", "eslint-plugin-import": "^2.26.0", - "node-fetch": "^3.2.6" + "node-fetch": "^3.2.6", + "vitest": "^0.14.2" }, "packageManager": "yarn@3.2.1" } diff --git a/src/arguments/contentWithDuration.ts b/src/arguments/contentWithDuration.ts index e92997a..0efba39 100644 --- a/src/arguments/contentWithDuration.ts +++ b/src/arguments/contentWithDuration.ts @@ -1,5 +1,5 @@ -import type { BushArgumentTypeCaster, ParsedDuration } from '#lib'; +import { parseDuration, type BushArgumentTypeCaster, type ParsedDuration } from '#lib'; export const contentWithDuration: BushArgumentTypeCaster<Promise<ParsedDuration>> = async (_, phrase) => { - return client.util.parseDuration(phrase); + return parseDuration(phrase); }; diff --git a/src/arguments/discordEmoji.ts b/src/arguments/discordEmoji.ts index 57710e4..92d6502 100644 --- a/src/arguments/discordEmoji.ts +++ b/src/arguments/discordEmoji.ts @@ -1,9 +1,9 @@ -import type { BushArgumentTypeCaster } from '#lib'; +import { regex, type BushArgumentTypeCaster } from '#lib'; import type { Snowflake } from 'discord.js'; export const discordEmoji: BushArgumentTypeCaster<DiscordEmojiInfo | null> = (_, phrase) => { if (!phrase) return null; - const validEmoji: RegExpExecArray | null = client.consts.regex.discordEmoji.exec(phrase); + const validEmoji: RegExpExecArray | null = regex.discordEmoji.exec(phrase); if (!validEmoji || !validEmoji.groups) return null; return { name: validEmoji.groups.name, id: validEmoji.groups.id }; }; diff --git a/src/arguments/duration.ts b/src/arguments/duration.ts index ffd9159..09dd3d5 100644 --- a/src/arguments/duration.ts +++ b/src/arguments/duration.ts @@ -1,5 +1,5 @@ -import type { BushArgumentTypeCaster } from '#lib'; +import { parseDuration, type BushArgumentTypeCaster } from '#lib'; export const duration: BushArgumentTypeCaster<number | null> = (_, phrase) => { - return client.util.parseDuration(phrase).duration; + return parseDuration(phrase).duration; }; diff --git a/src/arguments/durationSeconds.ts b/src/arguments/durationSeconds.ts index 432fd8c..d8d6749 100644 --- a/src/arguments/durationSeconds.ts +++ b/src/arguments/durationSeconds.ts @@ -1,6 +1,6 @@ -import type { BushArgumentTypeCaster } from '#lib'; +import { parseDuration, type BushArgumentTypeCaster } from '#lib'; export const durationSeconds: BushArgumentTypeCaster<number | null> = (_, phrase) => { phrase += 's'; - return client.util.parseDuration(phrase).duration; + return parseDuration(phrase).duration; }; diff --git a/src/arguments/messageLink.ts b/src/arguments/messageLink.ts index 457410e..a473485 100644 --- a/src/arguments/messageLink.ts +++ b/src/arguments/messageLink.ts @@ -1,8 +1,8 @@ -import type { BushArgumentTypeCaster } from '#lib'; +import { BushArgumentTypeCaster, regex } from '#lib'; import type { Message } from 'discord.js'; export const messageLink: BushArgumentTypeCaster<Promise<Message | null>> = async (_, phrase) => { - const match = new RegExp(client.consts.regex.messageLink).exec(phrase); + const match = new RegExp(regex.messageLink).exec(phrase); if (!match || !match.groups) return null; const { guild_id, channel_id, message_id } = match.groups; diff --git a/src/arguments/roleWithDuration.ts b/src/arguments/roleWithDuration.ts index e338b79..b97f205 100644 --- a/src/arguments/roleWithDuration.ts +++ b/src/arguments/roleWithDuration.ts @@ -1,12 +1,12 @@ -import type { BushArgumentTypeCaster } from '#lib'; +import { Arg, BushArgumentTypeCaster, parseDuration } from '#lib'; import type { Role } from 'discord.js'; export const roleWithDuration: BushArgumentTypeCaster<Promise<RoleWithDuration | null>> = async (message, phrase) => { // eslint-disable-next-line prefer-const - let { duration, content } = client.util.parseDuration(phrase); + let { duration, content } = parseDuration(phrase); if (content === null || content === undefined) return null; content = content.trim(); - const role = await util.arg.cast('role', message, content); + const role = await Arg.cast('role', message, content); if (!role) return null; return { duration, role }; }; diff --git a/src/arguments/snowflake.ts b/src/arguments/snowflake.ts index dc83909..b98a20f 100644 --- a/src/arguments/snowflake.ts +++ b/src/arguments/snowflake.ts @@ -1,8 +1,8 @@ -import type { BushArgumentTypeCaster } from '#lib'; +import { BushArgumentTypeCaster, regex } from '#lib'; import type { Snowflake } from 'discord.js'; export const snowflake: BushArgumentTypeCaster<Snowflake | null> = (_, phrase) => { if (!phrase) return null; - if (client.consts.regex.snowflake.test(phrase)) return phrase; + if (regex.snowflake.test(phrase)) return phrase; return null; }; @@ -5,7 +5,7 @@ init(); const { dirname } = await import('path'); const { fileURLToPath } = await import('url'); -const { default: config } = await import('./config/options.js'); +const { default: config } = await import('../config/options.js'); const { Sentry } = await import('./lib/common/Sentry.js'); const { BushClient } = await import('./lib/index.js'); @@ -14,7 +14,6 @@ if (!isDry) new Sentry(dirname(fileURLToPath(import.meta.url)) || process.cwd()) BushClient.extendStructures(); const client = new BushClient(config); global.client = client; -global.util = client.util; 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 f6240a5..12245a9 100644 --- a/src/commands/admin/channelPermissions.ts +++ b/src/commands/admin/channelPermissions.ts @@ -1,4 +1,14 @@ -import { BushCommand, ButtonPaginator, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + ButtonPaginator, + clientSendAndPermCheck, + emojis, + formatError, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -15,7 +25,7 @@ export default class ChannelPermissionsCommand extends BushCommand { { id: 'target', description: 'The user/role to change the permissions of.', - type: util.arg.union('member', 'role'), + type: Arg.union('member', 'role'), readableType: 'member|role', prompt: 'What user/role would you like to change?', retry: '{error} Choose a valid user/role to change.', @@ -48,7 +58,7 @@ export default class ChannelPermissionsCommand extends BushCommand { ] } ], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), userPermissions: [PermissionFlagsBits.Administrator], channel: 'guild', slash: true, @@ -64,9 +74,9 @@ export default class ChannelPermissionsCommand extends BushCommand { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); const permission = message.util.isSlashMessage(message) - ? await util.arg.cast('permission', message, args.permission) + ? await Arg.cast('permission', message, args.permission) : args.permission; - if (!permission) return await message.util.reply(`${util.emojis.error} Invalid permission.`); + if (!permission) return await message.util.reply(`${emojis.error} Invalid permission.`); const failedChannels = []; for (const [, channel] of message.guild.channels.cache) { try { @@ -79,7 +89,7 @@ export default class ChannelPermissionsCommand extends BushCommand { { reason: 'Changing overwrites for mass channel perms command' } ); } catch (e) { - void client.console.error('channelPermissions', util.formatError(e, false)); + void client.console.error('channelPermissions', formatError(e, false)); failedChannels.push(channel); } } diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts index 80952cc..c731f08 100644 --- a/src/commands/admin/roleAll.ts +++ b/src/commands/admin/roleAll.ts @@ -1,4 +1,12 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + emojis, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits, type GuildMember } from 'discord.js'; @@ -32,7 +40,7 @@ export default class RoleAllCommand extends BushCommand { } ], channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), userPermissions: [PermissionFlagsBits.Administrator], typing: true, slash: true, @@ -43,11 +51,11 @@ export default class RoleAllCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { role: ArgType<'role'>; bots: ArgType<'flag'> }) { assert(message.inGuild()); if (!message.member!.permissions.has(PermissionFlagsBits.Administrator) && !message.member!.user.isOwner()) - return await message.util.reply(`${util.emojis.error} You must have admin perms to use this command.`); + return await message.util.reply(`${emojis.error} You must have admin perms to use this command.`); if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); if (args.role.comparePositionTo(message.guild.members.me!.roles.highest) >= 0 && !args.role) { - return await message.util.reply(`${util.emojis.error} I cannot assign a role higher or equal to my highest role.`); + return await message.util.reply(`${emojis.error} I cannot assign a role higher or equal to my highest role.`); } let members = await message.guild.members.fetch(); @@ -62,7 +70,7 @@ export default class RoleAllCommand extends BushCommand { return true; }); - await message.util.reply(`${util.emojis.loading} adding roles to ${members.size} members`); + await message.util.reply(`${emojis.loading} adding roles to ${members.size} members`); const promises = members.map((member: GuildMember) => { return member.roles.add(args.role, `RoleAll Command - triggered by ${message.author.tag} (${message.author.id})`); @@ -72,7 +80,7 @@ export default class RoleAllCommand extends BushCommand { if (!failed.length) { await message.util.sendNew({ - content: `${util.emojis.success} Finished adding <@&${args.role.id}> to **${members.size}** member${ + content: `${emojis.success} Finished adding <@&${args.role.id}> to **${members.size}** member${ members.size > 1 ? 's' : '' }.`, allowedMentions: AllowedMentions.none() @@ -80,7 +88,7 @@ export default class RoleAllCommand extends BushCommand { } else { const array = [...members.values()]; await message.util.sendNew({ - content: `${util.emojis.warn} Finished adding <@&${args.role.id}> to **${members.size - failed.length}** member${ + content: `${emojis.warn} Finished adding <@&${args.role.id}> to **${members.size - failed.length}** member${ members.size - failed.length > 1 ? 's' : '' }! Failed members:\n${failed.map((_, index) => `<@${array[index].id}>`).join(' ')}`, allowedMentions: AllowedMentions.none() diff --git a/src/commands/config/_customAutomodPhrases.ts b/src/commands/config/_customAutomodPhrases.ts index 13887ae..d60688c 100644 --- a/src/commands/config/_customAutomodPhrases.ts +++ b/src/commands/config/_customAutomodPhrases.ts @@ -30,7 +30,7 @@ // ], // slash: true, // channel: 'guild', -// clientPermissions: (m) => util.clientSendAndPermCheck(m), +// clientPermissions: (m) => clientSendAndPermCheck(m), // userPermissions: [PermissionFlagsBits.ManageGuild] // }); // } diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts index de457c0..80acd0b 100644 --- a/src/commands/config/blacklist.ts +++ b/src/commands/config/blacklist.ts @@ -1,4 +1,17 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + addOrRemoveFromArray, + AllowedMentions, + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + format, + getGlobal, + setGlobal, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, GuildMember, PermissionFlagsBits, User } from 'discord.js'; @@ -23,7 +36,7 @@ export default class BlacklistCommand extends BushCommand { { id: 'target', description: 'The channel/user to blacklist.', - type: util.arg.union('channel', 'user'), + type: Arg.union('channel', 'user'), readableType: 'channel|user', prompt: 'What channel or user that you would like to blacklist/unblacklist?', retry: '{error} Pick a valid user or channel.', @@ -41,7 +54,7 @@ export default class BlacklistCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.ManageGuild] }); } @@ -59,26 +72,26 @@ export default class BlacklistCommand extends BushCommand { const global = args.global && message.author.isOwner(); const target = typeof args.target === 'string' - ? (await util.arg.cast('textChannel', message, args.target)) ?? (await util.arg.cast('user', message, args.target)) + ? (await Arg.cast('textChannel', message, args.target)) ?? (await Arg.cast('user', message, args.target)) : args.target; - if (!target) return await message.util.reply(`${util.emojis.error} Choose a valid channel or user.`); + if (!target) return await message.util.reply(`${emojis.error} Choose a valid channel or user.`); const targetID = target.id; if (!message.inGuild() && !global) - return await message.util.reply(`${util.emojis.error} You have to be in a guild to disable commands.`); + return await message.util.reply(`${emojis.error} You have to be in a guild to disable commands.`); if (!global) assert(message.inGuild()); const blacklistedUsers = global - ? util.getGlobal('blacklistedUsers') + ? getGlobal('blacklistedUsers') : (await message.guild!.getSetting('blacklistedChannels')) ?? []; const blacklistedChannels = global - ? util.getGlobal('blacklistedChannels') + ? getGlobal('blacklistedChannels') : (await message.guild!.getSetting('blacklistedUsers')) ?? []; if (action === 'toggle') { action = blacklistedUsers.includes(targetID) || blacklistedChannels.includes(targetID) ? 'unblacklist' : 'blacklist'; } - const newValue = util.addOrRemoveFromArray( + const newValue = addOrRemoveFromArray( action === 'blacklist' ? 'add' : 'remove', target instanceof User ? blacklistedUsers : blacklistedChannels, targetID @@ -87,22 +100,22 @@ export default class BlacklistCommand extends BushCommand { const key = target instanceof User ? 'blacklistedUsers' : 'blacklistedChannels'; const success = await (global - ? util.setGlobal(key, newValue) + ? setGlobal(key, newValue) : message.guild!.setSetting(key, newValue, message.member as GuildMember) ).catch(() => false); if (!success) return await message.util.reply({ - content: `${util.emojis.error} There was an error${global ? ' globally' : ''} ${action}ing ${util.format.input( + content: `${emojis.error} There was an error${global ? ' globally' : ''} ${action}ing ${format.input( target instanceof User ? target.tag : target.name )}.`, allowedMentions: AllowedMentions.none() }); else return await message.util.reply({ - content: `${util.emojis.success} Successfully ${action}ed ${util.format.input( - target instanceof User ? target.tag : target.name - )}${global ? ' globally' : ''}.`, + content: `${emojis.success} Successfully ${action}ed ${format.input(target instanceof User ? target.tag : target.name)}${ + global ? ' globally' : '' + }.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts index 689a3af..f0db467 100644 --- a/src/commands/config/config.ts +++ b/src/commands/config/config.ts @@ -1,7 +1,14 @@ import { + addOrRemoveFromArray, BushCommand, + clientSendAndPermCheck, + colors, + emojis, GuildNoArraySetting, guildSettingsObj, + inspectAndRedact, + oxford, + prefix, settingsArr, type ArgType, type CommandMessage, @@ -145,7 +152,7 @@ export default class ConfigCommand extends BushCommand { }; }), channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.ManageGuild] }); } @@ -171,11 +178,11 @@ export default class ConfigCommand extends BushCommand { id: 'action', type: actionType, prompt: { - start: `Would you like to ${util.oxford( + start: `Would you like to ${oxford( actionType!.map((a) => `\`${a}\``), 'or' )} the \`${setting}\` setting?`, - retry: `{error} Choose one of the following actions to perform on the ${setting} setting: ${util.oxford( + retry: `{error} Choose one of the following actions to perform on the ${setting} setting: ${oxford( actionType!.map((a) => `\`${a}\``), 'or' )}`, @@ -219,7 +226,7 @@ export default class ConfigCommand extends BushCommand { assert(message.member); if (!message.member.permissions.has(PermissionFlagsBits.ManageGuild) && !message.member?.user.isOwner()) - return await message.util.reply(`${util.emojis.error} You must have the **Manage Server** permission to run this command.`); + return await message.util.reply(`${emojis.error} You must have the **Manage Server** permission to run this command.`); const setting = message.util.isSlash ? (camelCase(args.subcommandGroup)! as GuildSettings) : args.setting!; const action = message.util.isSlash ? args.subcommand! : args.action!; const value = args.value; @@ -238,15 +245,13 @@ export default class ConfigCommand extends BushCommand { }; if (!value && !(['clear', 'delete'] as const).includes(action)) - return await message.util.reply( - `${util.emojis.error} You must choose a value to ${action} ${this.grammar(action, setting)}` - ); + return await message.util.reply(`${emojis.error} You must choose a value to ${action} ${this.grammar(action, setting)}`); switch (action) { case 'add': case 'remove': { const existing = (await message.guild.getSetting(setting)) as string[]; - const updated = util.addOrRemoveFromArray(action, existing, parseVal(value)); + const updated = addOrRemoveFromArray(action, existing, parseVal(value)); await message.guild.setSetting(setting, updated, message.member); const messageOptions = await this.generateMessageOptions(message, setting); msg = (await message.util.reply(messageOptions)) as Message; @@ -311,7 +316,7 @@ export default class ConfigCommand extends BushCommand { ): Promise<MessageOptions & InteractionUpdateOptions> { assert(message.inGuild()); - const settingsEmbed = new EmbedBuilder().setColor(util.colors.default); + const settingsEmbed = new EmbedBuilder().setColor(colors.default); if (!setting) { settingsEmbed.setTitle(`${message.guild.name}'s Settings`); const desc = settingsArr.map((s) => `:wrench: **${guildSettingsObj[s].name}**`).join('\n'); @@ -341,7 +346,7 @@ export default class ConfigCommand extends BushCommand { const func = ((): ((v: string | any) => string) => { switch (type.replace('-array', '') as BaseSettingTypes) { case 'string': - return (v) => util.inspectAndRedact(v); + return (v) => inspectAndRedact(v); case 'channel': return (v) => `<#${v}>`; case 'role': @@ -349,7 +354,7 @@ export default class ConfigCommand extends BushCommand { case 'user': return (v) => `<@${v}>`; case 'custom': - return util.inspectAndRedact; + return inspectAndRedact; default: return (v) => v; } @@ -372,7 +377,7 @@ export default class ConfigCommand extends BushCommand { ); settingsEmbed.setFooter({ - text: `Run "${util.prefix(message)}${message.util.parsed?.alias ?? 'config'} ${ + text: `Run "${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 373b5f6..4f52b7c 100644 --- a/src/commands/config/disable.ts +++ b/src/commands/config/disable.ts @@ -1,4 +1,16 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + addOrRemoveFromArray, + AllowedMentions, + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + getGlobal, + setGlobal, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, AutocompleteInteraction, PermissionFlagsBits } from 'discord.js'; import Fuse from 'fuse.js'; @@ -28,7 +40,7 @@ export default class DisableCommand extends BushCommand { { id: 'command', description: 'The command to disable/enable.', - type: util.arg.union('commandAlias', 'command'), + type: Arg.union('commandAlias', 'command'), readableType: 'command|commandAlias', prompt: 'What command would you like to enable/disable?', retry: '{error} Pick a valid command.', @@ -48,7 +60,7 @@ export default class DisableCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.ManageGuild] }); } @@ -62,23 +74,23 @@ export default class DisableCommand extends BushCommand { let action = (args.action ?? message.util?.parsed?.alias ?? 'toggle') as 'disable' | 'enable' | 'toggle'; const global = args.global && message.author.isOwner(); const commandID = - args.command instanceof BushCommand ? args.command.id : (await util.arg.cast('commandAlias', message, args.command))?.id; + args.command instanceof BushCommand ? args.command.id : (await Arg.cast('commandAlias', message, args.command))?.id; - if (!commandID) return await message.util.reply(`${util.emojis.error} Invalid command.`); + if (!commandID) return await message.util.reply(`${emojis.error} Invalid command.`); if (DisableCommand.blacklistedCommands.includes(commandID)) - return message.util.send(`${util.emojis.error} the ${commandID} command cannot be disabled.`); + return message.util.send(`${emojis.error} the ${commandID} command cannot be disabled.`); - const disabledCommands = global ? util.getGlobal('disabledCommands') : await message.guild.getSetting('disabledCommands'); + const disabledCommands = global ? getGlobal('disabledCommands') : await message.guild.getSetting('disabledCommands'); if (action === 'toggle') action = disabledCommands.includes(commandID) ? 'disable' : 'enable'; - const newValue = util.addOrRemoveFromArray(action === 'disable' ? 'add' : 'remove', disabledCommands, commandID); + const newValue = addOrRemoveFromArray(action === 'disable' ? 'add' : 'remove', disabledCommands, commandID); const success = global - ? await util.setGlobal('disabledCommands', newValue).catch(() => false) + ? await setGlobal('disabledCommands', newValue).catch(() => false) : await message.guild.setSetting('disabledCommands', newValue, message.member!).catch(() => false); if (!success) return await message.util.reply({ - content: `${util.emojis.error} There was an error${global ? ' globally' : ''} **${action.substring( + content: `${emojis.error} There was an error${global ? ' globally' : ''} **${action.substring( 0, action.length - 2 )}ing** the **${commandID}** command.`, @@ -86,10 +98,9 @@ export default class DisableCommand extends BushCommand { }); else return await message.util.reply({ - content: `${util.emojis.success} Successfully **${action.substring( - 0, - action.length - 2 - )}ed** the **${commandID}** command${global ? ' globally' : ''}.`, + content: `${emojis.success} Successfully **${action.substring(0, action.length - 2)}ed** the **${commandID}** command${ + global ? ' globally' : '' + }.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts index 95ae544..e88f4b7 100644 --- a/src/commands/config/features.ts +++ b/src/commands/config/features.ts @@ -1,5 +1,8 @@ import { BushCommand, + clientSendAndPermCheck, + colors, + emojis, guildFeaturesArr, guildFeaturesObj, type CommandMessage, @@ -27,7 +30,7 @@ export default class FeaturesCommand extends BushCommand { examples: ['features'], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [PermissionFlagsBits.ManageGuild] }); } @@ -35,7 +38,7 @@ export default class FeaturesCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage) { assert(message.inGuild()); - const featureEmbed = new EmbedBuilder().setTitle(`${message.guild.name}'s Features`).setColor(util.colors.default); + const featureEmbed = new EmbedBuilder().setTitle(`${message.guild.name}'s Features`).setColor(colors.default); const enabledFeatures = await message.guild.getSetting('enabledFeatures'); this.generateDescription(guildFeaturesArr, enabledFeatures, featureEmbed); @@ -76,8 +79,7 @@ export default class FeaturesCommand extends BushCommand { embed.setDescription( allFeatures .map( - (feature) => - `${currentFeatures.includes(feature) ? util.emojis.check : util.emojis.cross} **${guildFeaturesObj[feature].name}**` + (feature) => `${currentFeatures.includes(feature) ? emojis.check : emojis.cross} **${guildFeaturesObj[feature].name}**` ) .join('\n') ); diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts index 7c76bdf..3726105 100644 --- a/src/commands/config/log.ts +++ b/src/commands/config/log.ts @@ -1,4 +1,14 @@ -import { BushCommand, guildLogsArr, type ArgType, type CommandMessage, type GuildLogType, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + guildLogsArr, + oxford, + type ArgType, + type CommandMessage, + type GuildLogType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType, PermissionFlagsBits } from 'discord.js'; @@ -38,7 +48,7 @@ export default class LogCommand extends BushCommand { } ], channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.ManageGuild] }); } @@ -49,7 +59,7 @@ export default class LogCommand extends BushCommand { type: guildLogsArr, prompt: { start: 'What log type would you like to change?', - retry: `{error} Choose either ${util.oxford( + retry: `{error} Choose either ${oxford( guildLogsArr.map((l) => `\`${l}\``), 'or' )}`, @@ -87,8 +97,8 @@ export default class LogCommand extends BushCommand { return await message.util.reply( `${ success - ? `${util.emojis.success} Successfully ${oldChannel ? 'changed' : 'set'}` - : `${util.emojis.error} Unable to ${oldChannel ? 'change' : 'set'}` + ? `${emojis.success} Successfully ${oldChannel ? 'changed' : 'set'}` + : `${emojis.error} Unable to ${oldChannel ? 'change' : 'set'}` } ${ oldChannel ? `the **${args.log_type}** log channel from <#${oldChannel}>` : `the **${args.log_type}** log channel` } to ${args.channel ? `<#${args.channel.id}>` : '`disabled`'}` diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts index 7bcce32..df4d146 100644 --- a/src/commands/dev/__template.ts +++ b/src/commands/dev/__template.ts @@ -1,4 +1,12 @@ -import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class TemplateCommand extends BushCommand { @@ -33,7 +41,7 @@ export default class TemplateCommand extends BushCommand { ownerOnly: true, channel: 'guild', hidden: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -42,7 +50,7 @@ export default class TemplateCommand extends BushCommand { message: CommandMessage | SlashMessage, args: { required_argument: ArgType<'string'>; optional_argument: OptArgType<'string'> } ) { - return await message.util.reply(`${util.emojis.error} Do not use the template command.`); + return await message.util.reply(`${emojis.error} Do not use the template command.`); args; } } diff --git a/src/commands/dev/debug.ts b/src/commands/dev/debug.ts index 682a93d..dd9109c 100644 --- a/src/commands/dev/debug.ts +++ b/src/commands/dev/debug.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, AutocompleteInteraction, ChatInputCommandInteraction } from 'discord.js'; // import Fuse from 'fuse.js'; @@ -24,7 +24,7 @@ // slash: true, // slashGuilds: ['516977525906341928'], // superUserOnly: true, -// clientPermissions: (m) => util.clientSendAndPermCheck(m), +// clientPermissions: (m) => clientSendAndPermCheck(m), // userPermissions: [] // }); // } @@ -52,7 +52,7 @@ // embeds: [{ description: 'And an embed' }] // }); // } else { -// return await message.util.reply(`${util.emojis.error} Invalid action.`); +// return await message.util.reply(`${emojis.error} Invalid action.`); // } // } diff --git a/src/commands/dev/dm.ts b/src/commands/dev/dm.ts index 468fb20..f1e2bce 100644 --- a/src/commands/dev/dm.ts +++ b/src/commands/dev/dm.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, emojis, format, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class DMCommand extends BushCommand { @@ -31,7 +31,7 @@ export default class DMCommand extends BushCommand { slash: false, ownerOnly: true, hidden: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -43,8 +43,8 @@ export default class DMCommand extends BushCommand { try { await client.users.send(args.user.id, args.content); } catch (e) { - return message.util.reply(`${util.emojis.error} There was an error sending ${util.format.input(args.user.tag)} a dm.`); + return message.util.reply(`${emojis.error} There was an error sending ${format.input(args.user.tag)} a dm.`); } - return message.util.reply(`${util.emojis.success} Successfully sent ${util.format.input(args.user.tag)} a dm.`); + return message.util.reply(`${emojis.success} Successfully sent ${format.input(args.user.tag)} a dm.`); } } diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index b8ee9e4..239a06a 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -1,11 +1,17 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { ActivePunishment, + assertAll, BushCommand, BushInspectOptions, + clientSendAndPermCheck, CodeBlockLang, + colors, + emojis, + getMethods, Global, Guild, + inspectCleanRedactCodeblock, Level, ModLog, Shared, @@ -40,8 +46,7 @@ import { PermissionFlagsBits, PermissionsBitField, ReactionCollector, - SelectMenuComponent, - Util + SelectMenuComponent } from 'discord.js'; import got from 'got'; import path from 'path'; @@ -49,15 +54,13 @@ import ts from 'typescript'; import { fileURLToPath } from 'url'; import { promisify } from 'util'; const { transpile } = ts, - emojis = util.emojis, - colors = util.colors, sh = promisify(exec), SnowflakeUtil = new Snowflake_(1420070400000n), __dirname = path.dirname(fileURLToPath(import.meta.url)); /* eslint-enable @typescript-eslint/no-unused-vars */ // prettier-ignore -util.assertAll(ActivePunishment, BushCommand, Global, Guild, Level, ModLog, Shared, StickyRole, Snowflake_, Canvas, exec, ActionRow, ButtonComponent, ButtonInteraction, Collection, Collector, CommandInteraction, ContextMenuCommandInteraction, DMChannel, Embed, Emoji, Interaction, InteractionCollector, Message, Attachment, MessageCollector, OAuth2Scopes, PermissionFlagsBits, PermissionsBitField, ReactionCollector, SelectMenuComponent, Util, path, ts, fileURLToPath, promisify, assert, got, transpile, emojis, colors, sh, SnowflakeUtil, __dirname); +assertAll(ActivePunishment, BushCommand, Global, Guild, Level, ModLog, Shared, StickyRole, Snowflake_, Canvas, exec, ActionRow, ButtonComponent, ButtonInteraction, Collection, Collector, CommandInteraction, ContextMenuCommandInteraction, DMChannel, Embed, Emoji, Interaction, InteractionCollector, Message, Attachment, MessageCollector, OAuth2Scopes, PermissionFlagsBits, PermissionsBitField, ReactionCollector, SelectMenuComponent, path, ts, fileURLToPath, promisify, assert, got, transpile, sh, SnowflakeUtil, __dirname); export default class EvalCommand extends BushCommand { public constructor() { @@ -174,7 +177,7 @@ export default class EvalCommand extends BushCommand { ], slash: true, ownerOnly: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -207,12 +210,11 @@ export default class EvalCommand extends BushCommand { no_inspect_strings: ArgType<'flag'>; } ) { - if (!message.author.isOwner()) - return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); + if (!message.author.isOwner()) return await message.util.reply(`${emojis.error} Only my developers can run this command.`); if (message.util.isSlashMessage(message)) await message.interaction.deferReply({ ephemeral: silent }); if (!sudo && ['delete', 'destroy'].some((p) => argCode.includes(p))) { - return await message.util.send(`${util.emojis.error} This eval was blocked by smooth brain protection™.`); + return await message.util.send(`${emojis.error} This eval was blocked by smooth brain protection™.`); } const isTypescript = typescript || argCode.includes('```ts'); @@ -308,12 +310,12 @@ export default class EvalCommand extends BushCommand { private async codeblock(obj: any, language: CodeBlockLang, options: CodeBlockCustomOptions = {}) { if (options.prototype) obj = Object.getPrototypeOf(obj); - if (options.methods) obj = util.getMethods(obj); + if (options.methods) obj = getMethods(obj); options.depth ??= 1; options.getters ??= true; - return util.inspectCleanRedactCodeblock(obj, language, options); + return inspectCleanRedactCodeblock(obj, language, options); } } diff --git a/src/commands/dev/javascript.ts b/src/commands/dev/javascript.ts index fd1894b..7c47f2f 100644 --- a/src/commands/dev/javascript.ts +++ b/src/commands/dev/javascript.ts @@ -1,4 +1,14 @@ -import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + inspectCleanRedactCodeblock, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder } from 'discord.js'; import { VM } from 'vm2'; @@ -35,7 +45,7 @@ export default class JavascriptCommand extends BushCommand { ], slash: true, superUserOnly: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -44,36 +54,35 @@ export default class JavascriptCommand extends BushCommand { message: CommandMessage | SlashMessage, args: { code: ArgType<'string'>; sel_depth: OptArgType<'integer'> } ) { - if (!message.author.isSuperUser()) - return await message.util.reply(`${util.emojis.error} Only super users can run this command.`); + if (!message.author.isSuperUser()) return await message.util.reply(`${emojis.error} Only super users can run this command.`); if (message.util.isSlashMessage(message)) { await message.interaction.deferReply({ ephemeral: false }); } const code = args.code.replace(/[“”]/g, '"').replace(/```*(?:js)?/g, ''); const embed = new EmbedBuilder(); - const input = await util.inspectCleanRedactCodeblock(code, 'js'); + const input = await 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 util.inspectCleanRedactCodeblock(rawOutput, 'js', { + const output = await inspectCleanRedactCodeblock(rawOutput, 'js', { depth: args.sel_depth ?? 0, getters: true, inspectStrings: true, colors: false }); - embed.setTitle(`${util.emojis.successFull} Successfully Evaluated Expression`).setColor(util.colors.success); + embed.setTitle(`${emojis.successFull} Successfully Evaluated Expression`).setColor(colors.success); embed.addFields([ { name: '📥 Input', value: input }, { name: '📤 Output', value: output } ]); } catch (e) { - embed.setTitle(`${util.emojis.errorFull} Unable to Evaluate Expression`).setColor(util.colors.error); + embed.setTitle(`${emojis.errorFull} Unable to Evaluate Expression`).setColor(colors.error); embed.addFields([ { name: '📥 Input', value: input }, - { name: '📤 Error', value: await util.inspectCleanRedactCodeblock(e, 'js', { colors: false }) } + { name: '📤 Error', value: await inspectCleanRedactCodeblock(e, 'js', { colors: false }) } ]); } diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts index 96c395f..8c2000f 100644 --- a/src/commands/dev/reload.ts +++ b/src/commands/dev/reload.ts @@ -1,4 +1,13 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + codeblock, + emojis, + formatError, + shell, + type CommandMessage, + type SlashMessage +} from '#lib'; export default class ReloadCommand extends BushCommand { public constructor() { @@ -22,19 +31,18 @@ export default class ReloadCommand extends BushCommand { ownerOnly: true, typing: true, slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } public override async exec(message: CommandMessage | SlashMessage /* args: { fast: ArgType<'flag'> } */) { - if (!message.author.isOwner()) - return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); + if (!message.author.isOwner()) return await message.util.reply(`${emojis.error} Only my developers can run this command.`); let output: { stdout: string; stderr: string }; try { const s = new Date(); - output = await util.shell(`yarn build:${/* args.fast ? 'esbuild' : */ 'tsc'}`); + output = await shell(`yarn build:${/* args.fast ? 'esbuild' : */ 'tsc'}`); await Promise.all([ client.commandHandler.reloadAll(), client.listenerHandler.reloadAll(), @@ -46,9 +54,7 @@ export default class ReloadCommand extends BushCommand { 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 util.codeblock(util.formatError(e), 2048 - 34, 'js', true)}` - ); + return message.util.send(`An error occurred while reloading:\n${await codeblock(formatError(e), 2048 - 34, 'js', true)}`); } } } diff --git a/src/commands/dev/say.ts b/src/commands/dev/say.ts index a452126..6ec52a1 100644 --- a/src/commands/dev/say.ts +++ b/src/commands/dev/say.ts @@ -1,4 +1,12 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + emojis, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class SayCommand extends BushCommand { @@ -21,15 +29,14 @@ export default class SayCommand extends BushCommand { } ], ownerOnly: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], slash: true }); } public override async exec(message: CommandMessage | SlashMessage, args: { content: ArgType<'string'> }) { - if (!message.author.isOwner()) - return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); + if (!message.author.isOwner()) return await message.util.reply(`${emojis.error} Only my developers can run this command.`); await message.delete().catch(() => null); await message.util.send({ content: args.content, allowedMentions: AllowedMentions.none() }).catch(() => null); @@ -38,7 +45,7 @@ export default class SayCommand extends BushCommand { public override async execSlash(message: SlashMessage, args: { content: string }) { if (!client.config.owners.includes(message.author.id)) { return await message.interaction.reply({ - content: `${util.emojis.error} Only my developers can run this command.`, + 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 e99bcda..28a4e5d 100644 --- a/src/commands/dev/servers.ts +++ b/src/commands/dev/servers.ts @@ -1,4 +1,13 @@ -import { BushCommand, ButtonPaginator, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + ButtonPaginator, + chunk, + clientSendAndPermCheck, + colors, + format, + type CommandMessage, + type SlashMessage +} from '#lib'; import { stripIndent } from '#tags'; import { type APIEmbed, type Guild } from 'discord.js'; @@ -10,7 +19,7 @@ export default class ServersCommand extends BushCommand { description: 'Displays all the severs the bot is in', usage: ['servers'], examples: ['servers'], - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], ownerOnly: true }); @@ -18,13 +27,13 @@ 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 chunkedGuilds: Guild[][] = util.chunk(guilds, 10); + const chunkedGuilds: Guild[][] = chunk(guilds, 10); const embeds: APIEmbed[] = chunkedGuilds.map((chunk) => { return { title: `Server List [\`${guilds.length.toLocaleString()}\`]`, - color: util.colors.default, + color: colors.default, fields: chunk.map((guild) => ({ - name: util.format.input(guild.name), + 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} diff --git a/src/commands/dev/sh.ts b/src/commands/dev/sh.ts index 8c62f5d..f7c17bd 100644 --- a/src/commands/dev/sh.ts +++ b/src/commands/dev/sh.ts @@ -1,8 +1,18 @@ -import { ArgType, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { + ArgType, + BushCommand, + clientSendAndPermCheck, + codeblock, + colors, + emojis, + formatError, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import chalk from 'chalk'; import { exec } from 'child_process'; -import { ApplicationCommandOptionType, EmbedBuilder, Util } from 'discord.js'; +import { ApplicationCommandOptionType, cleanCodeBlockContent, EmbedBuilder } from 'discord.js'; import { promisify } from 'util'; assert(chalk); @@ -11,7 +21,7 @@ const sh = promisify(exec); const clean = (text: string | any) => { chalk.toString; if (typeof text === 'string') { - return (text = Util.cleanCodeBlockContent(text)); + return (text = cleanCodeBlockContent(text)); } else return text; }; @@ -35,24 +45,24 @@ export default class ShCommand extends BushCommand { } ], ownerOnly: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } public override async exec(message: CommandMessage | SlashMessage, args: { command: ArgType<'string'> }) { if (!client.config.owners.includes(message.author.id)) - return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); + return await message.util.reply(`${emojis.error} Only my developers can run this command.`); const input = clean(args.command); const embed = new EmbedBuilder() - .setColor(util.colors.gray) + .setColor(colors.gray) .setFooter({ text: message.author.tag, iconURL: message.author.avatarURL() ?? undefined }) .setTimestamp() .setTitle('Shell Command') .addFields([ - { name: '📥 Input', value: await util.codeblock(input, 1024, 'sh', true) }, - { name: 'Running', value: util.emojis.loading } + { name: '📥 Input', value: await codeblock(input, 1024, 'sh', true) }, + { name: 'Running', value: emojis.loading } ]); await message.util.reply({ embeds: [embed] }); @@ -69,20 +79,14 @@ export default class ShCommand extends BushCommand { const stdout = /* strip( */ clean(output.stdout); /* ) */ const stderr = /* strip( */ clean(output.stderr); /* ) */ - embed - .setTitle(`${util.emojis.successFull} Executed command successfully.`) - .setColor(util.colors.success) - .spliceFields(1, 1); + embed.setTitle(`${emojis.successFull} Executed command successfully.`).setColor(colors.success).spliceFields(1, 1); - if (stdout) embed.addFields([{ name: '📤 stdout', value: await util.codeblock(stdout, 1024, 'ansi', true) }]); - if (stderr) embed.addFields([{ name: '📤 stderr', value: await util.codeblock(stderr, 1024, 'ansi', true) }]); + 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) }]); } catch (e) { - embed - .setTitle(`${util.emojis.errorFull} An error occurred while executing.`) - .setColor(util.colors.error) - .spliceFields(1, 1); + embed.setTitle(`${emojis.errorFull} An error occurred while executing.`).setColor(colors.error).spliceFields(1, 1); - embed.addFields([{ name: '📤 Output', value: await util.codeblock(util.formatError(e, true), 1024, 'ansi', true) }]); + embed.addFields([{ name: '📤 Output', value: await 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 6a2b745..3de04bf 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,4 +1,13 @@ -import { BushCommand, type ArgType, type CommandMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + format, + getShared, + insertOrRemoveFromShared, + type ArgType, + type CommandMessage +} from '#lib'; import { type ArgumentGeneratorReturn, type ArgumentTypeCasterReturn } from 'discord-akairo'; export default class SuperUserCommand extends BushCommand { @@ -9,7 +18,7 @@ export default class SuperUserCommand extends BushCommand { description: 'A command to manage superusers.', usage: ['superuser <add/remove> <user>'], examples: ['superuser add IRONM00N'], - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], ownerOnly: true, helpArgs: [ @@ -54,29 +63,28 @@ 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(`${util.emojis.error} Only my developers can run this command.`); + if (!message.author.isOwner()) return await message.util.reply(`${emojis.error} Only my developers can run this command.`); - const superUsers: string[] = util.getShared('superUsers'); + const superUsers: string[] = getShared('superUsers'); if (args.action === 'add' ? superUsers.includes(args.user.id) : !superUsers.includes(args.user.id)) return message.util.reply( - `${util.emojis.warn} ${util.format.input(args.user.tag)} is ${args.action === 'add' ? 'already' : 'not'} a superuser.` + `${emojis.warn} ${format.input(args.user.tag)} is ${args.action === 'add' ? 'already' : 'not'} a superuser.` ); - const success = await util.insertOrRemoveFromShared(args.action, 'superUsers', args.user.id).catch(() => false); + const success = await insertOrRemoveFromShared(args.action, 'superUsers', args.user.id).catch(() => false); if (success) { return await message.util.reply( - `${util.emojis.success} ${args.action == 'remove' ? '' : 'made'} ${util.format.input(args.user.tag)} ${ + `${emojis.success} ${args.action == 'remove' ? '' : 'made'} ${format.input(args.user.tag)} ${ args.action == 'remove' ? 'is no longer ' : '' }a superuser.` ); } else { return await message.util.reply( - `${util.emojis.error} There was an error ${args.action == 'remove' ? `removing` : 'making'} ${util.format.input( - args.user.tag - )} ${args.action == 'remove' ? `from` : 'to'} the superuser list.` + `${emojis.error} There was an error ${args.action == 'remove' ? `removing` : 'making'} ${format.input(args.user.tag)} ${ + args.action == 'remove' ? `from` : 'to' + } the superuser list.` ); } } diff --git a/src/commands/dev/syncAutomod.ts b/src/commands/dev/syncAutomod.ts index 9954e70..c78e6c0 100644 --- a/src/commands/dev/syncAutomod.ts +++ b/src/commands/dev/syncAutomod.ts @@ -1,4 +1,4 @@ -import { BushCommand, Shared, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, emojis, Shared, type CommandMessage, type SlashMessage } from '#lib'; import got from 'got'; import typescript from 'typescript'; import { NodeVM } from 'vm2'; @@ -13,14 +13,14 @@ export default class SyncAutomodCommand extends BushCommand { examples: ['sync-automod'], slash: false, hidden: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } public override async exec(message: CommandMessage | SlashMessage) { if (!message.author.isOwner() && message.author.id !== '497789163555389441') - return await message.util.reply(`${util.emojis.error} Only a very select few may use this command.`); + return await message.util.reply(`${emojis.error} Only a very select few may use this command.`); const badLinks = (await got.get('https://raw.githubusercontent.com/NotEnoughUpdates/bush-bot/master/src/lib/badlinks.ts')) .body; @@ -38,6 +38,6 @@ export default class SyncAutomodCommand extends BushCommand { row.badWords = badWordsParsed; await row.save(); - return await message.util.reply(`${util.emojis.success} Automod info synced.`); + return await message.util.reply(`${emojis.success} Automod info synced.`); } } diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts index d54d544..9491d19 100644 --- a/src/commands/dev/test.ts +++ b/src/commands/dev/test.ts @@ -1,4 +1,13 @@ -import { BushCommand, ButtonPaginator, OptArgType, Shared, type CommandMessage } from '#lib'; +import { + BushCommand, + ButtonPaginator, + clientSendAndPermCheck, + colors, + emojis, + OptArgType, + Shared, + type CommandMessage +} from '#lib'; import { ActionRowBuilder, ButtonBuilder, @@ -33,7 +42,7 @@ export default class TestCommand extends BushCommand { } ], superUserOnly: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -65,7 +74,7 @@ export default class TestCommand extends BushCommand { const embed = new EmbedBuilder() .addFields([{ name: 'Field Name', value: 'Field Content' }]) .setAuthor({ name: 'Author', iconURL: 'https://www.w3schools.com/w3css/img_snowtops.jpg', url: 'https://google.com/' }) - .setColor(message.member?.displayColor ?? util.colors.default) + .setColor(message.member?.displayColor ?? colors.default) .setDescription('Description') .setFooter({ text: 'Footer', iconURL: message.author.avatarURL() ?? undefined }) .setURL('https://duckduckgo.com/') @@ -106,12 +115,12 @@ export default class TestCommand extends BushCommand { const fields = Array(25) .fill(0) .map((_, i) => ({ name: `Field ${i}`, value: 'Field Value' })); - const c = util.colors; const o = { description, author, footer, fields, time: Date.now() }; - const colors = [c.red, c.orange, c.gold, c.yellow, c.green, c.darkGreen, c.aqua, c.blue, c.purple, c.pink]; + const c = colors; + const embedColors = [c.red, c.orange, c.gold, c.yellow, c.green, c.darkGreen, c.aqua, c.blue, c.purple, c.pink]; - const embeds = colors.map((c, i) => ({ ...o, title: `Embed Title ${i}`, color: c })); + const embeds = embedColors.map((c, i) => ({ ...o, title: `Embed Title ${i}`, color: c })); const ButtonRows: ActionRowBuilder<ButtonBuilder>[] = []; for (let a = 1; a <= 5; a++) { @@ -125,7 +134,7 @@ 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(`${util.emojis.error} This test can only be run in a guild.`); + if (!message.guild) return await message.util.reply(`${emojis.error} This test can only be run in a guild.`); await client.guilds.fetch(); const promises: Promise<Collection<string, ApplicationCommand>>[] = []; client.guilds.cache.each((guild) => { @@ -136,16 +145,16 @@ export default class TestCommand extends BushCommand { await client.application!.commands.fetch(); await client.application!.commands.set([]); - return await message.util.reply(`${util.emojis.success} Removed guild commands and global commands.`); + 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())) { - return message.util.reply(`${util.emojis.error} no`); + return message.util.reply(`${emojis.error} no`); } else if (['sync automod'].includes(args.feature?.toLowerCase())) { const row = (await Shared.findByPk(0))!; row.badLinks = badLinksArray; row.badLinksSecret = badLinksSecretArray; row.badWords = badWords; await row.save(); - return await message.util.reply(`${util.emojis.success} Synced automod.`); + return await message.util.reply(`${emojis.success} Synced automod.`); } else if (['modal'].includes(args.feature?.toLowerCase())) { const m = await message.util.reply({ content: 'Click for modal', diff --git a/src/commands/fun/coinFlip.ts b/src/commands/fun/coinFlip.ts index fea5cb5..3adf7f2 100644 --- a/src/commands/fun/coinFlip.ts +++ b/src/commands/fun/coinFlip.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, type CommandMessage, type SlashMessage } from '#lib'; export default class CoinFlipCommand extends BushCommand { public constructor() { @@ -8,7 +8,7 @@ export default class CoinFlipCommand extends BushCommand { description: 'Flip a virtual coin.', usage: ['coinflip'], examples: ['coinflip'], - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], slash: true }); diff --git a/src/commands/fun/dice.ts b/src/commands/fun/dice.ts index b2bc7e4..e7e5927 100644 --- a/src/commands/fun/dice.ts +++ b/src/commands/fun/dice.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, type CommandMessage, type SlashMessage } from '#lib'; export default class DiceCommand extends BushCommand { public constructor() { @@ -8,7 +8,7 @@ export default class DiceCommand extends BushCommand { description: 'Roll virtual dice.', usage: ['dice'], examples: ['dice'], - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], slash: true }); diff --git a/src/commands/fun/eightBall.ts b/src/commands/fun/eightBall.ts index 66fcc45..eb5aee8 100644 --- a/src/commands/fun/eightBall.ts +++ b/src/commands/fun/eightBall.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, type CommandMessage, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class EightBallCommand extends BushCommand { @@ -21,7 +21,7 @@ export default class EightBallCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } diff --git a/src/commands/fun/minesweeper.ts b/src/commands/fun/minesweeper.ts index d25cb5d..c2fdccf 100644 --- a/src/commands/fun/minesweeper.ts +++ b/src/commands/fun/minesweeper.ts @@ -1,4 +1,12 @@ -import { BushCommand, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + OptArgType, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import { Minesweeper } from '@notenoughupdates/discord.js-minesweeper'; import assert from 'assert'; import { ApplicationCommandOptionType } from 'discord.js'; @@ -63,7 +71,7 @@ export default class MinesweeperCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -95,14 +103,14 @@ export default class MinesweeperCommand extends BushCommand { if (args.rows * args.columns <= args.mines * 2) return message.util.reply( - `${util.emojis.error} The number of roles multiplied by the number of columns must be greater than or equal to the number of mines multiplied by two.` + `${emojis.error} The number of roles multiplied by the number of columns must be greater than or equal to the number of mines multiplied by two.` ); - if (!matrix) return await message.util.reply(`${util.emojis.error} Something went wrong.`); + if (!matrix) return await message.util.reply(`${emojis.error} Something went wrong.`); const res = matrix.toString().replaceAll(':zero:', ':blue_square:'); - if (res.length > 2000) return message.util.reply(`${util.emojis.error} The minesweeper generated is over 2,000 characters.`); + if (res.length > 2000) return message.util.reply(`${emojis.error} The minesweeper generated is over 2,000 characters.`); return await message.util.reply(res); } diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts index 544c30a..3eae98c 100644 --- a/src/commands/info/avatar.ts +++ b/src/commands/info/avatar.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { Arg, BushCommand, clientSendAndPermCheck, colors, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, GuildMember, PermissionFlagsBits } from 'discord.js'; export default class AvatarCommand extends BushCommand { @@ -13,7 +13,7 @@ export default class AvatarCommand extends BushCommand { { id: 'user', description: 'The user you would like to find the avatar of.', - type: util.arg.union('member', 'globalUser'), + type: Arg.union('member', 'globalUser'), readableType: 'member|user', prompt: 'Who would you like to see the avatar of?', retry: '{error} Choose a valid user.', @@ -21,7 +21,7 @@ export default class AvatarCommand extends BushCommand { slashType: ApplicationCommandOptionType.User } ], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], slash: true }); @@ -37,7 +37,7 @@ export default class AvatarCommand extends BushCommand { const guildAvatar = member?.avatarURL(params); - const embed = new EmbedBuilder().setTimestamp().setColor(util.colors.default).setTitle(`${user.tag}'s Avatar`); + const embed = new EmbedBuilder().setTimestamp().setColor(colors.default).setTitle(`${user.tag}'s Avatar`); guildAvatar ? embed.setImage(guildAvatar).setThumbnail(user.avatarURL(params) ?? defaultAvatar) : embed.setImage(user.avatarURL(params) ?? defaultAvatar); diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts index 4a8a36a..decbe04 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -1,4 +1,13 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + colors, + humanizeDuration, + mapIDs, + shell, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { EmbedBuilder, PermissionFlagsBits, version as discordJSVersion } from 'discord.js'; import * as os from 'os'; @@ -15,7 +24,7 @@ export default class BotInfoCommand extends BushCommand { usage: ['bot-info'], examples: ['bot-info'], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -35,14 +44,14 @@ export default class BotInfoCommand extends BushCommand { haiku = 'Haiku' } - const developers = (await util.mapIDs(client.config.owners)).map((u) => u?.tag).join('\n'); - const currentCommit = (await util.shell('git rev-parse HEAD')).stdout.replace('\n', ''); - let repoUrl = (await util.shell('git remote get-url origin')).stdout.replace('\n', ''); + const developers = (await mapIDs(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: util.humanizeDuration(client.uptime!, 2), inline: true }, + { name: '**Uptime**', value: humanizeDuration(client.uptime!, 2), inline: true }, { name: '**Memory Usage**', value: `System: ${prettyBytes(os.totalmem() - os.freemem(), { binary: true })}/${prettyBytes(os.totalmem(), { @@ -73,7 +82,7 @@ export default class BotInfoCommand extends BushCommand { { name: '**Developers**', value: developers, inline: true } ]) .setTimestamp() - .setColor(util.colors.default); + .setColor(colors.default); await message.util.reply({ embeds: [embed] }); } } diff --git a/src/commands/info/color.ts b/src/commands/info/color.ts index f60e28a..84c4b3c 100644 --- a/src/commands/info/color.ts +++ b/src/commands/info/color.ts @@ -1,4 +1,13 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, GuildMember, PermissionFlagsBits, Role } from 'discord.js'; import tinycolor from 'tinycolor2'; @@ -16,7 +25,7 @@ export default class ColorCommand extends BushCommand { { id: 'color', description: 'The color string, role, or member to find the color of.', - type: util.arg.union('tinyColor', 'role', 'member'), + type: Arg.union('tinyColor', 'role', 'member'), readableType: 'color|role|member', match: 'restContent', prompt: 'What color code, role, or user would you like to find the color of?', @@ -25,7 +34,7 @@ export default class ColorCommand extends BushCommand { } ], channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -36,10 +45,7 @@ export default class ColorCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { color: ArgType<'tinyColor' | 'role' | 'member'> }) { const _color = message.util.isSlashMessage(message) - ? ((await util.arg.cast(util.arg.union('tinyColor', 'role', 'member'), message, args.color as string)) as - | string - | Role - | GuildMember) + ? ((await Arg.cast(Arg.union('tinyColor', 'role', 'member'), message, args.color as string)) as string | Role | GuildMember) : args.color; const color = @@ -51,7 +57,7 @@ export default class ColorCommand extends BushCommand { if (_color instanceof Role && _color.hexColor === '#000000') { return await message.util.reply({ - content: `${util.emojis.error} <@&${_color.id}> does not have a color.`, + content: `${emojis.error} <@&${_color.id}> does not have a color.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index 572cf06..92999a5 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -1,9 +1,23 @@ -import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + akairo, + Arg, + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + mappings, + timestampAndDelta, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, ChannelType, EmbedBuilder, + escapeMarkdown, Guild, GuildDefaultMessageNotifications, GuildExplicitContentFilter, @@ -29,7 +43,7 @@ export default class GuildInfoCommand extends BushCommand { { id: 'guild', description: 'The guild to find information about.', - type: util.arg.union('guild', 'snowflake'), + type: Arg.union('guild', 'snowflake'), readableType: 'guild|snowflake', prompt: 'What server would you like to find information about?', retry: '{error} Choose a valid server to find information about.', @@ -38,7 +52,7 @@ export default class GuildInfoCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -46,7 +60,7 @@ export default class GuildInfoCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { guild: OptArgType<'guild' | 'snowflake'> }) { if (!args.guild && !message.inGuild()) { return await message.util.reply( - `${util.emojis.error} You must either provide an server to provide info about or run this command in a server.` + `${emojis.error} You must either provide an server to provide info about or run this command in a server.` ); } @@ -54,7 +68,7 @@ export default class GuildInfoCommand extends BushCommand { if (typeof guild === 'string') { const preview = await client.fetchGuildPreview(`${args.guild}` as Snowflake).catch(() => undefined); if (preview) guild = preview; - else return await message.util.reply(`${util.emojis.error} That guild is not discoverable or does not exist.`); + else return await message.util.reply(`${emojis.error} That guild is not discoverable or does not exist.`); } assert(guild); @@ -63,7 +77,7 @@ export default class GuildInfoCommand extends BushCommand { await guild.fetch(); } - const guildInfoEmbed = new EmbedBuilder().setTitle(guild.name).setColor(util.colors.default); + const guildInfoEmbed = new EmbedBuilder().setTitle(guild.name).setColor(colors.default); if (guild.icon) guildInfoEmbed.setThumbnail(guild.iconURL({ size: 2048, extension: 'png' })); await this.generateAboutField(guildInfoEmbed, guild); @@ -79,16 +93,16 @@ export default class GuildInfoCommand extends BushCommand { private generateDescription(embed: EmbedBuilder, guild: Guild | GuildPreview) { const description: string[] = []; - const otherEmojis = client.consts.mappings.otherEmojis; + const otherEmojis = mappings.otherEmojis; - const verifiedGuilds = Object.values(client.consts.mappings.guilds); + const verifiedGuilds = Object.values(mappings.guilds); if (verifiedGuilds.includes(guild.id as typeof verifiedGuilds[number])) description.push(otherEmojis.BushVerified); if (guild instanceof Guild) { if (guild.premiumTier !== GuildPremiumTier.None) description.push(otherEmojis[`BoostTier${guild.premiumTier}`]); } - const features = client.consts.mappings.features; + const features = mappings.features; const guildFeatures = guild.features.sort((a, b): number => { const aWeight = features[a]?.weight; const bWeight = features[b]?.weight; @@ -103,7 +117,7 @@ export default class GuildInfoCommand extends BushCommand { guildFeatures.forEach((feature) => { if (features[feature]?.emoji) description.push(`${features[feature].emoji}`); else if (features[feature]?.name) description.push(`\`${features[feature].name}\``); - else description.push(`\`${feature.charAt(0) + util.akairo.snakeToCamelCase(feature).substring(1)}\``); + else description.push(`\`${feature.charAt(0) + akairo.snakeToCamelCase(feature).substring(1)}\``); }); } @@ -125,12 +139,12 @@ export default class GuildInfoCommand extends BushCommand { ] as RTCRegion[]; guildAbout.push( - `**Owner:** ${util.discord.escapeMarkdown(guild.members.cache.get(guild.ownerId)?.user.tag ?? '¯\\_(ツ)_/¯')}`, - `**Created** ${util.timestampAndDelta(guild.createdAt, 'd')}`, - `**Members:** ${guild.memberCount.toLocaleString() ?? 0} (${util.emojis.onlineCircle} ${ + `**Owner:** ${escapeMarkdown(guild.members.cache.get(guild.ownerId)?.user.tag ?? '¯\\_(ツ)_/¯')}`, + `**Created** ${timestampAndDelta(guild.createdAt, 'd')}`, + `**Members:** ${guild.memberCount.toLocaleString() ?? 0} (${emojis.onlineCircle} ${ guild.approximatePresenceCount?.toLocaleString() ?? 0 - }, ${util.emojis.offlineCircle} ${(guild.memberCount - (guild.approximatePresenceCount ?? 0)).toLocaleString() ?? 0})`, - `**Regions:** ${guildRegions.map((region) => client.consts.mappings.regions[region] || region).join(', ')}` + }, ${emojis.offlineCircle} ${(guild.memberCount - (guild.approximatePresenceCount ?? 0)).toLocaleString() ?? 0})`, + `**Regions:** ${guildRegions.map((region) => mappings.regions[region] || region).join(', ')}` ); if (guild.premiumSubscriptionCount) guildAbout.push(`**Boosts:** Level ${guild.premiumTier} with ${guild.premiumSubscriptionCount ?? 0} boosts`); @@ -144,9 +158,9 @@ export default class GuildInfoCommand extends BushCommand { if (guild.splash) guildAbout.push(`**Splash:** [link](${guild.splashURL({ size: 4096, extension: 'png' })})`); } else { guildAbout.push( - `**Members:** ${guild.approximateMemberCount?.toLocaleString() ?? 0} (${util.emojis.onlineCircle} ${ + `**Members:** ${guild.approximateMemberCount?.toLocaleString() ?? 0} (${emojis.onlineCircle} ${ guild.approximatePresenceCount?.toLocaleString() ?? 0 - }, ${util.emojis.offlineCircle} ${( + }, ${emojis.offlineCircle} ${( (guild.approximateMemberCount ?? 0) - (guild.approximatePresenceCount ?? 0) ).toLocaleString()})`, `**Emojis:** ${(guild as GuildPreview).emojis.size?.toLocaleString() ?? 0}`, @@ -173,7 +187,7 @@ export default class GuildInfoCommand extends BushCommand { ] as const ).map( (type) => - `${client.consts.mappings.otherEmojis[`Channel${type[0]}`]} ${guild.channels.cache + `${mappings.otherEmojis[`Channel${type[0]}`]} ${guild.channels.cache .filter((channel) => type[1].some((type) => channel.type === type)) .size.toLocaleString()}` ); diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index af44980..051fce5 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -1,4 +1,15 @@ -import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + colors, + format, + invite, + prefix, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ActionRowBuilder, @@ -48,7 +59,7 @@ export default class HelpCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -71,11 +82,11 @@ export default class HelpCommand extends BushCommand { } private helpAll(message: CommandMessage | SlashMessage, args: HelpArgs, row: ActionRowBuilder<ButtonBuilder>) { - const prefix = util.prefix(message); + const prefix_ = prefix(message); const embed = new EmbedBuilder() - .setColor(util.colors.default) + .setColor(colors.default) .setTimestamp() - .setFooter({ text: `For more information about a command use ${prefix}help <command>` }); + .setFooter({ text: `For more information about a command use ${prefix_}help <command>` }); for (const [, category] of this.handler.categories) { const categoryFilter = category.filter((command) => { if (command.pseudo) return false; @@ -100,7 +111,7 @@ export default class HelpCommand extends BushCommand { } private helpIndividual(message: CommandMessage | SlashMessage, row: ActionRowBuilder<ButtonBuilder>, command: BushCommand) { - const embed = new EmbedBuilder().setColor(util.colors.default).setTitle(`${command.id} Command`); + const embed = new EmbedBuilder().setColor(colors.default).setTitle(`${command.id} Command`); let description = `${command.description ?? '*This command does not have a description.*'}`; if (command.note) description += `\n\n${command.note}`; @@ -200,7 +211,7 @@ export default class HelpCommand extends BushCommand { if (command.restrictedGuilds?.length) restrictions.push( `__Restricted Servers__: ${command.restrictedGuilds - .map((g) => util.format.inlineCode(client.guilds.cache.find((g1) => g1.id === g)?.name ?? 'Unknown')) + .map((g) => format.inlineCode(client.guilds.cache.find((g1) => g1.id === g)?.name ?? 'Unknown')) .join(' ')}` ); if (restrictions.length) embed.addFields([{ name: '» Restrictions', value: restrictions.join('\n') }]); @@ -211,7 +222,7 @@ export default class HelpCommand extends BushCommand { const row = new ActionRowBuilder<ButtonBuilder>(); if (!client.config.isDevelopment && !client.guilds.cache.some((guild) => guild.ownerId === message.author.id)) { - row.addComponents([new ButtonBuilder({ style: ButtonStyle.Link, label: 'Invite Me', url: util.invite })]); + 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)) { row.addComponents([ diff --git a/src/commands/info/icon.ts b/src/commands/info/icon.ts index 9c9556b..e66f900 100644 --- a/src/commands/info/icon.ts +++ b/src/commands/info/icon.ts @@ -1,6 +1,6 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, colors, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; -import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; +import { EmbedBuilder, escapeMarkdown, PermissionFlagsBits } from 'discord.js'; export default class IconCommand extends BushCommand { public constructor() { @@ -10,7 +10,7 @@ export default class IconCommand extends BushCommand { description: "A command to get the server's icon", usage: ['icon'], examples: ['icon'], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], channel: 'guild', slash: true @@ -22,14 +22,14 @@ export default class IconCommand extends BushCommand { const embed = new EmbedBuilder() .setTimestamp() - .setColor(util.colors.default) + .setColor(colors.default) .setImage( message.guild.iconURL({ size: 2048, extension: 'png' })! ) - .setTitle(util.discord.escapeMarkdown(message.guild.name)); + .setTitle(escapeMarkdown(message.guild.name)); await message.util.reply({ embeds: [embed] }); } } diff --git a/src/commands/info/links.ts b/src/commands/info/links.ts index 0d5bd15..a7ff30e 100644 --- a/src/commands/info/links.ts +++ b/src/commands/info/links.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, invite, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import packageDotJSON from '../../../package.json' assert { type: 'json' }; @@ -13,7 +13,7 @@ export default class LinksCommand extends BushCommand { description: 'Sends bot links', usage: ['links'], examples: ['links'], - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], slash: true }); @@ -22,7 +22,7 @@ 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()) { - buttonRow.addComponents([new ButtonBuilder({ style: ButtonStyle.Link, label: 'Invite Me', url: util.invite })]); + 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 }), diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index ad7fdcc..060b1f6 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, colors, format, type CommandMessage, type SlashMessage } from '#lib'; import { EmbedBuilder, PermissionFlagsBits, type Message } from 'discord.js'; export default class PingCommand extends BushCommand { @@ -10,7 +10,7 @@ export default class PingCommand extends BushCommand { usage: ['ping'], examples: ['ping'], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -30,8 +30,8 @@ export default class PingCommand extends BushCommand { } private command(message: CommandMessage | SlashMessage, msgLatency: number) { - const botLatency = util.format.codeBlock(`${Math.round(msgLatency)}ms`); - const apiLatency = util.format.codeBlock(`${Math.round(message.client.ws.ping)}ms`); + const botLatency = format.codeBlock(`${Math.round(msgLatency)}ms`); + const apiLatency = format.codeBlock(`${Math.round(message.client.ws.ping)}ms`); const embed = new EmbedBuilder() .setTitle('Pong! 🏓') .addFields([ @@ -39,7 +39,7 @@ export default class PingCommand extends BushCommand { { name: 'API Latency', value: apiLatency, inline: true } ]) .setFooter({ text: message.author.username, iconURL: message.author.displayAvatarURL() }) - .setColor(util.colors.default) + .setColor(colors.default) .setTimestamp(); return message.util.reply({ content: null, diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index f916687..e87ca1f 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -1,5 +1,13 @@ -import { AllowedMentions, BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; -import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + getPronounsOf, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; +import { ApplicationCommandOptionType, EmbedBuilder, escapeMarkdown, PermissionFlagsBits } from 'discord.js'; export default class PronounsCommand extends BushCommand { public constructor() { @@ -20,7 +28,7 @@ export default class PronounsCommand extends BushCommand { slashType: ApplicationCommandOptionType.User } ], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], slash: true }); @@ -32,21 +40,19 @@ export default class PronounsCommand extends BushCommand { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); - const pronouns = await util.getPronounsOf(user); + const pronouns = await getPronounsOf(user); if (!pronouns) { return await message.util.reply({ - content: `${ - author ? 'You do' : `${util.discord.escapeMarkdown(user.tag)} does` - } not appear to have any pronouns set. Please${author ? '' : ' tell them to'} go to https://pronoundb.org/ and set ${ - author ? 'your' : 'their' - } pronouns.`, + content: `${author ? 'You do' : `${escapeMarkdown(user.tag)} does`} not appear to have any pronouns set. Please${ + author ? '' : ' tell them to' + } go to https://pronoundb.org/ and set ${author ? 'your' : 'their'} pronouns.`, allowedMentions: AllowedMentions.none() }); } else { return await message.util.reply({ embeds: [ new EmbedBuilder({ - title: `${author ? 'Your' : `${util.discord.escapeMarkdown(user.tag)}'s`} pronouns:`, + title: `${author ? 'Your' : `${escapeMarkdown(user.tag)}'s`} pronouns:`, description: pronouns, footer: { text: 'Data provided by https://pronoundb.org/' diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts index e74756f..a28f4c5 100644 --- a/src/commands/info/snowflake.ts +++ b/src/commands/info/snowflake.ts @@ -1,9 +1,18 @@ -import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + colors, + timestamp, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import { stripIndent } from '#tags'; import { ApplicationCommandOptionType, ChannelType, EmbedBuilder, + escapeMarkdown, PermissionFlagsBits, SnowflakeUtil, type DeconstructedSnowflake, @@ -31,7 +40,7 @@ export default class SnowflakeCommand extends BushCommand { slashType: ApplicationCommandOptionType.String } ], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], slash: true }); @@ -39,7 +48,7 @@ export default class SnowflakeCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { snowflake: ArgType<'snowflake'> }) { const snowflake = `${args.snowflake}` as Snowflake; - const snowflakeEmbed = new EmbedBuilder().setTitle('Unknown :snowflake:').setColor(util.colors.default); + const snowflakeEmbed = new EmbedBuilder().setTitle('Unknown :snowflake:').setColor(colors.default); // Channel if (client.channels.cache.has(snowflake)) { @@ -47,13 +56,9 @@ export default class SnowflakeCommand extends BushCommand { const channelInfo = [`**Type:** ${BushChannelType[channel.type] ?? ChannelType[channel.type]}`]; if (channel.type === ChannelType.DM) { channelInfo.push( - `**Recipient:** ${util.discord.escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} (${ - channel.recipient?.id ?? '¯\\_(ツ)_/¯' - })` - ); - snowflakeEmbed.setTitle( - `:snowflake: DM with ${util.discord.escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} \`[Channel]\`` + `**Recipient:** ${escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} (${channel.recipient?.id ?? '¯\\_(ツ)_/¯'})` ); + snowflakeEmbed.setTitle(`:snowflake: DM with ${escapeMarkdown(channel.recipient?.tag ?? '¯\\_(ツ)_/¯')} \`[Channel]\``); } else if ( channel.type === ChannelType.GuildCategory || channel.type === ChannelType.GuildNews || @@ -63,10 +68,10 @@ export default class SnowflakeCommand extends BushCommand { channel.isThread() ) { channelInfo.push( - `**Channel Name:** <#${channel.id}> (${util.discord.escapeMarkdown(channel.name)})`, - `**Channel's Server:** ${util.discord.escapeMarkdown(channel.guild.name)} (${channel.guild.id})` + `**Channel Name:** <#${channel.id}> (${escapeMarkdown(channel.name)})`, + `**Channel's Server:** ${escapeMarkdown(channel.guild.name)} (${channel.guild.id})` ); - snowflakeEmbed.setTitle(`:snowflake: ${util.discord.escapeMarkdown(channel.name)} \`[Channel]\``); + snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(channel.name)} \`[Channel]\``); } snowflakeEmbed.addFields([{ name: '» Channel Info', value: channelInfo.join('\n') }]); } @@ -75,12 +80,12 @@ export default class SnowflakeCommand extends BushCommand { if (client.guilds.cache.has(snowflake)) { const guild: Guild = client.guilds.cache.get(snowflake)!; const guildInfo = stripIndent` - **Name:** ${util.discord.escapeMarkdown(guild.name)} - **Owner:** ${util.discord.escapeMarkdown(client.users.cache.get(guild.ownerId)?.tag ?? '¯\\_(ツ)_/¯')} (${guild.ownerId}) + **Name:** ${escapeMarkdown(guild.name)} + **Owner:** ${escapeMarkdown(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 }]); - snowflakeEmbed.setTitle(`:snowflake: ${util.discord.escapeMarkdown(guild.name)} \`[Server]\``); + snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(guild.name)} \`[Server]\``); } // User @@ -88,28 +93,28 @@ export default class SnowflakeCommand extends BushCommand { if (client.users.cache.has(snowflake) || fetchedUser) { const user: User = (client.users.cache.get(snowflake) ?? fetchedUser)!; const userInfo = stripIndent` - **Name:** <@${user.id}> (${util.discord.escapeMarkdown(user.tag)})`; + **Name:** <@${user.id}> (${escapeMarkdown(user.tag)})`; if (user.avatar) snowflakeEmbed.setThumbnail(user.avatarURL({ size: 2048 })!); snowflakeEmbed.addFields([{ name: '» User Info', value: userInfo }]); - snowflakeEmbed.setTitle(`:snowflake: ${util.discord.escapeMarkdown(user.tag)} \`[User]\``); + snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(user.tag)} \`[User]\``); } // Emoji if (client.emojis.cache.has(snowflake)) { const emoji = client.emojis.cache.get(snowflake)!; const emojiInfo = stripIndent` - **Name:** ${util.discord.escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} + **Name:** ${escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} **Animated:** ${emoji.animated}`; if (emoji.url) snowflakeEmbed.setThumbnail(emoji.url); snowflakeEmbed.addFields([{ name: '» Emoji Info', value: emojiInfo }]); - snowflakeEmbed.setTitle(`:snowflake: ${util.discord.escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} \`[Emoji]\``); + snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(emoji.name ?? '¯\\_(ツ)_/¯')} \`[Emoji]\``); } // Role if (message.guild && message.guild.roles.cache.has(snowflake)) { const role: Role = message.guild.roles.cache.get(snowflake)!; const roleInfo = stripIndent` - **Name:** <@&${role.id}> (${util.discord.escapeMarkdown(role.name)}) + **Name:** <@&${role.id}> (${escapeMarkdown(role.name)}) **Members:** ${role.members.size} **Hoisted:** ${role.hoist} **Managed:** ${role.managed} @@ -117,14 +122,14 @@ export default class SnowflakeCommand extends BushCommand { **Hex Color:** ${role.hexColor}`; if (role.color) snowflakeEmbed.setColor(role.color); snowflakeEmbed.addFields([{ name: '» Role Info', value: roleInfo }]); - snowflakeEmbed.setTitle(`:snowflake: ${util.discord.escapeMarkdown(role.name)} \`[Role]\``); + snowflakeEmbed.setTitle(`:snowflake: ${escapeMarkdown(role.name)} \`[Role]\``); } // SnowflakeInfo const deconstructedSnowflake: DeconstructedSnowflake = SnowflakeUtil.deconstruct(snowflake); const snowflakeInfo = stripIndent` **Timestamp:** ${deconstructedSnowflake.timestamp} - **Created:** ${util.timestamp(new Date(Number(deconstructedSnowflake.timestamp)))} + **Created:** ${timestamp(new Date(Number(deconstructedSnowflake.timestamp)))} **Worker ID:** ${deconstructedSnowflake.workerId} **Process ID:** ${deconstructedSnowflake.processId} **Increment:** ${deconstructedSnowflake.increment}`; diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index d617756..a39e28a 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -1,9 +1,26 @@ -import { BushCommand, Time, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + getPronounsOf, + getShared, + mappings, + oxford, + sleep, + Time, + timestampAndDelta, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import { ActivityType, ApplicationCommandOptionType, ApplicationFlagsBitField, EmbedBuilder, + escapeMarkdown, PermissionFlagsBits, TeamMemberMembershipState, UserFlags, @@ -26,7 +43,7 @@ export default class UserInfoCommand extends BushCommand { { id: 'user', description: 'The user you would like to find information about.', - type: util.arg.union('user', 'snowflake'), + type: Arg.union('user', 'snowflake'), readableType: 'user|snowflake', prompt: 'What user would you like to find information about?', retry: '{error} Choose a valid user to find information about.', @@ -35,7 +52,7 @@ export default class UserInfoCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -47,7 +64,7 @@ export default class UserInfoCommand extends BushCommand { : typeof args.user === 'object' ? args.user : await client.users.fetch(`${args.user}`).catch(() => undefined); - if (user === undefined) return message.util.reply(`${util.emojis.error} Invalid user.`); + 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 @@ -58,23 +75,23 @@ export default class UserInfoCommand extends BushCommand { public static async makeUserInfoEmbed(user: User, member?: GuildMember, guild?: Guild | null) { const emojis = []; - const superUsers = util.getShared('superUsers'); + const superUsers = getShared('superUsers'); const userEmbed = new EmbedBuilder() - .setTitle(util.discord.escapeMarkdown(user.tag)) + .setTitle(escapeMarkdown(user.tag)) .setThumbnail(user.displayAvatarURL({ size: 2048, extension: 'png' })) .setTimestamp() .setFooter({ text: user.tag }) - .setColor(member?.displayColor ?? util.colors.default); + .setColor(member?.displayColor ?? colors.default); // Flags - if (client.config.owners.includes(user.id)) emojis.push(client.consts.mappings.otherEmojis.Developer); - if (superUsers.includes(user.id)) emojis.push(client.consts.mappings.otherEmojis.Superuser); + if (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) { flags.forEach((f) => { - if (client.consts.mappings.userFlags[f] !== undefined) { - emojis.push(client.consts.mappings.userFlags[f]); + if (mappings.userFlags[f] !== undefined) { + emojis.push(mappings.userFlags[f]); } else emojis.push(`\`${f}\``); }); } @@ -82,18 +99,18 @@ export default class UserInfoCommand extends BushCommand { // Since discord bald I just guess if someone has nitro if ( Number(user.discriminator) < 10 || - client.consts.mappings.maybeNitroDiscrims.includes(user.discriminator) || + mappings.maybeNitroDiscrims.includes(user.discriminator) || user.displayAvatarURL()?.endsWith('.gif') || user.flags?.has(UserFlags.Partner) || user.flags?.has(UserFlags.Staff) || member?.avatar // server avatar ) { - emojis.push(client.consts.mappings.otherEmojis.Nitro); + emojis.push(mappings.otherEmojis.Nitro); } - if (guild?.ownerId == user.id) emojis.push(client.consts.mappings.otherEmojis.Owner); - else if (member?.permissions.has(PermissionFlagsBits.Administrator)) emojis.push(client.consts.mappings.otherEmojis.Admin); - if (member?.premiumSinceTimestamp) emojis.push(client.consts.mappings.otherEmojis.Booster); + if (guild?.ownerId == user.id) emojis.push(mappings.otherEmojis.Owner); + else if (member?.permissions.has(PermissionFlagsBits.Administrator)) emojis.push(mappings.otherEmojis.Admin); + if (member?.premiumSinceTimestamp) emojis.push(mappings.otherEmojis.Booster); await this.generateGeneralInfoField(userEmbed, user); @@ -121,12 +138,12 @@ export default class UserInfoCommand extends BushCommand { const generalInfo = [ `**Mention:** <@${user.id}>`, `**ID:** ${user.id}`, - `**Created:** ${util.timestampAndDelta(user.createdAt, 'd')}` + `**Created:** ${timestampAndDelta(user.createdAt, 'd')}` ]; 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([util.getPronounsOf(user), util.sleep(2 * Time.Second)]); // cut off request after 2 seconds + const pronouns = await Promise.race([getPronounsOf(user), sleep(2 * Time.Second)]); // cut off request after 2 seconds if (pronouns && typeof pronouns === 'string' && pronouns !== 'Unspecified') generalInfo.push(`**Pronouns:** ${pronouns}`); @@ -140,21 +157,21 @@ export default class UserInfoCommand extends BushCommand { const serverUserInfo = []; if (member.joinedTimestamp) serverUserInfo.push( - `**${member.guild!.ownerId == member.user.id ? 'Created Server' : 'Joined'}:** ${util.timestampAndDelta( + `**${member.guild!.ownerId == member.user.id ? 'Created Server' : 'Joined'}:** ${timestampAndDelta( member.joinedAt!, 'd' )}` ); - if (member.premiumSince) serverUserInfo.push(`**Booster Since:** ${util.timestampAndDelta(member.premiumSince, 'd')}`); + if (member.premiumSince) serverUserInfo.push(`**Booster Since:** ${timestampAndDelta(member.premiumSince, 'd')}`); if (member.displayHexColor) serverUserInfo.push(`**Display Color:** ${member.displayHexColor}`); - if (member.user.id == '322862723090219008' && member.guild?.id == client.consts.mappings.guilds.bush) + if (member.user.id == '322862723090219008' && member.guild?.id == mappings.guilds.bush) serverUserInfo.push(`**General Deletions:** 1⅓`); if ( (['384620942577369088', '496409778822709251'] as const).includes(member.user.id) && - member.guild.id == client.consts.mappings.guilds.bush + member.guild.id == mappings.guilds.bush ) serverUserInfo.push(`**General Deletions:** ⅓`); - if (member?.nickname) serverUserInfo.push(`**Nickname:** ${util.discord.escapeMarkdown(member?.nickname)}`); + if (member?.nickname) serverUserInfo.push(`**Nickname:** ${escapeMarkdown(member?.nickname)}`); if (serverUserInfo.length) embed.addFields([{ name: title, value: serverUserInfo.join('\n') }]); } @@ -179,10 +196,10 @@ export default class UserInfoCommand extends BushCommand { const presenceInfo = []; if (member?.presence.status) presenceInfo.push(`**Status:** ${member.presence.status}`); if (devices && devices.length) - presenceInfo.push(`**${devices.length - 1 ? 'Devices' : 'Device'}:** ${util.oxford(devices, 'and', '')}`); + presenceInfo.push(`**${devices.length - 1 ? 'Devices' : 'Device'}:** ${oxford(devices, 'and', '')}`); if (activitiesNames.length) - presenceInfo.push(`**Activit${activitiesNames.length - 1 ? 'ies' : 'y'}:** ${util.oxford(activitiesNames, 'and', '')}`); - if (customStatus && customStatus.length) presenceInfo.push(`**Custom Status:** ${util.discord.escapeMarkdown(customStatus)}`); + presenceInfo.push(`**Activit${activitiesNames.length - 1 ? 'ies' : 'y'}:** ${oxford(activitiesNames, 'and', '')}`); + if (customStatus && customStatus.length) presenceInfo.push(`**Custom Status:** ${escapeMarkdown(customStatus)}`); embed.addFields([{ name: title, value: presenceInfo.join('\n') }]); enum statusEmojis { @@ -229,8 +246,8 @@ export default class UserInfoCommand extends BushCommand { perms.push('`Administrator`'); } else if (member?.permissions.toArray().length) { member.permissions.toArray().forEach((permission) => { - if (client.consts.mappings.permissions[permission]?.important) { - perms.push(`\`${client.consts.mappings.permissions[permission].name}\``); + if (mappings.permissions[permission]?.important) { + perms.push(`\`${mappings.permissions[permission].name}\``); } }); } @@ -247,14 +264,14 @@ export default class UserInfoCommand extends BushCommand { const flags = new ApplicationFlagsBitField(applicationInfo.flags); const intent = (check: ApplicationFlagsString, warn: ApplicationFlagsString) => { - if (flags.has(check)) return util.emojis.check; - if (flags.has(warn)) return util.emojis.warn; - return util.emojis.cross; + if (flags.has(check)) return emojis.check; + if (flags.has(warn)) return emojis.warn; + return emojis.cross; }; const botInfo = [ `**Publicity:** ${applicationInfo.bot_public ? 'Public' : 'Private'}`, - `**Requires Code Grant:** ${applicationInfo.bot_require_code_grant ? util.emojis.check : util.emojis.cross}`, + `**Requires Code Grant:** ${applicationInfo.bot_require_code_grant ? emojis.check : emojis.cross}`, `**Server Members Intent:** ${intent('GatewayGuildMembers', 'GatewayGuildMembersLimited')}`, `**Presence Intent:** ${intent('GatewayPresence', 'GatewayPresenceLimited')}`, `**Message Content Intent:** ${intent('GatewayMessageContent', 'GatewayMessageContentLimited')}` diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index 040ed4a..d81d88b 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -1,4 +1,15 @@ -import { BushCommand, ButtonPaginator, Level, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + BushCommand, + ButtonPaginator, + chunk, + clientSendAndPermCheck, + emojis, + Level, + prefix, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -23,7 +34,7 @@ export default class LeaderboardCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -33,9 +44,9 @@ export default class LeaderboardCommand extends BushCommand { if (!(await message.guild.hasFeature('leveling'))) return await message.util.reply( - `${util.emojis.error} This command can only be run in servers with the leveling feature enabled.${ + `${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 \`${util.prefix(message)}features\` command.` + ? ` You can toggle features using the \`${prefix(message)}features\` command.` : '' }` ); @@ -44,7 +55,7 @@ export default class LeaderboardCommand extends BushCommand { const mappedRanks = ranks.map( (val, index) => `\`${index + 1}\` <@${val.user}> - Level ${val.level} (${val.xp.toLocaleString()} xp)` ); - const chunked = util.chunk(mappedRanks, 25); + const chunked = chunk(mappedRanks, 25); const embeds = chunked.map((c) => new EmbedBuilder().setTitle(`${message.guild.name}'s Leaderboard`).setDescription(c.join('\n')) ); diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index 34a839a..df3e5b2 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -2,7 +2,10 @@ import { AllowedMentions, BushCommand, CanvasProgressBar, + clientSendAndPermCheck, + emojis, Level, + prefix, type CommandMessage, type OptArgType, type SlashMessage @@ -39,7 +42,7 @@ export default class LevelCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -49,9 +52,9 @@ export default class LevelCommand extends BushCommand { if (!(await message.guild.hasFeature('leveling'))) return await message.util.reply( - `${util.emojis.error} This command can only be run in servers with the leveling feature enabled.${ + `${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 \`${util.prefix(message)}features\` command.` + ? ` You can toggle features using the \`${prefix(message)}features\` command.` : '' }` ); @@ -63,7 +66,7 @@ export default class LevelCommand extends BushCommand { } catch (e) { if (e instanceof Error && e.message === 'User does not have a level') { return await message.util.reply({ - content: `${util.emojis.error} ${user} does not have a level.`, + content: `${emojis.error} ${user} does not have a level.`, allowedMentions: AllowedMentions.none() }); } else throw e; diff --git a/src/commands/leveling/levelRoles.ts b/src/commands/leveling/levelRoles.ts index afa4ab6..470cf58 100644 --- a/src/commands/leveling/levelRoles.ts +++ b/src/commands/leveling/levelRoles.ts @@ -1,4 +1,13 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + emojis, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -33,7 +42,7 @@ export default class LevelRolesCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), userPermissions: [PermissionFlagsBits.ManageGuild, PermissionFlagsBits.ManageRoles] }); } @@ -46,20 +55,20 @@ export default class LevelRolesCommand extends BushCommand { assert(message.member); if (!(await message.guild.hasFeature('leveling'))) { - return await reply(`${util.emojis.error} This command can only be run in servers with the leveling feature enabled.`); + return await reply(`${emojis.error} This command can only be run in servers with the leveling feature enabled.`); } - if (args.level < 1) return await reply(`${util.emojis.error} You cannot set a level role less than **1**.`); + if (args.level < 1) return await reply(`${emojis.error} You cannot set a level role less than **1**.`); if (args.role) { if (args.role.managed) - return await reply(`${util.emojis.error} You cannot set <@${args.role.id}> as a level role since it is managed.`); + return await reply(`${emojis.error} You cannot set <@${args.role.id}> as a level role since it is managed.`); else if (args.role.id === message.guild.id) - return await reply(`${util.emojis.error} You cannot set the @everyone role as a level role.`); + return await reply(`${emojis.error} You cannot set the @everyone role as a level role.`); else if (args.role.comparePositionTo(message.member.roles.highest) >= 0) - return await reply(`${util.emojis.error} <@${args.role.id}> is higher or equal to your highest role.`); + return await reply(`${emojis.error} <@${args.role.id}> is higher or equal to your highest role.`); else if (args.role.comparePositionTo(message.guild.members.me!.roles.highest) >= 0) - return await reply(`${util.emojis.error} <@${args.role.id}> is higher or equal to my highest role.`); + return await reply(`${emojis.error} <@${args.role.id}> is higher or equal to my highest role.`); } const oldRoles = Object.freeze(await message.guild.getSetting('levelRoles')); @@ -74,17 +83,17 @@ export default class LevelRolesCommand extends BushCommand { const success = await message.guild.setSetting('levelRoles', newRoles, message.member).catch(() => false); - if (!success) return await reply(`${util.emojis.error} An error occurred while setting the level roles.`); + if (!success) return await reply(`${emojis.error} An error occurred while setting the level roles.`); if (!oldRoles[args.level] && newRoles[args.level]) { - return await reply(`${util.emojis.success} The level role for **${args.level}** is now <@&${newRoles[args.level]}>.`); + return await reply(`${emojis.success} The level role for **${args.level}** is now <@&${newRoles[args.level]}>.`); } else if (oldRoles[args.level] && !newRoles[args.level]) { return await reply( - `${util.emojis.success} The level role for **${args.level}** was <@&${oldRoles[args.level]}> but is now disabled.` + `${emojis.success} The level role for **${args.level}** was <@&${oldRoles[args.level]}> but is now disabled.` ); } else if (oldRoles[args.level] && newRoles[args.level]) { return await reply( - `${util.emojis.success} The level role for **${args.level}** has been updated from <@&${oldRoles[args.level]}> to <@&${ + `${emojis.success} The level role for **${args.level}** has been updated from <@&${oldRoles[args.level]}> to <@&${ newRoles[args.level] }>.` ); diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts index 8abdb57..b1d9516 100644 --- a/src/commands/leveling/setLevel.ts +++ b/src/commands/leveling/setLevel.ts @@ -1,4 +1,14 @@ -import { AllowedMentions, BushCommand, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + emojis, + format, + Level, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -30,7 +40,7 @@ export default class SetLevelCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.Administrator] }); } @@ -43,9 +53,9 @@ export default class SetLevelCommand extends BushCommand { assert(user.id); if (isNaN(level) || !Number.isInteger(level)) - return await message.util.reply(`${util.emojis.error} Provide a valid number to set the user's level to.`); + return await message.util.reply(`${emojis.error} Provide a valid number to set the user's level to.`); if (level > 6553 || level < 0) - return await message.util.reply(`${util.emojis.error} You cannot set a level higher than **6,553**.`); + return await message.util.reply(`${emojis.error} You cannot set a level higher than **6,553**.`); const [levelEntry] = await Level.findOrBuild({ where: { user: user.id, guild: message.guild.id }, @@ -53,7 +63,7 @@ export default class SetLevelCommand extends BushCommand { }); await levelEntry.update({ xp: Level.convertLevelToXp(level), user: user.id, guild: message.guild.id }); return await message.util.send({ - content: `Successfully set level of <@${user.id}> to ${util.format.input(level.toLocaleString())} (${util.format.input( + content: `Successfully set level of <@${user.id}> to ${format.input(level.toLocaleString())} (${format.input( levelEntry.xp.toLocaleString() )} XP)`, allowedMentions: AllowedMentions.none() diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts index 60e0b94..96b3ad7 100644 --- a/src/commands/leveling/setXp.ts +++ b/src/commands/leveling/setXp.ts @@ -1,4 +1,14 @@ -import { AllowedMentions, BushCommand, Level, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + emojis, + format, + Level, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -31,7 +41,7 @@ export default class SetXpCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.Administrator] }); } @@ -43,10 +53,10 @@ export default class SetXpCommand extends BushCommand { assert(message.inGuild()); assert(user.id); - if (isNaN(xp)) return await message.util.reply(`${util.emojis.error} Provide a valid number.`); + if (isNaN(xp)) return await message.util.reply(`${emojis.error} Provide a valid number.`); if (xp > 2147483647 || xp < 0) return await message.util.reply( - `${util.emojis.error} Provide an positive integer under **2,147,483,647** to set the user's xp to.` + `${emojis.error} Provide an positive integer under **2,147,483,647** to set the user's xp to.` ); const [levelEntry] = await Level.findOrBuild({ @@ -57,9 +67,9 @@ export default class SetXpCommand extends BushCommand { await levelEntry.update({ xp: xp, user: user.id, guild: message.guild.id }); return await message.util.send({ - content: `Successfully set <@${user.id}>'s xp to ${util.format.input( - levelEntry.xp.toLocaleString() - )} (level ${util.format.input(Level.convertXpToLevel(levelEntry.xp).toLocaleString())}).`, + content: `Successfully set <@${user.id}>'s xp to ${format.input(levelEntry.xp.toLocaleString())} (level ${format.input( + Level.convertXpToLevel(levelEntry.xp).toLocaleString() + )}).`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/moderation/_activePunishments.ts b/src/commands/moderation/_activePunishments.ts index 92b4242..80cecf2 100644 --- a/src/commands/moderation/_activePunishments.ts +++ b/src/commands/moderation/_activePunishments.ts @@ -37,8 +37,8 @@ // slash: true, // channel: 'guild', // hidden: true, -// clientPermissions: (m) => util.clientSendAndPermCheck(m), -// userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) +// clientPermissions: (m) => clientSendAndPermCheck(m), +// userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) // }); // } @@ -66,7 +66,7 @@ // where.type = { [Op.or]: ['PERM_PUNISHMENT_ROLE', 'TEMP_PUNISHMENT_ROLE', 'REMOVE_PUNISHMENT_ROLE'] }; // break; // default: -// return message.util.reply(`${util.emojis.error} You supplied an invalid case type to filter by.`); +// return message.util.reply(`${emojis.error} You supplied an invalid case type to filter by.`); // } // } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index f4bd52b..e301fb2 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,8 +1,13 @@ import { AllowedMentions, + Arg, banResponse, BushCommand, + castDurationContent, + emojis, + format, Moderation, + resolveNonCachedUser, type ArgType, type CommandMessage, type OptArgType, @@ -23,7 +28,7 @@ export default class BanCommand extends BushCommand { { id: 'user', description: 'The user that will be banned.', - type: util.arg.union('user', 'snowflake'), + type: Arg.union('user', 'snowflake'), readableType: 'user|snowflake', prompt: 'What user would you like to ban?', retry: '{error} Choose a valid user to ban.', @@ -46,7 +51,7 @@ export default class BanCommand extends BushCommand { match: 'option', prompt: "How many days of the user's messages would you like to delete?", retry: '{error} Choose between 0 and 7 days to delete messages from the user for.', - type: util.arg.range('integer', 0, 7, true), + type: Arg.range('integer', 0, 7, true), readableType: 'integer [0, 7]', optional: true, slashType: ApplicationCommandOptionType.Integer, @@ -82,12 +87,12 @@ export default class BanCommand extends BushCommand { assert(message.inGuild()); assert(message.member); - const { duration, content } = await util.castDurationContent(args.reason_and_duration, message); + const { duration, content } = await castDurationContent(args.reason_and_duration, message); 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 util.resolveNonCachedUser(typeof args.user === 'string' ? args.user : args.user.id)); - if (!user) return message.util.reply(`${util.emojis.error} Invalid user.`); + const user = member?.user ?? (await 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(); const canModerateResponse = member ? await Moderation.permissionCheck(message.member, member, 'ban', true, useForce) : true; @@ -97,7 +102,7 @@ export default class BanCommand extends BushCommand { } if (!Number.isInteger(args.days) || args.days! < 0 || args.days! > 7) { - return message.util.reply(`${util.emojis.error} The delete days must be an integer between 0 and 7.`); + return message.util.reply(`${emojis.error} The delete days must be an integer between 0 and 7.`); } const opts = { reason: content, moderator: message.member, duration: duration, deleteDays: args.days }; @@ -105,24 +110,24 @@ export default class BanCommand extends BushCommand { const responseCode = member ? await member.bushBan(opts) : await message.guild.bushBan({ user, ...opts }); const responseMessage = (): string => { - const victim = util.format.input(user.tag); + const victim = format.input(user.tag); switch (responseCode) { case banResponse.ALREADY_BANNED: - return `${util.emojis.error} ${victim} is already banned.`; + return `${emojis.error} ${victim} is already banned.`; case banResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`; + return `${emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`; case banResponse.ACTION_ERROR: - return `${util.emojis.error} An error occurred while trying to ban ${victim}.`; + return `${emojis.error} An error occurred while trying to ban ${victim}.`; case banResponse.PUNISHMENT_ENTRY_ADD_ERROR: - return `${util.emojis.error} While banning ${victim}, there was an error creating a ban entry, please report this to my developers.`; + return `${emojis.error} While banning ${victim}, there was an error creating a ban entry, please report this to my developers.`; case banResponse.MODLOG_ERROR: - return `${util.emojis.error} While banning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} While banning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; case banResponse.DM_ERROR: - return `${util.emojis.warn} Banned ${victim} however I could not send them a dm.`; + return `${emojis.warn} Banned ${victim} however I could not send them a dm.`; case banResponse.SUCCESS: - return `${util.emojis.success} Successfully banned ${victim}.`; + return `${emojis.success} Successfully banned ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/block.ts b/src/commands/moderation/block.ts index 16beb6f..fc93fb1 100644 --- a/src/commands/moderation/block.ts +++ b/src/commands/moderation/block.ts @@ -2,7 +2,12 @@ import { AllowedMentions, blockResponse, BushCommand, + castDurationContent, + clientSendAndPermCheck, + emojis, + format, Moderation, + userGuildPermCheck, type ArgType, type CommandMessage, type OptArgType, @@ -51,8 +56,8 @@ export default class BlockCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]), lock: 'channel' }); } @@ -70,13 +75,13 @@ export default class BlockCommand extends BushCommand { assert(message.channel); if (!message.channel.isTextBased()) - return message.util.send(`${util.emojis.error} This command can only be used in text based channels.`); + return message.util.send(`${emojis.error} This command can only be used in text based channels.`); - const { duration, content } = await util.castDurationContent(args.reason_and_duration, message); + const { duration, content } = await castDurationContent(args.reason_and_duration, message); const member = await message.guild.members.fetch(args.user.id).catch(() => null); if (!member) - return await message.util.reply(`${util.emojis.error} The user you selected is not in the server or is not a valid user.`); + return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'block', true, useForce); @@ -93,24 +98,24 @@ export default class BlockCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(member.user.tag); + const victim = format.input(member.user.tag); switch (responseCode) { case blockResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not block ${victim} because I am missing the **Manage Channel** permission.`; + return `${emojis.error} Could not block ${victim} because I am missing the **Manage Channel** permission.`; case blockResponse.INVALID_CHANNEL: - return `${util.emojis.error} Could not block ${victim}, you can only block users in text or thread channels.`; + return `${emojis.error} Could not block ${victim}, you can only block users in text or thread channels.`; case blockResponse.ACTION_ERROR: - return `${util.emojis.error} An unknown error occurred while trying to block ${victim}.`; + return `${emojis.error} An unknown error occurred while trying to block ${victim}.`; case blockResponse.MODLOG_ERROR: - return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case blockResponse.PUNISHMENT_ENTRY_ADD_ERROR: - return `${util.emojis.error} There was an error creating a punishment entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a punishment entry, please report this to my developers.`; case blockResponse.DM_ERROR: - return `${util.emojis.warn} Blocked ${victim} however I could not send them a dm.`; + return `${emojis.warn} Blocked ${victim} however I could not send them a dm.`; case blockResponse.SUCCESS: - return `${util.emojis.success} Successfully blocked ${victim}.`; + return `${emojis.success} Successfully blocked ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index f1fac0d..3c9e726 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -1,4 +1,16 @@ -import { BushCommand, ModLog, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + format, + ModLog, + OptArgType, + regex, + userGuildPermCheck, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, PermissionFlagsBits, type Message } from 'discord.js'; @@ -42,8 +54,8 @@ export default class EvidenceCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientPermissions: (m) => clientSendAndPermCheck(m), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -89,18 +101,18 @@ export default class EvidenceCommand extends BushCommand { assert(message.inGuild()); if (message.interaction && !caseID && !user) - return message.util.send(`${util.emojis.error} You must provide either a user or a case ID.`); + return message.util.send(`${emojis.error} You must provide either a user or a case ID.`); const entry = messageCommandTarget - ? util.consts.regex.snowflake.test(messageCommandTarget) + ? regex.snowflake.test(messageCommandTarget) ? await ModLog.findOne({ where: { user: messageCommandTarget }, order: [['createdAt', 'DESC']] }) : await ModLog.findByPk(messageCommandTarget) : caseID ? await ModLog.findByPk(caseID) : await ModLog.findOne({ where: { user: user!.id }, order: [['createdAt', 'DESC']] }); - if (!entry || entry.pseudo) return message.util.send(`${util.emojis.error} Invalid modlog entry.`); - if (entry.guild !== message.guild.id) return message.util.reply(`${util.emojis.error} This modlog is from another server.`); + if (!entry || entry.pseudo) return message.util.send(`${emojis.error} Invalid modlog entry.`); + if (entry.guild !== message.guild.id) return message.util.reply(`${emojis.error} This modlog is from another server.`); const oldEntry = entry.evidence; @@ -112,14 +124,12 @@ export default class EvidenceCommand extends BushCommand { client.emit('bushUpdateModlog', message.member!, entry.id, 'evidence', oldEntry, entry.evidence); - return message.util.reply( - `${util.emojis.success} Successfully updated the evidence for case ${util.format.input(entry.id)}.` - ); + return message.util.reply(`${emojis.success} Successfully updated the evidence for case ${format.input(entry.id)}.`); } public static getEvidence(message: CommandMessage | SlashMessage, evidenceArg: OptArgType<'string'>): null | string { if (evidenceArg && (message as Message).attachments?.size) { - void message.util.reply(`${util.emojis.error} Please either attach an image or a reason not both.`); + void message.util.reply(`${emojis.error} Please either attach an image or a reason not both.`); return null; } @@ -129,7 +139,7 @@ export default class EvidenceCommand extends BushCommand { ? (message as Message).attachments.first()?.url : undefined; if (!_evidence) { - void message.util.reply(`${util.emojis.error} You must provide evidence for this modlog.`); + void message.util.reply(`${emojis.error} You must provide evidence for this modlog.`); return null; } diff --git a/src/commands/moderation/hideCase.ts b/src/commands/moderation/hideCase.ts index fc5baf4..0cc3de4 100644 --- a/src/commands/moderation/hideCase.ts +++ b/src/commands/moderation/hideCase.ts @@ -1,4 +1,13 @@ -import { BushCommand, ModLog, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + format, + ModLog, + userGuildPermCheck, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -21,8 +30,8 @@ export default class HideCaseCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]), + clientPermissions: (m) => clientSendAndPermCheck(m), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]), channel: 'guild' }); } @@ -31,8 +40,8 @@ export default class HideCaseCommand extends BushCommand { assert(message.inGuild()); const entry = await ModLog.findByPk(caseID); - if (!entry || entry.pseudo) return message.util.send(`${util.emojis.error} Invalid entry.`); - if (entry.guild !== message.guild.id) return message.util.reply(`${util.emojis.error} This modlog is from another server.`); + if (!entry || entry.pseudo) return message.util.send(`${emojis.error} Invalid entry.`); + if (entry.guild !== message.guild.id) return message.util.reply(`${emojis.error} This modlog is from another server.`); const action = entry.hidden ? 'no longer hidden' : 'now hidden'; const oldEntry = entry.hidden; entry.hidden = !entry.hidden; @@ -40,6 +49,6 @@ export default class HideCaseCommand extends BushCommand { client.emit('bushUpdateModlog', message.member!, entry.id, 'hidden', oldEntry, entry.hidden); - return await message.util.reply(`${util.emojis.success} CaseID ${util.format.input(caseID)} is ${action}.`); + return await message.util.reply(`${emojis.success} CaseID ${format.input(caseID)} is ${action}.`); } } diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index a9a0336..bf079f3 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -1,6 +1,9 @@ import { AllowedMentions, BushCommand, + clientSendAndPermCheck, + emojis, + format, kickResponse, Moderation, type ArgType, @@ -50,7 +53,7 @@ export default class KickCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.KickMembers]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.KickMembers]), userPermissions: [PermissionFlagsBits.KickMembers] }); } @@ -65,7 +68,7 @@ export default class KickCommand extends BushCommand { const member = await message.guild.members.fetch(user.id); if (!member) - return await message.util.reply(`${util.emojis.error} The user you selected is not in the server or is not a valid user.`); + return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'kick', true, useForce); @@ -79,20 +82,20 @@ export default class KickCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(member.user.tag); + const victim = format.input(member.user.tag); switch (responseCode) { case kickResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not kick ${victim} because I am missing the **Kick Members** permission.`; + return `${emojis.error} Could not kick ${victim} because I am missing the **Kick Members** permission.`; case kickResponse.ACTION_ERROR: - return `${util.emojis.error} An error occurred while trying to kick ${victim}.`; + return `${emojis.error} An error occurred while trying to kick ${victim}.`; case kickResponse.MODLOG_ERROR: - return `${util.emojis.error} While muting ${victim}, there was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} While muting ${victim}, there was an error creating a modlog entry, please report this to my developers.`; case kickResponse.DM_ERROR: - return `${util.emojis.warn} Kicked ${victim} however I could not send them a dm.`; + return `${emojis.warn} Kicked ${victim} however I could not send them a dm.`; case kickResponse.SUCCESS: - return `${util.emojis.success} Successfully kicked ${victim}.`; + return `${emojis.success} Successfully kicked ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/lockdown.ts b/src/commands/moderation/lockdown.ts index 7d8211c..76d61d0 100644 --- a/src/commands/moderation/lockdown.ts +++ b/src/commands/moderation/lockdown.ts @@ -1,7 +1,12 @@ import { AllowedMentions, + Arg, BushCommand, + clientSendAndPermCheck, + colors, ConfirmationPrompt, + emojis, + format, type ArgType, type CommandMessage, type OptArgType, @@ -31,7 +36,7 @@ export default class LockdownCommand extends BushCommand { { id: 'channel', description: 'Specify a different channel to lockdown instead of the one you trigger the command in.', - type: util.arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), + type: Arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), prompt: 'What channel would you like to lockdown?', slashType: ApplicationCommandOptionType.Channel, channelTypes: [ @@ -65,7 +70,7 @@ export default class LockdownCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), userPermissions: [PermissionFlagsBits.ManageChannels], lock: 'channel' }); @@ -95,7 +100,7 @@ export default class LockdownCommand extends BushCommand { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); if (args.channel && args.all) - return await message.util.reply(`${util.emojis.error} You can't specify a channel and set all to true at the same time.`); + return await message.util.reply(`${emojis.error} You can't specify a channel and set all to true at the same time.`); const channel = args.channel ?? message.channel; @@ -108,14 +113,14 @@ export default class LockdownCommand extends BushCommand { ) ) return await message.util.reply( - `${util.emojis.error} You can only ${action} text channels, news channels, and thread channels.` + `${emojis.error} You can only ${action} text channels, news channels, and thread channels.` ); if (args.all) { const confirmation = await ConfirmationPrompt.send(message, { content: `Are you sure you want to ${action} all channels?` }); - if (!confirmation) return message.util.sendNew(`${util.emojis.error} Lockdown cancelled.`); + if (!confirmation) return message.util.sendNew(`${emojis.error} Lockdown cancelled.`); } const response = await message.guild.lockdown({ @@ -128,33 +133,33 @@ export default class LockdownCommand extends BushCommand { if (response instanceof Collection) { return await message.util.sendNew({ - content: `${util.emojis.error} The following channels failed to ${action}:`, + content: `${emojis.error} The following channels failed to ${action}:`, embeds: [ { description: response.map((e, c) => `<#${c}> : ${e.message}`).join('\n'), - color: util.colors.warn + color: colors.warn } ] }); } else { let messageResponse; if (response === 'all not chosen and no channel specified') { - messageResponse = `${util.emojis.error} You must specify a channel to ${action}.`; + messageResponse = `${emojis.error} You must specify a channel to ${action}.`; } else if (response.startsWith('invalid channel configured: ')) { const channels = response.replace('invalid channel configured: ', ''); const actionFormatted = `${action.replace('down', '')}ed`; - messageResponse = `${util.emojis.error} Some of the channels configured to be ${actionFormatted} cannot be resolved: ${channels}}`; + messageResponse = `${emojis.error} Some of the channels configured to be ${actionFormatted} cannot be resolved: ${channels}}`; } else if (response === 'no channels configured') { - messageResponse = `${util.emojis.error} The all option is selected but there are no channels configured to be locked down.`; + messageResponse = `${emojis.error} The all option is selected but there are no channels configured to be locked down.`; } else if (response === 'moderator not found') { - messageResponse = `${util.emojis.error} For some reason I could not resolve you?`; + messageResponse = `${emojis.error} For some reason I could not resolve you?`; } else if (response.startsWith('success: ')) { const num = Number.parseInt(response.replace('success: ', '')); - messageResponse = `${util.emojis.success} Successfully ${ + messageResponse = `${emojis.success} Successfully ${ action === 'lockdown' ? 'locked down' : 'unlocked' } **${num}** channel${num > 0 ? 's' : ''}.`; } else { - return `${util.emojis.error} An error occurred: ${util.format.input(response)}}`; + return `${emojis.error} An error occurred: ${format.input(response)}}`; } assert(messageResponse); diff --git a/src/commands/moderation/massBan.ts b/src/commands/moderation/massBan.ts index a25c64f..ab9fbc8 100644 --- a/src/commands/moderation/massBan.ts +++ b/src/commands/moderation/massBan.ts @@ -1,7 +1,13 @@ import { + Arg, BanResponse, banResponse, BushCommand, + clientSendAndPermCheck, + colors, + emojis, + overflowEmbed, + regex, type ArgType, type CommandMessage, type OptArgType, @@ -45,7 +51,7 @@ export default class MassBanCommand extends BushCommand { match: 'option', prompt: "How many days of the user's messages would you like to delete?", retry: '{error} Choose between 0 and 7 days to delete messages from the user for.', - type: util.arg.range('integer', 0, 7, true), + type: Arg.range('integer', 0, 7, true), optional: true, slashType: ApplicationCommandOptionType.Integer, choices: [...Array(8).keys()].map((v) => ({ name: v.toString(), value: v })) @@ -54,7 +60,7 @@ export default class MassBanCommand extends BushCommand { quoted: true, slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.BanMembers], lock: 'user' }); @@ -69,14 +75,13 @@ export default class MassBanCommand extends BushCommand { args.days ??= message.util.parsed?.alias?.includes('dban') ? 1 : 0; const ids = args.users.split(/\n| /).filter((id) => id.length > 0); - if (ids.length === 0) return message.util.send(`${util.emojis.error} You must provide at least one user id.`); + if (ids.length === 0) return message.util.send(`${emojis.error} You must provide at least one user id.`); for (const id of ids) { - if (!client.constants.regex.snowflake.test(id)) - return message.util.send(`${util.emojis.error} ${id} is not a valid snowflake.`); + if (!regex.snowflake.test(id)) return message.util.send(`${emojis.error} ${id} is not a valid snowflake.`); } if (!Number.isInteger(args.days) || args.days! < 0 || args.days! > 7) { - return message.util.reply(`${util.emojis.error} The delete days must be an integer between 0 and 7.`); + return message.util.reply(`${emojis.error} The delete days must be an integer between 0 and 7.`); } const promises = ids.map((id) => @@ -99,13 +104,13 @@ export default class MassBanCommand extends BushCommand { const id = ids[i]; const status = res[i]; const isSuccess = success(status); - const emoji = isSuccess ? util.emojis.success : util.emojis.error; + const emoji = isSuccess ? emojis.success : emojis.error; return `${emoji} ${id}${isSuccess ? '' : ` - ${status}`}`; }); - const embeds = util.overflowEmbed( + const embeds = overflowEmbed( { - color: util.colors.DarkRed, + color: colors.DarkRed, title: 'Mass Ban Results' }, lines diff --git a/src/commands/moderation/massEvidence.ts b/src/commands/moderation/massEvidence.ts index 468d43c..ffe85d2 100644 --- a/src/commands/moderation/massEvidence.ts +++ b/src/commands/moderation/massEvidence.ts @@ -1,4 +1,16 @@ -import { BushCommand, ModLog, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + ModLog, + overflowEmbed, + regex, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; import { EvidenceCommand } from '../index.js'; @@ -37,7 +49,7 @@ export default class MassEvidenceCommand extends BushCommand { quoted: true, slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [PermissionFlagsBits.ManageMessages], lock: 'user' }); @@ -53,10 +65,9 @@ export default class MassEvidenceCommand extends BushCommand { if (!evidence) return; const ids = args.users.split(/\n| /).filter((id) => id.length > 0); - if (ids.length === 0) return message.util.send(`${util.emojis.error} You must provide at least one user id.`); + if (ids.length === 0) return message.util.send(`${emojis.error} You must provide at least one user id.`); for (const id of ids) { - if (!client.constants.regex.snowflake.test(id)) - return message.util.send(`${util.emojis.error} ${id} is not a valid snowflake.`); + if (!regex.snowflake.test(id)) return message.util.send(`${emojis.error} ${id} is not a valid snowflake.`); } const caseMap = ( @@ -78,15 +89,15 @@ export default class MassEvidenceCommand extends BushCommand { const lines = ids.map((id, i) => { const case_ = res[i]; - if (!case_) return `${util.emojis.error} ${id} - no case found.`; - return `${util.emojis.success} ${id} - ${case_.id}`; + if (!case_) return `${emojis.error} ${id} - no case found.`; + return `${emojis.success} ${id} - ${case_.id}`; }); client.emit('massEvidence', message.member!, message.guild, evidence, lines); - const embeds = util.overflowEmbed( + const embeds = overflowEmbed( { - color: util.colors.DarkRed, + color: colors.DarkRed, title: 'Mass Evidence' }, lines diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 71ed975..2c0e33a 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -1,6 +1,22 @@ -import { BushCommand, ButtonPaginator, ModLog, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + ButtonPaginator, + chunk, + clientSendAndPermCheck, + colors, + emojis, + humanizeDuration, + ModLog, + resolveUserAsync, + timestamp, + userGuildPermCheck, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; -import { ApplicationCommandOptionType, PermissionFlagsBits, User } from 'discord.js'; +import { ApplicationCommandOptionType, escapeMarkdown, PermissionFlagsBits, User } from 'discord.js'; export default class ModlogCommand extends BushCommand { public static separator = '\n━━━━━━━━━━━━━━━\n'; @@ -16,7 +32,7 @@ export default class ModlogCommand extends BushCommand { { id: 'search', description: 'The case id or user to search for modlogs by.', - type: util.arg.union('user', 'string'), + type: Arg.union('user', 'string'), prompt: 'What case id or user would you like to see?', retry: '{error} Choose a valid case id or user.', slashType: ApplicationCommandOptionType.String @@ -34,8 +50,8 @@ export default class ModlogCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientPermissions: (m) => clientSendAndPermCheck(m), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -45,7 +61,7 @@ export default class ModlogCommand extends BushCommand { ) { assert(message.inGuild()); - const foundUser = search instanceof User ? search : await util.resolveUserAsync(search); + const foundUser = search instanceof User ? search : await resolveUserAsync(search); if (foundUser) { const logs = await ModLog.findAll({ where: { @@ -59,23 +75,23 @@ export default class ModlogCommand extends BushCommand { .filter((log) => !(log.hidden && hidden)) .map((log) => ModlogCommand.generateModlogInfo(log, false)); if (!logs.length || !niceLogs.length) - return message.util.reply(`${util.emojis.error} **${foundUser.tag}** does not have any modlogs.`); - const chunked: string[][] = util.chunk(niceLogs, 4); + return message.util.reply(`${emojis.error} **${foundUser.tag}** does not have any modlogs.`); + const chunked: string[][] = chunk(niceLogs, 4); const embedPages = chunked.map((chunk) => ({ title: `${foundUser.tag}'s Mod Logs`, description: chunk.join(ModlogCommand.separator), - color: util.colors.default + color: colors.default })); return await ButtonPaginator.send(message, embedPages, undefined, true); } else if (search) { const entry = await ModLog.findByPk(search as string); if (!entry || entry.pseudo || (entry.hidden && !hidden)) - return message.util.send(`${util.emojis.error} That modlog does not exist.`); - if (entry.guild !== message.guild.id) return message.util.reply(`${util.emojis.error} This modlog is from another server.`); + return message.util.send(`${emojis.error} That modlog does not exist.`); + if (entry.guild !== message.guild.id) return message.util.reply(`${emojis.error} This modlog is from another server.`); const embed = { title: `Case ${entry.id}`, description: ModlogCommand.generateModlogInfo(entry, true), - color: util.colors.default + color: colors.default }; return await ButtonPaginator.send(message, [embed]); } @@ -83,12 +99,12 @@ export default class ModlogCommand extends BushCommand { public static generateModlogInfo(log: ModLog, showUser: boolean): string { const trim = (str: string): string => (str.endsWith('\n') ? str.substring(0, str.length - 1).trim() : str.trim()); - const modLog = [`**Case ID:** ${util.discord.escapeMarkdown(log.id)}`, `**Type:** ${log.type.toLowerCase()}`]; + const modLog = [`**Case ID:** ${escapeMarkdown(log.id)}`, `**Type:** ${log.type.toLowerCase()}`]; if (showUser) modLog.push(`**User:** <@!${log.user}>`); modLog.push(`**Moderator:** <@!${log.moderator}>`); - if (log.duration) modLog.push(`**Duration:** ${util.humanizeDuration(log.duration)}`); + if (log.duration) modLog.push(`**Duration:** ${humanizeDuration(log.duration)}`); modLog.push(`**Reason:** ${trim(log.reason ?? 'No Reason Specified.')}`); - modLog.push(`**Date:** ${util.timestamp(log.createdAt)}`); + modLog.push(`**Date:** ${timestamp(log.createdAt)}`); if (log.evidence) modLog.push(`**Evidence:** ${trim(log.evidence)}`); return modLog.join(`\n`); } diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index a665670..7ad023a 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -1,8 +1,14 @@ import { AllowedMentions, BushCommand, + castDurationContent, + clientSendAndPermCheck, + emojis, + format, Moderation, muteResponse, + prefix, + userGuildPermCheck, type ArgType, type CommandMessage, type OptArgType, @@ -51,8 +57,8 @@ export default class MuteCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -67,11 +73,11 @@ export default class MuteCommand extends BushCommand { assert(message.inGuild()); assert(message.member); - const { duration, content } = await util.castDurationContent(args.reason_and_duration, message); + const { duration, content } = await castDurationContent(args.reason_and_duration, message); const member = await message.guild.members.fetch(args.user.id).catch(() => null); if (!member) - return await message.util.reply(`${util.emojis.error} The user you selected is not in the server or is not a valid user.`); + return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'mute', true, useForce); @@ -87,29 +93,29 @@ export default class MuteCommand extends BushCommand { }); const responseMessage = (): string => { - const prefix = util.prefix(message); - const victim = util.format.input(member.user.tag); + const prefix_ = prefix(message); + const victim = format.input(member.user.tag); switch (responseCode) { case muteResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not mute ${victim} because I am missing the **Manage Roles** permission.`; + return `${emojis.error} Could not mute ${victim} because I am missing the **Manage Roles** permission.`; case muteResponse.NO_MUTE_ROLE: - return `${util.emojis.error} Could not mute ${victim}, you must set a mute role with \`${prefix}config muteRole\`.`; + return `${emojis.error} Could not mute ${victim}, you must set a mute role with \`${prefix_}config muteRole\`.`; case muteResponse.MUTE_ROLE_INVALID: - return `${util.emojis.error} Could not mute ${victim} because the current mute role no longer exists. Please set a new mute role with \`${prefix}config muteRole\`.`; + return `${emojis.error} Could not mute ${victim} because the current mute role no longer exists. Please set a new mute role with \`${prefix_}config muteRole\`.`; case muteResponse.MUTE_ROLE_NOT_MANAGEABLE: - return `${util.emojis.error} Could not mute ${victim} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${prefix}config muteRole\`.`; + return `${emojis.error} Could not mute ${victim} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${prefix_}config muteRole\`.`; case muteResponse.ACTION_ERROR: - return `${util.emojis.error} Could not mute ${victim}, there was an error assigning them the mute role.`; + return `${emojis.error} Could not mute ${victim}, there was an error assigning them the mute role.`; case muteResponse.MODLOG_ERROR: - return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case muteResponse.PUNISHMENT_ENTRY_ADD_ERROR: - return `${util.emojis.error} There was an error creating a punishment entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a punishment entry, please report this to my developers.`; case muteResponse.DM_ERROR: - return `${util.emojis.warn} Muted ${victim} however I could not send them a dm.`; + return `${emojis.warn} Muted ${victim} however I could not send them a dm.`; case muteResponse.SUCCESS: - return `${util.emojis.success} Successfully muted ${victim}.`; + return `${emojis.success} Successfully muted ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/purge.ts b/src/commands/moderation/purge.ts index 1652f6f..acf3897 100644 --- a/src/commands/moderation/purge.ts +++ b/src/commands/moderation/purge.ts @@ -1,4 +1,13 @@ -import { BushCommand, OptArgType, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + OptArgType, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, Collection, PermissionFlagsBits, type Message } from 'discord.js'; @@ -14,7 +23,7 @@ export default class PurgeCommand extends BushCommand { { id: 'amount', description: 'The amount of messages to purge.', - type: util.arg.range('integer', 1, 100, true), + type: Arg.range('integer', 1, 100, true), readableType: 'integer', prompt: 'How many messages would you like to purge?', retry: '{error} Please pick a number between 1 and 100.', @@ -43,7 +52,7 @@ export default class PurgeCommand extends BushCommand { ], slash: true, clientPermissions: (m) => - util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.EmbedLinks], true), + clientSendAndPermCheck(m, [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.EmbedLinks], true), userPermissions: [PermissionFlagsBits.ManageMessages], channel: 'guild' }); @@ -55,7 +64,7 @@ export default class PurgeCommand extends BushCommand { ) { assert(message.inGuild()); - if (args.amount > 100 || args.amount < 1) return message.util.reply(`${util.emojis.error} `); + if (args.amount > 100 || args.amount < 1) return message.util.reply(`${emojis.error} `); const messageFilter = (filterMessage: Message): boolean => { const shouldFilter: boolean[] = []; @@ -72,13 +81,13 @@ export default class PurgeCommand extends BushCommand { ); const purged = await message.channel!.bulkDelete(messages, true).catch(() => null); - if (!purged) return message.util.reply(`${util.emojis.error} Failed to purge messages.`).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); - await message.util.send(`${util.emojis.success} Successfully purged **${purged.size}** messages.`); + await message.util.send(`${emojis.success} Successfully purged **${purged.size}** messages.`); /* .then(async (purgeMessage) => { if (!message.util.isSlashMessage(message)) { - await util.sleep(5); + await sleep(5); await purgeMessage.delete().catch(() => {}); } }); */ diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts index 4359c5b..a088287 100644 --- a/src/commands/moderation/removeReactionEmoji.ts +++ b/src/commands/moderation/removeReactionEmoji.ts @@ -1,4 +1,13 @@ -import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + format, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, Message, PermissionFlagsBits } from 'discord.js'; @@ -14,7 +23,7 @@ export default class RemoveReactionEmojiCommand extends BushCommand { { id: 'message', description: 'The message to remove all the reactions of a certain emoji from.', - type: util.arg.union('message', 'messageLink'), + type: Arg.union('message', 'messageLink'), readableType: 'message|messageLink', prompt: 'What message would you like to remove a reaction from?', retry: '{error} Please pick a valid message.', @@ -23,7 +32,7 @@ export default class RemoveReactionEmojiCommand extends BushCommand { { id: 'emoji', description: 'The emoji to remove all the reactions of from a message.', - type: util.arg.union('emoji', 'snowflake'), + type: Arg.union('emoji', 'snowflake'), readableType: 'emoji|snowflake', match: 'restContent', prompt: 'What emoji would you like to remove?', @@ -34,7 +43,7 @@ export default class RemoveReactionEmojiCommand extends BushCommand { slash: true, channel: 'guild', clientPermissions: (m) => - util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.EmbedLinks], true), + clientSendAndPermCheck(m, [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.EmbedLinks], true), userPermissions: [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.ManageEmojisAndStickers] // Can't undo the removal of 1000s of reactions }); } @@ -54,15 +63,15 @@ export default class RemoveReactionEmojiCommand extends BushCommand { if (success) { return await message.util.reply( - `${util.emojis.success} Removed all reactions of ${util.format.input( - emojiID! - )} from the message with the id of ${util.format.input(resolvedMessage.id)}.` + `${emojis.success} Removed all reactions of ${format.input(emojiID!)} from the message with the id of ${format.input( + resolvedMessage.id + )}.` ); } else { return await message.util.reply( - `${util.emojis.error} There was an error removing all reactions of ${util.format.input( + `${emojis.error} There was an error removing all reactions of ${format.input( emojiID! - )} from the message with the id of ${util.format.input(resolvedMessage.id)}.` + )} from the message with the id of ${format.input(resolvedMessage.id)}.` ); } } diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index b9e1490..6febaa6 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -2,6 +2,11 @@ import { addRoleResponse, AllowedMentions, BushCommand, + clientSendAndPermCheck, + emojis, + format, + humanizeDuration, + mappings, removeRoleResponse, type ArgType, type CommandMessage, @@ -63,7 +68,7 @@ export default class RoleCommand extends BushCommand { flags: ['--force'], typing: true, clientPermissions: (m) => - util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles, PermissionFlagsBits.EmbedLinks], true), + clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles, PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -130,14 +135,13 @@ export default class RoleCommand extends BushCommand { } ) { assert(message.inGuild()); - if (!args.role) return await message.util.reply(`${util.emojis.error} You must specify a role.`); + if (!args.role) return await message.util.reply(`${emojis.error} You must specify a role.`); args.duration ??= 0; if ( !message.member!.permissions.has(PermissionFlagsBits.ManageRoles) && message.member!.id !== message.guild?.ownerId && !message.member!.user.isOwner() ) { - const mappings = client.consts.mappings; let mappedRole: { name: string; id: string }; for (let i = 0; i < mappings.roleMap.length; i++) { const a = mappings.roleMap[i]; @@ -145,7 +149,7 @@ export default class RoleCommand extends BushCommand { } if (!mappedRole! || !(mappedRole.name in mappings.roleWhitelist)) { return await message.util.reply({ - content: `${util.emojis.error} <@&${args.role.id}> is not whitelisted, and you do not have manage roles permission.`, + content: `${emojis.error} <@&${args.role.id}> is not whitelisted, and you do not have manage roles permission.`, allowedMentions: AllowedMentions.none() }); } @@ -157,7 +161,7 @@ export default class RoleCommand extends BushCommand { }); if (!message.member!.roles.cache.some((role) => (allowedRoles as Snowflake[]).includes(role.id))) { return await message.util.reply({ - content: `${util.emojis.error} <@&${args.role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, + content: `${emojis.error} <@&${args.role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, allowedMentions: AllowedMentions.none() }); } @@ -173,33 +177,33 @@ export default class RoleCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(args.member.user.tag); + const victim = format.input(args.member.user.tag); switch (responseCode) { case addRoleResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} I don't have the **Manage Roles** permission.`; + return `${emojis.error} I don't have the **Manage Roles** permission.`; case addRoleResponse.USER_HIERARCHY: - return `${util.emojis.error} <@&${args.role.id}> is higher or equal to your highest role.`; + return `${emojis.error} <@&${args.role.id}> is higher or equal to your highest role.`; case addRoleResponse.ROLE_MANAGED: - return `${util.emojis.error} <@&${args.role.id}> is managed by an integration and cannot be managed.`; + return `${emojis.error} <@&${args.role.id}> is managed by an integration and cannot be managed.`; case addRoleResponse.CLIENT_HIERARCHY: - return `${util.emojis.error} <@&${args.role.id}> is higher or equal to my highest role.`; + return `${emojis.error} <@&${args.role.id}> is higher or equal to my highest role.`; case addRoleResponse.MODLOG_ERROR: - return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR: case removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: - return `${util.emojis.error} There was an error ${ + return `${emojis.error} There was an error ${ args.action === 'add' ? 'creating' : 'removing' } a punishment entry, please report this to my developers.`; case addRoleResponse.ACTION_ERROR: - return `${util.emojis.error} An error occurred while trying to ${args.action} <@&${args.role.id}> ${ + return `${emojis.error} An error occurred while trying to ${args.action} <@&${args.role.id}> ${ args.action === 'add' ? 'to' : 'from' } ${victim}.`; case addRoleResponse.SUCCESS: - return `${util.emojis.success} Successfully ${args.action === 'add' ? 'added' : 'removed'} <@&${args.role.id}> ${ + return `${emojis.success} Successfully ${args.action === 'add' ? 'added' : 'removed'} <@&${args.role.id}> ${ args.action === 'add' ? 'to' : 'from' - } ${victim}${args.duration ? ` for ${util.humanizeDuration(args.duration)}` : ''}.`; + } ${victim}${args.duration ? ` for ${humanizeDuration(args.duration)}` : ''}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index e657b76..641f88e 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -1,4 +1,15 @@ -import { BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + format, + humanizeDuration, + userGuildPermCheck, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { Argument } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType, PermissionFlagsBits } from 'discord.js'; @@ -36,8 +47,8 @@ export default class SlowmodeCommand extends BushCommand { slash: true, channel: 'guild', clientPermissions: (m) => - util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels, PermissionFlagsBits.EmbedLinks], true), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels, PermissionFlagsBits.EmbedLinks], true), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -58,11 +69,11 @@ export default class SlowmodeCommand extends BushCommand { args.channel.type !== ChannelType.GuildVoice && !args.channel.isThread() ) - return await message.util.reply(`${util.emojis.error} <#${args.channel.id}> is not a text or thread channel.`); + return await message.util.reply(`${emojis.error} <#${args.channel.id}> is not a text or thread channel.`); args.length = typeof args.length === 'string' && !(['off', 'none', 'disable'] as const).includes(args.length) - ? await util.arg.cast('duration', message, args.length) + ? await Arg.cast('duration', message, args.length) : args.length; const length2: number = (['off', 'none', 'disable'] as const).includes(args.length) || args.length === null ? 0 : args.length; @@ -71,11 +82,11 @@ export default class SlowmodeCommand extends BushCommand { .setRateLimitPerUser(length2 / 1000, `Changed by ${message.author.tag} (${message.author.id}).`) .catch(() => {}); if (!setSlowmode) - return await message.util.reply(`${util.emojis.error} There was an error changing the slowmode of <#${args.channel.id}>.`); + return await message.util.reply(`${emojis.error} There was an error changing the slowmode of <#${args.channel.id}>.`); else return await message.util.reply( - `${util.emojis.success} Successfully changed the slowmode of <#${args.channel.id}> ${ - length2 ? `to ${util.format.input(util.humanizeDuration(length2))}` : '**off**' + `${emojis.success} Successfully changed the slowmode of <#${args.channel.id}> ${ + length2 ? `to ${format.input(humanizeDuration(length2))}` : '**off**' }.` ); } diff --git a/src/commands/moderation/timeout.ts b/src/commands/moderation/timeout.ts index ed3562d..7be8ecb 100644 --- a/src/commands/moderation/timeout.ts +++ b/src/commands/moderation/timeout.ts @@ -1,6 +1,10 @@ import { AllowedMentions, BushCommand, + castDurationContent, + clientSendAndPermCheck, + emojis, + format, Moderation, timeoutResponse, type ArgType, @@ -49,7 +53,7 @@ export default class TimeoutCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ModerateMembers]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ModerateMembers]), userPermissions: [PermissionFlagsBits.ModerateMembers] }); } @@ -61,12 +65,12 @@ export default class TimeoutCommand extends BushCommand { assert(message.inGuild()); assert(message.member); - const { duration, content } = await util.castDurationContent(args.reason_and_duration, message); + const { duration, content } = await castDurationContent(args.reason_and_duration, message); - if (!duration) return await message.util.reply(`${util.emojis.error} You must specify a duration for timeouts.`); + if (!duration) return await message.util.reply(`${emojis.error} You must specify a duration for timeouts.`); const member = await message.guild.members.fetch(args.user.id).catch(() => null); if (!member) - return await message.util.reply(`${util.emojis.error} The user you selected is not in the server or is not a valid user.`); + return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'timeout', true, useForce); @@ -82,22 +86,22 @@ export default class TimeoutCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(member.user.tag); + const victim = format.input(member.user.tag); switch (responseCode) { case timeoutResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not timeout ${victim} because I am missing the **Timeout Members** permission.`; + return `${emojis.error} Could not timeout ${victim} because I am missing the **Timeout Members** permission.`; case timeoutResponse.INVALID_DURATION: - return `${util.emojis.error} The duration you specified is too long, the longest you can timeout someone for is 28 days.`; + return `${emojis.error} The duration you specified is too long, the longest you can timeout someone for is 28 days.`; case timeoutResponse.ACTION_ERROR: - return `${util.emojis.error} An unknown error occurred while trying to timeout ${victim}.`; + return `${emojis.error} An unknown error occurred while trying to timeout ${victim}.`; case timeoutResponse.MODLOG_ERROR: - return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case timeoutResponse.DM_ERROR: - return `${util.emojis.warn} Timed out ${victim} however I could not send them a dm.`; + return `${emojis.warn} Timed out ${victim} however I could not send them a dm.`; case timeoutResponse.SUCCESS: - return `${util.emojis.success} Successfully timed out ${victim}.`; + return `${emojis.success} Successfully timed out ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts index d7549ab..a4c4992 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/unban.ts @@ -1,6 +1,9 @@ import { AllowedMentions, + Arg, BushCommand, + emojis, + format, unbanResponse, type ArgType, type CommandMessage, @@ -22,7 +25,7 @@ export default class UnbanCommand extends BushCommand { { id: 'user', description: 'The user to unban.', - type: util.arg.union('user', 'globalUser'), + type: Arg.union('user', 'globalUser'), prompt: 'What user would you like to unban?', retry: '{error} Choose a valid user to unban.', slashType: ApplicationCommandOptionType.User @@ -58,23 +61,23 @@ export default class UnbanCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(user.tag); + const victim = format.input(user.tag); switch (responseCode) { case unbanResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not unban ${victim} because I am missing the **Ban Members** permission.`; + return `${emojis.error} Could not unban ${victim} because I am missing the **Ban Members** permission.`; case unbanResponse.ACTION_ERROR: - return `${util.emojis.error} An error occurred while trying to unban ${victim}.`; + return `${emojis.error} An error occurred while trying to unban ${victim}.`; case unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: - return `${util.emojis.error} While unbanning ${victim}, there was an error removing their ban entry, please report this to my developers.`; + return `${emojis.error} While unbanning ${victim}, there was an error removing their ban entry, please report this to my developers.`; case unbanResponse.MODLOG_ERROR: - return `${util.emojis.error} While unbanning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} While unbanning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; case unbanResponse.NOT_BANNED: - return `${util.emojis.warn} ${victim} is not banned but I tried to unban them anyways.`; + return `${emojis.warn} ${victim} is not banned but I tried to unban them anyways.`; case unbanResponse.DM_ERROR: case unbanResponse.SUCCESS: - return `${util.emojis.success} Successfully unbanned ${victim}.`; + return `${emojis.success} Successfully unbanned ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/unblock.ts b/src/commands/moderation/unblock.ts index 886d546..09ec281 100644 --- a/src/commands/moderation/unblock.ts +++ b/src/commands/moderation/unblock.ts @@ -1,8 +1,12 @@ import { AllowedMentions, BushCommand, + clientSendAndPermCheck, + emojis, + format, Moderation, unblockResponse, + userGuildPermCheck, type ArgType, type CommandMessage, type OptArgType, @@ -51,8 +55,8 @@ export default class UnblockCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -65,11 +69,11 @@ export default class UnblockCommand extends BushCommand { assert(message.channel); if (!message.channel.isTextBased()) - return message.util.send(`${util.emojis.error} This command can only be used in text based channels.`); + return message.util.send(`${emojis.error} This command can only be used in text based channels.`); const member = await message.guild.members.fetch(args.user.id).catch(() => null); if (!member) - return await message.util.reply(`${util.emojis.error} The user you selected is not in the server or is not a valid user.`); + return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); const useForce = args.force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'unblock', true, useForce); @@ -85,24 +89,24 @@ export default class UnblockCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(member.user.tag); + const victim = format.input(member.user.tag); switch (responseCode) { case unblockResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not unblock ${victim} because I am missing the **Manage Channel** permission.`; + return `${emojis.error} Could not unblock ${victim} because I am missing the **Manage Channel** permission.`; case unblockResponse.INVALID_CHANNEL: - return `${util.emojis.error} Could not unblock ${victim}, you can only unblock users in text or thread channels.`; + return `${emojis.error} Could not unblock ${victim}, you can only unblock users in text or thread channels.`; case unblockResponse.ACTION_ERROR: - return `${util.emojis.error} An unknown error occurred while trying to unblock ${victim}.`; + return `${emojis.error} An unknown error occurred while trying to unblock ${victim}.`; case unblockResponse.MODLOG_ERROR: - return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case unblockResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: - return `${util.emojis.error} There was an error creating a punishment entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a punishment entry, please report this to my developers.`; case unblockResponse.DM_ERROR: - return `${util.emojis.warn} Unblocked ${victim} however I could not send them a dm.`; + return `${emojis.warn} Unblocked ${victim} however I could not send them a dm.`; case unblockResponse.SUCCESS: - return `${util.emojis.success} Successfully unblocked ${victim}.`; + return `${emojis.success} Successfully unblocked ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/unlockdown.ts b/src/commands/moderation/unlockdown.ts index dbcd429..db1a09d 100644 --- a/src/commands/moderation/unlockdown.ts +++ b/src/commands/moderation/unlockdown.ts @@ -1,5 +1,13 @@ import { LockdownCommand } from '#commands'; -import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType, ChannelType, PermissionFlagsBits } from 'discord.js'; export default class UnlockdownCommand extends BushCommand { @@ -14,7 +22,7 @@ export default class UnlockdownCommand extends BushCommand { { id: 'channel', description: 'Specify a different channel to unlockdown instead of the one you trigger the command in.', - type: util.arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), + type: Arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), prompt: 'What channel would you like to unlockdown?', slashType: ApplicationCommandOptionType.Channel, channelTypes: [ @@ -48,7 +56,7 @@ export default class UnlockdownCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageChannels]), userPermissions: [PermissionFlagsBits.ManageChannels] }); } diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index 202e341..9ac13ce 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -1,8 +1,13 @@ import { AllowedMentions, BushCommand, + clientSendAndPermCheck, + emojis, + format, Moderation, + prefix, unmuteResponse, + userGuildPermCheck, type ArgType, type CommandMessage, type OptArgType, @@ -51,8 +56,8 @@ export default class UnmuteCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageRoles]), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -63,7 +68,7 @@ export default class UnmuteCommand extends BushCommand { assert(message.inGuild()); assert(message.member); - const error = util.emojis.error; + const error = emojis.error; const member = message.guild.members.cache.get(user.id)!; const useForce = force && message.author.isOwner(); @@ -79,17 +84,17 @@ export default class UnmuteCommand extends BushCommand { }); const responseMessage = (): string => { - const prefix = util.prefix(message); - const victim = util.format.input(member.user.tag); + const prefix_ = prefix(message); + const victim = format.input(member.user.tag); switch (responseCode) { case unmuteResponse.MISSING_PERMISSIONS: return `${error} Could not unmute ${victim} because I am missing the **Manage Roles** permission.`; case unmuteResponse.NO_MUTE_ROLE: - return `${error} Could not unmute ${victim}, you must set a mute role with \`${prefix}config muteRole\`.`; + return `${error} Could not unmute ${victim}, you must set a mute role with \`${prefix_}config muteRole\`.`; case unmuteResponse.MUTE_ROLE_INVALID: - return `${error} Could not unmute ${victim} because the current mute role no longer exists. Please set a new mute role with \`${prefix}config muteRole\`.`; + return `${error} Could not unmute ${victim} because the current mute role no longer exists. Please set a new mute role with \`${prefix_}config muteRole\`.`; case unmuteResponse.MUTE_ROLE_NOT_MANAGEABLE: - return `${error} Could not unmute ${victim} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${prefix}config muteRole\`.`; + return `${error} Could not unmute ${victim} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${prefix_}config muteRole\`.`; case unmuteResponse.ACTION_ERROR: return `${error} Could not unmute ${victim}, there was an error removing their mute role.`; case unmuteResponse.MODLOG_ERROR: @@ -97,11 +102,11 @@ export default class UnmuteCommand extends BushCommand { case unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: return `${error} While muting ${victim}, there was an error removing their mute entry, please report this to my developers.`; case unmuteResponse.DM_ERROR: - return `${util.emojis.warn} unmuted ${victim} however I could not send them a dm.`; + return `${emojis.warn} unmuted ${victim} however I could not send them a dm.`; case unmuteResponse.SUCCESS: - return `${util.emojis.success} Successfully unmuted ${victim}.`; + return `${emojis.success} Successfully unmuted ${victim}.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/untimeout.ts b/src/commands/moderation/untimeout.ts index 7622f29..c6860c5 100644 --- a/src/commands/moderation/untimeout.ts +++ b/src/commands/moderation/untimeout.ts @@ -1,6 +1,9 @@ import { AllowedMentions, BushCommand, + clientSendAndPermCheck, + emojis, + format, Moderation, removeTimeoutResponse, type ArgType, @@ -51,7 +54,7 @@ export default class UntimeoutCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ModerateMembers]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ModerateMembers]), userPermissions: [PermissionFlagsBits.ModerateMembers] }); } @@ -65,9 +68,9 @@ export default class UntimeoutCommand extends BushCommand { const member = await message.guild.members.fetch(args.user.id).catch(() => null); if (!member) - return await message.util.reply(`${util.emojis.error} The user you selected is not in the server or is not a valid user.`); + return await message.util.reply(`${emojis.error} The user you selected is not in the server or is not a valid user.`); - if (!member.isCommunicationDisabled()) return message.util.reply(`${util.emojis.error} That user is not timed out.`); + if (!member.isCommunicationDisabled()) return message.util.reply(`${emojis.error} That user is not timed out.`); const useForce = args.force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'timeout', true, useForce); @@ -82,20 +85,20 @@ export default class UntimeoutCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(member.user.tag); + const victim = format.input(member.user.tag); switch (responseCode) { case removeTimeoutResponse.MISSING_PERMISSIONS: - return `${util.emojis.error} Could not untimeout ${victim} because I am missing the **Timeout Members** permission.`; + return `${emojis.error} Could not untimeout ${victim} because I am missing the **Timeout Members** permission.`; case removeTimeoutResponse.ACTION_ERROR: - return `${util.emojis.error} An unknown error occurred while trying to timeout ${victim}.`; + return `${emojis.error} An unknown error occurred while trying to timeout ${victim}.`; case removeTimeoutResponse.MODLOG_ERROR: - return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`; case removeTimeoutResponse.DM_ERROR: - return `${util.emojis.warn} Removed ${victim}'s timeout however I could not send them a dm.`; + return `${emojis.warn} Removed ${victim}'s timeout however I could not send them a dm.`; case removeTimeoutResponse.SUCCESS: - return `${util.emojis.success} Successfully removed ${victim}'s timeout.`; + return `${emojis.success} Successfully removed ${victim}'s timeout.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`; + return `${emojis.error} An error occurred: ${format.input(responseCode)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index b3bf965..81b2937 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -1,7 +1,12 @@ import { AllowedMentions, BushCommand, + clientSendAndPermCheck, + emojis, + format, Moderation, + ordinal, + userGuildPermCheck, warnResponse, type ArgType, type CommandMessage, @@ -50,8 +55,8 @@ export default class WarnCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), - userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + clientPermissions: (m) => clientSendAndPermCheck(m), + userPermissions: (m) => userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) }); } @@ -63,7 +68,7 @@ export default class WarnCommand extends BushCommand { assert(message.member); const member = message.guild.members.cache.get(user.id); - if (!member) return message.util.reply(`${util.emojis.error} I cannot warn users that are not in the server.`); + if (!member) return message.util.reply(`${emojis.error} I cannot warn users that are not in the server.`); const useForce = force && message.author.isOwner(); const canModerateResponse = await Moderation.permissionCheck(message.member, member, 'warn', true, useForce); @@ -77,19 +82,19 @@ export default class WarnCommand extends BushCommand { }); const responseMessage = (): string => { - const victim = util.format.input(member.user.tag); + const victim = format.input(member.user.tag); switch (response) { case warnResponse.MODLOG_ERROR: - return `${util.emojis.error} While warning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; + return `${emojis.error} While warning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; case warnResponse.ACTION_ERROR: case warnResponse.DM_ERROR: - return `${util.emojis.warn} ${victim} has been warned for the ${util.ordinal( + return `${emojis.warn} ${victim} has been warned for the ${ordinal( caseNum ?? 0 )} time, however I could not send them a dm.`; case warnResponse.SUCCESS: - return `${util.emojis.success} Successfully warned ${victim} for the ${util.ordinal(caseNum ?? 0)} time.`; + return `${emojis.success} Successfully warned ${victim} for the ${ordinal(caseNum ?? 0)} time.`; default: - return `${util.emojis.error} An error occurred: ${util.format.input(response)}}`; + return `${emojis.error} An error occurred: ${format.input(response)}}`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moulberry-bush/capePermissions.ts b/src/commands/moulberry-bush/capePermissions.ts index 5f15d9e..3ad9602 100644 --- a/src/commands/moulberry-bush/capePermissions.ts +++ b/src/commands/moulberry-bush/capePermissions.ts @@ -1,4 +1,15 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + format, + mcUUID, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import got from 'got'; @@ -21,7 +32,7 @@ export default class CapePermissionsCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], channel: 'guild' }); @@ -30,10 +41,10 @@ export default class CapePermissionsCommand extends BushCommand { public override async exec(message: CommandMessage | SlashMessage, args: { ign: ArgType<'string'> }) { let capePerms: CapePerms | null, uuid: string; try { - uuid = await util.mcUUID(args.ign); + uuid = await mcUUID(args.ign); } catch (e) { return await message.util.reply({ - content: `${util.emojis.error} ${util.format.input(args.ign)} doesn't appear to be a valid username.`, + content: `${emojis.error} ${format.input(args.ign)} doesn't appear to be a valid username.`, allowedMentions: AllowedMentions.none() }); } @@ -44,9 +55,7 @@ export default class CapePermissionsCommand extends BushCommand { capePerms = null; } if (capePerms == null) { - return await message.util.reply( - `${util.emojis.error} There was an error finding cape perms for ${util.format.input(args.ign)}.` - ); + return await message.util.reply(`${emojis.error} There was an error finding cape perms for ${format.input(args.ign)}.`); } else { if (capePerms?.perms) { let index = null; @@ -59,17 +68,15 @@ export default class CapePermissionsCommand extends BushCommand { continue; } if (index == null) - return await message.util.reply( - `${util.emojis.error} ${util.format.input(args.ign)} does not appear to have any capes.` - ); + return await message.util.reply(`${emojis.error} ${format.input(args.ign)} does not appear to have any capes.`); const userPerm: string[] = capePerms.perms[index].perms; const embed = new EmbedBuilder() .setTitle(`${args.ign}'s Capes`) .setDescription(userPerm.join('\n')) - .setColor(util.colors.default); + .setColor(colors.default); await message.util.reply({ embeds: [embed] }); } else { - return await message.util.reply(`${util.emojis.error} There was an error finding cape perms for ${args.ign}.`); + return await message.util.reply(`${emojis.error} There was an error finding cape perms for ${args.ign}.`); } } } diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts index 6bf7854..c2dae4d 100644 --- a/src/commands/moulberry-bush/capes.ts +++ b/src/commands/moulberry-bush/capes.ts @@ -2,7 +2,12 @@ import { AllowedMentions, BushCommand, ButtonPaginator, + clientSendAndPermCheck, + colors, DeleteButton, + emojis, + format, + mappings, type CommandMessage, type OptArgType, type SlashMessage @@ -36,7 +41,7 @@ export default class CapesCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -53,11 +58,11 @@ export default class CapesCommand extends BushCommand { .filter((f) => f.match !== null); const capes: { name: string; url: string; index: number; purchasable?: boolean }[] = [ - ...client.consts.mappings.capes + ...mappings.capes .filter((c) => !rawCapes.some((gitCape) => gitCape.match!.groups!.name === c.name) && c.custom) .map((c) => ({ name: c.name, url: c.custom!, index: c.index, purchasable: c.purchasable })), ...rawCapes.map((c) => { - const mapCape = client.consts.mappings.capes.find((a) => a.name === c.match!.groups!.name); + const mapCape = mappings.capes.find((a) => a.name === c.match!.groups!.name); const url = mapCape?.custom ?? `https://github.com/Moulberry/NotEnoughUpdates/raw/master/${c.f.path}`; const index = mapCape?.index !== undefined ? mapCape.index : null; return { name: c.match!.groups!.name, url, index: index!, purchasable: mapCape?.purchasable }; @@ -76,7 +81,7 @@ export default class CapesCommand extends BushCommand { await DeleteButton.send(message, { embeds: [embed] }); } else { await message.util.reply({ - content: `${util.emojis.error} Cannot find a cape called ${util.format.input(args.cape)}.`, + content: `${emojis.error} Cannot find a cape called ${format.input(args.cape)}.`, allowedMentions: AllowedMentions.none() }); } @@ -89,7 +94,7 @@ export default class CapesCommand extends BushCommand { private makeEmbed(cape: { name: string; url: string; index: number; purchasable?: boolean | undefined }): APIEmbed { return { title: `${cape.name} cape`, - color: util.colors.default, + color: colors.default, timestamp: new Date().toISOString(), image: { url: cape.url }, description: cape.purchasable @@ -99,7 +104,7 @@ export default class CapesCommand extends BushCommand { } public override autocomplete(interaction: AutocompleteInteraction) { - const capes = client.consts.mappings.capes.map((v) => v.name); + const capes = mappings.capes.map((v) => v.name); const fuzzy = new Fuse(capes, { threshold: 0.5, diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts index 23a6a20..0cadd6a 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type CommandMessage } from '#lib'; +import { AllowedMentions, BushCommand, clientSendAndPermCheck, emojis, type CommandMessage } from '#lib'; import { PermissionFlagsBits } from 'discord.js'; export default class GiveawayPingCommand extends BushCommand { @@ -9,7 +9,7 @@ export default class GiveawayPingCommand extends BushCommand { description: 'Pings the giveaway role.', usage: ['giveaway-ping'], examples: ['giveaway-ping'], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageMessages], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageMessages], true), userPermissions: [ PermissionFlagsBits.ManageGuild, PermissionFlagsBits.ManageMessages, @@ -30,7 +30,7 @@ export default class GiveawayPingCommand extends BushCommand { public override async exec(message: CommandMessage) { if (!message.member!.permissions.has(PermissionFlagsBits.ManageGuild) && !message.member!.user.isOwner()) - await message.util.reply(`${util.emojis.error} You are missing the **ManageGuild** permission.`); + await message.util.reply(`${emojis.error} You are missing the **ManageGuild** permission.`); await message.delete().catch(() => {}); diff --git a/src/commands/moulberry-bush/moulHammer.ts b/src/commands/moulberry-bush/moulHammer.ts index f07511a..0eeb769 100644 --- a/src/commands/moulberry-bush/moulHammer.ts +++ b/src/commands/moulberry-bush/moulHammer.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, colors, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -24,7 +24,7 @@ export default class MoulHammerCommand extends BushCommand { channel: 'guild', slashGuilds: ['516977525906341928'], restrictedGuilds: ['516977525906341928'], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -37,7 +37,7 @@ export default class MoulHammerCommand extends BushCommand { const embed = new EmbedBuilder() .setTitle('L') .setDescription(`${user.username} got moul'ed <:wideberry1:756223352598691942><:wideberry2:756223336832303154>`) - .setColor(util.colors.purple); + .setColor(colors.purple); await message.util.send({ embeds: [embed] }); } } diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts index 29eee76..06c1ad2 100644 --- a/src/commands/moulberry-bush/report.ts +++ b/src/commands/moulberry-bush/report.ts @@ -1,4 +1,13 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + timestampAndDelta, + type ArgType, + type CommandMessage +} from '#lib'; import { stripIndent } from '#tags'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; @@ -32,7 +41,7 @@ export default class ReportCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], channel: 'guild' }); @@ -42,23 +51,21 @@ export default class ReportCommand extends BushCommand { assert(message.inGuild()); if (!(await message.guild.hasFeature('reporting'))) - return await message.util.reply( - `${util.emojis.error} This command can only be used in servers where reporting is enabled.` - ); + return await message.util.reply(`${emojis.error} This command can only be used in servers where reporting is enabled.`); - if (!member) return await message.util.reply(`${util.emojis.error} Choose someone to report`); + if (!member) return await message.util.reply(`${emojis.error} Choose someone to report`); if (member.user.id === '322862723090219008') return await message.util.reply({ content: `Thank you for your report! We take these allegations very seriously and have reported <@${member.user.id}> to the FBI!`, allowedMentions: AllowedMentions.none() }); if (member.user.bot) - return await message.util.reply(`${util.emojis.error} You cannot report a bot <:WeirdChamp:756283321301860382>.`); + return await message.util.reply(`${emojis.error} You cannot report a bot <:WeirdChamp:756283321301860382>.`); const reportChannel = await message.guild.getLogChannel('report'); if (!reportChannel) return await message.util.reply( - `${util.emojis.error} This server has not setup a report logging channel or the channel no longer exists.` + `${emojis.error} This server has not setup a report logging channel or the channel no longer exists.` ); //The formatting of the report is mostly copied from carl since it is pretty good when it actually works @@ -70,24 +77,24 @@ export default class ReportCommand extends BushCommand { iconURL: message.author.avatarURL() ?? undefined }) .setTitle('New Report') - .setColor(util.colors.red) + .setColor(colors.red) .setDescription(evidence) .addFields([ { name: 'Reporter', value: stripIndent` **Name:**${message.author.tag} <@${message.author.id}> - **Joined:** $${util.timestampAndDelta(message.member!.joinedAt!)} - **Created:** ${util.timestampAndDelta(message.author.createdAt)} + **Joined:** $${timestampAndDelta(message.member!.joinedAt!)} + **Created:** ${timestampAndDelta(message.author.createdAt)} **Sent From**: <#${message.channel.id}> [Jump to context](${message.url})`, inline: true }, { name: 'Reported User', value: stripIndent` - **Name:**${member.user.tag} <@${member.user.id}> - **Joined:** $${util.timestampAndDelta(member.joinedAt!)} - **Created:** ${util.timestampAndDelta(member.user.createdAt)}`, + **Name:** ${member.user.tag} <@${member.user.id}> + **Joined:** ${timestampAndDelta(member.joinedAt!)} + **Created:** ${timestampAndDelta(member.user.createdAt)}`, inline: true } ]); @@ -102,8 +109,8 @@ export default class ReportCommand extends BushCommand { } await reportChannel.send({ embeds: [reportEmbed] }).then(async (ReportMessage) => { try { - await ReportMessage.react(util.emojis.check); - await ReportMessage.react(util.emojis.cross); + await ReportMessage.react(emojis.check); + await ReportMessage.react(emojis.cross); } catch { void client.console.warn('ReportCommand', 'Could not react to report message.'); } diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index 17f1bd8..574334a 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -1,4 +1,12 @@ -import { AllowedMentions, BushCommand, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + AllowedMentions, + Arg, + BushCommand, + clientSendAndPermCheck, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import { stripIndent } from '../../lib/common/tags.js'; @@ -70,7 +78,7 @@ export default class RuleCommand extends BushCommand { { id: 'rule', description: 'The rule to view.', - type: util.arg.range('integer', 1, rules.length, true), + type: Arg.range('integer', 1, rules.length, true), readableType: 'integer', prompt: 'What rule would you like to have cited?', retry: '{error} Choose a valid rule.', @@ -92,7 +100,7 @@ export default class RuleCommand extends BushCommand { slash: true, slashGuilds: ['516977525906341928'], channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], restrictedGuilds: ['516977525906341928'] }); diff --git a/src/commands/moulberry-bush/serverStatus.ts b/src/commands/moulberry-bush/serverStatus.ts index ad3903a..c0c0518 100644 --- a/src/commands/moulberry-bush/serverStatus.ts +++ b/src/commands/moulberry-bush/serverStatus.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, colors, emojis, type CommandMessage } from '#lib'; import assert from 'assert'; import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import got from 'got'; @@ -13,7 +13,7 @@ export default class ServerStatusCommand extends BushCommand { description: "Gives the status of moulberry's server", usage: ['server-status'], examples: ['server-status', 'ss'], - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], slash: true }); @@ -22,24 +22,24 @@ export default class ServerStatusCommand extends BushCommand { public override async exec(message: CommandMessage) { const msgEmbed = new EmbedBuilder() .setTitle('Server status') - .setDescription(`Checking server:\n${util.emojis.loading}`) - .setColor(util.colors.default) + .setDescription(`Checking server:\n${emojis.loading}`) + .setColor(colors.default) .setFooter({ text: 'Checking https://moulberry.codes/lowestbin.json' }); await message.util.reply({ embeds: [msgEmbed] }); let main; try { await got.get('https://moulberry.codes/lowestbin.json').json(); - main = util.emojis.success; + main = emojis.success; } catch (e) { - main = util.emojis.error; + main = emojis.error; } await message.util.edit({ embeds: [msgEmbed.setDescription(`Checking server:\n${main}`)] }); - if (main == util.emojis.success) { + if (main == emojis.success) { await message.util.edit({ embeds: [ msgEmbed .addFields([{ name: 'Status', value: 'The server is online, all features related to prices will likely work.' }]) - .setColor(util.colors.success) + .setColor(colors.success) ] }); } else { @@ -53,7 +53,7 @@ export default class ServerStatusCommand extends BushCommand { "It appears Moulberry's server is offline, this means that everything related to prices will likely not work." } ]) - .setColor(util.colors.error) + .setColor(colors.error) ] }); } diff --git a/src/commands/utilities/_poll.ts b/src/commands/utilities/_poll.ts index a843561..fdf6381 100644 --- a/src/commands/utilities/_poll.ts +++ b/src/commands/utilities/_poll.ts @@ -29,7 +29,7 @@ // } // ], // slash: true, -// clientPermissions: (m) => util.clientSendAndPermCheck(m), +// clientPermissions: (m) => clientSendAndPermCheck(m), // userPermissions: [] // }); // } @@ -38,8 +38,8 @@ // const { question, options } = this.parseArgs(message, args); // if (!question || !options.length) return; -// if (question.length > 256) return await message.util.reply(`${util.emojis.error} Question must be 256 characters or less.`); -// if (options.length > 10) return await message.util.reply(`${util.emojis.error} You can only have upto 10 options.`); +// if (question.length > 256) return await message.util.reply(`${emojis.error} Question must be 256 characters or less.`); +// if (options.length > 10) return await message.util.reply(`${emojis.error} You can only have upto 10 options.`); // return message.util.send({ // embeds: [ @@ -64,13 +64,13 @@ // const split = args.options.split(/[,|]/).filter((s) => s.trim().length > 0); // if (message.util.isSlash) { // if (split.length < 2) { -// void message.util.reply(`${util.emojis.error} You must provide at least two options.`); +// void message.util.reply(`${emojis.error} You must provide at least two options.`); // return { question: '', options: [] }; // } // return { question: args.question!, options: split }; // } else { // if (split.length < 3) { -// void message.util.reply(`${util.emojis.error} You must provide a question and at least two options.`); +// void message.util.reply(`${emojis.error} You must provide a question and at least two options.`); // return { question: '', options: [] }; // } diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts index 14cc5a0..dfbccfb 100644 --- a/src/commands/utilities/activity.ts +++ b/src/commands/utilities/activity.ts @@ -1,4 +1,13 @@ -import { BushCommand, type ArgType, type BushArgumentTypeCaster, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + clientSendAndPermCheck, + emojis, + regex, + type ArgType, + type BushArgumentTypeCaster, + type CommandMessage, + type SlashMessage +} from '#lib'; import { type ArgumentGeneratorReturn, type ArgumentTypeCaster } from 'discord-akairo'; import { ApplicationCommandOptionType, ChannelType, type DiscordAPIError, type Snowflake } from 'discord.js'; @@ -55,7 +64,7 @@ interface Activity { } function map(phase: string): Activity | null { - if (client.consts.regex.snowflake.test(phase)) return { id: phase, aliases: [] }; + if (regex.snowflake.test(phase)) return { id: phase, aliases: [] }; else if (phase in activityMap) return activityMap[phase as keyof typeof activityMap]; for (const activity in activityMap) { @@ -115,7 +124,7 @@ export default class ActivityCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -155,8 +164,7 @@ export default class ActivityCommand extends BushCommand { args: { channel: ArgType<'voiceChannel'>; activity: string } ) { const channel = typeof args.channel === 'string' ? message.guild?.channels.cache.get(args.channel) : args.channel; - if (channel?.type !== ChannelType.GuildVoice) - return await message.util.reply(`${util.emojis.error} Choose a valid voice channel`); + if (channel?.type !== ChannelType.GuildVoice) return await message.util.reply(`${emojis.error} Choose a valid voice channel`); const target_application_id = message.util.isSlashMessage(message) ? args.activity @@ -177,14 +185,12 @@ export default class ActivityCommand extends BushCommand { .catch((e: Error | DiscordAPIError) => { if ((e as DiscordAPIError)?.code === 50013) { - response = `${util.emojis.error} I am missing permissions to make an invite in that channel.`; + response = `${emojis.error} I am missing permissions to make an invite in that channel.`; return; - } else response = `${util.emojis.error} An error occurred while generating your invite: ${e?.message ?? e}`; + } else response = `${emojis.error} An error occurred while generating your invite: ${e?.message ?? e}`; }); if (response! || !invite || !invite.code) - return await message.util.reply( - response! ?? `${util.emojis.error} An unknown error occurred while generating your invite.` - ); + return await message.util.reply(response! ?? `${emojis.error} An unknown error occurred while generating your invite.`); else return await message.util.send(`https://discord.gg/${invite.code}`); } } diff --git a/src/commands/utilities/calculator.ts b/src/commands/utilities/calculator.ts index a318a79..75d63a1 100644 --- a/src/commands/utilities/calculator.ts +++ b/src/commands/utilities/calculator.ts @@ -1,4 +1,13 @@ -import { AllowedMentions, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + inspectCleanRedactCodeblock, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder } from 'discord.js'; import { evaluate } from 'mathjs'; @@ -25,7 +34,7 @@ export default class CalculatorCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -34,22 +43,20 @@ export default class CalculatorCommand extends BushCommand { const decodedEmbed = new EmbedBuilder().addFields([ { name: '📥 Input', - value: await util.inspectCleanRedactCodeblock(args.expression, 'mma') + value: await inspectCleanRedactCodeblock(args.expression, 'mma') } ]); try { const calculated = /^(9\s*?\+\s*?10)|(10\s*?\+\s*?9)$/.test(args.expression) ? '21' : evaluate(args.expression); decodedEmbed - .setTitle(`${util.emojis.successFull} Successfully Calculated Expression`) - .setColor(util.colors.success) - .addFields([{ name: '📤 Output', value: await util.inspectCleanRedactCodeblock(calculated.toString(), 'mma') }]); + .setTitle(`${emojis.successFull} Successfully Calculated Expression`) + .setColor(colors.success) + .addFields([{ name: '📤 Output', value: await inspectCleanRedactCodeblock(calculated.toString(), 'mma') }]); } catch (error) { decodedEmbed - .setTitle(`${util.emojis.errorFull} Unable to Calculate Expression`) - .setColor(util.colors.error) - .addFields([ - { name: `📤 Error`, value: await util.inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js') } - ]); + .setTitle(`${emojis.errorFull} Unable to Calculate Expression`) + .setColor(colors.error) + .addFields([{ name: `📤 Error`, value: await 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 8c82fcc..cc742c8 100644 --- a/src/commands/utilities/decode.ts +++ b/src/commands/utilities/decode.ts @@ -1,4 +1,14 @@ -import { AllowedMentions, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + capitalize, + clientSendAndPermCheck, + colors, + formatError, + inspectCleanRedactCodeblock, + type CommandMessage, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder } from 'discord.js'; const encodingTypesArray = ['ascii', 'utf8', 'utf-8', 'utf16le', 'ucs2', 'ucs-2', 'base64', 'latin1', 'binary', 'hex']; @@ -42,7 +52,7 @@ export default class DecodeCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -51,20 +61,18 @@ export default class DecodeCommand extends BushCommand { message: CommandMessage | SlashMessage, { from, to, data }: { from: BufferEncoding; to: BufferEncoding; data: string } ) { - const encodeOrDecode = util.capitalizeFirstLetter(message?.util?.parsed?.alias ?? 'decoded'); + const encodeOrDecode = capitalize(message?.util?.parsed?.alias ?? 'decoded'); const decodedEmbed = new EmbedBuilder() .setTitle(`${encodeOrDecode} Information`) - .addFields([{ name: '📥 Input', value: await util.inspectCleanRedactCodeblock(data) }]); + .addFields([{ name: '📥 Input', value: await inspectCleanRedactCodeblock(data) }]); try { const decoded = Buffer.from(data, from).toString(to); - decodedEmbed - .setColor(util.colors.success) - .addFields([{ name: '📤 Output', value: await util.inspectCleanRedactCodeblock(decoded) }]); + decodedEmbed.setColor(colors.success).addFields([{ name: '📤 Output', value: await inspectCleanRedactCodeblock(decoded) }]); } catch (error) { - decodedEmbed.setColor(util.colors.error).addFields([ + decodedEmbed.setColor(colors.error).addFields([ { name: `📤 Error ${encodeOrDecode.slice(1)}ing`, - value: await util.inspectCleanRedactCodeblock(util.formatError(error)) + value: await inspectCleanRedactCodeblock(formatError(error)) } ]); } diff --git a/src/commands/utilities/hash.ts b/src/commands/utilities/hash.ts index f47c388..6e8c37f 100644 --- a/src/commands/utilities/hash.ts +++ b/src/commands/utilities/hash.ts @@ -1,4 +1,4 @@ -import { BushCommand, type CommandMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, type CommandMessage } from '#lib'; import assert from 'assert'; import crypto from 'crypto'; import { ApplicationCommandOptionType } from 'discord.js'; @@ -25,7 +25,7 @@ export default class HashCommand extends BushCommand { slashType: ApplicationCommandOptionType.String } ], - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } diff --git a/src/commands/utilities/highlight-!.ts b/src/commands/utilities/highlight-!.ts index 6dca545..6847737 100644 --- a/src/commands/utilities/highlight-!.ts +++ b/src/commands/utilities/highlight-!.ts @@ -1,4 +1,4 @@ -import { BushCommand, Highlight, HighlightWord, type SlashMessage } from '#lib'; +import { BushCommand, clientSendAndPermCheck, Highlight, HighlightWord, type SlashMessage } from '#lib'; import { Flag, type ArgumentGeneratorReturn, type SlashOption } from 'discord-akairo'; import { ApplicationCommandOptionType, @@ -123,7 +123,7 @@ export default class HighlightCommand extends BushCommand { }), slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } diff --git a/src/commands/utilities/highlight-add.ts b/src/commands/utilities/highlight-add.ts index 726b887..9624a7e 100644 --- a/src/commands/utilities/highlight-add.ts +++ b/src/commands/utilities/highlight-add.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, emojis, format, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js'; @@ -44,16 +44,16 @@ export default class HighlightAddCommand extends BushCommand { if (!args.regex) { if (args.word.length < 2) - return message.util.send(`${util.emojis.error} You can only highlight words that are longer than 2 characters.`); + return message.util.send(`${emojis.error} You can only highlight words that are longer than 2 characters.`); if (args.word.length > 50) - return await message.util.reply(`${util.emojis.error} You can only highlight words that are shorter than 50 characters.`); + return await message.util.reply(`${emojis.error} You can only highlight words that are shorter than 50 characters.`); } else { try { new RegExp(args.word); } catch (e) { assert(e instanceof SyntaxError); return message.util.send({ - content: `${util.emojis.error} Invalid regex ${util.format.inlineCode(e.message)}.`, + content: `${emojis.error} Invalid regex ${format.inlineCode(e.message)}.`, allowedMentions: AllowedMentions.none() }); } @@ -65,15 +65,15 @@ export default class HighlightAddCommand extends BushCommand { }); if (typeof res === 'string') - return await message.util.reply({ content: `${util.emojis.error} ${res}`, allowedMentions: AllowedMentions.none() }); + return await message.util.reply({ content: `${emojis.error} ${res}`, allowedMentions: AllowedMentions.none() }); else if (!res) return await message.util.reply({ - content: `${util.emojis.error} There was an error highlighting "${args.word}".`, + content: `${emojis.error} There was an error highlighting "${args.word}".`, allowedMentions: AllowedMentions.none() }); return await message.util.reply({ - content: `${util.emojis.success} Successfully added "${args.word}" to your highlight list.`, + content: `${emojis.success} Successfully added "${args.word}" to your highlight list.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/utilities/highlight-block.ts b/src/commands/utilities/highlight-block.ts index 61e1254..5429071 100644 --- a/src/commands/utilities/highlight-block.ts +++ b/src/commands/utilities/highlight-block.ts @@ -1,4 +1,14 @@ -import { AllowedMentions, BushCommand, Highlight, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + addToArray, + AllowedMentions, + Arg, + BushCommand, + emojis, + Highlight, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { Channel, GuildMember } from 'discord.js'; @@ -35,15 +45,13 @@ export default class HighlightBlockCommand extends BushCommand { assert(message.inGuild()); args.target = - typeof args.target === 'string' - ? (await util.arg.cast(util.arg.union('member', 'channel'), message, args.target))! - : args.target; + typeof args.target === 'string' ? (await Arg.cast(Arg.union('member', 'channel'), message, args.target))! : args.target; if (!args.target || !(args.target instanceof GuildMember || args.target instanceof Channel)) - return await message.util.reply(`${util.emojis.error} You can only block users or channels.`); + return await message.util.reply(`${emojis.error} You can only block users or channels.`); if (args.target instanceof Channel && !args.target.isTextBased()) - return await message.util.reply(`${util.emojis.error} You can only block text-based channels.`); + return await message.util.reply(`${emojis.error} You can only block text-based channels.`); const [highlight] = await Highlight.findOrCreate({ where: { guild: message.guild.id, user: message.author.id } @@ -54,16 +62,16 @@ export default class HighlightBlockCommand extends BushCommand { if (highlight[key].includes(args.target.id)) return await message.util.reply({ // eslint-disable-next-line @typescript-eslint/no-base-to-string - content: `${util.emojis.error} You have already blocked ${args.target}.`, + content: `${emojis.error} You have already blocked ${args.target}.`, allowedMentions: AllowedMentions.none() }); - highlight[key] = util.addToArray(highlight[key], args.target.id); + highlight[key] = addToArray(highlight[key], args.target.id); await highlight.save(); return await message.util.reply({ // eslint-disable-next-line @typescript-eslint/no-base-to-string - content: `${util.emojis.success} Successfully blocked ${args.target} from triggering your highlights.`, + content: `${emojis.success} Successfully blocked ${args.target} from triggering your highlights.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/utilities/highlight-clear.ts b/src/commands/utilities/highlight-clear.ts index b905f3b..df9f387 100644 --- a/src/commands/utilities/highlight-clear.ts +++ b/src/commands/utilities/highlight-clear.ts @@ -1,4 +1,4 @@ -import { BushCommand, ConfirmationPrompt, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, ConfirmationPrompt, emojis, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { highlightSubcommands } from './highlight-!.js'; @@ -21,11 +21,11 @@ export default class HighlightClearCommand extends BushCommand { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); const confirm = await ConfirmationPrompt.send(message, { content: `Are you sure you want to clear your highlight list?` }); - if (!confirm) return await message.util.reply(`${util.emojis.warn} You decided not 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); - if (!success) return await message.util.reply(`${util.emojis.error} There was an error clearing your highlight list.`); + if (!success) return await message.util.reply(`${emojis.error} There was an error clearing your highlight list.`); - return await message.util.reply(`${util.emojis.success} Successfully cleared 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 40020cd..b458550 100644 --- a/src/commands/utilities/highlight-matches.ts +++ b/src/commands/utilities/highlight-matches.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { BushCommand, ButtonPaginator, chunk, colors, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { type ArgumentGeneratorReturn } from 'discord-akairo'; import { type APIEmbed } from 'discord.js'; @@ -36,18 +36,16 @@ export default class HighlightMatchesCommand extends BushCommand { const res = await client.highlightManager.checkPhrase(message.guild.id, message.author.id, args.phrase); - if (!res.size) return await message.util.reply(`${util.emojis.error} You are not highlighting any words`); + if (!res.size) return await message.util.reply(`${emojis.error} You are not highlighting any words`); - const lines = res.map( - (passed, hl) => `${passed ? util.emojis.check : util.emojis.cross} ${hl.regex ? `/${hl.word}/gi` : hl.word}` - ); - const chunked = util.chunk(lines, 10); + const lines = res.map((passed, hl) => `${passed ? emojis.check : emojis.cross} ${hl.regex ? `/${hl.word}/gi` : hl.word}`); + const chunked = chunk(lines, 10); const pages = chunked.map( (chunk): APIEmbed => ({ title: `Matches`, description: chunk.join('\n'), - color: util.colors.default + color: colors.default }) ); diff --git a/src/commands/utilities/highlight-remove.ts b/src/commands/utilities/highlight-remove.ts index a2f2367..bb1300a 100644 --- a/src/commands/utilities/highlight-remove.ts +++ b/src/commands/utilities/highlight-remove.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, emojis, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { highlightCommandArgs, highlightSubcommands } from './highlight-!.js'; @@ -34,15 +34,15 @@ export default class HighlightRemoveCommand extends BushCommand { const res = await client.highlightManager.removeHighlight(message.guild.id, message.author.id, args.word); if (typeof res === 'string') - return await message.util.reply({ content: `${util.emojis.error} ${res}`, allowedMentions: AllowedMentions.none() }); + return await message.util.reply({ content: `${emojis.error} ${res}`, allowedMentions: AllowedMentions.none() }); else if (!res) return await message.util.reply({ - content: `${util.emojis.error} There was an error unhighlighting "${args.word}".`, + content: `${emojis.error} There was an error unhighlighting "${args.word}".`, allowedMentions: AllowedMentions.none() }); return await message.util.reply({ - content: `${util.emojis.success} Successfully removed "${args.word}" from your highlight list.`, + content: `${emojis.success} Successfully removed "${args.word}" from your highlight list.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/utilities/highlight-show.ts b/src/commands/utilities/highlight-show.ts index 80ba4ca..d966f3a 100644 --- a/src/commands/utilities/highlight-show.ts +++ b/src/commands/utilities/highlight-show.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Highlight, type CommandMessage, type SlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, colors, emojis, Highlight, type CommandMessage, type SlashMessage } from '#lib'; import assert from 'assert'; import { EmbedBuilder } from 'discord.js'; import { highlightSubcommands } from './highlight-!.js'; @@ -25,7 +25,7 @@ export default class HighlightShowCommand extends BushCommand { void client.highlightManager.syncCache(); - if (!highlight.words.length) return message.util.reply(`${util.emojis.error} You are not highlighting any words.`); + if (!highlight.words.length) return message.util.reply(`${emojis.error} You are not highlighting any words.`); const embed = new EmbedBuilder() .setTitle('Highlight List') @@ -35,7 +35,7 @@ export default class HighlightShowCommand extends BushCommand { .join('\n') .substring(0, 4096) ) - .setColor(util.colors.default); + .setColor(colors.default); if (highlight.blacklistedChannels.length) embed.addFields([ diff --git a/src/commands/utilities/highlight-unblock.ts b/src/commands/utilities/highlight-unblock.ts index b738ee9..7f416eb 100644 --- a/src/commands/utilities/highlight-unblock.ts +++ b/src/commands/utilities/highlight-unblock.ts @@ -1,4 +1,13 @@ -import { AllowedMentions, BushCommand, Highlight, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + emojis, + Highlight, + removeFromArray, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { Argument, ArgumentGeneratorReturn } from 'discord-akairo'; import { Channel, GuildMember } from 'discord.js'; @@ -35,10 +44,10 @@ export default class HighlightUnblockCommand extends BushCommand { assert(message.inGuild()); if (!(args.target instanceof GuildMember || args.target instanceof Channel)) - return await message.util.reply(`${util.emojis.error} You can only unblock users or channels.`); + return await message.util.reply(`${emojis.error} You can only unblock users or channels.`); if (args.target instanceof Channel && !args.target.isTextBased()) - return await message.util.reply(`${util.emojis.error} You can only unblock text-based channels.`); + return await message.util.reply(`${emojis.error} You can only unblock text-based channels.`); const [highlight] = await Highlight.findOrCreate({ where: { guild: message.guild.id, user: message.author.id } @@ -48,15 +57,15 @@ export default class HighlightUnblockCommand extends BushCommand { if (!highlight[key].includes(args.target.id)) return await message.util.reply({ - content: `${util.emojis.error} ${args.target} is not blocked so cannot be unblock.`, + content: `${emojis.error} ${args.target} is not blocked so cannot be unblock.`, allowedMentions: AllowedMentions.none() }); - highlight[key] = util.removeFromArray(highlight[key], args.target.id); + highlight[key] = removeFromArray(highlight[key], args.target.id); await highlight.save(); return await message.util.reply({ - content: `${util.emojis.success} Successfully allowed ${args.target} to trigger your highlights.`, + content: `${emojis.success} Successfully allowed ${args.target} to trigger your highlights.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/utilities/price.ts b/src/commands/utilities/price.ts index a4f4c03..c569437 100644 --- a/src/commands/utilities/price.ts +++ b/src/commands/utilities/price.ts @@ -1,4 +1,4 @@ -import { ArgType, BushCommand, type CommandMessage } from '#lib'; +import { ArgType, BushCommand, clientSendAndPermCheck, colors, emojis, format, oxford, type CommandMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, AutocompleteInteraction, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import Fuse from 'fuse.js'; @@ -39,7 +39,7 @@ export default class PriceCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [], typing: true }); @@ -58,12 +58,12 @@ export default class PriceCommand extends BushCommand { ])) as [Bazaar | undefined, LowestBIN | undefined, LowestBIN | undefined, AuctionAverages | undefined]; let parsedItem = args.item.toString().toUpperCase().replace(/ /g, '_').replace(/'S/g, ''); - const priceEmbed = new EmbedBuilder().setColor(errors?.length ? util.colors.warn : util.colors.success).setTimestamp(); + const priceEmbed = new EmbedBuilder().setColor(errors?.length ? colors.warn : colors.success).setTimestamp(); if (bazaar?.success === false) errors.push('bazaar'); if (errors.length) { - priceEmbed.setFooter({ text: `Could not fetch data for ${util.oxford(errors, 'and')}` }); + priceEmbed.setFooter({ text: `Could not fetch data for ${oxford(errors, 'and')}` }); } // create a set from all the item names so that there are no duplicates for the fuzzy search @@ -86,7 +86,7 @@ export default class PriceCommand extends BushCommand { // if its a bazaar item then it there should not be any ah data if (bazaar?.products?.[parsedItem]) { - priceEmbed.setTitle(`Bazaar Information for ${util.format.input(parsedItem)}`).addFields([ + priceEmbed.setTitle(`Bazaar Information for ${format.input(parsedItem)}`).addFields([ { name: 'Sell Price', value: addBazaarInformation('sellPrice', 2, true) }, { name: 'Buy Price', value: addBazaarInformation('buyPrice', 2, true) }, { @@ -103,7 +103,7 @@ export default class PriceCommand extends BushCommand { // checks if the item exists in any of the action information otherwise it is not a valid item if (currentLowestBIN?.[parsedItem] || averageLowestBIN?.[parsedItem] || auctionAverages?.[parsedItem]) { - priceEmbed.setTitle(`Price Information for ${util.format.input(parsedItem)}`).setFooter({ + priceEmbed.setTitle(`Price Information for ${format.input(parsedItem)}`).setFooter({ text: `${ priceEmbed.data.footer?.text ? `${priceEmbed.data.footer.text} | ` : '' }All information is based on the last 3 days.` @@ -111,10 +111,8 @@ export default class PriceCommand extends BushCommand { } else { const errorEmbed = new EmbedBuilder(); errorEmbed - .setColor(util.colors.error) - .setDescription( - `${util.emojis.error} ${util.format.input(parsedItem)} is not a valid item id, or it has no auction data.` - ); + .setColor(colors.error) + .setDescription(`${emojis.error} ${format.input(parsedItem)} is not a valid item id, or it has no auction data.`); return await message.util.reply({ embeds: [errorEmbed] }); } diff --git a/src/commands/utilities/remind.ts b/src/commands/utilities/remind.ts index 8727879..3a1cd18 100644 --- a/src/commands/utilities/remind.ts +++ b/src/commands/utilities/remind.ts @@ -1,4 +1,17 @@ -import { BushCommand, Reminder, Time, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + BushCommand, + castDurationContent, + clientSendAndPermCheck, + dateDelta, + emojis, + format, + Reminder, + Time, + timestamp, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class RemindCommand extends BushCommand { @@ -22,7 +35,7 @@ export default class RemindCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -31,13 +44,13 @@ export default class RemindCommand extends BushCommand { message: CommandMessage | SlashMessage, args: { reminder: OptArgType<'contentWithDuration'> | string } ) { - const { duration, content } = await util.castDurationContent(args.reminder, message); + const { duration, content } = await castDurationContent(args.reminder, message); - if (!content.trim()) return await message.util.reply(`${util.emojis.error} Please enter a reason to be reminded about.`); - if (!duration) return await message.util.reply(`${util.emojis.error} Please enter a time to remind you in.`); + if (!content.trim()) return await message.util.reply(`${emojis.error} Please enter a reason to be reminded about.`); + if (!duration) return await message.util.reply(`${emojis.error} Please enter a time to remind you in.`); if (duration < Time.Second * 30) - return await message.util.reply(`${util.emojis.error} You cannot be reminded in less than 30 seconds.`); + return await message.util.reply(`${emojis.error} You cannot be reminded in less than 30 seconds.`); const expires = new Date(Date.now() + duration); @@ -49,10 +62,10 @@ export default class RemindCommand extends BushCommand { expires: expires }).catch(() => false); - if (!success) return await message.util.reply(`${util.emojis.error} Could not create a reminder.`); + if (!success) return await message.util.reply(`${emojis.error} Could not create a reminder.`); // This isn't technically accurate, but it prevents it from being .99 seconds - const delta = util.format.bold(util.dateDelta(new Date(Date.now() + duration))); - return await message.util.reply(`${util.emojis.success} I will remind you in ${delta} (${util.timestamp(expires, 'T')}).`); + const delta = format.bold(dateDelta(new Date(Date.now() + duration))); + return await message.util.reply(`${emojis.success} I will remind you in ${delta} (${timestamp(expires, 'T')}).`); } } diff --git a/src/commands/utilities/reminders.ts b/src/commands/utilities/reminders.ts index 79284ac..18243fe 100644 --- a/src/commands/utilities/reminders.ts +++ b/src/commands/utilities/reminders.ts @@ -1,4 +1,15 @@ -import { BushCommand, ButtonPaginator, Reminder, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + ButtonPaginator, + chunk, + clientSendAndPermCheck, + colors, + emojis, + Reminder, + timestamp, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { PermissionFlagsBits, type APIEmbed } from 'discord.js'; import { Op } from 'sequelize'; @@ -14,22 +25,22 @@ export default class RemindersCommand extends BushCommand { usage: ['reminder'], examples: ['reminders'], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks]), userPermissions: [] }); } public override async exec(message: CommandMessage | SlashMessage) { const reminders = await Reminder.findAll({ where: { user: message.author.id, expires: { [Op.gt]: new Date() } } }); - if (!reminders.length) return message.util.send(`${util.emojis.error} You don't have any reminders set.`); + if (!reminders.length) return message.util.send(`${emojis.error} You don't have any reminders set.`); - const formattedReminders = reminders.map((reminder) => `${util.timestamp(reminder.expires, 't')} - ${reminder.content}`); + const formattedReminders = reminders.map((reminder) => `${timestamp(reminder.expires, 't')} - ${reminder.content}`); - const chunked = util.chunk(formattedReminders, 15); + const chunked = chunk(formattedReminders, 15); const embeds: APIEmbed[] = chunked.map((chunk) => ({ title: `Reminders`, description: chunk.join('\n'), - color: util.colors.default + color: colors.default })); return await ButtonPaginator.send(message, embeds); } diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts index 69d3054..83ec338 100644 --- a/src/commands/utilities/steal.ts +++ b/src/commands/utilities/steal.ts @@ -1,4 +1,13 @@ -import { BushCommand, OptArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + emojis, + OptArgType, + regex, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { type ArgumentGeneratorReturn, type ArgumentType, type ArgumentTypeCaster } from 'discord-akairo'; import { ApplicationCommandOptionType, Attachment, PermissionFlagsBits } from 'discord.js'; @@ -36,7 +45,7 @@ export default class StealCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.ManageEmojisAndStickers]), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.ManageEmojisAndStickers]), userPermissions: [PermissionFlagsBits.ManageEmojisAndStickers] }); } @@ -47,7 +56,7 @@ export default class StealCommand extends BushCommand { const emoji = hasImage ? message.attachments.first()!.url : yield { - type: util.arg.union('discordEmoji', 'snowflake', 'url') as ArgumentType | ArgumentTypeCaster, + type: Arg.union('discordEmoji', 'snowflake', 'url') as ArgumentType | ArgumentTypeCaster, prompt: { start: lang.emojiStart, retry: lang.emojiRetry } }; @@ -65,20 +74,20 @@ export default class StealCommand extends BushCommand { ) { assert(message.inGuild()); - if (!args.emoji) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); + if (!args.emoji) return await message.util.reply(`${emojis.error} You must provide an emoji to steal.`); const image = args.emoji instanceof URL ? args.emoji.href : typeof args.emoji === 'object' ? `https://cdn.discordapp.com/emojis/${args.emoji.id}` - : client.consts.regex.snowflake.test(args.emoji ?? '') + : regex.snowflake.test(args.emoji ?? '') ? `https://cdn.discordapp.com/emojis/${args!.emoji}` : (args.emoji ?? '').match(/https?:\/\//) ? args.emoji : undefined; - if (image === undefined) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); + if (image === undefined) return await message.util.reply(`${emojis.error} You must provide an emoji to steal.`); const emojiName = args.name ?? args.emoji instanceof URL @@ -96,11 +105,9 @@ export default class StealCommand extends BushCommand { .catch((e: Error) => e); if (!(creationSuccess instanceof Error)) - return await message.util.reply(`${util.emojis.success} You successfully stole ${creationSuccess}.`); + return await message.util.reply(`${emojis.success} You successfully stole ${creationSuccess}.`); else { - return await message.util.reply( - `${util.emojis.error} The was an error stealing that emoji \`${creationSuccess.message}\`.` - ); + return await message.util.reply(`${emojis.error} The was an error stealing that emoji \`${creationSuccess.message}\`.`); } } @@ -129,11 +136,9 @@ export default class StealCommand extends BushCommand { .catch((e: Error) => e); if (!(creationSuccess instanceof Error)) - return await message.util.reply(`${util.emojis.success} You successfully stole ${creationSuccess}.`); + return await message.util.reply(`${emojis.success} You successfully stole ${creationSuccess}.`); else { - return await message.util.reply( - `${util.emojis.error} The was an error stealing that emoji \`${creationSuccess.message}\`.` - ); + return await message.util.reply(`${emojis.error} The was an error stealing that emoji \`${creationSuccess.message}\`.`); } } } diff --git a/src/commands/utilities/suicide.ts b/src/commands/utilities/suicide.ts index c4151e8..d2510cc 100644 --- a/src/commands/utilities/suicide.ts +++ b/src/commands/utilities/suicide.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, clientSendAndPermCheck, colors, type CommandMessage, type SlashMessage } from '#lib'; import { stripIndent } from '#tags'; import { EmbedBuilder } from 'discord.js'; @@ -11,7 +11,7 @@ export default class SuicideCommand extends BushCommand { usage: ['suicide'], examples: ['suicide'], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], bypassChannelBlacklist: true }); @@ -21,7 +21,7 @@ export default class SuicideCommand extends BushCommand { // stolen from https://github.com/dexbiobot/Zeppelin const suicideEmbed = new EmbedBuilder() .setTitle('Mental Health Resources') - .setColor(util.colors.red) + .setColor(colors.red) .setAuthor({ name: 'Remember, You Matter <3', iconURL: diff --git a/src/commands/utilities/uuid.ts b/src/commands/utilities/uuid.ts index 1e70e91..44cfeda 100644 --- a/src/commands/utilities/uuid.ts +++ b/src/commands/utilities/uuid.ts @@ -1,4 +1,14 @@ -import { AllowedMentions, ArgType, BushCommand, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + ArgType, + BushCommand, + clientSendAndPermCheck, + emojis, + format, + mcUUID, + type CommandMessage, + type SlashMessage +} from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class UuidCommand extends BushCommand { @@ -30,7 +40,7 @@ export default class UuidCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -41,17 +51,17 @@ export default class UuidCommand extends BushCommand { ) { if (typeof args.ign === 'string') args.ign = { match: /\w{1,16}/im.exec(args.ign)!, matches: [] }; - if (!args.ign.match) return await message.util.reply(`${util.emojis.error} Please enter a valid ign.`); + if (!args.ign.match) return await message.util.reply(`${emojis.error} Please enter a valid ign.`); const readableIGN = args.ign.match[0]; try { - const uuid = await util.mcUUID(readableIGN, args.dashed); + const uuid = await mcUUID(readableIGN, args.dashed); return await message.util.reply({ - content: `The uuid for ${util.format.input(readableIGN)} is ${util.format.input(uuid)}`, + content: `The uuid for ${format.input(readableIGN)} is ${format.input(uuid)}`, allowedMentions: AllowedMentions.none() }); } catch (e) { return await message.util.reply({ - content: `${util.emojis.error} Could not find an uuid for ${util.format.input(readableIGN)}.`, + content: `${emojis.error} Could not find an uuid for ${format.input(readableIGN)}.`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index 5c2c146..8114cef 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.ts @@ -1,4 +1,16 @@ -import { BushCommand, type ArgType, type CommandMessage, type OptArgType, type SlashMessage } from '#lib'; +import { + Arg, + BushCommand, + clientSendAndPermCheck, + codeblock, + colors, + emojis, + inspect, + type ArgType, + type CommandMessage, + type OptArgType, + type SlashMessage +} from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, ChannelType, EmbedBuilder, Message, PermissionFlagsBits } from 'discord.js'; @@ -14,7 +26,7 @@ export default class ViewRawCommand extends BushCommand { { id: 'message', description: 'The message to view the raw content of.', - type: util.arg.union('message', 'messageLink'), + type: Arg.union('message', 'messageLink'), readableType: 'message|messageLink', prompt: 'What message would you like to view?', retry: '{error} Choose a valid message.', @@ -23,7 +35,7 @@ export default class ViewRawCommand extends BushCommand { { id: 'channel', description: 'The channel that the message is in.', - type: util.arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), + type: Arg.union('textChannel', 'newsChannel', 'threadChannel', 'voiceChannel'), prompt: 'What channel is the message in?', retry: '{error} Choose a valid channel.', optional: true, @@ -59,7 +71,7 @@ export default class ViewRawCommand extends BushCommand { ], slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), + clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true), userPermissions: [] }); } @@ -81,7 +93,7 @@ export default class ViewRawCommand extends BushCommand { args.message instanceof Message ? args.message : await args.channel!.messages.fetch(`${args.message}`).catch(() => null); if (!newMessage) return await message.util.reply( - `${util.emojis.error} There was an error fetching that message, make sure that is a valid id and if the message is not in this channel, please provide a channel.` + `${emojis.error} There was an error fetching that message, make sure that is a valid id and if the message is not in this channel, please provide a channel.` ); const Embed = await ViewRawCommand.getRawData(newMessage, { json: args.json, js: args.js }); @@ -94,14 +106,14 @@ export default class ViewRawCommand extends BushCommand { options.json || options.js ? options.json ? JSON.stringify(message.toJSON(), undefined, 2) - : util.inspect(message.toJSON()) || '[No Content]' + : inspect(message.toJSON()) || '[No Content]' : message.content || '[No Content]'; const lang = options.json ? 'json' : options.js ? 'js' : undefined; return new EmbedBuilder() .setFooter({ text: message.author.tag, iconURL: message.author.avatarURL() ?? undefined }) .setTimestamp(message.createdTimestamp) - .setColor(message.member?.roles?.color?.color ?? util.colors.default) + .setColor(message.member?.roles?.color?.color ?? colors.default) .setTitle('Raw Message Information') - .setDescription(await util.codeblock(content, 2048, lang)); + .setDescription(await codeblock(content, 2048, lang)); } } diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index ae96a65..851411a 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -1,6 +1,17 @@ -import { BushCommand, ButtonPaginator, OptArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + BushCommand, + ButtonPaginator, + chunk, + clientSendAndPermCheck, + colors, + emojis, + OptArgType, + oxford, + type CommandMessage, + type SlashMessage +} from '#lib'; import assert from 'assert'; -import { ApplicationCommandOptionType, Util, type CommandInteraction, type Role } from 'discord.js'; +import { ApplicationCommandOptionType, escapeMarkdown, type CommandInteraction, type Role } from 'discord.js'; export default class WhoHasRoleCommand extends BushCommand { public constructor() { @@ -24,7 +35,7 @@ export default class WhoHasRoleCommand extends BushCommand { ), slash: true, channel: 'guild', - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [], typing: true }); @@ -44,18 +55,18 @@ export default class WhoHasRoleCommand extends BushCommand { const members = message.guild.members.cache.filter((m) => roles.every((r) => m.roles.cache.has(r))); - const roleMembers = members.map((member) => `${member.user} (${Util.escapeMarkdown(member.user.tag)})`); - const chunkedRoleMembers = util.chunk(roleMembers, 30); + const roleMembers = members.map((member) => `${member.user} (${escapeMarkdown(member.user.tag)})`); + const chunkedRoleMembers = chunk(roleMembers, 30); const title = `Members with ${ roles.length < 4 - ? util.oxford( + ? oxford( rawRoles.map((r) => r.name), 'and' ) : `${rawRoles.length} Roles` } [\`${members.size.toLocaleString()}\`]`; - const color = util.colors.default; + const color = colors.default; const embedPages = chunkedRoleMembers.map((chunk) => ({ title, description: chunk.join('\n'), @@ -63,7 +74,7 @@ export default class WhoHasRoleCommand extends BushCommand { })); if (embedPages.length === 0) { - return await message.util.reply(`${util.emojis.error} No members found matching the given roles.`); + return await message.util.reply(`${emojis.error} No members found matching the given roles.`); } return await ButtonPaginator.send(message, embedPages, null, true); diff --git a/src/commands/utilities/wolframAlpha.ts b/src/commands/utilities/wolframAlpha.ts index 98cac69..b682c85 100644 --- a/src/commands/utilities/wolframAlpha.ts +++ b/src/commands/utilities/wolframAlpha.ts @@ -1,4 +1,15 @@ -import { AllowedMentions, BushCommand, type ArgType, type CommandMessage, type SlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + clientSendAndPermCheck, + colors, + emojis, + inspectCleanRedactCodeblock, + uploadImageToImgur, + type ArgType, + type CommandMessage, + type SlashMessage +} from '#lib'; import { initializeClass as WolframAlphaAPI } from '@notenoughupdates/wolfram-alpha-api'; import assert from 'assert'; import { ApplicationCommandOptionType, EmbedBuilder, type MessageOptions } from 'discord.js'; @@ -34,7 +45,7 @@ export default class WolframAlphaCommand extends BushCommand { } ], slash: true, - clientPermissions: (m) => util.clientSendAndPermCheck(m), + clientPermissions: (m) => clientSendAndPermCheck(m), userPermissions: [] }); } @@ -45,13 +56,13 @@ export default class WolframAlphaCommand extends BushCommand { ) { if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); - args.image && void message.util.reply({ content: `${util.emojis.loading} Loading...`, embeds: [] }); + args.image && void message.util.reply({ content: `${emojis.loading} Loading...`, embeds: [] }); const waApi = WolframAlphaAPI(client.config.credentials.wolframAlphaAppId); const decodedEmbed = new EmbedBuilder().addFields([ { name: '📥 Input', - value: await util.inspectCleanRedactCodeblock(args.expression) + value: await inspectCleanRedactCodeblock(args.expression) } ]); const sendOptions: MessageOptions = { content: null, allowedMentions: AllowedMentions.none() }; @@ -59,21 +70,19 @@ export default class WolframAlphaCommand extends BushCommand { const calculated = await (args.image ? waApi.getSimple({ i: args.expression, timeout: 1, background: '2C2F33', foreground: 'white' }) : waApi.getShort(args.expression)); - decodedEmbed.setTitle(`${util.emojis.successFull} Successfully Queried Expression`).setColor(util.colors.success); + decodedEmbed.setTitle(`${emojis.successFull} Successfully Queried Expression`).setColor(colors.success); if (args.image) { - decodedEmbed.setImage(await util.uploadImageToImgur(calculated.split(',')[1])); + decodedEmbed.setImage(await uploadImageToImgur(calculated.split(',')[1])); decodedEmbed.addFields([{ name: '📤 Output', value: '' }]); } else { - decodedEmbed.addFields([{ name: '📤 Output', value: await util.inspectCleanRedactCodeblock(calculated.toString()) }]); + decodedEmbed.addFields([{ name: '📤 Output', value: await inspectCleanRedactCodeblock(calculated.toString()) }]); } } catch (error) { decodedEmbed - .setTitle(`${util.emojis.errorFull} Unable to Query Expression`) - .setColor(util.colors.error) - .addFields([ - { name: `📤 Error`, value: await util.inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js') } - ]); + .setTitle(`${emojis.errorFull} Unable to Query Expression`) + .setColor(colors.error) + .addFields([{ name: `📤 Error`, value: await 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 bdb311f..7f3103d 100644 --- a/src/context-menu-commands/user/modlog.ts +++ b/src/context-menu-commands/user/modlog.ts @@ -1,5 +1,5 @@ import { ModlogCommand } from '#commands'; -import { SlashMessage } from '#lib'; +import { emojis, SlashMessage } from '#lib'; import { CommandUtil, ContextMenuCommand } from 'discord-akairo'; import { ApplicationCommandType, type ContextMenuCommandInteraction } from 'discord.js'; @@ -15,12 +15,12 @@ export default class ModlogContextMenuCommand extends ContextMenuCommand { public override async exec(interaction: ContextMenuCommandInteraction) { if (!interaction.inCachedGuild()) return interaction.reply({ - content: `${util.emojis.error} You can't use this command outside of a server.`, + content: `${emojis.error} You can't use this command outside of a server.`, ephemeral: true }); if (!interaction.member?.permissions.has('ManageMessages')) return interaction.reply({ - content: `${util.emojis.error} You can't use this command because you have the **Manage Messages** permission.`, + content: `${emojis.error} You can't use this command because you have the **Manage Messages** permission.`, ephemeral: true }); diff --git a/src/context-menu-commands/user/userInfo.ts b/src/context-menu-commands/user/userInfo.ts index 80e9883..075b681 100644 --- a/src/context-menu-commands/user/userInfo.ts +++ b/src/context-menu-commands/user/userInfo.ts @@ -1,4 +1,5 @@ import { UserInfoCommand } from '#commands'; +import { format } from '#lib'; import { ContextMenuCommand } from 'discord-akairo'; import { ApplicationCommandType, type ContextMenuCommandInteraction, type Guild } from 'discord.js'; @@ -20,7 +21,7 @@ export default class UserInfoContextMenuCommand extends ContextMenuCommand { const guild = interaction.guild as Guild; const member = await guild.members.fetch(interaction.targetId).catch(() => null); - if (!member) return interaction.reply(`${util.format.input(user.tag)} doesn't appear to be a member of this server anymore.`); + if (!member) return interaction.reply(`${format.input(user.tag)} doesn't appear to be a member of this server anymore.`); const userEmbed = await UserInfoCommand.makeUserInfoEmbed(user, member, guild); diff --git a/src/inhibitors/blacklist/channelGlobalBlacklist.ts b/src/inhibitors/blacklist/channelGlobalBlacklist.ts index 1ac26f1..86ab21f 100644 --- a/src/inhibitors/blacklist/channelGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/channelGlobalBlacklist.ts @@ -10,7 +10,7 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { }); } - public override exec(message: CommandMessage | SlashMessage, command: BushCommand): boolean { + 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; diff --git a/src/inhibitors/blacklist/channelGuildBlacklist.ts b/src/inhibitors/blacklist/channelGuildBlacklist.ts index a634015..8cf35ec 100644 --- a/src/inhibitors/blacklist/channelGuildBlacklist.ts +++ b/src/inhibitors/blacklist/channelGuildBlacklist.ts @@ -10,7 +10,7 @@ export default class ChannelGuildBlacklistInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + 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; diff --git a/src/inhibitors/blacklist/guildBlacklist.ts b/src/inhibitors/blacklist/guildBlacklist.ts index 087bdb7..60c5755 100644 --- a/src/inhibitors/blacklist/guildBlacklist.ts +++ b/src/inhibitors/blacklist/guildBlacklist.ts @@ -10,7 +10,7 @@ export default class GuildBlacklistInhibitor extends BushInhibitor { }); } - public override exec(message: CommandMessage | SlashMessage): boolean { + 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) diff --git a/src/inhibitors/blacklist/userGlobalBlacklist.ts b/src/inhibitors/blacklist/userGlobalBlacklist.ts index ae488c8..bf0d4bd 100644 --- a/src/inhibitors/blacklist/userGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/userGlobalBlacklist.ts @@ -10,7 +10,7 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { }); } - public override exec(message: CommandMessage | SlashMessage): boolean { + 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; diff --git a/src/inhibitors/blacklist/userGuildBlacklist.ts b/src/inhibitors/blacklist/userGuildBlacklist.ts index c4fa527..a2ac1aa 100644 --- a/src/inhibitors/blacklist/userGuildBlacklist.ts +++ b/src/inhibitors/blacklist/userGuildBlacklist.ts @@ -10,7 +10,7 @@ export default class UserGuildBlacklistInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage): Promise<boolean> { + 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) diff --git a/src/inhibitors/checks/fatal.ts b/src/inhibitors/checks/fatal.ts index 520754a..a3601b5 100644 --- a/src/inhibitors/checks/fatal.ts +++ b/src/inhibitors/checks/fatal.ts @@ -11,7 +11,7 @@ export default class FatalInhibitor extends BushInhibitor { }); } - public override async exec(message: Message | SlashMessage): Promise<boolean> { + 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]) { diff --git a/src/inhibitors/checks/guildUnavailable.ts b/src/inhibitors/checks/guildUnavailable.ts index 45ec0da..6c741a1 100644 --- a/src/inhibitors/checks/guildUnavailable.ts +++ b/src/inhibitors/checks/guildUnavailable.ts @@ -11,7 +11,7 @@ export default class GuildUnavailableInhibitor extends BushInhibitor { }); } - public override async exec(message: Message | SlashMessage): Promise<boolean> { + public async exec(message: Message | SlashMessage): Promise<boolean> { if (message.inGuild() && !message.guild.available) { void client.console.verbose( 'guildUnavailable', diff --git a/src/inhibitors/command/dm.ts b/src/inhibitors/command/dm.ts index d6c13a9..1fc43c6 100644 --- a/src/inhibitors/command/dm.ts +++ b/src/inhibitors/command/dm.ts @@ -10,7 +10,7 @@ export default class DMInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.channel === 'dm' && message.guild) { void client.console.verbose( 'dm', diff --git a/src/inhibitors/command/globalDisabledCommand.ts b/src/inhibitors/command/globalDisabledCommand.ts index ada9716..7e855ae 100644 --- a/src/inhibitors/command/globalDisabledCommand.ts +++ b/src/inhibitors/command/globalDisabledCommand.ts @@ -10,7 +10,7 @@ export default class DisabledGuildCommandInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + 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( diff --git a/src/inhibitors/command/guild.ts b/src/inhibitors/command/guild.ts index 8b249ff..6ea4e6a 100644 --- a/src/inhibitors/command/guild.ts +++ b/src/inhibitors/command/guild.ts @@ -10,7 +10,7 @@ export default class GuildInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.channel === 'guild' && !message.guild) { void client.console.verbose( 'guild', diff --git a/src/inhibitors/command/guildDisabledCommand.ts b/src/inhibitors/command/guildDisabledCommand.ts index a97f5cc..7d63093 100644 --- a/src/inhibitors/command/guildDisabledCommand.ts +++ b/src/inhibitors/command/guildDisabledCommand.ts @@ -10,7 +10,7 @@ export default class DisabledGuildCommandInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (!message.guild || !message.guild) return false; if (message.author.isOwner() || message.author.isSuperUser()) return false; // super users bypass guild disabled commands diff --git a/src/inhibitors/command/nsfw.ts b/src/inhibitors/command/nsfw.ts index 6eb2878..f8f2180 100644 --- a/src/inhibitors/command/nsfw.ts +++ b/src/inhibitors/command/nsfw.ts @@ -11,7 +11,7 @@ export default class NsfwInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.onlyNsfw && !(message.channel as TextChannel).nsfw) { void client.console.verbose( 'notNsfw', diff --git a/src/inhibitors/command/owner.ts b/src/inhibitors/command/owner.ts index 2331e04..3769d84 100644 --- a/src/inhibitors/command/owner.ts +++ b/src/inhibitors/command/owner.ts @@ -10,7 +10,7 @@ export default class OwnerInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.ownerOnly) { if (!client.isOwner(message.author)) { void client.console.verbose( diff --git a/src/inhibitors/command/restrictedChannel.ts b/src/inhibitors/command/restrictedChannel.ts index 6b06f95..867756d 100644 --- a/src/inhibitors/command/restrictedChannel.ts +++ b/src/inhibitors/command/restrictedChannel.ts @@ -10,7 +10,7 @@ export default class RestrictedChannelInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + 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( diff --git a/src/inhibitors/command/restrictedGuild.ts b/src/inhibitors/command/restrictedGuild.ts index 47c61ce..4d43c21 100644 --- a/src/inhibitors/command/restrictedGuild.ts +++ b/src/inhibitors/command/restrictedGuild.ts @@ -10,7 +10,7 @@ export default class RestrictedGuildInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + 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( diff --git a/src/inhibitors/command/superUser.ts b/src/inhibitors/command/superUser.ts index 23b1c64..2e44b67 100644 --- a/src/inhibitors/command/superUser.ts +++ b/src/inhibitors/command/superUser.ts @@ -10,7 +10,7 @@ export default class SuperUserInhibitor extends BushInhibitor { }); } - public override async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { + public async exec(message: CommandMessage | SlashMessage, command: BushCommand): Promise<boolean> { if (command.superUserOnly) { if (!client.isSuperUser(message.author)) { void client.console.verbose( diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts index 982e0e8..7f19e63 100644 --- a/src/lib/common/AutoMod.ts +++ b/src/lib/common/AutoMod.ts @@ -1,4 +1,4 @@ -import { banResponse, Moderation } from '#lib'; +import { banResponse, codeblock, colors, emojis, format, formatError, getShared, Moderation, resolveNonCachedUser } from '#lib'; import assert from 'assert'; import chalk from 'chalk'; import { @@ -18,11 +18,6 @@ import { */ export class AutoMod { /** - * The message to check for blacklisted phrases on - */ - private message: Message; - - /** * Whether or not a punishment has already been given to the user */ private punished = false; @@ -30,8 +25,12 @@ export class AutoMod { /** * @param message The message to check and potentially perform automod actions to */ - public constructor(message: Message) { - this.message = message; + public constructor( + /** + * The message to check for blacklisted phrases on + */ + private message: Message + ) { if (message.author.id === client.user?.id) return; void this.handle(); } @@ -57,9 +56,9 @@ export class AutoMod { traditional: { if (this.isImmune) break traditional; - const badLinksArray = util.getShared('badLinks'); - const badLinksSecretArray = util.getShared('badLinksSecret'); - const badWordsRaw = util.getShared('badWords'); + const badLinksArray = getShared('badLinks'); + const badLinksSecretArray = getShared('badLinksSecret'); + const badWordsRaw = getShared('badWords'); const customAutomodPhrases = (await this.message.guild.getSetting('autoModPhases')) ?? []; const uniqueLinks = [...new Set([...badLinksArray, ...badLinksSecretArray])]; @@ -90,8 +89,8 @@ export class AutoMod { embeds: [ { title: 'AutoMod Error', - description: `Unable to find severity information for ${util.format.inlineCode(highestOffence.match)}`, - color: util.colors.error + description: `Unable to find severity information for ${format.inlineCode(highestOffence.match)}`, + color: colors.error } ] }); @@ -168,7 +167,7 @@ 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 util.codeblock(this.message.content, 1024)}` }]) + .addFields([{ name: 'Message Content', value: `${await codeblock(this.message.content, 1024)}` }]) .setColor(color) .setTimestamp() ], @@ -252,13 +251,13 @@ export class AutoMod { let color; switch (highestOffence.severity) { case Severity.DELETE: { - color = util.colors.lightGray; + color = colors.lightGray; void this.message.delete().catch((e) => deleteError.bind(this, e)); this.punished = true; break; } case Severity.WARN: { - color = util.colors.yellow; + color = colors.yellow; void this.message.delete().catch((e) => deleteError.bind(this, e)); void this.message.member?.bushWarn({ moderator: this.message.guild!.members.me!, @@ -268,7 +267,7 @@ export class AutoMod { break; } case Severity.TEMP_MUTE: { - color = util.colors.orange; + color = colors.orange; void this.message.delete().catch((e) => deleteError.bind(this, e)); void this.message.member?.bushMute({ moderator: this.message.guild!.members.me!, @@ -279,7 +278,7 @@ export class AutoMod { break; } case Severity.PERM_MUTE: { - color = util.colors.red; + color = colors.red; void this.message.delete().catch((e) => deleteError.bind(this, e)); void this.message.member?.bushMute({ moderator: this.message.guild!.members.me!, @@ -302,8 +301,8 @@ export class AutoMod { { title: 'AutoMod Error', description: `Unable to delete triggered message.`, - fields: [{ name: 'Error', value: await util.codeblock(`${util.formatError(e)}`, 1024, 'js', true) }], - color: util.colors.error + fields: [{ name: 'Error', value: await codeblock(`${formatError(e)}`, 1024, 'js', true) }], + color: colors.error } ] }); @@ -333,7 +332,7 @@ 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 util.codeblock(this.message.content, 1024)}` }]) + .addFields([{ name: 'Message Content', value: `${await codeblock(this.message.content, 1024)}` }]) .setColor(color) .setTimestamp() .setAuthor({ name: this.message.author.tag, url: this.message.author.displayAvatarURL() }) @@ -360,7 +359,7 @@ export class AutoMod { public static async handleInteraction(interaction: ButtonInteraction) { if (!interaction.memberPermissions?.has(PermissionFlagsBits.BanMembers)) return interaction.reply({ - content: `${util.emojis.error} You are missing the **Ban Members** permission.`, + content: `${emojis.error} You are missing the **Ban Members** permission.`, ephemeral: true }); const [action, userId, reason] = interaction.customId.replace('automod;', '').split(';'); @@ -387,20 +386,20 @@ export class AutoMod { evidence: (interaction.message as Message).url ?? undefined }); - const victimUserFormatted = (await util.resolveNonCachedUser(userId))?.tag ?? userId; + const victimUserFormatted = (await resolveNonCachedUser(userId))?.tag ?? userId; if (result === banResponse.SUCCESS) return interaction.reply({ - content: `${util.emojis.success} Successfully banned **${victimUserFormatted}**.`, + content: `${emojis.success} Successfully banned **${victimUserFormatted}**.`, ephemeral: true }); else if (result === banResponse.DM_ERROR) return interaction.reply({ - content: `${util.emojis.warn} Banned ${victimUserFormatted} however I could not send them a dm.`, + content: `${emojis.warn} Banned ${victimUserFormatted} however I could not send them a dm.`, ephemeral: true }); else return interaction.reply({ - content: `${util.emojis.error} Could not ban **${victimUserFormatted}**: \`${result}\` .`, + content: `${emojis.error} Could not ban **${victimUserFormatted}**: \`${result}\` .`, ephemeral: true }); } diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts index 64870cf..9560247 100644 --- a/src/lib/common/ButtonPaginator.ts +++ b/src/lib/common/ButtonPaginator.ts @@ -15,26 +15,6 @@ import { */ export class ButtonPaginator { /** - * The message that triggered the command - */ - protected message: CommandMessage | SlashMessage; - - /** - * The embeds to paginate - */ - protected embeds: EmbedBuilder[] | APIEmbed[]; - - /** - * The optional text to send with the paginator - */ - protected text: string | null; - - /** - * Whether the paginator message gets deleted when the exit button is pressed - */ - protected deleteOnExit: boolean; - - /** * The current page of the paginator */ protected curPage: number; @@ -52,16 +32,27 @@ export class ButtonPaginator { * @param startOn The page to start from (**not** the index) */ protected constructor( - message: CommandMessage | SlashMessage, - embeds: EmbedBuilder[] | APIEmbed[], - text: string | null, - deleteOnExit: boolean, + /** + * The message that triggered the command + */ + protected message: CommandMessage | SlashMessage, + + /** + * The embeds to paginate + */ + protected embeds: EmbedBuilder[] | APIEmbed[], + + /** + * The optional text to send with the paginator + */ + protected text: string | null, + + /** + * Whether the paginator message gets deleted when the exit button is pressed + */ + protected deleteOnExit: boolean, startOn: number ) { - this.message = message; - this.embeds = embeds; - this.text = text ? text : null; - this.deleteOnExit = deleteOnExit; this.curPage = startOn - 1; // add footers diff --git a/src/lib/common/ConfirmationPrompt.ts b/src/lib/common/ConfirmationPrompt.ts index c95dbbc..4593d24 100644 --- a/src/lib/common/ConfirmationPrompt.ts +++ b/src/lib/common/ConfirmationPrompt.ts @@ -6,23 +6,20 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type MessageComponentInte */ export class ConfirmationPrompt { /** - * Options for sending the message - */ - protected messageOptions: MessageOptions; - - /** - * The message that triggered the command - */ - protected message: CommandMessage | SlashMessage; - - /** * @param message The message to respond to - * @param options The send message options + * @param messageOptions The send message options */ - protected constructor(message: CommandMessage | SlashMessage, messageOptions: MessageOptions) { - this.message = message; - this.messageOptions = messageOptions; - } + protected constructor( + /** + * The message that triggered the command + */ + protected message: CommandMessage | SlashMessage, + + /** + * Options for sending the message + */ + protected messageOptions: MessageOptions + ) {} /** * Sends a message with buttons for the user to confirm or cancel the action. diff --git a/src/lib/common/DeleteButton.ts b/src/lib/common/DeleteButton.ts index 91f4bfa..b561d94 100644 --- a/src/lib/common/DeleteButton.ts +++ b/src/lib/common/DeleteButton.ts @@ -15,23 +15,20 @@ import { */ export class DeleteButton { /** - * Options for sending the message - */ - protected messageOptions: MessageOptions; - - /** - * The message that triggered the command - */ - protected message: CommandMessage | SlashMessage; - - /** * @param message The message to respond to - * @param options The send message options + * @param messageOptions The send message options */ - protected constructor(message: CommandMessage | SlashMessage, options: MessageOptions) { - this.message = message; - this.messageOptions = options; - } + protected constructor( + /** + * The message that triggered the command + */ + protected message: CommandMessage | SlashMessage, + + /** + * Options for sending the message + */ + protected messageOptions: MessageOptions + ) {} /** * Sends a message with a button for the user to delete it. diff --git a/src/lib/common/HighlightManager.ts b/src/lib/common/HighlightManager.ts index fdec322..caaa6a5 100644 --- a/src/lib/common/HighlightManager.ts +++ b/src/lib/common/HighlightManager.ts @@ -1,7 +1,7 @@ -import { Highlight, type HighlightWord } from '#lib'; +import { addToArray, format, Highlight, removeFromArray, timestamp, type HighlightWord } from '#lib'; import assert from 'assert'; import { Collection, type Message, type Snowflake } from 'discord.js'; -import { Time } from '../utils/BushConstants.js'; +import { colors, Time } from '../utils/BushConstants.js'; const NOTIFY_COOLDOWN = 5 * Time.Minute; const OWNER_NOTIFY_COOLDOWN = 1 * Time.Minute; @@ -162,7 +162,7 @@ export class HighlightManager { if (highlight.words.some((w) => w.word === hl.word)) return `You have already highlighted "${hl.word}".`; - highlight.words = util.addToArray(highlight.words, hl); + highlight.words = addToArray(highlight.words, hl); return Boolean(await highlight.save().catch(() => false)); } @@ -189,7 +189,7 @@ export class HighlightManager { const toRemove = highlight.words.find((w) => w.word === hl); if (!toRemove) return `Uhhhhh... This shouldn't happen.`; - highlight.words = util.removeFromArray(highlight.words, toRemove); + highlight.words = removeFromArray(highlight.words, toRemove); return Boolean(await highlight.save().catch(() => false)); } @@ -271,20 +271,18 @@ export class HighlightManager { return client.users .send(user, { // eslint-disable-next-line @typescript-eslint/no-base-to-string - content: `In ${util.format.input(message.guild.name)} ${message.channel}, your highlight "${hl.word}" was matched:`, + content: `In ${format.input(message.guild.name)} ${message.channel}, your highlight "${hl.word}" was matched:`, embeds: [ { description: [...recentMessages, message] .map( (m) => - `${util.timestamp(m.createdAt, 't')} ${util.format.input(`${m.author.tag}:`)} ${m.cleanContent - .trim() - .substring(0, 512)}` + `${timestamp(m.createdAt, 't')} ${format.input(`${m.author.tag}:`)} ${m.cleanContent.trim().substring(0, 512)}` ) .join('\n'), author: { name: hl.regex ? `/${hl.word}/gi` : hl.word }, fields: [{ name: 'Source message', value: `[Jump to message](${message.url})` }], - color: util.colors.default, + color: colors.default, footer: { text: 'Triggered' }, timestamp: message.createdAt.toISOString() } diff --git a/src/lib/common/Sentry.ts b/src/lib/common/Sentry.ts index e18555b..34bc06f 100644 --- a/src/lib/common/Sentry.ts +++ b/src/lib/common/Sentry.ts @@ -1,7 +1,7 @@ import { RewriteFrames } from '@sentry/integrations'; import * as SentryNode from '@sentry/node'; import { Integrations } from '@sentry/node'; -import config from './../../config/options.js'; +import config from '../../../config/options.js'; export class Sentry { public constructor(rootdir: string) { diff --git a/src/lib/common/util/Arg.ts b/src/lib/common/util/Arg.ts index 51d8065..a7795b1 100644 --- a/src/lib/common/util/Arg.ts +++ b/src/lib/common/util/Arg.ts @@ -9,155 +9,150 @@ import { Argument, type Flag, type ParsedValuePredicate } from 'discord-akairo'; import { type Message } from 'discord.js'; /** - * A wrapper for the {@link Argument} class that adds custom typings. + * Casts a phrase to this argument's type. + * @param type - The type to cast to. + * @param message - Message that called the command. + * @param phrase - Phrase to process. */ -export class Arg { - /** - * Casts a phrase to this argument's type. - * @param type - The type to cast to. - * @param message - Message that called the command. - * @param phrase - Phrase to process. - */ - public static async cast<T extends ATC>(type: T, message: CommandMessage | SlashMessage, phrase: string): Promise<ATCR<T>>; - public static async cast<T extends KBAT>(type: T, message: CommandMessage | SlashMessage, phrase: string): Promise<BAT[T]>; - public static async cast(type: AT | ATC, message: CommandMessage | SlashMessage, phrase: string): Promise<any>; - public static async cast(type: ATC | AT, message: CommandMessage | SlashMessage, phrase: string): Promise<any> { - return Argument.cast(type as any, client.commandHandler.resolver, message as Message, phrase); - } +export async function cast<T extends ATC>(type: T, message: CommandMessage | SlashMessage, phrase: string): Promise<ATCR<T>>; +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); +} - /** - * Creates a type that is the left-to-right composition of the given types. - * If any of the types fails, the entire composition fails. - * @param types - Types to use. - */ - public static compose<T extends ATC>(...types: T[]): ATCATCR<T>; - public static compose<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static compose(...types: (AT | ATC)[]): ATC; - public static compose(...types: (AT | ATC)[]): ATC { - return Argument.compose(...(types as any)); - } +/** + * Creates a type that is the left-to-right composition of the given types. + * If any of the types fails, the entire composition fails. + * @param types - Types to use. + */ +export function compose<T extends ATC>(...types: T[]): ATCATCR<T>; +export function compose<T extends KBAT>(...types: T[]): ATCBAT<T>; +export function compose(...types: (AT | ATC)[]): ATC; +export function compose(...types: (AT | ATC)[]): ATC { + return Argument.compose(...(types as any)); +} - /** - * Creates a type that is the left-to-right composition of the given types. - * If any of the types fails, the composition still continues with the failure passed on. - * @param types - Types to use. - */ - public static composeWithFailure<T extends ATC>(...types: T[]): ATCATCR<T>; - public static composeWithFailure<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static composeWithFailure(...types: (AT | ATC)[]): ATC; - public static composeWithFailure(...types: (AT | ATC)[]): ATC { - return Argument.composeWithFailure(...(types as any)); - } +/** + * Creates a type that is the left-to-right composition of the given types. + * If any of the types fails, the composition still continues with the failure passed on. + * @param types - Types to use. + */ +export function composeWithFailure<T extends ATC>(...types: T[]): ATCATCR<T>; +export function composeWithFailure<T extends KBAT>(...types: T[]): ATCBAT<T>; +export function composeWithFailure(...types: (AT | ATC)[]): ATC; +export function composeWithFailure(...types: (AT | ATC)[]): ATC { + return Argument.composeWithFailure(...(types as any)); +} - /** - * Checks if something is null, undefined, or a fail flag. - * @param value - Value to check. - */ - public static isFailure(value: any): value is null | undefined | (Flag & { value: any }) { - return Argument.isFailure(value); - } +/** + * Checks if something is null, undefined, or a fail flag. + * @param value - Value to check. + */ +export function isFailure(value: any): value is null | undefined | (Flag & { value: any }) { + return Argument.isFailure(value); +} - /** - * Creates a type from multiple types (product type). - * Only inputs where each type resolves with a non-void value are valid. - * @param types - Types to use. - */ - public static product<T extends ATC>(...types: T[]): ATCATCR<T>; - public static product<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static product(...types: (AT | ATC)[]): ATC; - public static product(...types: (AT | ATC)[]): ATC { - return Argument.product(...(types as any)); - } +/** + * Creates a type from multiple types (product type). + * Only inputs where each type resolves with a non-void value are valid. + * @param types - Types to use. + */ +export function product<T extends ATC>(...types: T[]): ATCATCR<T>; +export function product<T extends KBAT>(...types: T[]): ATCBAT<T>; +export function product(...types: (AT | ATC)[]): ATC; +export function product(...types: (AT | ATC)[]): ATC { + return Argument.product(...(types as any)); +} - /** - * Creates a type where the parsed value must be within a range. - * @param type - The type to use. - * @param min - Minimum value. - * @param max - Maximum value. - * @param inclusive - Whether or not to be inclusive on the upper bound. - */ - public static range<T extends ATC>(type: T, min: number, max: number, inclusive?: boolean): ATCATCR<T>; - public static range<T extends KBAT>(type: T, min: number, max: number, inclusive?: boolean): ATCBAT<T>; - public static range(type: AT | ATC, min: number, max: number, inclusive?: boolean): ATC; - public static range(type: AT | ATC, min: number, max: number, inclusive?: boolean): ATC { - return Argument.range(type as any, min, max, inclusive); - } +/** + * Creates a type where the parsed value must be within a range. + * @param type - The type to use. + * @param min - Minimum value. + * @param max - Maximum value. + * @param inclusive - Whether or not to be inclusive on the upper bound. + */ +export function range<T extends ATC>(type: T, min: number, max: number, inclusive?: boolean): ATCATCR<T>; +export function range<T extends KBAT>(type: T, min: number, max: number, inclusive?: boolean): ATCBAT<T>; +export function range(type: AT | ATC, min: number, max: number, inclusive?: boolean): ATC; +export function range(type: AT | ATC, min: number, max: number, inclusive?: boolean): ATC { + return Argument.range(type as any, min, max, inclusive); +} - /** - * Creates a type that parses as normal but also tags it with some data. - * Result is in an object `{ tag, value }` and wrapped in `Flag.fail` when failed. - * @param type - The type to use. - * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string. - */ - public static tagged<T extends ATC>(type: T, tag?: any): ATCATCR<T>; - public static tagged<T extends KBAT>(type: T, tag?: any): ATCBAT<T>; - public static tagged(type: AT | ATC, tag?: any): ATC; - public static tagged(type: AT | ATC, tag?: any): ATC { - return Argument.tagged(type as any, tag); - } +/** + * Creates a type that parses as normal but also tags it with some data. + * Result is in an object `{ tag, value }` and wrapped in `Flag.fail` when failed. + * @param type - The type to use. + * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string. + */ +export function tagged<T extends ATC>(type: T, tag?: any): ATCATCR<T>; +export function tagged<T extends KBAT>(type: T, tag?: any): ATCBAT<T>; +export function tagged(type: AT | ATC, tag?: any): ATC; +export function tagged(type: AT | ATC, tag?: any): ATC { + return Argument.tagged(type as any, tag); +} - /** - * Creates a type from multiple types (union type). - * The first type that resolves to a non-void value is used. - * Each type will also be tagged using `tagged` with themselves. - * @param types - Types to use. - */ - public static taggedUnion<T extends ATC>(...types: T[]): ATCATCR<T>; - public static taggedUnion<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static taggedUnion(...types: (AT | ATC)[]): ATC; - public static taggedUnion(...types: (AT | ATC)[]): ATC { - return Argument.taggedUnion(...(types as any)); - } +/** + * Creates a type from multiple types (union type). + * The first type that resolves to a non-void value is used. + * Each type will also be tagged using `tagged` with themselves. + * @param types - Types to use. + */ +export function taggedUnion<T extends ATC>(...types: T[]): ATCATCR<T>; +export function taggedUnion<T extends KBAT>(...types: T[]): ATCBAT<T>; +export function taggedUnion(...types: (AT | ATC)[]): ATC; +export function taggedUnion(...types: (AT | ATC)[]): ATC { + return Argument.taggedUnion(...(types as any)); +} - /** - * Creates a type that parses as normal but also tags it with some data and carries the original input. - * Result is in an object `{ tag, input, value }` and wrapped in `Flag.fail` when failed. - * @param type - The type to use. - * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string. - */ - public static taggedWithInput<T extends ATC>(type: T, tag?: any): ATCATCR<T>; - public static taggedWithInput<T extends KBAT>(type: T, tag?: any): ATCBAT<T>; - public static taggedWithInput(type: AT | ATC, tag?: any): ATC; - public static taggedWithInput(type: AT | ATC, tag?: any): ATC { - return Argument.taggedWithInput(type as any, tag); - } +/** + * Creates a type that parses as normal but also tags it with some data and carries the original input. + * Result is in an object `{ tag, input, value }` and wrapped in `Flag.fail` when failed. + * @param type - The type to use. + * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string. + */ +export function taggedWithInput<T extends ATC>(type: T, tag?: any): ATCATCR<T>; +export function taggedWithInput<T extends KBAT>(type: T, tag?: any): ATCBAT<T>; +export function taggedWithInput(type: AT | ATC, tag?: any): ATC; +export function taggedWithInput(type: AT | ATC, tag?: any): ATC { + return Argument.taggedWithInput(type as any, tag); +} - /** - * Creates a type from multiple types (union type). - * The first type that resolves to a non-void value is used. - * @param types - Types to use. - */ - public static union<T extends ATC>(...types: T[]): ATCATCR<T>; - public static union<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static union(...types: (AT | ATC)[]): ATC; - public static union(...types: (AT | ATC)[]): ATC { - return Argument.union(...(types as any)); - } +/** + * Creates a type from multiple types (union type). + * The first type that resolves to a non-void value is used. + * @param types - Types to use. + */ +export function union<T extends ATC>(...types: T[]): ATCATCR<T>; +export function union<T extends KBAT>(...types: T[]): ATCBAT<T>; +export function union(...types: (AT | ATC)[]): ATC; +export function union(...types: (AT | ATC)[]): ATC { + return Argument.union(...(types as any)); +} - /** - * Creates a type with extra validation. - * If the predicate is not true, the value is considered invalid. - * @param type - The type to use. - * @param predicate - The predicate function. - */ - public static validate<T extends ATC>(type: T, predicate: ParsedValuePredicate): ATCATCR<T>; - public static validate<T extends KBAT>(type: T, predicate: ParsedValuePredicate): ATCBAT<T>; - public static validate(type: AT | ATC, predicate: ParsedValuePredicate): ATC; - public static validate(type: AT | ATC, predicate: ParsedValuePredicate): ATC { - return Argument.validate(type as any, predicate); - } +/** + * Creates a type with extra validation. + * If the predicate is not true, the value is considered invalid. + * @param type - The type to use. + * @param predicate - The predicate function. + */ +export function validate<T extends ATC>(type: T, predicate: ParsedValuePredicate): ATCATCR<T>; +export function validate<T extends KBAT>(type: T, predicate: ParsedValuePredicate): ATCBAT<T>; +export function validate(type: AT | ATC, predicate: ParsedValuePredicate): ATC; +export function validate(type: AT | ATC, predicate: ParsedValuePredicate): ATC { + return Argument.validate(type as any, predicate); +} - /** - * Creates a type that parses as normal but also carries the original input. - * Result is in an object `{ input, value }` and wrapped in `Flag.fail` when failed. - * @param type - The type to use. - */ - public static withInput<T extends ATC>(type: T): ATC<ATCR<T>>; - public static withInput<T extends KBAT>(type: T): ATCBAT<T>; - public static withInput(type: AT | ATC): ATC; - public static withInput(type: AT | ATC): ATC { - return Argument.withInput(type as any); - } +/** + * Creates a type that parses as normal but also carries the original input. + * Result is in an object `{ input, value }` and wrapped in `Flag.fail` when failed. + * @param type - The type to use. + */ +export function withInput<T extends ATC>(type: T): ATC<ATCR<T>>; +export function withInput<T extends KBAT>(type: T): ATCBAT<T>; +export function withInput(type: AT | ATC): ATC; +export function withInput(type: AT | ATC): ATC { + return Argument.withInput(type as any); } type BushArgumentTypeCasterReturn<R> = R extends BushArgumentTypeCaster<infer S> ? S : R; diff --git a/src/lib/common/util/Format.ts b/src/lib/common/util/Format.ts index 6cb6edc..260a0be 100644 --- a/src/lib/common/util/Format.ts +++ b/src/lib/common/util/Format.ts @@ -1,107 +1,112 @@ import { type CodeBlockLang } from '#lib'; -import { EscapeMarkdownOptions, Formatters, Util } from 'discord.js'; +import { + escapeBold, + escapeCodeBlock, + escapeInlineCode, + escapeItalic, + EscapeMarkdownOptions, + escapeSpoiler, + escapeStrikethrough, + escapeUnderline, + Formatters +} from 'discord.js'; /** - * Formats and escapes content for formatting + * Wraps the content inside a codeblock with no language. + * @param content The content to wrap. */ -export class Format { - /** - * Wraps the content inside a codeblock with no language. - * @param content The content to wrap. - */ - public static codeBlock(content: string): string; +export function codeBlock(content: string): string; - /** - * Wraps the content inside a codeblock with the specified language. - * @param language The language for the codeblock. - * @param content The content to wrap. - */ - public static codeBlock(language: CodeBlockLang, content: string): string; - public static codeBlock(languageOrContent: string, content?: string): string { - return typeof content === 'undefined' - ? Formatters.codeBlock(Util.escapeCodeBlock(`${languageOrContent}`)) - : Formatters.codeBlock(`${languageOrContent}`, Util.escapeCodeBlock(`${content}`)); - } +/** + * Wraps the content inside a codeblock with the specified language. + * @param language The language for the codeblock. + * @param content The content to wrap. + */ +export function codeBlock(language: CodeBlockLang, content: string): string; +export function codeBlock(languageOrContent: string, content?: string): string { + return typeof content === 'undefined' + ? Formatters.codeBlock(escapeCodeBlock(`${languageOrContent}`)) + : Formatters.codeBlock(`${languageOrContent}`, escapeCodeBlock(`${content}`)); +} - /** - * Wraps the content inside \`backticks\`, which formats it as inline code. - * @param content The content to wrap. - */ - public static inlineCode(content: string): string { - return Formatters.inlineCode(Util.escapeInlineCode(`${content}`)); - } +/** + * Wraps the content inside \`backticks\`, which formats it as inline code. + * @param content The content to wrap. + */ +export function inlineCode(content: string): string { + return Formatters.inlineCode(escapeInlineCode(`${content}`)); +} - /** - * Formats the content into italic text. - * @param content The content to wrap. - */ - public static italic(content: string): string { - return Formatters.italic(Util.escapeItalic(`${content}`)); - } +/** + * Formats the content into italic text. + * @param content The content to wrap. + */ +export function italic(content: string): string { + return Formatters.italic(escapeItalic(`${content}`)); +} - /** - * Formats the content into bold text. - * @param content The content to wrap. - */ - public static bold(content: string): string { - return Formatters.bold(Util.escapeBold(`${content}`)); - } +/** + * Formats the content into bold text. + * @param content The content to wrap. + */ +export function bold(content: string): string { + return Formatters.bold(escapeBold(`${content}`)); +} - /** - * Formats the content into underscored text. - * @param content The content to wrap. - */ - public static underscore(content: string): string { - return Formatters.underscore(Util.escapeUnderline(`${content}`)); - } +/** + * Formats the content into underscored text. + * @param content The content to wrap. + */ +export function underscore(content: string): string { + return Formatters.underscore(escapeUnderline(`${content}`)); +} - /** - * Formats the content into strike-through text. - * @param content The content to wrap. - */ - public static strikethrough(content: string): string { - return Formatters.strikethrough(Util.escapeStrikethrough(`${content}`)); - } +/** + * Formats the content into strike-through text. + * @param content The content to wrap. + */ +export function strikethrough(content: string): string { + return Formatters.strikethrough(escapeStrikethrough(`${content}`)); +} - /** - * Wraps the content inside spoiler (hidden text). - * @param content The content to wrap. - */ - public static spoiler(content: string): string { - return Formatters.spoiler(Util.escapeSpoiler(`${content}`)); - } +/** + * Wraps the content inside spoiler (hidden text). + * @param content The content to wrap. + */ +export function spoiler(content: string): string { + return Formatters.spoiler(escapeSpoiler(`${content}`)); +} - /** - * Escapes any Discord-flavour markdown in a string. - * @param text Content to escape - * @param options Options for escaping the markdown - */ - public static escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string { - return Util.escapeMarkdown(`${text}`, options); - } +/** + * Escapes any Discord-flavour markdown in a string. + * @param text Content to escape + * @param options Options for escaping the markdown + */ +export function escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string { + return escapeMarkdown(`${text}`, options); +} - /** - * Formats input: makes it bold and escapes any other markdown - * @param text The input - */ - public static input(text: string): string { - return this.bold(this.escapeMarkdown(this.sanitizeWtlAndControl(`${text}`))); - } +/** + * Formats input: makes it bold and escapes any other markdown + * @param text The input + */ +export function input(text: string): string { + return bold(escapeMarkdown(sanitizeWtlAndControl(`${text}`))); +} - /** - * Formats input for logs: makes it highlighted - * @param text The input - */ - public static inputLog(text: string): string { - return `<<${this.sanitizeWtlAndControl(`${text}`)}>>`; - } +/** + * Formats input for logs: makes it highlighted + * @param text The input + */ +export function inputLog(text: string): string { + return `<<${sanitizeWtlAndControl(`${text}`)}>>`; +} - /** - * Removes all characters in a string that are either control characters or change the direction of text etc. - * @param str The string you would like sanitized - */ - public static sanitizeWtlAndControl(str: string) { - // eslint-disable-next-line no-control-regex - return `${str}`.replace(/[\u0000-\u001F\u007F-\u009F\u200B]/g, ''); - } +/** + * Removes all characters in a string that are either control characters or change the direction of text etc. + * @param str The string you would like sanitized + */ +export function sanitizeWtlAndControl(str: string) { + // eslint-disable-next-line no-control-regex + return `${str}`.replace(/[\u0000-\u001F\u007F-\u009F\u200B]/g, ''); } diff --git a/src/lib/common/util/Moderation.ts b/src/lib/common/util/Moderation.ts index 6cdc141..a08dfa4 100644 --- a/src/lib/common/util/Moderation.ts +++ b/src/lib/common/util/Moderation.ts @@ -1,4 +1,16 @@ -import { ActivePunishment, ActivePunishmentType, Guild as GuildDB, ModLog, type ModLogType } from '#lib'; +import { + ActivePunishment, + ActivePunishmentType, + colors, + emojis, + format, + Guild as GuildDB, + handleError, + humanizeDuration, + ModLog, + resolveNonCachedUser, + type ModLogType +} from '#lib'; import assert from 'assert'; import { ActionRowBuilder, @@ -40,275 +52,270 @@ enum reversedPunishMap { } /** - * A utility class with moderation-related methods. + * Checks if a moderator can perform a moderation action on another user. + * @param moderator The person trying to perform the action. + * @param victim The person getting punished. + * @param type The type of punishment - used to format the response. + * @param checkModerator Whether or not to check if the victim is a moderator. + * @param force Override permissions checks. + * @returns `true` if the moderator can perform the action otherwise a reason why they can't. */ -export class Moderation { - /** - * Checks if a moderator can perform a moderation action on another user. - * @param moderator The person trying to perform the action. - * @param victim The person getting punished. - * @param type The type of punishment - used to format the response. - * @param checkModerator Whether or not to check if the victim is a moderator. - * @param force Override permissions checks. - * @returns `true` if the moderator can perform the action otherwise a reason why they can't. - */ - public static async permissionCheck( - moderator: GuildMember, - victim: GuildMember, - type: - | 'mute' - | 'unmute' - | 'warn' - | 'kick' - | 'ban' - | 'unban' - | 'add a punishment role to' - | 'remove a punishment role from' - | 'block' - | 'unblock' - | 'timeout' - | 'untimeout', - checkModerator = true, - force = false - ): Promise<true | string> { - if (force) return true; - - // If the victim is not in the guild anymore it will be undefined - if ((!victim || !victim.guild) && !['ban', 'unban'].includes(type)) return true; - - if (moderator.guild.id !== victim.guild.id) { - throw new Error('moderator and victim not in same guild'); - } - - const isOwner = moderator.guild.ownerId === moderator.id; - if (moderator.id === victim.id && !type.startsWith('un')) { - return `${util.emojis.error} You cannot ${type} yourself.`; - } - if ( - moderator.roles.highest.position <= victim.roles.highest.position && - !isOwner && - !(type.startsWith('un') && moderator.id === victim.id) - ) { - return `${util.emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as you do.`; - } - if ( - victim.roles.highest.position >= victim.guild.members.me!.roles.highest.position && - !(type.startsWith('un') && moderator.id === victim.id) - ) { - return `${util.emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as I do.`; - } - if ( - checkModerator && - victim.permissions.has(PermissionFlagsBits.ManageMessages) && - !(type.startsWith('un') && moderator.id === victim.id) - ) { - if (await moderator.guild.hasFeature('modsCanPunishMods')) { - return true; - } else { - return `${util.emojis.error} You cannot ${type} **${victim.user.tag}** because they are a moderator.`; - } - } - return true; +export async function permissionCheck( + moderator: GuildMember, + victim: GuildMember, + type: + | 'mute' + | 'unmute' + | 'warn' + | 'kick' + | 'ban' + | 'unban' + | 'add a punishment role to' + | 'remove a punishment role from' + | 'block' + | 'unblock' + | 'timeout' + | 'untimeout', + checkModerator = true, + force = false +): Promise<true | string> { + if (force) return true; + + // If the victim is not in the guild anymore it will be undefined + if ((!victim || !victim.guild) && !['ban', 'unban'].includes(type)) return true; + + if (moderator.guild.id !== victim.guild.id) { + throw new Error('moderator and victim not in same guild'); } - /** - * Creates a modlog entry for a punishment. - * @param options Options for creating a modlog entry. - * @param getCaseNumber Whether or not to get the case number of the entry. - * @returns An object with the modlog and the case number. - */ - public static async createModLogEntry( - options: CreateModLogEntryOptions, - getCaseNumber = false - ): Promise<{ log: ModLog | null; caseNum: number | null }> { - const user = (await util.resolveNonCachedUser(options.user))!.id; - const moderator = (await util.resolveNonCachedUser(options.moderator))!.id; - const guild = client.guilds.resolveId(options.guild)!; - - return this.createModLogEntrySimple( - { - ...options, - user: user, - moderator: moderator, - guild: guild - }, - getCaseNumber - ); + const isOwner = moderator.guild.ownerId === moderator.id; + if (moderator.id === victim.id && !type.startsWith('un')) { + return `${emojis.error} You cannot ${type} yourself.`; } - - /** - * Creates a modlog entry with already resolved ids. - * @param options Options for creating a modlog entry. - * @param getCaseNumber Whether or not to get the case number of the entry. - * @returns An object with the modlog and the case number. - */ - public static async createModLogEntrySimple( - options: SimpleCreateModLogEntryOptions, - getCaseNumber = false - ): Promise<{ log: ModLog | null; caseNum: number | null }> { - // If guild does not exist create it so the modlog can reference a guild. - await GuildDB.findOrCreate({ - where: { id: options.guild }, - defaults: { id: options.guild } - }); - - const modLogEntry = ModLog.build({ - type: options.type, - user: options.user, - moderator: options.moderator, - reason: options.reason, - duration: options.duration ? options.duration : undefined, - guild: options.guild, - pseudo: options.pseudo ?? false, - evidence: options.evidence, - hidden: options.hidden ?? false - }); - const saveResult: ModLog | null = await modLogEntry.save().catch(async (e) => { - await util.handleError('createModLogEntry', e); - return null; - }); - - if (!getCaseNumber) return { log: saveResult, caseNum: null }; - - const caseNum = ( - await ModLog.findAll({ where: { type: options.type, user: options.user, guild: options.guild, hidden: false } }) - )?.length; - return { log: saveResult, caseNum }; + if ( + moderator.roles.highest.position <= victim.roles.highest.position && + !isOwner && + !(type.startsWith('un') && moderator.id === victim.id) + ) { + return `${emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as you do.`; } - - /** - * Creates a punishment entry. - * @param options Options for creating the punishment entry. - * @returns The database entry, or null if no entry is created. - */ - public static async createPunishmentEntry(options: CreatePunishmentEntryOptions): Promise<ActivePunishment | null> { - const expires = options.duration ? new Date(+new Date() + options.duration ?? 0) : undefined; - const user = (await util.resolveNonCachedUser(options.user))!.id; - const guild = client.guilds.resolveId(options.guild)!; - const type = this.findTypeEnum(options.type)!; - - const entry = ActivePunishment.build( - options.extraInfo - ? { user, type, guild, expires, modlog: options.modlog, extraInfo: options.extraInfo } - : { user, type, guild, expires, modlog: options.modlog } - ); - return await entry.save().catch(async (e) => { - await util.handleError('createPunishmentEntry', e); - return null; - }); + if ( + victim.roles.highest.position >= victim.guild.members.me!.roles.highest.position && + !(type.startsWith('un') && moderator.id === victim.id) + ) { + return `${emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as I do.`; } - - /** - * Destroys a punishment entry. - * @param options Options for destroying the punishment entry. - * @returns Whether or not the entry was destroyed. - */ - public static async removePunishmentEntry(options: RemovePunishmentEntryOptions): Promise<boolean> { - const user = await util.resolveNonCachedUser(options.user); - const guild = client.guilds.resolveId(options.guild); - const type = this.findTypeEnum(options.type); - - if (!user || !guild) return false; - - let success = true; - - const entries = await ActivePunishment.findAll({ - // finding all cases of a certain type incase there were duplicates or something - where: options.extraInfo - ? { user: user.id, guild: guild, type, extraInfo: options.extraInfo } - : { user: user.id, guild: guild, type } - }).catch(async (e) => { - await util.handleError('removePunishmentEntry', e); - success = false; - }); - if (entries) { - const promises = entries.map(async (entry) => - entry.destroy().catch(async (e) => { - await util.handleError('removePunishmentEntry', e); - success = false; - }) - ); - - await Promise.all(promises); + if ( + checkModerator && + victim.permissions.has(PermissionFlagsBits.ManageMessages) && + !(type.startsWith('un') && moderator.id === victim.id) + ) { + if (await moderator.guild.hasFeature('modsCanPunishMods')) { + return true; + } else { + return `${emojis.error} You cannot ${type} **${victim.user.tag}** because they are a moderator.`; } - return success; } + return true; +} - /** - * Returns the punishment type enum for the given type. - * @param type The type of the punishment. - * @returns The punishment type enum. - */ - private static findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') { - const typeMap = { - ['mute']: ActivePunishmentType.MUTE, - ['ban']: ActivePunishmentType.BAN, - ['role']: ActivePunishmentType.ROLE, - ['block']: ActivePunishmentType.BLOCK - }; - return typeMap[type]; - } +/** + * Creates a modlog entry for a punishment. + * @param options Options for creating a modlog entry. + * @param getCaseNumber Whether or not to get the case number of the entry. + * @returns An object with the modlog and the case number. + */ +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)!; + + return createModLogEntrySimple( + { + ...options, + user: user, + moderator: moderator, + guild: guild + }, + getCaseNumber + ); +} - public static punishmentToPresentTense(punishment: PunishmentTypeDM): PunishmentTypePresent { - return punishMap[punishment]; - } +/** + * Creates a modlog entry with already resolved ids. + * @param options Options for creating a modlog entry. + * @param getCaseNumber Whether or not to get the case number of the entry. + * @returns An object with the modlog and the case number. + */ +export async function createModLogEntrySimple( + options: SimpleCreateModLogEntryOptions, + getCaseNumber = false +): Promise<{ log: ModLog | null; caseNum: number | null }> { + // If guild does not exist create it so the modlog can reference a guild. + await GuildDB.findOrCreate({ + where: { id: options.guild }, + defaults: { id: options.guild } + }); + + const modLogEntry = ModLog.build({ + type: options.type, + user: options.user, + moderator: options.moderator, + reason: options.reason, + duration: options.duration ? options.duration : undefined, + guild: options.guild, + pseudo: options.pseudo ?? false, + evidence: options.evidence, + hidden: options.hidden ?? false + }); + const saveResult: ModLog | null = await modLogEntry.save().catch(async (e) => { + await handleError('createModLogEntry', e); + return null; + }); + + if (!getCaseNumber) return { log: saveResult, caseNum: null }; + + const caseNum = ( + await ModLog.findAll({ where: { type: options.type, user: options.user, guild: options.guild, hidden: false } }) + )?.length; + return { log: saveResult, caseNum }; +} - public static punishmentToPastTense(punishment: PunishmentTypePresent): PunishmentTypeDM { - return reversedPunishMap[punishment]; - } +/** + * Creates a punishment entry. + * @param options Options for creating the punishment entry. + * @returns The database entry, or null if no entry is created. + */ +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 type = findTypeEnum(options.type)!; + + const entry = ActivePunishment.build( + options.extraInfo + ? { user, type, guild, expires, modlog: options.modlog, extraInfo: options.extraInfo } + : { user, type, guild, expires, modlog: options.modlog } + ); + return await entry.save().catch(async (e) => { + await handleError('createPunishmentEntry', e); + return null; + }); +} - /** - * Notifies the specified user of their punishment. - * @param options Options for notifying the user. - * @returns Whether or not the dm was successfully sent. - */ - public static async punishDM(options: PunishDMOptions): Promise<boolean> { - const ending = await options.guild.getSetting('punishmentEnding'); - const dmEmbed = - ending && ending.length && options.sendFooter - ? new EmbedBuilder().setDescription(ending).setColor(util.colors.newBlurple) - : undefined; - - const appealsEnabled = !!( - (await options.guild.hasFeature('punishmentAppeals')) && (await options.guild.getLogChannel('appeals')) +/** + * Destroys a punishment entry. + * @param options Options for destroying the punishment entry. + * @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 type = findTypeEnum(options.type); + + if (!user || !guild) return false; + + let success = true; + + const entries = await ActivePunishment.findAll({ + // finding all cases of a certain type incase there were duplicates or something + where: options.extraInfo + ? { user: user.id, guild: guild, type, extraInfo: options.extraInfo } + : { user: user.id, guild: guild, type } + }).catch(async (e) => { + await handleError('removePunishmentEntry', e); + success = false; + }); + if (entries) { + const promises = entries.map(async (entry) => + entry.destroy().catch(async (e) => { + await handleError('removePunishmentEntry', e); + success = false; + }) ); - let content = `You have been ${options.punishment} `; - if (options.punishment.includes('blocked')) { - assert(options.channel); - content += `from <#${options.channel}> `; - } - content += `in ${util.format.input(options.guild.name)} `; - if (options.duration !== null && options.duration !== undefined) - content += options.duration ? `for ${util.humanizeDuration(options.duration)} ` : 'permanently '; - const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided'; - content += `for ${util.format.input(reason)}.`; - - let components; - if (appealsEnabled && options.modlog) - components = [ - new ActionRowBuilder<ButtonBuilder>({ - components: [ - new ButtonBuilder({ - customId: `appeal;${this.punishmentToPresentTense(options.punishment)};${options.guild.id};${client.users.resolveId( - options.user - )};${options.modlog}`, - style: ButtonStyle.Primary, - label: 'Appeal' - }).toJSON() - ] - }) - ]; - - const dmSuccess = await client.users - .send(options.user, { - content, - embeds: dmEmbed ? [dmEmbed] : undefined, - components - }) - .catch(() => false); - return !!dmSuccess; + await Promise.all(promises); } + return success; +} + +/** + * Returns the punishment type enum for the given type. + * @param type The type of the punishment. + * @returns The punishment type enum. + */ +function findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') { + const typeMap = { + ['mute']: ActivePunishmentType.MUTE, + ['ban']: ActivePunishmentType.BAN, + ['role']: ActivePunishmentType.ROLE, + ['block']: ActivePunishmentType.BLOCK + }; + return typeMap[type]; +} + +export function punishmentToPresentTense(punishment: PunishmentTypeDM): PunishmentTypePresent { + return punishMap[punishment]; +} + +export function punishmentToPastTense(punishment: PunishmentTypePresent): PunishmentTypeDM { + return reversedPunishMap[punishment]; +} + +/** + * Notifies the specified user of their punishment. + * @param options Options for notifying the user. + * @returns Whether or not the dm was successfully sent. + */ +export async function punishDM(options: PunishDMOptions): Promise<boolean> { + const ending = await options.guild.getSetting('punishmentEnding'); + const dmEmbed = + ending && ending.length && options.sendFooter + ? new EmbedBuilder().setDescription(ending).setColor(colors.newBlurple) + : undefined; + + const appealsEnabled = !!( + (await options.guild.hasFeature('punishmentAppeals')) && (await options.guild.getLogChannel('appeals')) + ); + + let content = `You have been ${options.punishment} `; + if (options.punishment.includes('blocked')) { + assert(options.channel); + content += `from <#${options.channel}> `; + } + content += `in ${format.input(options.guild.name)} `; + if (options.duration !== null && options.duration !== undefined) + content += options.duration ? `for ${humanizeDuration(options.duration)} ` : 'permanently '; + const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided'; + content += `for ${format.input(reason)}.`; + + let components; + if (appealsEnabled && options.modlog) + components = [ + new ActionRowBuilder<ButtonBuilder>({ + components: [ + new ButtonBuilder({ + customId: `appeal;${punishmentToPresentTense(options.punishment)};${options.guild.id};${client.users.resolveId( + options.user + )};${options.modlog}`, + style: ButtonStyle.Primary, + label: 'Appeal' + }).toJSON() + ] + }) + ]; + + const dmSuccess = await client.users + .send(options.user, { + content, + embeds: dmEmbed ? [dmEmbed] : undefined, + components + }) + .catch(() => false); + return !!dmSuccess; } interface BaseCreateModLogEntryOptions { diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index 2644231..b382121 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -10,7 +10,7 @@ import { roleWithDuration, snowflake } from '#args'; -import type { BushClientEvents, Config } from '#lib'; +import { BushClientEvents, emojis, formatError, inspect } from '#lib'; import { patch, type PatchedElements } from '@notenoughupdates/events-intercept'; import * as Sentry from '@sentry/node'; import { @@ -18,7 +18,6 @@ import { ContextMenuCommandHandler, version as akairoVersion, type ArgumentPromptData, - type ClientUtil, type OtherwiseContentSupplier } from 'discord-akairo'; import { @@ -46,6 +45,7 @@ import path from 'path'; import readline from 'readline'; import type { Options as SequelizeOptions, Sequelize as SequelizeType } from 'sequelize'; import { fileURLToPath } from 'url'; +import type { Config } from '../../../../config/Config.js'; import { tinyColor } from '../../../arguments/tinyColor.js'; import UpdateCacheTask from '../../../tasks/updateCache.js'; import UpdateStatsTask from '../../../tasks/updateStats.js'; @@ -63,13 +63,11 @@ 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 { BushConstants } from '../../utils/BushConstants.js'; -import { BushLogger } from '../../utils/BushLogger.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'; import { ExtendedUser } from '../discord.js/ExtendedUser.js'; -import { BushClientUtil } from './BushClientUtil.js'; import { BushCommandHandler } from './BushCommandHandler.js'; import { BushInhibitorHandler } from './BushInhibitorHandler.js'; import { BushListenerHandler } from './BushListenerHandler.js'; @@ -86,10 +84,6 @@ declare module 'discord.js' { * The ID of the superUser(s). */ superUserID: Snowflake | Snowflake[]; - /** - * Utility methods. - */ - util: ClientUtil | BushClientUtil; 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; @@ -128,7 +122,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Ready> { public declare ownerID: Snowflake[]; public declare superUserID: Snowflake[]; - public declare util: BushClientUtil; /** * Whether or not the client is ready. @@ -141,11 +134,6 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re public stats: BushStats = { cpu: undefined, commandsUsed: 0n, slashCommandsUsed: 0n }; /** - * The configuration for the client. - */ - public config: Config; - - /** * The handler for the bot's listeners. */ public listenerHandler: BushListenerHandler; @@ -186,11 +174,6 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re public logger = BushLogger; /** - * Constants for the bot. - */ - public constants = BushConstants; - - /** * Cached global and guild database data. */ public cache = new BushCache(); @@ -213,7 +196,12 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re /** * @param config The configuration for the bot. */ - public constructor(config: Config) { + public constructor( + /** + * The configuration for the client. + */ + public config: Config + ) { super({ ownerID: config.owners, intents: Object.keys(GatewayIntentBits) @@ -233,7 +221,6 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re this.token = config.token as If<Ready, string, string | null>; this.config = config; - this.util = new BushClientUtil(this); /* =-=-= handlers =-=-= */ this.listenerHandler = new BushListenerHandler(this, { @@ -258,7 +245,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re const ending = '\n\n Type **cancel** to cancel the command'; const options = typeof text === 'function' ? await text(message, data) : text; const search = '{error}', - replace = this.consts.emojis.error; + replace = emojis.error; if (typeof options === 'string') return (replaceError ? options.replace(search, replace) : options) + ending; @@ -338,13 +325,6 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re } /** - * Constants for the bot. - */ - public get consts(): typeof BushConstants { - return this.constants; - } - - /** * Extends discord.js structures before the client is instantiated. */ public static extendStructures(): void { @@ -422,11 +402,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re void this.logger.success('startup', `Successfully loaded <<${handlerName}>>.`, false); }) .catch((e) => { - void this.logger.error( - 'startup', - `Unable to load loader <<${handlerName}>> with error:\n${util.formatError(e)}`, - false - ); + void this.logger.error('startup', `Unable to load loader <<${handlerName}>> with error:\n${formatError(e)}`, false); if (process.argv.includes('dry')) process.exit(1); }) ); @@ -451,7 +427,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re } catch (e) { await this.console.error( 'startup', - `Failed to connect to <<instance database>> with error:\n${util.inspect(e, { colors: true, depth: 1 })}`, + `Failed to connect to <<instance database>> with error:\n${inspect(e, { colors: true, depth: 1 })}`, false ); process.exit(2); @@ -471,7 +447,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re } catch (e) { await this.console.error( 'startup', - `Failed to connect to <<shared database>> with error:\n${util.inspect(e, { colors: true, depth: 1 })}`, + `Failed to connect to <<shared database>> with error:\n${inspect(e, { colors: true, depth: 1 })}`, false ); process.exit(2); @@ -503,7 +479,7 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re this.stats.slashCommandsUsed = stats.slashCommandsUsed; await this.login(this.token!); } catch (e) { - await this.console.error('start', util.inspect(e, { colors: true, depth: 1 }), false); + await this.console.error('start', inspect(e, { colors: true, depth: 1 }), false); process.exit(1); } } diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts deleted file mode 100644 index 19810bd..0000000 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ /dev/null @@ -1,1187 +0,0 @@ -import { - Arg, - BushConstants, - CommandMessage, - Global, - Shared, - type BaseBushArgumentType, - type BushClient, - type BushInspectOptions, - type CodeBlockLang, - type GlobalCache, - type Pronoun, - type PronounCode, - type SharedCache, - type SlashEditMessageType, - type SlashMessage, - type SlashSendMessageType -} from '#lib'; -import { humanizeDuration } from '@notenoughupdates/humanize-duration'; -import assert from 'assert'; -import { exec } from 'child_process'; -import deepLock from 'deep-lock'; -import { ClientUtil, Util as AkairoUtil } from 'discord-akairo'; -import { - Constants as DiscordConstants, - EmbedBuilder, - GuildMember, - Message, - OAuth2Scopes, - PermissionFlagsBits, - PermissionsBitField, - Routes, - ThreadMember, - User, - Util as DiscordUtil, - type APIEmbed, - type APIMessage, - type CommandInteraction, - type InteractionReplyOptions, - type PermissionsString, - type Snowflake, - type TextChannel, - type UserResolvable -} from 'discord.js'; -import got from 'got'; -import _ from 'lodash'; -import { inspect, promisify } from 'util'; -import CommandErrorListener from '../../../listeners/commands/commandError.js'; -import { 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] }; - -export class BushClientUtil extends ClientUtil { - /** - * The client. - */ - public declare readonly client: BushClient; - - /** - * 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' - ]; - - /** - * Creates this client util - * @param client The client to initialize with - */ - public constructor(client: BushClient) { - super(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) => client.users.fetch(id))); - } - - /** - * Capitalizes the first letter of the given text - * @param text The text to capitalize - * @returns The capitalized text - */ - public capitalize(text: string): string { - return text.charAt(0).toUpperCase() + text.slice(1); - } - - /** - * Runs a shell command and gives the output - * @param command The shell command to run - * @returns The stdout and stderr of the shell command - */ - public async shell(command: string): Promise<{ - stdout: string; - stderr: string; - }> { - return await promisify(exec)(command); - } - - /** - * 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 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 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 - */ - public ordinal(n: number): string { - const s = ['th', 'st', 'nd', 'rd'], - v = n % 100; - return n + (s[(v - 20) % 10] || s[v] || s[0]); - } - - /** - * Chunks an array to the specified size - * @param arr The array to chunk - * @param perChunk The amount of items per chunk - * @returns The chunked array - */ - public chunk<T>(arr: T[], perChunk: number): T[][] { - return arr.reduce((all, one, i) => { - const ch: number = Math.floor(i / perChunk); - (all as any[])[ch] = [].concat(all[ch] || [], one as any); - return all; - }, []); - } - - /** - * Commonly Used Colors - */ - public get colors() { - return client.consts.colors; - } - - /** - * Commonly Used Emojis - */ - public get emojis() { - return client.consts.emojis; - } - - /** - * Just the ids of Commonly Used Emojis - */ - public get emojisRaw() { - return client.consts.emojisRaw; - } - - /** - * Fetches a user's uuid from the mojang api. - * @param username The username to get the uuid of. - * @returns The the uuid of the user. - */ - public async mcUUID(username: string, dashed = false): Promise<string> { - const apiRes = (await got.get(`https://api.ashcon.app/mojang/v2/user/${username}`).json()) as UuidRes; - return dashed ? apiRes.uuid : apiRes.uuid.replace(/-/g, ''); - } - - /** - * Surrounds text in a code block with the specified language and puts it in a hastebin if its too long. - * * Embed Description Limit = 4096 characters - * * Embed Field Limit = 1024 characters - * @param code The content of the code block. - * @param length The maximum length of the code block. - * @param language The language of the code. - * @param substr Whether or not to substring the code if it is too long. - * @returns The generated code block - */ - public async codeblock(code: string, length: number, language: CodeBlockLang | '' = '', substr = false): Promise<string> { - let hasteOut = ''; - code = this.discord.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}` : ''}` - : `${this.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. - */ - #getDefaultInspectOptions(options?: BushInspectOptions): BushInspectOptions { - return { - showHidden: options?.showHidden ?? false, - depth: options?.depth ?? 2, - colors: options?.colors ?? false, - customInspect: options?.customInspect ?? true, - showProxy: options?.showProxy ?? false, - maxArrayLength: options?.maxArrayLength ?? Infinity, - maxStringLength: options?.maxStringLength ?? Infinity, - breakLength: options?.breakLength ?? 80, - compact: options?.compact ?? 3, - sorted: options?.sorted ?? false, - getters: options?.getters ?? true - }; - } - - /** - * 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 { ...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 = 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; - } - - /** - * Uses {@link inspect} with custom defaults. - * @param object - The object you would like to inspect. - * @param options - The options you would like to use to inspect the object. - * @returns The inspected object. - */ - public inspect(object: any, options?: BushInspectOptions): string { - const optionsWithDefaults = this.#getDefaultInspectOptions(options); - - if (!optionsWithDefaults.inspectStrings && typeof object === 'string') return object; - - return inspect(object, optionsWithDefaults); - } - - /** - * 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 = this.inspect(input, inspectOptions ?? undefined); - if (inspectOptions) inspectOptions.inspectStrings = undefined; - input = this.discord.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 = this.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 = this.inspect(input, inspectOptions ?? undefined); - return this.redact(input); - } - - /** - * Responds to a slash command interaction. - * @param interaction The interaction to respond to. - * @param responseOptions The options for the response. - * @returns The message sent. - */ - public async slashRespond( - interaction: CommandInteraction, - responseOptions: SlashSendMessageType | SlashEditMessageType - ): Promise<Message | APIMessage | undefined> { - const newResponseOptions = typeof responseOptions === 'string' ? { content: responseOptions } : responseOptions; - if (interaction.replied || interaction.deferred) { - delete (newResponseOptions as InteractionReplyOptions).ephemeral; // Cannot change a preexisting message to be ephemeral - return (await interaction.editReply(newResponseOptions)) as Message | APIMessage; - } else { - await interaction.reply(newResponseOptions); - return await interaction.fetchReply().catch(() => undefined); - } - } - - /** - * Gets a a configured channel as a TextChannel. - * @channel The channel to retrieve. - */ - public async getConfigChannel(channel: keyof typeof client['config']['channels']): Promise<TextChannel> { - return (await client.channels.fetch(client.config.channels[channel])) as unknown as TextChannel; - } - - /** - * Takes an array and combines the elements using the supplied conjunction. - * @param array The array to combine. - * @param conjunction The conjunction to use. - * @param ifEmpty What to return if the array is empty. - * @returns The combined elements or `ifEmpty`. - * - * @example - * const permissions = oxford(['Administrator', 'SendMessages', 'ManageMessages'], 'and', 'none'); - * console.log(permissions); // Administrator, SendMessages and ManageMessages - */ - public oxford(array: string[], conjunction: string, ifEmpty?: string): string | undefined { - const l = array.length; - if (!l) return ifEmpty; - if (l < 2) return array[0]; - if (l < 3) return array.join(` ${conjunction} `); - array = array.slice(); - array[l - 1] = `${conjunction} ${array[l - 1]}`; - return array.join(', '); - } - - /** - * 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 ? client.cache.global[key] : client.cache.global; - } - - public getShared(): SharedCache; - public getShared<K extends keyof SharedCache>(key: K): SharedCache[K]; - public 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. - */ - public async 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 = this.addOrRemoveFromArray(action, oldValue, value); - row[key] = newValue; - 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 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 = this.addOrRemoveFromArray(action, oldValue, value); - row[key] = newValue; - 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 typeof client['cache']['global']>( - key: K, - value: typeof client['cache']['global'][K] - ): Promise<Global | void> { - const row = - (await Global.findByPk(client.config.environment)) ?? (await Global.create({ environment: client.config.environment })); - row[key] = value; - client.cache.global[key] = value; - return await row.save().catch((e) => this.handleError('setGlobal', e)); - } - - /** - * 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 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) => this.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. - * @param value The element to add/remove from the array. - */ - public addOrRemoveFromArray<T>(action: 'add' | 'remove', array: T[], value: T): T[] { - const set = new Set(array); - action === 'add' ? set.add(value) : set.delete(value); - return [...set]; - } - - /** - * Remove an item from an array. All duplicates will be removed. - * @param array The array to remove an element from. - * @param value The element to remove from the array. - */ - public removeFromArray<T>(array: T[], value: T): T[] { - return this.addOrRemoveFromArray('remove', array, value); - } - - /** - * Add an item from an array. All duplicates will be removed. - * @param array The array to add an element to. - * @param value The element to add to the array. - */ - public addToArray<T>(array: T[], value: T): T[] { - return this.addOrRemoveFromArray('add', array, value); - } - - /** - * Surrounds a string to the begging an end of each element in an array. - * @param array The array you want to surround. - * @param surroundChar1 The character placed in the beginning of the element. - * @param surroundChar2 The character placed in the end of the element. Defaults to `surroundChar1`. - */ - public surroundArray(array: string[], surroundChar1: string, surroundChar2?: string): string[] { - return array.map((a) => `${surroundChar1}${a}${surroundChar2 ?? surroundChar1}`); - } - - /** - * Gets the duration from a specified string. - * @param content The string to look for a duration in. - * @param remove Whether or not to remove the duration from the original string. - * @returns The {@link ParsedDuration}. - */ - public parseDuration(content: string, remove = true): ParsedDuration { - if (!content) return { duration: 0, content: null }; - - // eslint-disable-next-line prefer-const - let duration: number | null = null; - // Try to reduce false positives by requiring a space before the duration, this makes sure it still matches if it is - // in the beginning of the argument - let contentWithoutTime = ` ${content}`; - - for (const unit in BushConstants.timeUnits) { - const regex = BushConstants.timeUnits[unit as keyof typeof BushConstants.timeUnits].match; - const match = regex.exec(contentWithoutTime); - const value = Number(match?.groups?.[unit]); - if (!isNaN(value)) duration! += value * BushConstants.timeUnits[unit as keyof typeof BushConstants.timeUnits].value; - - if (remove) contentWithoutTime = contentWithoutTime.replace(regex, ''); - } - // remove the space added earlier - if (contentWithoutTime.startsWith(' ')) contentWithoutTime.replace(' ', ''); - return { duration, content: contentWithoutTime }; - } - - /** - * Converts a duration in milliseconds to a human readable form. - * @param duration The duration in milliseconds to convert. - * @param largest The maximum number of units to display for the duration. - * @param round Whether or not to round the smallest unit displayed. - * @returns A humanized string of the duration. - */ - public humanizeDuration(duration: number, largest?: number, round = true): string { - if (largest) return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2, largest, round })!; - else return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2, round })!; - } - - /** - * Creates a formatted relative timestamp from a duration in milliseconds. - * @param duration The duration in milliseconds. - * @returns The formatted relative timestamp. - */ - public timestampDuration(duration: number): string { - return `<t:${Math.round(new Date().getTime() / 1_000 + duration / 1_000)}:R>`; - } - - /** - * Creates a timestamp from a date. - * @param date The date to create a timestamp from. - * @param style The style of the timestamp. - * @returns The formatted timestamp. - * - * @see - * **Styles:** - * - **t**: Short Time ex. `16:20` - * - **T**: Long Time ex. `16:20:30 ` - * - **d**: Short Date ex. `20/04/2021` - * - **D**: Long Date ex. `20 April 2021` - * - **f**: Short Date/Time ex. `20 April 2021 16:20` - * - **F**: Long Date/Time ex. `Tuesday, 20 April 2021 16:20` - * - **R**: Relative Time ex. `2 months ago` - */ - public timestamp<D extends Date | undefined | null>(date: D, style: TimestampStyle = 'f'): D extends Date ? string : undefined { - if (!date) return date as unknown as D extends Date ? string : undefined; - return `<t:${Math.round(date.getTime() / 1_000)}:${style}>` as unknown as D extends Date ? string : undefined; - } - - /** - * Creates a human readable representation between a date and the current time. - * @param date The date to be compared with the current time. - * @param largest The maximum number of units to display for the duration. - * @param round Whether or not to round the smallest unit displayed. - * @returns A humanized string of the delta. - */ - public dateDelta(date: Date, largest?: number, round = true): string { - return this.humanizeDuration(new Date().getTime() - date.getTime(), largest ?? 3, round); - } - - /** - * Combines {@link timestamp} and {@link dateDelta} - * @param date The date to be compared with the current time. - * @param style The style of the timestamp. - * @returns The formatted timestamp. - * - * @see - * **Styles:** - * - **t**: Short Time ex. `16:20` - * - **T**: Long Time ex. `16:20:30 ` - * - **d**: Short Date ex. `20/04/2021` - * - **D**: Long Date ex. `20 April 2021` - * - **f**: Short Date/Time ex. `20 April 2021 16:20` - * - **F**: Long Date/Time ex. `Tuesday, 20 April 2021 16:20` - * - **R**: Relative Time ex. `2 months ago` - */ - public timestampAndDelta(date: Date, style: TimestampStyle = 'D'): string { - return `${this.timestamp(date, style)} (${this.dateDelta(date)} ago)`; - } - - /** - * Convert a hex code to an rbg value. - * @param hex The hex code to convert. - * @returns The rbg value. - */ - public hexToRgb(hex: string): string { - const arrBuff = new ArrayBuffer(4); - const vw = new DataView(arrBuff); - vw.setUint32(0, parseInt(hex, 16), false); - const arrByte = new Uint8Array(arrBuff); - - return `${arrByte[1]}, ${arrByte[2]}, ${arrByte[3]}`; - } - - /** - * Capitalize the first letter of a string. - * @param string The string to capitalize the first letter of. - * @returns The string with the first letter capitalized. - */ - public capitalizeFirstLetter(string: string): string { - return string.charAt(0)?.toUpperCase() + string.slice(1); - } - - /** - * Wait an amount in milliseconds. - * @returns A promise that resolves after the specified amount of milliseconds - */ - public get sleep() { - return promisify(setTimeout); - } - - /** - * Send a message in the error logging channel and console for an error. - * @param context - * @param error - */ - public async handleError(context: string, error: Error) { - await client.console.error(_.camelCase(context), `An error occurred:\n${util.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. - */ - 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 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 client.constants.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. - */ - public getMethods(obj: Record<string, any>): string { - // modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class/31055217#31055217 - let props: string[] = []; - let obj_: Record<string, any> = new Object(obj); - - do { - const l = Object.getOwnPropertyNames(obj_) - .concat(Object.getOwnPropertySymbols(obj_).map((s) => s.toString())) - .sort() - .filter( - (p, i, arr) => - typeof Object.getOwnPropertyDescriptor(obj_, p)?.['get'] !== 'function' && // ignore getters - typeof Object.getOwnPropertyDescriptor(obj_, p)?.['set'] !== 'function' && // ignore setters - typeof obj_[p] === 'function' && // only the methods - p !== 'constructor' && // not the constructor - (i == 0 || p !== arr[i - 1]) && // not overriding in this prototype - props.indexOf(p) === -1 // not overridden in a child - ); - - const reg = /\(([\s\S]*?)\)/; - props = props.concat( - l.map( - (p) => - `${obj_[p] && obj_[p][Symbol.toStringTag] === 'AsyncFunction' ? 'async ' : ''}function ${p}(${ - reg.exec(obj_[p].toString())?.[1] - ? reg - .exec(obj_[p].toString())?.[1] - .split(', ') - .map((arg) => arg.split('=')[0].trim()) - .join(', ') - : '' - });` - ) - ); - } while ( - (obj_ = Object.getPrototypeOf(obj_)) && // walk-up the prototype chain - Object.getPrototypeOf(obj_) // not the the Object prototype methods (hasOwnProperty, etc...) - ); - - return props.join('\n'); - } - - /** - * List the symbols of an object. - * @param obj The object to get the symbols of. - * @returns An array of the symbols of the object. - */ - public getSymbols(obj: Record<string, any>): symbol[] { - let symbols: symbol[] = []; - let obj_: Record<string, any> = new Object(obj); - - do { - const l = Object.getOwnPropertySymbols(obj_).sort(); - - symbols = [...symbols, ...l]; - } while ( - (obj_ = Object.getPrototypeOf(obj_)) && // walk-up the prototype chain - Object.getPrototypeOf(obj_) // not the the Object prototype methods (hasOwnProperty, etc...) - ); - - return symbols; - } - - /** - * 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; - } - - /** - * 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. - * @returns The missing permissions or null if none are missing. - */ - public userGuildPermCheck( - message: CommandMessage | SlashMessage, - permissions: typeof PermissionFlagsBits[keyof typeof PermissionFlagsBits][] - ): PermissionsString[] | null { - if (!message.inGuild()) return null; - const missing = message.member?.permissions.missing(permissions) ?? []; - - return missing.length ? missing : null; - } - - /** - * Check if the client has certain permissions in the guild (doesn't check channel permissions). - * @param message The message to check the client user from. - * @param permissions The permissions to check for. - * @returns The missing permissions or null if none are missing. - */ - public clientGuildPermCheck(message: CommandMessage | SlashMessage, permissions: bigint[]): PermissionsString[] | null { - const missing = message.guild?.members.me?.permissions.missing(permissions) ?? []; - - return missing.length ? missing : null; - } - - /** - * Check if the client has permission to send messages in the channel as well as check if they have other permissions - * in the guild (or the channel if `checkChannel` is `true`). - * @param message The message to check the client user from. - * @param permissions The permissions to check for. - * @param checkChannel Whether to check the channel permissions instead of the guild permissions. - * @returns The missing permissions or null if none are missing. - */ - public clientSendAndPermCheck( - message: CommandMessage | SlashMessage, - permissions: bigint[] = [], - checkChannel = false - ): PermissionsString[] | null { - const missing: PermissionsString[] = []; - const sendPerm = message.channel!.isThread() ? 'SendMessages' : 'SendMessagesInThreads'; - if (!message.inGuild()) return null; - - if (!message.guild.members.me!.permissionsIn(message.channel!.id).has(sendPerm)) missing.push(sendPerm); - - missing.push( - ...(checkChannel - ? message.guild!.members.me!.permissionsIn(message.channel!.id!).missing(permissions) - : this.clientGuildPermCheck(message, permissions) ?? []) - ); - - 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. - */ - public prefix(message: CommandMessage | SlashMessage): string { - return message.util.isSlash - ? '/' - : client.config.isDevelopment - ? 'dev ' - : message.util.parsed?.prefix ?? client.config.prefix; - } - - /** - * Recursively apply provided options operations on object - * and all of the object properties that are either object or function. - * - * By default freezes object. - * - * @param obj - The object to which will be applied `freeze`, `seal` or `preventExtensions` - * @param options default `{ action: 'freeze' }` - * @param options.action - * ``` - * | action | Add | Modify | Delete | Reconfigure | - * | ----------------- | --- | ------ | ------ | ----------- | - * | preventExtensions | - | + | + | + | - * | seal | - | + | - | - | - * | freeze | - | - | - | - | - * ``` - * - * @returns Initial object with applied options action - */ - public get deepFreeze() { - return deepLock; - } - - /** - * Recursively apply provided options operations on object - * and all of the object properties that are either object or function. - * - * By default freezes object. - * - * @param obj - The object to which will be applied `freeze`, `seal` or `preventExtensions` - * @param options default `{ action: 'freeze' }` - * @param options.action - * ``` - * | action | Add | Modify | Delete | Reconfigure | - * | ----------------- | --- | ------ | ------ | ----------- | - * | preventExtensions | - | + | + | + | - * | seal | - | + | - | - | - * | freeze | - | - | - | - | - * ``` - * - * @returns Initial object with applied options action - */ - public static get deepFreeze() { - return deepLock; - } - - /** - * The link to invite the bot with all permissions. - */ - public get invite() { - return client.generateInvite({ - permissions: - PermissionsBitField.All - - PermissionFlagsBits.UseEmbeddedActivities - - PermissionFlagsBits.ViewGuildInsights - - PermissionFlagsBits.Stream, - scopes: [OAuth2Scopes.Bot, OAuth2Scopes.ApplicationsCommands] - }); - } - - /** - * Asset multiple statements at a time. - * @param args - */ - public assertAll(...args: any[]): void { - for (let i = 0; i < args.length; i++) { - assert(args[i], `assertAll index ${i} failed`); - } - } - - /** - * Casts a string to a duration and reason for slash commands. - * @param arg The argument received. - * @param message The message that triggered the command. - * @returns The casted argument. - */ - public async castDurationContent( - arg: string | ParsedDuration | null, - message: CommandMessage | SlashMessage - ): Promise<ParsedDurationRes> { - const res = typeof arg === 'string' ? await util.arg.cast('contentWithDuration', message, arg) : arg; - - return { duration: res?.duration ?? 0, content: res?.content ?? '' }; - } - - /** - * Casts a string to a the specified argument type. - * @param type The type of the argument to cast to. - * @param arg The argument received. - * @param message The message that triggered the command. - * @returns The casted argument. - */ - public async cast<T extends keyof BaseBushArgumentType>( - type: T, - arg: BaseBushArgumentType[T] | string, - message: CommandMessage | SlashMessage - ) { - return typeof arg === 'string' ? await util.arg.cast(type, message, arg) : arg; - } - - /** - * Overflows the description of an embed into multiple embeds. - * @param embed The options to be applied to the (first) embed. - * @param lines Each line of the description as an element in an array. - */ - public overflowEmbed(embed: Omit<APIEmbed, 'description'>, lines: string[], maxLength = 4096): EmbedBuilder[] { - const embeds: EmbedBuilder[] = []; - - const makeEmbed = () => { - embeds.push(new EmbedBuilder().setColor(embed.color ?? null)); - return embeds.at(-1)!; - }; - - for (const line of lines) { - let current = embeds.length ? embeds.at(-1)! : makeEmbed(); - let joined = current.data.description ? `${current.data.description}\n${line}` : line; - if (joined.length > maxLength) { - current = makeEmbed(); - joined = line; - } - - current.setDescription(joined); - } - - if (!embeds.length) makeEmbed(); - - if (embed.author) embeds.at(0)?.setAuthor(embed.author); - if (embed.title) embeds.at(0)?.setTitle(embed.title); - if (embed.url) embeds.at(0)?.setURL(embed.url); - if (embed.fields) embeds.at(-1)?.setFields(embed.fields); - if (embed.thumbnail) embeds.at(-1)?.setThumbnail(embed.thumbnail.url); - if (embed.footer) embeds.at(-1)?.setFooter(embed.footer); - if (embed.image) embeds.at(-1)?.setImage(embed.image.url); - if (embed.timestamp) embeds.at(-1)?.setTimestamp(new Date(embed.timestamp)); - - return embeds; - } - - public async resolveMessageLinks(content: string | null): Promise<MessageLinkParts[]> { - const res: MessageLinkParts[] = []; - - if (!content) return res; - - const regex = new RegExp(this.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 = 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. - * @param colors Whether to use colors in the output. - * @returns The formatted error. - */ - public formatError(error: Error | any, colors = false): string { - if (!error) return error; - if (typeof error !== 'object') return String.prototype.toString.call(error); - if ( - this.getSymbols(error) - .map((s) => s.toString()) - .includes('Symbol(nodejs.util.inspect.custom)') - ) - return this.inspect(error, { colors }); - - return error.stack; - } - - /** - * A wrapper for the Argument class that adds custom typings. - */ - public get arg() { - return Arg; - } - - /** - * Formats and escapes content for formatting - */ - public get format() { - return Format; - } - - /** - * Discord.js's Util class - */ - public get discord() { - return DiscordUtil; - } - - /** - * Discord.js's Util constants - */ - public get discordConstants() { - return DiscordConstants; - } - - /** - * discord-akairo's Util class - */ - public get akairo() { - return AkairoUtil; - } - - public get consts() { - return client.consts; - } - - public get regex() { - return client.consts.regex; - } -} - -interface HastebinRes { - key: string; -} - -export interface UuidRes { - uuid: string; - username: string; - username_history?: { username: string }[] | null; - textures: { - custom: boolean; - slim: boolean; - skin: { - url: string; - data: string; - }; - raw: { - value: string; - signature: string; - }; - }; - 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; -} - -export interface ParsedDurationRes { - duration: number; - content: string; -} - -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/lib/extensions/discord-akairo/BushInhibitor.ts b/src/lib/extensions/discord-akairo/BushInhibitor.ts index 12b2baf..b4e6797 100644 --- a/src/lib/extensions/discord-akairo/BushInhibitor.ts +++ b/src/lib/extensions/discord-akairo/BushInhibitor.ts @@ -1,11 +1,9 @@ import { type BushClient, type BushCommand, type CommandMessage, type SlashMessage } from '#lib'; import { Inhibitor } from 'discord-akairo'; -export class BushInhibitor extends Inhibitor { +export abstract class BushInhibitor extends Inhibitor { public declare client: BushClient; -} -export interface BushInhibitor { /** * Checks if message should be blocked. * A return value of true will block the message. @@ -16,6 +14,6 @@ export interface BushInhibitor { * @param message - Message being handled. * @param command - Command to check. */ - exec(message: CommandMessage, command: BushCommand): any; - exec(message: CommandMessage | SlashMessage, command: BushCommand): any; + public abstract override exec(message: CommandMessage, command: BushCommand): any; + public abstract override exec(message: CommandMessage | SlashMessage, command: BushCommand): any; } diff --git a/src/lib/extensions/discord-akairo/BushListener.ts b/src/lib/extensions/discord-akairo/BushListener.ts index 3efe527..6917641 100644 --- a/src/lib/extensions/discord-akairo/BushListener.ts +++ b/src/lib/extensions/discord-akairo/BushListener.ts @@ -1,16 +1,3 @@ import { Listener } from 'discord-akairo'; -import type EventEmitter from 'events'; -export class BushListener extends Listener { - public constructor( - id: string, - options: { - emitter: string | EventEmitter; - event: string; - type?: 'on' | 'once' | 'prependListener' | 'prependOnceListener'; - category?: string; - } - ) { - super(id, options); - } -} +export abstract class BushListener extends Listener {} diff --git a/src/lib/extensions/discord-akairo/BushTask.ts b/src/lib/extensions/discord-akairo/BushTask.ts index 9f5c0cd..1b70c88 100644 --- a/src/lib/extensions/discord-akairo/BushTask.ts +++ b/src/lib/extensions/discord-akairo/BushTask.ts @@ -1,3 +1,3 @@ import { Task } from 'discord-akairo'; -export class BushTask extends Task {} +export abstract class BushTask extends Task {} diff --git a/src/lib/extensions/discord.js/ExtendedGuild.ts b/src/lib/extensions/discord.js/ExtendedGuild.ts index b8b7b22..c199899 100644 --- a/src/lib/extensions/discord.js/ExtendedGuild.ts +++ b/src/lib/extensions/discord.js/ExtendedGuild.ts @@ -1,7 +1,9 @@ import { AllowedMentions, banResponse, + colors, dmResponse, + emojis, permissionsResponse, punishmentEntryRemove, type BanResponse, @@ -36,9 +38,10 @@ import { type WebhookMessageOptions } from 'discord.js'; import _ from 'lodash'; -import { Moderation } from '../../common/util/Moderation.js'; +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'; declare module 'discord.js' { export interface Guild { @@ -152,7 +155,7 @@ export class ExtendedGuild extends Guild { */ public override async addFeature(feature: GuildFeatures, moderator?: GuildMember): Promise<GuildModel['enabledFeatures']> { const features = await this.getSetting('enabledFeatures'); - const newFeatures = util.addOrRemoveFromArray('add', features, feature); + const newFeatures = addOrRemoveFromArray('add', features, feature); return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; } @@ -163,7 +166,7 @@ export class ExtendedGuild extends Guild { */ public override async removeFeature(feature: GuildFeatures, moderator?: GuildMember): Promise<GuildModel['enabledFeatures']> { const features = await this.getSetting('enabledFeatures'); - const newFeatures = util.addOrRemoveFromArray('remove', features, feature); + const newFeatures = addOrRemoveFromArray('remove', features, feature); return (await this.setSetting('enabledFeatures', newFeatures, moderator)).enabledFeatures; } @@ -251,7 +254,7 @@ export class ExtendedGuild extends Guild { */ public override async error(title: string, message: string): Promise<void> { void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>')); - void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: util.colors.error }] }); + void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: colors.error }] }); } /** @@ -265,7 +268,7 @@ export class ExtendedGuild extends Guild { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const user = await util.resolveNonCachedUser(options.user); + const user = await resolveNonCachedUser(options.user); const moderator = client.users.resolve(options.moderator ?? client.user!); if (!user || !moderator) return banResponse.CANNOT_RESOLVE_USER; @@ -408,7 +411,7 @@ export class ExtendedGuild extends Guild { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const user = await util.resolveNonCachedUser(options.user); + const user = await resolveNonCachedUser(options.user); const moderator = client.users.resolve(options.moderator ?? client.user!); if (!user || !moderator) return unbanResponse.CANNOT_RESOLVE_USER; @@ -534,7 +537,7 @@ export class ExtendedGuild extends Guild { author: { name: moderator.user.tag, icon_url: moderator.displayAvatarURL() }, title: `This channel has been ${options.unlock ? 'un' : ''}locked`, description: options.reason ?? 'No reason provided', - color: options.unlock ? util.colors.Green : util.colors.Red, + color: options.unlock ? colors.Green : colors.Red, timestamp: new Date().toISOString() } ] @@ -600,16 +603,16 @@ export class ExtendedGuild extends Guild { case MessageType.RecipientAdd: { const recipient = rawQuote.mentions[0]; if (!recipient) { - sendOptions.content = `${util.emojis.error} Cannot resolve recipient.`; + sendOptions.content = `${emojis.error} Cannot resolve recipient.`; break; } if (quote.channel.isThread()) { const recipientDisplay = quote.guild?.members.cache.get(recipient.id)?.displayName ?? recipient.username; - sendOptions.content = `${util.emojis.join} ${displayName} added ${recipientDisplay} to the thread.`; + sendOptions.content = `${emojis.join} ${displayName} added ${recipientDisplay} to the thread.`; } else { // this should never happen - sendOptions.content = `${util.emojis.join} ${displayName} added ${recipient.username} to the group.`; + sendOptions.content = `${emojis.join} ${displayName} added ${recipient.username} to the group.`; } break; @@ -617,16 +620,16 @@ export class ExtendedGuild extends Guild { case MessageType.RecipientRemove: { const recipient = rawQuote.mentions[0]; if (!recipient) { - sendOptions.content = `${util.emojis.error} Cannot resolve recipient.`; + sendOptions.content = `${emojis.error} Cannot resolve recipient.`; break; } if (quote.channel.isThread()) { const recipientDisplay = quote.guild?.members.cache.get(recipient.id)?.displayName ?? recipient.username; - sendOptions.content = `${util.emojis.leave} ${displayName} removed ${recipientDisplay} from the thread.`; + sendOptions.content = `${emojis.leave} ${displayName} removed ${recipientDisplay} from the thread.`; } else { // this should never happen - sendOptions.content = `${util.emojis.leave} ${displayName} removed ${recipient.username} from the group.`; + sendOptions.content = `${emojis.leave} ${displayName} removed ${recipient.username} from the group.`; } break; @@ -661,7 +664,7 @@ export class ExtendedGuild extends Guild { // this is the same way that the discord client decides what message to use. const message = messages[timestamp % messages.length].replace(/{username}/g, displayName); - sendOptions.content = `${util.emojis.join} ${message}`; + sendOptions.content = `${emojis.join} ${message}`; break; } case MessageType.UserPremiumGuildSubscription: @@ -717,7 +720,7 @@ export class ExtendedGuild extends Guild { case MessageType.ChannelIconChange: case MessageType.Call: default: - sendOptions.content = `${util.emojis.error} I cannot quote **${ + sendOptions.content = `${emojis.error} I cannot quote **${ MessageType[quote.type] || quote.type }** messages, please report this to my developers.`; diff --git a/src/lib/extensions/discord.js/ExtendedGuildMember.ts b/src/lib/extensions/discord.js/ExtendedGuildMember.ts index 28acc1a..ad29236 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, Moderation, ModLogType, PunishmentTypeDM, Time } from '#lib'; +import { BushClientEvents, formatError, Moderation, ModLogType, PunishmentTypeDM, resolveNonCachedUser, Time } from '#lib'; import { ChannelType, GuildChannelResolvable, @@ -148,7 +148,7 @@ 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 util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await 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 }> => { @@ -195,7 +195,7 @@ export class ExtendedGuildMember extends GuildMember { if (ifShouldAddRole !== true) return ifShouldAddRole; let caseID: string | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return addRoleResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -266,7 +266,7 @@ export class ExtendedGuildMember extends GuildMember { if (ifShouldAddRole !== true) return ifShouldAddRole; let caseID: string | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return removeRoleResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -362,7 +362,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return muteResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -448,7 +448,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return unmuteResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -456,7 +456,7 @@ 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', util.formatError(e, true)); + await client.console.warn('muteRoleAddError', formatError(e, true)); return false; }); if (!muteSuccess) return unmuteResponse.ACTION_ERROR; @@ -526,7 +526,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return kickResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { // add modlog entry @@ -580,7 +580,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await 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 @@ -663,7 +663,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return blockResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -754,7 +754,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return unblockResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -839,7 +839,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return timeoutResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { @@ -901,7 +901,7 @@ export class ExtendedGuildMember extends GuildMember { let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; - const moderator = await util.resolveNonCachedUser(options.moderator ?? this.guild.members.me); + const moderator = await resolveNonCachedUser(options.moderator ?? this.guild.members.me); if (!moderator) return removeTimeoutResponse.CANNOT_RESOLVE_USER; const ret = await (async () => { diff --git a/src/lib/extensions/global.ts b/src/lib/extensions/global.ts index a6f2b5a..d9cfaec 100644 --- a/src/lib/extensions/global.ts +++ b/src/lib/extensions/global.ts @@ -1,16 +1,11 @@ /* eslint-disable no-var */ -import type { BushClient, BushClientUtil } from '#lib'; +import type { BushClient } from '#lib'; declare global { /** * The bushbot client. */ var client: BushClient; - /** - * The bushbot client util. - */ - var util: BushClientUtil; - // eslint-disable-next-line @typescript-eslint/no-unused-vars interface ReadonlyArray<T> { includes<S, R extends `${Extract<S, string>}`>( diff --git a/src/lib/index.ts b/src/lib/index.ts index 221f360..3e57f9e 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -4,12 +4,21 @@ export * from './common/ConfirmationPrompt.js'; export * from './common/DeleteButton.js'; export type { BushInspectOptions } from './common/typings/BushInspectOptions.js'; export type { CodeBlockLang } from './common/typings/CodeBlockLang.js'; -export * from './common/util/Arg.js'; -export * from './common/util/Format.js'; -export * from './common/util/Moderation.js'; +export * as Arg from './common/util/Arg.js'; +export * as Format from './common/util/Format.js'; +export * as Moderation from './common/util/Moderation.js'; +export type { + AppealButtonId, + CreateModLogEntryOptions, + CreatePunishmentEntryOptions, + PunishDMOptions, + PunishmentTypeDM, + PunishmentTypePresent, + RemovePunishmentEntryOptions, + SimpleCreateModLogEntryOptions +} from './common/util/Moderation.js'; export * from './extensions/discord-akairo/BushArgumentTypeCaster.js'; export * from './extensions/discord-akairo/BushClient.js'; -export * from './extensions/discord-akairo/BushClientUtil.js'; export * from './extensions/discord-akairo/BushCommand.js'; export * from './extensions/discord-akairo/BushCommandHandler.js'; export * from './extensions/discord-akairo/BushInhibitor.js'; @@ -40,5 +49,5 @@ export * from './utils/AllowedMentions.js'; export * from './utils/BushCache.js'; export * from './utils/BushConstants.js'; export * from './utils/BushLogger.js'; +export * from './utils/BushUtils.js'; export * from './utils/CanvasProgressBar.js'; -export * from './utils/Config.js'; diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts index 8c3d27f..0f9311f 100644 --- a/src/lib/utils/BushConstants.ts +++ b/src/lib/utils/BushConstants.ts @@ -1,6 +1,11 @@ -import { ArgumentMatches, ArgumentTypes, BuiltInReasons, CommandHandlerEvents } from 'discord-akairo/dist/src/util/Constants.js'; +import deepLock from 'deep-lock'; +import { + ArgumentMatches as AkairoArgumentMatches, + ArgumentTypes as AkairoArgumentTypes, + BuiltInReasons, + CommandHandlerEvents as AkairoCommandHandlerEvents +} from 'discord-akairo/dist/src/util/Constants.js'; import { Colors, GuildFeature } from 'discord.js'; -import { BushClientUtil } from '../extensions/discord-akairo/BushClientUtil.js'; const rawCapeUrl = 'https://raw.githubusercontent.com/NotEnoughUpdates/capes/master/'; @@ -49,207 +54,206 @@ export const enum Time { Year = Day * 365.25 // average with leap years } -export class BushConstants { - public static emojis = Object.freeze({ - success: '<:success:837109864101707807>', - warn: '<:warn:848726900876247050>', - error: '<:error:837123021016924261>', - successFull: '<:success_full:850118767576088646>', - warnFull: '<:warn_full:850118767391539312>', - errorFull: '<:error_full:850118767295201350>', - mad: '<:mad:783046135392239626>', - join: '<:join:850198029809614858>', - leave: '<:leave:850198048205307919>', - loading: '<a:Loading:853419254619963392>', - offlineCircle: '<:offline:787550565382750239>', - dndCircle: '<:dnd:787550487633330176>', - idleCircle: '<:idle:787550520956551218>', - onlineCircle: '<:online:787550449435803658>', - cross: '<:cross:878319362539421777>', - check: '<:check:878320135297961995>' - } as const); - - public static emojisRaw = Object.freeze({ - success: '837109864101707807', - warn: '848726900876247050', - error: '837123021016924261', - successFull: '850118767576088646', - warnFull: '850118767391539312', - errorFull: '850118767295201350', - mad: '783046135392239626', - join: '850198029809614858', - leave: '850198048205307919', - loading: '853419254619963392', - offlineCircle: '787550565382750239', - dndCircle: '787550487633330176', - idleCircle: '787550520956551218', - onlineCircle: '787550449435803658', - cross: '878319362539421777', - check: '878320135297961995' - } as const); - - public static colors = Object.freeze({ - default: 0x1fd8f1, - error: 0xef4947, - warn: 0xfeba12, - success: 0x3bb681, - info: 0x3b78ff, - red: 0xff0000, - blue: 0x0055ff, - aqua: 0x00bbff, - purple: 0x8400ff, - blurple: 0x5440cd, - newBlurple: 0x5865f2, - pink: 0xff00e6, - green: 0x00ff1e, - darkGreen: 0x008f11, - gold: 0xb59400, - yellow: 0xffff00, - white: 0xffffff, - gray: 0xa6a6a6, - lightGray: 0xcfcfcf, - darkGray: 0x7a7a7a, - black: 0x000000, - orange: 0xe86100, - ...Colors - } as const); - - // Somewhat stolen from @Mzato0001 - public static timeUnits = BushClientUtil.deepFreeze({ - milliseconds: { - match: / (?:(?<milliseconds>-?(?:\d+)?\.?\d+) *(?:milliseconds?|msecs?|ms))/im, - value: Time.Millisecond - }, - seconds: { - match: / (?:(?<seconds>-?(?:\d+)?\.?\d+) *(?:seconds?|secs?|s))/im, - value: Time.Second - }, - minutes: { - match: / (?:(?<minutes>-?(?:\d+)?\.?\d+) *(?:minutes?|mins?|m))/im, - value: Time.Minute - }, - hours: { - match: / (?:(?<hours>-?(?:\d+)?\.?\d+) *(?:hours?|hrs?|h))/im, - value: Time.Hour - }, - days: { - match: / (?:(?<days>-?(?:\d+)?\.?\d+) *(?:days?|d))/im, - value: Time.Day - }, - weeks: { - match: / (?:(?<weeks>-?(?:\d+)?\.?\d+) *(?:weeks?|w))/im, - value: Time.Week - }, - months: { - match: / (?:(?<months>-?(?:\d+)?\.?\d+) *(?:months?|mon|mo))/im, - value: Time.Month - }, - years: { - match: / (?:(?<years>-?(?:\d+)?\.?\d+) *(?:years?|y))/im, - value: Time.Year - } - } as const); - - public static regex = BushClientUtil.deepFreeze({ - snowflake: /^\d{15,21}$/im, - - discordEmoji: /<a?:(?<name>[a-zA-Z0-9_]+):(?<id>\d{15,21})>/im, - - /* - * Taken with permission from Geek: - * https://github.com/FireDiscordBot/bot/blob/5d1990e5f8b52fcc72261d786aa3c7c7c65ab5e8/lib/util/constants.ts#L276 - */ - /** **This has the global flag, make sure to handle it correctly.** */ - messageLink: - /<?(?:ptb\.|canary\.|staging\.)?discord(?:app)?\.com?\/channels\/(?<guild_id>\d{15,21})\/(?<channel_id>\d{15,21})\/(?<message_id>\d{15,21})>?/gim - } as const); - - /** - * Maps the response from pronoundb.org to a readable format +export const emojis = Object.freeze({ + success: '<:success:837109864101707807>', + warn: '<:warn:848726900876247050>', + error: '<:error:837123021016924261>', + successFull: '<:success_full:850118767576088646>', + warnFull: '<:warn_full:850118767391539312>', + errorFull: '<:error_full:850118767295201350>', + mad: '<:mad:783046135392239626>', + join: '<:join:850198029809614858>', + leave: '<:leave:850198048205307919>', + loading: '<a:Loading:853419254619963392>', + offlineCircle: '<:offline:787550565382750239>', + dndCircle: '<:dnd:787550487633330176>', + idleCircle: '<:idle:787550520956551218>', + onlineCircle: '<:online:787550449435803658>', + cross: '<:cross:878319362539421777>', + check: '<:check:878320135297961995>' +} as const); + +export const emojisRaw = Object.freeze({ + success: '837109864101707807', + warn: '848726900876247050', + error: '837123021016924261', + successFull: '850118767576088646', + warnFull: '850118767391539312', + errorFull: '850118767295201350', + mad: '783046135392239626', + join: '850198029809614858', + leave: '850198048205307919', + loading: '853419254619963392', + offlineCircle: '787550565382750239', + dndCircle: '787550487633330176', + idleCircle: '787550520956551218', + onlineCircle: '787550449435803658', + cross: '878319362539421777', + check: '878320135297961995' +} as const); + +export const colors = Object.freeze({ + default: 0x1fd8f1, + error: 0xef4947, + warn: 0xfeba12, + success: 0x3bb681, + info: 0x3b78ff, + red: 0xff0000, + blue: 0x0055ff, + aqua: 0x00bbff, + purple: 0x8400ff, + blurple: 0x5440cd, + newBlurple: 0x5865f2, + pink: 0xff00e6, + green: 0x00ff1e, + darkGreen: 0x008f11, + gold: 0xb59400, + yellow: 0xffff00, + white: 0xffffff, + gray: 0xa6a6a6, + lightGray: 0xcfcfcf, + darkGray: 0x7a7a7a, + black: 0x000000, + orange: 0xe86100, + ...Colors +} as const); + +// Somewhat stolen from @Mzato0001 +export const timeUnits = deepLock({ + milliseconds: { + match: / (?:(?<milliseconds>-?(?:\d+)?\.?\d+) *(?:milliseconds?|msecs?|ms))/im, + value: Time.Millisecond + }, + seconds: { + match: / (?:(?<seconds>-?(?:\d+)?\.?\d+) *(?:seconds?|secs?|s))/im, + value: Time.Second + }, + minutes: { + match: / (?:(?<minutes>-?(?:\d+)?\.?\d+) *(?:minutes?|mins?|m))/im, + value: Time.Minute + }, + hours: { + match: / (?:(?<hours>-?(?:\d+)?\.?\d+) *(?:hours?|hrs?|h))/im, + value: Time.Hour + }, + days: { + match: / (?:(?<days>-?(?:\d+)?\.?\d+) *(?:days?|d))/im, + value: Time.Day + }, + weeks: { + match: / (?:(?<weeks>-?(?:\d+)?\.?\d+) *(?:weeks?|w))/im, + value: Time.Week + }, + months: { + match: / (?:(?<months>-?(?:\d+)?\.?\d+) *(?:months?|mon|mo))/im, + value: Time.Month + }, + years: { + match: / (?:(?<years>-?(?:\d+)?\.?\d+) *(?:years?|y))/im, + value: Time.Year + } +} as const); + +export const regex = deepLock({ + snowflake: /^\d{15,21}$/im, + + discordEmoji: /<a?:(?<name>[a-zA-Z0-9_]+):(?<id>\d{15,21})>/im, + + /* + * Taken with permission from Geek: + * https://github.com/FireDiscordBot/bot/blob/5d1990e5f8b52fcc72261d786aa3c7c7c65ab5e8/lib/util/constants.ts#L276 */ - public static pronounMapping = Object.freeze({ - unspecified: 'Unspecified', - hh: 'He/Him', - hi: 'He/It', - hs: 'He/She', - ht: 'He/They', - ih: 'It/Him', - ii: 'It/Its', - is: 'It/She', - it: 'It/They', - shh: 'She/He', - sh: 'She/Her', - si: 'She/It', - st: 'She/They', - th: 'They/He', - ti: 'They/It', - ts: 'They/She', - tt: 'They/Them', - any: 'Any pronouns', - other: 'Other pronouns', - ask: 'Ask me my pronouns', - avoid: 'Avoid pronouns, use my name' - } as const); + /** **This has the global flag, make sure to handle it correctly.** */ + messageLink: + /<?(?:ptb\.|canary\.|staging\.)?discord(?:app)?\.com?\/channels\/(?<guild_id>\d{15,21})\/(?<channel_id>\d{15,21})\/(?<message_id>\d{15,21})>?/gim +} as const); - /** - * A bunch of mappings - */ - public static mappings = BushClientUtil.deepFreeze({ - guilds: { - bush: '516977525906341928', - tree: '767448775450820639', - staff: '784597260465995796', - space_ship: '717176538717749358', - sbr: '839287012409999391' - }, - - permissions: { - CreateInstantInvite: { name: 'Create Invite', important: false }, - KickMembers: { name: 'Kick Members', important: true }, - BanMembers: { name: 'Ban Members', important: true }, - Administrator: { name: 'Administrator', important: true }, - ManageChannels: { name: 'Manage Channels', important: true }, - ManageGuild: { name: 'Manage Server', important: true }, - AddReactions: { name: 'Add Reactions', important: false }, - ViewAuditLog: { name: 'View Audit Log', important: true }, - PrioritySpeaker: { name: 'Priority Speaker', important: true }, - Stream: { name: 'Video', important: false }, - ViewChannel: { name: 'View Channel', important: false }, - SendMessages: { name: 'Send Messages', important: false }, - SendTTSMessages: { name: 'Send Text-to-Speech Messages', important: true }, - ManageMessages: { name: 'Manage Messages', important: true }, - EmbedLinks: { name: 'Embed Links', important: false }, - AttachFiles: { name: 'Attach Files', important: false }, - ReadMessageHistory: { name: 'Read Message History', important: false }, - MentionEveryone: { name: 'Mention @\u200Beveryone, @\u200Bhere, and All Roles', important: true }, // name has a zero-width space to prevent accidents - UseExternalEmojis: { name: 'Use External Emoji', important: false }, - ViewGuildInsights: { name: 'View Server Insights', important: true }, - Connect: { name: 'Connect', important: false }, - Speak: { name: 'Speak', important: false }, - MuteMembers: { name: 'Mute Members', important: true }, - DeafenMembers: { name: 'Deafen Members', important: true }, - MoveMembers: { name: 'Move Members', important: true }, - UseVAD: { name: 'Use Voice Activity', important: false }, - ChangeNickname: { name: 'Change Nickname', important: false }, - ManageNicknames: { name: 'Change Nicknames', important: true }, - ManageRoles: { name: 'Manage Roles', important: true }, - ManageWebhooks: { name: 'Manage Webhooks', important: true }, - ManageEmojisAndStickers: { name: 'Manage Emojis and Stickers', important: true }, - UseApplicationCommands: { name: 'Use Slash Commands', important: false }, - RequestToSpeak: { name: 'Request to Speak', important: false }, - ManageEvents: { name: 'Manage Events', important: true }, - ManageThreads: { name: 'Manage Threads', important: true }, - CreatePublicThreads: { name: 'Create Public Threads', important: false }, - CreatePrivateThreads: { name: 'Create Private Threads', important: false }, - UseExternalStickers: { name: 'Use External Stickers', important: false }, - SendMessagesInThreads: { name: 'Send Messages In Threads', important: false }, - StartEmbeddedActivities: { name: 'Start Activities', important: false }, - ModerateMembers: { name: 'Timeout Members', important: true }, - UseEmbeddedActivities: { name: 'Use Activities', important: false } - }, +/** + * Maps the response from pronoundb.org to a readable format + */ +export const pronounMapping = Object.freeze({ + unspecified: 'Unspecified', + hh: 'He/Him', + hi: 'He/It', + hs: 'He/She', + ht: 'He/They', + ih: 'It/Him', + ii: 'It/Its', + is: 'It/She', + it: 'It/They', + shh: 'She/He', + sh: 'She/Her', + si: 'She/It', + st: 'She/They', + th: 'They/He', + ti: 'They/It', + ts: 'They/She', + tt: 'They/Them', + any: 'Any pronouns', + other: 'Other pronouns', + ask: 'Ask me my pronouns', + avoid: 'Avoid pronouns, use my name' +} as const); - // prettier-ignore - features: { +/** + * A bunch of mappings + */ +export const mappings = deepLock({ + guilds: { + bush: '516977525906341928', + tree: '767448775450820639', + staff: '784597260465995796', + space_ship: '717176538717749358', + sbr: '839287012409999391' + }, + + permissions: { + CreateInstantInvite: { name: 'Create Invite', important: false }, + KickMembers: { name: 'Kick Members', important: true }, + BanMembers: { name: 'Ban Members', important: true }, + Administrator: { name: 'Administrator', important: true }, + ManageChannels: { name: 'Manage Channels', important: true }, + ManageGuild: { name: 'Manage Server', important: true }, + AddReactions: { name: 'Add Reactions', important: false }, + ViewAuditLog: { name: 'View Audit Log', important: true }, + PrioritySpeaker: { name: 'Priority Speaker', important: true }, + Stream: { name: 'Video', important: false }, + ViewChannel: { name: 'View Channel', important: false }, + SendMessages: { name: 'Send Messages', important: false }, + SendTTSMessages: { name: 'Send Text-to-Speech Messages', important: true }, + ManageMessages: { name: 'Manage Messages', important: true }, + EmbedLinks: { name: 'Embed Links', important: false }, + AttachFiles: { name: 'Attach Files', important: false }, + ReadMessageHistory: { name: 'Read Message History', important: false }, + MentionEveryone: { name: 'Mention @\u200Beveryone, @\u200Bhere, and All Roles', important: true }, // name has a zero-width space to prevent accidents + UseExternalEmojis: { name: 'Use External Emoji', important: false }, + ViewGuildInsights: { name: 'View Server Insights', important: true }, + Connect: { name: 'Connect', important: false }, + Speak: { name: 'Speak', important: false }, + MuteMembers: { name: 'Mute Members', important: true }, + DeafenMembers: { name: 'Deafen Members', important: true }, + MoveMembers: { name: 'Move Members', important: true }, + UseVAD: { name: 'Use Voice Activity', important: false }, + ChangeNickname: { name: 'Change Nickname', important: false }, + ManageNicknames: { name: 'Change Nicknames', important: true }, + ManageRoles: { name: 'Manage Roles', important: true }, + ManageWebhooks: { name: 'Manage Webhooks', important: true }, + ManageEmojisAndStickers: { name: 'Manage Emojis and Stickers', important: true }, + UseApplicationCommands: { name: 'Use Slash Commands', important: false }, + RequestToSpeak: { name: 'Request to Speak', important: false }, + ManageEvents: { name: 'Manage Events', important: true }, + ManageThreads: { name: 'Manage Threads', important: true }, + CreatePublicThreads: { name: 'Create Public Threads', important: false }, + CreatePrivateThreads: { name: 'Create Private Threads', important: false }, + UseExternalStickers: { name: 'Use External Stickers', important: false }, + SendMessagesInThreads: { name: 'Send Messages In Threads', important: false }, + StartEmbeddedActivities: { name: 'Start Activities', important: false }, + ModerateMembers: { name: 'Timeout Members', important: true }, + UseEmbeddedActivities: { name: 'Use Activities', important: false } + }, + + // prettier-ignore + features: { [GuildFeature.Verified]: { name: 'Verified', important: true, emoji: '<:verified:850795049817473066>', weight: 0 }, [GuildFeature.Partnered]: { name: 'Partnered', important: true, emoji: '<:partneredServer:850794851955507240>', weight: 1 }, [GuildFeature.MoreStickers]: { name: 'More Stickers', important: true, emoji: null, weight: 2 }, @@ -283,206 +287,123 @@ export class BushConstants { [GuildFeature.LinkedToHub]: { name: 'Linked To Hub', important: false, emoji: null, weight: 31 }, }, - regions: { - 'automatic': ':united_nations: Automatic', - 'brazil': ':flag_br: Brazil', - 'europe': ':flag_eu: Europe', - 'hongkong': ':flag_hk: Hongkong', - 'india': ':flag_in: India', - 'japan': ':flag_jp: Japan', - 'russia': ':flag_ru: Russia', - 'singapore': ':flag_sg: Singapore', - 'southafrica': ':flag_za: South Africa', - 'sydney': ':flag_au: Sydney', - 'us-central': ':flag_us: US Central', - 'us-east': ':flag_us: US East', - 'us-south': ':flag_us: US South', - 'us-west': ':flag_us: US West' - }, - - otherEmojis: { - ServerBooster1: '<:serverBooster1:848740052091142145>', - ServerBooster2: '<:serverBooster2:848740090506510388>', - ServerBooster3: '<:serverBooster3:848740124992077835>', - ServerBooster6: '<:serverBooster6:848740155245461514>', - ServerBooster9: '<:serverBooster9:848740188846030889>', - ServerBooster12: '<:serverBooster12:848740304365551668>', - ServerBooster15: '<:serverBooster15:848740354890137680>', - ServerBooster18: '<:serverBooster18:848740402886606868>', - ServerBooster24: '<:serverBooster24:848740444628320256>', - Nitro: '<:nitro:848740498054971432>', - Booster: '<:booster:848747775020892200>', - Owner: '<:owner:848746439311753286>', - Admin: '<:admin:848963914628333598>', - Superuser: '<:superUser:848947986326224926>', - Developer: '<:developer:848954538111139871>', - BushVerified: '<:verfied:853360152090771497>', - BoostTier1: '<:boostitle:853363736679940127>', - BoostTier2: '<:boostitle:853363752728789075>', - BoostTier3: '<:boostitle:853363769132056627>', - ChannelText: '<:text:853375537791893524>', - ChannelNews: '<:announcements:853375553531674644>', - ChannelVoice: '<:voice:853375566735212584>', - ChannelStage: '<:stage:853375583521210468>', - // ChannelStore: '<:store:853375601175691266>', - ChannelCategory: '<:category:853375615260819476>', - ChannelThread: '<:thread:865033845753249813>' - }, - - userFlags: { - Staff: '<:discordEmployee:848742947826434079>', - Partner: '<:partneredServerOwner:848743051593777152>', - Hypesquad: '<:hypeSquadEvents:848743108283072553>', - BugHunterLevel1: '<:bugHunter:848743239850393640>', - HypeSquadOnlineHouse1: '<:hypeSquadBravery:848742910563844127>', - HypeSquadOnlineHouse2: '<:hypeSquadBrilliance:848742840649646101>', - HypeSquadOnlineHouse3: '<:hypeSquadBalance:848742877537370133>', - PremiumEarlySupporter: '<:earlySupporter:848741030102171648>', - TeamPseudoUser: 'TeamPseudoUser', - BugHunterLevel2: '<:bugHunterGold:848743283080822794>', - VerifiedBot: '<:verifiedbot_rebrand1:938928232667947028><:verifiedbot_rebrand2:938928355707879475>', - VerifiedDeveloper: '<:earlyVerifiedBotDeveloper:848741079875846174>', - CertifiedModerator: '<:discordCertifiedModerator:877224285901582366>', - BotHTTPInteractions: 'BotHTTPInteractions', - Spammer: 'Spammer' - }, - - status: { - online: '<:online:848937141639577690>', - idle: '<:idle:848937158261211146>', - dnd: '<:dnd:848937173780135986>', - offline: '<:offline:848939387277672448>', - streaming: '<:streaming:848937187479519242>' - }, - - maybeNitroDiscrims: ['1111', '2222', '3333', '4444', '5555', '6666', '6969', '7777', '8888', '9999'], - - capes: [ - /* supporter capes */ - { name: 'patreon1', purchasable: false /* moulberry no longer offers */ }, - { name: 'patreon2', purchasable: false /* moulberry no longer offers */ }, - { name: 'fade', custom: `${rawCapeUrl}fade.gif`, purchasable: true }, - { name: 'lava', custom: `${rawCapeUrl}lava.gif`, purchasable: true }, - { name: 'mcworld', custom: `${rawCapeUrl}mcworld_compressed.gif`, purchasable: true }, - { name: 'negative', custom: `${rawCapeUrl}negative_compressed.gif`, purchasable: true }, - { name: 'space', custom: `${rawCapeUrl}space_compressed.gif`, purchasable: true }, - { name: 'void', custom: `${rawCapeUrl}void.gif`, purchasable: true }, - { name: 'tunnel', custom: `${rawCapeUrl}tunnel.gif`, purchasable: true }, - /* Staff capes */ - { name: 'contrib' }, - { name: 'mbstaff' }, - { name: 'ironmoon' }, - { name: 'gravy' }, - { name: 'nullzee' }, - /* partner capes */ - { name: 'thebakery' }, - { name: 'dsm' }, - { name: 'packshq' }, - { name: 'furf' }, - { name: 'skytils' }, - { name: 'sbp' }, - { name: 'subreddit_light' }, - { name: 'subreddit_dark' }, - { name: 'skyclient' }, - { name: 'sharex' }, - { name: 'sharex_white' }, - /* streamer capes */ - { name: 'alexxoffi' }, - { name: 'jakethybro' }, - { name: 'krusty' }, - { name: 'krusty_day' }, - { name: 'krusty_night' }, - { name: 'krusty_sunset' }, - { name: 'soldier' }, - { name: 'zera' }, - { name: 'secondpfirsisch' }, - { name: 'stormy_lh' } - ].map((value, index) => ({ ...value, index })), - - roleMap: [ - { name: '*', id: '792453550768390194' }, - { name: 'Admin Perms', id: '746541309853958186' }, - { name: 'Sr. Moderator', id: '782803470205190164' }, - { name: 'Moderator', id: '737308259823910992' }, - { name: 'Helper', id: '737440116230062091' }, - { name: 'Trial Helper', id: '783537091946479636' }, - { name: 'Contributor', id: '694431057532944425' }, - { name: 'Giveaway Donor', id: '784212110263451649' }, - { name: 'Giveaway (200m)', id: '810267756426690601' }, - { name: 'Giveaway (100m)', id: '801444430522613802' }, - { name: 'Giveaway (50m)', id: '787497512981757982' }, - { name: 'Giveaway (25m)', id: '787497515771232267' }, - { name: 'Giveaway (10m)', id: '787497518241153025' }, - { name: 'Giveaway (5m)', id: '787497519768403989' }, - { name: 'Giveaway (1m)', id: '787497521084891166' }, - { name: 'Suggester', id: '811922322767609877' }, - { name: 'Partner', id: '767324547312779274' }, - { name: 'Level Locked', id: '784248899044769792' }, - { name: 'No Files', id: '786421005039173633' }, - { name: 'No Reactions', id: '786421270924361789' }, - { name: 'No Links', id: '786421269356740658' }, - { name: 'No Bots', id: '786804858765312030' }, - { name: 'No VC', id: '788850482554208267' }, - { name: 'No Giveaways', id: '808265422334984203' }, - { name: 'No Support', id: '790247359824396319' } - ], - - roleWhitelist: { - 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Suggester': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator', 'Helper', 'Trial Helper', 'Contributor'], - 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Giveaways': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator', 'Helper'], - 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (100m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (50m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (25m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (10m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (5m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] - } - } as const); - - public static ArgumentMatches = Object.freeze({ - ...ArgumentMatches - } as const); - - public static ArgumentTypes = Object.freeze({ - ...ArgumentTypes, - DURATION: 'duration', - CONTENT_WITH_DURATION: 'contentWithDuration', - PERMISSION: 'permission', - SNOWFLAKE: 'snowflake', - DISCORD_EMOJI: 'discordEmoji', - ROLE_WITH_DURATION: 'roleWithDuration', - ABBREVIATED_NUMBER: 'abbreviatedNumber', - GLOBAL_USER: 'globalUser' - } as const); - - public static BlockedReasons = Object.freeze({ - ...BuiltInReasons, - DISABLED_GUILD: 'disabledGuild', - DISABLED_GLOBAL: 'disabledGlobal', - ROLE_BLACKLIST: 'roleBlacklist', - USER_GUILD_BLACKLIST: 'userGuildBlacklist', - USER_GLOBAL_BLACKLIST: 'userGlobalBlacklist', - RESTRICTED_GUILD: 'restrictedGuild', - CHANNEL_GUILD_BLACKLIST: 'channelGuildBlacklist', - CHANNEL_GLOBAL_BLACKLIST: 'channelGlobalBlacklist', - RESTRICTED_CHANNEL: 'restrictedChannel' - } as const); - - public static CommandHandlerEvents = Object.freeze({ - ...CommandHandlerEvents - } as const); - - public static moulberryBushRoleMap = BushClientUtil.deepFreeze([ + regions: { + 'automatic': ':united_nations: Automatic', + 'brazil': ':flag_br: Brazil', + 'europe': ':flag_eu: Europe', + 'hongkong': ':flag_hk: Hongkong', + 'india': ':flag_in: India', + 'japan': ':flag_jp: Japan', + 'russia': ':flag_ru: Russia', + 'singapore': ':flag_sg: Singapore', + 'southafrica': ':flag_za: South Africa', + 'sydney': ':flag_au: Sydney', + 'us-central': ':flag_us: US Central', + 'us-east': ':flag_us: US East', + 'us-south': ':flag_us: US South', + 'us-west': ':flag_us: US West' + }, + + otherEmojis: { + ServerBooster1: '<:serverBooster1:848740052091142145>', + ServerBooster2: '<:serverBooster2:848740090506510388>', + ServerBooster3: '<:serverBooster3:848740124992077835>', + ServerBooster6: '<:serverBooster6:848740155245461514>', + ServerBooster9: '<:serverBooster9:848740188846030889>', + ServerBooster12: '<:serverBooster12:848740304365551668>', + ServerBooster15: '<:serverBooster15:848740354890137680>', + ServerBooster18: '<:serverBooster18:848740402886606868>', + ServerBooster24: '<:serverBooster24:848740444628320256>', + Nitro: '<:nitro:848740498054971432>', + Booster: '<:booster:848747775020892200>', + Owner: '<:owner:848746439311753286>', + Admin: '<:admin:848963914628333598>', + Superuser: '<:superUser:848947986326224926>', + Developer: '<:developer:848954538111139871>', + BushVerified: '<:verfied:853360152090771497>', + BoostTier1: '<:boostitle:853363736679940127>', + BoostTier2: '<:boostitle:853363752728789075>', + BoostTier3: '<:boostitle:853363769132056627>', + ChannelText: '<:text:853375537791893524>', + ChannelNews: '<:announcements:853375553531674644>', + ChannelVoice: '<:voice:853375566735212584>', + ChannelStage: '<:stage:853375583521210468>', + // ChannelStore: '<:store:853375601175691266>', + ChannelCategory: '<:category:853375615260819476>', + ChannelThread: '<:thread:865033845753249813>' + }, + + userFlags: { + Staff: '<:discordEmployee:848742947826434079>', + Partner: '<:partneredServerOwner:848743051593777152>', + Hypesquad: '<:hypeSquadEvents:848743108283072553>', + BugHunterLevel1: '<:bugHunter:848743239850393640>', + HypeSquadOnlineHouse1: '<:hypeSquadBravery:848742910563844127>', + HypeSquadOnlineHouse2: '<:hypeSquadBrilliance:848742840649646101>', + HypeSquadOnlineHouse3: '<:hypeSquadBalance:848742877537370133>', + PremiumEarlySupporter: '<:earlySupporter:848741030102171648>', + TeamPseudoUser: 'TeamPseudoUser', + BugHunterLevel2: '<:bugHunterGold:848743283080822794>', + VerifiedBot: '<:verifiedbot_rebrand1:938928232667947028><:verifiedbot_rebrand2:938928355707879475>', + VerifiedDeveloper: '<:earlyVerifiedBotDeveloper:848741079875846174>', + CertifiedModerator: '<:discordCertifiedModerator:877224285901582366>', + BotHTTPInteractions: 'BotHTTPInteractions', + Spammer: 'Spammer' + }, + + status: { + online: '<:online:848937141639577690>', + idle: '<:idle:848937158261211146>', + dnd: '<:dnd:848937173780135986>', + offline: '<:offline:848939387277672448>', + streaming: '<:streaming:848937187479519242>' + }, + + maybeNitroDiscrims: ['1111', '2222', '3333', '4444', '5555', '6666', '6969', '7777', '8888', '9999'], + + capes: [ + /* supporter capes */ + { name: 'patreon1', purchasable: false /* moulberry no longer offers */ }, + { name: 'patreon2', purchasable: false /* moulberry no longer offers */ }, + { name: 'fade', custom: `${rawCapeUrl}fade.gif`, purchasable: true }, + { name: 'lava', custom: `${rawCapeUrl}lava.gif`, purchasable: true }, + { name: 'mcworld', custom: `${rawCapeUrl}mcworld_compressed.gif`, purchasable: true }, + { name: 'negative', custom: `${rawCapeUrl}negative_compressed.gif`, purchasable: true }, + { name: 'space', custom: `${rawCapeUrl}space_compressed.gif`, purchasable: true }, + { name: 'void', custom: `${rawCapeUrl}void.gif`, purchasable: true }, + { name: 'tunnel', custom: `${rawCapeUrl}tunnel.gif`, purchasable: true }, + /* Staff capes */ + { name: 'contrib' }, + { name: 'mbstaff' }, + { name: 'ironmoon' }, + { name: 'gravy' }, + { name: 'nullzee' }, + /* partner capes */ + { name: 'thebakery' }, + { name: 'dsm' }, + { name: 'packshq' }, + { name: 'furf' }, + { name: 'skytils' }, + { name: 'sbp' }, + { name: 'subreddit_light' }, + { name: 'subreddit_dark' }, + { name: 'skyclient' }, + { name: 'sharex' }, + { name: 'sharex_white' }, + /* streamer capes */ + { name: 'alexxoffi' }, + { name: 'jakethybro' }, + { name: 'krusty' }, + { name: 'krusty_day' }, + { name: 'krusty_night' }, + { name: 'krusty_sunset' }, + { name: 'soldier' }, + { name: 'zera' }, + { name: 'secondpfirsisch' }, + { name: 'stormy_lh' } + ].map((value, index) => ({ ...value, index })), + + roleMap: [ { name: '*', id: '792453550768390194' }, { name: 'Admin Perms', id: '746541309853958186' }, { name: 'Sr. Moderator', id: '782803470205190164' }, @@ -508,8 +429,90 @@ export class BushConstants { { name: 'No VC', id: '788850482554208267' }, { name: 'No Giveaways', id: '808265422334984203' }, { name: 'No Support', id: '790247359824396319' } - ] as const); -} - -export type PronounCode = keyof typeof BushConstants['pronounMapping']; -export type Pronoun = typeof BushConstants['pronounMapping'][PronounCode]; + ], + + roleWhitelist: { + 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Suggester': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator', 'Helper', 'Trial Helper', 'Contributor'], + 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Giveaways': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator', 'Helper'], + 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (100m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (50m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (25m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (10m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (5m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] + } +} as const); + +export const ArgumentMatches = Object.freeze({ + ...AkairoArgumentMatches +} as const); + +export const ArgumentTypes = Object.freeze({ + ...AkairoArgumentTypes, + DURATION: 'duration', + CONTENT_WITH_DURATION: 'contentWithDuration', + PERMISSION: 'permission', + SNOWFLAKE: 'snowflake', + DISCORD_EMOJI: 'discordEmoji', + ROLE_WITH_DURATION: 'roleWithDuration', + ABBREVIATED_NUMBER: 'abbreviatedNumber', + GLOBAL_USER: 'globalUser' +} as const); + +export const BlockedReasons = Object.freeze({ + ...BuiltInReasons, + DISABLED_GUILD: 'disabledGuild', + DISABLED_GLOBAL: 'disabledGlobal', + ROLE_BLACKLIST: 'roleBlacklist', + USER_GUILD_BLACKLIST: 'userGuildBlacklist', + USER_GLOBAL_BLACKLIST: 'userGlobalBlacklist', + RESTRICTED_GUILD: 'restrictedGuild', + CHANNEL_GUILD_BLACKLIST: 'channelGuildBlacklist', + CHANNEL_GLOBAL_BLACKLIST: 'channelGlobalBlacklist', + RESTRICTED_CHANNEL: 'restrictedChannel' +} as const); + +export const CommandHandlerEvents = Object.freeze({ + ...AkairoCommandHandlerEvents +} as const); + +export const moulberryBushRoleMap = deepLock([ + { name: '*', id: '792453550768390194' }, + { name: 'Admin Perms', id: '746541309853958186' }, + { name: 'Sr. Moderator', id: '782803470205190164' }, + { name: 'Moderator', id: '737308259823910992' }, + { name: 'Helper', id: '737440116230062091' }, + { name: 'Trial Helper', id: '783537091946479636' }, + { name: 'Contributor', id: '694431057532944425' }, + { name: 'Giveaway Donor', id: '784212110263451649' }, + { name: 'Giveaway (200m)', id: '810267756426690601' }, + { name: 'Giveaway (100m)', id: '801444430522613802' }, + { name: 'Giveaway (50m)', id: '787497512981757982' }, + { name: 'Giveaway (25m)', id: '787497515771232267' }, + { name: 'Giveaway (10m)', id: '787497518241153025' }, + { name: 'Giveaway (5m)', id: '787497519768403989' }, + { name: 'Giveaway (1m)', id: '787497521084891166' }, + { name: 'Suggester', id: '811922322767609877' }, + { name: 'Partner', id: '767324547312779274' }, + { name: 'Level Locked', id: '784248899044769792' }, + { name: 'No Files', id: '786421005039173633' }, + { name: 'No Reactions', id: '786421270924361789' }, + { name: 'No Links', id: '786421269356740658' }, + { name: 'No Bots', id: '786804858765312030' }, + { name: 'No VC', id: '788850482554208267' }, + { name: 'No Giveaways', id: '808265422334984203' }, + { name: 'No Support', id: '790247359824396319' } +] as const); + +export type PronounCode = keyof typeof pronounMapping; +export type Pronoun = typeof pronounMapping[PronounCode]; diff --git a/src/lib/utils/BushLogger.ts b/src/lib/utils/BushLogger.ts index 073b8e2..7d42574 100644 --- a/src/lib/utils/BushLogger.ts +++ b/src/lib/utils/BushLogger.ts @@ -1,10 +1,11 @@ import chalk from 'chalk'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { EmbedBuilder, Util, type Message, type PartialTextBasedChannelFields } from 'discord.js'; +import { EmbedBuilder, escapeMarkdown, PartialTextBasedChannelFields, type Message } from 'discord.js'; import repl, { REPLServer, REPL_MODE_STRICT } from 'repl'; import { WriteStream } from 'tty'; -import { inspect } from 'util'; import { type SendMessageType } from '../extensions/discord-akairo/BushClient.js'; +import { colors } from './BushConstants.js'; +import { getConfigChannel, inspect } from './BushUtils.js'; let REPL: REPLServer; let replGone = false; @@ -59,78 +60,78 @@ export function init() { } /** - * Custom logging utility for the bot. + * Parses the content surrounding by `<<>>` and emphasizes it with the given color or by making it bold. + * @param content The content to parse. + * @param color The color to emphasize the content with. + * @param discordFormat Whether or not to format the content for discord. + * @returns The formatted content. */ -export class BushLogger { - /** - * Parses the content surrounding by `<<>>` and emphasizes it with the given color or by making it bold. - * @param content The content to parse. - * @param color The color to emphasize the content with. - * @param discordFormat Whether or not to format the content for discord. - * @returns The formatted content. - */ - static #parseFormatting( - content: any, - color: 'blueBright' | 'blackBright' | 'redBright' | 'yellowBright' | 'greenBright' | '', - discordFormat = false - ): string | typeof content { - if (typeof content !== 'string') return content; - const newContent: Array<string> = content.split(/<<|>>/); - const tempParsedArray: Array<string> = []; - newContent.forEach((value, index) => { - if (index % 2 !== 0) { - tempParsedArray.push(discordFormat ? `**${Util.escapeMarkdown(value)}**` : color ? chalk[color](value) : value); - } else { - tempParsedArray.push(discordFormat ? Util.escapeMarkdown(value) : value); - } - }); - return tempParsedArray.join(''); - } - - /** - * Inspects the content and returns a string. - * @param content The content to inspect. - * @param depth The depth the content will inspected. Defaults to `2`. - * @param colors Whether or not to use colors in the output. Defaults to `true`. - * @returns The inspected content. - */ - static #inspectContent(content: any, depth = 2, colors = true): string { - if (typeof content !== 'string') { - return inspect(content, { depth, colors }); +function parseFormatting( + content: any, + color: 'blueBright' | 'blackBright' | 'redBright' | 'yellowBright' | 'greenBright' | '', + discordFormat = false +): string | typeof content { + if (typeof content !== 'string') return content; + const newContent: Array<string> = content.split(/<<|>>/); + const tempParsedArray: Array<string> = []; + newContent.forEach((value, index) => { + if (index % 2 !== 0) { + tempParsedArray.push(discordFormat ? `**${escapeMarkdown(value)}**` : color ? chalk[color](value) : value); + } else { + tempParsedArray.push(discordFormat ? escapeMarkdown(value) : value); } - return content; - } + }); + return tempParsedArray.join(''); +} - /** - * Strips ANSI color codes from a string. - * @param text The string to strip color codes from. - * @returns A string without ANSI color codes. - */ - static #stripColor(text: string): string { - return text.replace( - // eslint-disable-next-line no-control-regex - /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, - '' - ); +/** + * Inspects the content and returns a string. + * @param content The content to inspect. + * @param depth The depth the content will inspected. Defaults to `2`. + * @param colors Whether or not to use colors in the output. Defaults to `true`. + * @returns The inspected content. + */ +function inspectContent(content: any, depth = 2, colors = true): string { + if (typeof content !== 'string') { + return inspect(content, { depth, colors }); } + return content; +} - /** - * Generates a formatted timestamp for logging. - * @returns The formatted timestamp. - */ - static #getTimeStamp(): string { - const now = new Date(); - const hours = now.getHours(); - const minute = now.getMinutes(); - let hour = hours; - let amOrPm: 'AM' | 'PM' = 'AM'; - if (hour > 12) { - amOrPm = 'PM'; - hour = hour - 12; - } - return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 ? minute : `0${minute}`} ${amOrPm}`; +/** + * Strips ANSI color codes from a string. + * @param text The string to strip color codes from. + * @returns A string without ANSI color codes. + */ +function stripColor(text: string): string { + return text.replace( + // eslint-disable-next-line no-control-regex + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, + '' + ); +} + +/** + * Generates a formatted timestamp for logging. + * @returns The formatted timestamp. + */ +function getTimeStamp(): string { + const now = new Date(); + const hours = now.getHours(); + const minute = now.getMinutes(); + let hour = hours; + let amOrPm: 'AM' | 'PM' = 'AM'; + if (hour > 12) { + amOrPm = 'PM'; + hour = hour - 12; } + return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 ? minute : `0${minute}`} ${amOrPm}`; +} +/** + * Custom logging utility for the bot. + */ +export default { /** * Logs information. Highlight information by surrounding it in `<<>>`. * @param header The header displayed before the content, displayed in cyan. @@ -138,31 +139,31 @@ export class BushLogger { * @param sendChannel Should this also be logged to discord? Defaults to false. * @param depth The depth the content will inspected. Defaults to 0. */ - public static get log() { - return BushLogger.info; - } + 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. */ - public static async channelLog(message: SendMessageType): Promise<Message | null> { - const channel = await util.getConfigChannel('log'); + async channelLog(message: SendMessageType): Promise<Message | null> { + const channel = await 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. */ - public static async channelError(message: SendMessageType): Promise<Message | null> { - const channel = await util.getConfigChannel('error'); + async channelError(message: SendMessageType): Promise<Message | null> { + const channel = await getConfigChannel('error'); if (!channel) { void this.error( 'BushLogger', - `Could not find error channel, was originally going to send: \n${util.inspect(message, { + `Could not find error channel, was originally going to send: \n${inspect(message, { colors: true })}\n${new Error().stack?.substring(8)}`, false @@ -170,27 +171,27 @@ export class BushLogger { 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`. */ - public static debug(content: any, depth = 0): void { + debug(content: any, depth = 0): void { if (!client.config.isDevelopment) return; - const newContent = this.#inspectContent(content, depth, true); - console.log(`${chalk.bgMagenta(this.#getTimeStamp())} ${chalk.magenta('[Debug]')} ${newContent}`); - } + 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. */ - public static debugRaw(...content: any): void { + debugRaw(...content: any): void { if (!client.config.isDevelopment) return; - console.log(`${chalk.bgMagenta(this.#getTimeStamp())} ${chalk.magenta('[Debug]')}`, ...content); - } + console.log(`${chalk.bgMagenta(getTimeStamp())} ${chalk.magenta('[Debug]')}`, ...content); + }, /** * Logs verbose information. Highlight information by surrounding it in `<<>>`. @@ -199,19 +200,17 @@ export class BushLogger { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - public static async verbose(header: string, content: any, sendChannel = false, depth = 0): Promise<void> { + async verbose(header: string, content: any, sendChannel = false, depth = 0): Promise<void> { if (!client.config.logging.verbose) return; - const newContent = this.#inspectContent(content, depth, true); - console.log( - `${chalk.bgGrey(this.#getTimeStamp())} ${chalk.grey(`[${header}]`)} ${this.#parseFormatting(newContent, 'blackBright')}` - ); + const newContent = inspectContent(content, depth, true); + console.log(`${chalk.bgGrey(getTimeStamp())} ${chalk.grey(`[${header}]`)} ${parseFormatting(newContent, 'blackBright')}`); if (!sendChannel) return; const embed = new EmbedBuilder() - .setDescription(`**[${header}]** ${this.#parseFormatting(this.#stripColor(newContent), '', true)}`) - .setColor(util.colors.gray) + .setDescription(`**[${header}]** ${parseFormatting(stripColor(newContent), '', true)}`) + .setColor(colors.gray) .setTimestamp(); await this.channelLog({ embeds: [embed] }); - } + }, /** * Logs very verbose information. Highlight information by surrounding it in `<<>>`. @@ -219,23 +218,23 @@ export class BushLogger { * @param content The content to log, highlights displayed in bright black. * @param depth The depth the content will inspected. Defaults to `0`. */ - public static async superVerbose(header: string, content: any, depth = 0): Promise<void> { + async superVerbose(header: string, content: any, depth = 0): Promise<void> { if (!client.config.logging.verbose) return; - const newContent = this.#inspectContent(content, depth, true); + const newContent = inspectContent(content, depth, true); console.log( - `${chalk.bgHex('#949494')(this.#getTimeStamp())} ${chalk.hex('#949494')(`[${header}]`)} ${chalk.hex('#b3b3b3')(newContent)}` + `${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. */ - public static async superVerboseRaw(header: string, ...content: any[]): Promise<void> { + async superVerboseRaw(header: string, ...content: any[]): Promise<void> { if (!client.config.logging.verbose) return; - console.log(`${chalk.bgHex('#a3a3a3')(this.#getTimeStamp())} ${chalk.hex('#a3a3a3')(`[${header}]`)}`, ...content); - } + console.log(`${chalk.bgHex('#a3a3a3')(getTimeStamp())} ${chalk.hex('#a3a3a3')(`[${header}]`)}`, ...content); + }, /** * Logs information. Highlight information by surrounding it in `<<>>`. @@ -244,19 +243,17 @@ export class BushLogger { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - public static async info(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + async info(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { if (!client.config.logging.info) return; - const newContent = this.#inspectContent(content, depth, true); - console.log( - `${chalk.bgCyan(this.#getTimeStamp())} ${chalk.cyan(`[${header}]`)} ${this.#parseFormatting(newContent, 'blueBright')}` - ); + const newContent = inspectContent(content, depth, true); + console.log(`${chalk.bgCyan(getTimeStamp())} ${chalk.cyan(`[${header}]`)} ${parseFormatting(newContent, 'blueBright')}`); if (!sendChannel) return; const embed = new EmbedBuilder() - .setDescription(`**[${header}]** ${this.#parseFormatting(this.#stripColor(newContent), '', true)}`) - .setColor(util.colors.info) + .setDescription(`**[${header}]** ${parseFormatting(stripColor(newContent), '', true)}`) + .setColor(colors.info) .setTimestamp(); await this.channelLog({ embeds: [embed] }); - } + }, /** * Logs warnings. Highlight information by surrounding it in `<<>>`. @@ -265,22 +262,19 @@ export class BushLogger { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - public static async warn(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { - const newContent = this.#inspectContent(content, depth, true); + async warn(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + const newContent = inspectContent(content, depth, true); console.warn( - `${chalk.bgYellow(this.#getTimeStamp())} ${chalk.yellow(`[${header}]`)} ${this.#parseFormatting( - newContent, - 'yellowBright' - )}` + `${chalk.bgYellow(getTimeStamp())} ${chalk.yellow(`[${header}]`)} ${parseFormatting(newContent, 'yellowBright')}` ); if (!sendChannel) return; const embed = new EmbedBuilder() - .setDescription(`**[${header}]** ${this.#parseFormatting(this.#stripColor(newContent), '', true)}`) - .setColor(util.colors.warn) + .setDescription(`**[${header}]** ${parseFormatting(stripColor(newContent), '', true)}`) + .setColor(colors.warn) .setTimestamp(); await this.channelError({ embeds: [embed] }); - } + }, /** * Logs errors. Highlight information by surrounding it in `<<>>`. @@ -289,22 +283,19 @@ export class BushLogger { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - public static async error(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { - const newContent = this.#inspectContent(content, depth, true); + async error(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + const newContent = inspectContent(content, depth, true); console.warn( - `${chalk.bgRedBright(this.#getTimeStamp())} ${chalk.redBright(`[${header}]`)} ${this.#parseFormatting( - newContent, - 'redBright' - )}` + `${chalk.bgRedBright(getTimeStamp())} ${chalk.redBright(`[${header}]`)} ${parseFormatting(newContent, 'redBright')}` ); if (!sendChannel) return; const embed = new EmbedBuilder() - .setDescription(`**[${header}]** ${this.#parseFormatting(this.#stripColor(newContent), '', true)}`) - .setColor(util.colors.error) + .setDescription(`**[${header}]** ${parseFormatting(stripColor(newContent), '', true)}`) + .setColor(colors.error) .setTimestamp(); await this.channelError({ embeds: [embed] }); return; - } + }, /** * Logs successes. Highlight information by surrounding it in `<<>>`. @@ -313,21 +304,18 @@ export class BushLogger { * @param sendChannel Should this also be logged to discord? Defaults to `false`. * @param depth The depth the content will inspected. Defaults to `0`. */ - public static async success(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { - const newContent = this.#inspectContent(content, depth, true); + async success(header: string, content: any, sendChannel = true, depth = 0): Promise<void> { + const newContent = inspectContent(content, depth, true); console.log( - `${chalk.bgGreen(this.#getTimeStamp())} ${chalk.greenBright(`[${header}]`)} ${this.#parseFormatting( - newContent, - 'greenBright' - )}` + `${chalk.bgGreen(getTimeStamp())} ${chalk.greenBright(`[${header}]`)} ${parseFormatting(newContent, 'greenBright')}` ); if (!sendChannel) return; const embed = new EmbedBuilder() - .setDescription(`**[${header}]** ${this.#parseFormatting(this.#stripColor(newContent), '', true)}`) - .setColor(util.colors.success) + .setDescription(`**[${header}]** ${parseFormatting(stripColor(newContent), '', true)}`) + .setColor(colors.success) .setTimestamp(); await this.channelLog({ embeds: [embed] }).catch(() => {}); } -} +}; /** @typedef {PartialTextBasedChannelFields} vscodeDontDeleteMyImportTy */ diff --git a/src/lib/utils/BushUtils.ts b/src/lib/utils/BushUtils.ts new file mode 100644 index 0000000..8a84d80 --- /dev/null +++ b/src/lib/utils/BushUtils.ts @@ -0,0 +1,1058 @@ +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'; +import assert from 'assert'; +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 +} 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 + */ +export function capitalize(text: string): string { + return text.charAt(0).toUpperCase() + text.slice(1); +} + +/** + * Runs a shell command and gives the output + * @param command The shell command to run + * @returns The stdout and stderr of the shell command + */ +export async function shell(command: string): Promise<{ stdout: string; stderr: string }> { + return await promisify(exec)(command); +} + +/** + * 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 + */ +export function ordinal(n: number): string { + const s = ['th', 'st', 'nd', 'rd'], + v = n % 100; + return n + (s[(v - 20) % 10] || s[v] || s[0]); +} + +/** + * Chunks an array to the specified size + * @param arr The array to chunk + * @param perChunk The amount of items per chunk + * @returns The chunked array + */ +export function chunk<T>(arr: T[], perChunk: number): T[][] { + return arr.reduce((all, one, i) => { + const ch: number = Math.floor(i / perChunk); + (all as any[])[ch] = [].concat(all[ch] || [], one as any); + return all; + }, []); +} + +/** + * Fetches a user's uuid from the mojang api. + * @param username The username to get the uuid of. + * @returns The the uuid of the user. + */ +export async function mcUUID(username: string, dashed = false): Promise<string> { + const apiRes = (await got.get(`https://api.ashcon.app/mojang/v2/user/${username}`).json()) as UuidRes; + return dashed ? apiRes.uuid : apiRes.uuid.replace(/-/g, ''); +} + +/** + * Surrounds text in a code block with the specified language and puts it in a hastebin if its too long. + * * Embed Description Limit = 4096 characters + * * Embed Field Limit = 1024 characters + * @param code The content of the code block. + * @param length The maximum length of the code block. + * @param language The language of the code. + * @param substr Whether or not to substring the code if it is too long. + * @returns The generated code block + */ +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. + */ +function getDefaultInspectOptions(options?: BushInspectOptions): BushInspectOptions { + return { + showHidden: options?.showHidden ?? false, + depth: options?.depth ?? 2, + colors: options?.colors ?? false, + customInspect: options?.customInspect ?? true, + showProxy: options?.showProxy ?? false, + maxArrayLength: options?.maxArrayLength ?? Infinity, + maxStringLength: options?.maxStringLength ?? Infinity, + breakLength: options?.breakLength ?? 80, + compact: options?.compact ?? 3, + sorted: options?.sorted ?? false, + getters: options?.getters ?? true + }; +} + +/** + * 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. + * @returns The inspected object. + */ +export function inspect(object: any, options?: BushInspectOptions): string { + const optionsWithDefaults = getDefaultInspectOptions(options); + + if (!optionsWithDefaults.inspectStrings && typeof object === 'string') return object; + + return inspectUtil(object, optionsWithDefaults); +} + +/** + * 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. + * @returns The message sent. + */ +export async function slashRespond( + interaction: CommandInteraction, + responseOptions: SlashSendMessageType | SlashEditMessageType +): Promise<Message | APIMessage | undefined> { + const newResponseOptions = typeof responseOptions === 'string' ? { content: responseOptions } : responseOptions; + if (interaction.replied || interaction.deferred) { + delete (newResponseOptions as InteractionReplyOptions).ephemeral; // Cannot change a preexisting message to be ephemeral + return (await interaction.editReply(newResponseOptions)) as Message | APIMessage; + } else { + await interaction.reply(newResponseOptions); + return await interaction.fetchReply().catch(() => undefined); + } +} + +/** + * 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. + * @param ifEmpty What to return if the array is empty. + * @returns The combined elements or `ifEmpty`. + * + * @example + * const permissions = oxford(['Administrator', 'SendMessages', 'ManageMessages'], 'and', 'none'); + * console.log(permissions); // Administrator, SendMessages and ManageMessages + */ +export function oxford(array: string[], conjunction: string, ifEmpty?: string): string | undefined { + const l = array.length; + if (!l) return ifEmpty; + if (l < 2) return array[0]; + if (l < 3) return array.join(` ${conjunction} `); + array = array.slice(); + array[l - 1] = `${conjunction} ${array[l - 1]}`; + return array.join(', '); +} + +/** + * 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. + * @param value The element to add/remove from the array. + */ +export function addOrRemoveFromArray<T>(action: 'add' | 'remove', array: T[], value: T): T[] { + const set = new Set(array); + action === 'add' ? set.add(value) : set.delete(value); + return [...set]; +} + +/** + * Remove an item from an array. All duplicates will be removed. + * @param array The array to remove an element from. + * @param value The element to remove from the array. + */ +export function removeFromArray<T>(array: T[], value: T): T[] { + return addOrRemoveFromArray('remove', array, value); +} + +/** + * Add an item from an array. All duplicates will be removed. + * @param array The array to add an element to. + * @param value The element to add to the array. + */ +export function addToArray<T>(array: T[], value: T): T[] { + return addOrRemoveFromArray('add', array, value); +} + +/** + * Surrounds a string to the begging an end of each element in an array. + * @param array The array you want to surround. + * @param surroundChar1 The character placed in the beginning of the element. + * @param surroundChar2 The character placed in the end of the element. Defaults to `surroundChar1`. + */ +export function surroundArray(array: string[], surroundChar1: string, surroundChar2?: string): string[] { + return array.map((a) => `${surroundChar1}${a}${surroundChar2 ?? surroundChar1}`); +} + +/** + * Gets the duration from a specified string. + * @param content The string to look for a duration in. + * @param remove Whether or not to remove the duration from the original string. + * @returns The {@link ParsedDuration}. + */ +export function parseDuration(content: string, remove = true): ParsedDuration { + if (!content) return { duration: 0, content: null }; + + // eslint-disable-next-line prefer-const + let duration: number | null = null; + // Try to reduce false positives by requiring a space before the duration, this makes sure it still matches if it is + // in the beginning of the argument + let contentWithoutTime = ` ${content}`; + + for (const unit in timeUnits) { + const regex = timeUnits[unit as keyof typeof timeUnits].match; + const match = regex.exec(contentWithoutTime); + const value = Number(match?.groups?.[unit]); + if (!isNaN(value)) duration! += value * timeUnits[unit as keyof typeof timeUnits].value; + + if (remove) contentWithoutTime = contentWithoutTime.replace(regex, ''); + } + // remove the space added earlier + if (contentWithoutTime.startsWith(' ')) contentWithoutTime.replace(' ', ''); + return { duration, content: contentWithoutTime }; +} + +/** + * Converts a duration in milliseconds to a human readable form. + * @param duration The duration in milliseconds to convert. + * @param largest The maximum number of units to display for the duration. + * @param round Whether or not to round the smallest unit displayed. + * @returns A humanized string of the duration. + */ +export function humanizeDuration(duration: number, largest?: number, round = true): string { + if (largest) return humanizeDurationMod(duration, { language: 'en', maxDecimalPoints: 2, largest, round })!; + else return humanizeDurationMod(duration, { language: 'en', maxDecimalPoints: 2, round })!; +} + +/** + * Creates a formatted relative timestamp from a duration in milliseconds. + * @param duration The duration in milliseconds. + * @returns The formatted relative timestamp. + */ +export function timestampDuration(duration: number): string { + return `<t:${Math.round(new Date().getTime() / 1_000 + duration / 1_000)}:R>`; +} + +/** + * Creates a timestamp from a date. + * @param date The date to create a timestamp from. + * @param style The style of the timestamp. + * @returns The formatted timestamp. + * + * @see + * **Styles:** + * - **t**: Short Time ex. `16:20` + * - **T**: Long Time ex. `16:20:30 ` + * - **d**: Short Date ex. `20/04/2021` + * - **D**: Long Date ex. `20 April 2021` + * - **f**: Short Date/Time ex. `20 April 2021 16:20` + * - **F**: Long Date/Time ex. `Tuesday, 20 April 2021 16:20` + * - **R**: Relative Time ex. `2 months ago` + */ +export function timestamp<D extends Date | undefined | null>( + date: D, + style: TimestampStyle = 'f' +): D extends Date ? string : undefined { + if (!date) return date as unknown as D extends Date ? string : undefined; + return `<t:${Math.round(date.getTime() / 1_000)}:${style}>` as unknown as D extends Date ? string : undefined; +} + +/** + * Creates a human readable representation between a date and the current time. + * @param date The date to be compared with the current time. + * @param largest The maximum number of units to display for the duration. + * @param round Whether or not to round the smallest unit displayed. + * @returns A humanized string of the delta. + */ +export function dateDelta(date: Date, largest?: number, round = true): string { + return humanizeDuration(new Date().getTime() - date.getTime(), largest ?? 3, round); +} + +/** + * Combines {@link timestamp} and {@link dateDelta} + * @param date The date to be compared with the current time. + * @param style The style of the timestamp. + * @returns The formatted timestamp. + * + * @see + * **Styles:** + * - **t**: Short Time ex. `16:20` + * - **T**: Long Time ex. `16:20:30 ` + * - **d**: Short Date ex. `20/04/2021` + * - **D**: Long Date ex. `20 April 2021` + * - **f**: Short Date/Time ex. `20 April 2021 16:20` + * - **F**: Long Date/Time ex. `Tuesday, 20 April 2021 16:20` + * - **R**: Relative Time ex. `2 months ago` + */ +export function timestampAndDelta(date: Date, style: TimestampStyle = 'D'): string { + return `${timestamp(date, style)} (${dateDelta(date)} ago)`; +} + +/** + * Convert a hex code to an rbg value. + * @param hex The hex code to convert. + * @returns The rbg value. + */ +export function hexToRgb(hex: string): string { + const arrBuff = new ArrayBuffer(4); + const vw = new DataView(arrBuff); + vw.setUint32(0, parseInt(hex, 16), false); + const arrByte = new Uint8Array(arrBuff); + + return `${arrByte[1]}, ${arrByte[2]}, ${arrByte[3]}`; +} + +/** + * Wait an amount in milliseconds. + * @returns A promise that resolves after the specified amount of milliseconds + */ +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. + */ +export function getMethods(obj: Record<string, any>): string { + // modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class/31055217#31055217 + let props: string[] = []; + let obj_: Record<string, any> = new Object(obj); + + do { + const l = Object.getOwnPropertyNames(obj_) + .concat(Object.getOwnPropertySymbols(obj_).map((s) => s.toString())) + .sort() + .filter( + (p, i, arr) => + typeof Object.getOwnPropertyDescriptor(obj_, p)?.['get'] !== 'function' && // ignore getters + typeof Object.getOwnPropertyDescriptor(obj_, p)?.['set'] !== 'function' && // ignore setters + typeof obj_[p] === 'function' && // only the methods + p !== 'constructor' && // not the constructor + (i == 0 || p !== arr[i - 1]) && // not overriding in this prototype + props.indexOf(p) === -1 // not overridden in a child + ); + + const reg = /\(([\s\S]*?)\)/; + props = props.concat( + l.map( + (p) => + `${obj_[p] && obj_[p][Symbol.toStringTag] === 'AsyncFunction' ? 'async ' : ''}function ${p}(${ + reg.exec(obj_[p].toString())?.[1] + ? reg + .exec(obj_[p].toString())?.[1] + .split(', ') + .map((arg) => arg.split('=')[0].trim()) + .join(', ') + : '' + });` + ) + ); + } while ( + (obj_ = Object.getPrototypeOf(obj_)) && // walk-up the prototype chain + Object.getPrototypeOf(obj_) // not the the Object prototype methods (hasOwnProperty, etc...) + ); + + return props.join('\n'); +} + +/** + * List the symbols of an object. + * @param obj The object to get the symbols of. + * @returns An array of the symbols of the object. + */ +export function getSymbols(obj: Record<string, any>): symbol[] { + let symbols: symbol[] = []; + let obj_: Record<string, any> = new Object(obj); + + do { + const l = Object.getOwnPropertySymbols(obj_).sort(); + + symbols = [...symbols, ...l]; + } while ( + (obj_ = Object.getPrototypeOf(obj_)) && // walk-up the prototype chain + Object.getPrototypeOf(obj_) // not the the Object prototype methods (hasOwnProperty, etc...) + ); + + return symbols; +} + +/** + * 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. + * @returns The missing permissions or null if none are missing. + */ +export function userGuildPermCheck( + message: CommandMessage | SlashMessage, + permissions: typeof PermissionFlagsBits[keyof typeof PermissionFlagsBits][] +): PermissionsString[] | null { + if (!message.inGuild()) return null; + const missing = message.member?.permissions.missing(permissions) ?? []; + + return missing.length ? missing : null; +} + +/** + * Check if the client has certain permissions in the guild (doesn't check channel permissions). + * @param message The message to check the client user from. + * @param permissions The permissions to check for. + * @returns The missing permissions or null if none are missing. + */ +export function clientGuildPermCheck(message: CommandMessage | SlashMessage, permissions: bigint[]): PermissionsString[] | null { + const missing = message.guild?.members.me?.permissions.missing(permissions) ?? []; + + return missing.length ? missing : null; +} + +/** + * Check if the client has permission to send messages in the channel as well as check if they have other permissions + * in the guild (or the channel if `checkChannel` is `true`). + * @param message The message to check the client user from. + * @param permissions The permissions to check for. + * @param checkChannel Whether to check the channel permissions instead of the guild permissions. + * @returns The missing permissions or null if none are missing. + */ +export function clientSendAndPermCheck( + message: CommandMessage | SlashMessage, + permissions: bigint[] = [], + checkChannel = false +): PermissionsString[] | null { + const missing: PermissionsString[] = []; + const sendPerm = message.channel!.isThread() ? 'SendMessages' : 'SendMessagesInThreads'; + if (!message.inGuild()) return null; + + if (!message.guild.members.me!.permissionsIn(message.channel!.id).has(sendPerm)) missing.push(sendPerm); + + missing.push( + ...(checkChannel + ? message.guild!.members.me!.permissionsIn(message.channel!.id!).missing(permissions) + : clientGuildPermCheck(message, permissions) ?? []) + ); + + 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 }; +export { DiscordConstants as discordConstants }; +export { AkairoUtil as akairo }; + +/** + * The link to invite the bot with all permissions. + */ +export function invite(client: BushClient) { + return client.generateInvite({ + permissions: + PermissionsBitField.All - + PermissionFlagsBits.UseEmbeddedActivities - + PermissionFlagsBits.ViewGuildInsights - + PermissionFlagsBits.Stream, + scopes: [OAuth2Scopes.Bot, OAuth2Scopes.ApplicationsCommands] + }); +} + +/** + * Asset multiple statements at a time. + * @param args + */ +export function assertAll(...args: any[]): void { + for (let i = 0; i < args.length; i++) { + assert(args[i], `assertAll index ${i} failed`); + } +} + +/** + * Casts a string to a duration and reason for slash commands. + * @param arg The argument received. + * @param message The message that triggered the command. + * @returns The casted argument. + */ +export async function castDurationContent( + arg: string | ParsedDuration | null, + message: CommandMessage | SlashMessage +): Promise<ParsedDurationRes> { + const res = typeof arg === 'string' ? await Arg.cast('contentWithDuration', message, arg) : arg; + + return { duration: res?.duration ?? 0, content: res?.content ?? '' }; +} + +/** + * Casts a string to a the specified argument type. + * @param type The type of the argument to cast to. + * @param arg The argument received. + * @param message The message that triggered the command. + * @returns The casted argument. + */ +export async function cast<T extends keyof BaseBushArgumentType>( + type: T, + arg: BaseBushArgumentType[T] | string, + message: CommandMessage | SlashMessage +) { + return typeof arg === 'string' ? await Arg.cast(type, message, arg) : arg; +} + +/** + * Overflows the description of an embed into multiple embeds. + * @param embed The options to be applied to the (first) embed. + * @param lines Each line of the description as an element in an array. + */ +export function overflowEmbed(embed: Omit<APIEmbed, 'description'>, lines: string[], maxLength = 4096): EmbedBuilder[] { + const embeds: EmbedBuilder[] = []; + + const makeEmbed = () => { + embeds.push(new EmbedBuilder().setColor(embed.color ?? null)); + return embeds.at(-1)!; + }; + + for (const line of lines) { + let current = embeds.length ? embeds.at(-1)! : makeEmbed(); + let joined = current.data.description ? `${current.data.description}\n${line}` : line; + if (joined.length > maxLength) { + current = makeEmbed(); + joined = line; + } + + current.setDescription(joined); + } + + if (!embeds.length) makeEmbed(); + + if (embed.author) embeds.at(0)?.setAuthor(embed.author); + if (embed.title) embeds.at(0)?.setTitle(embed.title); + if (embed.url) embeds.at(0)?.setURL(embed.url); + if (embed.fields) embeds.at(-1)?.setFields(embed.fields); + if (embed.thumbnail) embeds.at(-1)?.setThumbnail(embed.thumbnail.url); + if (embed.footer) embeds.at(-1)?.setFooter(embed.footer); + if (embed.image) embeds.at(-1)?.setImage(embed.image.url); + if (embed.timestamp) embeds.at(-1)?.setTimestamp(new Date(embed.timestamp)); + + 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. + * @param colors Whether to use colors in the output. + * @returns The formatted error. + */ +export function formatError(error: Error | any, colors = false): string { + if (!error) return error; + if (typeof error !== 'object') return String.prototype.toString.call(error); + if ( + getSymbols(error) + .map((s) => s.toString()) + .includes('Symbol(nodejs.util.inspect.custom)') + ) + return inspect(error, { colors }); + + return error.stack; +} + +interface HastebinRes { + key: string; +} + +export interface UuidRes { + uuid: string; + username: string; + username_history?: { username: string }[] | null; + textures: { + custom: boolean; + slim: boolean; + skin: { + url: string; + data: string; + }; + raw: { + value: string; + signature: string; + }; + }; + 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; +} + +export interface ParsedDurationRes { + duration: number; + content: string; +} + +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 64979b5..06eb245 100644 --- a/src/listeners/bush/appealListener.ts +++ b/src/listeners/bush/appealListener.ts @@ -1,5 +1,5 @@ /* eslint-disable no-control-regex */ -import { BushListener, ModLog, type BushClientEvents } from '#lib'; +import { BushListener, colors, mappings, ModLog, type BushClientEvents } from '#lib'; import assert from 'assert'; import { EmbedBuilder } from 'discord.js'; import UserInfoCommand from '../../commands/info/userInfo.js'; @@ -14,8 +14,8 @@ export default class AppealListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']): Promise<any> { - if (!client.config.isProduction || !message.inGuild() || message.guildId !== client.consts.mappings.guilds.bush) return; + public async exec(...[message]: BushClientEvents['messageCreate']): Promise<any> { + if (!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; @@ -32,7 +32,7 @@ export default class AppealListener extends BushListener { embeds: [ new EmbedBuilder() .setTimestamp() - .setColor(util.colors.error) + .setColor(colors.error) .setTitle( `${message.embeds[0].fields!.find((f) => f.name === 'What type of punishment are you appealing?')!.value} appeal` ) @@ -54,7 +54,7 @@ export default class AppealListener extends BushListener { const embed = new EmbedBuilder() .setTimestamp() - .setColor(util.colors.default) + .setColor(colors.default) .setTitle(`${message.embeds[0].fields!.find((f) => f.name === 'What type of punishment are you appealing?')!.value} appeal`) .setThumbnail(user.displayAvatarURL()); diff --git a/src/listeners/bush/joinAutoBan.ts b/src/listeners/bush/joinAutoBan.ts index 83efa06..eae18d3 100644 --- a/src/listeners/bush/joinAutoBan.ts +++ b/src/listeners/bush/joinAutoBan.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushListener, type BushClientEvents } from '#lib'; +import { AllowedMentions, BushListener, colors, emojis, format, getShared, mappings, type BushClientEvents } from '#lib'; import { TextChannel } from 'discord.js'; export default class JoinAutoBanListener extends BushListener { @@ -10,14 +10,14 @@ export default class JoinAutoBanListener extends BushListener { }); } - public override async exec(...[member]: BushClientEvents['guildMemberAdd']): Promise<void> { + public async exec(...[member]: BushClientEvents['guildMemberAdd']): Promise<void> { if (!client.config.isProduction) return; - if (member.guild.id !== client.consts.mappings.guilds.bush) 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 = util.getShared('autoBanCode'); + const code = getShared('autoBanCode'); if (!code) return; if (eval(code)) { const res = await member.bushBan({ @@ -28,7 +28,7 @@ export default class JoinAutoBanListener extends BushListener { if (!['success', 'failed to dm'].includes(res)) { return await guild.error( 'nameAutoBan', - `Failed to auto ban ${util.format.input(member.user.tag)} for blacklisted name, with error: ${util.format.input(res)}.` + `Failed to auto ban ${format.input(member.user.tag)} for blacklisted name, with error: ${format.input(res)}.` ); } @@ -38,7 +38,7 @@ export default class JoinAutoBanListener extends BushListener { { title: 'Name Auto Ban - User Join', description: `**User:** ${member.user} (${member.user.tag})\n **Action:** Banned for blacklisted name.`, - color: util.colors.red, + color: colors.red, author: { name: member.user.tag, icon_url: member.displayAvatarURL() @@ -50,8 +50,8 @@ export default class JoinAutoBanListener extends BushListener { const content = res === 'failed to dm' - ? `${util.emojis.warn} Banned ${util.format.input(member.user.tag)} however I could not send them a dm.` - : `${util.emojis.success} Successfully banned ${util.format.input(member.user.tag)}.`; + ? `${emojis.warn} Banned ${format.input(member.user.tag)} however I could not send them a dm.` + : `${emojis.success} Successfully banned ${format.input(member.user.tag)}.`; (<TextChannel>guild.channels.cache.find((c) => c.name === 'general')) ?.send({ content, allowedMentions: AllowedMentions.none() }) diff --git a/src/listeners/bush/supportThread.ts b/src/listeners/bush/supportThread.ts index 46ac96f..3651409 100644 --- a/src/listeners/bush/supportThread.ts +++ b/src/listeners/bush/supportThread.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { stripIndent } from '#tags'; import assert from 'assert'; import { EmbedBuilder, MessageType, PermissionFlagsBits, TextChannel } from 'discord.js'; @@ -12,7 +12,7 @@ export default class SupportThreadListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']): Promise<void | undefined> { + public async exec(...[message]: BushClientEvents['messageCreate']): Promise<void | undefined> { if (!client.config.isProduction || !message.inGuild()) return; if (![MessageType.Default, MessageType.Reply].includes(message.type)) return; if (message.thread) return; @@ -50,7 +50,7 @@ export default class SupportThreadListener extends BushListener { Please make sure you have the latest version found in <#693586404256645231>. Additionally if you need help installing the mod be sure to read <#737444942724726915> for a guide on how to do so.` ) - .setColor(util.colors.Blurple); + .setColor(colors.Blurple); void thread .send({ embeds: [embed] }) .then(() => diff --git a/src/listeners/bush/userUpdateAutoBan.ts b/src/listeners/bush/userUpdateAutoBan.ts index b9485b3..07381e0 100644 --- a/src/listeners/bush/userUpdateAutoBan.ts +++ b/src/listeners/bush/userUpdateAutoBan.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushListener, type BushClientEvents } from '#lib'; +import { AllowedMentions, BushListener, colors, emojis, format, getShared, mappings, type BushClientEvents } from '#lib'; import { GuildMember, type TextChannel } from 'discord.js'; export default class UserUpdateAutoBanListener extends BushListener { @@ -10,16 +10,16 @@ export default class UserUpdateAutoBanListener extends BushListener { }); } - public override async exec(...[_oldUser, newUser]: BushClientEvents['userUpdate']): Promise<void> { + public async exec(...[_oldUser, newUser]: BushClientEvents['userUpdate']): Promise<void> { if (!client.config.isProduction) return; // eslint-disable-next-line @typescript-eslint/no-unused-vars const user = newUser; - const code = util.getShared('autoBanCode'); + const code = getShared('autoBanCode'); if (!code) return; if (eval(code)) { const member = await client.guilds.cache - .get(client.consts.mappings.guilds.bush) + .get(mappings.guilds.bush) ?.members.fetch(newUser.id) .catch(() => undefined); if (!member || !(member instanceof GuildMember)) return; @@ -34,7 +34,7 @@ export default class UserUpdateAutoBanListener extends BushListener { if (!['success', 'failed to dm'].includes(res)) { return await guild.error( 'nameAutoBan', - `Failed to auto ban ${util.format.input(member.user.tag)} for blacklisted name, with error: ${util.format.input(res)}.` + `Failed to auto ban ${format.input(member.user.tag)} for blacklisted name, with error: ${format.input(res)}.` ); } @@ -44,7 +44,7 @@ export default class UserUpdateAutoBanListener extends BushListener { { title: 'Name Auto Ban - User Update', description: `**User:** ${member.user} (${member.user.tag})\n **Action:** Banned for using blacklisted name.`, - color: util.colors.red, + color: colors.red, author: { name: member.user.tag, icon_url: member.displayAvatarURL() @@ -56,8 +56,8 @@ export default class UserUpdateAutoBanListener extends BushListener { const content = res === 'failed to dm' - ? `${util.emojis.warn} Banned ${util.format.input(member.user.tag)} however I could not send them a dm.` - : `${util.emojis.success} Successfully banned ${util.format.input(member.user.tag)}.`; + ? `${emojis.warn} Banned ${format.input(member.user.tag)} however I could not send them a dm.` + : `${emojis.success} Successfully banned ${format.input(member.user.tag)}.`; (<TextChannel>guild.channels.cache.find((c) => c.name === 'general')) ?.send({ content, allowedMentions: AllowedMentions.none() }) diff --git a/src/listeners/client/akairoDebug.ts b/src/listeners/client/akairoDebug.ts index 0cb57a5..208e289 100644 --- a/src/listeners/client/akairoDebug.ts +++ b/src/listeners/client/akairoDebug.ts @@ -9,7 +9,7 @@ export default class DiscordJsDebugListener extends BushListener { }); } - public override async exec(...[message, ...other]: BushClientEvents['debug']): Promise<void> { + 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); diff --git a/src/listeners/client/dcjsDebug.ts b/src/listeners/client/dcjsDebug.ts index 52406c1..56a9eab 100644 --- a/src/listeners/client/dcjsDebug.ts +++ b/src/listeners/client/dcjsDebug.ts @@ -9,7 +9,7 @@ export default class DiscordJsDebugListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['debug']): Promise<void> { + public async exec(...[message]: BushClientEvents['debug']): Promise<void> { void client.console.superVerbose('dc.js-debug', message); } } diff --git a/src/listeners/client/dcjsError.ts b/src/listeners/client/dcjsError.ts index 4908720..c3cf8f0 100644 --- a/src/listeners/client/dcjsError.ts +++ b/src/listeners/client/dcjsError.ts @@ -9,7 +9,7 @@ export default class DiscordJsErrorListener extends BushListener { }); } - public override async exec(...[error]: BushClientEvents['error']): Promise<void> { + public async exec(...[error]: BushClientEvents['error']): Promise<void> { void client.console.superVerbose('dc.js-error', error); } } diff --git a/src/listeners/client/dcjsWarn.ts b/src/listeners/client/dcjsWarn.ts index cc2b30f..6c13630 100644 --- a/src/listeners/client/dcjsWarn.ts +++ b/src/listeners/client/dcjsWarn.ts @@ -9,7 +9,7 @@ export default class DiscordJsWarnListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['warn']): Promise<void> { + public async exec(...[message]: BushClientEvents['warn']): Promise<void> { void client.console.superVerbose('dc.js-warn', message); } } diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts index c550c6b..ae9919c 100644 --- a/src/listeners/client/ready.ts +++ b/src/listeners/client/ready.ts @@ -11,7 +11,7 @@ export default class ReadyListener extends BushListener { } // eslint-disable-next-line no-empty-pattern - public override async exec(...[]: BushClientEvents['ready']) { + public async exec(...[]: BushClientEvents['ready']) { process.emit('ready' as any); const tag = `<<${client.user?.tag}>>`, diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index feb85d1..8a05d39 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -1,4 +1,14 @@ -import { BushListener, type BushCommand, type BushCommandHandlerEvents, type CommandMessage, type SlashMessage } from '#lib'; +import { + BlockedReasons, + BushListener, + emojis, + format, + oxford, + type BushCommand, + type BushCommandHandlerEvents, + type CommandMessage, + type SlashMessage +} from '#lib'; import { type InteractionReplyOptions, type MessagePayload, type ReplyMessageOptions } from 'discord.js'; export default class CommandBlockedListener extends BushListener { @@ -10,7 +20,7 @@ export default class CommandBlockedListener extends BushListener { }); } - public override async exec(...[message, command, reason]: BushCommandHandlerEvents['commandBlocked']) { + public async exec(...[message, command, reason]: BushCommandHandlerEvents['commandBlocked']) { return await CommandBlockedListener.handleBlocked(message, command, reason); } @@ -24,85 +34,84 @@ export default class CommandBlockedListener extends BushListener { } was blocked because <<${reason}>>.`, true ); - const reasons = client.consts.BlockedReasons; switch (reason) { - case reasons.OWNER: { + case BlockedReasons.OWNER: { return await respond({ - content: `${util.emojis.error} Only my developers can run the ${util.format.input(command!.id)} command.`, + content: `${emojis.error} Only my developers can run the ${format.input(command!.id)} command.`, ephemeral: true }); } - case reasons.SUPER_USER: { + case BlockedReasons.SUPER_USER: { return await respond({ - content: `${util.emojis.error} You must be a superuser to run the ${util.format.input(command!.id)} command.`, + content: `${emojis.error} You must be a superuser to run the ${format.input(command!.id)} command.`, ephemeral: true }); } - case reasons.DISABLED_GLOBAL: { + case BlockedReasons.DISABLED_GLOBAL: { return await respond({ - content: `${util.emojis.error} My developers disabled the ${util.format.input(command!.id)} command.`, + content: `${emojis.error} My developers disabled the ${format.input(command!.id)} command.`, ephemeral: true }); } - case reasons.DISABLED_GUILD: { + case BlockedReasons.DISABLED_GUILD: { return await respond({ - content: `${util.emojis.error} The ${util.format.input( - command!.id - )} command is currently disabled in ${util.format.input(message.guild!.name)}.`, + content: `${emojis.error} The ${format.input(command!.id)} command is currently disabled in ${format.input( + message.guild!.name + )}.`, ephemeral: true }); } - case reasons.CHANNEL_GLOBAL_BLACKLIST: - case reasons.CHANNEL_GUILD_BLACKLIST: + case BlockedReasons.CHANNEL_GLOBAL_BLACKLIST: + case BlockedReasons.CHANNEL_GUILD_BLACKLIST: return isSlash ? await respond({ - content: `${util.emojis.error} You cannot use this bot in this channel.`, + content: `${emojis.error} You cannot use this bot in this channel.`, ephemeral: true }) - : await (message as CommandMessage).react(util.emojis.cross); - case reasons.USER_GLOBAL_BLACKLIST: - case reasons.USER_GUILD_BLACKLIST: + : await (message as CommandMessage).react(emojis.cross); + case BlockedReasons.USER_GLOBAL_BLACKLIST: + case BlockedReasons.USER_GUILD_BLACKLIST: return isSlash ? await respond({ - content: `${util.emojis.error} You are blacklisted from using this bot.`, + content: `${emojis.error} You are blacklisted from using this bot.`, ephemeral: true }) - : await (message as CommandMessage).react(util.emojis.cross); - case reasons.ROLE_BLACKLIST: { + : await (message as CommandMessage).react(emojis.cross); + case BlockedReasons.ROLE_BLACKLIST: { return isSlash ? await respond({ - content: `${util.emojis.error} One of your roles blacklists you from using this bot.`, + content: `${emojis.error} One of your roles blacklists you from using this bot.`, ephemeral: true }) - : await (message as CommandMessage).react(util.emojis.cross); + : await (message as CommandMessage).react(emojis.cross); } - case reasons.RESTRICTED_CHANNEL: { + case BlockedReasons.RESTRICTED_CHANNEL: { if (!command) break; const channels = command.restrictedChannels; const names: string[] = []; channels!.forEach((c) => { names.push(`<#${c}>`); }); - const pretty = util.oxford(names, 'and'); + const pretty = oxford(names, 'and'); return await respond({ - content: `${util.emojis.error} ${util.format.input(command!.id)} can only be run in ${pretty}.`, + content: `${emojis.error} ${format.input(command!.id)} can only be run in ${pretty}.`, ephemeral: true }); } - case reasons.RESTRICTED_GUILD: { + case BlockedReasons.RESTRICTED_GUILD: { if (!command) break; const guilds = command.restrictedGuilds; - const names = guilds!.map((g) => util.format.input(client.guilds.cache.get(g)?.name ?? g)); - const pretty = util.oxford(names, 'and'); + const names = guilds!.map((g) => format.input(client.guilds.cache.get(g)?.name ?? g)); + const pretty = oxford(names, 'and'); return await respond({ - content: `${util.emojis.error} ${util.format.input(command!.id)} can only be run in ${pretty}.`, + content: `${emojis.error} ${format.input(command!.id)} can only be run in ${pretty}.`, ephemeral: true }); } default: { return await respond({ - content: `${util.emojis.error} Command blocked with reason ${util.format.input(reason ?? 'unknown')}.`, + content: `${emojis.error} Command blocked with reason ${format.input(reason ?? 'unknown')}.`, ephemeral: true }); } diff --git a/src/listeners/commands/commandCooldown.ts b/src/listeners/commands/commandCooldown.ts index 92b0abe..74dd4eb 100644 --- a/src/listeners/commands/commandCooldown.ts +++ b/src/listeners/commands/commandCooldown.ts @@ -9,7 +9,7 @@ export default class CommandCooldownListener extends BushListener { }); } - public override async exec(...[message, command, remaining]: BushCommandHandlerEvents['cooldown']) { + public async exec(...[message, command, remaining]: BushCommandHandlerEvents['cooldown']) { void client.console.info( 'commandCooldown', `<<${message.author.tag}>> tried to run <<${ diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index 878e459..ca6565e 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -1,6 +1,16 @@ -import { SlashMessage, type BushCommandHandlerEvents } from '#lib'; +import { + capitalize, + colors, + format, + formatError, + inspectAndRedact, + inspectCleanRedactCodeblock, + inspectCleanRedactHaste, + SlashMessage, + type BushCommandHandlerEvents +} from '#lib'; import { type AkairoMessage, type Command } from 'discord-akairo'; -import { ChannelType, EmbedBuilder, Formatters, GuildTextBasedChannel, type Message } from 'discord.js'; +import { ChannelType, EmbedBuilder, escapeInlineCode, Formatters, GuildTextBasedChannel, type Message } from 'discord.js'; import { BushListener } from '../../lib/extensions/discord-akairo/BushListener.js'; export default class CommandErrorListener extends BushListener { @@ -12,7 +22,7 @@ export default class CommandErrorListener extends BushListener { }); } - public override exec(...[error, message, command]: BushCommandHandlerEvents['error']) { + public exec(...[error, message, command]: BushCommandHandlerEvents['error']) { return CommandErrorListener.handleError(error, message, command); } @@ -49,7 +59,7 @@ export default class CommandErrorListener extends BushListener { `${isSlash ? 'slashC' : 'c'}ommandError`, `an error occurred with the <<${command}>> ${isSlash ? 'slash ' : ''}command in <<${channel}>> triggered by <<${ message?.author?.tag - }>>:\n${util.formatError(error, true)})}`, + }>>:\n${formatError(error, true)})}`, false ); @@ -127,16 +137,16 @@ export default class CommandErrorListener extends BushListener { stack: string; } ): EmbedBuilder[] { - const embeds = [new EmbedBuilder().setColor(util.colors.error)]; + const embeds = [new EmbedBuilder().setColor(colors.error)]; if (options.type === 'command-user') { embeds[0] .setTitle('An Error Occurred') .setDescription( `Oh no! ${ options.command - ? `While running the ${options.isSlash ? 'slash ' : ''}command ${util.format.input(options.command.id)}, a` + ? `While running the ${options.isSlash ? 'slash ' : ''}command ${format.input(options.command.id)}, a` : 'A' - }n error occurred. Please give the developers code ${util.format.input(`${options.errorNum}`)}.` + }n error occurred. Please give the developers code ${format.input(`${options.errorNum}`)}.` ) .setTimestamp(); return embeds; @@ -155,11 +165,11 @@ export default class CommandErrorListener extends BushListener { description.push(...options.haste); - embeds.push(new EmbedBuilder().setColor(util.colors.error).setTimestamp().setDescription(options.stack.substring(0, 4000))); + embeds.push(new EmbedBuilder().setColor(colors.error).setTimestamp().setDescription(options.stack.substring(0, 4000))); if (description.length) embeds[0].setDescription(description.join('\n').substring(0, 4000)); if (options.type === 'command-dev' || options.type === 'command-log') - embeds[0].setTitle(`${options.isSlash ? 'Slash ' : ''}CommandError #${util.format.input(`${options.errorNum}`)}`); + embeds[0].setTitle(`${options.isSlash ? 'Slash ' : ''}CommandError #${format.input(`${options.errorNum}`)}`); else if (options.type === 'uncaughtException') embeds[0].setTitle(`${options.context ? `[${Formatters.bold(options.context)}] An Error Occurred` : 'Uncaught Exception'}`); else if (options.type === 'unhandledRejection') @@ -199,7 +209,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(util.inspectCleanRedactHaste((error as any)[element], inspectOptions)); + promises.push(inspectCleanRedactHaste((error as any)[element], inspectOptions)); } } @@ -218,14 +228,14 @@ export default class CommandErrorListener extends BushListener { if (['stack', 'name', 'message'].includes(element)) continue; else { ret.push( - `**Error ${util.capitalizeFirstLetter(element)}:** ${ - typeof (error as any)[element] === 'object' + `**Error ${capitalize(element)}:** ${ + typeof error[element] === 'object' ? `${ pair[element].url ? `[haste](${pair[element].url})${pair[element].error ? ` - ${pair[element].error}` : ''}` : pair[element].error }` - : `\`${util.discord.escapeInlineCode(util.inspectAndRedact((error as any)[element], inspectOptions))}\`` + : `\`${escapeInlineCode(inspectAndRedact((error as any)[element], inspectOptions))}\`` }` ); } @@ -234,7 +244,7 @@ export default class CommandErrorListener extends BushListener { } public static async getErrorStack(error: Error | any): Promise<string> { - return await util.inspectCleanRedactCodeblock(error, 'js', { colors: false }, 4000); + return await inspectCleanRedactCodeblock(error, 'js', { colors: false }, 4000); } } diff --git a/src/listeners/commands/commandLocked.ts b/src/listeners/commands/commandLocked.ts index 285eb50..22ed8e1 100644 --- a/src/listeners/commands/commandLocked.ts +++ b/src/listeners/commands/commandLocked.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushCommandHandlerEvents } from '#lib'; +import { BushListener, emojis, format, type BushCommandHandlerEvents } from '#lib'; export default class CommandLockedListener extends BushListener { public constructor() { @@ -9,9 +9,9 @@ export default class CommandLockedListener extends BushListener { }); } - public override async exec(...[message, command]: BushCommandHandlerEvents['commandLocked']) { + public async exec(...[message, command]: BushCommandHandlerEvents['commandLocked']) { return message.util.reply( - `${util.emojis.error} You cannot use the ${util.format.input(command.id)} command because it is already in use.` + `${emojis.error} You cannot use the ${format.input(command.id)} command because it is already in use.` ); } } diff --git a/src/listeners/commands/commandMissingPermissions.ts b/src/listeners/commands/commandMissingPermissions.ts index 2cbf17c..352899d 100644 --- a/src/listeners/commands/commandMissingPermissions.ts +++ b/src/listeners/commands/commandMissingPermissions.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushCommandHandlerEvents } from '#lib'; +import { BushListener, emojis, format, mappings, oxford, surroundArray, type BushCommandHandlerEvents } from '#lib'; import { type PermissionsString } from 'discord.js'; export default class CommandMissingPermissionsListener extends BushListener { @@ -10,7 +10,7 @@ export default class CommandMissingPermissionsListener extends BushListener { }); } - public override async exec(...[message, command, type, missing]: BushCommandHandlerEvents['missingPermissions']) { + public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['missingPermissions']) { return await CommandMissingPermissionsListener.handleMissing(message, command, type, missing); } @@ -20,11 +20,11 @@ export default class CommandMissingPermissionsListener extends BushListener { | BushCommandHandlerEvents['slashMissingPermissions'] ) { const niceMissing = (missing.includes('Administrator') ? (['Administrator'] as PermissionsString[]) : missing).map( - (perm) => client.consts.mappings.permissions[perm]?.name ?? missing + (perm) => mappings.permissions[perm]?.name ?? missing ); - const discordFormat = util.oxford(util.surroundArray(niceMissing, '**'), 'and', ''); - const consoleFormat = util.oxford(util.surroundArray(niceMissing, '<<', '>>'), 'and', ''); + const discordFormat = oxford(surroundArray(niceMissing, '**'), 'and', ''); + const consoleFormat = oxford(surroundArray(niceMissing, '<<', '>>'), 'and', ''); void client.console.info( 'commandMissingPermissions', `<<${message.author.tag}>> tried to run <<${ @@ -34,15 +34,15 @@ export default class CommandMissingPermissionsListener extends BushListener { if (type == 'client') { return await message.util .reply( - `${util.emojis.error} I am missing the ${discordFormat} permission${ + `${emojis.error} I am missing the ${discordFormat} permission${ missing.length ? 's' : '' - } required for the ${util.format.input(command?.id)} command.` + } required for the ${format.input(command?.id)} command.` ) .catch(() => {}); } else if (type == 'user') { return await message.util .reply( - `${util.emojis.error} You are missing the ${discordFormat} permission${missing.length ? 's' : ''} required for the **${ + `${emojis.error} You are missing the ${discordFormat} permission${missing.length ? 's' : ''} required for the **${ command?.id }** command.` ) diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts index 02a0b75..89fea28 100644 --- a/src/listeners/commands/commandStarted.ts +++ b/src/listeners/commands/commandStarted.ts @@ -10,7 +10,7 @@ export default class CommandStartedListener extends BushListener { }); } - public override exec(...[message, command]: BushCommandHandlerEvents['commandStarted']): void { + public exec(...[message, command]: BushCommandHandlerEvents['commandStarted']): void { client.sentry.addBreadcrumb({ message: `[commandStarted] The ${command.id} was started by ${message.author.tag}.`, level: 'info', diff --git a/src/listeners/commands/messageBlocked.ts b/src/listeners/commands/messageBlocked.ts index 5a2b10d..997579c 100644 --- a/src/listeners/commands/messageBlocked.ts +++ b/src/listeners/commands/messageBlocked.ts @@ -9,7 +9,7 @@ export default class MessageBlockedListener extends BushListener { }); } - public override async exec(...[message, reason]: BushCommandHandlerEvents['messageBlocked']) { + 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}`); diff --git a/src/listeners/commands/slashBlocked.ts b/src/listeners/commands/slashBlocked.ts index 6c0ed14..915b953 100644 --- a/src/listeners/commands/slashBlocked.ts +++ b/src/listeners/commands/slashBlocked.ts @@ -10,7 +10,7 @@ export default class SlashBlockedListener extends BushListener { }); } - public override async exec(...[message, command, reason]: BushCommandHandlerEvents['slashBlocked']) { + public async exec(...[message, command, reason]: BushCommandHandlerEvents['slashBlocked']) { return await CommandBlockedListener.handleBlocked(message, command, reason); } } diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts index 25c0f3e..f851f4f 100644 --- a/src/listeners/commands/slashCommandError.ts +++ b/src/listeners/commands/slashCommandError.ts @@ -10,7 +10,7 @@ export default class SlashCommandErrorListener extends BushListener { }); } - public override async exec(...[error, message, command]: BushCommandHandlerEvents['slashError']) { + public async exec(...[error, message, command]: BushCommandHandlerEvents['slashError']) { return await CommandErrorListener.handleError(error, message, command); } } diff --git a/src/listeners/commands/slashMissingPermissions.ts b/src/listeners/commands/slashMissingPermissions.ts index 296f44b..3ae03e3 100644 --- a/src/listeners/commands/slashMissingPermissions.ts +++ b/src/listeners/commands/slashMissingPermissions.ts @@ -10,7 +10,7 @@ export default class SlashMissingPermissionsListener extends BushListener { }); } - public override async exec(...[message, command, type, missing]: BushCommandHandlerEvents['slashMissingPermissions']) { + public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['slashMissingPermissions']) { return await CommandMissingPermissionsListener.handleMissing(message, command, type, missing); } } diff --git a/src/listeners/commands/slashNotFound.ts b/src/listeners/commands/slashNotFound.ts index a8e4267..8c6d2d4 100644 --- a/src/listeners/commands/slashNotFound.ts +++ b/src/listeners/commands/slashNotFound.ts @@ -9,7 +9,7 @@ export default class SlashNotFoundListener extends BushListener { }); } - public override async exec(...[interaction]: BushCommandHandlerEvents['slashNotFound']) { + public async exec(...[interaction]: BushCommandHandlerEvents['slashNotFound']) { void 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 3d8334d..e2c8a47 100644 --- a/src/listeners/commands/slashStarted.ts +++ b/src/listeners/commands/slashStarted.ts @@ -10,7 +10,7 @@ export default class SlashStartedListener extends BushListener { }); } - public override async exec(...[message, command]: BushCommandHandlerEvents['slashStarted']) { + public async exec(...[message, command]: BushCommandHandlerEvents['slashStarted']) { client.sentry.addBreadcrumb({ message: `[slashStarted] The ${command.id} was started by ${message.author.tag}.`, level: 'info', diff --git a/src/listeners/contextCommands/contextCommandBlocked.ts b/src/listeners/contextCommands/contextCommandBlocked.ts index ba4af8c..a9397b6 100644 --- a/src/listeners/contextCommands/contextCommandBlocked.ts +++ b/src/listeners/contextCommands/contextCommandBlocked.ts @@ -1,4 +1,4 @@ -import { BushListener } from '#lib'; +import { BlockedReasons, BushListener, emojis, format } from '#lib'; import { type ContextMenuCommandHandlerEvents } from 'discord-akairo'; export default class ContextCommandBlockedListener extends BushListener { @@ -10,7 +10,7 @@ export default class ContextCommandBlockedListener extends BushListener { }); } - public override async exec(...[interaction, command, reason]: ContextMenuCommandHandlerEvents['blocked']) { + public async exec(...[interaction, command, reason]: ContextMenuCommandHandlerEvents['blocked']) { void client.console.info( `ContextCommandBlocked`, `<<${interaction.user.tag}>> tried to run <<${command}>> but was blocked because <<${reason}>>.`, @@ -18,21 +18,21 @@ export default class ContextCommandBlockedListener extends BushListener { ); switch (reason) { - case client.consts.BlockedReasons.OWNER: { + case BlockedReasons.OWNER: { return await interaction.reply({ - content: `${util.emojis.error} Only my developers can run the ${util.format.input(command!.id)} command.`, + content: `${emojis.error} Only my developers can run the ${format.input(command!.id)} command.`, ephemeral: true }); } - case client.consts.BlockedReasons.SUPER_USER: { + case BlockedReasons.SUPER_USER: { return await interaction.reply({ - content: `${util.emojis.error} You must be a superuser to run the ${util.format.input(command!.id)} command.`, + content: `${emojis.error} You must be a superuser to run the ${format.input(command!.id)} command.`, ephemeral: true }); } default: { return await interaction.reply({ - content: `${util.emojis.error} Command blocked with reason ${util.format.input(reason ?? 'unknown')}.`, + content: `${emojis.error} Command blocked with reason ${format.input(reason ?? 'unknown')}.`, ephemeral: true }); } diff --git a/src/listeners/contextCommands/contextCommandError.ts b/src/listeners/contextCommands/contextCommandError.ts index 19b5708..e69ed4e 100644 --- a/src/listeners/contextCommands/contextCommandError.ts +++ b/src/listeners/contextCommands/contextCommandError.ts @@ -1,4 +1,4 @@ -import { BushListener } from '#lib'; +import { BushListener, colors, format, formatError } from '#lib'; import { type ContextMenuCommand, type ContextMenuCommandHandlerEvents } from 'discord-akairo'; import { ChannelType, ContextMenuCommandInteraction, EmbedBuilder, GuildTextBasedChannel } from 'discord.js'; import CommandErrorListener, { IFuckedUpError } from '../commands/commandError.js'; @@ -12,7 +12,7 @@ export default class ContextCommandErrorListener extends BushListener { }); } - public override exec(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { + public exec(...[error, interaction, command]: ContextMenuCommandHandlerEvents['error']) { return ContextCommandErrorListener.handleError(error, interaction, command); } @@ -45,7 +45,7 @@ export default class ContextCommandErrorListener extends BushListener { `contextCommandError`, `an error occurred with the <<${command}>> context command in <<${channel}>> triggered by <<${ interaction?.user?.tag - }>>:\n${util.formatError(error, true)}`, + }>>:\n${formatError(error, true)}`, false ); @@ -92,14 +92,14 @@ export default class ContextCommandErrorListener extends BushListener { haste: string[]; stack: string; }): EmbedBuilder[] { - const embeds = [new EmbedBuilder().setColor(util.colors.error)]; + const embeds = [new EmbedBuilder().setColor(colors.error)]; if (options.type === 'command-user') { embeds[0] .setTitle('An Error Occurred') .setDescription( `Oh no! ${ - options.command ? `While running the command ${util.format.input(options.command.id)}, a` : 'A' - }n error occurred. Please give the developers code ${util.format.input(`${options.errorNum}`)}.` + options.command ? `While running the command ${format.input(options.command.id)}, a` : 'A' + }n error occurred. Please give the developers code ${format.input(`${options.errorNum}`)}.` ) .setTimestamp(); return embeds; @@ -116,11 +116,11 @@ export default class ContextCommandErrorListener extends BushListener { description.push(...options.haste); - embeds.push(new EmbedBuilder().setColor(util.colors.error).setTimestamp().setDescription(options.stack.substring(0, 4000))); + embeds.push(new EmbedBuilder().setColor(colors.error).setTimestamp().setDescription(options.stack.substring(0, 4000))); if (description.length) embeds[0].setDescription(description.join('\n').substring(0, 4000)); if (options.type === 'command-dev' || options.type === 'command-log') - embeds[0].setTitle(`ContextCommandError #${util.format.input(`${options.errorNum}`)}`); + embeds[0].setTitle(`ContextCommandError #${format.input(`${options.errorNum}`)}`); return embeds; } } diff --git a/src/listeners/contextCommands/contextCommandNotFound.ts b/src/listeners/contextCommands/contextCommandNotFound.ts index ca97ad1..65954a2 100644 --- a/src/listeners/contextCommands/contextCommandNotFound.ts +++ b/src/listeners/contextCommands/contextCommandNotFound.ts @@ -10,7 +10,7 @@ export default class ContextCommandNotFoundListener extends BushListener { }); } - public override async exec(...[interaction]: ContextMenuCommandHandlerEvents['notFound']) { + public async exec(...[interaction]: ContextMenuCommandHandlerEvents['notFound']) { void client.console.info('contextCommandNotFound', `<<${interaction?.commandName}>> could not be found.`); } } diff --git a/src/listeners/contextCommands/contextCommandStarted.ts b/src/listeners/contextCommands/contextCommandStarted.ts index 965b8b9..3aab07a 100644 --- a/src/listeners/contextCommands/contextCommandStarted.ts +++ b/src/listeners/contextCommands/contextCommandStarted.ts @@ -11,7 +11,7 @@ export default class ContextCommandStartedListener extends BushListener { }); } - public override async exec(...[interaction, command]: ContextMenuCommandHandlerEvents['started']) { + public async exec(...[interaction, command]: ContextMenuCommandHandlerEvents['started']) { client.sentry.addBreadcrumb({ message: `[contextCommandStarted] The ${command.id} was started by ${interaction.user.tag}.`, level: 'info', diff --git a/src/listeners/guild-custom/bushLockdown.ts b/src/listeners/guild-custom/bushLockdown.ts index 85890cf..930a8d2 100644 --- a/src/listeners/guild-custom/bushLockdown.ts +++ b/src/listeners/guild-custom/bushLockdown.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushLockdownListener extends BushListener { @@ -10,12 +10,12 @@ export default class BushLockdownListener extends BushListener { }); } - public override async exec(...[moderator, reason, channelsSuccessMap, _all]: BushClientEvents['bushLockdown']) { + public async exec(...[moderator, reason, channelsSuccessMap, _all]: BushClientEvents['bushLockdown']) { const logChannel = await moderator.guild.getLogChannel('moderation'); if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Blurple) + .setColor(colors.Blurple) .setTimestamp() .addFields([ { name: '**Action**', value: `${'Lockdown'}` }, @@ -24,7 +24,7 @@ export default class BushLockdownListener extends BushListener { { name: `**Channel${channelsSuccessMap.size > 1 ? 's' : ''}**`, value: channelsSuccessMap - .map((success, channel) => `<#${channel}> ${success ? util.emojis.success : util.emojis.error}`) + .map((success, channel) => `<#${channel}> ${success ? emojis.success : emojis.error}`) .join('\n') } ]); diff --git a/src/listeners/guild-custom/bushUnlockdown.ts b/src/listeners/guild-custom/bushUnlockdown.ts index 26217d8..b5a4482 100644 --- a/src/listeners/guild-custom/bushUnlockdown.ts +++ b/src/listeners/guild-custom/bushUnlockdown.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushUnlockdownListener extends BushListener { @@ -10,12 +10,12 @@ export default class BushUnlockdownListener extends BushListener { }); } - public override async exec(...[moderator, reason, channelsSuccessMap, _all]: BushClientEvents['bushUnlockdown']) { + public async exec(...[moderator, reason, channelsSuccessMap, _all]: BushClientEvents['bushUnlockdown']) { const logChannel = await moderator.guild.getLogChannel('moderation'); if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Blurple) + .setColor(colors.Blurple) .setTimestamp() .addFields([ { name: '**Action**', value: `${'Unlockdown'}` }, @@ -24,7 +24,7 @@ export default class BushUnlockdownListener extends BushListener { { name: `**Channel${channelsSuccessMap.size > 1 ? 's' : ''}**`, value: channelsSuccessMap - .map((success, channel) => `<#${channel}> ${success ? util.emojis.success : util.emojis.error}`) + .map((success, channel) => `<#${channel}> ${success ? emojis.success : emojis.error}`) .join('\n') } ]); diff --git a/src/listeners/guild/guildCreate.ts b/src/listeners/guild/guildCreate.ts index 56cd5df..fe23979 100644 --- a/src/listeners/guild/guildCreate.ts +++ b/src/listeners/guild/guildCreate.ts @@ -1,4 +1,4 @@ -import { BushListener, Guild, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, format, getConfigChannel, Guild, type BushClientEvents } from '#lib'; export default class GuildCreateListener extends BushListener { public constructor() { @@ -9,18 +9,18 @@ export default class GuildCreateListener extends BushListener { }); } - public override async exec(...[guild]: BushClientEvents['guildCreate']) { + public async exec(...[guild]: BushClientEvents['guildCreate']) { void 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 util.getConfigChannel('servers'); + const channel = await getConfigChannel('servers'); if (!channel) return; return await channel.send({ embeds: [ { - color: util.colors.Green, - description: `${util.emojis.join} Joined ${util.format.input( + 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.`, timestamp: new Date().toISOString(), diff --git a/src/listeners/guild/guildDelete.ts b/src/listeners/guild/guildDelete.ts index 054c5e0..5efbe9b 100644 --- a/src/listeners/guild/guildDelete.ts +++ b/src/listeners/guild/guildDelete.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, format, getConfigChannel, type BushClientEvents } from '#lib'; export default class GuildDeleteListener extends BushListener { public constructor() { @@ -9,16 +9,16 @@ export default class GuildDeleteListener extends BushListener { }); } - public override async exec(...[guild]: BushClientEvents['guildDelete']) { + public async exec(...[guild]: BushClientEvents['guildDelete']) { void client.console.info('guildDelete', `Left <<${guild.name}>> with <<${guild.memberCount?.toLocaleString()}>> members.`); - const channel = await util.getConfigChannel('servers'); + const channel = await getConfigChannel('servers'); if (!channel) return; return await channel.send({ embeds: [ { - color: util.colors.Red, - description: `${util.emojis.leave} Left ${util.format.input( + 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.`, timestamp: new Date().toISOString(), diff --git a/src/listeners/guild/guildMemberAdd.ts b/src/listeners/guild/guildMemberAdd.ts index de1f859..004a2c2 100644 --- a/src/listeners/guild/guildMemberAdd.ts +++ b/src/listeners/guild/guildMemberAdd.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, format, type BushClientEvents } from '#lib'; import { EmbedBuilder, type GuildMember, type TextChannel } from 'discord.js'; export default class GuildMemberAddListener extends BushListener { @@ -10,7 +10,7 @@ export default class GuildMemberAddListener extends BushListener { }); } - public override async exec(...[member]: BushClientEvents['guildMemberAdd']) { + public async exec(...[member]: BushClientEvents['guildMemberAdd']) { void this.sendWelcomeMessage(member); } @@ -30,24 +30,24 @@ export default class GuildMemberAddListener extends BushListener { const embed = new EmbedBuilder() .setDescription( - `${util.emojis.join} ${util.format.input( + `${emojis.join} ${format.input( member.user.tag )} joined the server. There are now ${member.guild.memberCount.toLocaleString()} members.` ) - .setColor(util.colors.green); + .setColor(colors.green); await welcome .send({ embeds: [embed] }) .then(() => client.console.info( 'guildMemberAdd', - `Sent a message for ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` + `Sent a message for ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ) ) .catch(() => welcome.guild.error( 'Welcome Message', - `Failed to send message for ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` + `Failed to send 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 59f8800..80e9ee8 100644 --- a/src/listeners/guild/guildMemberRemove.ts +++ b/src/listeners/guild/guildMemberRemove.ts @@ -1,4 +1,4 @@ -import { BushListener, StickyRole, Time, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, format, sleep, StickyRole, Time, type BushClientEvents } from '#lib'; import { EmbedBuilder, type GuildMember, type PartialGuildMember, type TextChannel } from 'discord.js'; export default class GuildMemberRemoveListener extends BushListener { @@ -10,7 +10,7 @@ export default class GuildMemberRemoveListener extends BushListener { }); } - public override async exec(...[member]: BushClientEvents['guildMemberRemove']) { + public async exec(...[member]: BushClientEvents['guildMemberRemove']) { void this.sendWelcomeMessage(member); void this.stickyRoles(member); } @@ -18,7 +18,7 @@ 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; - await util.sleep(50 * Time.Millisecond); // ban usually triggers after member leave + 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; @@ -26,23 +26,23 @@ export default class GuildMemberRemoveListener extends BushListener { if (member.guild.id !== welcome?.guild.id) throw new Error('Welcome channel must be in the guild.'); const embed: EmbedBuilder = new EmbedBuilder() .setDescription( - `${util.emojis.leave} ${util.format.input(user.tag)} ${ + `${emojis.leave} ${format.input(user.tag)} ${ isBan ? 'got banned from' : 'left' } the server. There are now ${welcome.guild.memberCount.toLocaleString()} members.` ) - .setColor(isBan ? util.colors.orange : util.colors.red); + .setColor(isBan ? colors.orange : colors.red); welcome .send({ embeds: [embed] }) .then(() => client.console.info( 'guildMemberRemove', - `Sent a message for ${util.format.inputLog(user.tag)} in ${util.format.inputLog(member.guild.name)}.` + `Sent a message for ${format.inputLog(user.tag)} in ${format.inputLog(member.guild.name)}.` ) ) .catch(() => member.guild.error( 'Welcome Message Error', - `Failed to send message for ${util.format.input(user.tag)} in ${util.format.input(member.guild.name)}.` + `Failed to send message for ${format.input(user.tag)} in ${format.input(member.guild.name)}.` ) ); } @@ -74,7 +74,7 @@ export default class GuildMemberRemoveListener extends BushListener { .then(() => client.console.info( 'guildMemberRemove', - `${isNew ? 'Created' : 'Updated'} info for ${util.format.inputLog(member.user.tag)}.` + `${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 dab623f..f2922c8 100644 --- a/src/listeners/guild/joinRoles.ts +++ b/src/listeners/guild/joinRoles.ts @@ -1,4 +1,4 @@ -import { BushListener, StickyRole, type BushClientEvents } from '#lib'; +import { BushListener, colors, format, StickyRole, type BushClientEvents } from '#lib'; import { type GuildMember, type Snowflake } from 'discord.js'; export default class JoinRolesListener extends BushListener { @@ -10,7 +10,7 @@ export default class JoinRolesListener extends BushListener { }); } - public override async exec(...[oldMember, newMember]: BushClientEvents['guildMemberUpdate']) { + public async exec(...[oldMember, newMember]: BushClientEvents['guildMemberUpdate']) { if (client.config.isDevelopment) return; if (oldMember.pending && !newMember.pending) { const feat = { @@ -58,8 +58,8 @@ export default class JoinRolesListener extends BushListener { embeds: [ { title: 'Sticky Roles Error', - description: `There was an error returning ${util.format.input(member.user.tag)}'s roles.`, - color: util.colors.error + description: `There was an error returning ${format.input(member.user.tag)}'s roles.`, + color: colors.error } ] }); @@ -68,7 +68,7 @@ export default class JoinRolesListener extends BushListener { if (addedRoles) { void client.console.info( 'guildMemberAdd', - `Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` + `Assigned sticky roles to ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ); } else if (!addedRoles) { const failedRoles: string[] = []; @@ -82,9 +82,7 @@ export default class JoinRolesListener extends BushListener { } else { void client.console.info( 'guildMemberAdd', - `[Fallback] Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog( - member.guild.name - )}.` + `[Fallback] Assigned sticky roles to ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ); } } @@ -106,13 +104,13 @@ export default class JoinRolesListener extends BushListener { .then(() => client.console.info( 'guildMemberAdd', - `Assigned join roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` + `Assigned join roles to ${format.inputLog(member.user.tag)} in ${format.inputLog(member.guild.name)}.` ) ) .catch(() => member.guild.error( 'Join Roles Error', - `Failed to assign join roles to ${util.format.input(member.user.tag)}, in ${util.format.input(member.guild.name)}.` + `Failed to assign join roles to ${format.input(member.user.tag)}, in ${format.input(member.guild.name)}.` ) ); } diff --git a/src/listeners/guild/syncUnbanPunishmentModel.ts b/src/listeners/guild/syncUnbanPunishmentModel.ts index 44f5bb4..80a8ce2 100644 --- a/src/listeners/guild/syncUnbanPunishmentModel.ts +++ b/src/listeners/guild/syncUnbanPunishmentModel.ts @@ -9,7 +9,7 @@ export default class SyncUnbanListener extends BushListener { }); } - public override async exec(...[ban]: BushClientEvents['guildBanRemove']) { + public async exec(...[ban]: BushClientEvents['guildBanRemove']) { const bans = await ActivePunishment.findAll({ where: { user: ban.user.id, diff --git a/src/listeners/interaction/interactionCreate.ts b/src/listeners/interaction/interactionCreate.ts index 48a2149..6c421d1 100644 --- a/src/listeners/interaction/interactionCreate.ts +++ b/src/listeners/interaction/interactionCreate.ts @@ -1,4 +1,4 @@ -import { AutoMod, BushListener, type BushClientEvents } from '#lib'; +import { AutoMod, BushListener, emojis, format, oxford, surroundArray, type BushClientEvents } from '#lib'; import { InteractionType } from 'discord.js'; export default class InteractionCreateListener extends BushListener { @@ -10,7 +10,7 @@ export default class InteractionCreateListener extends BushListener { }); } - public override async exec(...[interaction]: BushClientEvents['interactionCreate']) { + public async exec(...[interaction]: BushClientEvents['interactionCreate']) { if (!interaction) return; if ('customId' in interaction && (interaction as any)['customId'].startsWith('test')) return; void client.console.verbose( @@ -26,31 +26,31 @@ export default class InteractionCreateListener extends BushListener { else if (id.startsWith('button-role;') && interaction.inCachedGuild()) { const [, roleId] = id.split(';'); const role = interaction.guild.roles.cache.get(roleId); - if (!role) return interaction.reply({ content: `${util.emojis.error} That role does not exist.`, ephemeral: true }); + if (!role) return interaction.reply({ content: `${emojis.error} That role does not exist.`, ephemeral: true }); const has = interaction.member.roles.cache.has(roleId); await interaction.deferReply({ ephemeral: true }); if (has) { const success = await interaction.member.roles.remove(roleId).catch(() => false); if (success) return interaction.editReply({ - content: `${util.emojis.success} Removed the ${role} role from you.`, + content: `${emojis.success} Removed the ${role} role from you.`, allowedMentions: {} }); else return interaction.editReply({ - content: `${util.emojis.error} Failed to remove ${role} from you.`, + content: `${emojis.error} Failed to remove ${role} from you.`, allowedMentions: {} }); } else { const success = await interaction.member.roles.add(roleId).catch(() => false); if (success) return interaction.editReply({ - content: `${util.emojis.success} Added the ${role} role to you.`, + content: `${emojis.success} Added the ${role} role to you.`, allowedMentions: {} }); else return interaction.editReply({ - content: `${util.emojis.error} Failed to add ${role} to you.`, + content: `${emojis.error} Failed to add ${role} to you.`, allowedMentions: {} }); } @@ -60,8 +60,8 @@ export default class InteractionCreateListener extends BushListener { return await interaction.reply({ content: `You selected ${ Array.isArray(interaction.values) - ? util.oxford(util.surroundArray(interaction.values, '`'), 'and', '') - : util.format.input(interaction.values) + ? oxford(surroundArray(interaction.values, '`'), 'and', '') + : format.input(interaction.values) }.`, ephemeral: true }); diff --git a/src/listeners/member-custom/bushBan.ts b/src/listeners/member-custom/bushBan.ts index 1727433..32610ea 100644 --- a/src/listeners/member-custom/bushBan.ts +++ b/src/listeners/member-custom/bushBan.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushBanListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushBanListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushBan']) { + public async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushBan']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Red) + .setColor(colors.Red) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) @@ -26,7 +26,7 @@ export default class BushBanListener extends BushListener { { name: '**Moderator**', value: `${moderator} (${moderator.tag})` }, { name: '**Reason**', value: `${reason ? reason : '[No Reason Provided]'}` } ]); - if (duration) logEmbed.addFields([{ name: '**Duration**', value: util.humanizeDuration(duration) }]); + if (duration) logEmbed.addFields([{ name: '**Duration**', value: humanizeDuration(duration) }]); if (dmSuccess === false) logEmbed.addFields([{ name: '**Additional Info**', value: 'Could not dm user.' }]); return await logChannel.send({ embeds: [logEmbed] }); } diff --git a/src/listeners/member-custom/bushBlock.ts b/src/listeners/member-custom/bushBlock.ts index 7cc33d2..6d15a2b 100644 --- a/src/listeners/member-custom/bushBlock.ts +++ b/src/listeners/member-custom/bushBlock.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushBlockListener extends BushListener { @@ -10,15 +10,13 @@ export default class BushBlockListener extends BushListener { }); } - public override async exec( - ...[victim, moderator, guild, reason, caseID, duration, dmSuccess, channel]: BushClientEvents['bushBlock'] - ) { + public async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess, channel]: BushClientEvents['bushBlock']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Purple) + .setColor(colors.Purple) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) @@ -30,7 +28,7 @@ export default class BushBlockListener extends BushListener { { name: '**Reason**', value: `${reason ? reason : '[No Reason Provided]'}` } ]); - if (duration) logEmbed.addFields([{ name: '**Duration**', value: `${util.humanizeDuration(duration) || duration}` }]); + if (duration) logEmbed.addFields([{ name: '**Duration**', value: `${humanizeDuration(duration) || duration}` }]); if (dmSuccess === false) logEmbed.addFields([{ name: '**Additional Info**', value: 'Could not dm user.' }]); return await logChannel.send({ embeds: [logEmbed] }); } diff --git a/src/listeners/member-custom/bushKick.ts b/src/listeners/member-custom/bushKick.ts index 1be7bf9..65a6942 100644 --- a/src/listeners/member-custom/bushKick.ts +++ b/src/listeners/member-custom/bushKick.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushKickListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushKickListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushKick']) { + public async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushKick']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Red) + .setColor(colors.Red) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/bushLevelUpdate.ts b/src/listeners/member-custom/bushLevelUpdate.ts index 2a7b75b..a9c0820 100644 --- a/src/listeners/member-custom/bushLevelUpdate.ts +++ b/src/listeners/member-custom/bushLevelUpdate.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, format, type BushClientEvents } from '#lib'; import assert from 'assert'; import { type TextChannel } from 'discord.js'; @@ -13,7 +13,7 @@ export default class BushLevelUpdateListener extends BushListener { }); } - public override async exec(...[member, _oldLevel, newLevel, _currentXp, message]: Args) { + public async exec(...[member, _oldLevel, newLevel, _currentXp, message]: Args) { void this.sendLevelUpMessages(member, newLevel, message); void this.assignLevelRoles(member, newLevel, message); } @@ -27,7 +27,7 @@ export default class BushLevelUpdateListener extends BushListener { .catch(() => null)) ?? message.channel) as TextChannel; const success = await channel - .send(`${util.format.input(member.user.tag)} leveled up to level ${util.format.input(`${newLevel}`)}.`) + .send(`${format.input(member.user.tag)} leveled up to level ${format.input(`${newLevel}`)}.`) .catch(() => null); if (!success) diff --git a/src/listeners/member-custom/bushMute.ts b/src/listeners/member-custom/bushMute.ts index a0aa37d..77ea74c 100644 --- a/src/listeners/member-custom/bushMute.ts +++ b/src/listeners/member-custom/bushMute.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushMuteListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushMuteListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushMute']) { + public async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushMute']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Orange) + .setColor(colors.Orange) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) @@ -26,7 +26,7 @@ export default class BushMuteListener extends BushListener { { name: '**Moderator**', value: `${moderator} (${moderator.tag})` }, { name: '**Reason**', value: `${reason ? reason : '[No Reason Provided]'}` } ]); - if (duration) logEmbed.addFields([{ name: '**Duration**', value: `${util.humanizeDuration(duration) || duration}` }]); + if (duration) logEmbed.addFields([{ name: '**Duration**', value: `${humanizeDuration(duration) || duration}` }]); if (dmSuccess === false) logEmbed.addFields([{ name: '**Additional Info**', value: 'Could not dm user.' }]); return await logChannel.send({ embeds: [logEmbed] }); } diff --git a/src/listeners/member-custom/bushPunishRole.ts b/src/listeners/member-custom/bushPunishRole.ts index 5604bac..2e5b17b 100644 --- a/src/listeners/member-custom/bushPunishRole.ts +++ b/src/listeners/member-custom/bushPunishRole.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushPunishRoleListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushPunishRoleListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, duration]: BushClientEvents['bushPunishRole']) { + public async exec(...[victim, moderator, guild, reason, caseID, duration]: BushClientEvents['bushPunishRole']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Yellow) + .setColor(colors.Yellow) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) @@ -26,7 +26,7 @@ export default class BushPunishRoleListener extends BushListener { { name: '**Moderator**', value: `${moderator} (${moderator.tag})` }, { name: '**Reason**', value: `${reason ? reason : '[No Reason Provided]'}` } ]); - if (duration) logEmbed.addFields([{ name: '**Duration**', value: util.humanizeDuration(duration) }]); + if (duration) logEmbed.addFields([{ name: '**Duration**', value: humanizeDuration(duration) }]); return await logChannel.send({ embeds: [logEmbed] }); } } diff --git a/src/listeners/member-custom/bushPunishRoleRemove.ts b/src/listeners/member-custom/bushPunishRoleRemove.ts index 1a13de0..7bc9863 100644 --- a/src/listeners/member-custom/bushPunishRoleRemove.ts +++ b/src/listeners/member-custom/bushPunishRoleRemove.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushPunishRoleRemoveListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushPunishRoleRemoveListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, role]: BushClientEvents['bushPunishRoleRemove']) { + public async exec(...[victim, moderator, guild, reason, caseID, role]: BushClientEvents['bushPunishRoleRemove']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Green) + .setColor(colors.Green) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/bushPurge.ts b/src/listeners/member-custom/bushPurge.ts index 3334bad..ea30bc5 100644 --- a/src/listeners/member-custom/bushPurge.ts +++ b/src/listeners/member-custom/bushPurge.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, emojis, inspectCleanRedactHaste, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushPurgeListener extends BushListener { @@ -10,7 +10,7 @@ export default class BushPurgeListener extends BushListener { }); } - public override async exec(...[moderator, guild, channel, messages]: BushClientEvents['bushPurge']) { + public async exec(...[moderator, guild, channel, messages]: BushClientEvents['bushPurge']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; @@ -21,10 +21,10 @@ export default class BushPurgeListener extends BushListener { embeds: m.embeds, attachments: [...m.attachments.values()] })); - const haste = await util.inspectCleanRedactHaste(mappedMessages); + const haste = await inspectCleanRedactHaste(mappedMessages); const logEmbed = new EmbedBuilder() - .setColor(util.colors.DarkPurple) + .setColor(colors.DarkPurple) .setTimestamp() .setFooter({ text: `${messages.size.toLocaleString()} Messages` }) .setAuthor({ name: moderator.tag, iconURL: moderator.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) @@ -35,7 +35,7 @@ export default class BushPurgeListener extends BushListener { { name: '**Messages**', value: `${ - haste.url ? `[haste](${haste.url})${haste.error ? `- ${haste.error}` : ''}` : `${util.emojis.error} ${haste.error}` + haste.url ? `[haste](${haste.url})${haste.error ? `- ${haste.error}` : ''}` : `${emojis.error} ${haste.error}` }` } ]); diff --git a/src/listeners/member-custom/bushRemoveTimeout.ts b/src/listeners/member-custom/bushRemoveTimeout.ts index aafa11e..742d407 100644 --- a/src/listeners/member-custom/bushRemoveTimeout.ts +++ b/src/listeners/member-custom/bushRemoveTimeout.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushRemoveTimeoutListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushRemoveTimeoutListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushRemoveTimeout']) { + public async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushRemoveTimeout']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Green) + .setColor(colors.Green) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/bushTimeout.ts b/src/listeners/member-custom/bushTimeout.ts index 07a7105..4621921 100644 --- a/src/listeners/member-custom/bushTimeout.ts +++ b/src/listeners/member-custom/bushTimeout.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushTimeoutListener extends BushListener { @@ -10,15 +10,13 @@ export default class BushTimeoutListener extends BushListener { }); } - public override async exec( - ...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushTimeout'] - ) { + public async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushTimeout']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Orange) + .setColor(colors.Orange) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) @@ -27,7 +25,7 @@ export default class BushTimeoutListener extends BushListener { { name: '**User**', value: `${user} (${user.tag})` }, { name: '**Moderator**', value: `${moderator} (${moderator.tag})` }, { name: '**Reason**', value: `${reason ? reason : '[No Reason Provided]'}` }, - { name: '**Duration**', value: `${util.humanizeDuration(duration) || duration}` } + { name: '**Duration**', value: `${humanizeDuration(duration) || duration}` } ]); if (dmSuccess === false) logEmbed.addFields([{ name: '**Additional Info**', value: 'Could not dm user.' }]); return await logChannel.send({ embeds: [logEmbed] }); diff --git a/src/listeners/member-custom/bushUnban.ts b/src/listeners/member-custom/bushUnban.ts index 2628d36..463ce6c 100644 --- a/src/listeners/member-custom/bushUnban.ts +++ b/src/listeners/member-custom/bushUnban.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushUnbanListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushUnbanListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushUnban']) { + public async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushUnban']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Green) + .setColor(colors.Green) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/bushUnblock.ts b/src/listeners/member-custom/bushUnblock.ts index 238c5b5..43b7b2b 100644 --- a/src/listeners/member-custom/bushUnblock.ts +++ b/src/listeners/member-custom/bushUnblock.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushUnblockListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushUnblockListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess, channel]: BushClientEvents['bushUnblock']) { + public async exec(...[victim, moderator, guild, reason, caseID, dmSuccess, channel]: BushClientEvents['bushUnblock']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Green) + .setColor(colors.Green) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/bushUnmute.ts b/src/listeners/member-custom/bushUnmute.ts index e7f6793..614a826 100644 --- a/src/listeners/member-custom/bushUnmute.ts +++ b/src/listeners/member-custom/bushUnmute.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushUnmuteListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushUnmuteListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushUnmute']) { + public async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushUnmute']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Green) + .setColor(colors.Green) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/bushUpdateModlog.ts b/src/listeners/member-custom/bushUpdateModlog.ts index f49defe..5e29b9a 100644 --- a/src/listeners/member-custom/bushUpdateModlog.ts +++ b/src/listeners/member-custom/bushUpdateModlog.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, inspectCleanRedactCodeblock, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushUpdateModlogListener extends BushListener { @@ -10,12 +10,12 @@ export default class BushUpdateModlogListener extends BushListener { }); } - public override async exec(...[moderator, modlogID, key, oldModlog, newModlog]: BushClientEvents['bushUpdateModlog']) { + public async exec(...[moderator, modlogID, key, oldModlog, newModlog]: BushClientEvents['bushUpdateModlog']) { const logChannel = await moderator.guild.getLogChannel('moderation'); if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Blurple) + .setColor(colors.Blurple) .setTimestamp() .setAuthor({ name: moderator.user.tag, @@ -26,8 +26,8 @@ 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 util.inspectCleanRedactCodeblock(oldModlog, undefined, undefined, 1024) }, - { name: '**New Value**', value: await util.inspectCleanRedactCodeblock(newModlog, undefined, undefined, 1024) } + { name: '**Old Value**', value: await inspectCleanRedactCodeblock(oldModlog, undefined, undefined, 1024) }, + { name: '**New Value**', value: await 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 948bbe7..e367c4e 100644 --- a/src/listeners/member-custom/bushUpdateSettings.ts +++ b/src/listeners/member-custom/bushUpdateSettings.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, inspectCleanRedactCodeblock, type BushClientEvents } from '#lib'; import { EmbedBuilder } from 'discord.js'; export default class BushUpdateSettingsListener extends BushListener { @@ -10,11 +10,11 @@ export default class BushUpdateSettingsListener extends BushListener { }); } - public override async exec(...[setting, guild, oldSettings, newSettings, moderator]: BushClientEvents['bushUpdateSettings']) { + public async exec(...[setting, guild, oldSettings, newSettings, moderator]: BushClientEvents['bushUpdateSettings']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; - const logEmbed = new EmbedBuilder().setColor(util.colors.Blurple).setTimestamp(); + const logEmbed = new EmbedBuilder().setColor(colors.Blurple).setTimestamp(); if (moderator) logEmbed.setAuthor({ @@ -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 util.inspectCleanRedactCodeblock(oldSettings, 'js', undefined, 1024) }, - { name: '**New Value**', value: await util.inspectCleanRedactCodeblock(newSettings, 'js', undefined, 1024) } + { name: '**Old Value**', value: await inspectCleanRedactCodeblock(oldSettings, 'js', undefined, 1024) }, + { name: '**New Value**', value: await inspectCleanRedactCodeblock(newSettings, 'js', undefined, 1024) } ]); return await logChannel.send({ embeds: [logEmbed] }); diff --git a/src/listeners/member-custom/bushWarn.ts b/src/listeners/member-custom/bushWarn.ts index ede56fd..b9c1868 100644 --- a/src/listeners/member-custom/bushWarn.ts +++ b/src/listeners/member-custom/bushWarn.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, type BushClientEvents } from '#lib'; import { EmbedBuilder, GuildMember } from 'discord.js'; export default class BushWarnListener extends BushListener { @@ -10,13 +10,13 @@ export default class BushWarnListener extends BushListener { }); } - public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushWarn']) { + public async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushWarn']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const user = victim instanceof GuildMember ? victim.user : victim; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Yellow) + .setColor(colors.Yellow) .setTimestamp() .setFooter({ text: `CaseID: ${caseID}` }) .setAuthor({ name: user.tag, iconURL: user.avatarURL({ extension: 'png', size: 4096 }) ?? undefined }) diff --git a/src/listeners/member-custom/massBan.ts b/src/listeners/member-custom/massBan.ts index 29f7ce3..ca41f29 100644 --- a/src/listeners/member-custom/massBan.ts +++ b/src/listeners/member-custom/massBan.ts @@ -1,4 +1,4 @@ -import { BanResponse, banResponse, BushListener, type BushClientEvents } from '#lib'; +import { BanResponse, banResponse, BushListener, colors, emojis, overflowEmbed, type BushClientEvents } from '#lib'; export default class MassBanListener extends BushListener { public constructor() { @@ -9,20 +9,19 @@ export default class MassBanListener extends BushListener { }); } - public override async exec(...[moderator, guild, reason, results]: BushClientEvents['massBan']) { + public async exec(...[moderator, guild, reason, results]: BushClientEvents['massBan']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; const success = (res: BanResponse): boolean => [banResponse.SUCCESS, banResponse.DM_ERROR].includes(res as any); const lines = results.map( - (reason, user) => - `${success(reason) ? util.emojis.success : util.emojis.error} ${user}${success(reason) ? '' : ` - ${reason}`}` + (reason, user) => `${success(reason) ? emojis.success : emojis.error} ${user}${success(reason) ? '' : ` - ${reason}`}` ); - const embeds = util.overflowEmbed( + const embeds = overflowEmbed( { - color: util.colors.DarkRed, + color: colors.DarkRed, title: 'Mass Ban', timestamp: new Date().toISOString(), fields: [ diff --git a/src/listeners/member-custom/massEvidence.ts b/src/listeners/member-custom/massEvidence.ts index 4a45ee7..f0f75df 100644 --- a/src/listeners/member-custom/massEvidence.ts +++ b/src/listeners/member-custom/massEvidence.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, overflowEmbed, type BushClientEvents } from '#lib'; export default class MassEvidenceListener extends BushListener { public constructor() { @@ -9,13 +9,13 @@ export default class MassEvidenceListener extends BushListener { }); } - public override async exec(...[moderator, guild, evidence, lines]: BushClientEvents['massEvidence']) { + public async exec(...[moderator, guild, evidence, lines]: BushClientEvents['massEvidence']) { const logChannel = await guild.getLogChannel('moderation'); if (!logChannel) return; - const embeds = util.overflowEmbed( + const embeds = overflowEmbed( { - color: util.colors.DarkRed, + color: colors.DarkRed, title: 'Mass Evidence', timestamp: new Date().toISOString(), fields: [ diff --git a/src/listeners/message/autoPublisher.ts b/src/listeners/message/autoPublisher.ts index 8f3462f..c669930 100644 --- a/src/listeners/message/autoPublisher.ts +++ b/src/listeners/message/autoPublisher.ts @@ -10,7 +10,7 @@ export default class autoPublisherListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + public async exec(...[message]: BushClientEvents['messageCreate']) { if (!message.guild || !(await message.guild.hasFeature('autoPublish'))) return; const autoPublishChannels = await message.guild.getSetting('autoPublishChannels'); if (autoPublishChannels) { diff --git a/src/listeners/message/automodCreate.ts b/src/listeners/message/automodCreate.ts index 8824c72..0f089a3 100644 --- a/src/listeners/message/automodCreate.ts +++ b/src/listeners/message/automodCreate.ts @@ -9,7 +9,7 @@ export default class AutomodMessageCreateListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + public async exec(...[message]: BushClientEvents['messageCreate']) { return new AutoMod(message); } } diff --git a/src/listeners/message/automodUpdate.ts b/src/listeners/message/automodUpdate.ts index d2e6f40..fa00f92 100644 --- a/src/listeners/message/automodUpdate.ts +++ b/src/listeners/message/automodUpdate.ts @@ -9,7 +9,7 @@ export default class AutomodMessageUpdateListener extends BushListener { }); } - public override async exec(...[_, newMessage]: BushClientEvents['messageUpdate']) { + public async exec(...[_, newMessage]: BushClientEvents['messageUpdate']) { const fullMessage = newMessage.partial ? await newMessage.fetch().catch(() => null) : newMessage; if (!fullMessage) return; return new AutoMod(fullMessage); diff --git a/src/listeners/message/blacklistedFile.ts b/src/listeners/message/blacklistedFile.ts index 122f289..15d97e3 100644 --- a/src/listeners/message/blacklistedFile.ts +++ b/src/listeners/message/blacklistedFile.ts @@ -65,7 +65,7 @@ export default class BlacklistedFileListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + public async exec(...[message]: BushClientEvents['messageCreate']) { if (!message.guild || !(await message.guild.hasFeature('blacklistedFile'))) return; // const embedAttachments = message.embeds.filter((e) => ['image', 'video', 'gifv'].includes(e.type)); const foundEmojis = [...message.content.matchAll(/<(?<animated>a?):\w+:(?<id>\d+)>/g)]; diff --git a/src/listeners/message/boosterMessage.ts b/src/listeners/message/boosterMessage.ts index 989f671..e33abfb 100644 --- a/src/listeners/message/boosterMessage.ts +++ b/src/listeners/message/boosterMessage.ts @@ -10,7 +10,7 @@ export default class BoosterMessageListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + public async exec(...[message]: BushClientEvents['messageCreate']) { if (!message.guild || !(await message.guild?.hasFeature('boosterMessageReact'))) return; if (message.type === MessageType.UserPremiumGuildSubscription) { return await message.react('<:nitroboost:785160348885975062>').catch(() => { diff --git a/src/listeners/message/directMessage.ts b/src/listeners/message/directMessage.ts index 87480b0..463e4d8 100644 --- a/src/listeners/message/directMessage.ts +++ b/src/listeners/message/directMessage.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, colors, getConfigChannel, type BushClientEvents } from '#lib'; import { ChannelType, EmbedBuilder } from 'discord.js'; export default class DirectMessageListener extends BushListener { @@ -10,7 +10,7 @@ export default class DirectMessageListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + 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; @@ -23,7 +23,7 @@ export default class DirectMessageListener extends BushListener { iconURL: `${message.author.displayAvatarURL()}` }) .setDescription(`**DM:**\n${message}`) - .setColor(util.colors.blue); + .setColor(colors.blue); } else { dmLogEmbed .setAuthor({ @@ -31,7 +31,7 @@ export default class DirectMessageListener extends BushListener { iconURL: `${message.channel.recipient?.displayAvatarURL()}` }) .setDescription(`**DM:**\n${message}`) - .setColor(util.colors.red) + .setColor(colors.red) .setTimestamp(); } if (message.attachments.filter((a) => typeof a.size == 'number').size == 1) { @@ -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 util.getConfigChannel('dm'); + const dmChannel = await getConfigChannel('dm'); await dmChannel.send({ embeds: [dmLogEmbed] }); } } diff --git a/src/listeners/message/highlight.ts b/src/listeners/message/highlight.ts index d7965c5..d68337c 100644 --- a/src/listeners/message/highlight.ts +++ b/src/listeners/message/highlight.ts @@ -9,7 +9,7 @@ export default class HighlightListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + public async exec(...[message]: BushClientEvents['messageCreate']) { if (!message.inGuild()) return; 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 diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts index 08a8a7c..a72ae02 100644 --- a/src/listeners/message/level.ts +++ b/src/listeners/message/level.ts @@ -1,4 +1,4 @@ -import { BushListener, Level, type BushCommandHandlerEvents } from '#lib'; +import { BushListener, handleError, Level, type BushCommandHandlerEvents } from '#lib'; import { MessageType } from 'discord.js'; export default class LevelListener extends BushListener { @@ -11,7 +11,7 @@ export default class LevelListener extends BushListener { }); } - public override async exec(...[message]: BushCommandHandlerEvents['messageInvalid']) { + public async exec(...[message]: BushCommandHandlerEvents['messageInvalid']) { if (message.author.bot || !message.author || !message.inGuild()) return; if (!(await message.guild.hasFeature('leveling'))) return; if (this.#levelCooldowns.has(`${message.guildId}-${message.author.id}`)) return; @@ -33,7 +33,7 @@ export default class LevelListener extends BushListener { const xpToGive = Level.genRandomizedXp(); user.xp = user.xp + xpToGive; const success = await user.save().catch((e) => { - void util.handleError('level', e); + void handleError('level', e); return false; }); const newLevel = Level.convertXpToLevel(user.xp); diff --git a/src/listeners/message/quoteCreate.ts b/src/listeners/message/quoteCreate.ts index 0d8b464..d43eef7 100644 --- a/src/listeners/message/quoteCreate.ts +++ b/src/listeners/message/quoteCreate.ts @@ -1,4 +1,4 @@ -import { BushListener, type BushClientEvents } from '#lib'; +import { BushListener, resolveMessagesFromLinks, type BushClientEvents } from '#lib'; export default class QuoteCreateListener extends BushListener { public constructor() { @@ -9,11 +9,11 @@ export default class QuoteCreateListener extends BushListener { }); } - public override async exec(...[message]: BushClientEvents['messageCreate']) { + public async exec(...[message]: BushClientEvents['messageCreate']) { if (message.author.id !== '322862723090219008' || !client.config.isProduction) return; if (!message.inGuild()) return; - const messages = await util.resolveMessagesFromLinks(message.content); + const messages = await resolveMessagesFromLinks(message.content); if (!messages.length) return; for (const msg of messages) { diff --git a/src/listeners/message/quoteEdit.ts b/src/listeners/message/quoteEdit.ts index 790f05a..7da0d95 100644 --- a/src/listeners/message/quoteEdit.ts +++ b/src/listeners/message/quoteEdit.ts @@ -9,7 +9,7 @@ // }); // } -// public override async exec(...[_, newMessage]: BushClientEvents['messageUpdate']) { +// public async exec(...[_, newMessage]: BushClientEvents['messageUpdate']) { // return; // // if (newMessage.partial) newMessage = await newMessage.fetch(); // // return new QuoteCreateListener().exec(newMessage); diff --git a/src/listeners/message/verbose.ts b/src/listeners/message/verbose.ts index 1225412..e3ef04f 100644 --- a/src/listeners/message/verbose.ts +++ b/src/listeners/message/verbose.ts @@ -10,7 +10,7 @@ export default class MessageVerboseListener extends BushListener { }); } - public override exec(...[message]: BushClientEvents['messageCreate']): void { + public exec(...[message]: BushClientEvents['messageCreate']): void { if (client.customReady) { if (message.channel?.type === ChannelType.DM) return; void client.console.verbose( diff --git a/src/listeners/other/consoleListener.ts b/src/listeners/other/consoleListener.ts index 0c30724..543f459 100644 --- a/src/listeners/other/consoleListener.ts +++ b/src/listeners/other/consoleListener.ts @@ -11,7 +11,7 @@ import { promisify } from 'util'; }); } - public override async exec(line: string) { + public async exec(line: string) { if (line.startsWith('eval ') || line.startsWith('ev ')) { /* eslint-disable @typescript-eslint/no-unused-vars */ const sh = promisify(exec), @@ -32,7 +32,6 @@ import { promisify } from 'util'; Embed, SelectMenuComponent, ReactionCollector, - Util, Collection } = await import('discord.js'); /* eslint-enable @typescript-eslint/no-unused-vars */ diff --git a/src/listeners/other/exit.ts b/src/listeners/other/exit.ts index 26ed36f..e16aeb7 100644 --- a/src/listeners/other/exit.ts +++ b/src/listeners/other/exit.ts @@ -8,7 +8,7 @@ export default class ExitListener extends BushListener { }); } - public override async exec(code: number) { + public async exec(code: number) { await client.console.error('processExit', `Process ended with code <<${code}>>.`); } } diff --git a/src/listeners/other/promiseRejection.ts b/src/listeners/other/promiseRejection.ts index ad82469..79bf5de 100644 --- a/src/listeners/other/promiseRejection.ts +++ b/src/listeners/other/promiseRejection.ts @@ -1,4 +1,4 @@ -import { BushListener } from '#lib'; +import { BushListener, formatError } from '#lib'; import CommandErrorListener from '../commands/commandError.js'; export default class PromiseRejectionListener extends BushListener { @@ -10,7 +10,7 @@ export default class PromiseRejectionListener extends BushListener { }); } - public override async exec(error: Error) { + public async exec(error: Error) { process.listeners('unhandledRejection').forEach((listener) => { if (listener.toString() === this.exec.toString()) return; process.removeListener('unhandledRejection', listener); @@ -20,11 +20,7 @@ export default class PromiseRejectionListener extends BushListener { level: 'error' }); - void client.console.error( - 'promiseRejection', - `An unhanded promise rejection occurred:\n${util.formatError(error, true)}`, - false - ); + void 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 }) diff --git a/src/listeners/other/uncaughtException.ts b/src/listeners/other/uncaughtException.ts index 8eb326f..3e80feb 100644 --- a/src/listeners/other/uncaughtException.ts +++ b/src/listeners/other/uncaughtException.ts @@ -1,4 +1,4 @@ -import { BushListener } from '#lib'; +import { BushListener, formatError } from '#lib'; import CommandErrorListener from '../commands/commandError.js'; export default class UncaughtExceptionListener extends BushListener { @@ -10,7 +10,7 @@ export default class UncaughtExceptionListener extends BushListener { }); } - public override async exec(error: Error) { + public async exec(error: Error) { process.listeners('uncaughtException').forEach((listener) => { if (listener.toString() === this.exec.toString()) return; process.removeListener('uncaughtException', listener); @@ -19,7 +19,7 @@ export default class UncaughtExceptionListener extends BushListener { level: 'error' }); - void client.console.error('uncaughtException', `An uncaught exception occurred:\n${util.formatError(error, true)}`, false); + 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 }) }); diff --git a/src/listeners/other/warning.ts b/src/listeners/other/warning.ts index 800472a..ba38f5f 100644 --- a/src/listeners/other/warning.ts +++ b/src/listeners/other/warning.ts @@ -1,4 +1,4 @@ -import { BushListener } from '#lib'; +import { BushListener, colors, formatError } from '#lib'; import CommandErrorListener from '../commands/commandError.js'; export default class WarningListener extends BushListener { @@ -9,17 +9,17 @@ export default class WarningListener extends BushListener { }); } - public override async exec(error: Error) { + public async exec(error: Error) { if (error.name === 'ExperimentalWarning') return; client.sentry.captureException(error, { level: 'warning' }); - void client.console.warn('warning', `A warning occurred:\n${util.formatError(error, true)}`, false); + void client.console.warn('warning', `A warning occurred:\n${formatError(error, true)}`, false); const embeds = await CommandErrorListener.generateErrorEmbed({ type: 'unhandledRejection', error: error }); - embeds[0].setColor(util.colors.warn).setTitle('A Warning Occurred'); + embeds[0].setColor(colors.warn).setTitle('A Warning Occurred'); void client.console.channelError({ embeds }); } diff --git a/src/listeners/rest/rateLimit.ts b/src/listeners/rest/rateLimit.ts index 4884e9f..5878d81 100644 --- a/src/listeners/rest/rateLimit.ts +++ b/src/listeners/rest/rateLimit.ts @@ -10,7 +10,7 @@ export default class RateLimitedListener extends BushListener { }); } - public override async exec(...[message]: RestEvents['rateLimited']): Promise<void> { + public async exec(...[message]: RestEvents['rateLimited']): Promise<void> { void client.console.superVerboseRaw('rateLimited', message); } } diff --git a/src/listeners/track-manual-punishments/modlogSyncBan.ts b/src/listeners/track-manual-punishments/modlogSyncBan.ts index be937b9..45d6f51 100644 --- a/src/listeners/track-manual-punishments/modlogSyncBan.ts +++ b/src/listeners/track-manual-punishments/modlogSyncBan.ts @@ -1,4 +1,4 @@ -import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, Moderation, ModLogType, sleep, Time, type BushClientEvents } from '#lib'; import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncBanListener extends BushListener { @@ -10,7 +10,7 @@ export default class ModlogSyncBanListener extends BushListener { }); } - public override async exec(...[ban]: BushClientEvents['guildBanAdd']) { + public async exec(...[ban]: BushClientEvents['guildBanAdd']) { if (!(await ban.guild.hasFeature('logManualPunishments'))) return; if (!ban.guild.members.me) return; // bot was banned if (!ban.guild.members.me.permissions.has(PermissionFlagsBits.ViewAuditLog)) { @@ -21,7 +21,7 @@ export default class ModlogSyncBanListener extends BushListener { } const now = new Date(); - await util.sleep(500 * Time.Millisecond); // wait for audit log entry + await sleep(500 * Time.Millisecond); // wait for audit log entry const logs = (await ban.guild.fetchAuditLogs({ type: AuditLogEvent.MemberBanAdd })).entries.filter( (entry) => entry.target?.id === ban.user.id @@ -33,9 +33,7 @@ export default class ModlogSyncBanListener extends BushListener { if (!first.executor || first.executor?.bot) return; if (Math.abs(first.createdAt.getTime() - now.getTime()) > Time.Minute) { - throw new Error( - `Time is off by over a minute: ${util.humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}` - ); + throw new Error(`Time is off by over a minute: ${humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}`); } const { log } = await Moderation.createModLogEntry({ @@ -51,7 +49,7 @@ export default class ModlogSyncBanListener extends BushListener { if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Red) + .setColor(colors.Red) .setTimestamp() .setFooter({ text: `CaseID: ${log.id}` }) .setAuthor({ diff --git a/src/listeners/track-manual-punishments/modlogSyncKick.ts b/src/listeners/track-manual-punishments/modlogSyncKick.ts index d136780..d901f5f 100644 --- a/src/listeners/track-manual-punishments/modlogSyncKick.ts +++ b/src/listeners/track-manual-punishments/modlogSyncKick.ts @@ -1,4 +1,4 @@ -import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, Moderation, ModLogType, sleep, Time, type BushClientEvents } from '#lib'; import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncKickListener extends BushListener { @@ -10,7 +10,7 @@ export default class ModlogSyncKickListener extends BushListener { }); } - public override async exec(...[member]: BushClientEvents['guildMemberRemove']) { + public async exec(...[member]: BushClientEvents['guildMemberRemove']) { if (!(await member.guild.hasFeature('logManualPunishments'))) return; if (!member.guild.members.me) return; // bot was removed from guild if (!member.guild.members.me.permissions.has(PermissionFlagsBits.ViewAuditLog)) { @@ -21,7 +21,7 @@ export default class ModlogSyncKickListener extends BushListener { } const now = new Date(); - await util.sleep(500 * Time.Millisecond); // wait for audit log entry + await sleep(500 * Time.Millisecond); // wait for audit log entry const logs = (await member.guild.fetchAuditLogs({ type: AuditLogEvent.MemberKick })).entries.filter( (entry) => entry.target?.id === member.user.id @@ -33,9 +33,7 @@ export default class ModlogSyncKickListener extends BushListener { if (!first.executor || first.executor?.bot) return; if (Math.abs(first.createdAt.getTime() - now.getTime()) > Time.Minute) { - throw new Error( - `Time is off by over a minute: ${util.humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}` - ); + throw new Error(`Time is off by over a minute: ${humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}`); } const { log } = await Moderation.createModLogEntry({ @@ -51,7 +49,7 @@ export default class ModlogSyncKickListener extends BushListener { if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Red) + .setColor(colors.Red) .setTimestamp() .setFooter({ text: `CaseID: ${log.id}` }) .setAuthor({ diff --git a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts index adcd7fe..4619edd 100644 --- a/src/listeners/track-manual-punishments/modlogSyncTimeout.ts +++ b/src/listeners/track-manual-punishments/modlogSyncTimeout.ts @@ -1,4 +1,4 @@ -import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, Moderation, ModLogType, sleep, Time, type BushClientEvents } from '#lib'; import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncTimeoutListener extends BushListener { @@ -10,7 +10,7 @@ export default class ModlogSyncTimeoutListener extends BushListener { }); } - public override async exec(...[_oldMember, newMember]: BushClientEvents['guildMemberUpdate']) { + public async exec(...[_oldMember, newMember]: BushClientEvents['guildMemberUpdate']) { if (!(await newMember.guild.hasFeature('logManualPunishments'))) return; if (!newMember.guild.members.me!.permissions.has(PermissionFlagsBits.ViewAuditLog)) { return newMember.guild.error( @@ -20,7 +20,7 @@ export default class ModlogSyncTimeoutListener extends BushListener { } const now = new Date(); - await util.sleep(500 * Time.Millisecond); // wait for audit log entry + await sleep(500 * Time.Millisecond); // wait for audit log entry const logs = (await newMember.guild.fetchAuditLogs({ type: AuditLogEvent.MemberUpdate })).entries.filter( (entry) => entry.target?.id === newMember.user.id @@ -35,9 +35,7 @@ export default class ModlogSyncTimeoutListener extends BushListener { if (!timeOut) return; if (Math.abs(first.createdAt.getTime() - now.getTime()) > Time.Minute) { - throw new Error( - `Time is off by over a minute: ${util.humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}` - ); + throw new Error(`Time is off by over a minute: ${humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}`); } const newTime = <string | null>timeOut.new ? new Date(<string>timeOut.new) : null; @@ -56,7 +54,7 @@ export default class ModlogSyncTimeoutListener extends BushListener { if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors[newTime ? 'Orange' : 'Green']) + .setColor(colors[newTime ? 'Orange' : 'Green']) .setTimestamp() .setFooter({ text: `CaseID: ${log.id}` }) .setAuthor({ diff --git a/src/listeners/track-manual-punishments/modlogSyncUnban.ts b/src/listeners/track-manual-punishments/modlogSyncUnban.ts index af0b21f..34979a5 100644 --- a/src/listeners/track-manual-punishments/modlogSyncUnban.ts +++ b/src/listeners/track-manual-punishments/modlogSyncUnban.ts @@ -1,4 +1,4 @@ -import { BushListener, Moderation, ModLogType, Time, type BushClientEvents } from '#lib'; +import { BushListener, colors, humanizeDuration, Moderation, ModLogType, sleep, Time, type BushClientEvents } from '#lib'; import { AuditLogEvent, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class ModlogSyncUnbanListener extends BushListener { @@ -10,7 +10,7 @@ export default class ModlogSyncUnbanListener extends BushListener { }); } - public override async exec(...[ban]: BushClientEvents['guildBanRemove']) { + public async exec(...[ban]: BushClientEvents['guildBanRemove']) { if (!(await ban.guild.hasFeature('logManualPunishments'))) return; if (!ban.guild.members.me!.permissions.has(PermissionFlagsBits.ViewAuditLog)) { return ban.guild.error( @@ -20,7 +20,7 @@ export default class ModlogSyncUnbanListener extends BushListener { } const now = new Date(); - await util.sleep(500 * Time.Millisecond); // wait for audit log entry + await sleep(500 * Time.Millisecond); // wait for audit log entry const logs = (await ban.guild.fetchAuditLogs({ type: AuditLogEvent.MemberBanRemove })).entries.filter( (entry) => entry.target?.id === ban.user.id @@ -32,9 +32,7 @@ export default class ModlogSyncUnbanListener extends BushListener { if (!first.executor || first.executor?.bot) return; if (Math.abs(first.createdAt.getTime() - now.getTime()) > Time.Minute) { - throw new Error( - `Time is off by over a minute: ${util.humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}` - ); + throw new Error(`Time is off by over a minute: ${humanizeDuration(Math.abs(first.createdAt.getTime() - now.getTime()))}`); } const { log } = await Moderation.createModLogEntry({ @@ -50,7 +48,7 @@ export default class ModlogSyncUnbanListener extends BushListener { if (!logChannel) return; const logEmbed = new EmbedBuilder() - .setColor(util.colors.Orange) + .setColor(colors.Orange) .setTimestamp() .setFooter({ text: `CaseID: ${log.id}` }) .setAuthor({ diff --git a/src/listeners/ws/INTERACTION_CREATE.ts b/src/listeners/ws/INTERACTION_CREATE.ts index 408e7e5..5f479ad 100644 --- a/src/listeners/ws/INTERACTION_CREATE.ts +++ b/src/listeners/ws/INTERACTION_CREATE.ts @@ -1,4 +1,4 @@ -import { BushListener, Moderation, PunishmentTypePresent } from '#lib'; +import { BushListener, capitalize, colors, emojis, Moderation, PunishmentTypePresent } from '#lib'; import assert from 'assert'; import { ActionRowBuilder, @@ -30,7 +30,7 @@ export default class WsInteractionCreateListener extends BushListener { }); } - public override async exec(interaction: APIInteraction) { + public async exec(interaction: APIInteraction) { // console.dir(interaction); const respond = ( @@ -67,7 +67,7 @@ export default class WsInteractionCreateListener extends BushListener { return respond({ type: InteractionResponseType.ChannelMessageWithSource, data: { - content: `${util.emojis.error} I am no longer in that server.` + content: `${emojis.error} I am no longer in that server.` } }); @@ -75,7 +75,7 @@ export default class WsInteractionCreateListener extends BushListener { type: InteractionResponseType.Modal, data: { custom_id: `appeal_submit;${punishment};${guildId};${userId};${modlogCase}`, - title: `${util.capitalize(punishment)} Appeal`, + title: `${capitalize(punishment)} Appeal`, components: [ { type: ComponentType.ActionRow, @@ -177,7 +177,7 @@ export default class WsInteractionCreateListener extends BushListener { return respond({ type: InteractionResponseType.ChannelMessageWithSource, data: { - content: `${util.emojis.error} I am no longer in that server.` + content: `${emojis.error} I am no longer in that server.` } }); @@ -186,7 +186,7 @@ export default class WsInteractionCreateListener extends BushListener { return respond({ type: InteractionResponseType.ChannelMessageWithSource, data: { - content: `${util.emojis.error} ${guild.name} has misconfigured their appeals channel.` + content: `${emojis.error} ${guild.name} has misconfigured their appeals channel.` } }); @@ -197,8 +197,8 @@ export default class WsInteractionCreateListener extends BushListener { // const caseId = await ModLog.findOne({ where: { user: userId, guild: guildId, id: modlogCase } }); const embed = new EmbedBuilder() - .setTitle(`${util.capitalize(punishment)} Appeal`) - .setColor(util.colors.newBlurple) + .setTitle(`${capitalize(punishment)} Appeal`) + .setColor(colors.newBlurple) .setTimestamp() .setFooter({ text: `CaseID: ${modlogCase}` }) .setAuthor({ name: user.tag, iconURL: user.displayAvatarURL() }) diff --git a/src/tasks/cpuUsage.ts b/src/tasks/cpuUsage.ts index e5cfc00..6ce6671 100644 --- a/src/tasks/cpuUsage.ts +++ b/src/tasks/cpuUsage.ts @@ -1,5 +1,5 @@ import { BushTask, Time } from '#lib'; -import osu from 'node-os-utils'; +import { cpu } from 'node-os-utils'; export default class CpuUsageTask extends BushTask { public constructor() { @@ -9,8 +9,8 @@ export default class CpuUsageTask extends BushTask { }); } - public override async exec() { - const cpu = await osu.cpu.usage(client.stats.cpu === undefined ? 100 * Time.Millisecond : Time.Minute); - client.stats.cpu = cpu; + public async exec() { + const cpuStats = await cpu.usage(client.stats.cpu === undefined ? 100 * Time.Millisecond : Time.Minute); + client.stats.cpu = cpuStats; } } diff --git a/src/tasks/handleReminders.ts b/src/tasks/handleReminders.ts index e281903..79693f5 100644 --- a/src/tasks/handleReminders.ts +++ b/src/tasks/handleReminders.ts @@ -1,4 +1,4 @@ -import { BushTask, Reminder, Time } from '#lib'; +import { BushTask, dateDelta, format, Reminder, Time } from '#lib'; const { Op } = (await import('sequelize')).default; export default class HandlerRemindersTask extends BushTask { @@ -9,7 +9,7 @@ export default class HandlerRemindersTask extends BushTask { }); } - public override async exec() { + public async exec() { const expiredEntries = await Reminder.findAll({ where: { expires: { @@ -26,9 +26,7 @@ export default class HandlerRemindersTask extends BushTask { void client.users .send( entry.user, - `The reminder you set ${util.dateDelta(entry.created)} ago has expired: ${util.format.bold(entry.content)}\n${ - entry.messageUrl - }` + `The reminder you set ${dateDelta(entry.created)} ago has expired: ${format.bold(entry.content)}\n${entry.messageUrl}` ) .catch(() => false); void entry.update({ notified: true }); diff --git a/src/tasks/removeExpiredPunishements.ts b/src/tasks/removeExpiredPunishements.ts index 6b36473..3f6f6dd 100644 --- a/src/tasks/removeExpiredPunishements.ts +++ b/src/tasks/removeExpiredPunishements.ts @@ -1,4 +1,4 @@ -import { ActivePunishment, ActivePunishmentType, BushTask, Time } from '#lib'; +import { ActivePunishment, ActivePunishmentType, BushTask, resolveNonCachedUser, Time } from '#lib'; import assert from 'assert'; const { Op } = (await import('sequelize')).default; @@ -10,7 +10,7 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { }); } - public override async exec() { + public async exec() { const expiredEntries = await ActivePunishment.findAll({ where: { expires: { @@ -31,7 +31,7 @@ export default class RemoveExpiredPunishmentsTask extends BushTask { // eslint-disable-next-line @typescript-eslint/no-misused-promises setTimeout(async () => { const member = guild.members.cache.get(entry.user); - const user = await util.resolveNonCachedUser(entry.user); + const user = await resolveNonCachedUser(entry.user); assert(guild); switch (entry.type) { diff --git a/src/tasks/updateCache.ts b/src/tasks/updateCache.ts index 3794f76..487b9bc 100644 --- a/src/tasks/updateCache.ts +++ b/src/tasks/updateCache.ts @@ -1,7 +1,7 @@ import { Time } from '#constants'; import { Global, Guild, Shared, type BushClient } from '#lib'; +import config from '../../config/options.js'; import { BushTask } from '../lib/extensions/discord-akairo/BushTask.js'; -import config from './../config/options.js'; export default class UpdateCacheTask extends BushTask { public constructor() { @@ -11,7 +11,7 @@ export default class UpdateCacheTask extends BushTask { }); } - public override async exec() { + public async exec() { await Promise.all([ UpdateCacheTask.#updateGlobalCache(client), UpdateCacheTask.#updateSharedCache(client), diff --git a/src/tasks/updateHighlightCache.ts b/src/tasks/updateHighlightCache.ts index 22289ce..d4fca71 100644 --- a/src/tasks/updateHighlightCache.ts +++ b/src/tasks/updateHighlightCache.ts @@ -9,7 +9,7 @@ export default class UpdateHighlightCacheTask extends BushTask { }); } - public override async exec() { + public async exec() { return client.highlightManager.syncCache(); } } diff --git a/src/tasks/updatePriceItemCache.ts b/src/tasks/updatePriceItemCache.ts index 6414eb0..e6d2db4 100644 --- a/src/tasks/updatePriceItemCache.ts +++ b/src/tasks/updatePriceItemCache.ts @@ -10,7 +10,7 @@ export default class UpdatePriceItemCache extends BushTask { }); } - public override async exec() { + public async exec() { //prettier-ignore const [bazaar, currentLowestBIN, averageLowestBIN, auctionAverages] = (await Promise.all([ got.get('https://api.hypixel.net/skyblock/bazaar').json().catch(() => {}), diff --git a/src/tasks/updateStats.ts b/src/tasks/updateStats.ts index f27841f..0e50c23 100644 --- a/src/tasks/updateStats.ts +++ b/src/tasks/updateStats.ts @@ -8,7 +8,7 @@ export default class UpdateStatsTask extends BushTask { }); } - public override async exec() { + public async exec() { const row = (await Stat.findByPk(client.config.environment)) ?? (await Stat.create({ environment: client.config.environment })); row.commandsUsed = client.stats.commandsUsed; diff --git a/tsconfig.json b/tsconfig.json index 7e49a42..9046efe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,6 +27,15 @@ "#tags": ["./src/lib/common/tags.js"] } }, - "include": ["src/**/*.ts", "src/**/*d.ts", "lib/**/*.ts", "ecosystem.config.cjs", ".eslintrc.cjs"], + "include": [ + "src/**/*.ts", + "src/**/*d.ts", + "lib/**/*.ts", + "ecosystem.config.cjs", + ".eslintrc.cjs", + "config/**/*.ts", + "tests/**/*.ts", + "vitest.config.ts" + ], "exclude": ["dist", "node_modules"] } @@ -373,6 +373,22 @@ __metadata: languageName: node linkType: hard +"@types/chai-subset@npm:^1.3.3": + version: 1.3.3 + resolution: "@types/chai-subset@npm:1.3.3" + dependencies: + "@types/chai": "*" + checksum: 4481da7345022995f5a105e6683744f7203d2c3d19cfe88d8e17274d045722948abf55e0adfd97709e0f043dade37a4d4e98cd4c660e2e8a14f23e6ecf79418f + languageName: node + linkType: hard + +"@types/chai@npm:*, @types/chai@npm:^4.3.1": + version: 4.3.1 + resolution: "@types/chai@npm:4.3.1" + checksum: 2ee246b76c469cd620a7a1876a73bc597074361b67d547b4bd96a0c1adb43597ede2d8589ab626192e14349d83cbb646cc11e2c179eeeb43ff11596de94d82c4 + languageName: node + linkType: hard + "@types/connect@npm:*": version: 3.4.35 resolution: "@types/connect@npm:3.4.35" @@ -409,13 +425,13 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:^4.17.18": - version: 4.17.28 - resolution: "@types/express-serve-static-core@npm:4.17.28" + version: 4.17.29 + resolution: "@types/express-serve-static-core@npm:4.17.29" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" - checksum: 826489811a5b371c10f02443b4ca894ffc05813bfdf2b60c224f5c18ac9a30a2e518cb9ef9fdfcaa2a1bb17f8bfa4ed1859ccdb252e879c9276271b4ee2df5a9 + checksum: ec4194dc59276ec6dd906887fc377be0cadf4aaa4d535d9052ab9624937ef2b984a8d9da2c11c96979e21f3d9f78f1da93e767dbcec637f7f13d2e3003151145 languageName: node linkType: hard @@ -513,10 +529,17 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^17.0.42": - version: 17.0.42 - resolution: "@types/node@npm:17.0.42" - checksum: a200cd87e4f12d4d5682a893ad6e1140720c6c074a2cd075f254b3b8306d6174f5a3630e5d2347efb5e9b80d420404b5fafc8fe3c7d4c81998cd914c50b19f75 +"@types/node@npm:*": + version: 18.0.0 + resolution: "@types/node@npm:18.0.0" + checksum: aab2b325727a2599f6d25ebe0dedf58c40fb66a51ce4ca9c0226ceb70fcda2d3afccdca29db5942eb48b158ee8585a274a1e3750c718bbd5399d7f41d62dfdcc + languageName: node + linkType: hard + +"@types/node@npm:^17.0.42": + version: 17.0.45 + resolution: "@types/node@npm:17.0.45" + checksum: aa04366b9103b7d6cfd6b2ef64182e0eaa7d4462c3f817618486ea0422984c51fc69fd0d436eae6c9e696ddfdbec9ccaa27a917f7c2e8c75c5d57827fe3d95e8 languageName: node linkType: hard @@ -911,6 +934,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: fd9429d3a3d4fd61782eb3962ae76b6d08aa7383123fca0596020013b3ebd6647891a85b05ce821c47d1471ed1271f00b0545cf6a4326cf2fc91efcc3b0fbecf + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -1082,6 +1112,7 @@ __metadata: sequelize: 6.20.1 tinycolor2: ^1.4.2 typescript: ^4.7.3 + vitest: ^0.14.2 vm2: ^3.9.9 languageName: unknown linkType: soft @@ -1163,6 +1194,21 @@ __metadata: languageName: node linkType: hard +"chai@npm:^4.3.6": + version: 4.3.6 + resolution: "chai@npm:4.3.6" + dependencies: + assertion-error: ^1.1.0 + check-error: ^1.0.2 + deep-eql: ^3.0.1 + get-func-name: ^2.0.0 + loupe: ^2.3.1 + pathval: ^1.1.1 + type-detect: ^4.0.5 + checksum: acff93fd537f96d4a4d62dd83810285dffcfccb5089e1bf2a1205b28ec82d93dff551368722893cf85004282df10ee68802737c33c90c5493957ed449ed7ce71 + languageName: node + linkType: hard + "chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -1180,6 +1226,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^1.0.2": + version: 1.0.2 + resolution: "check-error@npm:1.0.2" + checksum: d9d106504404b8addd1ee3f63f8c0eaa7cd962a1a28eb9c519b1c4a1dc7098be38007fc0060f045ee00f075fbb7a2a4f42abcf61d68323677e11ab98dc16042e + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -1427,6 +1480,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^3.0.1": + version: 3.0.1 + resolution: "deep-eql@npm:3.0.1" + dependencies: + type-detect: ^4.0.0 + checksum: 4f4c9fb79eb994fb6e81d4aa8b063adc40c00f831588aa65e20857d5d52f15fb23034a6576ecf886f7ff6222d5ae42e71e9b7d57113e0715b1df7ea1e812b125 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -1489,9 +1551,9 @@ __metadata: linkType: hard "discord-akairo@npm:@notenoughupdates/discord-akairo@dev": - version: 9.1.3-dev.1655212954.1d6b00f - resolution: "@notenoughupdates/discord-akairo@npm:9.1.3-dev.1655212954.1d6b00f" - checksum: 0d17ecd941d594723dd1d82f75bf48a007c030798466400d47d88352766d46b25ed310fb5b946da363cd7b6d84727cf65ea1035883caf4ed35f25dbd7e8d30e4 + version: 9.1.3-dev.1655341685.1230a9d + resolution: "@notenoughupdates/discord-akairo@npm:9.1.3-dev.1655341685.1230a9d" + checksum: 4bd037175eb8eeb129e27dd6d52e3c60bdf918a024e28adcefd27d3bd0ad2d9b503f70c87b4fd03a08ee5c2bf8f9b44dd87ab1d6e2cae5d4bff76fcb151dad70 languageName: node linkType: hard @@ -1510,8 +1572,8 @@ __metadata: linkType: hard "discord.js@npm:@notenoughupdates/discord.js@dev": - version: 14.0.0-dev.1655126646-6dfc4f06d - resolution: "@notenoughupdates/discord.js@npm:14.0.0-dev.1655126646-6dfc4f06d" + version: 14.0.0-dev.1655248959-627886d + resolution: "@notenoughupdates/discord.js@npm:14.0.0-dev.1655248959-627886d" dependencies: "@discordjs/builders": ^0.16.0-dev "@discordjs/collection": ^0.8.0-dev @@ -1524,7 +1586,7 @@ __metadata: tslib: ^2.4.0 undici: ^5.4.0 ws: ^8.8.0 - checksum: ad0e12b2c99232a7e5b8f728b0a29c61832e5ad9507b5fa24835a593b7ba0639107d16eb15d4354bd20296127331af4fdfe0617640224b8433f775cfd126cf63 + checksum: e5450f82918cfb9f558ab9fd8aad93b4e9120af26600a3301601f93c89563773143d2960373a5ef299e59c6a6c08ab221267409ce8f9a48078d908b0ac443e38 languageName: node linkType: hard @@ -1652,6 +1714,217 @@ __metadata: languageName: node linkType: hard +"esbuild-android-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-android-64@npm:0.14.44" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"esbuild-android-arm64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-android-arm64@npm:0.14.44" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"esbuild-darwin-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-darwin-64@npm:0.14.44" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"esbuild-darwin-arm64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-darwin-arm64@npm:0.14.44" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"esbuild-freebsd-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-freebsd-64@npm:0.14.44" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"esbuild-freebsd-arm64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-freebsd-arm64@npm:0.14.44" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"esbuild-linux-32@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-32@npm:0.14.44" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"esbuild-linux-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-64@npm:0.14.44" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"esbuild-linux-arm64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-arm64@npm:0.14.44" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"esbuild-linux-arm@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-arm@npm:0.14.44" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"esbuild-linux-mips64le@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-mips64le@npm:0.14.44" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"esbuild-linux-ppc64le@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-ppc64le@npm:0.14.44" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"esbuild-linux-riscv64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-riscv64@npm:0.14.44" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"esbuild-linux-s390x@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-linux-s390x@npm:0.14.44" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"esbuild-netbsd-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-netbsd-64@npm:0.14.44" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"esbuild-openbsd-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-openbsd-64@npm:0.14.44" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"esbuild-sunos-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-sunos-64@npm:0.14.44" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"esbuild-windows-32@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-windows-32@npm:0.14.44" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"esbuild-windows-64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-windows-64@npm:0.14.44" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"esbuild-windows-arm64@npm:0.14.44": + version: 0.14.44 + resolution: "esbuild-windows-arm64@npm:0.14.44" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"esbuild@npm:^0.14.27": + version: 0.14.44 + resolution: "esbuild@npm:0.14.44" + dependencies: + esbuild-android-64: 0.14.44 + esbuild-android-arm64: 0.14.44 + esbuild-darwin-64: 0.14.44 + esbuild-darwin-arm64: 0.14.44 + esbuild-freebsd-64: 0.14.44 + esbuild-freebsd-arm64: 0.14.44 + esbuild-linux-32: 0.14.44 + esbuild-linux-64: 0.14.44 + esbuild-linux-arm: 0.14.44 + esbuild-linux-arm64: 0.14.44 + esbuild-linux-mips64le: 0.14.44 + esbuild-linux-ppc64le: 0.14.44 + esbuild-linux-riscv64: 0.14.44 + esbuild-linux-s390x: 0.14.44 + esbuild-netbsd-64: 0.14.44 + esbuild-openbsd-64: 0.14.44 + esbuild-sunos-64: 0.14.44 + esbuild-windows-32: 0.14.44 + esbuild-windows-64: 0.14.44 + esbuild-windows-arm64: 0.14.44 + dependenciesMeta: + esbuild-android-64: + optional: true + esbuild-android-arm64: + optional: true + esbuild-darwin-64: + optional: true + esbuild-darwin-arm64: + optional: true + esbuild-freebsd-64: + optional: true + esbuild-freebsd-arm64: + optional: true + esbuild-linux-32: + optional: true + esbuild-linux-64: + optional: true + esbuild-linux-arm: + optional: true + esbuild-linux-arm64: + optional: true + esbuild-linux-mips64le: + optional: true + esbuild-linux-ppc64le: + optional: true + esbuild-linux-riscv64: + optional: true + esbuild-linux-s390x: + optional: true + esbuild-netbsd-64: + optional: true + esbuild-openbsd-64: + optional: true + esbuild-sunos-64: + optional: true + esbuild-windows-32: + optional: true + esbuild-windows-64: + optional: true + esbuild-windows-arm64: + optional: true + bin: + esbuild: bin/esbuild + checksum: e6f41e22c0a005c3785e123f8d41d7f84421f4ae6fa259f95d6b2449922787bede46d23682d7758c12bd4d7af04d21f0e4eb0291409814f8726fe7db77973354 + languageName: node + linkType: hard + "escape-latex@npm:^1.2.0": version: 1.2.0 resolution: "escape-latex@npm:1.2.0" @@ -2083,6 +2356,25 @@ __metadata: languageName: node linkType: hard +"fsevents@npm:~2.3.2": + version: 2.3.2 + resolution: "fsevents@npm:2.3.2" + dependencies: + node-gyp: latest + checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>": + version: 2.3.2 + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=18f3a7" + dependencies: + node-gyp: latest + conditions: os=darwin + languageName: node + linkType: hard + "function-bind@npm:^1.1.1": version: 1.1.1 resolution: "function-bind@npm:1.1.1" @@ -2192,6 +2484,13 @@ __metadata: languageName: node linkType: hard +"get-func-name@npm:^2.0.0": + version: 2.0.0 + resolution: "get-func-name@npm:2.0.0" + checksum: 8d82e69f3e7fab9e27c547945dfe5cc0c57fc0adf08ce135dddb01081d75684a03e7a0487466f478872b341d52ac763ae49e660d01ab83741f74932085f693c3 + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": version: 1.1.2 resolution: "get-intrinsic@npm:1.1.2" @@ -2907,6 +3206,13 @@ __metadata: languageName: node linkType: hard +"local-pkg@npm:^0.4.1": + version: 0.4.1 + resolution: "local-pkg@npm:0.4.1" + checksum: 4ab3b4853ffbc3b292a3ae2e22618dbc348418a2ebf60ada55fdcc5a8583f9167c89fc7977194ffa39d0a4c5078f9c421d246afdd252fea7a840f7a8f3b5f96b + languageName: node + linkType: hard + "localforage@npm:^1.8.1": version: 1.10.0 resolution: "localforage@npm:1.10.0" @@ -2947,6 +3253,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^2.3.1": + version: 2.3.4 + resolution: "loupe@npm:2.3.4" + dependencies: + get-func-name: ^2.0.0 + checksum: 5af91db61aa18530f1749a64735ee194ac263e65e9f4d1562bf3036c591f1baa948289c193e0e34c7b5e2c1b75d3c1dc4fce87f5edb3cee10b0c0df46bc9ffb3 + languageName: node + linkType: hard + "lowercase-keys@npm:^2.0.0": version: 2.0.0 resolution: "lowercase-keys@npm:2.0.0" @@ -3243,6 +3558,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.4": + version: 3.3.4 + resolution: "nanoid@npm:3.3.4" + bin: + nanoid: bin/nanoid.cjs + checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c + languageName: node + linkType: hard + "nanoid@npm:^4.0.0": version: 4.0.0 resolution: "nanoid@npm:4.0.0" @@ -3563,6 +3887,13 @@ __metadata: languageName: node linkType: hard +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: 090e3147716647fb7fb5b4b8c8e5b55e5d0a6086d085b6cd23f3d3c01fcf0ff56fd3cc22f2f4a033bd2e46ed55d61ed8379e123b42afe7d531a2a5fc8bb556d6 + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -3651,6 +3982,13 @@ __metadata: languageName: node linkType: hard +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 + languageName: node + linkType: hard + "picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -3688,6 +4026,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.13": + version: 8.4.14 + resolution: "postcss@npm:8.4.14" + dependencies: + nanoid: ^3.3.4 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 + languageName: node + linkType: hard + "postgres-array@npm:~2.0.0": version: 2.0.0 resolution: "postgres-array@npm:2.0.0" @@ -3944,6 +4293,20 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^2.59.0": + version: 2.75.6 + resolution: "rollup@npm:2.75.6" + dependencies: + fsevents: ~2.3.2 + dependenciesMeta: + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: df83c6d43a144a296daf82bed7f12f2dfcc6c495a64245e840d15fd21f2ab8b66bb3423e61c974875b33ccf53fb659d2ed099f272937ecf23af48815277c6cd5 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -4171,6 +4534,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.0.2": + version: 1.0.2 + resolution: "source-map-js@npm:1.0.2" + checksum: c049a7fc4deb9a7e9b481ae3d424cc793cb4845daa690bc5a05d428bf41bf231ced49b4cf0c9e77f9d42fdb3d20d6187619fc586605f5eabe995a316da8d377c + languageName: node + linkType: hard + "split2@npm:^4.1.0": version: 4.1.0 resolution: "split2@npm:4.1.0" @@ -4350,6 +4720,20 @@ __metadata: languageName: node linkType: hard +"tinypool@npm:^0.1.3": + version: 0.1.3 + resolution: "tinypool@npm:0.1.3" + checksum: 13ac687a23c03b02c2bf0b9711a3bb191d2c37941775a001b9617aac541c11ba144fb08de74f3f9723ec2649410f5d4fa7f0398fedd462b39fe3b30d19615ad8 + languageName: node + linkType: hard + +"tinyspy@npm:^0.3.2": + version: 0.3.3 + resolution: "tinyspy@npm:0.3.3" + checksum: 1beb8f04c100d9ac769ff85266594f07d6f08f41cfb95c4bef9da3e59988b42e8d4e66c2ec455d3ecad80027347920e87d7a7098c5a2a28637f9fee25e11a8f1 + languageName: node + linkType: hard + "to-buffer@npm:^1.1.1": version: 1.1.1 resolution: "to-buffer@npm:1.1.1" @@ -4433,6 +4817,13 @@ __metadata: languageName: node linkType: hard +"type-detect@npm:^4.0.0, type-detect@npm:^4.0.5": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15 + languageName: node + linkType: hard + "type-fest@npm:^0.20.2": version: 0.20.2 resolution: "type-fest@npm:0.20.2" @@ -4567,6 +4958,67 @@ __metadata: languageName: node linkType: hard +"vite@npm:^2.9.9": + version: 2.9.12 + resolution: "vite@npm:2.9.12" + dependencies: + esbuild: ^0.14.27 + fsevents: ~2.3.2 + postcss: ^8.4.13 + resolve: ^1.22.0 + rollup: ^2.59.0 + peerDependencies: + less: "*" + sass: "*" + stylus: "*" + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + less: + optional: true + sass: + optional: true + stylus: + optional: true + bin: + vite: bin/vite.js + checksum: 33cf8b6bfbbcae4b7f856d739ae519bed6b1ce23fc652ca98db1d4878bf35424885951c13e0d2536248423a0836d91ef7cae744ff40cbcbd6e7ffe495bd15ea5 + languageName: node + linkType: hard + +"vitest@npm:^0.14.2": + version: 0.14.2 + resolution: "vitest@npm:0.14.2" + dependencies: + "@types/chai": ^4.3.1 + "@types/chai-subset": ^1.3.3 + chai: ^4.3.6 + debug: ^4.3.4 + local-pkg: ^0.4.1 + tinypool: ^0.1.3 + tinyspy: ^0.3.2 + vite: ^2.9.9 + peerDependencies: + "@vitest/ui": "*" + c8: "*" + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@vitest/ui": + optional: true + c8: + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 9678822b0ccc4b31f93e4454972177df85b9857355affbc2e9f3b8f54507d0f9500783c3788c9f39681718a59f5a5bd7848bc7198a2a906a0a28d9328a2bb9cb + languageName: node + linkType: hard + "vm2@npm:^3.9.9": version: 3.9.9 resolution: "vm2@npm:3.9.9" |