diff options
Diffstat (limited to 'src')
22 files changed, 227 insertions, 301 deletions
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index 4f6a293..dbdfc4b 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -106,7 +106,7 @@ export default class EvalCommand extends BushCommand { Util, Collection } = await import('discord.js'), - { Canvas } = await import('node-canvas'); + { Canvas } = await import('canvas'); /* eslint-enable @typescript-eslint/no-unused-vars */ const inputJS = await util.inspectCleanRedactCodeblock(code.js, 'js'); diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts index 4748aba..de12e34 100644 --- a/src/commands/dev/reload.ts +++ b/src/commands/dev/reload.ts @@ -10,35 +10,35 @@ export default class ReloadCommand extends BushCommand { usage: 'reload', examples: ['reload'] }, - args: [ - { - id: 'fast', - match: 'flag', - flag: '--fast' - } - ], + // args: [ + // { + // id: 'fast', + // match: 'flag', + // flag: '--fast' + // } + // ], ownerOnly: true, typing: true, - slashOptions: [ - { - name: 'fast', - description: 'Whether to use esbuild for fast compiling or not', - type: 'BOOLEAN', - required: false - } - ], + // slashOptions: [ + // { + // name: 'fast', + // description: 'Whether to use esbuild for fast compiling or not', + // type: 'BOOLEAN', + // required: false + // } + // ], slash: true }); } - public async exec(message: BushMessage | BushSlashMessage, { fast }: { fast: boolean }): Promise<unknown> { + public async exec(message: BushMessage | BushSlashMessage /* { fast }: { fast: boolean } */): Promise<unknown> { if (!message.author.isOwner()) return await message.util.reply(`${util.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-${fast ? 'esbuild' : 'tsc'}`); + output = await util.shell(`yarn build-${/* fast ? 'esbuild' : */ 'tsc'}`); client.commandHandler.reloadAll(); client.listenerHandler.reloadAll(); client.inhibitorHandler.reloadAll(); diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts index dc4562c..a088776 100644 --- a/src/commands/dev/servers.ts +++ b/src/commands/dev/servers.ts @@ -37,7 +37,7 @@ export default class ServersCommand extends BushCommand { `**ID:** ${g.id}\n**Owner:** ${owner ? owner : g.ownerId}\n**Members:** ${g.memberCount.toLocaleString()}`, false ) - .setTitle(`Server List [${client.guilds.cache.size}]`) + .setTitle(`Server List [\`${client.guilds.cache.size.toLocaleString()}\`]`) .setColor(util.colors.default); }); embeds.push(embed); diff --git a/src/commands/moderation/lockdown.ts b/src/commands/moderation/_lockdown.ts index db074b1..db074b1 100644 --- a/src/commands/moderation/lockdown.ts +++ b/src/commands/moderation/_lockdown.ts diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index 42e8fd5..4bd81bb 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -43,7 +43,7 @@ export default class WhoHasRoleCommand extends BushCommand { const embedPages = chunkedRoleMembers.map( (chunk) => new MessageEmbed({ - title: `${args.role.name}'s Members`, + title: `${args.role.name}'s Members [\`${args.role.members.size.toLocaleString()}\`]`, description: chunk.join('\n'), color: util.colors.default }) diff --git a/src/inhibitors/blacklist/channelGlobalBlacklist.ts b/src/inhibitors/blacklist/channelGlobalBlacklist.ts index 0ddb2bb..28a2c5e 100644 --- a/src/inhibitors/blacklist/channelGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/channelGlobalBlacklist.ts @@ -14,7 +14,7 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user.id === message.author.id) return false; if (client.cache.global.blacklistedChannels.includes(message.channel.id)) { - client.console.debug(`channelGlobalBlacklist blocked message.`); + // client.console.debug(`channelGlobalBlacklist blocked message.`); return true; } } diff --git a/src/inhibitors/blacklist/channelGuildBlacklist.ts b/src/inhibitors/blacklist/channelGuildBlacklist.ts index 13f3644..7fa4ccf 100644 --- a/src/inhibitors/blacklist/channelGuildBlacklist.ts +++ b/src/inhibitors/blacklist/channelGuildBlacklist.ts @@ -14,7 +14,7 @@ export default class ChannelGuildBlacklistInhibitor extends BushInhibitor { if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user.id === message.author.id) return false; if ((await message.guild.getSetting('blacklistedChannels'))?.includes(message.channel.id)) { - client.console.debug(`channelGuildBlacklist blocked message.`); + // client.console.debug(`channelGuildBlacklist blocked message.`); return true; } } diff --git a/src/inhibitors/blacklist/guildBlacklist.ts b/src/inhibitors/blacklist/guildBlacklist.ts index 0d99c38..5a2123c 100644 --- a/src/inhibitors/blacklist/guildBlacklist.ts +++ b/src/inhibitors/blacklist/guildBlacklist.ts @@ -17,7 +17,7 @@ export default class GuildBlacklistInhibitor extends BushInhibitor { ) return false; if (client.cache.global.blacklistedGuilds.includes(message.guild.id)) { - client.console.debug(`GuildBlacklistInhibitor blocked message.`); + // client.console.debug(`GuildBlacklistInhibitor blocked message.`); return true; } } diff --git a/src/inhibitors/blacklist/userGlobalBlacklist.ts b/src/inhibitors/blacklist/userGlobalBlacklist.ts index 4ad1982..d8964a4 100644 --- a/src/inhibitors/blacklist/userGlobalBlacklist.ts +++ b/src/inhibitors/blacklist/userGlobalBlacklist.ts @@ -14,7 +14,7 @@ export default class UserGlobalBlacklistInhibitor extends BushInhibitor { if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user.id === message.author.id) return false; if (client.cache.global.blacklistedUsers.includes(message.author.id)) { - client.console.debug(`userGlobalBlacklist blocked message.`); + // client.console.debug(`userGlobalBlacklist blocked message.`); return true; } } diff --git a/src/inhibitors/blacklist/userGuildBlacklist.ts b/src/inhibitors/blacklist/userGuildBlacklist.ts index 473c0fb..0bc6749 100644 --- a/src/inhibitors/blacklist/userGuildBlacklist.ts +++ b/src/inhibitors/blacklist/userGuildBlacklist.ts @@ -14,7 +14,7 @@ export default class UserGuildBlacklistInhibitor extends BushInhibitor { if (client.isOwner(message.author) || client.isSuperUser(message.author) || client.user.id === message.author.id) return false; if ((await message.guild.getSetting('blacklistedUsers'))?.includes(message.author.id)) { - client.console.debug(`userGuildBlacklist blocked message.`); + // client.console.debug(`userGuildBlacklist blocked message.`); return true; } } diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index 926a529..b6c9c1f 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -40,8 +40,6 @@ import got from 'got'; import humanizeDuration from 'humanize-duration'; import moment from 'moment'; import { inspect, InspectOptions, promisify } from 'util'; -import _badLinks from '../../badlinks.json'; // Stolen from https://github.com/nacrt/SkyblockClient-REPO/blob/main/files/scamlinks.json -import badWords from '../../badwords.json'; import { ActivePunishment, ActivePunishmentType } from '../../models/ActivePunishment'; import { BushNewsChannel } from '../discord.js/BushNewsChannel'; import { BushTextChannel } from '../discord.js/BushTextChannel'; @@ -455,17 +453,15 @@ export class BushClientUtil extends ClientUtil { * * Embed Description Limit = 4096 characters * * Embed Field Limit = 1024 characters */ - public async codeblock(code: string, length: number, language?: 'ts' | 'js' | 'sh' | 'json'): Promise<string> { + public async codeblock(code: string, length: number, language?: 'ts' | 'js' | 'sh' | 'json' | ''): Promise<string> { let hasteOut = ''; const tildes = '```'; + language = language ?? ''; const formattingLength = 2 * tildes.length + language?.length ?? 0 + 2 * '\n'.length; if (code.length + formattingLength >= length) hasteOut = 'Too large to display. Hastebin: ' + (await this.haste(code)); const code2 = hasteOut ? code.substring(0, length - (hasteOut.length + '\n'.length + formattingLength)) : code; - return ( - tildes + language ?? - '' + '\n' + Util.cleanCodeBlockContent(code2) + '\n' + tildes + (hasteOut.length ? '\n' + hasteOut : '') - ); + return tildes + language + '\n' + code2 + '\n' + tildes + (hasteOut.length ? '\n' + hasteOut : ''); } private mapCredential(old: string) { @@ -797,81 +793,6 @@ export class BushClientUtil extends ClientUtil { public async lockdownChannel(options: { channel: BushTextChannel | BushNewsChannel; moderator: BushUserResolvable }) {} /* eslint-enable @typescript-eslint/no-unused-vars */ - public async automod(message: BushMessage) { - if (message.guild.id !== client.consts.mappings.guilds.bush) return; // just temporary - /* await message.guild.getSetting('autoModPhases'); */ - const badLinks = _badLinks.map((link) => { - return { [link]: 3 }; - }); - - const wordArray = [...Object.keys(badWords), ...Object.keys(badLinks)]; - const offences: { [key: string]: number } = {}; - wordArray.forEach((word) => { - if (message.content?.toLowerCase().replace(/ /g, '').includes(word.toLowerCase().replace(/ /g, ''))) { - if (offences[word]) offences[word] = wordArray[word]; - } - }); - if (!Object.keys(offences)?.length) return; - - const highestOffence = Object.values(offences).sort((a, b) => b - a)[0]; - - switch (highestOffence) { - case 0: { - if (message.deletable) void message.delete(); - break; - } - case 1: { - if (message.deletable) void message.delete(); - void message.member.warn({ - moderator: message.guild.me, - reason: 'Saying a blacklisted word.' - }); - break; - } - case 2: { - if (message.deletable) void message.delete(); - void message.member.mute({ - moderator: message.guild.me, - reason: 'Saying a blacklisted word.', - duration: 900_000 // 15 minutes - }); - break; - } - case 3: { - if (message.deletable) void message.delete(); - void message.member.mute({ - moderator: message.guild.me, - reason: 'Saying a blacklisted word.', - duration: 0 // perm - }); - break; - } - } - - const color = - highestOffence === 0 - ? util.colors.lightGray - : highestOffence === 1 - ? util.colors.yellow - : highestOffence === 2 - ? util.colors.orange - : util.colors.red; - void (message.guild.channels.cache.get('783088333055066212') as TextChannel).send({ - embeds: [ - new MessageEmbed() - .setTitle(`[Severity ${highestOffence}] Automod Action Performed`) - .setDescription( - `**User:** ${message.author} (${message.author.tag})\n**Blacklisted Words:** ${util - .surroundArray(Object.keys(offences), '`') - .join()}` - ) - .addField('Message Content', `${this.codeblock(message.content, 1024)}`) - .setColor(color) - .setTimestamp() - ] - }); - } - public capitalizeFirstLetter(string: string): string { return string.charAt(0)?.toUpperCase() + string.slice(1); } diff --git a/src/lib/extensions/discord.js/BushClientEvents.d.ts b/src/lib/extensions/discord.js/BushClientEvents.d.ts index 6c1fec5..da5d647 100644 --- a/src/lib/extensions/discord.js/BushClientEvents.d.ts +++ b/src/lib/extensions/discord.js/BushClientEvents.d.ts @@ -1,6 +1,7 @@ import { ClientEvents } from 'discord.js'; -import { BushMessage } from './BushMessage'; +import { BushMessage, BushPartialMessage } from './BushMessage'; export interface BushClientEvents extends ClientEvents { messageCreate: [message: BushMessage]; + messageUpdate: [oldMessage: BushMessage | BushPartialMessage, newMessage: BushMessage | BushPartialMessage]; } diff --git a/src/lib/extensions/discord.js/BushMessage.ts b/src/lib/extensions/discord.js/BushMessage.ts index 68f3de0..7971a6d 100644 --- a/src/lib/extensions/discord.js/BushMessage.ts +++ b/src/lib/extensions/discord.js/BushMessage.ts @@ -1,4 +1,5 @@ -import { Message } from 'discord.js'; +/* eslint-disable @typescript-eslint/no-empty-interface */ +import { Message, Partialize } from 'discord.js'; import { BushClient } from '../discord-akairo/BushClient'; import { BushDMChannel } from './BushDMChannel'; import { BushGuild } from './BushGuild'; @@ -8,6 +9,8 @@ import { BushTextChannel } from './BushTextChannel'; import { BushThreadChannel } from './BushThreadChannel'; import { BushUser } from './BushUser'; +export interface BushPartialMessage + extends Partialize<BushMessage, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> {} export class BushMessage extends Message { public declare readonly client: BushClient; // public util: BushCommandUtil; @@ -23,4 +26,7 @@ export class BushMessage extends Message { super(client, data, channel); // this.util = new BushCommandUtil(client.commandHandler, this); } + public fetch(force?: boolean): Promise<BushMessage> { + return super.fetch(force) as Promise<BushMessage>; + } } diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index 526977b..095ce58 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -1,4 +1,4 @@ -import { BushCommandHandlerEvents, BushListener } from '@lib'; +import { BushCommandHandlerEvents, BushListener, BushMessage } from '@lib'; export default class CommandBlockedListener extends BushListener { public constructor() { @@ -9,9 +9,17 @@ export default class CommandBlockedListener extends BushListener { } public async exec(...[message, command, reason]: BushCommandHandlerEvents['commandBlocked']): Promise<unknown> { + return await CommandBlockedListener.handleBlocked(message, command, reason); + } + + public static async handleBlocked( + ...[message, command, reason]: BushCommandHandlerEvents['commandBlocked'] | BushCommandHandlerEvents['slashBlocked'] + ): Promise<unknown> { + const isSlash = message.util.isSlash; + void client.console.info( - 'CommandBlocked', - `<<${message.author.tag}>> tried to run <<${message.util.parsed.command}>> but was blocked because <<${reason}>>.`, + `${isSlash ? 'Slash' : 'Command'}Blocked`, + `<<${message.author.tag}>> tried to run <<${command}>> but was blocked because <<${reason}>>.`, true ); const reasons = client.consts.BlockedReasons; @@ -19,30 +27,45 @@ export default class CommandBlockedListener extends BushListener { switch (reason) { case reasons.OWNER: { return await message.util.reply({ - content: `${util.emojis.error} Only my developers can run the \`${message.util.parsed.command}\` command.` + content: `${util.emojis.error} Only my developers can run the \`${command}\` command.`, + ephemeral: true }); } case reasons.SUPER_USER: { return await message.util.reply({ - content: `${util.emojis.error} You must be a superuser to run the \`${message.util.parsed.command}\` command.` + content: `${util.emojis.error} You must be a superuser to run the \`${command}\` command.`, + ephemeral: true }); } case reasons.DISABLED_GLOBAL: { return await message.util.reply({ - content: `${util.emojis.error} My developers disabled the \`${message.util.parsed.command}\` command.` + content: `${util.emojis.error} My developers disabled the \`${command}\` command.`, + ephemeral: true }); } case reasons.DISABLED_GUILD: { return await message.util.reply({ - content: `${util.emojis.error} The \`${command.aliases[0]}\` command is currently disabled in \`${message.guild.name}\`.` + content: `${util.emojis.error} The \`${command}\` command is currently disabled in \`${message.guild.name}\`.`, + ephemeral: true }); } case reasons.CHANNEL_GLOBAL_BLACKLIST: case reasons.CHANNEL_GUILD_BLACKLIST: + return isSlash + ? message.util.reply({ content: `${util.emojis.error} You cannot use this bot in this channel.`, ephemeral: true }) + : (message as BushMessage).react(util.emojis.error); case reasons.USER_GLOBAL_BLACKLIST: case reasons.USER_GUILD_BLACKLIST: + return isSlash + ? message.util.reply({ content: `${util.emojis.error} You are blacklisted from using this bot.`, ephemeral: true }) + : (message as BushMessage).react(util.emojis.error); case reasons.ROLE_BLACKLIST: { - return; + return isSlash + ? message.util.reply({ + content: `${util.emojis.error} One of your roles blacklists you from using this bot.`, + ephemeral: true + }) + : (message as BushMessage).react(util.emojis.error); } case reasons.RESTRICTED_CHANNEL: { const channels = command.restrictedChannels; @@ -52,7 +75,8 @@ export default class CommandBlockedListener extends BushListener { }); const pretty = util.oxford(names, 'and', undefined); return await message.util.reply({ - content: `${util.emojis.error} \`${command}\` can only be run in ${pretty}.` + content: `${util.emojis.error} \`${command}\` can only be run in ${pretty}.`, + ephemeral: true }); } case reasons.RESTRICTED_GUILD: { @@ -63,12 +87,14 @@ export default class CommandBlockedListener extends BushListener { }); const pretty = util.oxford(names, 'and', undefined); return await message.util.reply({ - content: `${util.emojis.error} \`${command}\` can only be run in ${pretty}.` + content: `${util.emojis.error} \`${command}\` can only be run in ${pretty}.`, + ephemeral: true }); } default: { return await message.util.reply({ - content: `${util.emojis.error} Command blocked with reason \`${reason}\`` + content: `${util.emojis.error} Command blocked with reason \`${reason}\``, + ephemeral: true }); } } diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index c287656..0525fe6 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -1,5 +1,4 @@ import { BushCommandHandlerEvents, BushListener } from '@lib'; -import { stripIndents } from 'common-tags'; import { MessageEmbed } from 'discord.js'; export default class CommandErrorListener extends BushListener { @@ -10,21 +9,33 @@ export default class CommandErrorListener extends BushListener { }); } - public async exec(...[error, message, command]: BushCommandHandlerEvents['error']): Promise<void> { + public async exec(...[error, message, command]: BushCommandHandlerEvents['error']): Promise<unknown> { + return await CommandErrorListener.handleError(error, message, command); + } + + public static async handleError( + ...[error, message, command]: BushCommandHandlerEvents['error'] | BushCommandHandlerEvents['slashError'] + ): Promise<void> { + const isSlash = message.util.isSlash; + const errorNo = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number const errorEmbed: MessageEmbed = new MessageEmbed() - .setTitle(`Error # \`${errorNo}\`: An error occurred`) - .setDescription( - stripIndents`**User:** ${message.author} (${message.author.tag}) - **Command:** ${command} - **Channel:** ${message.channel} (${message.channel?.id}) - **Message:** [link](${message.url})` - ) + .setTitle(`${isSlash ? 'Slash ' : ''}Error # \`${errorNo}\`: An error occurred`) .addField('Error', await util.codeblock(`${error?.stack || error}`, 1024, 'js')) .setColor(util.colors.error) .setTimestamp(); - + const description = [ + `**User:** ${message.author} (${message.author.tag})`, + `**Command:** ${command}`, + `**Channel:** ${message.channel} (${message.channel?.id})`, + `**Message:** [link](${message.url})` + ]; + // @ts-ignore: shut + if (error?.code) description.push(`**Error Code:** \`${error.code}\``); + if (message?.util?.parsed?.content) description.push(`**Command Content:** ${message.util.parsed.content}`); + errorEmbed.setDescription(description.join('\n')); await client.logger.channelError({ embeds: [errorEmbed] }); + const heading = `${isSlash ? 'Slash' : 'Command'}Error`; if (message) { if (!client.config.owners.includes(message.author.id)) { const errorUserEmbed: MessageEmbed = new MessageEmbed() @@ -35,29 +46,33 @@ export default class CommandErrorListener extends BushListener { errorUserEmbed.setDescription(`Oh no! An error occurred. Please give the developers code \`${errorNo}\`.`); else errorUserEmbed.setDescription( - `Oh no! While running the command \`${command.id}\`, an error occurred. Please give the developers code \`${errorNo}\`.` + `Oh no! While running the ${isSlash ? 'slash ' : ''}command \`${ + command.id + }\`, an error occurred. Please give the developers code \`${errorNo}\`.` ); (await message.util?.send({ embeds: [errorUserEmbed] }).catch((e) => { const channel = message.channel.type === 'DM' ? message.channel.recipient.tag : message.channel.name; - void client.console.warn('CommandError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack || e); - })) ?? client.console.error('CommandError', `Failed to send user error embed.` + error?.stack || error, false); + void client.console.warn(heading, `Failed to send user error embed in <<${channel}>>:\n` + e?.stack || e); + })) ?? client.console.error(heading, `Failed to send user error embed.` + error?.stack || error, false); } else { const errorDevEmbed = new MessageEmbed() - .setTitle('A Command Error Occurred') + // @ts-ignore: shut + .setTitle(`A Command Error Occurred ${error?.code ? `\`${error.code}\`` : ''}`) .setColor(util.colors.error) .setTimestamp() .setDescription(await util.codeblock(`${error?.stack || error}`, 2048, 'js')); (await message.util?.send({ embeds: [errorDevEmbed] }).catch((e) => { const channel = message.channel.type === 'DM' ? message.channel.recipient.tag : message.channel.name; - void client.console.warn('CommandError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack || e); - })) ?? client.console.error('CommandError', `Failed to send owner error stack.` + error?.stack || error, false); + void client.console.warn(heading, `Failed to send owner error stack in <<${channel}>>.` + e?.stack || e); + })) ?? client.console.error(heading, `Failed to send owner error stack.` + error?.stack || error, false); } } const channel = message.channel.type === 'DM' ? message.channel.recipient.tag : message.channel.name; void client.console.error( - 'CommandError', - `an error occurred with the <<${command}>> command in <<${channel}>> triggered by <<${message?.author?.tag}>>:\n` + - error?.stack || error, + heading, + `an error occurred with the <<${command}>> ${isSlash ? 'slash ' : ''}command in <<${channel}>> triggered by <<${ + message?.author?.tag + }>>:\n` + error?.stack || error, false ); } diff --git a/src/listeners/commands/commandMissingPermissions.ts b/src/listeners/commands/commandMissingPermissions.ts index 3ada70f..70adcd1 100644 --- a/src/listeners/commands/commandMissingPermissions.ts +++ b/src/listeners/commands/commandMissingPermissions.ts @@ -9,7 +9,15 @@ export default class CommandMissingPermissionsListener extends BushListener { }); } - public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['missingPermissions']): Promise<void> { + public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['missingPermissions']): Promise<unknown> { + return await CommandMissingPermissionsListener.handleMissing(message, command, type, missing); + } + + public static async handleMissing( + ...[message, command, type, missing]: + | BushCommandHandlerEvents['missingPermissions'] + | BushCommandHandlerEvents['slashMissingPermissions'] + ): Promise<unknown> { const niceMissing = []; missing.forEach((missing) => { if (client.consts.mappings.permissions[missing]) { @@ -28,7 +36,7 @@ export default class CommandMissingPermissionsListener extends BushListener { }>> but could not because <<${type}>> is missing the ${consoleFormat} permissions${missing.length ? 's' : ''}.` ); if (type == 'client') { - await message.util + return await message.util .reply( `${util.emojis.error} I am missing the ${discordFormat} permission${missing.length ? 's' : ''} required for the \`${ command?.id @@ -36,7 +44,7 @@ export default class CommandMissingPermissionsListener extends BushListener { ) .catch(() => {}); } else if (type == 'user') { - await message.util + return await message.util .reply( `${util.emojis.error} You are missing the ${discordFormat} permission${ missing.length ? 's' : '' diff --git a/src/listeners/commands/slashBlocked.ts b/src/listeners/commands/slashBlocked.ts index f79cec3..bdad2ea 100644 --- a/src/listeners/commands/slashBlocked.ts +++ b/src/listeners/commands/slashBlocked.ts @@ -1,4 +1,5 @@ import { BushCommandHandlerEvents, BushListener } from '@lib'; +import CommandBlockedListener from './commandBlocked'; export default class SlashBlockedListener extends BushListener { public constructor() { @@ -10,69 +11,6 @@ export default class SlashBlockedListener extends BushListener { } public async exec(...[message, command, reason]: BushCommandHandlerEvents['slashBlocked']): Promise<unknown> { - void client.console.info( - 'SlashBlocked', - `<<${message.author.tag}>> tried to run <<${message.util.parsed.command}>> but was blocked because <<${reason}>>.`, - true - ); - - const reasons = client.consts.BlockedReasons; - - switch (reason) { - case reasons.OWNER: { - return await message.util.reply({ - content: `${util.emojis.error} Only my developers can run the \`${message.util.parsed.command}\` command.` - }); - } - case reasons.SUPER_USER: { - return await message.util.reply({ - content: `${util.emojis.error} You must be a superuser to run the \`${message.util.parsed.command}\` command.` - }); - } - case reasons.DISABLED_GLOBAL: { - return await message.util.reply({ - content: `${util.emojis.error} My developers disabled the \`${message.util.parsed.command}\` command.` - }); - } - case reasons.DISABLED_GUILD: { - return await message.util.reply({ - content: `${util.emojis.error} The \`${command.aliases[0]}\` command is currently disabled in \`${message.guild.name}\`.` - }); - } - case reasons.CHANNEL_GLOBAL_BLACKLIST: - case reasons.CHANNEL_GUILD_BLACKLIST: - case reasons.USER_GLOBAL_BLACKLIST: - case reasons.USER_GUILD_BLACKLIST: - case reasons.ROLE_BLACKLIST: { - return; - } - case reasons.RESTRICTED_CHANNEL: { - const channels = command.restrictedChannels; - const names = []; - channels.forEach((c) => { - names.push(`<#${c}>`); - }); - const pretty = util.oxford(names, 'and', undefined); - return await message.util.reply({ - content: `${util.emojis.error} \`${command}\` can only be run in ${pretty}.` - }); - } - case reasons.RESTRICTED_GUILD: { - const guilds = command.restrictedGuilds; - const names = []; - guilds.forEach((g) => { - names.push(`\`${client.guilds.cache.get(g).name}\``); - }); - const pretty = util.oxford(names, 'and', undefined); - return await message.util.reply({ - content: `${util.emojis.error} \`${command}\` can only be run in ${pretty}.` - }); - } - default: { - return await message.util.reply({ - content: `${util.emojis.error} Command blocked with reason \`${reason}\`` - }); - } - } + return await CommandBlockedListener.handleBlocked(message, command, reason); } } diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts index a108151..67febfd 100644 --- a/src/listeners/commands/slashCommandError.ts +++ b/src/listeners/commands/slashCommandError.ts @@ -1,6 +1,5 @@ import { BushCommandHandlerEvents, BushListener } from '@lib'; -import { stripIndents } from 'common-tags'; -import { GuildChannel, MessageEmbed } from 'discord.js'; +import CommandErrorListener from './commandError'; export default class SlashCommandErrorListener extends BushListener { public constructor() { @@ -11,53 +10,6 @@ export default class SlashCommandErrorListener extends BushListener { }); } async exec(...[error, message, command]: BushCommandHandlerEvents['slashError']): Promise<void> { - const errorNo = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number - const errorEmbed: MessageEmbed = new MessageEmbed() - .setTitle(`Slash Error # \`${errorNo}\`: An error occurred`) - .setDescription( - stripIndents`**User:** ${message.author} (${message.author.tag}) - **Slash Command:** ${command} - **Channel:** ${message.channel || message.interaction.user?.tag} ${message.channel ? `(${message.channel?.id})` : ''} - **Message:** [link](https://discord.com/${message.guild?.id}/${message.channel?.id}/${message.id})` - ) - .addField('Error', await util.codeblock(`${error?.stack || error}`, 1024, 'js')) - .setColor(util.colors.error) - .setTimestamp(); - - await client.logger.channelError({ embeds: [errorEmbed] }); - if (message) { - const channel = (message.channel as GuildChannel)?.name || message.interaction.user.tag; - if (!client.config.owners.includes(message.author.id)) { - const errorUserEmbed: MessageEmbed = new MessageEmbed() - .setTitle('A Slash Command Error Occurred') - .setColor(util.colors.error) - .setTimestamp(); - if (!command) - errorUserEmbed.setDescription(`Oh no! An error occurred. Please give the developers code \`${errorNo}\`.`); - else - errorUserEmbed.setDescription( - `Oh no! While running the command \`${command.id}\`, an error occurred. Please give the developers code \`${errorNo}\`.` - ); - (await message.util?.send({ embeds: [errorUserEmbed] }).catch((e) => { - void client.console.warn('SlashError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack || e); - })) ?? client.console.error('SlashError', `Failed to send user error embed.` + error?.stack || error, false); - } else { - const errorDevEmbed = new MessageEmbed() - .setTitle('A Slash Command Error Occurred') - .setColor(util.colors.error) - .setTimestamp() - .setDescription(await util.codeblock(`${error?.stack || error}`, 2048, 'js')); - (await message.util?.send({ embeds: [errorDevEmbed] }).catch((e) => { - void client.console.warn('SlashError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack || e); - })) ?? client.console.error('SlashError', `Failed to send user error embed.` + error?.stack || error, false); - } - } - const channel = (message.channel as GuildChannel)?.name || message.interaction.user.tag; - void client.console.error( - 'SlashError', - `an error occurred with the <<${command}>> command in <<${channel}>> triggered by <<${message?.author?.tag}>>:\n` + - error?.stack || error, - false - ); + return await CommandErrorListener.handleError(error, message, command); } } diff --git a/src/listeners/commands/slashMissingPermissions.ts b/src/listeners/commands/slashMissingPermissions.ts index 70ed7c2..07c63e9 100644 --- a/src/listeners/commands/slashMissingPermissions.ts +++ b/src/listeners/commands/slashMissingPermissions.ts @@ -1,4 +1,5 @@ import { BushCommandHandlerEvents, BushListener } from '@lib'; +import CommandMissingPermissionsListener from './commandMissingPermissions'; export default class SlashMissingPermissionsListener extends BushListener { public constructor() { @@ -9,41 +10,9 @@ export default class SlashMissingPermissionsListener extends BushListener { }); } - public async exec(...[message, command, type, missing]: BushCommandHandlerEvents['slashMissingPermissions']): Promise<void> { - const niceMissing = []; - missing.forEach((missing) => { - if (client.consts.mappings.permissions[missing]) { - niceMissing.push(client.consts.mappings.permissions[missing].name); - } else { - niceMissing.push(missing); - } - }); - - const discordFormat = util.oxford(util.surroundArray(niceMissing, '`'), 'and', ''); - const consoleFormat = util.oxford(util.surroundArray(niceMissing, '<<', '>>'), 'and', ''); - void client.console.info( - 'CommandMissingPermissions', - `<<${message.author.tag}>> tried to run <<${ - command?.id - }>> but could not because <<${type}>> is missing the ${consoleFormat} permissions${missing.length ? 's' : ''}.`, - true - ); - if (type == 'client') { - await message.util - .reply( - `${util.emojis.error} I am missing the ${discordFormat} permission${missing.length ? 's' : ''} required for the \`${ - command?.id - }\` command.` - ) - .catch(() => {}); - } else if (type == 'user') { - await message.util - .reply( - `${util.emojis.error} You are missing the ${discordFormat} permission${ - missing.length ? 's' : '' - } required for the \`${command?.id}\` command.` - ) - .catch(() => {}); - } + public async exec( + ...[message, command, type, missing]: BushCommandHandlerEvents['slashMissingPermissions'] + ): Promise<unknown> { + return await CommandMissingPermissionsListener.handleMissing(message, command, type, missing); } } diff --git a/src/listeners/message/automodCreate.ts b/src/listeners/message/automodCreate.ts index b6718fc..0321aca 100644 --- a/src/listeners/message/automodCreate.ts +++ b/src/listeners/message/automodCreate.ts @@ -1,5 +1,8 @@ import { BushListener, BushMessage } from '@lib'; -import { ClientEvents } from 'discord.js'; +import { MessageEmbed, TextChannel } from 'discord.js'; +import _badLinks from '../../lib/badlinks.json'; // Stolen from https://github.com/nacrt/SkyblockClient-REPO/blob/main/files/scamlinks.json +import badWords from '../../lib/badwords.json'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; export default class AutomodMessageCreateListener extends BushListener { public constructor() { @@ -10,7 +13,93 @@ export default class AutomodMessageCreateListener extends BushListener { }); } - async exec(...[message]: ClientEvents['messageCreate']): Promise<unknown> { - return await util.automod(message as BushMessage); + async exec(...[message]: BushClientEvents['messageCreate']): Promise<unknown> { + return await AutomodMessageCreateListener.automod(message); + } + + public static async automod(message: BushMessage): Promise<unknown> { + if (message.guild.id !== client.consts.mappings.guilds.bush) return; // just temporary + /* await message.guild.getSetting('autoModPhases'); */ + const badLinks = {}; + _badLinks.forEach((link) => { + badLinks[link] = 3; + }); + + // client.console.debug(badLinks, 1); + // client.console.debug(badWords, 1); + + const wordArray = [...Object.keys(badWords), ...Object.keys(badLinks)]; + const offences: { [key: string]: number } = {}; + + // client.console.debug(wordArray); + wordArray.forEach((word) => { + const cleanMessageContent = message.content?.toLowerCase().replace(/ /g, ''); + const cleanWord = word.toLowerCase().replace(/ /g, ''); + + // client.console.debug(cleanMessageContent); + // client.console.debug(cleanWord); + if (cleanMessageContent.includes(cleanWord)) { + if (offences[word]) offences[word] = wordArray[word]; + } + }); + if (!Object.keys(offences)?.length) return; + + const highestOffence = Object.values(offences).sort((a, b) => b - a)[0]; + + switch (highestOffence) { + case 0: { + if (message.deletable) void message.delete(); + break; + } + case 1: { + if (message.deletable) void message.delete(); + void message.member.warn({ + moderator: message.guild.me, + reason: 'Saying a blacklisted word.' + }); + break; + } + case 2: { + if (message.deletable) void message.delete(); + void message.member.mute({ + moderator: message.guild.me, + reason: 'Saying a blacklisted word.', + duration: 900_000 // 15 minutes + }); + break; + } + case 3: { + if (message.deletable) void message.delete(); + void message.member.mute({ + moderator: message.guild.me, + reason: 'Saying a blacklisted word.', + duration: 0 // perm + }); + break; + } + } + + const color = + highestOffence === 0 + ? util.colors.lightGray + : highestOffence === 1 + ? util.colors.yellow + : highestOffence === 2 + ? util.colors.orange + : util.colors.red; + void (message.guild.channels.cache.get('783088333055066212') as TextChannel).send({ + embeds: [ + new MessageEmbed() + .setTitle(`[Severity ${highestOffence}] Automod Action Performed`) + .setDescription( + `**User:** ${message.author} (${message.author.tag})\n**Blacklisted Words:** ${util + .surroundArray(Object.keys(offences), '`') + .join()}` + ) + .addField('Message Content', `${util.codeblock(message.content, 1024)}`) + .setColor(color) + .setTimestamp() + ] + }); } } diff --git a/src/listeners/message/automodUpdate.ts b/src/listeners/message/automodUpdate.ts index e455a3d..7b9e01a 100644 --- a/src/listeners/message/automodUpdate.ts +++ b/src/listeners/message/automodUpdate.ts @@ -1,5 +1,6 @@ import { BushListener, BushMessage } from '@lib'; -import { ClientEvents, Message } from 'discord.js'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; +import AutomodMessageCreateListener from './automodCreate'; export default class AutomodMessageUpdateListener extends BushListener { public constructor() { @@ -10,8 +11,9 @@ export default class AutomodMessageUpdateListener extends BushListener { }); } - async exec(...[message]: ClientEvents['messageUpdate']): Promise<unknown> { - const fullMessage = message.partial ? await message.fetch() : (message as Message); - return await util.automod(fullMessage as BushMessage); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async exec(...[_, newMessage]: BushClientEvents['messageUpdate']): Promise<unknown> { + const fullMessage = newMessage.partial ? await newMessage.fetch() : (newMessage as BushMessage); + return await AutomodMessageCreateListener.automod(fullMessage); } } diff --git a/src/listeners/other/consoleListener.ts b/src/listeners/other/consoleListener.ts index 4e72ec9..6d548ba 100644 --- a/src/listeners/other/consoleListener.ts +++ b/src/listeners/other/consoleListener.ts @@ -34,8 +34,7 @@ export default class ConsoleListener extends BushListener { ReactionCollector, Util, Collection - } = await import('discord.js'), - { Canvas } = await import('node-canvas'); + } = await import('discord.js'); try { const input = line.replace('eval ', '').replace('ev ', ''); let output = eval(input); |