diff options
Diffstat (limited to 'lib/common/Appeals.ts')
-rw-r--r-- | lib/common/Appeals.ts | 140 |
1 files changed, 118 insertions, 22 deletions
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, |