diff options
Diffstat (limited to 'src/lib/common')
-rw-r--r-- | src/lib/common/AutoMod.ts | 1 | ||||
-rw-r--r-- | src/lib/common/ButtonPaginator.ts | 3 | ||||
-rw-r--r-- | src/lib/common/DeleteButton.ts | 3 | ||||
-rw-r--r-- | src/lib/common/Moderation.ts | 166 | ||||
-rw-r--r-- | src/lib/common/util/Arg.ts | 56 |
5 files changed, 170 insertions, 59 deletions
diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts index 932d457..e5487c3 100644 --- a/src/lib/common/AutoMod.ts +++ b/src/lib/common/AutoMod.ts @@ -261,6 +261,7 @@ export class AutoMod { .addField('Message Content', `${await util.codeblock(this.message.content, 1024)}`) .setColor(color) .setTimestamp() + .setAuthor({name: this.message.author.tag, url: this.message.author.displayAvatarURL()}) ], components: highestOffence.severity >= 2 diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts index 983eb56..d193b4d 100644 --- a/src/lib/common/ButtonPaginator.ts +++ b/src/lib/common/ButtonPaginator.ts @@ -1,4 +1,5 @@ import { DeleteButton, type BushMessage, type BushSlashMessage } from '#lib'; +import { CommandUtil } from 'discord-akairo'; import { Constants, MessageActionRow, @@ -120,7 +121,7 @@ export class ButtonPaginator { } protected async end() { - if (this.sentMessage && !this.sentMessage.deleted) + if (this.sentMessage && !CommandUtil.deletedMessages.has(this.sentMessage.id)) return await this.sentMessage .edit({ content: this.text, diff --git a/src/lib/common/DeleteButton.ts b/src/lib/common/DeleteButton.ts index 38ce6df..e2509a9 100644 --- a/src/lib/common/DeleteButton.ts +++ b/src/lib/common/DeleteButton.ts @@ -1,4 +1,5 @@ import { PaginateEmojis, type BushMessage, type BushSlashMessage } from '#lib'; +import { CommandUtil } from 'discord-akairo'; import { Constants, MessageActionRow, MessageButton, type MessageComponentInteraction, type MessageOptions } from 'discord.js'; export class DeleteButton { @@ -32,7 +33,7 @@ export class DeleteButton { collector.on('collect', async (interaction: MessageComponentInteraction) => { await interaction.deferUpdate().catch(() => undefined); if (interaction.user.id == this.message.author.id || client.config.owners.includes(interaction.user.id)) { - if (msg.deletable && !msg.deleted) await msg.delete(); + if (msg.deletable && !CommandUtil.deletedMessages.has(msg.id)) await msg.delete(); } }); diff --git a/src/lib/common/Moderation.ts b/src/lib/common/Moderation.ts index a7a037f..ab2943b 100644 --- a/src/lib/common/Moderation.ts +++ b/src/lib/common/Moderation.ts @@ -10,13 +10,18 @@ import { } 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 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, @@ -61,17 +66,14 @@ export class Moderation { 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: { - type: ModLogType; - user: BushGuildMemberResolvable; - moderator: BushGuildMemberResolvable; - reason: string | undefined | null; - duration?: number; - guild: BushGuildResolvable; - pseudo?: boolean; - evidence?: string; - }, + options: CreateModLogEntryOptions, getCaseNumber = false ): Promise<{ log: ModLog | null; caseNum: number | null }> { const user = (await util.resolveNonCachedUser(options.user))!.id; @@ -111,14 +113,12 @@ export class Moderation { return { log: saveResult, caseNum }; } - public static async createPunishmentEntry(options: { - type: 'mute' | 'ban' | 'role' | 'block'; - user: BushGuildMemberResolvable; - duration: number | undefined; - guild: BushGuildResolvable; - modlog: string; - extraInfo?: Snowflake; - }): Promise<ActivePunishment | null> { + /** + * 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)!; @@ -135,12 +135,12 @@ export class Moderation { }); } - public static async removePunishmentEntry(options: { - type: 'mute' | 'ban' | 'role' | 'block'; - user: BushGuildMemberResolvable; - guild: BushGuildResolvable; - extraInfo?: Snowflake; - }): Promise<boolean> { + /** + * 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); @@ -171,6 +171,11 @@ export class Moderation { 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, @@ -181,3 +186,108 @@ export class Moderation { 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/common/util/Arg.ts b/src/lib/common/util/Arg.ts index 9ce8b54..2577db9 100644 --- a/src/lib/common/util/Arg.ts +++ b/src/lib/common/util/Arg.ts @@ -1,7 +1,10 @@ import { BaseBushArgumentType, BushArgumentTypeCaster, BushSlashMessage, type BushArgumentType } from '#lib'; -import { Argument, type ArgumentTypeCaster, type Flag, type ParsedValuePredicate } from 'discord-akairo'; +import { Argument, type Flag, type ParsedValuePredicate } from 'discord-akairo'; import { type Message } from 'discord.js'; +/** + * A wrapper for the {@link Argument} class that adds custom typings. + */ export class Arg { /** * Casts a phrase to this argument's type. @@ -11,14 +14,9 @@ export class Arg { */ public static async cast<T extends ATC>(type: T, message: Message | BushSlashMessage, phrase: string): Promise<ATCR<T>>; public static async cast<T extends KBAT>(type: T, message: Message | BushSlashMessage, phrase: string): Promise<BAT[T]>; - public static async cast<T extends AT | ATC>(type: T, message: Message | BushSlashMessage, phrase: string): Promise<any>; + public static async cast(type: AT | ATC, message: Message | BushSlashMessage, phrase: string): Promise<any>; public static async cast(type: ATC | AT, message: Message | BushSlashMessage, phrase: string): Promise<any> { - return Argument.cast( - type as ArgumentTypeCaster | keyof BushArgumentType, - client.commandHandler.resolver, - message as Message, - phrase - ); + return Argument.cast(type as any, client.commandHandler.resolver, message as Message, phrase); } /** @@ -28,7 +26,7 @@ export class Arg { */ public static compose<T extends ATC>(...types: T[]): ATCATCR<T>; public static compose<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static compose<T extends AT | ATC>(...types: T[]): ATC; + public static compose(...types: (AT | ATC)[]): ATC; public static compose(...types: (AT | ATC)[]): ATC { return Argument.compose(...(types as any)); } @@ -40,7 +38,7 @@ export class Arg { */ public static composeWithFailure<T extends ATC>(...types: T[]): ATCATCR<T>; public static composeWithFailure<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static composeWithFailure<T extends AT | ATC>(...types: T[]): ATC; + public static composeWithFailure(...types: (AT | ATC)[]): ATC; public static composeWithFailure(...types: (AT | ATC)[]): ATC { return Argument.composeWithFailure(...(types as any)); } @@ -60,7 +58,7 @@ export class Arg { */ public static product<T extends ATC>(...types: T[]): ATCATCR<T>; public static product<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static product<T extends AT | ATC>(...types: T[]): ATC; + public static product(...types: (AT | ATC)[]): ATC; public static product(...types: (AT | ATC)[]): ATC { return Argument.product(...(types as any)); } @@ -74,7 +72,7 @@ export class Arg { */ public static range<T extends ATC>(type: T, min: number, max: number, inclusive?: boolean): ATCATCR<T>; public static range<T extends KBAT>(type: T, min: number, max: number, inclusive?: boolean): ATCBAT<T>; - public static range<T extends AT | ATC>(type: T, min: number, max: number, inclusive?: boolean): ATC; + public static range(type: AT | ATC, min: number, max: number, inclusive?: boolean): ATC; public static range(type: AT | ATC, min: number, max: number, inclusive?: boolean): ATC { return Argument.range(type as any, min, max, inclusive); } @@ -87,7 +85,7 @@ export class Arg { */ public static tagged<T extends ATC>(type: T, tag?: any): ATCATCR<T>; public static tagged<T extends KBAT>(type: T, tag?: any): ATCBAT<T>; - public static tagged<T extends AT | ATC>(type: T, tag?: any): ATC; + public static tagged(type: AT | ATC, tag?: any): ATC; public static tagged(type: AT | ATC, tag?: any): ATC { return Argument.tagged(type as any, tag); } @@ -100,7 +98,7 @@ export class Arg { */ public static taggedUnion<T extends ATC>(...types: T[]): ATCATCR<T>; public static taggedUnion<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static taggedUnion<T extends AT | ATC>(...types: T[]): ATC; + public static taggedUnion(...types: (AT | ATC)[]): ATC; public static taggedUnion(...types: (AT | ATC)[]): ATC { return Argument.taggedUnion(...(types as any)); } @@ -113,7 +111,7 @@ export class Arg { */ public static taggedWithInput<T extends ATC>(type: T, tag?: any): ATCATCR<T>; public static taggedWithInput<T extends KBAT>(type: T, tag?: any): ATCBAT<T>; - public static taggedWithInput<T extends AT | ATC>(type: T, tag?: any): ATC; + public static taggedWithInput(type: AT | ATC, tag?: any): ATC; public static taggedWithInput(type: AT | ATC, tag?: any): ATC { return Argument.taggedWithInput(type as any, tag); } @@ -125,7 +123,7 @@ export class Arg { */ public static union<T extends ATC>(...types: T[]): ATCATCR<T>; public static union<T extends KBAT>(...types: T[]): ATCBAT<T>; - public static union<T extends AT | ATC>(...types: T[]): ATC; + public static union(...types: (AT | ATC)[]): ATC; public static union(...types: (AT | ATC)[]): ATC { return Argument.union(...(types as any)); } @@ -138,7 +136,7 @@ export class Arg { */ public static validate<T extends ATC>(type: T, predicate: ParsedValuePredicate): ATCATCR<T>; public static validate<T extends KBAT>(type: T, predicate: ParsedValuePredicate): ATCBAT<T>; - public static validate<T extends AT | ATC>(type: T, predicate: ParsedValuePredicate): ATC; + public static validate(type: AT | ATC, predicate: ParsedValuePredicate): ATC; public static validate(type: AT | ATC, predicate: ParsedValuePredicate): ATC { return Argument.validate(type as any, predicate); } @@ -150,39 +148,39 @@ export class Arg { */ public static withInput<T extends ATC>(type: T): ATC<ATCR<T>>; public static withInput<T extends KBAT>(type: T): ATCBAT<T>; - public static withInput<T extends AT | ATC>(type: T): ATC; + public static withInput(type: AT | ATC): ATC; public static withInput(type: AT | ATC): ATC { return Argument.withInput(type as any); } } -type ArgumentTypeCasterReturn<R> = R extends BushArgumentTypeCaster<infer S> ? S : R; +type BushArgumentTypeCasterReturn<R> = R extends BushArgumentTypeCaster<infer S> ? S : R; /** ```ts - * <R = unknown> = ArgumentTypeCaster<R> + * <R = unknown> = BushArgumentTypeCaster<R> * ``` */ type ATC<R = unknown> = BushArgumentTypeCaster<R>; /** ```ts - * keyof BaseArgumentType + * keyof BaseBushArgumentType * ``` */ type KBAT = keyof BaseBushArgumentType; /** ```ts - * <R> = ArgumentTypeCasterReturn<R> + * <R> = BushArgumentTypeCasterReturn<R> * ``` */ -type ATCR<R> = ArgumentTypeCasterReturn<R>; +type ATCR<R> = BushArgumentTypeCasterReturn<R>; /** ```ts - * keyof BaseBushArgumentType | string + * BushArgumentType * ``` */ -type AT = BushArgumentTypeCaster | keyof BaseBushArgumentType | string; +type AT = BushArgumentType; /** ```ts - * BaseArgumentType + * BaseBushArgumentType * ``` */ type BAT = BaseBushArgumentType; /** ```ts - * <T extends ArgumentTypeCaster> = ArgumentTypeCaster<ArgumentTypeCasterReturn<T>> + * <T extends BushArgumentTypeCaster> = BushArgumentTypeCaster<BushArgumentTypeCasterReturn<T>> * ``` */ -type ATCATCR<T extends BushArgumentTypeCaster> = BushArgumentTypeCaster<ArgumentTypeCasterReturn<T>>; +type ATCATCR<T extends BushArgumentTypeCaster> = BushArgumentTypeCaster<BushArgumentTypeCasterReturn<T>>; /** ```ts - * <T extends keyof BaseArgumentType> = ArgumentTypeCaster<BaseArgumentType[T]> + * <T extends keyof BaseBushArgumentType> = BushArgumentTypeCaster<BaseBushArgumentType[T]> * ``` */ type ATCBAT<T extends keyof BaseBushArgumentType> = BushArgumentTypeCaster<BaseBushArgumentType[T]>; |