aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/automod/AutomodShared.ts8
-rw-r--r--lib/common/Appeals.ts140
-rw-r--r--lib/common/Moderation.ts53
-rw-r--r--lib/extensions/discord.js/BotClientEvents.ts20
-rw-r--r--lib/extensions/discord.js/ExtendedGuild.ts137
-rw-r--r--lib/extensions/discord.js/ExtendedGuildMember.ts508
-rw-r--r--lib/utils/FormatResponse.ts267
7 files changed, 764 insertions, 369 deletions
diff --git a/lib/automod/AutomodShared.ts b/lib/automod/AutomodShared.ts
index 48217dd..e886ad0 100644
--- a/lib/automod/AutomodShared.ts
+++ b/lib/automod/AutomodShared.ts
@@ -175,9 +175,9 @@ export async function handleAutomodInteraction(interaction: ButtonInteraction) {
const victimUserFormatted = (await interaction.client.utils.resolveNonCachedUser(userId))?.tag ?? userId;
const content = (() => {
- if (result === unmuteResponse.SUCCESS) {
+ if (result === unmuteResponse.Success) {
return `${emojis.success} Successfully banned ${Format.input(victimUserFormatted)}.`;
- } else if (result === unmuteResponse.DM_ERROR) {
+ } else if (result === unmuteResponse.DmError) {
return `${emojis.warn} Banned ${Format.input(victimUserFormatted)} however I could not send them a dm.`;
} else {
return `${emojis.error} Could not ban ${Format.input(victimUserFormatted)}: \`${result}\` .`;
@@ -218,9 +218,9 @@ export async function handleAutomodInteraction(interaction: ButtonInteraction) {
const victimUserFormatted = victim.user.tag;
const content = (() => {
- if (result === unmuteResponse.SUCCESS) {
+ if (result === unmuteResponse.Success) {
return `${emojis.success} Successfully unmuted ${Format.input(victimUserFormatted)}.`;
- } else if (result === unmuteResponse.DM_ERROR) {
+ } else if (result === unmuteResponse.DmError) {
return `${emojis.warn} Unmuted ${Format.input(victimUserFormatted)} however I could not send them a dm.`;
} else {
return `${emojis.error} Could not unmute ${Format.input(victimUserFormatted)}: \`${result}\` .`;
diff --git a/lib/common/Appeals.ts b/lib/common/Appeals.ts
index 43c56fd..4eb0ea3 100644
--- a/lib/common/Appeals.ts
+++ b/lib/common/Appeals.ts
@@ -1,6 +1,12 @@
import { AppealStatus, ModLog } from '#lib/models/instance/ModLog.js';
import { colors, emojis } from '#lib/utils/Constants.js';
import { input } from '#lib/utils/Format.js';
+import {
+ formatUnbanResponse,
+ formatUnblockResponse,
+ formatUnmuteResponse,
+ formatUntimeoutResponse
+} from '#lib/utils/FormatResponse.js';
import { capitalize, ModalInput } from '#lib/utils/Utils.js';
import {
ActionRowBuilder,
@@ -17,12 +23,28 @@ import { Action, punishments } from './Moderation.js';
type AppealBase = 'appeal_attempt' | 'appeal_submit' | 'appeal_accept' | 'appeal_deny';
-type RawAppealInfo = [baseId: AppealBase, punishment: `${Action}`, guildId: Snowflake, userId: Snowflake, modlogId: string];
-
-type AppealInfo = [baseId: AppealBase, punishment: Action, guildId: Snowflake, userId: Snowflake, modlogId: string];
+type RawAppealInfo = [
+ baseId: AppealBase,
+ punishment: `${Action}`,
+ guildId: Snowflake,
+ userId: Snowflake,
+ modlogId: string,
+ extraId?: Snowflake
+];
+
+type AppealInfo = [
+ baseId: AppealBase,
+ punishment: Action,
+ guildId: Snowflake,
+ userId: Snowflake,
+ modlogId: string,
+ extraId?: Snowflake
+];
export type AppealIdString =
- `${RawAppealInfo[0]};${RawAppealInfo[1]};${RawAppealInfo[2]};${RawAppealInfo[3]};${RawAppealInfo[4]}`;
+ `${RawAppealInfo[0]};${RawAppealInfo[1]};${RawAppealInfo[2]};${RawAppealInfo[3]};${RawAppealInfo[4]}${RawAppealInfo[5] extends undefined
+ ? ''
+ : `;${RawAppealInfo[5]}`}`;
function parseAppeal(customId: AppealIdString | string): AppealInfo {
const [baseId, _punishment, guildId, userId, modlogId] = customId.split(';') as RawAppealInfo;
@@ -37,7 +59,7 @@ function parseAppeal(customId: AppealIdString | string): AppealInfo {
* @param interaction A button interaction with a custom id thar starts with "appeal_attempt;".
*/
export async function handleAppealAttempt(interaction: ButtonInteraction) {
- const [baseId, punishment, guildId, userId, modlogId] = parseAppeal(interaction.customId);
+ const [baseId, punishment, guildId, userId, modlogId, extraId] = parseAppeal(interaction.customId);
const { base, past, appealCustom } = punishments[punishment];
const appealName = appealCustom ?? capitalize(base);
@@ -79,7 +101,7 @@ export async function handleAppealAttempt(interaction: ButtonInteraction) {
};
return await interaction.showModal({
- customId: `appeal_submit;${punishment};${guildId};${userId};${modlogId}`,
+ customId: `appeal_submit;${punishment};${guildId};${userId};${modlogId}${extraId ? `;${extraId}` : ''}`,
title: `${appealName} Appeal`,
components: [
ModalInput({
@@ -109,7 +131,7 @@ export async function handleAppealAttempt(interaction: ButtonInteraction) {
* @param interaction A modal interaction with a custom id that starts with "appeal_submit;".
*/
export async function handleAppealSubmit(interaction: ModalSubmitInteraction) {
- const [baseId, punishment, guildId, userId, modlogId] = parseAppeal(interaction.customId);
+ const [baseId, punishment, guildId, userId, modlogId, extraId] = parseAppeal(interaction.customId);
const { base, past, appealCustom } = punishments[punishment];
const appealName = appealCustom ?? capitalize(base);
@@ -159,12 +181,12 @@ export async function handleAppealSubmit(interaction: ModalSubmitInteraction) {
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder({
- customId: `appeal_accept;${punishment};${guildId};${userId};${modlogId}`,
+ customId: `appeal_accept;${punishment};${guildId};${userId};${modlogId}${extraId ? `;${extraId}` : ''}`,
label: 'Accept Appeal',
style: ButtonStyle.Success
}),
new ButtonBuilder({
- customId: `appeal_deny;${punishment};${guildId};${userId};${modlogId}`,
+ customId: `appeal_deny;${punishment};${guildId};${userId};${modlogId}${extraId ? `;${extraId}` : ''}`,
label: 'Deny Appeal',
style: ButtonStyle.Danger
})
@@ -178,7 +200,12 @@ export async function handleAppealSubmit(interaction: ModalSubmitInteraction) {
* @param interaction A button interaction with a custom id that starts with "appeal_accept;" or "appeal_deny;".
*/
export async function handleAppealDecision(interaction: ButtonInteraction) {
- const [baseId, punishment, guildId, userId, modlogId] = parseAppeal(interaction.customId);
+ if (!interaction.inCachedGuild()) {
+ void interaction.client.console.warn('Appeals', `Appeal decision made in uncached guild: ${interaction.guildId}`);
+ return;
+ }
+
+ const [baseId, punishment, guildId, userId, modlogId, extraId] = parseAppeal(interaction.customId);
const { base, past, appealCustom } = punishments[punishment];
const appealName = (appealCustom ?? base).toLowerCase();
@@ -186,12 +213,12 @@ export async function handleAppealDecision(interaction: ButtonInteraction) {
const modlog = await ModLog.findByPk(modlogId);
if (!modlog) {
- return await interaction.reply(`:skull: I cannot find the modlog ${input(modlogId)}. Please report this to my developers.`);
+ return await interaction.reply(`:boom: I cannot find the modlog ${input(modlogId)}. Please report this to my developers.`);
}
if (modlog.appeal !== AppealStatus.Submitted) {
return await interaction.reply(
- `:skull: Case ${input(modlogId)} has an invalid state of ${input(modlog.appeal)}. Please report this to my developers.`
+ `:boom: Case ${input(modlogId)} has an invalid state of ${input(modlog.appeal)}. Please report this to my developers.`
);
}
@@ -218,12 +245,8 @@ export async function handleAppealDecision(interaction: ButtonInteraction) {
]
});
} else if (baseId === 'appeal_accept') {
- modlog.appeal = AppealStatus.Accepted;
- await modlog.save();
-
- await interaction.client.users
- .send(userId, `Your ${appealName} appeal has been accepted in ${interaction.client.guilds.resolve(guildId)!}.`)
- .catch(() => {});
+ const guild = interaction.client.guilds.resolve(guildId);
+ if (!guild) return await interaction.reply(`:boom: I can't find this server.`);
switch (punishment) {
case Action.Warn:
@@ -236,25 +259,98 @@ export async function handleAppealDecision(interaction: ButtonInteraction) {
assert.fail(`Cannot appeal ${appealName} (Action.${Action[punishment]})`);
return;
case Action.Mute: {
- throw new Error('Not implemented');
+ const member = await guild.members.fetch(userId);
+
+ const res = await member.customUnmute({
+ reason: `Appeal accepted.`,
+ moderator: interaction.member,
+ noDM: true
+ });
+
+ if (res !== 'success') {
+ return await interaction.reply({
+ content: formatUnmuteResponse('/', member, res),
+ ephemeral: false
+ });
+ }
+
+ break;
}
case Action.Ban: {
- throw new Error('Not implemented');
+ const user = await interaction.client.users.fetch(userId);
+
+ const res = await guild.customUnban({
+ user: userId,
+ reason: `Appeal accepted.`,
+ moderator: interaction.member,
+ noDM: true
+ });
+
+ if (res !== 'success') {
+ return await interaction.reply({
+ content: formatUnbanResponse(user, res),
+ ephemeral: false
+ });
+ }
+
+ break;
}
case Action.Timeout: {
- throw new Error('Not implemented');
+ const member = await guild.members.fetch(userId);
+
+ const res = await member.customRemoveTimeout({
+ reason: `Appeal accepted.`,
+ moderator: interaction.member,
+ noDM: true
+ });
+
+ if (res !== 'success') {
+ return await interaction.reply({
+ content: formatUntimeoutResponse(member, res),
+ ephemeral: false
+ });
+ }
+
+ break;
}
case Action.Block: {
- throw new Error('Not implemented');
+ assert(extraId, 'Block appeal must have extraId');
+ const member = await guild.members.fetch(userId);
+
+ const res = await member.customUnblock({
+ reason: `Appeal accepted.`,
+ channel: extraId,
+ moderator: interaction.member,
+ noDM: true
+ });
+
+ if (res !== 'success') {
+ return await interaction.reply({
+ content: formatUnblockResponse(member, res),
+ ephemeral: false
+ });
+ }
+
+ break;
}
case Action.AddPunishRole: {
throw new Error('Not implemented');
+
+ break;
}
default: {
const _exhaustiveCheck: never = punishment;
}
}
+ modlog.appeal = AppealStatus.Accepted;
+ await modlog.save();
+
+ // dm
+ await interaction.client.users
+ .send(userId, `Your ${appealName} appeal (${input(modlogId)}) has been accepted in ${guild}.`)
+ .catch(() => {});
+
return await interaction.update({
content: `${emojis.check} Appeal accepted.`,
embeds: interaction.message.embeds,
diff --git a/lib/common/Moderation.ts b/lib/common/Moderation.ts
index 7697b2f..a79d1df 100644
--- a/lib/common/Moderation.ts
+++ b/lib/common/Moderation.ts
@@ -264,17 +264,17 @@ export async function checkMutePermissions(
guild: Guild
): Promise<ValueOf<typeof baseMuteResponse> | ValueOf<typeof permissionsResponse> | true> {
if (!guild.members.me!.permissions.has('ManageRoles')) {
- return permissionsResponse.MISSING_PERMISSIONS;
+ return permissionsResponse.MissingPermissions;
}
const muteRoleID = await guild.getSetting('muteRole');
- if (!muteRoleID) return baseMuteResponse.NO_MUTE_ROLE;
+ if (!muteRoleID) return baseMuteResponse.NoMuteRole;
const muteRole = guild.roles.cache.get(muteRoleID);
- if (!muteRole) return baseMuteResponse.MUTE_ROLE_INVALID;
+ if (!muteRole) return baseMuteResponse.MuteRoleInvalid;
if (muteRole.position >= guild.members.me!.roles.highest.position || muteRole.managed) {
- return baseMuteResponse.MUTE_ROLE_NOT_MANAGEABLE;
+ return baseMuteResponse.MuteRoleNotManageable;
}
return true;
@@ -570,6 +570,14 @@ export interface PunishDMOptions extends BaseOptions {
* The channel that the user was (un)blocked from.
*/
channel?: Snowflake;
+
+ /**
+ * The role that the user was given/removed.
+ */
+ role?: {
+ id: Snowflake;
+ name: string;
+ };
}
/**
@@ -587,17 +595,38 @@ export async function punishDM(options: PunishDMOptions): Promise<boolean> {
const appealsEnabled =
(await options.guild.hasFeature('punishmentAppeals')) && Boolean(await options.guild.getLogChannel('appeals'));
- let content = `You have been ${options.punishment} `;
+ let content = '';
+
+ switch (options.punishment) {
+ case Action.AddPunishRole:
+ assert(options.role, 'Role is required for adding a punishment role.');
+ content += `You have received the "${options.role.name}" punishment role`;
+ break;
+ case Action.RemovePunishRole:
+ assert(options.role, 'Role is required for removing a punishment role.');
+ content += `The "${options.role.name}" punishment role has been removed from you`;
+ break;
+ default:
+ content += `You have been ${options.punishment}`;
+ break;
+ }
+
if ([Action.Block, Action.Unblock].includes(options.punishment)) {
assert(options.channel);
- content += `from <#${options.channel}> `;
+ content += ` from <#${options.channel}>`;
}
- content += `in ${format.input(options.guild.name)} `;
+
+ content += ` in ${format.input(options.guild.name)}`;
if (options.duration !== null && options.duration !== undefined) {
- content += options.duration ? `for ${humanizeDuration(options.duration)} ` : 'permanently ';
+ content += options.duration ? ` for ${humanizeDuration(options.duration)}` : ' permanently';
+ }
+
+ if (![Action.AddPunishRole, Action.RemovePunishRole].includes(options.punishment)) {
+ const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided';
+ content += ` for ${format.input(reason)}.`;
+ } else {
+ content += '.';
}
- const reason = options.reason?.trim() ? options.reason?.trim() : 'No reason provided';
- content += `for ${format.input(reason)}.`;
let components;
if (appealsEnabled && options.modlog) {
@@ -606,11 +635,13 @@ export async function punishDM(options: PunishDMOptions): Promise<boolean> {
const userId = options.client.users.resolveId(options.user);
const modlogCase = options.modlog;
+ const extraId = options.channel ?? options.role?.id;
+
components = [
new ActionRowBuilder<ButtonBuilder>({
components: [
new ButtonBuilder({
- customId: `appeal_attempt;${Action[punishment]};${guildId};${userId};${modlogCase}`,
+ customId: `appeal_attempt;${Action[punishment]};${guildId};${userId};${modlogCase}${extraId ? `;${extraId}` : ''}`,
style: ButtonStyle.Primary,
label: 'Appeal Punishment'
})
diff --git a/lib/extensions/discord.js/BotClientEvents.ts b/lib/extensions/discord.js/BotClientEvents.ts
index 88f67e9..de06d62 100644
--- a/lib/extensions/discord.js/BotClientEvents.ts
+++ b/lib/extensions/discord.js/BotClientEvents.ts
@@ -25,7 +25,7 @@ export interface BotClientEvents extends AkairoClientEvents {
reason: string | undefined,
caseID: string,
duration: number,
- dmSuccess?: boolean,
+ dmSuccess?: boolean | null,
evidence?: string
];
[TanzaniteEvent.Block]: [
@@ -35,7 +35,7 @@ export interface BotClientEvents extends AkairoClientEvents {
reason: string | undefined,
caseID: string,
duration: number,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
channel: GuildTextBasedChannel,
evidence?: string
];
@@ -45,7 +45,7 @@ export interface BotClientEvents extends AkairoClientEvents {
guild: Guild,
reason: string | undefined,
caseID: string,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.Mute]: [
@@ -55,7 +55,7 @@ export interface BotClientEvents extends AkairoClientEvents {
reason: string | undefined,
caseID: string,
duration: number,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.PunishRoleAdd]: [
@@ -89,7 +89,7 @@ export interface BotClientEvents extends AkairoClientEvents {
guild: Guild,
reason: string | undefined,
caseID: string,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.Timeout]: [
@@ -99,7 +99,7 @@ export interface BotClientEvents extends AkairoClientEvents {
reason: string | undefined,
caseID: string,
duration: number,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.Unban]: [
@@ -108,7 +108,7 @@ export interface BotClientEvents extends AkairoClientEvents {
guild: Guild,
reason: string | undefined,
caseID: string,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.Unblock]: [
@@ -117,7 +117,7 @@ export interface BotClientEvents extends AkairoClientEvents {
guild: Guild,
reason: string | undefined,
caseID: string,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
channel: GuildTextBasedChannel,
evidence?: string
];
@@ -127,7 +127,7 @@ export interface BotClientEvents extends AkairoClientEvents {
guild: Guild,
reason: string | undefined,
caseID: string,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.UpdateModlog]: [
@@ -150,7 +150,7 @@ export interface BotClientEvents extends AkairoClientEvents {
guild: Guild,
reason: string | undefined,
caseID: string,
- dmSuccess: boolean,
+ dmSuccess: boolean | null,
evidence?: string
];
[TanzaniteEvent.LevelUpdate]: [
diff --git a/lib/extensions/discord.js/ExtendedGuild.ts b/lib/extensions/discord.js/ExtendedGuild.ts
index 84db7d0..2c1fd62 100644
--- a/lib/extensions/discord.js/ExtendedGuild.ts
+++ b/lib/extensions/discord.js/ExtendedGuild.ts
@@ -23,7 +23,6 @@ import {
PermissionFlagsBits,
SnowflakeUtil,
ThreadChannel,
- WebhookCreateMessageOptions,
type APIMessage,
type GuildMember,
type GuildMemberResolvable,
@@ -35,11 +34,12 @@ import {
type User,
type UserResolvable,
type VoiceChannel,
- type Webhook
+ type Webhook,
+ type WebhookCreateMessageOptions
} from 'discord.js';
import { camelCase } from 'lodash-es';
import { TanzaniteClient } from '../discord-akairo/TanzaniteClient.js';
-import { banResponse, BanResponse, dmResponse, permissionsResponse, punishmentEntryRemove } from './ExtendedGuildMember.js';
+import { banResponse, BanResponse, dmResponse, permissionsResponse, punishmentEntryError } from './ExtendedGuildMember.js';
interface Extension {
/**
@@ -113,7 +113,7 @@ interface Extension {
* @param options Options for banning the user.
* @returns A string status message of the ban.
* **Preconditions:**
- * - {@link me} has the `BanMembers` permission
+ * - {@link members.me} has the `BanMembers` permission
* **Warning:**
* - Doesn't emit customBan Event
*/
@@ -230,15 +230,15 @@ export class ExtendedGuild extends Guild implements Extension {
public override async customBan(options: GuildCustomBanOptions): Promise<BanResponse> {
// checks
- if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return banResponse.MISSING_PERMISSIONS;
+ if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return banResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const user = await this.client.utils.resolveNonCachedUser(options.user);
const moderator = this.client.users.resolve(options.moderator ?? this.client.user!);
- if (!user || !moderator) return banResponse.CANNOT_RESOLVE_USER;
+ if (!user || !moderator) return banResponse.CannotResolveUser;
- if ((await this.bans.fetch()).has(user.id)) return banResponse.ALREADY_BANNED;
+ if ((await this.bans.fetch()).has(user.id)) return banResponse.AlreadyBanned;
const ret = await (async () => {
// add modlog entry
@@ -252,20 +252,22 @@ export class ExtendedGuild extends Guild implements Extension {
guild: this,
evidence: options.evidence
});
- if (!modlog) return banResponse.MODLOG_ERROR;
+ if (!modlog) return banResponse.ModlogError;
caseID = modlog.id;
- // dm user
- dmSuccessEvent = await punishDM({
- client: this.client,
- modlog: modlog.id,
- guild: this,
- user: user,
- punishment: Action.Ban,
- duration: options.duration ?? 0,
- reason: options.reason ?? undefined,
- sendFooter: true
- });
+ if (!options.noDM) {
+ // dm user
+ dmSuccess = await punishDM({
+ client: this.client,
+ modlog: modlog.id,
+ guild: this,
+ user: user,
+ punishment: Action.Ban,
+ duration: options.duration ?? 0,
+ reason: options.reason ?? undefined,
+ sendFooter: true
+ });
+ }
// ban
const banSuccess = await this.bans
@@ -274,7 +276,7 @@ export class ExtendedGuild extends Guild implements Extension {
deleteMessageDays: options.deleteDays
})
.catch(() => false);
- if (!banSuccess) return banResponse.ACTION_ERROR;
+ if (!banSuccess) return banResponse.ActionError;
// add punishment entry so they can be unbanned later
const punishmentEntrySuccess = await createPunishmentEntry({
@@ -285,13 +287,16 @@ export class ExtendedGuild extends Guild implements Extension {
duration: options.duration,
modlog: modlog.id
});
- if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR;
+ if (!punishmentEntrySuccess) return banResponse.PunishmentEntryError;
- if (!dmSuccessEvent) return banResponse.DM_ERROR;
- return banResponse.SUCCESS;
+ if (!options.noDM && !dmSuccess) {
+ return banResponse.DmError;
+ }
+
+ return banResponse.Success;
})();
- if (!([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret))
+ if (!([banResponse.ActionError, banResponse.ModlogError, banResponse.PunishmentEntryError] as const).includes(ret))
this.client.emit(
TanzaniteEvent.Ban,
user,
@@ -300,14 +305,14 @@ export class ExtendedGuild extends Guild implements Extension {
options.reason ?? undefined,
caseID!,
options.duration ?? 0,
- dmSuccessEvent,
+ dmSuccess,
options.evidence
);
return ret;
}
public override async massBanOne(options: GuildMassBanOneOptions): Promise<BanResponse> {
- if (this.bans.cache.has(options.user)) return banResponse.ALREADY_BANNED;
+ if (this.bans.cache.has(options.user)) return banResponse.AlreadyBanned;
const ret = await (async () => {
// add modlog entry
@@ -320,7 +325,7 @@ export class ExtendedGuild extends Guild implements Extension {
duration: 0,
guild: this.id
});
- if (!modlog) return banResponse.MODLOG_ERROR;
+ if (!modlog) return banResponse.ModlogError;
let dmSuccessEvent: boolean | undefined = undefined;
// dm user
@@ -344,7 +349,7 @@ export class ExtendedGuild extends Guild implements Extension {
deleteMessageDays: options.deleteDays
})
.catch(() => false);
- if (!banSuccess) return banResponse.ACTION_ERROR;
+ if (!banSuccess) return banResponse.ActionError;
// add punishment entry so they can be unbanned later
const punishmentEntrySuccess = await createPunishmentEntry({
@@ -355,29 +360,29 @@ export class ExtendedGuild extends Guild implements Extension {
duration: 0,
modlog: modlog.id
});
- if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR;
+ if (!punishmentEntrySuccess) return banResponse.PunishmentEntryError;
- if (!dmSuccessEvent) return banResponse.DM_ERROR;
- return banResponse.SUCCESS;
+ if (!dmSuccessEvent) return banResponse.DmError;
+ return banResponse.Success;
})();
return ret;
}
public override async customUnban(options: GuildCustomUnbanOptions): Promise<UnbanResponse> {
// checks
- if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return unbanResponse.MISSING_PERMISSIONS;
+ if (!this.members.me!.permissions.has(PermissionFlagsBits.BanMembers)) return unbanResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const user = await this.client.utils.resolveNonCachedUser(options.user);
const moderator = this.client.users.resolve(options.moderator ?? this.client.user!);
- if (!user || !moderator) return unbanResponse.CANNOT_RESOLVE_USER;
+ if (!user || !moderator) return unbanResponse.CannotResolveUser;
const ret = await (async () => {
- const bans = await this.bans.fetch();
+ const ban = await this.bans.fetch(user.id);
let notBanned = false;
- if (!bans.has(user.id)) notBanned = true;
+ if (ban?.user?.id === user.id) notBanned = true;
const unbanSuccess = await this.bans
.remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`)
@@ -388,8 +393,8 @@ export class ExtendedGuild extends Guild implements Extension {
} else return false;
});
- if (notBanned) return unbanResponse.NOT_BANNED;
- if (!unbanSuccess) return unbanResponse.ACTION_ERROR;
+ if (notBanned) return unbanResponse.NotBanned;
+ if (!unbanSuccess) return unbanResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -401,7 +406,7 @@ export class ExtendedGuild extends Guild implements Extension {
guild: this,
evidence: options.evidence
});
- if (!modlog) return unbanResponse.MODLOG_ERROR;
+ if (!modlog) return unbanResponse.ModlogError;
caseID = modlog.id;
// remove punishment entry
@@ -411,26 +416,24 @@ export class ExtendedGuild extends Guild implements Extension {
user: user.id,
guild: this
});
- if (!removePunishmentEntrySuccess) return unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR;
+ if (!removePunishmentEntrySuccess) return unbanResponse.PunishmentEntryError;
- // dm user
- dmSuccessEvent = await punishDM({
- client: this.client,
- guild: this,
- user: user,
- punishment: Action.Unban,
- reason: options.reason ?? undefined,
- sendFooter: false
- });
+ if (!options.noDM) {
+ // dm user
+ dmSuccess = await punishDM({
+ client: this.client,
+ guild: this,
+ user: user,
+ punishment: Action.Unban,
+ reason: options.reason ?? undefined,
+ sendFooter: false
+ });
- if (!dmSuccessEvent) return unbanResponse.DM_ERROR;
- return unbanResponse.SUCCESS;
+ if (dmSuccess === false) return unbanResponse.DmError;
+ }
+ return unbanResponse.Success;
})();
- if (
- !([unbanResponse.ACTION_ERROR, unbanResponse.MODLOG_ERROR, unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const).includes(
- ret
- )
- )
+ if (!([unbanResponse.ActionError, unbanResponse.ModlogError, unbanResponse.PunishmentEntryError] as const).includes(ret))
this.client.emit(
TanzaniteEvent.Unban,
user,
@@ -438,7 +441,7 @@ export class ExtendedGuild extends Guild implements Extension {
this,
options.reason ?? undefined,
caseID!,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
@@ -756,6 +759,11 @@ export interface GuildCustomUnbanOptions {
* The evidence for the unban
*/
evidence?: string;
+
+ /**
+ * Don't send a dm to the user.
+ */
+ noDM?: boolean;
}
export interface GuildMassBanOneOptions {
@@ -813,6 +821,11 @@ export interface GuildCustomBanOptions {
* The evidence for the ban
*/
evidence?: string;
+
+ /**
+ * Don't send a dm to the user.
+ */
+ noDM?: boolean;
}
type ValueOf<T> = T[keyof T];
@@ -820,8 +833,8 @@ type ValueOf<T> = T[keyof T];
export const unbanResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
- ...punishmentEntryRemove,
- NOT_BANNED: 'user not banned'
+ ...punishmentEntryError,
+ NotBanned: 'user not banned'
} as const);
/**
diff --git a/lib/extensions/discord.js/ExtendedGuildMember.ts b/lib/extensions/discord.js/ExtendedGuildMember.ts
index b11e9e3..8cfd4f6 100644
--- a/lib/extensions/discord.js/ExtendedGuildMember.ts
+++ b/lib/extensions/discord.js/ExtendedGuildMember.ts
@@ -42,51 +42,51 @@ interface Extension {
* Warn the user, create a modlog entry, and send a dm to the user.
* @param options Options for warning the user.
* @returns An object with the result of the warning, and the case number of the warn.
- * @emits {@link BotClientEvents.warnMember}
+ * @emits {@link TanzaniteEvent.Warn}
*/
customWarn(options: CustomPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }>;
/**
* Add a role to the user, if it is a punishment create a modlog entry, and create a punishment entry if it is temporary or a punishment.
* @param options Options for adding a role to the user.
* @returns A status message for adding the add.
- * @emits {@link BotClientEvents.punishRole}
+ * @emits {@link TanzaniteEvent.PunishRoleAdd}
*/
customAddRole(options: AddRoleOptions): Promise<AddRoleResponse>;
/**
* Remove a role from the user, if it is a punishment create a modlog entry, and destroy a punishment entry if it was temporary or a punishment.
* @param options Options for removing a role from the user.
* @returns A status message for removing the role.
- * @emits {@link BotClientEvents.punishRoleRemove}
+ * @emits {@link TanzaniteEvent.PunishRoleRemove}
*/
customRemoveRole(options: RemoveRoleOptions): Promise<RemoveRoleResponse>;
/**
* Mute the user, create a modlog entry, creates a punishment entry, and dms the user.
* @param options Options for muting the user.
* @returns A status message for muting the user.
- * @emits {@link BotClientEvents.customMute}
+ * @emits {@link TanzaniteEvent.Mute}
*/
customMute(options: CustomTimedPunishmentOptions): Promise<MuteResponse>;
/**
* Unmute the user, create a modlog entry, remove the punishment entry, and dm the user.
* @param options Options for unmuting the user.
* @returns A status message for unmuting the user.
- * @emits {@link BotClientEvents.customUnmute}
+ * @emits {@link TanzaniteEvent.Unmute}
*/
customUnmute(options: CustomPunishmentOptions): Promise<UnmuteResponse>;
/**
* Kick the user, create a modlog entry, and dm the user.
* @param options Options for kicking the user.
* @returns A status message for kicking the user.
- * @emits {@link BotClientEvents.customKick}
+ * @emits {@link TanzaniteEvent.Kick}
*/
customKick(options: CustomPunishmentOptions): Promise<KickResponse>;
/**
* Ban the user, create a modlog entry, create a punishment entry, and dm the user.
* @param options Options for banning the user.
* @returns A status message for banning the user.
- * @emits {@link BotClientEvents.customBan}
+ * @emits {@link TanzaniteEvent.Ban}
*/
- customBan(options: CustomBanOptions): Promise<Exclude<BanResponse, typeof banResponse['ALREADY_BANNED']>>;
+ customBan(options: CustomBanOptions): Promise<Exclude<BanResponse, typeof banResponse['AlreadyBanned']>>;
/**
* Prevents a user from speaking in a channel.
* @param options Options for blocking the user.
@@ -124,6 +124,34 @@ declare module 'discord.js' {
}
export class ExtendedGuildMember extends GuildMember implements Extension {
+ /**
+ * Sets the default values for the options.
+ */
+ #optionDefaults<T extends CustomPunishmentOptions>(options: T): T {
+ options.noDM ??= options.silent ?? false;
+ return options;
+ }
+
+ /**
+ * Check whether or not a role should be added/removed from the user based on hierarchy.
+ * @param role The role to check if can be modified.
+ * @param moderator The moderator that is trying to add/remove the role.
+ * @returns `true` if the role should be added/removed or a string for the reason why it shouldn't.
+ */
+ #checkIfShouldAddRole(
+ role: Role | Role,
+ moderator?: GuildMember
+ ): true | 'user hierarchy' | 'role managed' | 'client hierarchy' {
+ if (moderator && moderator.roles.highest.position <= role.position && this.guild.ownerId !== this.user.id) {
+ return shouldAddRoleResponse.UserHierarchy;
+ } else if (role.managed) {
+ return shouldAddRoleResponse.RoleManaged;
+ } else if (this.guild.members.me!.roles.highest.position <= role.position) {
+ return shouldAddRoleResponse.ClientHierarchy;
+ }
+ return true;
+ }
+
public override async customPunishDM(
punishment: Action,
reason?: string | null,
@@ -144,10 +172,12 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
}
public override async customWarn(options: CustomPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }> {
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ options = this.#optionDefaults(options);
+
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return { result: warnResponse.CANNOT_RESOLVE_USER, caseNum: null };
+ if (!moderator) return { result: warnResponse.CannotResolveUser, caseNum: null };
const ret = await (async (): Promise<{ result: WarnResponse; caseNum: number | null }> => {
// add modlog entry
@@ -164,32 +194,33 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
},
true
);
- caseID = result.log?.id;
- if (!result || !result.log) return { result: warnResponse.MODLOG_ERROR, caseNum: null };
- if (!options.silent) {
+ caseID = result.log?.id ?? null;
+ if (!result || !result.log) return { result: warnResponse.ModlogError, caseNum: null };
+
+ if (!options.noDM) {
// dm user
- const dmSuccess = await this.customPunishDM(Action.Warn, options.reason);
- dmSuccessEvent = dmSuccess;
- if (!dmSuccess) return { result: warnResponse.DM_ERROR, caseNum: result.caseNum };
+ dmSuccess = await this.customPunishDM(Action.Warn, options.reason);
+
+ if (dmSuccess === false) return { result: warnResponse.DmError, caseNum: result.caseNum };
}
- return { result: warnResponse.SUCCESS, caseNum: result.caseNum };
+ return { result: warnResponse.Success, caseNum: result.caseNum };
})();
- if (!([warnResponse.MODLOG_ERROR] as const).includes(ret.result) && !options.silent)
- this.client.emit(TanzaniteEvent.Warn, this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!);
+ if (!([warnResponse.ModlogError] as const).includes(ret.result) && !options.silent)
+ this.client.emit(TanzaniteEvent.Warn, this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccess);
return ret;
}
public override async customAddRole(options: AddRoleOptions): Promise<AddRoleResponse> {
// checks
- if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return addRoleResponse.MISSING_PERMISSIONS;
+ if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return roleResponse.MissingPermissions;
const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator);
if (ifShouldAddRole !== true) return ifShouldAddRole;
- let caseID: string | undefined = undefined;
+ let caseID: string | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return addRoleResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return roleResponse.CannotResolveUser;
const ret = await (async () => {
if (options.addToModlog || options.duration) {
@@ -205,7 +236,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
hidden: options.silent ?? false
});
- if (!modlog) return addRoleResponse.MODLOG_ERROR;
+ if (!modlog) return roleResponse.ModlogError;
caseID = modlog.id;
if (options.addToModlog || options.duration) {
@@ -218,19 +249,17 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
duration: options.duration,
extraInfo: options.role.id
});
- if (!punishmentEntrySuccess) return addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR;
+ if (!punishmentEntrySuccess) return roleResponse.PunishmentEntryError;
}
}
const removeRoleSuccess = await this.roles.add(options.role, `${moderator.tag}`);
- if (!removeRoleSuccess) return addRoleResponse.ACTION_ERROR;
+ if (!removeRoleSuccess) return roleResponse.ActionError;
- return addRoleResponse.SUCCESS;
+ return roleResponse.Success;
})();
if (
- !(
- [addRoleResponse.ACTION_ERROR, addRoleResponse.MODLOG_ERROR, addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const
- ).includes(ret) &&
+ !([roleResponse.ActionError, roleResponse.ModlogError, roleResponse.PunishmentEntryError] as const).includes(ret) &&
options.addToModlog &&
!options.silent
)
@@ -250,13 +279,13 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
public override async customRemoveRole(options: RemoveRoleOptions): Promise<RemoveRoleResponse> {
// checks
- if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return removeRoleResponse.MISSING_PERMISSIONS;
+ if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ManageRoles)) return roleResponse.MissingPermissions;
const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator);
if (ifShouldAddRole !== true) return ifShouldAddRole;
let caseID: string | undefined = undefined;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return removeRoleResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return roleResponse.CannotResolveUser;
const ret = await (async () => {
if (options.addToModlog) {
@@ -271,7 +300,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
hidden: options.silent ?? false
});
- if (!modlog) return removeRoleResponse.MODLOG_ERROR;
+ if (!modlog) return roleResponse.ModlogError;
caseID = modlog.id;
const punishmentEntrySuccess = await removePunishmentEntry({
@@ -282,26 +311,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
extraInfo: options.role.id
});
- if (!punishmentEntrySuccess) return removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR;
+ if (!punishmentEntrySuccess) return roleResponse.PunishmentEntryError;
}
const removeRoleSuccess = await this.roles.remove(options.role, `${moderator.tag}`);
- if (!removeRoleSuccess) return removeRoleResponse.ACTION_ERROR;
+ if (!removeRoleSuccess) return roleResponse.ActionError;
- return removeRoleResponse.SUCCESS;
+ return roleResponse.Success;
})();
- if (
- !(
- [
- removeRoleResponse.ACTION_ERROR,
- removeRoleResponse.MODLOG_ERROR,
- removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR
- ] as const
- ).includes(ret) &&
- options.addToModlog &&
- !options.silent
- )
+ const nonSuccess = (
+ [roleResponse.ActionError, roleResponse.ModlogError, roleResponse.PunishmentEntryError] as const
+ ).includes(ret);
+
+ if (!nonSuccess && options.addToModlog && !options.silent) {
this.client.emit(
TanzaniteEvent.PunishRoleRemove,
this,
@@ -312,30 +335,13 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
options.role,
options.evidence
);
- return ret;
- }
-
- /**
- * Check whether or not a role should be added/removed from the user based on hierarchy.
- * @param role The role to check if can be modified.
- * @param moderator The moderator that is trying to add/remove the role.
- * @returns `true` if the role should be added/removed or a string for the reason why it shouldn't.
- */
- #checkIfShouldAddRole(
- role: Role | Role,
- moderator?: GuildMember
- ): true | 'user hierarchy' | 'role managed' | 'client hierarchy' {
- if (moderator && moderator.roles.highest.position <= role.position && this.guild.ownerId !== this.user.id) {
- return shouldAddRoleResponse.USER_HIERARCHY;
- } else if (role.managed) {
- return shouldAddRoleResponse.ROLE_MANAGED;
- } else if (this.guild.members.me!.roles.highest.position <= role.position) {
- return shouldAddRoleResponse.CLIENT_HIERARCHY;
}
- return true;
+ return ret;
}
public override async customMute(options: CustomTimedPunishmentOptions): Promise<MuteResponse> {
+ options = this.#optionDefaults(options);
+
// checks
const checks = await checkMutePermissions(this.guild);
if (checks !== true) return checks;
@@ -343,10 +349,10 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
const muteRoleID = (await this.guild.getSetting('muteRole'))!;
const muteRole = this.guild.roles.cache.get(muteRoleID)!;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return muteResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return muteResponse.CannotResolveUser;
const ret = await (async () => {
// add role
@@ -357,7 +363,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
this.client.console.debug(e);
return false;
});
- if (!muteSuccess) return muteResponse.ACTION_ERROR;
+ if (!muteSuccess) return muteResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -372,7 +378,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
hidden: options.silent ?? false
});
- if (!modlog) return muteResponse.MODLOG_ERROR;
+ if (!modlog) return muteResponse.ModlogError;
caseID = modlog.id;
// add punishment entry so they can be unmuted later
@@ -385,20 +391,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
modlog: modlog.id
});
- if (!punishmentEntrySuccess) return muteResponse.PUNISHMENT_ENTRY_ADD_ERROR;
+ if (!punishmentEntrySuccess) return muteResponse.PunishmentEntryError;
- if (!options.silent) {
+ if (!options.noDM) {
// dm user
- const dmSuccess = await this.customPunishDM(Action.Mute, options.reason, options.duration ?? 0, modlog.id);
- dmSuccessEvent = dmSuccess;
- if (!dmSuccess) return muteResponse.DM_ERROR;
+ dmSuccess = await this.customPunishDM(Action.Mute, options.reason, options.duration ?? 0, modlog.id);
+
+ if (dmSuccess === false) return muteResponse.DmError;
}
- return muteResponse.SUCCESS;
+ return muteResponse.Success;
})();
if (
- !([muteResponse.ACTION_ERROR, muteResponse.MODLOG_ERROR, muteResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) &&
+ !([muteResponse.ActionError, muteResponse.ModlogError, muteResponse.PunishmentEntryError] as const).includes(ret) &&
!options.silent
)
this.client.emit(
@@ -409,13 +415,15 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
options.reason ?? undefined,
caseID!,
options.duration ?? 0,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
}
public override async customUnmute(options: CustomPunishmentOptions): Promise<UnmuteResponse> {
+ options = this.#optionDefaults(options);
+
// checks
const checks = await checkMutePermissions(this.guild);
if (checks !== true) return checks;
@@ -423,10 +431,10 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
const muteRoleID = (await this.guild.getSetting('muteRole'))!;
const muteRole = this.guild.roles.cache.get(muteRoleID)!;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return unmuteResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return unmuteResponse.CannotResolveUser;
const ret = await (async () => {
// remove role
@@ -436,7 +444,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
await this.client.console.warn('muteRoleAddError', formatError(e, true));
return false;
});
- if (!muteSuccess) return unmuteResponse.ACTION_ERROR;
+ if (!muteSuccess) return unmuteResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -450,7 +458,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
hidden: options.silent ?? false
});
- if (!modlog) return unmuteResponse.MODLOG_ERROR;
+ if (!modlog) return unmuteResponse.ModlogError;
caseID = modlog.id;
// remove mute entry
@@ -461,22 +469,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
guild: this.guild
});
- if (!removePunishmentEntrySuccess) return unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR;
+ if (!removePunishmentEntrySuccess) return unmuteResponse.PunishmentEntryError;
- if (!options.silent) {
+ if (!options.noDM) {
// dm user
- const dmSuccess = await this.customPunishDM(Action.Unmute, options.reason, undefined, '', false);
- dmSuccessEvent = dmSuccess;
- if (!dmSuccess) return unmuteResponse.DM_ERROR;
+ dmSuccess = await this.customPunishDM(Action.Unmute, options.reason, undefined, '', false);
+
+ if (dmSuccess === false) return unmuteResponse.DmError;
}
- return unmuteResponse.SUCCESS;
+ return unmuteResponse.Success;
})();
if (
- !(
- [unmuteResponse.ACTION_ERROR, unmuteResponse.MODLOG_ERROR, unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const
- ).includes(ret) &&
+ !([unmuteResponse.ActionError, unmuteResponse.ModlogError, unmuteResponse.PunishmentEntryError] as const).includes(ret) &&
!options.silent
)
this.client.emit(
@@ -486,21 +492,23 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
this.guild,
options.reason ?? undefined,
caseID!,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
}
public override async customKick(options: CustomPunishmentOptions): Promise<KickResponse> {
+ options = this.#optionDefaults(options);
+
// checks
if (!this.guild.members.me?.permissions.has(PermissionFlagsBits.KickMembers) || !this.kickable)
- return kickResponse.MISSING_PERMISSIONS;
+ return kickResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return kickResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return kickResponse.CannotResolveUser;
const ret = await (async () => {
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -513,21 +521,26 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
evidence: options.evidence,
hidden: options.silent ?? false
});
- if (!modlog) return kickResponse.MODLOG_ERROR;
+ if (!modlog) return kickResponse.ModlogError;
caseID = modlog.id;
// dm user
- const dmSuccess = options.silent ? null : await this.customPunishDM(Action.Kick, options.reason, undefined, modlog.id);
- dmSuccessEvent = dmSuccess ?? undefined;
+ if (!options.noDM) {
+ dmSuccess = await this.customPunishDM(Action.Kick, options.reason, undefined, modlog.id);
+ }
// kick
const kickSuccess = await this.kick(`${moderator?.tag} | ${options.reason ?? 'No reason provided.'}`).catch(() => false);
- if (!kickSuccess) return kickResponse.ACTION_ERROR;
- if (dmSuccess === false) return kickResponse.DM_ERROR;
- return kickResponse.SUCCESS;
+ if (!kickSuccess) return kickResponse.ActionError;
+
+ if (!options.noDM && dmSuccess === false) {
+ return kickResponse.DmError;
+ }
+
+ return kickResponse.Success;
})();
- if (!([kickResponse.ACTION_ERROR, kickResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent)
+ if (!([kickResponse.ActionError, kickResponse.ModlogError] as const).includes(ret) && !options.silent)
this.client.emit(
TanzaniteEvent.Kick,
this,
@@ -535,23 +548,23 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
this.guild,
options.reason ?? undefined,
caseID!,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
}
- public override async customBan(
- options: CustomBanOptions
- ): Promise<Exclude<BanResponse, typeof banResponse['ALREADY_BANNED']>> {
+ public override async customBan(options: CustomBanOptions): Promise<Exclude<BanResponse, typeof banResponse['AlreadyBanned']>> {
+ options = this.#optionDefaults(options);
+
// checks
if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.BanMembers) || !this.bannable)
- return banResponse.MISSING_PERMISSIONS;
+ return banResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return banResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return banResponse.CannotResolveUser;
// ignore result, they should still be banned even if their mute cannot be removed
await this.customUnmute({
@@ -573,21 +586,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
evidence: options.evidence,
hidden: options.silent ?? false
});
- if (!modlog) return banResponse.MODLOG_ERROR;
+ if (!modlog) return banResponse.ModlogError;
caseID = modlog.id;
- // dm user
- const dmSuccess = options.silent
- ? null
- : await this.customPunishDM(Action.Ban, options.reason, options.duration ?? 0, modlog.id);
- dmSuccessEvent = dmSuccess ?? undefined;
+ if (!options.noDM) {
+ // dm user
+ dmSuccess = await this.customPunishDM(Action.Ban, options.reason, options.duration ?? 0, modlog.id);
+ }
// ban
const banSuccess = await this.ban({
reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`,
deleteMessageDays: options.deleteDays
}).catch(() => false);
- if (!banSuccess) return banResponse.ACTION_ERROR;
+ if (!banSuccess) return banResponse.ActionError;
// add punishment entry so they can be unbanned later
const punishmentEntrySuccess = await createPunishmentEntry({
@@ -598,13 +610,16 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
duration: options.duration,
modlog: modlog.id
});
- if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR;
+ if (!punishmentEntrySuccess) return banResponse.PunishmentEntryError;
- if (!dmSuccess) return banResponse.DM_ERROR;
- return banResponse.SUCCESS;
+ if (!options.noDM && dmSuccess === false) {
+ return banResponse.DmError;
+ }
+
+ return banResponse.Success;
})();
if (
- !([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) &&
+ !([banResponse.ActionError, banResponse.ModlogError, banResponse.PunishmentEntryError] as const).includes(ret) &&
!options.silent
)
this.client.emit(
@@ -615,24 +630,26 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
options.reason ?? undefined,
caseID!,
options.duration ?? 0,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
}
public override async customBlock(options: BlockOptions): Promise<BlockResponse> {
+ options = this.#optionDefaults(options);
+
const channel = this.guild.channels.resolve(options.channel);
- if (!channel || (!channel.isTextBased() && !channel.isThread())) return blockResponse.INVALID_CHANNEL;
+ if (!channel || (!channel.isTextBased() && !channel.isThread())) return blockResponse.InvalidChannel;
// checks
if (!channel.permissionsFor(this.guild.members.me!)!.has(PermissionFlagsBits.ManageChannels))
- return blockResponse.MISSING_PERMISSIONS;
+ return blockResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return blockResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return blockResponse.CannotResolveUser;
const ret = await (async () => {
// change channel permissions
@@ -641,7 +658,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
const blockSuccess = await channelToUse.permissionOverwrites
.edit(this, perm, { reason: `[Block] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` })
.catch(() => false);
- if (!blockSuccess) return blockResponse.ACTION_ERROR;
+ if (!blockSuccess) return blockResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -654,7 +671,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
evidence: options.evidence,
hidden: options.silent ?? false
});
- if (!modlog) return blockResponse.MODLOG_ERROR;
+ if (!modlog) return blockResponse.ModlogError;
caseID = modlog.id;
// add punishment entry so they can be unblocked later
@@ -667,32 +684,29 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
modlog: modlog.id,
extraInfo: channel.id
});
- if (!punishmentEntrySuccess) return blockResponse.PUNISHMENT_ENTRY_ADD_ERROR;
+ if (!punishmentEntrySuccess) return blockResponse.PunishmentEntryError;
- // dm user
- const dmSuccess = options.silent
- ? null
- : await punishDM({
- client: this.client,
- punishment: Action.Block,
- reason: options.reason ?? undefined,
- duration: options.duration ?? 0,
- modlog: modlog.id,
- guild: this.guild,
- user: this,
- sendFooter: true,
- channel: channel.id
- });
- dmSuccessEvent = !!dmSuccess;
- if (!dmSuccess) return blockResponse.DM_ERROR;
+ if (!options.noDM) {
+ // dm user
+ dmSuccess = await punishDM({
+ client: this.client,
+ punishment: Action.Block,
+ reason: options.reason ?? undefined,
+ duration: options.duration ?? 0,
+ modlog: modlog.id,
+ guild: this.guild,
+ user: this,
+ sendFooter: true,
+ channel: channel.id
+ });
+ if (dmSuccess === false) return blockResponse.DmError;
+ }
- return blockResponse.SUCCESS;
+ return blockResponse.Success;
})();
if (
- !([blockResponse.ACTION_ERROR, blockResponse.MODLOG_ERROR, blockResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(
- ret
- ) &&
+ !([blockResponse.ActionError, blockResponse.ModlogError, blockResponse.PunishmentEntryError] as const).includes(ret) &&
!options.silent
)
this.client.emit(
@@ -703,7 +717,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
options.reason ?? undefined,
caseID!,
options.duration ?? 0,
- dmSuccessEvent!,
+ dmSuccess,
channel,
options.evidence
);
@@ -711,18 +725,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
}
public override async customUnblock(options: UnblockOptions): Promise<UnblockResponse> {
+ options = this.#optionDefaults(options);
+
const _channel = this.guild.channels.resolve(options.channel);
- if (!_channel || (_channel.type !== ChannelType.GuildText && !_channel.isThread())) return unblockResponse.INVALID_CHANNEL;
+ if (!_channel || (_channel.type !== ChannelType.GuildText && !_channel.isThread())) return unblockResponse.InvalidChannel;
const channel = _channel as GuildTextBasedChannel;
// checks
if (!channel.permissionsFor(this.guild.members.me!)!.has(PermissionFlagsBits.ManageChannels))
- return unblockResponse.MISSING_PERMISSIONS;
+ return unblockResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return unblockResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return unblockResponse.CannotResolveUser;
const ret = await (async () => {
// change channel permissions
@@ -731,7 +747,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
const blockSuccess = await channelToUse.permissionOverwrites
.edit(this, perm, { reason: `[Unblock] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` })
.catch(() => false);
- if (!blockSuccess) return unblockResponse.ACTION_ERROR;
+ if (!blockSuccess) return unblockResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -744,7 +760,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
evidence: options.evidence,
hidden: options.silent ?? false
});
- if (!modlog) return unblockResponse.MODLOG_ERROR;
+ if (!modlog) return unblockResponse.ModlogError;
caseID = modlog.id;
// remove punishment entry
@@ -755,31 +771,28 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
guild: this.guild,
extraInfo: channel.id
});
- if (!punishmentEntrySuccess) return unblockResponse.ACTION_ERROR;
+ if (!punishmentEntrySuccess) return unblockResponse.ActionError;
- // dm user
- const dmSuccess = options.silent
- ? null
- : await punishDM({
- client: this.client,
- punishment: Action.Unblock,
- reason: options.reason ?? undefined,
- guild: this.guild,
- user: this,
- sendFooter: false,
- channel: channel.id
- });
- dmSuccessEvent = !!dmSuccess;
- if (!dmSuccess) return blockResponse.DM_ERROR;
+ if (!options.noDM) {
+ // dm user
+ dmSuccess = await punishDM({
+ client: this.client,
+ punishment: Action.Unblock,
+ reason: options.reason ?? undefined,
+ guild: this.guild,
+ user: this,
+ sendFooter: false,
+ channel: channel.id
+ });
- dmSuccessEvent = !!dmSuccess;
- if (!dmSuccess) return unblockResponse.DM_ERROR;
+ if (dmSuccess === false) return unblockResponse.DmError;
+ }
- return unblockResponse.SUCCESS;
+ return unblockResponse.Success;
})();
if (
- !([unblockResponse.ACTION_ERROR, unblockResponse.MODLOG_ERROR, unblockResponse.ACTION_ERROR] as const).includes(ret) &&
+ !([unblockResponse.ActionError, unblockResponse.ModlogError, unblockResponse.ActionError] as const).includes(ret) &&
!options.silent
)
this.client.emit(
@@ -789,7 +802,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
this.guild,
options.reason ?? undefined,
caseID!,
- dmSuccessEvent!,
+ dmSuccess,
channel,
options.evidence
);
@@ -797,16 +810,18 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
}
public override async customTimeout(options: CustomTimeoutOptions): Promise<TimeoutResponse> {
+ options = this.#optionDefaults(options);
+
// checks
- if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers)) return timeoutResponse.MISSING_PERMISSIONS;
+ if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers)) return timeoutResponse.MissingPermissions;
const twentyEightDays = Time.Day * 28;
- if (options.duration > twentyEightDays) return timeoutResponse.INVALID_DURATION;
+ if (options.duration > twentyEightDays) return timeoutResponse.InvalidDuration;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return timeoutResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return timeoutResponse.CannotResolveUser;
const ret = await (async () => {
// timeout
@@ -814,7 +829,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
options.duration,
`${moderator.tag} | ${options.reason ?? 'No reason provided.'}`
).catch(() => false);
- if (!timeoutSuccess) return timeoutResponse.ACTION_ERROR;
+ if (!timeoutSuccess) return timeoutResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -829,20 +844,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
hidden: options.silent ?? false
});
- if (!modlog) return timeoutResponse.MODLOG_ERROR;
+ if (!modlog) return timeoutResponse.ModlogError;
caseID = modlog.id;
- if (!options.silent) {
+ if (!options.noDM) {
// dm user
- const dmSuccess = await this.customPunishDM(Action.Timeout, options.reason, options.duration, modlog.id);
- dmSuccessEvent = dmSuccess;
- if (!dmSuccess) return timeoutResponse.DM_ERROR;
+ dmSuccess = await this.customPunishDM(Action.Timeout, options.reason, options.duration, modlog.id);
+
+ if (dmSuccess === false) return timeoutResponse.DmError;
}
- return timeoutResponse.SUCCESS;
+ return timeoutResponse.Success;
})();
- if (!([timeoutResponse.ACTION_ERROR, timeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent)
+ if (!([timeoutResponse.ActionError, timeoutResponse.ModlogError] as const).includes(ret) && !options.silent)
this.client.emit(
TanzaniteEvent.Timeout,
this,
@@ -851,28 +866,30 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
options.reason ?? undefined,
caseID!,
options.duration ?? 0,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
}
public override async customRemoveTimeout(options: CustomPunishmentOptions): Promise<RemoveTimeoutResponse> {
+ options = this.#optionDefaults(options);
+
// checks
if (!this.guild.members.me!.permissions.has(PermissionFlagsBits.ModerateMembers))
- return removeTimeoutResponse.MISSING_PERMISSIONS;
+ return removeTimeoutResponse.MissingPermissions;
- let caseID: string | undefined = undefined;
- let dmSuccessEvent: boolean | undefined = undefined;
+ let caseID: string | null = null;
+ let dmSuccess: boolean | null = null;
const moderator = await this.client.utils.resolveNonCachedUser(options.moderator ?? this.guild.members.me);
- if (!moderator) return removeTimeoutResponse.CANNOT_RESOLVE_USER;
+ if (!moderator) return removeTimeoutResponse.CannotResolveUser;
const ret = await (async () => {
// remove timeout
const timeoutSuccess = await this.timeout(null, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`).catch(
() => false
);
- if (!timeoutSuccess) return removeTimeoutResponse.ACTION_ERROR;
+ if (!timeoutSuccess) return removeTimeoutResponse.ActionError;
// add modlog entry
const { log: modlog } = await createModLogEntry({
@@ -886,20 +903,20 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
hidden: options.silent ?? false
});
- if (!modlog) return removeTimeoutResponse.MODLOG_ERROR;
+ if (!modlog) return removeTimeoutResponse.ModlogError;
caseID = modlog.id;
- if (!options.silent) {
+ if (!options.noDM) {
// dm user
- const dmSuccess = await this.customPunishDM(Action.Untimeout, options.reason, undefined, '', false);
- dmSuccessEvent = dmSuccess;
- if (!dmSuccess) return removeTimeoutResponse.DM_ERROR;
+ dmSuccess = await this.customPunishDM(Action.Untimeout, options.reason, undefined, '', false);
+
+ if (dmSuccess === false) return removeTimeoutResponse.DmError;
}
- return removeTimeoutResponse.SUCCESS;
+ return removeTimeoutResponse.Success;
})();
- if (!([removeTimeoutResponse.ACTION_ERROR, removeTimeoutResponse.MODLOG_ERROR] as const).includes(ret) && !options.silent)
+ if (!([removeTimeoutResponse.ActionError, removeTimeoutResponse.ModlogError] as const).includes(ret) && !options.silent)
this.client.emit(
TanzaniteEvent.RemoveTimeout,
this,
@@ -907,7 +924,7 @@ export class ExtendedGuildMember extends GuildMember implements Extension {
this.guild,
options.reason ?? undefined,
caseID!,
- dmSuccessEvent!,
+ dmSuccess,
options.evidence
);
return ret;
@@ -945,6 +962,12 @@ export interface CustomPunishmentOptions {
* Makes the punishment silent by not sending the user a punishment dm and not broadcasting the event to be logged.
*/
silent?: boolean;
+
+ /**
+ * Don't send a dm to the user.
+ * @default silent
+ */
+ noDM?: boolean;
}
/**
@@ -960,7 +983,7 @@ export interface CustomTimedPunishmentOptions extends CustomPunishmentOptions {
/**
* Options for a role add punishment.
*/
-export interface AddRoleOptions extends CustomTimedPunishmentOptions {
+export interface AddRoleOptions extends Omit<CustomTimedPunishmentOptions, 'noDM'> {
/**
* The role to add to the user.
*/
@@ -975,7 +998,7 @@ export interface AddRoleOptions extends CustomTimedPunishmentOptions {
/**
* Options for a role remove punishment.
*/
-export interface RemoveRoleOptions extends CustomTimedPunishmentOptions {
+export interface RemoveRoleOptions extends Omit<CustomTimedPunishmentOptions, 'noDM'> {
/**
* The role to remove from the user.
*/
@@ -1028,75 +1051,64 @@ export interface CustomTimeoutOptions extends CustomPunishmentOptions {
}
export const basePunishmentResponse = Object.freeze({
- SUCCESS: 'success',
- MODLOG_ERROR: 'error creating modlog entry',
- ACTION_ERROR: 'error performing action',
- CANNOT_RESOLVE_USER: 'cannot resolve user'
+ Success: 'success',
+ ModlogError: 'error creating modlog entry',
+ ActionError: 'error performing action',
+ CannotResolveUser: 'cannot resolve user'
} as const);
export const dmResponse = Object.freeze({
...basePunishmentResponse,
- DM_ERROR: 'failed to dm'
+ DmError: 'failed to dm'
} as const);
export const permissionsResponse = Object.freeze({
- MISSING_PERMISSIONS: 'missing permissions'
+ MissingPermissions: 'missing permissions'
} as const);
-export const punishmentEntryAdd = Object.freeze({
- PUNISHMENT_ENTRY_ADD_ERROR: 'error creating punishment entry'
-} as const);
-
-export const punishmentEntryRemove = Object.freeze({
- PUNISHMENT_ENTRY_REMOVE_ERROR: 'error removing punishment entry'
+export const punishmentEntryError = Object.freeze({
+ PunishmentEntryError: 'punishment entry error'
} as const);
export const shouldAddRoleResponse = Object.freeze({
- USER_HIERARCHY: 'user hierarchy',
- CLIENT_HIERARCHY: 'client hierarchy',
- ROLE_MANAGED: 'role managed'
+ UserHierarchy: 'user hierarchy',
+ ClientHierarchy: 'client hierarchy',
+ RoleManaged: 'role managed'
} as const);
export const baseBlockResponse = Object.freeze({
- INVALID_CHANNEL: 'invalid channel'
+ InvalidChannel: 'invalid channel'
} as const);
export const baseMuteResponse = Object.freeze({
- NO_MUTE_ROLE: 'no mute role',
- MUTE_ROLE_INVALID: 'invalid mute role',
- MUTE_ROLE_NOT_MANAGEABLE: 'mute role not manageable'
+ NoMuteRole: 'no mute role',
+ MuteRoleInvalid: 'invalid mute role',
+ MuteRoleNotManageable: 'mute role not manageable'
} as const);
export const warnResponse = Object.freeze({
...dmResponse
} as const);
-export const addRoleResponse = Object.freeze({
- ...basePunishmentResponse,
- ...permissionsResponse,
- ...shouldAddRoleResponse,
- ...punishmentEntryAdd
-} as const);
-
-export const removeRoleResponse = Object.freeze({
+export const roleResponse = Object.freeze({
...basePunishmentResponse,
...permissionsResponse,
...shouldAddRoleResponse,
- ...punishmentEntryRemove
+ ...punishmentEntryError
} as const);
export const muteResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
...baseMuteResponse,
- ...punishmentEntryAdd
+ ...punishmentEntryError
} as const);
export const unmuteResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
...baseMuteResponse,
- ...punishmentEntryRemove
+ ...punishmentEntryError
} as const);
export const kickResponse = Object.freeze({
@@ -1107,28 +1119,28 @@ export const kickResponse = Object.freeze({
export const banResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
- ...punishmentEntryAdd,
- ALREADY_BANNED: 'already banned'
+ ...punishmentEntryError,
+ AlreadyBanned: 'already banned'
} as const);
export const blockResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
...baseBlockResponse,
- ...punishmentEntryAdd
+ ...punishmentEntryError
});
export const unblockResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
...baseBlockResponse,
- ...punishmentEntryRemove
+ ...punishmentEntryError
});
export const timeoutResponse = Object.freeze({
...dmResponse,
...permissionsResponse,
- INVALID_DURATION: 'duration too long'
+ InvalidDuration: 'duration too long'
} as const);
export const removeTimeoutResponse = Object.freeze({
@@ -1144,12 +1156,12 @@ export type WarnResponse = ValueOf<typeof warnResponse>;
/**
* Response returned when adding a role to a user.
*/
-export type AddRoleResponse = ValueOf<typeof addRoleResponse>;
+export type AddRoleResponse = ValueOf<typeof roleResponse>;
/**
* Response returned when removing a role from a user.
*/
-export type RemoveRoleResponse = ValueOf<typeof removeRoleResponse>;
+export type RemoveRoleResponse = ValueOf<typeof roleResponse>;
/**
* Response returned when muting a user.
diff --git a/lib/utils/FormatResponse.ts b/lib/utils/FormatResponse.ts
index 470fea7..14583b3 100644
--- a/lib/utils/FormatResponse.ts
+++ b/lib/utils/FormatResponse.ts
@@ -1,32 +1,275 @@
-import type { GuildMember } from 'discord.js';
-import { unmuteResponse, UnmuteResponse } from '../extensions/discord.js/ExtendedGuildMember.js';
+import { unbanResponse, type UnbanResponse } from '#lib/extensions/discord.js/ExtendedGuild.js';
+import {
+ AddRoleResponse,
+ banResponse,
+ blockResponse,
+ kickResponse,
+ muteResponse,
+ removeTimeoutResponse,
+ roleResponse,
+ timeoutResponse,
+ unblockResponse,
+ unmuteResponse,
+ warnResponse,
+ WarnResponse,
+ type BanResponse,
+ type BlockResponse,
+ type KickResponse,
+ type MuteResponse,
+ type TimeoutResponse,
+ type UnblockResponse,
+ type UnmuteResponse
+} from '#lib/extensions/discord.js/ExtendedGuildMember.js';
+import type { GuildMember, Snowflake, User } from 'discord.js';
import { emojis } from './Constants.js';
import { input } from './Format.js';
-import { format } from './Utils.js';
+import { format, humanizeDuration, ordinal } from './Utils.js';
+
+export function formatBanResponse(user: User, code: BanResponse): string {
+ const victim = format.input(user.tag);
+ switch (code) {
+ case banResponse.AlreadyBanned:
+ return `${emojis.error} ${victim} is already banned.`;
+ case banResponse.MissingPermissions:
+ return `${emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`;
+ case banResponse.ActionError:
+ return `${emojis.error} An error occurred while trying to ban ${victim}.`;
+ case banResponse.PunishmentEntryError:
+ return `${emojis.error} While banning ${victim}, there was an error creating a ban entry, please report this to my developers.`;
+ case banResponse.ModlogError:
+ return `${emojis.error} While banning ${victim}, there was an error creating a modlog entry, please report this to my developers.`;
+ case banResponse.DmError:
+ return `${emojis.warn} Banned ${victim} however I could not send them a dm.`;
+ case banResponse.Success:
+ return `${emojis.success} Successfully banned ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatUnbanResponse(user: User, code: UnbanResponse): string {
+ const victim = format.input(user.tag);
+ switch (code) {
+ case unbanResponse.MissingPermissions:
+ return `${emojis.error} Could not unban ${victim} because I am missing the **Ban Members** permission.`;
+ case unbanResponse.ActionError:
+ return `${emojis.error} An error occurred while trying to unban ${victim}.`;
+ case unbanResponse.PunishmentEntryError:
+ return `${emojis.error} While unbanning ${victim}, there was an error removing their ban entry, please report this to my developers.`;
+ case unbanResponse.ModlogError:
+ return `${emojis.error} While unbanning ${victim}, there was an error creating a modlog entry, please report this to my developers.`;
+ case unbanResponse.NotBanned:
+ return `${emojis.warn} ${victim} is not banned but I tried to unban them anyways.`;
+ case unbanResponse.DmError:
+ case unbanResponse.Success:
+ return `${emojis.success} Successfully unbanned ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatBlockResponse(member: GuildMember, code: BlockResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case blockResponse.MissingPermissions:
+ return `${emojis.error} Could not block ${victim} because I am missing the **Manage Channel** permission.`;
+ case blockResponse.InvalidChannel:
+ return `${emojis.error} Could not block ${victim}, you can only block users in text or thread channels.`;
+ case blockResponse.ActionError:
+ return `${emojis.error} An unknown error occurred while trying to block ${victim}.`;
+ case blockResponse.ModlogError:
+ return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`;
+ case blockResponse.PunishmentEntryError:
+ return `${emojis.error} There was an error creating a punishment entry, please report this to my developers.`;
+ case blockResponse.DmError:
+ return `${emojis.warn} Blocked ${victim} however I could not send them a dm.`;
+ case blockResponse.Success:
+ return `${emojis.success} Successfully blocked ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatUnblockResponse(member: GuildMember, code: UnblockResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case unblockResponse.MissingPermissions:
+ return `${emojis.error} Could not unblock ${victim} because I am missing the **Manage Channel** permission.`;
+ case unblockResponse.InvalidChannel:
+ return `${emojis.error} Could not unblock ${victim}, you can only unblock users in text or thread channels.`;
+ case unblockResponse.ActionError:
+ return `${emojis.error} An unknown error occurred while trying to unblock ${victim}.`;
+ case unblockResponse.ModlogError:
+ return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`;
+ case unblockResponse.PunishmentEntryError:
+ return `${emojis.error} There was an error creating a punishment entry, please report this to my developers.`;
+ case unblockResponse.DmError:
+ return `${emojis.warn} Unblocked ${victim} however I could not send them a dm.`;
+ case unblockResponse.Success:
+ return `${emojis.success} Successfully unblocked ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatMuteResponse(prefix: string, member: GuildMember, code: MuteResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case muteResponse.MissingPermissions:
+ return `${emojis.error} Could not mute ${victim} because I am missing the **Manage Roles** permission.`;
+ case muteResponse.NoMuteRole:
+ return `${emojis.error} Could not mute ${victim}, you must set a mute role with \`${prefix}config muteRole\`.`;
+ case muteResponse.MuteRoleInvalid:
+ return `${emojis.error} Could not mute ${victim} because the current mute role no longer exists. Please set a new mute role with \`${prefix}config muteRole\`.`;
+ case muteResponse.MuteRoleNotManageable:
+ return `${emojis.error} Could not mute ${victim} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${prefix}config muteRole\`.`;
+ case muteResponse.ActionError:
+ return `${emojis.error} Could not mute ${victim}, there was an error assigning them the mute role.`;
+ case muteResponse.ModlogError:
+ return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`;
+ case muteResponse.PunishmentEntryError:
+ return `${emojis.error} There was an error creating a punishment entry, please report this to my developers.`;
+ case muteResponse.DmError:
+ return `${emojis.warn} Muted ${victim} however I could not send them a dm.`;
+ case muteResponse.Success:
+ return `${emojis.success} Successfully muted ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
export function formatUnmuteResponse(prefix: string, member: GuildMember, code: UnmuteResponse): string {
const error = emojis.error;
const victim = input(member.user.tag);
switch (code) {
- case unmuteResponse.MISSING_PERMISSIONS:
+ case unmuteResponse.MissingPermissions:
return `${error} Could not unmute ${victim} because I am missing the **Manage Roles** permission.`;
- case unmuteResponse.NO_MUTE_ROLE:
+ case unmuteResponse.NoMuteRole:
return `${error} Could not unmute ${victim}, you must set a mute role with \`${prefix}config muteRole\`.`;
- case unmuteResponse.MUTE_ROLE_INVALID:
+ case unmuteResponse.MuteRoleInvalid:
return `${error} Could not unmute ${victim} because the current mute role no longer exists. Please set a new mute role with \`${prefix}config muteRole\`.`;
- case unmuteResponse.MUTE_ROLE_NOT_MANAGEABLE:
+ case unmuteResponse.MuteRoleNotManageable:
return `${error} Could not unmute ${victim} because I cannot assign the current mute role, either change the role's position or set a new mute role with \`${prefix}config muteRole\`.`;
- case unmuteResponse.ACTION_ERROR:
+ case unmuteResponse.ActionError:
return `${error} Could not unmute ${victim}, there was an error removing their mute role.`;
- case unmuteResponse.MODLOG_ERROR:
+ case unmuteResponse.ModlogError:
return `${error} While muting ${victim}, there was an error creating a modlog entry, please report this to my developers.`;
- case unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR:
+ case unmuteResponse.PunishmentEntryError:
return `${error} While muting ${victim}, there was an error removing their mute entry, please report this to my developers.`;
- case unmuteResponse.DM_ERROR:
+ case unmuteResponse.DmError:
return `${emojis.warn} unmuted ${victim} however I could not send them a dm.`;
- case unmuteResponse.SUCCESS:
+ case unmuteResponse.Success:
return `${emojis.success} Successfully unmuted ${victim}.`;
default:
return `${emojis.error} An error occurred: ${format.input(code)}}`;
}
}
+
+export function formatTimeoutResponse(member: GuildMember, code: TimeoutResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case timeoutResponse.MissingPermissions:
+ return `${emojis.error} Could not timeout ${victim} because I am missing the **Timeout Members** permission.`;
+ case timeoutResponse.InvalidDuration:
+ return `${emojis.error} The duration you specified is too long, the longest you can timeout someone for is 28 days.`;
+ case timeoutResponse.ActionError:
+ return `${emojis.error} An unknown error occurred while trying to timeout ${victim}.`;
+ case timeoutResponse.ModlogError:
+ return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`;
+ case timeoutResponse.DmError:
+ return `${emojis.warn} Timed out ${victim} however I could not send them a dm.`;
+ case timeoutResponse.Success:
+ return `${emojis.success} Successfully timed out ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatUntimeoutResponse(member: GuildMember, code: TimeoutResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case removeTimeoutResponse.MissingPermissions:
+ return `${emojis.error} Could not untimeout ${victim} because I am missing the **Timeout Members** permission.`;
+ case removeTimeoutResponse.ActionError:
+ return `${emojis.error} An unknown error occurred while trying to timeout ${victim}.`;
+ case removeTimeoutResponse.ModlogError:
+ return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`;
+ case removeTimeoutResponse.DmError:
+ return `${emojis.warn} Removed ${victim}'s timeout however I could not send them a dm.`;
+ case removeTimeoutResponse.Success:
+ return `${emojis.success} Successfully removed ${victim}'s timeout.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatKickResponse(member: GuildMember, code: KickResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case kickResponse.MissingPermissions:
+ return `${emojis.error} Could not kick ${victim} because I am missing the **Kick Members** permission.`;
+ case kickResponse.ActionError:
+ return `${emojis.error} An error occurred while trying to kick ${victim}.`;
+ case kickResponse.ModlogError:
+ return `${emojis.error} While muting ${victim}, there was an error creating a modlog entry, please report this to my developers.`;
+ case kickResponse.DmError:
+ return `${emojis.warn} Kicked ${victim} however I could not send them a dm.`;
+ case kickResponse.Success:
+ return `${emojis.success} Successfully kicked ${victim}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatWarnResponse(caseNum: number | null, member: GuildMember, code: WarnResponse): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case warnResponse.ModlogError:
+ return `${emojis.error} While warning ${victim}, there was an error creating a modlog entry, please report this to my developers.`;
+ case warnResponse.ActionError:
+ case warnResponse.DmError:
+ return `${emojis.warn} ${victim} has been warned for the ${ordinal(
+ caseNum ?? 0
+ )} time, however I could not send them a dm.`;
+ case warnResponse.Success:
+ return `${emojis.success} Successfully warned ${victim} for the ${ordinal(caseNum ?? 0)} time.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}
+
+export function formatRoleResponse(
+ roleId: Snowflake,
+ action: 'add' | 'remove',
+ duration: number | null,
+ member: GuildMember,
+ code: AddRoleResponse
+): string {
+ const victim = format.input(member.user.tag);
+ switch (code) {
+ case roleResponse.MissingPermissions:
+ return `${emojis.error} I don't have the **Manage Roles** permission.`;
+ case roleResponse.UserHierarchy:
+ return `${emojis.error} <@&${roleId}> is higher or equal to your highest role.`;
+ case roleResponse.RoleManaged:
+ return `${emojis.error} <@&${roleId}> is managed by an integration and cannot be managed.`;
+ case roleResponse.ClientHierarchy:
+ return `${emojis.error} <@&${roleId}> is higher or equal to my highest role.`;
+ case roleResponse.ModlogError:
+ return `${emojis.error} There was an error creating a modlog entry, please report this to my developers.`;
+ case roleResponse.PunishmentEntryError:
+ return `${emojis.error} There was an error ${
+ action === 'add' ? 'creating' : 'removing'
+ } a punishment entry, please report this to my developers.`;
+ case roleResponse.ActionError:
+ return `${emojis.error} An error occurred while trying to ${action} <@&${roleId}> ${
+ action === 'add' ? 'to' : 'from'
+ } ${victim}.`;
+ case roleResponse.Success:
+ return `${emojis.success} Successfully ${action === 'add' ? 'added' : 'removed'} <@&${roleId}> ${
+ action === 'add' ? 'to' : 'from'
+ } ${victim}${duration ? ` for ${humanizeDuration(duration)}` : ''}.`;
+ default:
+ return `${emojis.error} An error occurred: ${format.input(code)}}`;
+ }
+}