aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commands/admin/channelPermissions.ts2
-rw-r--r--src/commands/admin/roleAll.ts4
-rw-r--r--src/commands/config/_customAutomodPhrases.ts (renamed from src/commands/config/customAutomodPhrases.ts)38
-rw-r--r--src/commands/config/_levelRoles.ts (renamed from src/commands/config/levelRoles.ts)38
-rw-r--r--src/commands/config/blacklist.ts13
-rw-r--r--src/commands/dev/__template.ts4
-rw-r--r--src/commands/dev/dm.ts17
-rw-r--r--src/commands/dev/say.ts2
-rw-r--r--src/commands/dev/superUser.ts7
-rw-r--r--src/commands/moderation/_activePunishments.ts (renamed from src/commands/moderation/activePunishments.ts)0
-rw-r--r--src/commands/moderation/removeReactionEmoji.ts6
-rw-r--r--src/commands/moderation/unmute.ts8
-rw-r--r--src/commands/moderation/warn.ts8
-rw-r--r--src/commands/utilities/remind.ts2
-rw-r--r--src/commands/utilities/reminders.ts2
-rw-r--r--src/commands/utilities/steal.ts4
-rw-r--r--src/commands/utilities/suicide.ts2
-rw-r--r--src/commands/utilities/uuid.ts4
-rw-r--r--src/lib/common/Format.ts107
-rw-r--r--src/lib/common/Moderation.ts293
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts11
-rw-r--r--src/lib/extensions/discord-akairo/BushSlashMessage.ts7
-rw-r--r--src/lib/models/ActivePunishment.ts4
-rw-r--r--src/lib/models/Global.ts4
-rw-r--r--src/lib/models/Guild.ts80
-rw-r--r--src/lib/models/Level.ts4
-rw-r--r--src/lib/models/ModLog.ts4
-rw-r--r--src/lib/models/Reminder.ts4
-rw-r--r--src/lib/models/Stat.ts4
-rw-r--r--src/lib/models/StickyRole.ts4
-rw-r--r--src/lib/models/__helpers.ts4
31 files changed, 158 insertions, 533 deletions
diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts
index 8c7c540..026f1a6 100644
--- a/src/commands/admin/channelPermissions.ts
+++ b/src/commands/admin/channelPermissions.ts
@@ -62,7 +62,7 @@ export default class ChannelPermissionsCommand extends BushCommand {
state: 'true' | 'false' | 'neutral';
}
) {
- if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`);
+ if (!message.inGuild()) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`);
if (!message.member!.permissions.has('ADMINISTRATOR') && !message.member!.user.isOwner())
return await message.util.reply(`${util.emojis.error} You must have admin perms to use this command.`);
if (message.util.isSlashMessage(message)) await message.interaction.deferReply();
diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts
index 5ef379a..6e82011 100644
--- a/src/commands/admin/roleAll.ts
+++ b/src/commands/admin/roleAll.ts
@@ -37,8 +37,8 @@ export default class RoleAllCommand extends BushCommand {
});
}
- public override async exec(message: BushMessage | BushSlashMessage, args: { role: ArgType<'role'>; bots: boolean }) {
- if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`);
+ public override async exec(message: BushMessage | BushSlashMessage, args: { role: ArgType<'role'>; bots: ArgType<'boolean'> }) {
+ if (!message.inGuild()) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`);
if (!message.member!.permissions.has('ADMINISTRATOR') && !message.member!.user.isOwner())
return await message.util.reply(`${util.emojis.error} You must have admin perms to use this command.`);
if (message.util.isSlashMessage(message)) await message.interaction.deferReply();
diff --git a/src/commands/config/customAutomodPhrases.ts b/src/commands/config/_customAutomodPhrases.ts
index cf37595..cd59c9a 100644
--- a/src/commands/config/customAutomodPhrases.ts
+++ b/src/commands/config/_customAutomodPhrases.ts
@@ -12,37 +12,22 @@
// {
// id: 'required_argument',
// type: 'string',
-// prompt: {
-// start: 'What would you like to set your first argument to be?',
-// retry: '{error} Pick a valid argument.',
-// optional: false
-// }
+// description: 'This is the first argument.',
+// prompt: 'What would you like to set your first argument to be?',
+// retry: '{error} Pick a valid argument.',
+// slashType: 'STRING'
// },
// {
// id: 'optional_argument',
// type: 'string',
-// prompt: {
-// start: 'What would you like to set your second argument to be?',
-// retry: '{error} Pick a valid argument.',
-// optional: true
-// }
+// description: 'This is the second argument.',
+// prompt: 'What would you like to set your second argument to be?',
+// retry: '{error} Pick a valid argument.',
+// optional: true,
+// slashType: 'STRING'
// }
// ],
// slash: true,
-// slashOptions: [
-// {
-// name: 'required_argument',
-// description: 'What would you like to set your first argument to be?',
-// type: 'STRING',
-// required: true
-// },
-// {
-// name: 'optional_argument',
-// description: 'What would you like to set your second argument to be?',
-// type: 'STRING',
-// required: false
-// }
-// ],
// channel: 'guild',
// clientPermissions: (m) => util.clientSendAndPermCheck(m),
// userPermissions: ['MANAGE_GUILD']
@@ -52,8 +37,5 @@
// public override async exec(
// message: BushMessage | BushSlashMessage,
// args: { required_argument: string; optional_argument: string }
-// ) {
-// return await message.util.reply(`${util.emojis.error} Do not use the template command.`);
-// args;
-// }
+// ) {}
// }
diff --git a/src/commands/config/levelRoles.ts b/src/commands/config/_levelRoles.ts
index 7f99580..af7e637 100644
--- a/src/commands/config/levelRoles.ts
+++ b/src/commands/config/_levelRoles.ts
@@ -10,39 +10,24 @@
// examples: ['level-role 1 2'],
// args: [
// {
-// id: 'action',
-// customType: ['add', 'remove']
-// },
-// {
-// id: 'role',
-// type: 'role',
+// id: 'required_argument',
+// type: 'string',
+// description: 'This is the first argument.',
// prompt: 'What would you like to set your first argument to be?',
// retry: '{error} Pick a valid argument.',
-// optional: false
+// slashType: 'STRING'
// },
// {
-// id: 'level',
-// type: 'integer',
+// id: 'optional_argument',
+// type: 'string',
+// description: 'This is the second argument.',
// prompt: 'What would you like to set your second argument to be?',
// retry: '{error} Pick a valid argument.',
-// optional: false
+// optional: true,
+// slashType: 'STRING'
// }
// ],
// slash: true,
-// slashOptions: [
-// {
-// name: 'role',
-// description: 'What would you like to set your first argument to be?',
-// type: 'STRING',
-// required: true
-// },
-// {
-// name: 'level',
-// description: 'What would you like to set your second argument to be?',
-// type: 'STRING',
-// required: true
-// }
-// ],
// channel: 'guild',
// clientPermissions: (m) => util.clientSendAndPermCheck(m),
// userPermissions: ['MANAGE_GUILD', 'MANAGE_ROLES']
@@ -52,8 +37,5 @@
// public override async exec(
// message: BushMessage | BushSlashMessage,
// args: { required_argument: string; optional_argument: string }
-// ) {
-// return await message.util.reply(`${util.emojis.error} Do not use the template command.`);
-// args;
-// }
+// ) {}
// }
diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts
index d119774..d15b8d7 100644
--- a/src/commands/config/blacklist.ts
+++ b/src/commands/config/blacklist.ts
@@ -1,4 +1,4 @@
-import { AllowedMentions, ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib';
+import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
import { User } from 'discord.js';
export default class BlacklistCommand extends BushCommand {
@@ -16,10 +16,7 @@ export default class BlacklistCommand extends BushCommand {
readableType: "'blacklist'|'unblacklist'",
prompt: 'Would you like to add or remove someone or something from/to the blacklist?',
slashType: 'STRING',
- choices: [
- { name: 'blacklist', value: 'blacklist' },
- { name: 'unblacklist', value: 'unblacklist' }
- ],
+ choices: ['blacklist', 'unblacklist'].map((c) => ({ name: c, value: c })),
only: 'slash'
},
{
@@ -51,7 +48,11 @@ export default class BlacklistCommand extends BushCommand {
public override async exec(
message: BushMessage | BushSlashMessage,
- args: { action?: 'blacklist' | 'unblacklist'; target: ArgType<'channel'> | ArgType<'user'> | string; global: boolean }
+ args: {
+ action?: 'blacklist' | 'unblacklist';
+ target: ArgType<'channel'> | ArgType<'user'> | string; // there is no way to combine channel and user in slash commands without making subcommands
+ global: ArgType<'boolean'>;
+ }
) {
let action: 'blacklist' | 'unblacklist' | 'toggle' =
args.action ?? (message?.util?.parsed?.alias as 'blacklist' | 'unblacklist' | undefined) ?? 'toggle';
diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts
index 4341198..11d9649 100644
--- a/src/commands/dev/__template.ts
+++ b/src/commands/dev/__template.ts
@@ -1,4 +1,4 @@
-import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib';
+import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib';
export default class TemplateCommand extends BushCommand {
public constructor() {
@@ -39,7 +39,7 @@ export default class TemplateCommand extends BushCommand {
public override async exec(
message: BushMessage | BushSlashMessage,
- args: { required_argument: string; optional_argument: string }
+ args: { required_argument: ArgType<'string'>; optional_argument: OptionalArgType<'string'> }
) {
return await message.util.reply(`${util.emojis.error} Do not use the template command.`);
args;
diff --git a/src/commands/dev/dm.ts b/src/commands/dev/dm.ts
index 4133b96..d702875 100644
--- a/src/commands/dev/dm.ts
+++ b/src/commands/dev/dm.ts
@@ -1,4 +1,4 @@
-import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib';
+import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
export default class DMCommand extends BushCommand {
public constructor() {
@@ -21,10 +21,9 @@ export default class DMCommand extends BushCommand {
id: 'content',
type: 'string',
match: 'rest',
- description: 'This is the second argument.',
- prompt: 'What would you like to set your second argument to be?',
- retry: '{error} Pick a valid argument.',
- optional: true,
+ description: 'The content to send to the user.',
+ prompt: 'What would you like to send to the user?',
+ retry: '{error} Pick something to send the user.',
slashType: 'STRING'
}
],
@@ -35,10 +34,12 @@ export default class DMCommand extends BushCommand {
userPermissions: []
});
}
- public override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'>; content: string }) {
+ public override async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { user: ArgType<'user'>; content: ArgType<'string'> }
+ ) {
try {
- const u = await client.users.fetch(args.user.id);
- await u.send(args.content);
+ await client.users.send(args.user.id, args.content);
} catch (e) {
return message.util.reply(`${util.emojis.error} There was an error sending ${util.format.input(args.user.tag)} a dm.`);
}
diff --git a/src/commands/dev/say.ts b/src/commands/dev/say.ts
index 80cbbe2..2c8151f 100644
--- a/src/commands/dev/say.ts
+++ b/src/commands/dev/say.ts
@@ -26,7 +26,7 @@ export default class SayCommand extends BushCommand {
});
}
- public override async exec(message: BushMessage, args: { content: string }) {
+ public override async exec(message: BushMessage | BushSlashMessage, args: { content: string }) {
if (!message.author.isOwner())
return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`);
diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts
index 35e98aa..3a6406d 100644
--- a/src/commands/dev/superUser.ts
+++ b/src/commands/dev/superUser.ts
@@ -1,4 +1,4 @@
-import { ArgType, BushCommand, Global, type BushMessage, type BushSlashMessage } from '#lib';
+import { BushCommand, Global, type ArgType, type BushMessage } from '#lib';
import { type ArgumentOptions, type Flag } from 'discord-akairo';
export default class SuperUserCommand extends BushCommand {
@@ -53,10 +53,7 @@ export default class SuperUserCommand extends BushCommand {
return { action, user };
}
- public override async exec(
- message: BushMessage | BushSlashMessage,
- { action, user }: { action: 'add' | 'remove'; user: ArgType<'user'> }
- ) {
+ public override async exec(message: BushMessage, { action, user }: { action: 'add' | 'remove'; user: ArgType<'user'> }) {
if (!message.author.isOwner())
return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`);
diff --git a/src/commands/moderation/activePunishments.ts b/src/commands/moderation/_activePunishments.ts
index d40f2ba..d40f2ba 100644
--- a/src/commands/moderation/activePunishments.ts
+++ b/src/commands/moderation/_activePunishments.ts
diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts
index bede2cf..919ee40 100644
--- a/src/commands/moderation/removeReactionEmoji.ts
+++ b/src/commands/moderation/removeReactionEmoji.ts
@@ -1,4 +1,5 @@
-import { ArgType, BushCommand, type BushMessage } from '#lib';
+import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import assert from 'assert';
import { Message, type Emoji } from 'discord.js';
export default class RemoveReactionEmojiCommand extends BushCommand {
@@ -37,9 +38,10 @@ export default class RemoveReactionEmojiCommand extends BushCommand {
}
public override async exec(
- message: BushMessage,
+ message: BushMessage | BushSlashMessage,
args: { message: ArgType<'guildMessage'> | string; emoji: ArgType<'emoji'> | ArgType<'snowflake'> }
) {
+ assert(message.channel);
const resolvedMessage = args.message instanceof Message ? args.message : await message.channel.messages.fetch(args.message);
const id = !(['string'] as const).includes(typeof args.emoji);
diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts
index d36a5db..e7a19d6 100644
--- a/src/commands/moderation/unmute.ts
+++ b/src/commands/moderation/unmute.ts
@@ -1,12 +1,12 @@
import {
AllowedMentions,
- ArgType,
BushCommand,
Moderation,
- OptionalArgType,
+ type ArgType,
type BushGuildMember,
type BushMessage,
- type BushSlashMessage
+ type BushSlashMessage,
+ type OptionalArgType
} from '#lib';
export default class UnmuteCommand extends BushCommand {
@@ -56,7 +56,7 @@ export default class UnmuteCommand extends BushCommand {
public override async exec(
message: BushMessage | BushSlashMessage,
- { user, reason, force }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force: boolean }
+ { user, reason, force = false }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: boolean }
) {
const error = util.emojis.error;
const member = message.guild!.members.cache.get(user.id) as BushGuildMember;
diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts
index 95e409d..5093c9b 100644
--- a/src/commands/moderation/warn.ts
+++ b/src/commands/moderation/warn.ts
@@ -1,12 +1,12 @@
import {
AllowedMentions,
- ArgType,
BushCommand,
Moderation,
- OptionalArgType,
+ type ArgType,
type BushGuildMember,
type BushMessage,
- type BushSlashMessage
+ type BushSlashMessage,
+ type OptionalArgType
} from '#lib';
export default class WarnCommand extends BushCommand {
@@ -55,7 +55,7 @@ export default class WarnCommand extends BushCommand {
public override async exec(
message: BushMessage | BushSlashMessage,
- { user, reason, force }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: boolean }
+ { user, reason, force = false }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: boolean }
) {
const member = message.guild!.members.cache.get(user.id) as BushGuildMember;
if (!member) return message.util.reply(`${util.emojis.error} I cannot warn users that are not in the server.`);
diff --git a/src/commands/utilities/remind.ts b/src/commands/utilities/remind.ts
index 8df24c1..6339343 100644
--- a/src/commands/utilities/remind.ts
+++ b/src/commands/utilities/remind.ts
@@ -1,4 +1,4 @@
-import { ArgType, BushCommand, Reminder, type BushMessage, type BushSlashMessage } from '#lib';
+import { BushCommand, Reminder, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
export default class RemindCommand extends BushCommand {
public constructor() {
diff --git a/src/commands/utilities/reminders.ts b/src/commands/utilities/reminders.ts
index 7180aa9..34034d8 100644
--- a/src/commands/utilities/reminders.ts
+++ b/src/commands/utilities/reminders.ts
@@ -1,5 +1,5 @@
import { BushCommand, ButtonPaginator, Reminder, type BushMessage, type BushSlashMessage } from '#lib';
-import { MessageEmbedOptions } from 'discord.js';
+import { type MessageEmbedOptions } from 'discord.js';
import { Op } from 'sequelize';
export default class RemindersCommand extends BushCommand {
diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts
index 480201a..67b8382 100644
--- a/src/commands/utilities/steal.ts
+++ b/src/commands/utilities/steal.ts
@@ -1,5 +1,5 @@
-import { ArgType, BushCommand, BushSlashMessage, type BushMessage } from '#lib';
-import { ArgumentOptions, ArgumentType, ArgumentTypeCaster, Flag } from 'discord-akairo';
+import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib';
+import { type ArgumentOptions, type ArgumentType, type ArgumentTypeCaster, type Flag } from 'discord-akairo';
import _ from 'lodash';
import { URL } from 'url';
diff --git a/src/commands/utilities/suicide.ts b/src/commands/utilities/suicide.ts
index 641f7ec..f0d75a2 100644
--- a/src/commands/utilities/suicide.ts
+++ b/src/commands/utilities/suicide.ts
@@ -1,5 +1,5 @@
import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib';
-import { Message, MessageEmbed } from 'discord.js';
+import { MessageEmbed, type Message } from 'discord.js';
export default class TemplateCommand extends BushCommand {
public constructor() {
diff --git a/src/commands/utilities/uuid.ts b/src/commands/utilities/uuid.ts
index 6195edd..d842b58 100644
--- a/src/commands/utilities/uuid.ts
+++ b/src/commands/utilities/uuid.ts
@@ -1,4 +1,4 @@
-import { BushCommand, type BushMessage } from '#lib';
+import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib';
export default class UuidCommand extends BushCommand {
public constructor() {
@@ -35,7 +35,7 @@ export default class UuidCommand extends BushCommand {
}
public override async exec(
- message: BushMessage,
+ message: BushMessage | BushSlashMessage,
{ ign, dashed }: { ign: { match: RegExpMatchArray; matches: any[] }; dashed: boolean }
) {
if (!ign) return await message.util.reply(`${util.emojis.error} Please enter a valid ign.`);
diff --git a/src/lib/common/Format.ts b/src/lib/common/Format.ts
deleted file mode 100644
index 6cb6edc..0000000
--- a/src/lib/common/Format.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { type CodeBlockLang } from '#lib';
-import { EscapeMarkdownOptions, Formatters, Util } from 'discord.js';
-
-/**
- * Formats and escapes content for formatting
- */
-export class Format {
- /**
- * Wraps the content inside a codeblock with no language.
- * @param content The content to wrap.
- */
- public static codeBlock(content: string): string;
-
- /**
- * Wraps the content inside a codeblock with the specified language.
- * @param language The language for the codeblock.
- * @param content The content to wrap.
- */
- public static codeBlock(language: CodeBlockLang, content: string): string;
- public static codeBlock(languageOrContent: string, content?: string): string {
- return typeof content === 'undefined'
- ? Formatters.codeBlock(Util.escapeCodeBlock(`${languageOrContent}`))
- : Formatters.codeBlock(`${languageOrContent}`, Util.escapeCodeBlock(`${content}`));
- }
-
- /**
- * Wraps the content inside \`backticks\`, which formats it as inline code.
- * @param content The content to wrap.
- */
- public static inlineCode(content: string): string {
- return Formatters.inlineCode(Util.escapeInlineCode(`${content}`));
- }
-
- /**
- * Formats the content into italic text.
- * @param content The content to wrap.
- */
- public static italic(content: string): string {
- return Formatters.italic(Util.escapeItalic(`${content}`));
- }
-
- /**
- * Formats the content into bold text.
- * @param content The content to wrap.
- */
- public static bold(content: string): string {
- return Formatters.bold(Util.escapeBold(`${content}`));
- }
-
- /**
- * Formats the content into underscored text.
- * @param content The content to wrap.
- */
- public static underscore(content: string): string {
- return Formatters.underscore(Util.escapeUnderline(`${content}`));
- }
-
- /**
- * Formats the content into strike-through text.
- * @param content The content to wrap.
- */
- public static strikethrough(content: string): string {
- return Formatters.strikethrough(Util.escapeStrikethrough(`${content}`));
- }
-
- /**
- * Wraps the content inside spoiler (hidden text).
- * @param content The content to wrap.
- */
- public static spoiler(content: string): string {
- return Formatters.spoiler(Util.escapeSpoiler(`${content}`));
- }
-
- /**
- * Escapes any Discord-flavour markdown in a string.
- * @param text Content to escape
- * @param options Options for escaping the markdown
- */
- public static escapeMarkdown(text: string, options?: EscapeMarkdownOptions): string {
- return Util.escapeMarkdown(`${text}`, options);
- }
-
- /**
- * Formats input: makes it bold and escapes any other markdown
- * @param text The input
- */
- public static input(text: string): string {
- return this.bold(this.escapeMarkdown(this.sanitizeWtlAndControl(`${text}`)));
- }
-
- /**
- * Formats input for logs: makes it highlighted
- * @param text The input
- */
- public static inputLog(text: string): string {
- return `<<${this.sanitizeWtlAndControl(`${text}`)}>>`;
- }
-
- /**
- * Removes all characters in a string that are either control characters or change the direction of text etc.
- * @param str The string you would like sanitized
- */
- public static sanitizeWtlAndControl(str: string) {
- // eslint-disable-next-line no-control-regex
- return `${str}`.replace(/[\u0000-\u001F\u007F-\u009F\u200B]/g, '');
- }
-}
diff --git a/src/lib/common/Moderation.ts b/src/lib/common/Moderation.ts
deleted file mode 100644
index ab2943b..0000000
--- a/src/lib/common/Moderation.ts
+++ /dev/null
@@ -1,293 +0,0 @@
-import {
- ActivePunishment,
- ActivePunishmentType,
- Guild,
- ModLog,
- ModLogType,
- type BushGuildMember,
- type BushGuildMemberResolvable,
- type BushGuildResolvable
-} from '#lib';
-import { type Snowflake } from 'discord.js';
-
-/**
- * A utility class with moderation-related methods.
- */
-export class Moderation {
- /**
- * Checks if a moderator can perform a moderation action on another user.
- * @param moderator The person trying to perform the action.
- * @param victim The person getting punished.
- * @param type The type of punishment - used to format the response.
- * @param checkModerator Whether or not to check if the victim is a moderator.
- * @param force Override permissions checks.
- * @returns `true` if the moderator can perform the action otherwise a reason why they can't.
- */
- public static async permissionCheck(
- moderator: BushGuildMember,
- victim: BushGuildMember,
- type: 'mute' | 'unmute' | 'warn' | 'kick' | 'ban' | 'unban' | 'add a punishment role to' | 'remove a punishment role from',
- checkModerator = true,
- force = false
- ): Promise<true | string> {
- if (force) return true;
-
- // If the victim is not in the guild anymore it will be undefined
- if ((!victim || !victim.guild) && !['ban', 'unban'].includes(type)) return true;
-
- if (moderator.guild.id !== victim.guild.id) {
- throw new Error('moderator and victim not in same guild');
- }
-
- const isOwner = moderator.guild.ownerId === moderator.id;
- if (moderator.id === victim.id && !type.startsWith('un')) {
- return `${util.emojis.error} You cannot ${type} yourself.`;
- }
- if (
- moderator.roles.highest.position <= victim.roles.highest.position &&
- !isOwner &&
- !(type.startsWith('un') && moderator.id === victim.id)
- ) {
- return `${util.emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as you do.`;
- }
- if (
- victim.roles.highest.position >= victim.guild.me!.roles.highest.position &&
- !(type.startsWith('un') && moderator.id === victim.id)
- ) {
- return `${util.emojis.error} You cannot ${type} **${victim.user.tag}** because they have higher or equal role hierarchy as I do.`;
- }
- if (checkModerator && victim.permissions.has('MANAGE_MESSAGES') && !(type.startsWith('un') && moderator.id === victim.id)) {
- if (await moderator.guild.hasFeature('modsCanPunishMods')) {
- return true;
- } else {
- return `${util.emojis.error} You cannot ${type} **${victim.user.tag}** because they are a moderator.`;
- }
- }
- return true;
- }
-
- /**
- * Creates a modlog entry for a punishment.
- * @param options Options for creating a modlog entry.
- * @param getCaseNumber Whether or not to get the case number of the entry.
- * @returns An object with the modlog and the case number.
- */
- public static async createModLogEntry(
- options: CreateModLogEntryOptions,
- getCaseNumber = false
- ): Promise<{ log: ModLog | null; caseNum: number | null }> {
- const user = (await util.resolveNonCachedUser(options.user))!.id;
- const moderator = (await util.resolveNonCachedUser(options.moderator))!.id;
- const guild = client.guilds.resolveId(options.guild)!;
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- const duration = options.duration || undefined;
-
- // If guild does not exist create it so the modlog can reference a guild.
- await Guild.findOrCreate({
- where: {
- id: guild
- },
- defaults: {
- id: guild
- }
- });
-
- const modLogEntry = ModLog.build({
- type: options.type,
- user,
- moderator,
- reason: options.reason,
- duration: duration,
- guild,
- pseudo: options.pseudo ?? false,
- evidence: options.evidence
- });
- const saveResult: ModLog | null = await modLogEntry.save().catch(async (e) => {
- await util.handleError('createModLogEntry', e);
- return null;
- });
-
- if (!getCaseNumber) return { log: saveResult, caseNum: null };
-
- const caseNum = (await ModLog.findAll({ where: { type: options.type, user: user, guild: guild, hidden: 'false' } }))?.length;
- return { log: saveResult, caseNum };
- }
-
- /**
- * Creates a punishment entry.
- * @param options Options for creating the punishment entry.
- * @returns The database entry, or null if no entry is created.
- */
- public static async createPunishmentEntry(options: CreatePunishmentEntryOptions): Promise<ActivePunishment | null> {
- const expires = options.duration ? new Date(+new Date() + options.duration ?? 0) : undefined;
- const user = (await util.resolveNonCachedUser(options.user))!.id;
- const guild = client.guilds.resolveId(options.guild)!;
- const type = this.findTypeEnum(options.type)!;
-
- const entry = ActivePunishment.build(
- options.extraInfo
- ? { user, type, guild, expires, modlog: options.modlog, extraInfo: options.extraInfo }
- : { user, type, guild, expires, modlog: options.modlog }
- );
- return await entry.save().catch(async (e) => {
- await util.handleError('createPunishmentEntry', e);
- return null;
- });
- }
-
- /**
- * Destroys a punishment entry.
- * @param options Options for destroying the punishment entry.
- * @returns Whether or not the entry was destroyed.
- */
- public static async removePunishmentEntry(options: RemovePunishmentEntryOptions): Promise<boolean> {
- const user = await util.resolveNonCachedUser(options.user);
- const guild = client.guilds.resolveId(options.guild);
- const type = this.findTypeEnum(options.type);
-
- if (!user || !guild) return false;
-
- let success = true;
-
- const entries = await ActivePunishment.findAll({
- // finding all cases of a certain type incase there were duplicates or something
- where: options.extraInfo
- ? { user: user.id, guild: guild, type, extraInfo: options.extraInfo }
- : { user: user.id, guild: guild, type }
- }).catch(async (e) => {
- await util.handleError('removePunishmentEntry', e);
- success = false;
- });
- if (entries) {
- const promises = entries.map(async (entry) =>
- entry.destroy().catch(async (e) => {
- await util.handleError('removePunishmentEntry', e);
- success = false;
- })
- );
-
- await Promise.all(promises);
- }
- return success;
- }
-
- /**
- * Returns the punishment type enum for the given type.
- * @param type The type of the punishment.
- * @returns The punishment type enum.
- */
- private static findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') {
- const typeMap = {
- ['mute']: ActivePunishmentType.MUTE,
- ['ban']: ActivePunishmentType.BAN,
- ['role']: ActivePunishmentType.ROLE,
- ['block']: ActivePunishmentType.BLOCK
- };
- return typeMap[type];
- }
-}
-
-/**
- * Options for creating a modlog entry.
- */
-export interface CreateModLogEntryOptions {
- /**
- * The type of modlog entry.
- */
- type: ModLogType;
-
- /**
- * The user that a modlog entry is created for.
- */
- user: BushGuildMemberResolvable;
-
- /**
- * The moderator that created the modlog entry.
- */
- moderator: BushGuildMemberResolvable;
-
- /**
- * The reason for the punishment.
- */
- reason: string | undefined | null;
-
- /**
- * The duration of the punishment.
- */
- duration?: number;
-
- /**
- * The guild that the punishment is created for.
- */
- guild: BushGuildResolvable;
-
- /**
- * Whether the punishment is a pseudo punishment.
- */
- pseudo?: boolean;
-
- /**
- * The evidence for the punishment.
- */
- evidence?: string;
-}
-
-/**
- * Options for creating a punishment entry.
- */
-export interface CreatePunishmentEntryOptions {
- /**
- * The type of punishment.
- */
- type: 'mute' | 'ban' | 'role' | 'block';
-
- /**
- * The user that the punishment is created for.
- */
- user: BushGuildMemberResolvable;
-
- /**
- * The length of time the punishment lasts for.
- */
- duration: number | undefined;
-
- /**
- * The guild that the punishment is created for.
- */
- guild: BushGuildResolvable;
-
- /**
- * The id of the modlog that is linked to the punishment entry.
- */
- modlog: string;
-
- /**
- * The role id if the punishment is a role punishment.
- */
- extraInfo?: Snowflake;
-}
-
-/**
- * Options for removing a punishment entry.
- */
-export interface RemovePunishmentEntryOptions {
- /**
- * The type of punishment.
- */
- type: 'mute' | 'ban' | 'role' | 'block';
-
- /**
- * The user that the punishment is destroyed for.
- */
- user: BushGuildMemberResolvable;
-
- /**
- * The guild that the punishment was in.
- */
- guild: BushGuildResolvable;
-
- /**
- * The role id if the punishment is a role punishment.
- */
- extraInfo?: Snowflake;
-}
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index f44c80d..f20a53e 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -6,13 +6,10 @@ import {
type BushClient,
type BushInspectOptions,
type BushMessage,
- type BushNewsChannel,
type BushSlashEditMessageType,
type BushSlashMessage,
type BushSlashSendMessageType,
- type BushTextChannel,
type BushUser,
- type BushUserResolvable,
type CodeBlockLang,
type Pronoun,
type PronounCode
@@ -42,7 +39,7 @@ import got from 'got';
import _ from 'lodash';
import { inspect, promisify } from 'util';
import CommandErrorListener from '../../../listeners/commands/commandError.js';
-import { Format } from '../../common/Format.js';
+import { Format } from '../../common/util/Format.js';
export class BushClientUtil extends ClientUtil {
/**
@@ -599,12 +596,6 @@ export class BushClientUtil extends ClientUtil {
return `${arrByte[1]}, ${arrByte[2]}, ${arrByte[3]}`;
}
- /* eslint-disable @typescript-eslint/no-unused-vars */
- public async lockdownChannel(options: { channel: BushTextChannel | BushNewsChannel; moderator: BushUserResolvable }) {
- // todo: implement lockdowns
- }
- /* eslint-enable @typescript-eslint/no-unused-vars */
-
/**
* Capitalize the first letter of a string.
* @param string The string to capitalize the first letter of.
diff --git a/src/lib/extensions/discord-akairo/BushSlashMessage.ts b/src/lib/extensions/discord-akairo/BushSlashMessage.ts
index 3cd167d..cefd360 100644
--- a/src/lib/extensions/discord-akairo/BushSlashMessage.ts
+++ b/src/lib/extensions/discord-akairo/BushSlashMessage.ts
@@ -20,12 +20,7 @@ export class BushSlashMessage extends AkairoMessage {
}
export interface BushSlashMessage extends AkairoMessage {
- /**
- * The channel that the interaction was sent in.
- */
get channel(): BushTextBasedChannel | null;
-}
-
-export interface BushSlashMessage extends AkairoMessage {
get guild(): BushGuild | null;
+ inGuild(): this is this & { guild: BushGuild; member: BushGuildMember };
}
diff --git a/src/lib/models/ActivePunishment.ts b/src/lib/models/ActivePunishment.ts
index 4512101..5fae2ac 100644
--- a/src/lib/models/ActivePunishment.ts
+++ b/src/lib/models/ActivePunishment.ts
@@ -70,6 +70,10 @@ export class ActivePunishment
*/
public declare modlog: string;
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
ActivePunishment.init(
{
diff --git a/src/lib/models/Global.ts b/src/lib/models/Global.ts
index e0cd7f9..30a9d38 100644
--- a/src/lib/models/Global.ts
+++ b/src/lib/models/Global.ts
@@ -54,6 +54,10 @@ export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes
*/
public declare blacklistedChannels: Snowflake[];
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
Global.init(
{
diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts
index 50113bf..dfee37b 100644
--- a/src/lib/models/Guild.ts
+++ b/src/lib/models/Guild.ts
@@ -1,4 +1,5 @@
-import { type Snowflake } from 'discord.js';
+import { ExcludeEnum, type Snowflake } from 'discord.js';
+import { ChannelTypes } from 'discord.js/typings/enums';
import { type Sequelize } from 'sequelize';
import { type BadWords } from '../common/AutoMod.js';
import { type BushClient } from '../extensions/discord-akairo/BushClient.js';
@@ -140,6 +141,10 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
*/
public declare levelUpChannel: Snowflake;
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize, client: BushClient): void {
Guild.init(
{
@@ -154,7 +159,10 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
disabledCommands: jsonArray('disabledCommands'),
lockdownChannels: jsonArray('lockdownChannels'),
autoModPhases: jsonObject('autoModPhases'),
- enabledFeatures: jsonArray('enabledFeatures'),
+ enabledFeatures: jsonArray(
+ 'enabledFeatures',
+ Object.keys(guildFeaturesObj).filter((key) => guildFeaturesObj[key as keyof typeof guildFeaturesObj].default)
+ ),
joinRoles: jsonArray('joinRoles'),
logChannels: jsonObject('logChannels'),
bypassChannelBlacklist: jsonArray('bypassChannelBlacklist'),
@@ -167,10 +175,14 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
}
}
+export type BaseGuildSetting = 'channel' | 'role' | 'user';
+export type GuildSettingType = 'string' | 'custom' | BaseGuildSetting | `${BaseGuildSetting}-array`;
+
export interface GuildSetting {
name: string;
description: string;
- type: 'string' | 'custom' | 'channel' | 'role' | 'user' | 'channel-array' | 'role-array' | 'user-array';
+ type: GuildSettingType;
+ subType: ExcludeEnum<typeof ChannelTypes, 'UNKNOWN'>[] | undefined;
configurable: boolean;
}
const asGuildSetting = <T>(et: { [K in keyof T]: GuildSetting }) => et;
@@ -180,78 +192,91 @@ export const guildSettingsObj = asGuildSetting({
name: 'Prefix',
description: 'The phrase required to trigger text commands in this server.',
type: 'string',
+ subType: undefined,
configurable: true
},
autoPublishChannels: {
name: 'Auto Publish Channels',
description: 'Channels were every message is automatically published.',
type: 'channel-array',
+ subType: ['GUILD_NEWS'],
configurable: true
},
welcomeChannel: {
name: 'Welcome Channel',
description: 'The channel where the bot will send join and leave message.',
type: 'channel',
+ subType: ['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_NEWS_THREAD', 'GUILD_PUBLIC_THREAD', 'GUILD_PRIVATE_THREAD'],
configurable: true
},
muteRole: {
name: 'Mute Role',
description: 'The role assigned when muting someone.',
type: 'role',
+ subType: undefined,
configurable: true
},
punishmentEnding: {
name: 'Punishment Ending',
description: 'The message after punishment information to a user in a dm.',
type: 'string',
+ subType: undefined,
configurable: true
},
lockdownChannels: {
name: 'Lockdown Channels',
description: 'Channels that are locked down when a mass lockdown is specified.',
type: 'channel-array',
- configurable: false // not implemented yet
+ subType: ['GUILD_TEXT'],
+ configurable: true
},
joinRoles: {
name: 'Join Roles',
description: 'Roles assigned to users on join who do not have sticky role information.',
type: 'role-array',
+ subType: undefined,
configurable: true
},
bypassChannelBlacklist: {
name: 'Bypass Channel Blacklist',
description: 'These users will be able to use commands in channels blacklisted.',
type: 'user-array',
+ subType: undefined,
configurable: true
},
logChannels: {
name: 'Log Channels',
description: 'The channel were logs are sent.',
type: 'custom',
+ subType: ['GUILD_TEXT'],
configurable: false
},
autoModPhases: {
name: 'Automod Phases',
description: 'Custom phrases to be detected by automod.',
type: 'custom',
+ subType: undefined,
configurable: false
},
noXpChannels: {
name: 'No Xp Channels',
description: 'Channels where users will not earn xp for leveling.',
type: 'channel-array',
+ subType: ['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_NEWS_THREAD', 'GUILD_PUBLIC_THREAD', 'GUILD_PRIVATE_THREAD'],
configurable: true
},
levelRoles: {
name: 'Level Roles',
description: 'What roles get given to users when they reach certain levels.',
type: 'custom',
+ subType: undefined,
configurable: false
},
levelUpChannel: {
name: 'Level Up Channel',
description: 'The channel to send level up messages in instead of last channel.',
type: 'channel',
+ subType: ['GUILD_TEXT', 'GUILD_NEWS', 'GUILD_NEWS_THREAD', 'GUILD_PUBLIC_THREAD', 'GUILD_PRIVATE_THREAD'],
configurable: true
}
});
@@ -264,61 +289,81 @@ export const settingsArr = Object.keys(guildSettingsObj).filter(
interface GuildFeature {
name: string;
description: string;
+ default: boolean;
}
const asGuildFeature = <T>(gf: { [K in keyof T]: GuildFeature }) => gf;
export const guildFeaturesObj = asGuildFeature({
automod: {
name: 'Automod',
- description: 'Deletes offensive content as well as phishing links.'
+ description: 'Deletes offensive content as well as phishing links.',
+ default: false
},
excludeDefaultAutomod: {
name: 'Exclude Default Automod',
- description: 'Opt out of using the default automod options.'
+ description: 'Opt out of using the default automod options.',
+ default: false
},
excludeAutomodScamLinks: {
name: 'Exclude Automod Scam Links',
- description: 'Opt out of having automod delete scam links.'
+ description: 'Opt out of having automod delete scam links.',
+ default: false
},
delScamMentions: {
name: 'Delete Scam Mentions',
- description: 'Deletes messages with @everyone and @here mentions that have common scam phrases.'
+ description: 'Deletes messages with @everyone and @here mentions that have common scam phrases.',
+ default: false
},
autoPublish: {
name: 'Auto Publish',
- description: 'Publishes messages in configured announcement channels.'
+ description: 'Publishes messages in configured announcement channels.',
+ default: false
},
+ // todo implement a better auto thread system
// autoThread: {
// name: 'Auto Thread',
- // description: 'Creates a new thread for messages in configured channels.'
+ // description: 'Creates a new thread for messages in configured channels.',
+ // default: false
// },
blacklistedFile: {
name: 'Blacklisted File',
- description: 'Automatically deletes malicious files.'
+ description: 'Automatically deletes malicious files.',
+ default: false
},
boosterMessageReact: {
name: 'Booster Message React',
- description: 'Reacts to booster messages with the boost emoji.'
+ description: 'Reacts to booster messages with the boost emoji.',
+ default: false
},
leveling: {
name: 'Leveling',
- description: "Tracks users' messages and assigns them xp."
+ description: "Tracks users' messages and assigns them xp.",
+ default: false
},
stickyRoles: {
name: 'Sticky Roles',
- description: 'Restores past roles to a user when they rejoin.'
+ description: 'Restores past roles to a user when they rejoin.',
+ default: false
},
reporting: {
name: 'Reporting',
- description: 'Allow users to make reports.'
+ description: 'Allow users to make reports.',
+ default: false
},
modsCanPunishMods: {
name: 'Mods Can Punish Mods',
- description: 'Allow moderators to punish other moderators.'
+ description: 'Allow moderators to punish other moderators.',
+ default: false
},
sendLevelUpMessages: {
name: 'Send Level Up Messages',
- description: 'Send a message when a user levels up.'
+ description: 'Send a message when a user levels up.',
+ default: true
+ },
+ logManualPunishments: {
+ name: 'Log Manual Punishments',
+ description: "Adds manual punishment to the user's modlogs and the logging channels.",
+ default: true
}
});
@@ -340,6 +385,7 @@ export const guildLogsObj = {
configurable: true
}
};
+
export type GuildLogType = keyof typeof guildLogsObj;
export const guildLogsArr = Object.keys(guildLogsObj).filter(
(s) => guildLogsObj[s as GuildLogType].configurable
diff --git a/src/lib/models/Level.ts b/src/lib/models/Level.ts
index baf359b..2ed787d 100644
--- a/src/lib/models/Level.ts
+++ b/src/lib/models/Level.ts
@@ -39,6 +39,10 @@ export class Level extends BaseModel<LevelModel, LevelModelCreationAttributes> i
return Level.convertXpToLevel(this.xp);
}
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
Level.init(
{
diff --git a/src/lib/models/ModLog.ts b/src/lib/models/ModLog.ts
index 7d656a4..e19ddd4 100644
--- a/src/lib/models/ModLog.ts
+++ b/src/lib/models/ModLog.ts
@@ -99,6 +99,10 @@ export class ModLog extends BaseModel<ModLogModel, ModLogModelCreationAttributes
*/
public declare hidden: boolean;
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
ModLog.init(
{
diff --git a/src/lib/models/Reminder.ts b/src/lib/models/Reminder.ts
index b8cd669..d1460bf 100644
--- a/src/lib/models/Reminder.ts
+++ b/src/lib/models/Reminder.ts
@@ -60,6 +60,10 @@ export class Reminder extends BaseModel<ReminderModel, ReminderModelCreationAttr
*/
public declare notified: boolean;
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
Reminder.init(
{
diff --git a/src/lib/models/Stat.ts b/src/lib/models/Stat.ts
index 4ecabb8..06ee21f 100644
--- a/src/lib/models/Stat.ts
+++ b/src/lib/models/Stat.ts
@@ -26,6 +26,10 @@ export class Stat extends BaseModel<StatModel, StatModelCreationAttributes> impl
*/
public declare commandsUsed: bigint;
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
Stat.init(
{
diff --git a/src/lib/models/StickyRole.ts b/src/lib/models/StickyRole.ts
index 64f0b4d..bbe81ae 100644
--- a/src/lib/models/StickyRole.ts
+++ b/src/lib/models/StickyRole.ts
@@ -38,6 +38,10 @@ export class StickyRole extends BaseModel<StickyRoleModel, StickyRoleModelCreati
*/
public declare nickname: string;
+ /**
+ * Initializes the model.
+ * @param sequelize The sequelize instance.
+ */
public static initModel(sequelize: Sequelize): void {
StickyRole.init(
{
diff --git a/src/lib/models/__helpers.ts b/src/lib/models/__helpers.ts
index 75737c5..049dc00 100644
--- a/src/lib/models/__helpers.ts
+++ b/src/lib/models/__helpers.ts
@@ -23,7 +23,7 @@ export function jsonObject(key: string): any {
};
}
-export function jsonArray(key: string): any {
+export function jsonArray(key: string, defaultValue: string[] = []): any {
return {
type: DataTypes.TEXT,
get: function (): string[] {
@@ -33,7 +33,7 @@ export function jsonArray(key: string): any {
return jsonParseSet.call(this, key, val);
},
allowNull: false,
- defaultValue: '[]'
+ defaultValue: JSON.stringify(defaultValue)
};
}