aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2022-10-16 18:56:47 -0400
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2022-10-16 18:56:47 -0400
commit46d087e17a549a3b1bb9797714d5c3df440b67f5 (patch)
treea536a9dacf59e53d4288b839d80e20a392ac1190
parent2b12b94e08f4ffdbb35d6f623604657944450a82 (diff)
downloadtanzanite-46d087e17a549a3b1bb9797714d5c3df440b67f5.tar.gz
tanzanite-46d087e17a549a3b1bb9797714d5c3df440b67f5.tar.bz2
tanzanite-46d087e17a549a3b1bb9797714d5c3df440b67f5.zip
moderation changes + appeals progress
-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
-rw-r--r--src/commands/config/disable.ts18
-rw-r--r--src/commands/moderation/ban.ts30
-rw-r--r--src/commands/moderation/block.ts30
-rw-r--r--src/commands/moderation/kick.ts26
-rw-r--r--src/commands/moderation/massBan.ts2
-rw-r--r--src/commands/moderation/mute.ts34
-rw-r--r--src/commands/moderation/role.ts41
-rw-r--r--src/commands/moderation/timeout.ts30
-rw-r--r--src/commands/moderation/unban.ts32
-rw-r--r--src/commands/moderation/unblock.ts32
-rw-r--r--src/commands/moderation/untimeout.ts26
-rw-r--r--src/commands/moderation/warn.ts28
-rw-r--r--src/listeners/bush/supportThread.ts2
-rw-r--r--src/listeners/member-custom/massBan.ts2
21 files changed, 814 insertions, 652 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(op