aboutsummaryrefslogtreecommitdiff
path: root/src/lib/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/common')
-rw-r--r--src/lib/common/AutoMod.ts2
-rw-r--r--src/lib/common/ButtonPaginator.ts7
-rw-r--r--src/lib/common/ConfirmationPrompt.ts2
-rw-r--r--src/lib/common/DeleteButton.ts1
-rw-r--r--src/lib/common/util/Moderation.ts124
5 files changed, 125 insertions, 11 deletions
diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts
index 784085d..9024260 100644
--- a/src/lib/common/AutoMod.ts
+++ b/src/lib/common/AutoMod.ts
@@ -156,6 +156,7 @@ export class AutoMod {
? [
new ActionRow().addComponents(
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Danger)
.setLabel('Ban User')
.setCustomId(`automod;ban;${this.message.author.id};everyone mention and scam phrase`)
@@ -277,6 +278,7 @@ export class AutoMod {
? [
new ActionRow().addComponents(
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Danger)
.setLabel('Ban User')
.setCustomId(`automod;ban;${this.message.author.id};${highestOffence.reason}`)
diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts
index 0399e74..09e059c 100644
--- a/src/lib/common/ButtonPaginator.ts
+++ b/src/lib/common/ButtonPaginator.ts
@@ -1,6 +1,6 @@
import { DeleteButton, type BushMessage, type BushSlashMessage } from '#lib';
import { CommandUtil } from 'discord-akairo';
-import { APIEmbed } from 'discord-api-types';
+import { APIEmbed } from 'discord-api-types/v9';
import { ActionRow, ActionRowComponent, ButtonComponent, ButtonStyle, Embed, type MessageComponentInteraction } from 'discord.js';
/**
@@ -173,26 +173,31 @@ export class ButtonPaginator {
protected getPaginationRow(disableAll = false): ActionRow<ActionRowComponent> {
return new ActionRow().addComponents(
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('paginate_beginning')
.setEmoji(PaginateEmojis.BEGINNING)
.setDisabled(disableAll || this.curPage === 0),
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('paginate_back')
.setEmoji(PaginateEmojis.BACK)
.setDisabled(disableAll || this.curPage === 0),
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('paginate_stop')
.setEmoji(PaginateEmojis.STOP)
.setDisabled(disableAll),
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('paginate_next')
.setEmoji(PaginateEmojis.FORWARD)
.setDisabled(disableAll || this.curPage === this.embeds.length - 1),
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('paginate_end')
.setEmoji(PaginateEmojis.END)
diff --git a/src/lib/common/ConfirmationPrompt.ts b/src/lib/common/ConfirmationPrompt.ts
index bd11c5c..1f027ef 100644
--- a/src/lib/common/ConfirmationPrompt.ts
+++ b/src/lib/common/ConfirmationPrompt.ts
@@ -31,11 +31,13 @@ export class ConfirmationPrompt {
this.messageOptions.components = [
new ActionRow().addComponents(
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('confirmationPrompt_confirm')
.setEmoji({ id: util.emojisRaw.successFull, name: 'successFull', animated: false })
.setLabel('Yes'),
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Danger)
.setCustomId('confirmationPrompt_cancel')
.setEmoji({ id: util.emojisRaw.errorFull, name: 'errorFull', animated: false })
diff --git a/src/lib/common/DeleteButton.ts b/src/lib/common/DeleteButton.ts
index cf3b416..f2e0ff3 100644
--- a/src/lib/common/DeleteButton.ts
+++ b/src/lib/common/DeleteButton.ts
@@ -68,6 +68,7 @@ export class DeleteButton {
this.messageOptions.components = [
new ActionRow().addComponents(
new ButtonComponent()
+ // @ts-expect-error: outdated @discord.js/builders
.setStyle(ButtonStyle.Primary)
.setCustomId('paginate__stop')
.setEmoji(PaginateEmojis.STOP)
diff --git a/src/lib/common/util/Moderation.ts b/src/lib/common/util/Moderation.ts
index 0ba6fca..c2236ab 100644
--- a/src/lib/common/util/Moderation.ts
+++ b/src/lib/common/util/Moderation.ts
@@ -10,7 +10,33 @@ import {
type BushUserResolvable,
type ModLogType
} from '#lib';
-import { Embed, PermissionFlagsBits, type Snowflake } from 'discord.js';
+import assert from 'assert';
+import { ActionRow, ButtonComponent, ButtonStyle, ComponentType, Embed, PermissionFlagsBits, type Snowflake } from 'discord.js';
+
+enum punishMap {
+ 'warned' = 'warn',
+ 'muted' = 'mute',
+ 'unmuted' = 'unmute',
+ 'kicked' = 'kick',
+ 'banned' = 'ban',
+ 'unbanned' = 'unban',
+ 'timedout' = 'timeout',
+ 'untimedout' = 'untimeout',
+ 'blocked' = 'block',
+ 'unblocked' = 'unblock'
+}
+enum reversedPunishMap {
+ 'warn' = 'warned',
+ 'mute' = 'muted',
+ 'unmute' = 'unmuted',
+ 'kick' = 'kicked',
+ 'ban' = 'banned',
+ 'unban' = 'unbanned',
+ 'timeout' = 'timedout',
+ 'untimeout' = 'untimedout',
+ 'block' = 'blocked',
+ 'unblock' = 'unblocked'
+}
/**
* A utility class with moderation-related methods.
@@ -204,6 +230,19 @@ export class Moderation {
return typeMap[type];
}
+ public static punishmentToPresentTense(punishment: PunishmentTypeDM): PunishmentTypePresent {
+ return punishMap[punishment];
+ }
+
+ public static punishmentToPastTense(punishment: PunishmentTypePresent): PunishmentTypeDM {
+ return reversedPunishMap[punishment];
+ }
+
+ /**
+ * Notifies the specified user of their punishment.
+ * @param options Options for notifying the user.
+ * @returns Whether or not the dm was successfully sent.
+ */
public static async punishDM(options: PunishDMOptions): Promise<boolean> {
const ending = await options.guild.getSetting('punishmentEnding');
const dmEmbed =
@@ -211,16 +250,45 @@ export class Moderation {
? new Embed().setDescription(ending).setColor(util.colors.newBlurple)
: undefined;
+ const appealsEnabled = !!(
+ (await options.guild.hasFeature('punishmentAppeals')) && (await options.guild.getLogChannel('appeals'))
+ );
+
+ let content = `You have been ${options.punishment} `;
+ if (options.punishment.includes('blocked')) {
+ assert(options.channel);
+ content += `from <#${options.channel}> `;
+ }
+ content += `in ${util.format.input(options.guild.name)} `;
+ if (options.duration !== null && options.duration !== undefined)
+ content += options.duration ? `for ${util.humanizeDuration(options.duration)} ` : 'permanently ';
+ const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided';
+ content += `for ${util.format.input(reason)}.`;
+
+ let components;
+ if (appealsEnabled && options.modlog)
+ components = [
+ new ActionRow({
+ type: ComponentType.ActionRow,
+ components: [
+ // @ts-expect-error: outdated @discord.js/builders
+ new ButtonComponent({
+ custom_id: `appeal;${this.punishmentToPresentTense(options.punishment)};${
+ options.guild.id
+ };${client.users.resolveId(options.user)};${options.modlog}`,
+ style: ButtonStyle.Primary,
+ type: ComponentType.Button,
+ label: 'Appeal'
+ })
+ ]
+ })
+ ];
+
const dmSuccess = await client.users
.send(options.user, {
- content: `You have been ${options.punishment} in **${options.guild.name}** ${
- options.duration !== null && options.duration !== undefined
- ? options.duration
- ? `for ${util.humanizeDuration(options.duration)} `
- : 'permanently '
- : ''
- }for **${options.reason?.trim() ? options.reason?.trim() : 'No reason provided'}**.`,
- embeds: dmEmbed ? [dmEmbed] : undefined
+ content,
+ embeds: dmEmbed ? [dmEmbed] : undefined,
+ components
})
.catch(() => false);
return !!dmSuccess;
@@ -342,6 +410,11 @@ export interface RemovePunishmentEntryOptions {
*/
export interface PunishDMOptions {
/**
+ * The modlog case id so the user can make an appeal.
+ */
+ modlog?: string;
+
+ /**
* The guild that the punishment is taking place in.
*/
guild: BushGuild;
@@ -354,7 +427,7 @@ export interface PunishDMOptions {
/**
* The punishment that the user has received.
*/
- punishment: string;
+ punishment: PunishmentTypeDM;
/**
* The reason the user's punishment.
@@ -371,4 +444,35 @@ export interface PunishDMOptions {
* @default true
*/
sendFooter: boolean;
+
+ /**
+ * The channel that the user was (un)blocked from.
+ */
+ channel?: Snowflake;
}
+
+export type PunishmentTypeDM =
+ | 'warned'
+ | 'muted'
+ | 'unmuted'
+ | 'kicked'
+ | 'banned'
+ | 'unbanned'
+ | 'timedout'
+ | 'untimedout'
+ | 'blocked'
+ | 'unblocked';
+
+export type PunishmentTypePresent =
+ | 'warn'
+ | 'mute'
+ | 'unmute'
+ | 'kick'
+ | 'ban'
+ | 'unban'
+ | 'timeout'
+ | 'untimeout'
+ | 'block'
+ | 'unblock';
+
+export type AppealButtonId = `appeal;${PunishmentTypePresent};${Snowflake};${Snowflake};${string}`;