aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json4
-rw-r--r--src/commands/dev/eval.ts2
-rw-r--r--src/commands/dev/reload.ts34
-rw-r--r--src/commands/dev/servers.ts2
-rw-r--r--src/commands/moderation/_lockdown.ts (renamed from src/commands/moderation/lockdown.ts)0
-rw-r--r--src/commands/utilities/whoHasRole.ts2
-rw-r--r--src/inhibitors/blacklist/channelGlobalBlacklist.ts2
-rw-r--r--src/inhibitors/blacklist/channelGuildBlacklist.ts2
-rw-r--r--src/inhibitors/blacklist/guildBlacklist.ts2
-rw-r--r--src/inhibitors/blacklist/userGlobalBlacklist.ts2
-rw-r--r--src/inhibitors/blacklist/userGuildBlacklist.ts2
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts85
-rw-r--r--src/lib/extensions/discord.js/BushClientEvents.d.ts3
-rw-r--r--src/lib/extensions/discord.js/BushMessage.ts8
-rw-r--r--src/listeners/commands/commandBlocked.ts48
-rw-r--r--src/listeners/commands/commandError.ts53
-rw-r--r--src/listeners/commands/commandMissingPermissions.ts14
-rw-r--r--src/listeners/commands/slashBlocked.ts66
-rw-r--r--src/listeners/commands/slashCommandError.ts52
-rw-r--r--src/listeners/commands/slashMissingPermissions.ts41
-rw-r--r--src/listeners/message/automodCreate.ts95
-rw-r--r--src/listeners/message/automodUpdate.ts10
-rw-r--r--src/listeners/other/consoleListener.ts3
-rw-r--r--yarn.lock36
24 files changed, 247 insertions, 321 deletions
diff --git a/package.json b/package.json
index 6e218a5..ab53e62 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "bush-bot",
"version": "3.0.0",
- "description": "A custom bot for Moulberry's Bush",
+ "description": "A multipurpose moderation bot developed for Moulberry's Bush.",
"main": "dist/src/bot.js",
"repository": "https://github.com/NotEnoughUpdates/bush-bot-3.0",
"author": "IRONM00N#0001 (@IRONM00N)",
@@ -51,6 +51,7 @@
"dependencies": {
"@discordjs/voice": "^0.4.0",
"body-parser": "^1.19.0",
+ "canvas": "^2.8.0",
"chalk": "^4.1.1",
"common-tags": "^1.8.0",
"discord-akairo": "NotEnoughUpdates/discord-akairo",
@@ -63,7 +64,6 @@
"madge": "^5.0.1",
"module-alias": "^2.2.2",
"moment": "^2.29.1",
- "node-canvas": "^2.7.0",
"node-fetch": "^2.6.1",
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
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);
diff --git a/yarn.lock b/yarn.lock
index 2f86a9a..226bcb7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -801,6 +801,7 @@ __metadata:
"@typescript-eslint/eslint-plugin": ^4.14.1
"@typescript-eslint/parser": ^4.14.1
body-parser: ^1.19.0
+ canvas: ^2.8.0
chalk: ^4.1.1
common-tags: ^1.8.0
discord-akairo: NotEnoughUpdates/discord-akairo
@@ -816,7 +817,6 @@ __metadata:
madge: ^5.0.1
module-alias: ^2.2.2
moment: ^2.29.1
- node-canvas: ^2.7.0
node-fetch: ^2.6.1
pg: ^8.5.1
pg-hstore: ^2.3.3
@@ -891,6 +891,18 @@ __metadata:
languageName: node
linkType: hard
+"canvas@npm:^2.8.0":
+ version: 2.8.0
+ resolution: "canvas@npm:2.8.0"
+ dependencies:
+ "@mapbox/node-pre-gyp": ^1.0.0
+ nan: ^2.14.0
+ node-gyp: latest
+ simple-get: ^3.0.3
+ checksum: 4cc909f63eaf88d22f9164601903745abcc6ccb7f70090b9389dc2cb68cbf139c220dbd75837e6d04602ff122b44a2eb17413bca850f9c6c602f74f1f0f1cc3f
+ languageName: node
+ linkType: hard
+
"chalk@npm:^2.0.0":
version: 2.4.2
resolution: "chalk@npm:2.4.2"
@@ -903,12 +915,12 @@ __metadata:
linkType: hard
"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1":
- version: 4.1.1
- resolution: "chalk@npm:4.1.1"
+ version: 4.1.2
+ resolution: "chalk@npm:4.1.2"
dependencies:
ansi-styles: ^4.1.0
supports-color: ^7.1.0
- checksum: 036e973e665ba1a32c975e291d5f3d549bceeb7b1b983320d4598fb75d70fe20c5db5d62971ec0fe76cdbce83985a00ee42372416abfc3a5584465005a7855ed
+ checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc
languageName: node
linkType: hard
@@ -1323,8 +1335,8 @@ __metadata:
discord-akairo@NotEnoughUpdates/discord-akairo:
version: 8.2.2
- resolution: "discord-akairo@https://github.com/NotEnoughUpdates/discord-akairo.git#commit=b0f6e916b7acde857a2b58690d72f10165c3a537"
- checksum: 0109cb18a4b09bbfc3e509f1ce6edb720a2826f5104b5462889cdd5f95bb4ef2ef79f79e1864b2316a4e316f675c72cb95b7492410de0b452623cc7b2c336118
+ resolution: "discord-akairo@https://github.com/NotEnoughUpdates/discord-akairo.git#commit=ce7f4a08d97bf28af7f35184de750b01b59dc34a"
+ checksum: 89a0c720a14ddb32d588f9f043bf5f38ed86793f2bed51e5173185e20fbb87d334cc3c833e1c7804473d1e81b64e6c36f69df355fffd339544a4fa849b349b12
languageName: node
linkType: hard
@@ -2748,18 +2760,6 @@ discord.js@NotEnoughUpdates/discord.js:
languageName: node
linkType: hard
-"node-canvas@npm:^2.7.0":
- version: 2.7.0
- resolution: "node-canvas@npm:2.7.0"
- dependencies:
- "@mapbox/node-pre-gyp": ^1.0.0
- nan: ^2.14.0
- node-gyp: latest
- simple-get: ^3.0.3
- checksum: b4ffb83e7a95c5fcd51867723a55e7b588f9b15b566d07c0e49fb6eb41462b400705531d456803264d27890fdb9f79f5a46d957fc6f03d7d3ab42b070f5d6966
- languageName: node
- linkType: hard
-
"node-fetch@npm:^2.6.1":
version: 2.6.1
resolution: "node-fetch@npm:2.6.1"