diff options
Diffstat (limited to 'src')
21 files changed, 515 insertions, 300 deletions
diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index f21ccdc..7f0b91f 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,5 +1,6 @@ import { AllowedMentions, + banResponse, BushCommand, Moderation, type ArgType, @@ -126,17 +127,17 @@ export default class BanCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(user.tag); switch (responseCode) { - case 'missing permissions': + case banResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`; - case 'error banning': + case banResponse.ACTION_ERROR: return `${util.emojis.error} An error occurred while trying to ban ${victim}.`; - case 'error creating ban entry': + case banResponse.PUNISHMENT_ENTRY_ADD_ERROR: return `${util.emojis.error} While banning ${victim}, there was an error creating a ban entry, please report this to my developers.`; - case 'error creating modlog entry': + case banResponse.MODLOG_ERROR: return `${util.emojis.error} While banning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; - case 'failed to dm': + case banResponse.DM_ERROR: return `${util.emojis.warn} Banned ${victim} however I could not send them a dm.`; - case 'success': + case banResponse.SUCCESS: return `${util.emojis.success} Successfully banned ${victim}.`; } }; diff --git a/src/commands/moderation/block.ts b/src/commands/moderation/block.ts index d53ffd5..57f909a 100644 --- a/src/commands/moderation/block.ts +++ b/src/commands/moderation/block.ts @@ -1,5 +1,6 @@ import { AllowedMentions, + blockResponse, BushCommand, BushTextChannel, BushThreadChannel, @@ -105,19 +106,19 @@ export default class BlockCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': + case blockResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not block ${victim} because I am missing the **Manage Channel** permission.`; - case 'invalid channel': + case blockResponse.INVALID_CHANNEL: return `${util.emojis.error} Could not block ${victim}, you can only block users in text or thread channels.`; - case 'error blocking': + case blockResponse.ACTION_ERROR: return `${util.emojis.error} An unknown error occurred while trying to block ${victim}.`; - case 'error creating modlog entry': + case blockResponse.MODLOG_ERROR: return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; - case 'error creating block entry': + case blockResponse.PUNISHMENT_ENTRY_ADD_ERROR: return `${util.emojis.error} There was an error creating a punishment entry, please report this to my developers.`; - case 'failed to dm': + case blockResponse.DM_ERROR: return `${util.emojis.warn} Blocked ${victim} however I could not send them a dm.`; - case 'success': + case blockResponse.SUCCESS: return `${util.emojis.success} Successfully blocked ${victim}.`; } }; diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index 714a2e5..cff63ed 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -7,8 +7,8 @@ export default class EvidenceCommand extends BushCommand { aliases: ['evidence'], category: 'moderation', description: 'Add evidence to a modlog case.', - usage: ['evidence <case_id> <evidence>'], - examples: ['evidence 9210b1ea-91f5-4ea2-801b-02b394469c77 was spamming in #general'], + usage: ['evidence <caseId> <evidence>'], + examples: ['evidence IgQvFpzgIKJ77mZ62TEuG was spamming in #general'], args: [ { id: 'case_id', diff --git a/src/commands/moderation/hideCase.ts b/src/commands/moderation/hideCase.ts index 04160fd..e12d8c8 100644 --- a/src/commands/moderation/hideCase.ts +++ b/src/commands/moderation/hideCase.ts @@ -6,8 +6,8 @@ export default class HideCaseCommand extends BushCommand { aliases: ['hide-case', 'hide_case', 'show-case', 'show_case', 'cover-up-mod-abuse', 'cover_up_mod_abuse'], category: 'moderation', description: 'Hide a particular modlog case from the modlog command unless the `--hidden` flag is specified', - usage: ['hide-case <case_id>'], - examples: ['hide-case 9210b1ea-91f5-4ea2-801b-02b394469c77'], + usage: ['hide-case <caseId>'], + examples: ['hide-case Xurm---HdRyHlrKLsOcIO'], args: [ { id: 'case_id', diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 6d96ae9..af486ac 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -1,4 +1,12 @@ -import { AllowedMentions, BushCommand, Moderation, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + kickResponse, + Moderation, + type ArgType, + type BushMessage, + type BushSlashMessage +} from '#lib'; export default class KickCommand extends BushCommand { public constructor() { @@ -68,15 +76,15 @@ export default class KickCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': - return `${util.emojis.error} Could not kick ${victim} because I am missing the \`Kick Members\` permission.`; - case 'error kicking': + case kickResponse.MISSING_PERMISSIONS: + return `${util.emojis.error} Could not kick ${victim} because I am missing the **Kick Members** permission.`; + case kickResponse.ACTION_ERROR: return `${util.emojis.error} An error occurred while trying to kick ${victim}.`; - case 'error creating modlog entry': + case kickResponse.MODLOG_ERROR: return `${util.emojis.error} While muting ${victim}, there was an error creating a modlog entry, please report this to my developers.`; - case 'failed to dm': + case kickResponse.DM_ERROR: return `${util.emojis.warn} Kicked ${victim} however I could not send them a dm.`; - case 'success': + case kickResponse.SUCCESS: return `${util.emojis.success} Successfully kicked ${victim}.`; } }; diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index e731e30..40e13e7 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -2,6 +2,7 @@ import { AllowedMentions, BushCommand, Moderation, + muteResponse, type ArgType, type BushMessage, type BushSlashMessage, @@ -99,23 +100,23 @@ export default class MuteCommand extends BushCommand { const prefix = util.prefix(message); const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': + case muteResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not mute ${victim} because I am missing the **Manage Roles** permission.`; - case 'no mute role': + case muteResponse.NO_MUTE_ROLE: return `${util.emojis.error} Could not mute ${victim}, you must set a mute role with \`${prefix}config muteRole\`.`; - case 'invalid mute role': + case muteResponse.MUTE_ROLE_INVALID: return `${util.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 'mute role not manageable': + case muteResponse.MUTE_ROLE_NOT_MANAGEABLE: return `${util.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 'error giving mute role': + case muteResponse.ACTION_ERROR: return `${util.emojis.error} Could not mute ${victim}, there was an error assigning them the mute role.`; - case 'error creating modlog entry': + case muteResponse.MODLOG_ERROR: return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; - case 'error creating mute entry': + case muteResponse.PUNISHMENT_ENTRY_ADD_ERROR: return `${util.emojis.error} There was an error creating a punishment entry, please report this to my developers.`; - case 'failed to dm': + case muteResponse.DM_ERROR: return `${util.emojis.warn} Muted ${victim} however I could not send them a dm.`; - case 'success': + case muteResponse.SUCCESS: return `${util.emojis.success} Successfully muted ${victim}.`; } }; diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index 6cb8d2f..f0d0448 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -1,4 +1,13 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib'; +import { + addRoleResponse, + AllowedMentions, + BushCommand, + removeRoleResponse, + type ArgType, + type BushMessage, + type BushSlashMessage, + type OptionalArgType +} from '#lib'; import { type ArgumentOptions, type Flag } from 'discord-akairo'; import { type Snowflake } from 'discord.js'; @@ -170,25 +179,26 @@ export default class RoleCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(args.member.user.tag); switch (responseCode) { - case 'user hierarchy': + case addRoleResponse.MISSING_PERMISSIONS: + return `${util.emojis.error} I don't have the **Manage Roles** permission.`; + case addRoleResponse.USER_HIERARCHY: return `${util.emojis.error} <@&${args.role.id}> is higher or equal to your highest role.`; - case 'role managed': + case addRoleResponse.ROLE_MANAGED: return `${util.emojis.error} <@&${args.role.id}> is managed by an integration and cannot be managed.`; - case 'client hierarchy': + case addRoleResponse.CLIENT_HIERARCHY: return `${util.emojis.error} <@&${args.role.id}> is higher or equal to my highest role.`; - case 'error creating modlog entry': + case addRoleResponse.MODLOG_ERROR: return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; - case 'error creating role entry': - case 'error removing role entry': + case addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR: + case removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: return `${util.emojis.error} There was an error ${ args.action === 'add' ? 'creating' : 'removing' } a punishment entry, please report this to my developers.`; - case 'error adding role': - case 'error removing role': + case addRoleResponse.ACTION_ERROR: return `${util.emojis.error} An error occurred while trying to ${args.action} <@&${args.role.id}> ${ args.action === 'add' ? 'to' : 'from' } ${victim}.`; - case 'success': + case addRoleResponse.SUCCESS: return `${util.emojis.success} Successfully ${args.action === 'add' ? 'added' : 'removed'} <@&${args.role.id}> ${ args.action === 'add' ? 'to' : 'from' } ${victim}${args.duration ? ` for ${util.humanizeDuration(args.duration)}` : ''}.`; diff --git a/src/commands/moderation/timeout.ts b/src/commands/moderation/timeout.ts index 3968bde..ec79271 100644 --- a/src/commands/moderation/timeout.ts +++ b/src/commands/moderation/timeout.ts @@ -1,4 +1,12 @@ -import { AllowedMentions, BushCommand, Moderation, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { + AllowedMentions, + BushCommand, + Moderation, + timeoutResponse, + type ArgType, + type BushMessage, + type BushSlashMessage +} from '#lib'; import assert from 'assert'; export default class TimeoutCommand extends BushCommand { @@ -81,17 +89,17 @@ export default class TimeoutCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': + case timeoutResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not timeout ${victim} because I am missing the **Timeout Members** permission.`; - case 'duration too long': + case timeoutResponse.INVALID_DURATION: return `${util.emojis.error} The duration you specified is too long, the longest you can timeout someone for is 28 days.`; - case 'error timing out': + case timeoutResponse.ACTION_ERROR: return `${util.emojis.error} An unknown error occurred while trying to timeout ${victim}.`; - case 'error creating modlog entry': + case timeoutResponse.MODLOG_ERROR: return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; - case 'failed to dm': + case timeoutResponse.DM_ERROR: return `${util.emojis.warn} Timed out ${victim} however I could not send them a dm.`; - case 'success': + case timeoutResponse.SUCCESS: return `${util.emojis.success} Successfully timed out ${victim}.`; } }; diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts index bbce4e0..c939792 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/unban.ts @@ -1,4 +1,12 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib'; +import { + AllowedMentions, + BushCommand, + unbanResponse, + type ArgType, + type BushMessage, + type BushSlashMessage, + type OptionalArgType +} from '#lib'; export default class UnbanCommand extends BushCommand { public constructor() { @@ -47,17 +55,18 @@ export default class UnbanCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(user.tag); switch (responseCode) { - case 'missing permissions': + case unbanResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not unban ${victim} because I am missing the **Ban Members** permission.`; - case 'error unbanning': + case unbanResponse.ACTION_ERROR: return `${util.emojis.error} An error occurred while trying to unban ${victim}.`; - case 'error removing ban entry': + case unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: return `${util.emojis.error} While unbanning ${victim}, there was an error removing their ban entry, please report this to my developers.`; - case 'error creating modlog entry': + case unbanResponse.MODLOG_ERROR: return `${util.emojis.error} While unbanning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; - case 'user not banned': + case unbanResponse.NOT_BANNED: return `${util.emojis.warn} ${victim} is not banned but I tried to unban them anyways.`; - case 'success': + case unbanResponse.DM_ERROR: + case unbanResponse.SUCCESS: return `${util.emojis.success} Successfully unbanned ${victim}.`; } }; diff --git a/src/commands/moderation/unblock.ts b/src/commands/moderation/unblock.ts index 4cc8b49..454de8b 100644 --- a/src/commands/moderation/unblock.ts +++ b/src/commands/moderation/unblock.ts @@ -4,6 +4,7 @@ import { BushTextChannel, BushThreadChannel, Moderation, + unblockResponse, type ArgType, type BushMessage, type BushSlashMessage, @@ -87,19 +88,19 @@ export default class UnblockCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': + case unblockResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not unblock ${victim} because I am missing the **Manage Channel** permission.`; - case 'invalid channel': + case unblockResponse.INVALID_CHANNEL: return `${util.emojis.error} Could not unblock ${victim}, you can only unblock users in text or thread channels.`; - case 'error unblocking': + case unblockResponse.ACTION_ERROR: return `${util.emojis.error} An unknown error occurred while trying to unblock ${victim}.`; - case 'error creating modlog entry': + case unblockResponse.MODLOG_ERROR: return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; - case 'error removing block entry': + case unblockResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: return `${util.emojis.error} There was an error creating a punishment entry, please report this to my developers.`; - case 'failed to dm': + case unblockResponse.DM_ERROR: return `${util.emojis.warn} Unblocked ${victim} however I could not send them a dm.`; - case 'success': + case unblockResponse.SUCCESS: return `${util.emojis.success} Successfully unblocked ${victim}.`; } }; diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index 631f213..3bd399f 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -2,6 +2,7 @@ import { AllowedMentions, BushCommand, Moderation, + unmuteResponse, type ArgType, type BushGuildMember, type BushMessage, @@ -79,23 +80,23 @@ export default class UnmuteCommand extends BushCommand { const prefix = util.prefix(message); const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': + case unmuteResponse.MISSING_PERMISSIONS: return `${error} Could not unmute ${victim} because I am missing the **Manage Roles** permission.`; - case 'no mute role': + case unmuteResponse.NO_MUTE_ROLE: return `${error} Could not unmute ${victim}, you must set a mute role with \`${prefix}config muteRole\`.`; - case 'invalid mute role': + case unmuteResponse.MUTE_ROLE_INVALID: 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 'mute role not manageable': + case unmuteResponse.MUTE_ROLE_NOT_MANAGEABLE: 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 'error removing mute role': + case unmuteResponse.ACTION_ERROR: return `${error} Could not unmute ${victim}, there was an error removing their mute role.`; - case 'error creating modlog entry': + case unmuteResponse.MODLOG_ERROR: return `${error} While muting ${victim}, there was an error creating a modlog entry, please report this to my developers.`; - case 'error removing mute entry': + case unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR: return `${error} While muting ${victim}, there was an error removing their mute entry, please report this to my developers.`; - case 'failed to dm': + case unmuteResponse.DM_ERROR: return `${util.emojis.warn} unmuted ${victim} however I could not send them a dm.`; - case 'success': + case unmuteResponse.SUCCESS: return `${util.emojis.success} Successfully unmuted ${victim}.`; } }; diff --git a/src/commands/moderation/untimeout.ts b/src/commands/moderation/untimeout.ts index b5ea5be..c5518b4 100644 --- a/src/commands/moderation/untimeout.ts +++ b/src/commands/moderation/untimeout.ts @@ -2,6 +2,7 @@ import { AllowedMentions, BushCommand, Moderation, + removeTimeoutResponse, type ArgType, type BushMessage, type BushSlashMessage, @@ -81,18 +82,16 @@ export default class UntimeoutCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(member.user.tag); switch (responseCode) { - case 'missing permissions': - return `${util.emojis.error} Could not timeout ${victim} because I am missing the **Timeout Members** permission.`; - case 'duration too long': - return `${util.emojis.error} The duration you specified is too long, the longest you can timeout someone for is 28 days.`; - case 'error removing timeout': + case removeTimeoutResponse.MISSING_PERMISSIONS: + return `${util.emojis.error} Could not untimeout ${victim} because I am missing the **Timeout Members** permission.`; + case removeTimeoutResponse.ACTION_ERROR: return `${util.emojis.error} An unknown error occurred while trying to timeout ${victim}.`; - case 'error creating modlog entry': + case removeTimeoutResponse.MODLOG_ERROR: return `${util.emojis.error} There was an error creating a modlog entry, please report this to my developers.`; - case 'failed to dm': - return `${util.emojis.warn} Timed out ${victim} however I could not send them a dm.`; - case 'success': - return `${util.emojis.success} Successfully timed out ${victim}.`; + case removeTimeoutResponse.DM_ERROR: + return `${util.emojis.warn} Removed ${victim}'s timeout however I could not send them a dm.`; + case removeTimeoutResponse.SUCCESS: + return `${util.emojis.success} Successfully removed ${victim}'s timeout.`; } }; return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() }); diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 05b1e36..aebf300 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -2,6 +2,7 @@ import { AllowedMentions, BushCommand, Moderation, + warnResponse, type ArgType, type BushGuildMember, type BushMessage, @@ -75,13 +76,14 @@ export default class WarnCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(member.user.tag); switch (response) { - case 'error creating modlog entry': + case warnResponse.MODLOG_ERROR: return `${util.emojis.error} While warning ${victim}, there was an error creating a modlog entry, please report this to my developers.`; - case 'failed to dm': + case warnResponse.ACTION_ERROR: + case warnResponse.DM_ERROR: return `${util.emojis.warn} ${victim} has been warned for the ${util.ordinal( caseNum ?? 0 )} time, however I could not send them a dm.`; - case 'success': + case warnResponse.SUCCESS: return `${util.emojis.success} Successfully warned ${victim} for the ${util.ordinal(caseNum ?? 0)} time.`; } }; diff --git a/src/lib/common/util/Moderation.ts b/src/lib/common/util/Moderation.ts index a09930b..9757f25 100644 --- a/src/lib/common/util/Moderation.ts +++ b/src/lib/common/util/Moderation.ts @@ -3,12 +3,14 @@ import { ActivePunishmentType, Guild, ModLog, + type BushGuild, type BushGuildMember, type BushGuildMemberResolvable, type BushGuildResolvable, + type BushUserResolvable, type ModLogType } from '#lib'; -import { type Snowflake } from 'discord.js'; +import { MessageEmbed, type Snowflake } from 'discord.js'; /** * A utility class with moderation-related methods. @@ -197,6 +199,28 @@ export class Moderation { }; return typeMap[type]; } + + public static async punishDM(options: PunishDMOptions): Promise<boolean> { + const ending = await options.guild.getSetting('punishmentEnding'); + const dmEmbed = + ending && ending.length && options.sendFooter + ? new MessageEmbed().setDescription(ending).setColor(util.colors.newBlurple) + : undefined; + + const dmSuccess = await client.users + .send(options.user, { + content: `You have been ${options.punishment} in **${options.guild.name}** ${ + options.duration !== null && options.duration !== undefined + ? options.duration + ? `for ${util.humanizeDuration(options.duration)} ` + : 'permanently ' + : '' + }for **${options.reason?.trim() ? options.reason?.trim() : 'No reason provided'}**.`, + embeds: dmEmbed ? [dmEmbed] : undefined + }) + .catch(() => false); + return !!dmSuccess; + } } /** @@ -303,3 +327,39 @@ export interface RemovePunishmentEntryOptions { */ extraInfo?: Snowflake; } + +/** + * Options for sending a user a punishment dm. + */ +export interface PunishDMOptions { + /** + * The guild that the punishment is taking place in. + */ + guild: BushGuild; + + /** + * The user that is being punished. + */ + user: BushUserResolvable; + + /** + * The punishment that the user has received. + */ + punishment: string; + + /** + * The reason the user's punishment. + */ + reason?: string; + + /** + * The duration of the punishment. + */ + duration?: number; + + /** + * Whether or not to send the guild's punishment footer with the dm. + * @default true + */ + sendFooter: boolean; +} diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index 47a3717..d7416bb 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -2,7 +2,6 @@ import { Arg, BushConstants, Global, - GlobalCache, type BushClient, type BushInspectOptions, type BushMessage, @@ -11,14 +10,15 @@ import { type BushSlashSendMessageType, type BushUser, type CodeBlockLang, + type GlobalCache, type Pronoun, type PronounCode } from '#lib'; +import type { APIMessage } from '@discordjs/builders/node_modules/discord-api-types'; import { humanizeDuration } from '@notenoughupdates/humanize-duration'; import { exec } from 'child_process'; import deepLock from 'deep-lock'; import { ClientUtil, Util as AkairoUtil } from 'discord-akairo'; -import { APIMessage } from 'discord-api-types'; import { Constants as DiscordConstants, GuildMember, diff --git a/src/lib/extensions/discord.js/BushButtonInteraction.ts b/src/lib/extensions/discord.js/BushButtonInteraction.ts index 2bc1c25..191ad5c 100644 --- a/src/lib/extensions/discord.js/BushButtonInteraction.ts +++ b/src/lib/extensions/discord.js/BushButtonInteraction.ts @@ -1,5 +1,5 @@ import type { BushClient, BushGuild, BushGuildMember, BushGuildTextBasedChannel, BushTextBasedChannel, BushUser } from '#lib'; -import type { APIInteractionGuildMember } from 'discord-api-types/v9'; +import type { APIInteractionGuildMember } from '@discordjs/builders/node_modules/discord-api-types/payloads/v9/_interactions/base'; import { ButtonInteraction, type CacheType, type CacheTypeReducer } from 'discord.js'; import type { RawMessageButtonInteractionData } from 'discord.js/typings/rawDataTypes'; diff --git a/src/lib/extensions/discord.js/BushClientEvents.d.ts b/src/lib/extensions/discord.js/BushClientEvents.d.ts index 16eccd4..b5ad749 100644 --- a/src/lib/extensions/discord.js/BushClientEvents.d.ts +++ b/src/lib/extensions/discord.js/BushClientEvents.d.ts @@ -173,7 +173,7 @@ export interface BushClientEvents extends AkairoClientEvents { evidence?: string ]; bushBlock: [ - victim: BushGuildMember | BushUser, + victim: BushGuildMember, moderator: BushUser, guild: BushGuild, reason: string | undefined, diff --git a/src/lib/extensions/discord.js/BushCommandInteraction.ts b/src/lib/extensions/discord.js/BushCommandInteraction.ts index 5f32829..f4be5ed 100644 --- a/src/lib/extensions/discord.js/BushCommandInteraction.ts +++ b/src/lib/extensions/discord.js/BushCommandInteraction.ts @@ -10,7 +10,7 @@ import type { BushTextBasedChannel, BushUser } from '#lib'; -import type { APIInteractionGuildMember } from 'discord-api-types/v9'; +import type { APIInteractionGuildMember } from '@discordjs/builders/node_modules/discord-api-types'; import { CommandInteraction, type CacheType, type CacheTypeReducer, type Invite, type Snowflake } from 'discord.js'; import type { RawCommandInteractionData } from 'discord.js/typings/rawDataTypes'; diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index ea8b67e..0011ce6 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -1,26 +1,23 @@ -import type { - BushClient, - BushGuildMember, - BushGuildMemberManager, - BushGuildMemberResolvable, - BushNewsChannel, - BushTextChannel, - BushThreadChannel, - BushUser, - BushUserResolvable, - GuildFeatures, - GuildLogType, - GuildModel -} from '#lib'; import { - Collection, - Guild, - GuildChannelManager, - Snowflake, - type MessageOptions, - type MessagePayload, - type UserResolvable -} from 'discord.js'; + banResponse, + dmResponse, + permissionsResponse, + punishmentEntryRemove, + type BanResponse, + type BushClient, + type BushGuildMember, + type BushGuildMemberManager, + type BushGuildMemberResolvable, + type BushNewsChannel, + type BushTextChannel, + type BushThreadChannel, + type BushUser, + type BushUserResolvable, + type GuildFeatures, + type GuildLogType, + type GuildModel +} from '#lib'; +import { Collection, Guild, Snowflake, type GuildChannelManager, type MessageOptions, type MessagePayload } from 'discord.js'; import type { RawGuildData } from 'discord.js/typings/rawDataTypes'; import _ from 'lodash'; import { Moderation } from '../../common/util/Moderation.js'; @@ -157,16 +154,25 @@ export class BushGuild extends Guild { * @param options Options for banning the user. * @returns A string status message of the ban. */ - public async bushBan(options: GuildBushBanOptions): Promise<GuildBanResponse> { + public async bushBan(options: GuildBushBanOptions): Promise<BanResponse> { // checks - if (!this.me!.permissions.has('BAN_MEMBERS')) return 'missing permissions'; + if (!this.me!.permissions.has('BAN_MEMBERS')) return banResponse.MISSING_PERMISSIONS; let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; const user = (await util.resolveNonCachedUser(options.user))!; - const moderator = (await util.resolveNonCachedUser(options.moderator!)) ?? client.user!; + const moderator = client.users.resolve(options.moderator ?? client.user!)!; const ret = await (async () => { - await this.members.cache.get(user.id)?.bushPunishDM('banned', options.reason, options.duration ?? 0); + // dm user + dmSuccessEvent = await Moderation.punishDM({ + guild: this, + user: user, + punishment: 'banned', + duration: options.duration ?? 0, + reason: options.reason ?? undefined, + sendFooter: true + }); // ban const banSuccess = await this.bans @@ -175,7 +181,7 @@ export class BushGuild extends Guild { days: options.deleteDays }) .catch(() => false); - if (!banSuccess) return 'error banning'; + if (!banSuccess) return banResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -187,7 +193,7 @@ export class BushGuild extends Guild { guild: this, evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return banResponse.MODLOG_ERROR; caseID = modlog.id; // add punishment entry so they can be unbanned later @@ -198,13 +204,24 @@ export class BushGuild extends Guild { duration: options.duration, modlog: modlog.id }); - if (!punishmentEntrySuccess) return 'error creating ban entry'; + if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; - return 'success'; + if (!dmSuccessEvent) return banResponse.DM_ERROR; + return banResponse.SUCCESS; })(); - if (!['error banning', 'error creating modlog entry', 'error creating ban entry'].includes(ret)) - client.emit('bushBan', user, moderator, this, options.reason ?? undefined, caseID!, options.duration ?? 0); + if (!([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret)) + client.emit( + 'bushBan', + user, + moderator, + this, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent, + options.evidence + ); return ret; } @@ -213,11 +230,14 @@ export class BushGuild extends Guild { * @param options Options for unbanning the user. * @returns A status message of the unban. */ - public async bushUnban(options: GuildBushUnbanOptions): Promise<GuildUnbanResponse> { + public async bushUnban(options: GuildBushUnbanOptions): Promise<UnbanResponse> { + // checks + if (!this.me!.permissions.has('BAN_MEMBERS')) return unbanResponse.MISSING_PERMISSIONS; + let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; const user = (await util.resolveNonCachedUser(options.user))!; - const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.me))!; + const moderator = client.users.resolve(options.moderator ?? client.user!)!; const ret = await (async () => { const bans = await this.bans.fetch(); @@ -234,8 +254,8 @@ export class BushGuild extends Guild { } else return false; }); - if (notBanned) return 'user not banned'; - if (!unbanSuccess) return 'error unbanning'; + if (notBanned) return unbanResponse.NOT_BANNED; + if (!unbanSuccess) return unbanResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -243,9 +263,10 @@ export class BushGuild extends Guild { user: user.id, moderator: moderator.id, reason: options.reason, - guild: this + guild: this, + evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return unbanResponse.MODLOG_ERROR; caseID = modlog.id; // remove punishment entry @@ -254,23 +275,26 @@ export class BushGuild extends Guild { user: user.id, guild: this }); - if (!removePunishmentEntrySuccess) return 'error removing ban entry'; + if (!removePunishmentEntrySuccess) return unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; - const userObject = client.users.cache.get(user.id); - - const dmSuccess = await userObject - ?.send( - `You have been unbanned from **${util.discord.escapeMarkdown(this.toString())}** for **${ - options.reason ?? 'No reason provided' - }**.` - ) - .catch(() => false); - dmSuccessEvent = !!dmSuccess; + // dm user + dmSuccessEvent = await Moderation.punishDM({ + guild: this, + user: user, + punishment: 'unbanned', + reason: options.reason ?? undefined, + sendFooter: false + }); - return 'success'; + if (!dmSuccessEvent) return unbanResponse.DM_ERROR; + return unbanResponse.SUCCESS; })(); - if (!['error unbanning', 'error creating modlog entry', 'error removing ban entry'].includes(ret)) - client.emit('bushUnban', user, moderator, this, options.reason ?? undefined, caseID!, dmSuccessEvent!); + if ( + !([unbanResponse.ACTION_ERROR, unbanResponse.MODLOG_ERROR, unbanResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const).includes( + ret + ) + ) + client.emit('bushUnban', user, moderator, this, options.reason ?? undefined, caseID!, dmSuccessEvent!, options.evidence); return ret; } @@ -367,6 +391,11 @@ export interface GuildBushUnbanOptions { * The moderator who unbanned the user */ moderator?: BushUserResolvable; + + /** + * The evidence for the unban + */ + evidence?: string; } /** @@ -376,7 +405,7 @@ export interface GuildBushBanOptions { /** * The user to ban */ - user: BushUserResolvable | UserResolvable; + user: BushUserResolvable; /** * The reason to ban the user @@ -404,17 +433,19 @@ export interface GuildBushBanOptions { evidence?: string; } -export type GuildPunishmentResponse = 'success' | 'missing permissions' | 'error creating modlog entry'; +type ValueOf<T> = T[keyof T]; -/** - * Response returned when banning a user - */ -export type GuildBanResponse = GuildPunishmentResponse | 'error banning' | 'error creating ban entry'; +export const unbanResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...punishmentEntryRemove, + NOT_BANNED: 'user not banned' +} as const); /** * Response returned when unbanning a user */ -export type GuildUnbanResponse = GuildPunishmentResponse | 'user not banned' | 'error unbanning' | 'error removing ban entry'; +export type UnbanResponse = ValueOf<typeof unbanResponse>; /** * Options for locking down channel(s) diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts index e7df926..8ff1943 100644 --- a/src/lib/extensions/discord.js/BushGuildMember.ts +++ b/src/lib/extensions/discord.js/BushGuildMember.ts @@ -11,7 +11,7 @@ import { type BushThreadChannelResolvable, type BushUser } from '#lib'; -import { GuildMember, MessageEmbed, type Partialize, type Role } from 'discord.js'; +import { GuildMember, type Partialize, type Role } from 'discord.js'; import type { RawGuildMemberData } from 'discord.js/typings/rawDataTypes'; /* eslint-enable @typescript-eslint/no-unused-vars */ @@ -30,25 +30,13 @@ export class BushGuildMember extends GuildMember { /** * Send a punishment dm to the user. * @param punishment The punishment that the user has received. - * @param reason The reason the user to be punished. + * @param reason The reason for the user's punishment. * @param duration The duration of the punishment. * @param sendFooter Whether or not to send the guild's punishment footer with the dm. * @returns Whether or not the dm was sent successfully. */ public async bushPunishDM(punishment: string, reason?: string | null, duration?: number, sendFooter = true): Promise<boolean> { - const ending = await this.guild.getSetting('punishmentEnding'); - const dmEmbed = - ending && ending.length && sendFooter - ? new MessageEmbed().setDescription(ending).setColor(util.colors.newBlurple) - : undefined; - const dmSuccess = await this.send({ - content: `You have been ${punishment} in **${this.guild.name}** ${ - duration !== null && duration !== undefined ? (duration ? `for ${util.humanizeDuration(duration)} ` : 'permanently ') : '' - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - }for **${reason?.trim() || 'No reason provided'}**.`, - embeds: dmEmbed ? [dmEmbed] : undefined - }).catch(() => false); - return !!dmSuccess; + return Moderation.punishDM({ guild: this.guild, user: this, punishment, reason: reason ?? undefined, duration, sendFooter }); } /** @@ -76,16 +64,16 @@ export class BushGuildMember extends GuildMember { true ); caseID = result.log?.id; - if (!result || !result.log) return { result: 'error creating modlog entry', caseNum: null }; + if (!result || !result.log) return { result: warnResponse.MODLOG_ERROR, caseNum: null }; // dm user const dmSuccess = await this.bushPunishDM('warned', options.reason); dmSuccessEvent = dmSuccess; - if (!dmSuccess) return { result: 'failed to dm', caseNum: result.caseNum }; + if (!dmSuccess) return { result: warnResponse.DM_ERROR, caseNum: result.caseNum }; - return { result: 'success', caseNum: result.caseNum }; + return { result: warnResponse.SUCCESS, caseNum: result.caseNum }; })(); - if (!(['error creating modlog entry'] as const).includes(ret.result)) + if (!([warnResponse.MODLOG_ERROR] as const).includes(ret.result)) client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); return ret; } @@ -97,6 +85,8 @@ export class BushGuildMember extends GuildMember { * @emits {@link BushClientEvents.bushPunishRole} */ public async bushAddRole(options: AddRoleOptions): Promise<AddRoleResponse> { + // checks + if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return addRoleResponse.MISSING_PERMISSIONS; const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator); if (ifShouldAddRole !== true) return ifShouldAddRole; @@ -115,7 +105,7 @@ export class BushGuildMember extends GuildMember { evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return addRoleResponse.MODLOG_ERROR; caseID = modlog.id; if (options.addToModlog || options.duration) { @@ -127,17 +117,19 @@ export class BushGuildMember extends GuildMember { duration: options.duration, extraInfo: options.role.id }); - if (!punishmentEntrySuccess) return 'error creating role entry'; + if (!punishmentEntrySuccess) return addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR; } } const removeRoleSuccess = await this.roles.add(options.role, `${moderator.tag}`); - if (!removeRoleSuccess) return 'error adding role'; + if (!removeRoleSuccess) return addRoleResponse.ACTION_ERROR; - return 'success'; + return addRoleResponse.SUCCESS; })(); if ( - !(['error adding role', 'error creating modlog entry', 'error creating role entry'] as const).includes(ret) && + !( + [addRoleResponse.ACTION_ERROR, addRoleResponse.MODLOG_ERROR, addRoleResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const + ).includes(ret) && options.addToModlog ) client.emit( @@ -161,6 +153,8 @@ export class BushGuildMember extends GuildMember { * @emits {@link BushClientEvents.bushPunishRoleRemove} */ public async bushRemoveRole(options: RemoveRoleOptions): Promise<RemoveRoleResponse> { + // checks + if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return removeRoleResponse.MISSING_PERMISSIONS; const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator); if (ifShouldAddRole !== true) return ifShouldAddRole; @@ -178,7 +172,7 @@ export class BushGuildMember extends GuildMember { evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return removeRoleResponse.MODLOG_ERROR; caseID = modlog.id; const punishmentEntrySuccess = await Moderation.removePunishmentEntry({ @@ -188,17 +182,23 @@ export class BushGuildMember extends GuildMember { extraInfo: options.role.id }); - if (!punishmentEntrySuccess) return 'error removing role entry'; + if (!punishmentEntrySuccess) return removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; } const removeRoleSuccess = await this.roles.remove(options.role, `${moderator.tag}`); - if (!removeRoleSuccess) return 'error removing role'; + if (!removeRoleSuccess) return removeRoleResponse.ACTION_ERROR; - return 'success'; + return removeRoleResponse.SUCCESS; })(); if ( - !(['error removing role', 'error creating modlog entry', 'error removing role entry'] as const).includes(ret) && + !( + [ + removeRoleResponse.ACTION_ERROR, + removeRoleResponse.MODLOG_ERROR, + removeRoleResponse.PUNISHMENT_ENTRY_REMOVE_ERROR + ] as const + ).includes(ret) && options.addToModlog ) client.emit( @@ -225,12 +225,11 @@ export class BushGuildMember extends GuildMember { moderator?: BushGuildMember ): true | 'user hierarchy' | 'role managed' | 'client hierarchy' { if (moderator && moderator.roles.highest.position <= role.position && this.guild.ownerId !== this.user.id) { - client.console.debug(`${this.roles.highest.position} <= ${role.position}`); - return 'user hierarchy'; + return shouldAddRoleResponse.USER_HIERARCHY; } else if (role.managed) { - return 'role managed'; + return shouldAddRoleResponse.ROLE_MANAGED; } else if (this.guild.me!.roles.highest.position <= role.position) { - return 'client hierarchy'; + return shouldAddRoleResponse.CLIENT_HIERARCHY; } return true; } @@ -243,12 +242,13 @@ export class BushGuildMember extends GuildMember { */ public async bushMute(options: BushTimedPunishmentOptions): Promise<MuteResponse> { // checks - if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return 'missing permissions'; + if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return muteResponse.MISSING_PERMISSIONS; const muteRoleID = await this.guild.getSetting('muteRole'); - if (!muteRoleID) return 'no mute role'; + if (!muteRoleID) return muteResponse.NO_MUTE_ROLE; const muteRole = this.guild.roles.cache.get(muteRoleID); - if (!muteRole) return 'invalid mute role'; - if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) return 'mute role not manageable'; + if (!muteRole) return muteResponse.MUTE_ROLE_INVALID; + if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) + return muteResponse.MUTE_ROLE_NOT_MANAGEABLE; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -263,7 +263,7 @@ export class BushGuildMember extends GuildMember { client.console.debug(e); return false; }); - if (!muteSuccess) return 'error giving mute role'; + if (!muteSuccess) return muteResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -276,7 +276,7 @@ export class BushGuildMember extends GuildMember { evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return muteResponse.MODLOG_ERROR; caseID = modlog.id; // add punishment entry so they can be unmuted later @@ -288,18 +288,18 @@ export class BushGuildMember extends GuildMember { modlog: modlog.id }); - if (!punishmentEntrySuccess) return 'error creating mute entry'; + if (!punishmentEntrySuccess) return muteResponse.PUNISHMENT_ENTRY_ADD_ERROR; // dm user const dmSuccess = await this.bushPunishDM('muted', options.reason, options.duration ?? 0); dmSuccessEvent = dmSuccess; - if (!dmSuccess) return 'failed to dm'; + if (!dmSuccess) return muteResponse.DM_ERROR; - return 'success'; + return muteResponse.SUCCESS; })(); - if (!(['error giving mute role', 'error creating modlog entry', 'error creating mute entry'] as const).includes(ret)) + if (!([muteResponse.ACTION_ERROR, muteResponse.MODLOG_ERROR, muteResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret)) client.emit( 'bushMute', this, @@ -322,12 +322,13 @@ export class BushGuildMember extends GuildMember { */ public async bushUnmute(options: BushPunishmentOptions): Promise<UnmuteResponse> { // checks - if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return 'missing permissions'; + if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return unmuteResponse.MISSING_PERMISSIONS; const muteRoleID = await this.guild.getSetting('muteRole'); - if (!muteRoleID) return 'no mute role'; + if (!muteRoleID) return unmuteResponse.NO_MUTE_ROLE; const muteRole = this.guild.roles.cache.get(muteRoleID); - if (!muteRole) return 'invalid mute role'; - if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) return 'mute role not manageable'; + if (!muteRole) return unmuteResponse.MUTE_ROLE_INVALID; + if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) + return unmuteResponse.MUTE_ROLE_NOT_MANAGEABLE; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -341,7 +342,7 @@ export class BushGuildMember extends GuildMember { await client.console.warn('muteRoleAddError', e?.stack || e); return false; }); - if (!muteSuccess) return 'error removing mute role'; + if (!muteSuccess) return unmuteResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -353,7 +354,7 @@ export class BushGuildMember extends GuildMember { evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return unmuteResponse.MODLOG_ERROR; caseID = modlog.id; // remove mute entry @@ -363,18 +364,22 @@ export class BushGuildMember extends GuildMember { guild: this.guild }); - if (!removePunishmentEntrySuccess) return 'error removing mute entry'; + if (!removePunishmentEntrySuccess) return unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR; // dm user const dmSuccess = await this.bushPunishDM('unmuted', options.reason, undefined, false); dmSuccessEvent = dmSuccess; - if (!dmSuccess) return 'failed to dm'; + if (!dmSuccess) return unmuteResponse.DM_ERROR; - return 'success'; + return unmuteResponse.SUCCESS; })(); - if (!(['error removing mute role', 'error creating modlog entry', 'error removing mute entry'] as const).includes(ret)) + if ( + !( + [unmuteResponse.ACTION_ERROR, unmuteResponse.MODLOG_ERROR, unmuteResponse.PUNISHMENT_ENTRY_REMOVE_ERROR] as const + ).includes(ret) + ) client.emit( 'bushUnmute', this, @@ -396,7 +401,7 @@ export class BushGuildMember extends GuildMember { */ public async bushKick(options: BushPunishmentOptions): Promise<KickResponse> { // checks - if (!this.guild.me?.permissions.has('KICK_MEMBERS') || !this.kickable) return 'missing permissions'; + if (!this.guild.me?.permissions.has('KICK_MEMBERS') || !this.kickable) return kickResponse.MISSING_PERMISSIONS; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -408,7 +413,7 @@ export class BushGuildMember extends GuildMember { // kick const kickSuccess = await this.kick(`${moderator?.tag} | ${options.reason ?? 'No reason provided.'}`).catch(() => false); - if (!kickSuccess) return 'error kicking'; + if (!kickSuccess) return kickResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -419,12 +424,12 @@ export class BushGuildMember extends GuildMember { guild: this.guild, evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return kickResponse.MODLOG_ERROR; caseID = modlog.id; - if (!dmSuccess) return 'failed to dm'; - return 'success'; + if (!dmSuccess) return kickResponse.DM_ERROR; + return kickResponse.SUCCESS; })(); - if (!(['error kicking', 'error creating modlog entry'] as const).includes(ret)) + if (!([kickResponse.ACTION_ERROR, kickResponse.MODLOG_ERROR] as const).includes(ret)) client.emit( 'bushKick', this, @@ -446,11 +451,18 @@ export class BushGuildMember extends GuildMember { */ public async bushBan(options: BushBanOptions): Promise<BanResponse> { // checks - if (!this.guild.me!.permissions.has('BAN_MEMBERS') || !this.bannable) return 'missing permissions'; + if (!this.guild.me!.permissions.has('BAN_MEMBERS') || !this.bannable) return banResponse.MISSING_PERMISSIONS; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; + + // ignore result, they should still be banned even if their mute cannot be removed + await this.bushUnmute({ + reason: 'User is about to be banned, a mute is no longer necessary.', + moderator: this.guild.me! + }); + const ret = await (async () => { // dm user const dmSuccess = await this.bushPunishDM('banned', options.reason, options.duration ?? 0); @@ -461,7 +473,7 @@ export class BushGuildMember extends GuildMember { reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, days: options.deleteDays }).catch(() => false); - if (!banSuccess) return 'error banning'; + if (!banSuccess) return banResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -473,7 +485,7 @@ export class BushGuildMember extends GuildMember { guild: this.guild, evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return banResponse.MODLOG_ERROR; caseID = modlog.id; // add punishment entry so they can be unbanned later @@ -484,12 +496,12 @@ export class BushGuildMember extends GuildMember { duration: options.duration, modlog: modlog.id }); - if (!punishmentEntrySuccess) return 'error creating ban entry'; + if (!punishmentEntrySuccess) return banResponse.PUNISHMENT_ENTRY_ADD_ERROR; - if (!dmSuccess) return 'failed to dm'; - return 'success'; + if (!dmSuccess) return banResponse.DM_ERROR; + return banResponse.SUCCESS; })(); - if (!(['error banning', 'error creating modlog entry', 'error creating ban entry'] as const).includes(ret)) + if (!([banResponse.ACTION_ERROR, banResponse.MODLOG_ERROR, banResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret)) client.emit( 'bushBan', this, @@ -510,11 +522,11 @@ export class BushGuildMember extends GuildMember { */ public async bushBlock(options: BlockOptions): Promise<BlockResponse> { const _channel = this.guild.channels.resolve(options.channel); - if (!_channel || (!_channel.isText() && !_channel.isThread())) return 'invalid channel'; + if (!_channel || (!_channel.isText() && !_channel.isThread())) return blockResponse.INVALID_CHANNEL; const channel = _channel as BushGuildTextBasedChannel; // checks - if (!channel.permissionsFor(this.guild.me!)!.has('MANAGE_CHANNELS')) return 'missing permissions'; + if (!channel.permissionsFor(this.guild.me!)!.has('MANAGE_CHANNELS')) return blockResponse.MISSING_PERMISSIONS; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -527,7 +539,7 @@ export class BushGuildMember extends GuildMember { const blockSuccess = await channelToUse.permissionOverwrites .edit(this, perm, { reason: `[Block] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` }) .catch(() => false); - if (!blockSuccess) return 'error blocking'; + if (!blockSuccess) return blockResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -538,7 +550,7 @@ export class BushGuildMember extends GuildMember { guild: this.guild, evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return blockResponse.MODLOG_ERROR; caseID = modlog.id; // add punishment entry so they can be unblocked later @@ -550,7 +562,7 @@ export class BushGuildMember extends GuildMember { modlog: modlog.id, extraInfo: channel.id }); - if (!punishmentEntrySuccess) return 'error creating block entry'; + if (!punishmentEntrySuccess) return blockResponse.PUNISHMENT_ENTRY_ADD_ERROR; // dm user const dmSuccess = await this.send({ @@ -560,17 +572,18 @@ export class BushGuildMember extends GuildMember { ? `for ${util.humanizeDuration(options.duration)} ` : 'permanently ' : '' - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - }for **${options.reason?.trim() || 'No reason provided'}**.` + }for **${options.reason?.trim() ? options.reason?.trim() : 'No reason provided'}**.` }).catch(() => false); dmSuccessEvent = !!dmSuccess; - if (!dmSuccess) return 'failed to dm'; + if (!dmSuccess) return blockResponse.DM_ERROR; - return 'success'; + return blockResponse.SUCCESS; })(); - if (!(['error creating modlog entry', 'error creating block entry', 'error blocking'] as const).includes(ret)) + if ( + !([blockResponse.ACTION_ERROR, blockResponse.MODLOG_ERROR, blockResponse.PUNISHMENT_ENTRY_ADD_ERROR] as const).includes(ret) + ) client.emit( 'bushBlock', this, @@ -592,11 +605,11 @@ export class BushGuildMember extends GuildMember { */ public async bushUnblock(options: UnblockOptions): Promise<UnblockResponse> { const _channel = this.guild.channels.resolve(options.channel); - if (!_channel || (!_channel.isText() && !_channel.isThread())) return 'invalid channel'; + if (!_channel || (!_channel.isText() && !_channel.isThread())) return unblockResponse.INVALID_CHANNEL; const channel = _channel as BushGuildTextBasedChannel; // checks - if (!channel.permissionsFor(this.guild.me!)!.has('MANAGE_CHANNELS')) return 'missing permissions'; + if (!channel.permissionsFor(this.guild.me!)!.has('MANAGE_CHANNELS')) return unblockResponse.MISSING_PERMISSIONS; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -609,7 +622,7 @@ export class BushGuildMember extends GuildMember { const blockSuccess = await channelToUse.permissionOverwrites .edit(this, perm, { reason: `[Unblock] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}` }) .catch(() => false); - if (!blockSuccess) return 'error unblocking'; + if (!blockSuccess) return unblockResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -620,7 +633,7 @@ export class BushGuildMember extends GuildMember { guild: this.guild, evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return unblockResponse.MODLOG_ERROR; caseID = modlog.id; // remove punishment entry @@ -630,23 +643,22 @@ export class BushGuildMember extends GuildMember { guild: this.guild, extraInfo: channel.id }); - if (!punishmentEntrySuccess) return 'error removing block entry'; + if (!punishmentEntrySuccess) return unblockResponse.ACTION_ERROR; // dm user const dmSuccess = await this.send({ content: `You have been unblocked from <#${channel.id}> in **${this.guild.name}** for **${ - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - options.reason?.trim() || 'No reason provided' + options.reason?.trim() ? options.reason?.trim() : 'No reason provided' }**.` }).catch(() => false); dmSuccessEvent = !!dmSuccess; - if (!dmSuccess) return 'failed to dm'; + if (!dmSuccess) return unblockResponse.DM_ERROR; - return 'success'; + return unblockResponse.SUCCESS; })(); - if (!(['error creating modlog entry', 'error removing block entry', 'error unblocking'] as const).includes(ret)) + if (!([unblockResponse.ACTION_ERROR, unblockResponse.MODLOG_ERROR, unblockResponse.ACTION_ERROR] as const).includes(ret)) client.emit( 'bushUnblock', this, @@ -667,10 +679,10 @@ export class BushGuildMember extends GuildMember { */ public async bushTimeout(options: BushTimeoutOptions): Promise<TimeoutResponse> { // checks - if (!this.guild.me!.permissions.has('MODERATE_MEMBERS')) return 'missing permissions'; + if (!this.guild.me!.permissions.has('MODERATE_MEMBERS')) return timeoutResponse.MISSING_PERMISSIONS; const twentyEightDays = client.consts.timeUnits.days.value * 28; - if (options.duration > twentyEightDays) return 'duration too long'; + if (options.duration > twentyEightDays) return timeoutResponse.INVALID_DURATION; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -682,7 +694,7 @@ export class BushGuildMember extends GuildMember { options.duration, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}` ).catch(() => false); - if (!timeoutSuccess) return 'error timing out'; + if (!timeoutSuccess) return timeoutResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -695,19 +707,19 @@ export class BushGuildMember extends GuildMember { evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return timeoutResponse.MODLOG_ERROR; caseID = modlog.id; // dm user const dmSuccess = await this.bushPunishDM('timed out', options.reason, options.duration); dmSuccessEvent = dmSuccess; - if (!dmSuccess) return 'failed to dm'; + if (!dmSuccess) return timeoutResponse.DM_ERROR; - return 'success'; + return timeoutResponse.SUCCESS; })(); - if (!(['error timing out', 'error creating modlog entry'] as const).includes(ret)) + if (!([timeoutResponse.ACTION_ERROR, timeoutResponse.MODLOG_ERROR] as const).includes(ret)) client.emit( 'bushTimeout', this, @@ -728,7 +740,7 @@ export class BushGuildMember extends GuildMember { */ public async bushRemoveTimeout(options: BushPunishmentOptions): Promise<RemoveTimeoutResponse> { // checks - if (!this.guild.me!.permissions.has('MODERATE_MEMBERS')) return 'missing permissions'; + if (!this.guild.me!.permissions.has('MODERATE_MEMBERS')) return removeTimeoutResponse.MISSING_PERMISSIONS; let caseID: string | undefined = undefined; let dmSuccessEvent: boolean | undefined = undefined; @@ -739,7 +751,7 @@ export class BushGuildMember extends GuildMember { const timeoutSuccess = await this.timeout(null, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`).catch( () => false ); - if (!timeoutSuccess) return 'error removing timeout'; + if (!timeoutSuccess) return removeTimeoutResponse.ACTION_ERROR; // add modlog entry const { log: modlog } = await Moderation.createModLogEntry({ @@ -751,19 +763,19 @@ export class BushGuildMember extends GuildMember { evidence: options.evidence }); - if (!modlog) return 'error creating modlog entry'; + if (!modlog) return removeTimeoutResponse.MODLOG_ERROR; caseID = modlog.id; // dm user - const dmSuccess = await this.bushPunishDM('un timed out', options.reason); + const dmSuccess = await this.bushPunishDM('untimedout', options.reason); dmSuccessEvent = dmSuccess; - if (!dmSuccess) return 'failed to dm'; + if (!dmSuccess) return removeTimeoutResponse.DM_ERROR; - return 'success'; + return removeTimeoutResponse.SUCCESS; })(); - if (!(['error removing timeout', 'error creating modlog entry'] as const).includes(ret)) + if (!([removeTimeoutResponse.ACTION_ERROR, removeTimeoutResponse.MODLOG_ERROR] as const).includes(ret)) client.emit( 'bushRemoveTimeout', this, @@ -892,98 +904,169 @@ export interface BushTimeoutOptions extends BushPunishmentOptions { duration: number; } -export type PunishmentResponse = 'success' | 'error creating modlog entry' | 'failed to dm'; +type ValueOf<T> = T[keyof T]; + +export const basePunishmentResponse = Object.freeze({ + SUCCESS: 'success', + MODLOG_ERROR: 'error creating modlog entry', + ACTION_ERROR: 'error performing action' +} as const); + +export const dmResponse = Object.freeze({ + ...basePunishmentResponse, + DM_ERROR: 'failed to dm' +} as const); + +export const permissionsResponse = Object.freeze({ + MISSING_PERMISSIONS: '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' +} as const); + +export const shouldAddRoleResponse = Object.freeze({ + USER_HIERARCHY: 'user hierarchy', + CLIENT_HIERARCHY: 'client hierarchy', + ROLE_MANAGED: 'role managed' +} as const); + +export const baseBlockResponse = Object.freeze({ + INVALID_CHANNEL: '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' +} 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({ + ...basePunishmentResponse, + ...permissionsResponse, + ...shouldAddRoleResponse, + ...punishmentEntryRemove +} as const); + +export const muteResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseMuteResponse, + ...punishmentEntryAdd +} as const); + +export const unmuteResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseMuteResponse, + ...punishmentEntryRemove +} as const); + +export const kickResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse +} as const); + +export const banResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...punishmentEntryAdd +} as const); + +export const blockResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseBlockResponse, + ...punishmentEntryAdd +}); + +export const unblockResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + ...baseBlockResponse, + ...punishmentEntryRemove +}); + +export const timeoutResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse, + INVALID_DURATION: 'duration too long' +} as const); + +export const removeTimeoutResponse = Object.freeze({ + ...dmResponse, + ...permissionsResponse +} as const); /** * Response returned when warning a user. */ -export type WarnResponse = PunishmentResponse; +export type WarnResponse = ValueOf<typeof warnResponse>; /** * Response returned when adding a role to a user. */ -export type AddRoleResponse = - | Exclude<PunishmentResponse, 'failed to dm'> - | 'user hierarchy' - | 'role managed' - | 'client hierarchy' - | 'error creating role entry' - | 'error adding role'; +export type AddRoleResponse = ValueOf<typeof addRoleResponse>; /** * Response returned when removing a role from a user. */ -export type RemoveRoleResponse = - | Exclude<PunishmentResponse, 'failed to dm'> - | 'user hierarchy' - | 'role managed' - | 'client hierarchy' - | 'error removing role entry' - | 'error removing role'; +export type RemoveRoleResponse = ValueOf<typeof removeRoleResponse>; /** * Response returned when muting a user. */ -export type MuteResponse = - | PunishmentResponse - | 'missing permissions' - | 'no mute role' - | 'invalid mute role' - | 'mute role not manageable' - | 'error giving mute role' - | 'error creating mute entry'; +export type MuteResponse = ValueOf<typeof muteResponse>; /** * Response returned when unmuting a user. */ -export type UnmuteResponse = - | PunishmentResponse - | 'missing permissions' - | 'no mute role' - | 'invalid mute role' - | 'mute role not manageable' - | 'error removing mute role' - | 'error removing mute entry'; +export type UnmuteResponse = ValueOf<typeof unmuteResponse>; /** * Response returned when kicking a user. */ -export type KickResponse = PunishmentResponse | 'missing permissions' | 'error kicking'; +export type KickResponse = ValueOf<typeof kickResponse>; /** * Response returned when banning a user. */ -export type BanResponse = PunishmentResponse | 'missing permissions' | 'error creating ban entry' | 'error banning'; +export type BanResponse = ValueOf<typeof banResponse>; /** * Response returned when blocking a user. */ -export type BlockResponse = - | PunishmentResponse - | 'invalid channel' - | 'error creating block entry' - | 'missing permissions' - | 'error blocking'; +export type BlockResponse = ValueOf<typeof blockResponse>; /** * Response returned when unblocking a user. */ -export type UnblockResponse = - | PunishmentResponse - | 'invalid channel' - | 'error removing block entry' - | 'missing permissions' - | 'error unblocking'; +export type UnblockResponse = ValueOf<typeof unblockResponse>; /** * Response returned when timing out a user. */ -export type TimeoutResponse = PunishmentResponse | 'missing permissions' | 'duration too long' | 'error timing out'; +export type TimeoutResponse = ValueOf<typeof timeoutResponse>; /** * Response returned when removing a timeout from a user. */ -export type RemoveTimeoutResponse = PunishmentResponse | 'missing permissions' | 'duration too long' | 'error removing timeout'; +export type RemoveTimeoutResponse = ValueOf<typeof removeTimeoutResponse>; export type PartialBushGuildMember = Partialize<BushGuildMember, 'joinedAt' | 'joinedTimestamp'>; diff --git a/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts b/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts index 903b43f..23e2453 100644 --- a/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts +++ b/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts @@ -1,5 +1,5 @@ import type { BushClient, BushGuild, BushGuildMember, BushGuildTextBasedChannel, BushTextBasedChannel, BushUser } from '#lib'; -import type { APIInteractionGuildMember } from 'discord-api-types/v9'; +import type { APIInteractionGuildMember } from '@discordjs/builders/node_modules/discord-api-types'; import { SelectMenuInteraction, type CacheType, type CacheTypeReducer } from 'discord.js'; import type { RawMessageSelectMenuInteractionData } from 'discord.js/typings/rawDataTypes'; |