aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/config/prefix.ts1
-rw-r--r--src/commands/dev/setLevel.ts6
-rw-r--r--src/commands/info/help.ts120
-rw-r--r--src/commands/info/ping.ts2
-rw-r--r--src/commands/moderation/role.ts11
-rw-r--r--src/lib/extensions/BushClient.ts14
-rw-r--r--src/lib/extensions/BushClientUtil.ts125
-rw-r--r--src/lib/extensions/BushCommand.ts6
-rw-r--r--src/lib/extensions/BushCommandUtil.ts10
-rw-r--r--src/lib/extensions/BushMessage.ts2
-rw-r--r--src/lib/models/Global.ts50
-rw-r--r--src/lib/models/Guild.ts16
-rw-r--r--src/lib/models/Level.ts2
-rw-r--r--src/lib/models/Modlog.ts2
-rw-r--r--src/lib/models/StickyRole.ts40
-rw-r--r--src/lib/models/index.ts6
-rw-r--r--src/lib/utils/BushConstants.ts31
-rw-r--r--src/lib/utils/BushLogger.ts8
-rw-r--r--src/lib/utils/CanvasProgressBar.ts78
-rw-r--r--src/listeners/commands/commandBlocked.ts66
-rw-r--r--src/listeners/commands/commandError.ts5
-rw-r--r--src/listeners/commands/commandStarted.ts2
-rw-r--r--src/listeners/commands/slashBlocked.ts81
-rw-r--r--src/listeners/commands/slashCommandError.ts73
-rw-r--r--src/listeners/commands/slashStarted.ts21
25 files changed, 570 insertions, 208 deletions
diff --git a/src/commands/config/prefix.ts b/src/commands/config/prefix.ts
index 9ddf81b..3bb717b 100644
--- a/src/commands/config/prefix.ts
+++ b/src/commands/config/prefix.ts
@@ -39,6 +39,7 @@ export default class PrefixCommand extends BushCommand {
id: guild.id
});
}
+ // this.client.console.debug(row);
if (prefix) {
row.prefix = prefix;
await row.save();
diff --git a/src/commands/dev/setLevel.ts b/src/commands/dev/setLevel.ts
index 5a702e9..58c01dd 100644
--- a/src/commands/dev/setLevel.ts
+++ b/src/commands/dev/setLevel.ts
@@ -21,15 +21,15 @@ export default class SetLevelCommand extends BushCommand {
type: 'user',
prompt: {
start: 'What user would you like to change the level of?',
- retry: 'Invalid user. What user would you like to change the level of?'
+ retry: '{error} Choose a valid user to change the level of.'
}
},
{
id: 'level',
type: 'number',
prompt: {
- start: 'What level would you like to set?',
- retry: 'Invalid user. What level would you like to set?'
+ start: 'What level would you like to set the user to?',
+ retry: '{error} Choose a valid level to set the user to.'
}
}
],
diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts
index 9caae24..2ffac1b 100644
--- a/src/commands/info/help.ts
+++ b/src/commands/info/help.ts
@@ -1,4 +1,4 @@
-import { MessageEmbed } from 'discord.js';
+import { MessageActionRow, MessageButton, MessageEmbed } from 'discord.js';
import { BushCommand } from '../../lib/extensions/BushCommand';
import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage';
import { BushMessage } from '../../lib/extensions/BushMessage';
@@ -9,22 +9,33 @@ export default class HelpCommand extends BushCommand {
aliases: ['help'],
category: 'info',
description: {
- content: 'Shows the commands of the bot',
- usage: 'help',
- examples: ['help']
+ content: 'Displays a list of commands, or detailed information for a specific command.',
+ usage: 'help [command]',
+ examples: ['help prefix']
},
clientPermissions: ['EMBED_LINKS'],
args: [
{
id: 'command',
- type: 'commandAlias'
+ type: 'commandAlias',
+ match: 'content',
+ prompt: {
+ start: 'What command do you need help with?',
+ retry: '{error} Choose a valid command to find help for.',
+ optional: true
+ }
+ },
+ {
+ id: 'showHidden',
+ match: 'flag',
+ flag: '--hidden'
}
],
slashOptions: [
{
type: 'STRING',
name: 'command',
- description: 'The command to get help for',
+ description: `The command you would like to find information about.`,
required: false
}
],
@@ -32,46 +43,73 @@ export default class HelpCommand extends BushCommand {
});
}
- private generateEmbed(command?: BushCommand, message?: BushInteractionMessage | BushMessage): MessageEmbed {
+ public async exec(
+ message: BushMessage | BushInteractionMessage,
+ args: { command: BushCommand | string; showHidden?: boolean }
+ ): Promise<unknown> {
const prefix = this.client.config.dev ? 'dev ' : message.util.parsed.prefix;
+ let ButtonRow: MessageActionRow;
+ if (!this.client.config.dev) {
+ ButtonRow = new MessageActionRow().addComponents(
+ new MessageButton({
+ style: 'LINK',
+ label: 'Invite Me',
+ url: `https://discord.com/api/oauth2/authorize?client_id=${this.client.user.id}&permissions=2147483647&scope=bot%20applications.commands`
+ })
+ );
+ }
+ const isOwner = this.client.isOwner(message.author);
+ const isSuperUser = this.client.isSuperUser(message.author);
+ const command = args.command
+ ? typeof args.command === 'string'
+ ? this.client.commandHandler.modules.get(args.command) || null
+ : args.command
+ : null;
+ if (!isOwner) args.showHidden = false;
if (!command) {
- const embed = new MessageEmbed()
- .setFooter(`For more information about a command use '${prefix}help <command>'`)
- .setTimestamp()
- .setColor(this.client.util.colors.default);
- for (const category of this.handler.categories.values()) {
- embed.addField(
- `${category.id.replace(/(\b\w)/gi, (lc): string => lc.toUpperCase())}`,
- `${category
- .filter((cmd): boolean => cmd.aliases.length > 0)
- .map((cmd): string => `\`${cmd.aliases[0]}\``)
- .join(' ')}`
- );
+ const embed = new MessageEmbed().setColor(this.client.util.colors.default).setTimestamp();
+ if (message.guild) {
+ embed.setFooter(`For more information about a command use '${prefix}help <command>'`);
+ }
+ for (const [, category] of this.handler.categories) {
+ const categoryFilter = category.filter((command) => {
+ if (command.hidden && !args.showHidden) return false;
+ if (command.channel == 'guild' && !message.guild && !args.showHidden) return false;
+ if (command.ownerOnly && !isOwner) return false;
+ if (command.superUserOnly && !isSuperUser) {
+ return false;
+ }
+ if (command.restrictedGuilds?.includes(message.guild.id) == !true && !args.showHidden) return false;
+ return true;
+ });
+ const categoryNice = category.id
+ .replace(/(\b\w)/gi, (lc): string => lc.toUpperCase())
+ .replace(/'(S)/g, (letter): string => letter.toLowerCase());
+ const categoryCommands = categoryFilter
+ .filter((cmd): boolean => cmd.aliases.length > 0)
+ .map((cmd): string => `\`${cmd.aliases[0]}\``);
+ if (categoryCommands.length > 0) {
+ embed.addField(`${categoryNice}`, `${categoryCommands.join(' ')}`);
+ }
}
- return embed;
- } else {
- const embed = new MessageEmbed()
- .setColor([155, 200, 200])
- .setTitle(`\`${command.description.usage ? command.description.usage : ''}\``)
- .addField(
- 'Description',
- `${command.description.content ? command.description.content : ''} ${command.ownerOnly ? '\n__Owner Only__' : ''}`
- );
+ return await message.util.reply({ embeds: [embed], components: ButtonRow ? [ButtonRow] : undefined });
+ }
+
+ const embed = new MessageEmbed()
+ .setColor(this.client.util.colors.default)
+ .setTitle(`\`${command.description?.usage ? command.description.usage : 'This command does not have usages.'}\``)
+ .addField(
+ 'Description',
+ `${command.description?.content ? command.description.content : '*This command does not have a description.*'} ${
+ command.ownerOnly ? '\n__Dev Only__' : ''
+ } ${command.superUserOnly ? '\n__Super User Only__' : ''}`
+ );
- if (command.aliases.length > 1) embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true);
- if (command.description.examples && command.description.examples.length)
- embed.addField('Examples', `\`${command.description.examples.join('`\n`')}\``, true);
- return embed;
+ if (command.aliases?.length > 1) embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true);
+ if (command.description?.examples && command.description.examples.length) {
+ embed.addField('Examples', `\`${command.description.examples.join('`\n`')}\``, true);
}
- }
- public async exec(
- message: BushMessage | BushInteractionMessage,
- { command }: { command: BushCommand | string }
- ): Promise<void> {
- const parsedCommand = message.util.isSlash
- ? (this.handler.findCommand(command as string) as BushCommand)
- : (command as BushCommand);
- await message.util.send({ embeds: [this.generateEmbed(parsedCommand, message)] });
+ return await message.util.reply({ embeds: [embed], components: ButtonRow ? [ButtonRow] : undefined });
}
}
diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts
index 5552573..fb93c50 100644
--- a/src/commands/info/ping.ts
+++ b/src/commands/info/ping.ts
@@ -26,6 +26,7 @@ export default class PingCommand extends BushCommand {
.addField('Bot Latency', botLatency, true)
.addField('API Latency', apiLatency, true)
.setFooter(message.author.username, message.author.displayAvatarURL({ dynamic: true }))
+ .setColor(this.client.util.colors.default)
.setTimestamp();
await sentMessage.edit({
content: null,
@@ -44,6 +45,7 @@ export default class PingCommand extends BushCommand {
.addField('Bot Latency', botLatency, true)
.addField('API Latency', apiLatency, true)
.setFooter(message.interaction.user.username, message.interaction.user.displayAvatarURL({ dynamic: true }))
+ .setColor(this.client.util.colors.default)
.setTimestamp();
await message.interaction.editReply({
content: null,
diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts
index 1b82245..f0918f0 100644
--- a/src/commands/moderation/role.ts
+++ b/src/commands/moderation/role.ts
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-empty-function */
-import { ApplicationCommandOptionType } from 'discord-api-types';
import { GuildMember, Message, Role } from 'discord.js';
import { BushCommand } from '../../lib/extensions/BushCommand';
import AllowedMentions from '../../lib/utils/AllowedMentions';
@@ -56,15 +55,15 @@ export default class RoleCommand extends BushCommand {
}
}
],
- slashCommandOptions: [
+ slashOptions: [
{
- type: ApplicationCommandOptionType.USER,
+ type: 'USER',
name: 'user',
description: 'The user to add/remove the role on',
required: true
},
{
- type: ApplicationCommandOptionType.ROLE,
+ type: 'ROLE',
name: 'role',
description: 'The role to add/remove',
required: true
@@ -75,7 +74,7 @@ export default class RoleCommand extends BushCommand {
public async exec(message: Message, { user, role }: { user: GuildMember; role: Role }): Promise<unknown> {
if (!message.member.permissions.has('MANAGE_ROLES') && !this.client.ownerID.includes(message.author.id)) {
- const mappedRole = this.client.util.moulberryBushRoleMap.find((m) => m.id === role.id);
+ const mappedRole = this.client.consts.moulberryBushRoleMap.find((m) => m.id === role.id);
if (!mappedRole || !this.roleWhitelist[mappedRole.name]) {
return message.util.reply({
content: `${this.client.util.emojis.error} <@&${role.id}> is not whitelisted, and you do not have manage roles permission.`,
@@ -83,7 +82,7 @@ export default class RoleCommand extends BushCommand {
});
}
const allowedRoles = this.roleWhitelist[mappedRole.name].map((r) => {
- return this.client.util.moulberryBushRoleMap.find((m) => m.name === r).id;
+ return this.client.consts.moulberryBushRoleMap.find((m) => m.name === r).id;
});
if (!message.member.roles.cache.some((role) => allowedRoles.includes(role.id))) {
return message.util.reply({
diff --git a/src/lib/extensions/BushClient.ts b/src/lib/extensions/BushClient.ts
index 8592281..ad22cfe 100644
--- a/src/lib/extensions/BushClient.ts
+++ b/src/lib/extensions/BushClient.ts
@@ -9,6 +9,7 @@ import * as config from '../../config/options';
import * as Models from '../models';
import AllowedMentions from '../utils/AllowedMentions';
import { BushCache } from '../utils/BushCache';
+import { BushConstants } from '../utils/BushConstants';
import { BushLogger } from '../utils/BushLogger';
import { BushClientUtil } from './BushClientUtil';
import { BushCommandHandler } from './BushCommandHandler';
@@ -34,6 +35,7 @@ export class BushClient extends AkairoClient {
public declare ownerID: Snowflake[];
public db: Sequelize;
public logger: BushLogger;
+ public constants = BushConstants;
constructor(config: BotConfig) {
super(
{
@@ -107,7 +109,7 @@ export class BushClient extends AkairoClient {
dialect: 'postgres',
host: this.config.db.host,
port: this.config.db.port,
- logging: false
+ logging: (a, b) => this.logger.debug(a)
});
this.logger = new BushLogger(this);
}
@@ -116,6 +118,10 @@ export class BushClient extends AkairoClient {
return this.logger;
}
+ get consts(): typeof BushConstants {
+ return this.constants;
+ }
+
// Initialize everything
private async _init(): Promise<void> {
this.commandHandler.useListenerHandler(this.listenerHandler);
@@ -152,17 +158,20 @@ export class BushClient extends AkairoClient {
public async dbPreInit(): Promise<void> {
try {
await this.db.authenticate();
+ Models.Global.initModel(this.db);
Models.Guild.initModel(this.db, this);
Models.Modlog.initModel(this.db);
Models.Ban.initModel(this.db);
Models.Level.initModel(this.db);
+ Models.StickyRole.initModel(this.db);
await this.db.sync(); // Sync all tables to fix everything if updated
this.console.success('Startup', `Successfully connected to <<database>>.`, false);
} catch (error) {
- this.console.error('Startup', `Failed to connect to <<database>> with error:\n` + error, false);
+ this.console.error('Startup', `Failed to connect to <<database>> with error:\n` + error?.stack, false);
}
}
+ /** Starts the bot */
public async start(): Promise<void> {
try {
await this._init();
@@ -173,6 +182,7 @@ export class BushClient extends AkairoClient {
}
}
+ /** Logs out, terminates the connection to Discord, and destroys the client. */
public destroy(relogin = false): void | Promise<string> {
super.destroy();
if (relogin) {
diff --git a/src/lib/extensions/BushClientUtil.ts b/src/lib/extensions/BushClientUtil.ts
index 2ecdc42..6687cb0 100644
--- a/src/lib/extensions/BushClientUtil.ts
+++ b/src/lib/extensions/BushClientUtil.ts
@@ -267,34 +267,6 @@ export class BushClientUtil extends ClientUtil {
return apiRes.uuid.replace(/-/g, '');
}
- public moulberryBushRoleMap = [
- { 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' }
- ];
-
/** Paginates an array of embeds using buttons. */
public async buttonPaginate(
message: BushMessage,
@@ -487,83 +459,26 @@ export class BushClientUtil extends ClientUtil {
public getConfigChannel(channel: 'log' | 'error' | 'dm'): Promise<TextChannel> {
return this.client.channels.fetch(this.client.config.channels[channel]) as Promise<TextChannel>;
}
-}
-
-// I just copy pasted this code from stackoverflow don't yell at me if there is issues for it
-export class CanvasProgressBar {
- private x: number;
- private y: number;
- private w: number;
- private h: number;
- private color: string;
- private percentage: number;
- private p: number;
- private ctx: CanvasRenderingContext2D;
-
- constructor(
- ctx: CanvasRenderingContext2D,
- dimension: { x: number; y: number; width: number; height: number },
- color: string,
- percentage: number
- ) {
- ({ x: this.x, y: this.y, width: this.w, height: this.h } = dimension);
- this.color = color;
- this.percentage = percentage;
- this.p;
- this.ctx = ctx;
- }
- draw(): void {
- // -----------------
- this.p = this.percentage * this.w;
- if (this.p <= this.h) {
- this.ctx.beginPath();
- this.ctx.arc(
- this.h / 2 + this.x,
- this.h / 2 + this.y,
- this.h / 2,
- Math.PI - Math.acos((this.h - this.p) / this.h),
- Math.PI + Math.acos((this.h - this.p) / this.h)
- );
- this.ctx.save();
- this.ctx.scale(-1, 1);
- this.ctx.arc(
- this.h / 2 - this.p - this.x,
- this.h / 2 + this.y,
- this.h / 2,
- Math.PI - Math.acos((this.h - this.p) / this.h),
- Math.PI + Math.acos((this.h - this.p) / this.h)
- );
- this.ctx.restore();
- this.ctx.closePath();
- } else {
- this.ctx.beginPath();
- this.ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI / 2, (3 / 2) * Math.PI);
- this.ctx.lineTo(this.p - this.h + this.x, 0 + this.y);
- this.ctx.arc(this.p - this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, (3 / 2) * Math.PI, Math.PI / 2);
- this.ctx.lineTo(this.h / 2 + this.x, this.h + this.y);
- this.ctx.closePath();
- }
- this.ctx.fillStyle = this.color;
- this.ctx.fill();
- }
-
- // showWholeProgressBar(){
- // this.ctx.beginPath();
- // this.ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI / 2, 3 / 2 * Math.PI);
- // this.ctx.lineTo(this.w - this.h + this.x, 0 + this.y);
- // this.ctx.arc(this.w - this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, 3 / 2 *Math.PI, Math.PI / 2);
- // this.ctx.lineTo(this.h / 2 + this.x, this.h + this.y);
- // this.ctx.strokeStyle = '#000000';
- // this.ctx.stroke();
- // this.ctx.closePath();
- // }
-
- get PPercentage(): number {
- return this.percentage * 100;
- }
-
- set PPercentage(x: number) {
- this.percentage = x / 100;
+ /**
+ * Takes an array and combines the elements using the supplied conjunction.
+ *
+ * @param {string[]} array The array to combine.
+ * @param {string} conjunction The conjunction to use.
+ * @param {string} ifEmpty What to return if the array is empty.
+ * @returns The combined elements or `ifEmpty`
+ *
+ * @example
+ * const permissions = oxford(['ADMINISTRATOR', 'SEND_MESSAGES', 'MANAGE_MESSAGES'], 'and', 'none');
+ * console.log(permissions); // ADMINISTRATOR, SEND_MESSAGES and MANAGE_MESSAGES
+ */
+ public oxford(array: string[], conjunction: string, ifEmpty: string): string {
+ 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(', ');
}
}
diff --git a/src/lib/extensions/BushCommand.ts b/src/lib/extensions/BushCommand.ts
index edd3c31..8358c46 100644
--- a/src/lib/extensions/BushCommand.ts
+++ b/src/lib/extensions/BushCommand.ts
@@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Command, CommandOptions } from 'discord-akairo';
-import { APIApplicationCommandOption } from 'discord-api-types';
import { Snowflake } from 'discord.js';
import { BushClient } from './BushClient';
+import { BushCommandHandler } from './BushCommandHandler';
import { BushInteractionMessage } from './BushInteractionMessage';
import { BushMessage } from './BushMessage';
@@ -11,7 +11,6 @@ export interface BushCommandOptions extends CommandOptions {
hidden?: boolean;
restrictedChannels?: Snowflake[];
restrictedGuilds?: Snowflake[];
- slashCommandOptions?: APIApplicationCommandOption[];
description: {
content: string;
usage: string;
@@ -21,6 +20,7 @@ export interface BushCommandOptions extends CommandOptions {
export class BushCommand extends Command {
public declare client: BushClient;
+ public declare handler: BushCommandHandler;
public options: BushCommandOptions;
/** The channels the command is limited to run in. */
public restrictedChannels: Snowflake[];
@@ -37,9 +37,7 @@ export class BushCommand extends Command {
}
public exec(message: BushMessage, args: any): any;
- // @ts-ignore: They are close enough
public exec(message: BushMessage | BushInteractionMessage, args: any): any {
- // @ts-ignore: They are close enough
super.exec(message, args);
}
}
diff --git a/src/lib/extensions/BushCommandUtil.ts b/src/lib/extensions/BushCommandUtil.ts
new file mode 100644
index 0000000..b4084bd
--- /dev/null
+++ b/src/lib/extensions/BushCommandUtil.ts
@@ -0,0 +1,10 @@
+import { CommandUtil, ParsedComponentData } from 'discord-akairo';
+import { BushCommand } from './BushCommand';
+
+export interface BushParsedComponentData extends ParsedComponentData {
+ command?: BushCommand;
+}
+
+export class BushCommandUtil extends CommandUtil {
+ declare parsed?: BushParsedComponentData;
+}
diff --git a/src/lib/extensions/BushMessage.ts b/src/lib/extensions/BushMessage.ts
index e7146f6..afa6bde 100644
--- a/src/lib/extensions/BushMessage.ts
+++ b/src/lib/extensions/BushMessage.ts
@@ -1,8 +1,10 @@
import { DMChannel, Message, NewsChannel, TextChannel } from 'discord.js';
import { BushClient } from './BushClient';
+import { BushCommandUtil } from './BushCommandUtil';
export class BushMessage extends Message {
declare client: BushClient;
+ declare util: BushCommandUtil;
constructor(client: BushClient, data: unknown, channel: TextChannel | DMChannel | NewsChannel) {
super(client, data, channel);
this.client = client;
diff --git a/src/lib/models/Global.ts b/src/lib/models/Global.ts
new file mode 100644
index 0000000..65f51c4
--- /dev/null
+++ b/src/lib/models/Global.ts
@@ -0,0 +1,50 @@
+import { Snowflake } from 'discord.js';
+import { DataTypes, Optional, Sequelize } from 'sequelize';
+import { BaseModel } from './BaseModel';
+
+export interface GlobalModel {
+ superUsers: Snowflake[];
+ disabledCommands: string[];
+ blacklistedUsers: Snowflake[];
+ blacklistedGuilds: Snowflake[];
+ blacklistedChannels: Snowflake[];
+}
+export type GlobalModelCreationAttributes = Optional<
+ GlobalModel,
+ 'superUsers' | 'disabledCommands' | 'blacklistedUsers' | 'blacklistedGuilds' | 'blacklistedChannels'
+>;
+
+export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes> implements GlobalModel {
+ superUsers: Snowflake[];
+ disabledCommands: string[];
+ blacklistedUsers: Snowflake[];
+ blacklistedGuilds: Snowflake[];
+ blacklistedChannels: Snowflake[];
+ static initModel(sequelize: Sequelize): void {
+ Global.init(
+ {
+ superUsers: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
+ },
+ disabledCommands: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
+ },
+ blacklistedUsers: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
+ },
+ blacklistedGuilds: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
+ },
+ blacklistedChannels: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
+ }
+ },
+ { sequelize }
+ );
+ }
+}
diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts
index 7902461..bc93951 100644
--- a/src/lib/models/Guild.ts
+++ b/src/lib/models/Guild.ts
@@ -1,3 +1,4 @@
+import { Snowflake } from 'discord.js';
import { DataTypes, Optional, Sequelize } from 'sequelize';
import { BushClient } from '../extensions/BushClient';
import { BaseModel } from './BaseModel';
@@ -5,12 +6,17 @@ import { BaseModel } from './BaseModel';
export interface GuildModel {
id: string;
prefix: string;
+ autoPublishChannels: string[];
+ blacklistedChannels: Snowflake[];
}
-export type GuildModelCreationAttributes = Optional<GuildModel, 'prefix'>;
+
+export type GuildModelCreationAttributes = Optional<GuildModel, 'prefix' | 'autoPublishChannels' | 'blacklistedChannels'>;
export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> implements GuildModel {
id: string;
prefix: string;
+ autoPublishChannels: string[];
+ blacklistedChannels: Snowflake[];
static initModel(seqeulize: Sequelize, client: BushClient): void {
Guild.init(
{
@@ -22,6 +28,14 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
type: DataTypes.STRING,
allowNull: false,
defaultValue: client.config.prefix
+ },
+ autoPublishChannels: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
+ },
+ blacklistedChannels: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: true
}
},
{ sequelize: seqeulize }
diff --git a/src/lib/models/Level.ts b/src/lib/models/Level.ts
index 426ec1a..6113627 100644
--- a/src/lib/models/Level.ts
+++ b/src/lib/models/Level.ts
@@ -32,7 +32,7 @@ export class Level extends BaseModel<LevelModel, LevelModelCreationAttributes> {
defaultValue: 0
}
},
- { sequelize: sequelize }
+ { sequelize }
);
}
static convertXpToLevel(xp: number): number {
diff --git a/src/lib/models/Modlog.ts b/src/lib/models/Modlog.ts
index 5a2cb69..15c5030 100644
--- a/src/lib/models/Modlog.ts
+++ b/src/lib/models/Modlog.ts
@@ -77,7 +77,7 @@ export class Modlog extends BaseModel<ModlogModel, ModlogModelCreationAttributes
// }
}
},
- { sequelize: sequelize }
+ { sequelize }
);
}
}
diff --git a/src/lib/models/StickyRole.ts b/src/lib/models/StickyRole.ts
new file mode 100644
index 0000000..597d7c5
--- /dev/null
+++ b/src/lib/models/StickyRole.ts
@@ -0,0 +1,40 @@
+import { Snowflake } from 'discord.js';
+import { DataTypes, Sequelize } from 'sequelize';
+import { BaseModel } from './BaseModel';
+
+export interface StickyRoleModel {
+ user: Snowflake;
+ guild: Snowflake;
+ roles: Snowflake[];
+}
+export interface StickyRoleModelCreationAttributes {
+ user: Snowflake;
+ guild: Snowflake;
+ roles: Snowflake[];
+}
+
+export class StickyRole extends BaseModel<StickyRoleModel, StickyRoleModelCreationAttributes> implements StickyRoleModel {
+ user: Snowflake;
+ guild: Snowflake;
+ roles: Snowflake[];
+ static initModel(sequelize: Sequelize): void {
+ StickyRole.init(
+ {
+ user: {
+ type: DataTypes.STRING,
+ allowNull: false
+ },
+ guild: {
+ type: DataTypes.STRING,
+ allowNull: false
+ },
+
+ roles: {
+ type: DataTypes.ARRAY(DataTypes.STRING),
+ allowNull: false
+ }
+ },
+ { sequelize }
+ );
+ }
+}
diff --git a/src/lib/models/index.ts b/src/lib/models/index.ts
index 8eb817e..e38ad69 100644
--- a/src/lib/models/index.ts
+++ b/src/lib/models/index.ts
@@ -1,5 +1,7 @@
+export * from './Ban';
export * from './BaseModel';
+export * from './Global';
export * from './Guild';
-export * from './Ban';
-export * from './Modlog';
export * from './Level';
+export * from './Modlog';
+export * from './StickyRole';
diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts
index 701f818..1015229 100644
--- a/src/lib/utils/BushConstants.ts
+++ b/src/lib/utils/BushConstants.ts
@@ -311,7 +311,8 @@ export class BushConstants {
GUILD: 'guild',
DM: 'dm',
AUTHOR_NOT_FOUND: 'authorNotFound',
- DISABLED: 'disabled',
+ DISABLED_GUILD: 'disabledGuild',
+ DISABLED_GLOBAL: 'disabledGlobal',
ROLE_BLACKLIST: 'roleBlacklist',
USER_BLACKLIST: 'userBlacklist',
RESTRICTED_GUILD: 'restrictedGuild',
@@ -340,4 +341,32 @@ export class BushConstants {
SLASH_NOT_FOUND: 'slashNotFound',
SLASH_MISSING_PERMISSIONS: 'slashMissingPermissions'
};
+
+ public static moulberryBushRoleMap = [
+ { 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' }
+ ];
}
diff --git a/src/lib/utils/BushLogger.ts b/src/lib/utils/BushLogger.ts
index d48ec07..2225bde 100644
--- a/src/lib/utils/BushLogger.ts
+++ b/src/lib/utils/BushLogger.ts
@@ -85,7 +85,7 @@ export class BushLogger {
*/
public debug(...content: any): void {
if (!this.client.config.dev) return;
- console.log(`${chalk.bgGrey(this.getTimeStamp())} ${chalk.grey('[Debug]')}`, ...content);
+ console.log(`${chalk.bgMagenta(this.getTimeStamp())} ${chalk.magenta('[Debug]')}`, ...content);
}
/**
@@ -102,7 +102,7 @@ export class BushLogger {
);
if (!sendChannel) return;
const embed = new MessageEmbed()
- .setDescription(`**[${header}]** ${this.stripColor(newContent)}`)
+ .setDescription(`**[${header}]** ${this.parseFormatting(this.stripColor(newContent), '', true)}`)
.setColor(this.client.util.colors.gray)
.setTimestamp();
this.channelLog({ embeds: [embed] });
@@ -163,7 +163,7 @@ export class BushLogger {
);
if (!sendChannel) return;
const embed = new MessageEmbed()
- .setDescription(`**[${header}]** ${this.stripColor(newContent)}`)
+ .setDescription(`**[${header}]** ${this.parseFormatting(this.stripColor(newContent), '', true)}`)
.setColor(this.client.util.colors.error)
.setTimestamp();
this.channelError({ embeds: [embed] });
@@ -183,7 +183,7 @@ export class BushLogger {
);
if (!sendChannel) return;
const embed = new MessageEmbed()
- .setDescription(`**[${header}]** ${this.stripColor(newContent)}`)
+ .setDescription(`**[${header}]** ${this.parseFormatting(this.stripColor(newContent), '', true)}`)
.setColor(this.client.util.colors.success)
.setTimestamp();
await this.channelLog({ embeds: [embed] }).catch(() => {});
diff --git a/src/lib/utils/CanvasProgressBar.ts b/src/lib/utils/CanvasProgressBar.ts
new file mode 100644
index 0000000..aa8630a
--- /dev/null
+++ b/src/lib/utils/CanvasProgressBar.ts
@@ -0,0 +1,78 @@
+// I just copy pasted this code from stackoverflow don't yell at me if there is issues for it
+export class CanvasProgressBar {
+ private x: number;
+ private y: number;
+ private w: number;
+ private h: number;
+ private color: string;
+ private percentage: number;
+ private p: number;
+ private ctx: CanvasRenderingContext2D;
+
+ constructor(
+ ctx: CanvasRenderingContext2D,
+ dimension: { x: number; y: number; width: number; height: number },
+ color: string,
+ percentage: number
+ ) {
+ ({ x: this.x, y: this.y, width: this.w, height: this.h } = dimension);
+ this.color = color;
+ this.percentage = percentage;
+ this.p;
+ this.ctx = ctx;
+ }
+
+ draw(): void {
+ // -----------------
+ this.p = this.percentage * this.w;
+ if (this.p <= this.h) {
+ this.ctx.beginPath();
+ this.ctx.arc(
+ this.h / 2 + this.x,
+ this.h / 2 + this.y,
+ this.h / 2,
+ Math.PI - Math.acos((this.h - this.p) / this.h),
+ Math.PI + Math.acos((this.h - this.p) / this.h)
+ );
+ this.ctx.save();
+ this.ctx.scale(-1, 1);
+ this.ctx.arc(
+ this.h / 2 - this.p - this.x,
+ this.h / 2 + this.y,
+ this.h / 2,
+ Math.PI - Math.acos((this.h - this.p) / this.h),
+ Math.PI + Math.acos((this.h - this.p) / this.h)
+ );
+ this.ctx.restore();
+ this.ctx.closePath();
+ } else {
+ this.ctx.beginPath();
+ this.ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI / 2, (3 / 2) * Math.PI);
+ this.ctx.lineTo(this.p - this.h + this.x, 0 + this.y);
+ this.ctx.arc(this.p - this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, (3 / 2) * Math.PI, Math.PI / 2);
+ this.ctx.lineTo(this.h / 2 + this.x, this.h + this.y);
+ this.ctx.closePath();
+ }
+ this.ctx.fillStyle = this.color;
+ this.ctx.fill();
+ }
+
+ // showWholeProgressBar(){
+ // this.ctx.beginPath();
+ // this.ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI / 2, 3 / 2 * Math.PI);
+ // this.ctx.lineTo(this.w - this.h + this.x, 0 + this.y);
+ // this.ctx.arc(this.w - this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, 3 / 2 *Math.PI, Math.PI / 2);
+ // this.ctx.lineTo(this.h / 2 + this.x, this.h + this.y);
+ // this.ctx.strokeStyle = '#000000';
+ // this.ctx.stroke();
+ // this.ctx.closePath();
+ // }
+
+ get PPercentage(): number {
+ return this.percentage * 100;
+ }
+
+ set PPercentage(x: number) {
+ this.percentage = x / 100;
+ }
+}
diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts
index 61433a6..febfc93 100644
--- a/src/listeners/commands/commandBlocked.ts
+++ b/src/listeners/commands/commandBlocked.ts
@@ -1,6 +1,6 @@
-import { Command } from 'discord-akairo';
-import { Message } from 'discord.js';
+import { BushCommand } from '../../lib/extensions/BushCommand';
import { BushListener } from '../../lib/extensions/BushListener';
+import { BushMessage } from '../../lib/extensions/BushMessage';
export default class CommandBlockedListener extends BushListener {
public constructor() {
@@ -10,24 +10,70 @@ export default class CommandBlockedListener extends BushListener {
});
}
- public async exec(message: Message, command: Command, reason: string): Promise<void> {
+ public async exec(message: BushMessage, command: BushCommand, reason: string): Promise<unknown> {
this.client.console.info(
'CommandBlocked',
`<<${message.author.tag}>> tried to run <<${message.util.parsed.command}>> but was blocked because <<${reason}>>.`,
false
);
+ const reasons = this.client.consts.BlockedReasons;
switch (reason) {
- case 'owner': {
- await message.util.send(`You must be an owner to run command \`${message.util.parsed.command}\``);
- break;
+ case reasons.OWNER: {
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} Only my developers can run the \`${message.util.parsed.command}\` command.`
+ });
}
- case 'blacklist': {
- // pass
- break;
+ case reasons.SUPER_USER: {
+ return await message.util.reply({
+ content: `${this.client.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: `${this.client.util.emojis.error} My developers disabled the \`${message.util.parsed.command}\` command.`
+ });
+ }
+ case reasons.DISABLED_GUILD: {
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} The \`${command.aliases[0]}\` command is currently disabled in \`${message.guild.name}\`.`
+ });
+ }
+ case reasons.CHANNEL_BLACKLIST: {
+ return;
+ }
+ case reasons.USER_BLACKLIST: {
+ return;
+ }
+ case reasons.ROLE_BLACKLIST: {
+ return;
+ }
+ case reasons.RESTRICTED_CHANNEL: {
+ const channels = command.restrictedChannels;
+ const names = [];
+ channels.forEach((c) => {
+ names.push(`<#${c}>`);
+ });
+ const pretty = this.client.util.oxford(names, 'and', undefined);
+ return await message.util.reply({
+ content: `${this.client.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(`\`${this.client.guilds.cache.get(g).name}\``);
+ });
+ const pretty = this.client.util.oxford(names, 'and', undefined);
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} \`${command}\` can only be run in ${pretty}.`
+ });
}
default: {
- await message.util.send(`Command blocked with reason \`${reason}\``);
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} Command blocked with reason \`${reason}\``
+ });
}
}
}
diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts
index cb8c5d2..7f765ae 100644
--- a/src/listeners/commands/commandError.ts
+++ b/src/listeners/commands/commandError.ts
@@ -19,7 +19,7 @@ export default class CommandErrorListener extends BushListener {
.setDescription(
stripIndents`**User:** ${message.author} (${message.author.tag})
**Command:** ${command}
- **Channel:** ${message.channel} (${message.channel.id})
+ **Channel:** ${message.channel} (${message.channel?.id})
**Message:** [link](${message.url})`
)
.addField('Error', await this.client.util.codeblock(`${error?.stack}`, 1024, 'js'))
@@ -59,7 +59,8 @@ export default class CommandErrorListener extends BushListener {
this.client.console.error(
'CommandError',
`an error occurred with the <<${command}>> command in <<${channel}>> triggered by <<${message?.author?.tag}>>:\n` +
- error?.stack
+ error?.stack,
+ false
);
}
}
diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts
index 28ed0f8..8c860f8 100644
--- a/src/listeners/commands/commandStarted.ts
+++ b/src/listeners/commands/commandStarted.ts
@@ -4,7 +4,7 @@ import { BushListener } from '../../lib/extensions/BushListener';
export default class CommandStartedListener extends BushListener {
constructor() {
- super('logCommands', {
+ super('commandStarted', {
emitter: 'commandHandler',
event: 'commandStarted'
});
diff --git a/src/listeners/commands/slashBlocked.ts b/src/listeners/commands/slashBlocked.ts
new file mode 100644
index 0000000..d8ef736
--- /dev/null
+++ b/src/listeners/commands/slashBlocked.ts
@@ -0,0 +1,81 @@
+import { BushCommand } from '../../lib/extensions/BushCommand';
+import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage';
+import { BushListener } from '../../lib/extensions/BushListener';
+
+export default class SlashBlockedListener extends BushListener {
+ public constructor() {
+ super('slashBlocked', {
+ emitter: 'commandHandler',
+ event: 'slashBlocked'
+ });
+ }
+
+ public async exec(message: BushInteractionMessage, command: BushCommand, reason: string): Promise<unknown> {
+ this.client.console.info(
+ 'SlashBlocked',
+ `<<${message.author.tag}>> tried to run <<${message.util.parsed.command}>> but was blocked because <<${reason}>>.`,
+ false
+ );
+
+ const reasons = this.client.consts.BlockedReasons;
+
+ switch (reason) {
+ case reasons.OWNER: {
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} Only my developers can run the \`${message.util.parsed.command}\` command.`
+ });
+ }
+ case reasons.SUPER_USER: {
+ return await message.util.reply({
+ content: `${this.client.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: `${this.client.util.emojis.error} My developers disabled the \`${message.util.parsed.command}\` command.`
+ });
+ }
+ case reasons.DISABLED_GUILD: {
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} The \`${command.aliases[0]}\` command is currently disabled in \`${message.guild.name}\`.`
+ });
+ }
+ case reasons.CHANNEL_BLACKLIST: {
+ return;
+ }
+ case reasons.USER_BLACKLIST: {
+ return;
+ }
+ case reasons.ROLE_BLACKLIST: {
+ return;
+ }
+ case reasons.RESTRICTED_CHANNEL: {
+ const channels = command.restrictedChannels;
+ const names = [];
+ channels.forEach((c) => {
+ names.push(`<#${c}>`);
+ });
+ const pretty = this.client.util.oxford(names, 'and', undefined);
+ return await message.util.reply({
+ content: `${this.client.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(`\`${this.client.guilds.cache.get(g).name}\``);
+ });
+ const pretty = this.client.util.oxford(names, 'and', undefined);
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} \`${command}\` can only be run in ${pretty}.`
+ });
+ }
+ default: {
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} Command blocked with reason \`${reason}\``
+ });
+ }
+ }
+ }
+}
diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts
index b8d9b6b..718e992 100644
--- a/src/listeners/commands/slashCommandError.ts
+++ b/src/listeners/commands/slashCommandError.ts
@@ -1,6 +1,7 @@
import { stripIndents } from 'common-tags';
-import { CommandInteraction, MessageEmbed, TextChannel } from 'discord.js';
+import { MessageEmbed } from 'discord.js';
import { BushCommand } from '../../lib/extensions/BushCommand';
+import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage';
import { BushListener } from '../../lib/extensions/BushListener';
export default class SlashCommandErrorListener extends BushListener {
@@ -10,31 +11,55 @@ export default class SlashCommandErrorListener extends BushListener {
event: 'slashError'
});
}
- async exec(error: Error, message: CommandInteraction, command: BushCommand): Promise<void> {
- const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers
- const errorDevEmbed = this.client.util
- .createEmbed(this.client.util.colors.error)
- .setTitle(`Slash Error # \`${errorNumber}\`: An error occurred`)
+ async exec(error: Error, message: BushInteractionMessage, command: BushCommand): 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.user.id}> (${message.user.tag})
- **Slash Command:** ${command}
- **Channel:** <#${message.channelID}> (${message.channelID})
- **Message:** [link](https://discord.com/${message.guildID}/${message.channelID}/${message.id})`
+ stripIndents`**User:** ${message.author} (${message.author.tag})
+ **Slash Command:** ${command}
+ **Channel:** ${message.channel} (${message.channel.id})
+ **Message:** [link](https://discord.com/${message.guild.id}/${message.guild.id}/${message.id})`
)
- .addField('Error', `${await this.client.util.haste(error.stack)}`);
- let errorUserEmbed: MessageEmbed;
- if (command) {
- errorUserEmbed = this.client.util
- .createEmbed(this.client.util.colors.error)
- .setTitle('An error occurred')
- .setDescription(
- stripIndents`Whoops! It appears like something broke.
- The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`.
- `
- );
+ .addField('Error', await this.client.util.codeblock(`${error?.stack}`, 1024, 'js'))
+ .setColor(this.client.util.colors.error)
+ .setTimestamp();
+
+ if (message) {
+ if (!this.client.config.owners.includes(message.author.id)) {
+ const errorUserEmbed: MessageEmbed = new MessageEmbed()
+ .setTitle('An error occurred')
+ .setColor(this.client.util.colors.error)
+ .setTimestamp();
+ await this.client.logger.channelError({ embeds: [errorEmbed] });
+ 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) => {
+ const channel = message.channel.type === 'dm' ? message.channel.recipient.tag : message.channel.name;
+ this.client.console.warn('SlashError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack);
+ });
+ } else {
+ const errorDevEmbed = new MessageEmbed()
+ .setTitle('An error occurred')
+ .setColor(this.client.util.colors.error)
+ .setTimestamp()
+ .setDescription(await this.client.util.codeblock(`${error?.stack}`, 2048, 'js'));
+ await message.util.send({ embeds: [errorDevEmbed] }).catch((e) => {
+ const channel = message.channel.type === 'dm' ? message.channel.recipient.tag : message.channel.name;
+ this.client.console.warn('SlashError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack);
+ });
+ }
}
- const channel = (await this.client.channels.fetch(this.client.config.channels.log)) as TextChannel;
- await channel.send({ embeds: [errorDevEmbed] });
- if (errorUserEmbed) await message.reply({ embeds: [errorUserEmbed] });
+ const channel = message.channel.type === 'dm' ? message.channel.recipient.tag : message.channel.name;
+ this.client.console.error(
+ 'SlashError',
+ `an error occurred with the <<${command}>> command in <<${channel}>> triggered by <<${message?.author?.tag}>>:\n` +
+ error?.stack,
+ false
+ );
}
}
diff --git a/src/listeners/commands/slashStarted.ts b/src/listeners/commands/slashStarted.ts
new file mode 100644
index 0000000..6a45546
--- /dev/null
+++ b/src/listeners/commands/slashStarted.ts
@@ -0,0 +1,21 @@
+import { Message } from 'discord.js';
+import { BushCommand } from '../../lib/extensions/BushCommand';
+import { BushListener } from '../../lib/extensions/BushListener';
+
+export default class SlashStartedListener extends BushListener {
+ constructor() {
+ super('slashStarted', {
+ emitter: 'commandHandler',
+ event: 'slashStarted'
+ });
+ }
+ exec(message: Message, command: BushCommand): void {
+ this.client.logger.info(
+ 'SlashCommand',
+ `The <<${command.id}>> command was used by <<${message.author.tag}>> in ${
+ message.channel.type === 'dm' ? `their <<DMs>>` : `<<#${message.channel.name}>> in <<${message.guild?.name}>>`
+ }.`,
+ false // I don't want to spam the log channel when people use commands
+ );
+ }
+}