diff options
author | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-09-05 20:24:50 -0400 |
---|---|---|
committer | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-09-05 20:24:50 -0400 |
commit | c4c1d9ffeb179e208792c88dd099caea5030581b (patch) | |
tree | dc075bda115de5f6cec925c398f3c9547d1bad55 /src/lib/extensions | |
parent | c238b0279c7686ca45506b0909e376f241cf0e30 (diff) | |
download | tanzanite-c4c1d9ffeb179e208792c88dd099caea5030581b.tar.gz tanzanite-c4c1d9ffeb179e208792c88dd099caea5030581b.tar.bz2 tanzanite-c4c1d9ffeb179e208792c88dd099caea5030581b.zip |
add moderation logging, fixes, hide modlog, jank
Diffstat (limited to 'src/lib/extensions')
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClient.ts | 46 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClientUtil.ts | 9 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushCommand.ts | 8 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushClientEvents.d.ts | 139 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuild.ts | 171 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuildMember.ts | 454 |
6 files changed, 558 insertions, 269 deletions
diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index 5c1cb35..59c4df8 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -1,6 +1,7 @@ import chalk from 'chalk'; import { AkairoClient, ContextMenuCommandHandler } from 'discord-akairo'; import { + Awaited, Collection, Intents, InteractionReplyOptions, @@ -48,6 +49,7 @@ import { BushButtonInteraction } from '../discord.js/BushButtonInteraction'; import { BushCategoryChannel } from '../discord.js/BushCategoryChannel'; import { BushChannel } from '../discord.js/BushChannel'; import { BushChannelManager } from '../discord.js/BushChannelManager'; +import { BushClientEvents } from '../discord.js/BushClientEvents'; import { BushClientUser } from '../discord.js/BushClientUser'; import { BushCommandInteraction } from '../discord.js/BushCommandInteraction'; import { BushDMChannel } from '../discord.js/BushDMChannel'; @@ -159,6 +161,50 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re public logger = BushLogger; public constants = BushConstants; public cache = BushCache; + + public override on<K extends keyof BushClientEvents>( + event: K, + listener: (...args: BushClientEvents[K]) => Awaited<void> + ): this; + public override on<S extends string | symbol>( + event: Exclude<S, keyof BushClientEvents>, + listener: (...args: any[]) => Awaited<void> + ): this { + return super.on(event as any, listener); + } + + public override once<K extends keyof BushClientEvents>( + event: K, + listener: (...args: BushClientEvents[K]) => Awaited<void> + ): this; + public override once<S extends string | symbol>( + event: Exclude<S, keyof BushClientEvents>, + listener: (...args: any[]) => Awaited<void> + ): this { + return super.once(event as any, listener); + } + + public override emit<K extends keyof BushClientEvents>(event: K, ...args: BushClientEvents[K]): boolean; + public override emit<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, ...args: unknown[]): boolean { + return super.emit(event as any, ...args); + } + + public override off<K extends keyof BushClientEvents>( + event: K, + listener: (...args: BushClientEvents[K]) => Awaited<void> + ): this; + public override off<S extends string | symbol>( + event: Exclude<S, keyof BushClientEvents>, + listener: (...args: any[]) => Awaited<void> + ): this { + return super.off(event as any, listener); + } + + public override removeAllListeners<K extends keyof BushClientEvents>(event?: K): this; + public override removeAllListeners<S extends string | symbol>(event?: Exclude<S, keyof BushClientEvents>): this { + return super.removeAllListeners(event as any); + } + public constructor(config: Config) { super({ ownerID: config.owners, diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index 3f9e0b6..fec0174 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -8,6 +8,7 @@ import { BushGuildResolvable, BushMessage, BushSlashMessage, + BushUser, Global, Guild, ModLog, @@ -576,7 +577,7 @@ export class BushClientUtil extends ClientUtil { if (content.length > 400_000 && !substr) { void this.handleError('haste', new Error(`content over 400,000 characters (${content.length.toLocaleString()})`)); return { error: 'content too long' }; - } else { + } else if (content.length > 400_000) { content = content.substr(0, 400_000); isSubstr = true; } @@ -876,7 +877,7 @@ export class BushClientUtil extends ClientUtil { const haste = await this.haste(code, substr); hasteOut = `Too large to display. ${ haste.url - ? `Hastebin: ${haste.url}${haste.error ? `(${haste.error})` : ''}` + ? `Hastebin: ${haste.url}${haste.error ? ` - ${haste.error}` : ''}` : `${this.emojis.error} Hastebin: ${haste.error}` }`; } @@ -969,7 +970,7 @@ export class BushClientUtil extends ClientUtil { public async inspectCleanRedactHaste(input: any, inspectOptions?: BushInspectOptions) { input = typeof input !== 'string' ? this.inspect(input, inspectOptions ?? undefined) : input; input = this.redact(input); - return this.haste(input); + return this.haste(input, true); } public inspectAndRedact(input: any, inspectOptions?: BushInspectOptions) { @@ -1413,7 +1414,7 @@ export class BushClientUtil extends ClientUtil { }); } - public async resolveNonCachedUser(user: UserResolvable | undefined | null): Promise<User | undefined> { + public async resolveNonCachedUser(user: UserResolvable | undefined | null): Promise<BushUser | undefined> { if (!user) return undefined; const id = user instanceof User || user instanceof GuildMember || user instanceof ThreadMember diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 495a454..1c8ea5b 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -147,7 +147,7 @@ export interface BushCommandOptions extends CommandOptions { }; args?: BushArgumentOptions[] & CustomBushArgumentOptions[]; category: string; - completelyHide?: boolean; + pseudo?: boolean; } export class BushCommand extends Command { @@ -166,8 +166,8 @@ export class BushCommand extends Command { /** Whether the command is hidden from the help command. */ public hidden: boolean; - /** Completely hide this command from the help command. */ - public completelyHide: boolean; + /** A fake command, completely hidden from the help command. */ + public pseudo: boolean; public constructor(id: string, options: BushCommandOptions) { if (options.args && typeof options.args !== 'function') { @@ -184,7 +184,7 @@ export class BushCommand extends Command { this.hidden = options.hidden ?? false; this.restrictedChannels = options.restrictedChannels!; this.restrictedGuilds = options.restrictedGuilds!; - this.completelyHide = options.completelyHide!; + this.pseudo = options.pseudo!; } public override exec(message: BushMessage, args: any): any; diff --git a/src/lib/extensions/discord.js/BushClientEvents.d.ts b/src/lib/extensions/discord.js/BushClientEvents.d.ts index ae9b186..8695e7a 100644 --- a/src/lib/extensions/discord.js/BushClientEvents.d.ts +++ b/src/lib/extensions/discord.js/BushClientEvents.d.ts @@ -9,7 +9,10 @@ import { Sticker, Typing } from 'discord.js'; -import { BushClient, BushTextBasedChannels } from '../discord-akairo/BushClient'; +import { + BushClient, + BushTextBasedChannels +} from '../discord-akairo/BushClient'; import { BushApplicationCommand } from './BushApplicationCommand'; import { BushDMChannel } from './BushDMChannel'; import { BushGuild } from './BushGuild'; @@ -18,7 +21,11 @@ import { BushGuildChannel } from './BushGuildChannel'; import { BushGuildEmoji } from './BushGuildEmoji'; import { BushGuildMember, PartialBushGuildMember } from './BushGuildMember'; import { BushMessage, PartialBushMessage } from './BushMessage'; -import { BushMessageReaction, PartialBushMessageReaction } from './BushMessageReaction'; +import { + BushMessageReaction, + PartialBushMessageReaction +} from './BushMessageReaction'; +import { BushNewsChannel } from './BushNewsChannel'; import { BushPresence } from './BushPresence'; import { BushRole } from './BushRole'; import { BushStageInstance } from './BushStageInstance'; @@ -31,11 +38,17 @@ import { BushVoiceState } from './BushVoiceState'; export interface BushClientEvents extends ClientEvents { applicationCommandCreate: [command: BushApplicationCommand]; applicationCommandDelete: [command: BushApplicationCommand]; - applicationCommandUpdate: [oldCommand: BushApplicationCommand | null, newCommand: BushApplicationCommand]; + applicationCommandUpdate: [ + oldCommand: BushApplicationCommand | null, + newCommand: BushApplicationCommand + ]; channelCreate: [channel: BushGuildChannel]; channelDelete: [channel: BushDMChannel | BushGuildChannel]; channelPinsUpdate: [channel: BushTextBasedChannels, date: Date]; - channelUpdate: [oldChannel: BushDMChannel | BushGuildChannel, newChannel: BushDMChannel | BushGuildChannel]; + channelUpdate: [ + oldChannel: BushDMChannel | BushGuildChannel, + newChannel: BushDMChannel | BushGuildChannel + ]; debug: [message: string]; warn: [message: string]; emojiCreate: [emoji: BushGuildEmoji]; @@ -54,20 +67,40 @@ export interface BushClientEvents extends ClientEvents { guildMembersChunk: [ members: Collection<Snowflake, BushGuildMember>, guild: BushGuild, - data: { count: number; index: number; nonce: string | undefined } + data: { + count: number; + index: number; + nonce: string | undefined; + } + ]; + guildMemberUpdate: [ + oldMember: BushGuildMember | PartialBushGuildMember, + newMember: BushGuildMember ]; - guildMemberUpdate: [oldMember: BushGuildMember | PartialBushGuildMember, newMember: BushGuildMember]; guildUpdate: [oldGuild: BushGuild, newGuild: BushGuild]; inviteCreate: [invite: Invite]; inviteDelete: [invite: Invite]; messageCreate: [message: BushMessage]; messageDelete: [message: BushMessage | PartialBushMessage]; messageReactionRemoveAll: [message: BushMessage | PartialBushMessage]; - messageReactionRemoveEmoji: [reaction: BushMessageReaction | PartialBushMessageReaction]; - messageDeleteBulk: [messages: Collection<Snowflake, BushMessage | PartialBushMessage>]; - messageReactionAdd: [reaction: BushMessageReaction | PartialBushMessageReaction, user: BushUser | PartialBushUser]; - messageReactionRemove: [reaction: BushMessageReaction | PartialBushMessageReaction, user: BushUser | PartialBushUser]; - messageUpdate: [oldMessage: BushMessage | PartialBushMessage, newMessage: BushMessage | PartialBushMessage]; + messageReactionRemoveEmoji: [ + reaction: BushMessageReaction | PartialBushMessageReaction + ]; + messageDeleteBulk: [ + messages: Collection<Snowflake, BushMessage | PartialBushMessage> + ]; + messageReactionAdd: [ + reaction: BushMessageReaction | PartialBushMessageReaction, + user: BushUser | PartialBushUser + ]; + messageReactionRemove: [ + reaction: BushMessageReaction | PartialBushMessageReaction, + user: BushUser | PartialBushUser + ]; + messageUpdate: [ + oldMessage: BushMessage | PartialBushMessage, + newMessage: BushMessage | PartialBushMessage + ]; presenceUpdate: [oldPresence: BushPresence | null, newPresence: BushPresence]; rateLimit: [rateLimitData: RateLimitData]; invalidRequestWarning: [invalidRequestWarningData: InvalidRequestWarningData]; @@ -79,7 +112,10 @@ export interface BushClientEvents extends ClientEvents { threadCreate: [thread: BushThreadChannel]; threadDelete: [thread: BushThreadChannel]; threadListSync: [threads: Collection<Snowflake, BushThreadChannel>]; - threadMemberUpdate: [oldMember: BushThreadMember, newMember: BushThreadMember]; + threadMemberUpdate: [ + oldMember: BushThreadMember, + newMember: BushThreadMember + ]; threadMembersUpdate: [ oldMembers: Collection<Snowflake, BushThreadMember>, mewMembers: Collection<Snowflake, BushThreadMember> @@ -95,9 +131,86 @@ export interface BushClientEvents extends ClientEvents { shardReconnecting: [shardId: number]; shardResume: [shardId: number, replayedEvents: number]; stageInstanceCreate: [stageInstance: BushStageInstance]; - stageInstanceUpdate: [oldStageInstance: BushStageInstance | null, newStageInstance: BushStageInstance]; + stageInstanceUpdate: [ + oldStageInstance: BushStageInstance | null, + newStageInstance: BushStageInstance + ]; stageInstanceDelete: [stageInstance: BushStageInstance]; stickerCreate: [sticker: Sticker]; stickerDelete: [sticker: Sticker]; stickerUpdate: [oldSticker: Sticker, newSticker: Sticker]; + /* Custom */ + bushBan: [ + victim: BushGuildMember | BushUser, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + duration: number, + dmSuccess?: boolean + ]; + bushKick: [ + victim: BushGuildMember, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + dmSuccess: boolean + ]; + bushMute: [ + victim: BushGuildMember, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + duration: number, + dmSuccess: boolean + ]; + bushPunishRole: [ + victim: BushGuildMember, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + duration: number, + role: BushRole + ]; + bushPunishRoleRemove: [ + victim: BushGuildMember, + moderator: BushUser, + guild: BushGuild, + caseID: string, + reason: string | undefined, + role: BushRole + ]; + bushPurge: [ + moderator: BushUser, + guild: BushGuild, + channel: BushTextChannel | BushNewsChannel | BushThreadChannel, + messages: Collection<Snowflake, BushMessage> + ]; + bushUnban: [ + victim: BushUser, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + dmSuccess: boolean + ]; + bushUnmute: [ + victim: BushGuildMember, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + dmSuccess: boolean + ]; + bushWarn: [ + victim: BushGuildMember, + moderator: BushUser, + guild: BushGuild, + reason: string | undefined, + caseID: string, + dmSuccess: boolean + ]; } diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index efecdcd..18f6542 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -1,10 +1,11 @@ import { Guild, UserResolvable } from 'discord.js'; import { RawGuildData } from 'discord.js/typings/rawDataTypes'; -import { Guild as GuildDB, GuildFeatures, GuildModel } from '../../models/Guild'; +import { Guild as GuildDB, GuildFeatures, GuildLogType, GuildModel } from '../../models/Guild'; import { ModLogType } from '../../models/ModLog'; import { BushClient, BushUserResolvable } from '../discord-akairo/BushClient'; import { BushGuildMember } from './BushGuildMember'; import { BushGuildMemberManager } from './BushGuildMemberManager'; +import { BushTextChannel } from './BushTextChannel'; import { BushUser } from './BushUser'; export class BushGuild extends Guild { @@ -50,7 +51,17 @@ export class BushGuild extends Guild { return await row.save(); } - public async ban(options: { + public async getLogChannel(logType: GuildLogType): Promise<BushTextChannel | undefined> { + const channelId = (await this.getSetting('logChannels'))[logType]; + if (!channelId) return undefined; + return ( + (this.channels.cache.get(channelId) as BushTextChannel | undefined) ?? + ((await this.channels.fetch(channelId)) as BushTextChannel | null) ?? + undefined + ); + } + + public async bushBan(options: { user: BushUserResolvable | UserResolvable; reason?: string | null; moderator?: BushUserResolvable; @@ -62,42 +73,51 @@ export class BushGuild extends Guild { // checks if (!this.me!.permissions.has('BAN_MEMBERS')) return 'missing permissions'; + let caseID: string | undefined = undefined; + const user = (await util.resolveNonCachedUser(options.user))!; const moderator = (await util.resolveNonCachedUser(options.moderator!)) ?? client.user!; - // ban - const banSuccess = await this.bans - .create(options.user, { - reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, - days: options.deleteDays - }) - .catch(() => false); - if (!banSuccess) return 'error banning'; - - // add modlog entry - const { log: modlog } = await util.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, - user: options.user as BushUserResolvable, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this - }); - if (!modlog) return 'error creating modlog entry'; - - // add punishment entry so they can be unbanned later - const punishmentEntrySuccess = await util.createPunishmentEntry({ - type: 'ban', - user: options.user as BushUserResolvable, - guild: this, - duration: options.duration, - modlog: modlog.id - }); - if (!punishmentEntrySuccess) return 'error creating ban entry'; - - return 'success'; + const ret = await (async () => { + // ban + const banSuccess = await this.bans + .create(user?.id ?? options.user, { + reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, + days: options.deleteDays + }) + .catch(() => false); + if (!banSuccess) return 'error banning'; + + // add modlog entry + const { log: modlog } = await util.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, + user: user, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this + }); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; + + // add punishment entry so they can be unbanned later + const punishmentEntrySuccess = await util.createPunishmentEntry({ + type: 'ban', + user: user, + guild: this, + duration: options.duration, + modlog: modlog.id + }); + if (!punishmentEntrySuccess) return 'error creating ban entry'; + + return '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); + return ret; } - public async unban(options: { + public async bushUnban(options: { user: BushUserResolvable | BushUser; reason?: string | null; moderator?: BushUserResolvable; @@ -109,48 +129,59 @@ export class BushGuild extends Guild { | 'error creating modlog entry' | 'error removing ban entry' > { + 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 bans = await this.bans.fetch(); - - let notBanned = false; - if (!bans.has(user.id)) notBanned = true; - - const unbanSuccess = await this.bans - .remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) - .catch((e) => { - if (e?.code === 'UNKNOWN_BAN') { - notBanned = true; - return true; - } else return false; + const ret = await (async () => { + const bans = await this.bans.fetch(); + + let notBanned = false; + if (!bans.has(user.id)) notBanned = true; + + const unbanSuccess = await this.bans + .remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) + .catch((e) => { + if (e?.code === 'UNKNOWN_BAN') { + notBanned = true; + return true; + } else return false; + }); + + if (notBanned) return 'user not banned'; + if (!unbanSuccess) return 'error unbanning'; + + // add modlog entry + const { log: modlog } = await util.createModLogEntry({ + type: ModLogType.UNBAN, + user: user.id, + moderator: moderator.id, + reason: options.reason, + guild: this }); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; + + // remove punishment entry + const removePunishmentEntrySuccess = await util.removePunishmentEntry({ + type: 'ban', + user: user.id, + guild: this + }); + if (!removePunishmentEntrySuccess) return 'error removing ban entry'; - if (!unbanSuccess) return 'error unbanning'; - - // add modlog entry - const modlog = await util.createModLogEntry({ - type: ModLogType.UNBAN, - user: user.id, - moderator: moderator.id, - reason: options.reason, - guild: this - }); - if (!modlog) return 'error creating modlog entry'; - - // remove punishment entry - const removePunishmentEntrySuccess = await util.removePunishmentEntry({ - type: 'ban', - user: user.id, - guild: this - }); - if (!removePunishmentEntrySuccess) return 'error removing ban entry'; - - const userObject = client.users.cache.get(user.id); + const userObject = client.users.cache.get(user.id); - userObject?.send(`You have been unbanned from **${this}** for **${options.reason ?? 'No reason provided'}**.`); + const dmSuccess = await userObject + ?.send(`You have been unbanned from **${this}** for **${options.reason ?? 'No reason provided'}**.`) + .catch(() => false); + dmSuccessEvent = !!dmSuccess; - if (notBanned) return 'user not banned'; - return 'success'; + return '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!); + return ret; } } diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts index ab4eee4..4dd1a5d 100644 --- a/src/lib/extensions/discord.js/BushGuildMember.ts +++ b/src/lib/extensions/discord.js/BushGuildMember.ts @@ -105,96 +105,140 @@ export class BushGuildMember extends GuildMember { } public async warn(options: BushPunishmentOptions): Promise<{ result: WarnResponse | null; caseNum: number | null }> { + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; - // add modlog entry - const result = await util.createModLogEntry( - { - type: ModLogType.WARN, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild - }, - true - ); - if (!result || !result.log) return { result: 'error creating modlog entry', caseNum: null }; - - // dm user - const dmSuccess = await this.punishDM('warned', options.reason); - if (!dmSuccess) return { result: 'failed to dm', caseNum: result.caseNum }; - - return { result: 'success', caseNum: result.caseNum }; + const ret = await (async () => { + // add modlog entry + const result = await util.createModLogEntry( + { + type: ModLogType.WARN, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild + }, + true + ); + caseID = result.log?.id; + if (!result || !result.log) return { result: 'error creating modlog entry', caseNum: null }; + + // dm user + const dmSuccess = await this.punishDM('warned', options.reason); + dmSuccessEvent = dmSuccess; + if (!dmSuccess) return { result: 'failed to dm', caseNum: result.caseNum }; + + return { result: 'success', caseNum: result.caseNum }; + })(); + if (!['error creating modlog entry'].includes(ret.result)) + client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); + return ret as { result: WarnResponse | null; caseNum: number | null }; } public async addRole(options: AddRoleOptions): Promise<AddRoleResponse> { const ifShouldAddRole = this.#checkIfShouldAddRole(options.role); if (ifShouldAddRole !== true) return ifShouldAddRole; + let caseID: string | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; - if (options.addToModlog || options.duration) { - const { log: modlog } = options.addToModlog - ? await util.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE, - guild: this.guild, - moderator: moderator.id, - user: this, - reason: 'N/A' - }) - : { log: null }; - - if (!modlog && options.addToModlog) return 'error creating modlog entry'; - + const ret = await (async () => { if (options.addToModlog || options.duration) { - const punishmentEntrySuccess = await util.createPunishmentEntry({ - type: 'role', - user: this, - guild: this.guild, - modlog: modlog?.id ?? undefined, - duration: options.duration, - extraInfo: options.role.id - }); - if (!punishmentEntrySuccess) return 'error creating role entry'; + const { log: modlog } = options.addToModlog + ? await util.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE, + guild: this.guild, + moderator: moderator.id, + user: this, + reason: 'N/A' + }) + : { log: null }; + caseID = modlog?.id; + + if (!modlog && options.addToModlog) return 'error creating modlog entry'; + + if (options.addToModlog || options.duration) { + const punishmentEntrySuccess = await util.createPunishmentEntry({ + type: 'role', + user: this, + guild: this.guild, + modlog: modlog?.id ?? undefined, + duration: options.duration, + extraInfo: options.role.id + }); + if (!punishmentEntrySuccess) return 'error creating role entry'; + } } - } - - const removeRoleSuccess = await this.roles.add(options.role, `${moderator.tag}`); - if (!removeRoleSuccess) return 'error adding role'; - return 'success'; + const removeRoleSuccess = await this.roles.add(options.role, `${moderator.tag}`); + if (!removeRoleSuccess) return 'error adding role'; + + return 'success'; + })(); + if (!['error adding role', 'error creating modlog entry', 'error creating role entry'].includes(ret) && options.addToModlog) + client.emit( + 'bushPunishRole', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + options.role as BushRole + ); + return ret; } public async removeRole(options: RemoveRoleOptions): Promise<RemoveRoleResponse> { const ifShouldAddRole = this.#checkIfShouldAddRole(options.role); if (ifShouldAddRole !== true) return ifShouldAddRole; + let caseID: string | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; - if (options.addToModlog) { - const { log: modlog } = await util.createModLogEntry({ - type: ModLogType.PERM_PUNISHMENT_ROLE, - guild: this.guild, - moderator: moderator.id, - user: this, - reason: 'N/A' - }); - - if (!modlog) return 'error creating modlog entry'; + const ret = await (async () => { + if (options.addToModlog) { + const { log: modlog } = await util.createModLogEntry({ + type: ModLogType.PERM_PUNISHMENT_ROLE, + guild: this.guild, + moderator: moderator.id, + user: this, + reason: 'N/A' + }); - const punishmentEntrySuccess = await util.removePunishmentEntry({ - type: 'role', - user: this, - guild: this.guild - }); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; - if (!punishmentEntrySuccess) return 'error removing role entry'; - } + const punishmentEntrySuccess = await util.removePunishmentEntry({ + type: 'role', + user: this, + guild: this.guild + }); - const removeRoleSuccess = await this.roles.remove(options.role, `${moderator.tag}`); - if (!removeRoleSuccess) return 'error removing role'; + if (!punishmentEntrySuccess) return 'error removing role entry'; + } - return 'success'; + const removeRoleSuccess = await this.roles.remove(options.role, `${moderator.tag}`); + if (!removeRoleSuccess) return 'error removing role'; + + return 'success'; + })(); + + if ( + !['error removing role', 'error creating modlog entry', 'error removing role entry'].includes(ret) && + options.addToModlog + ) + client.emit( + 'bushPunishRoleRemove', + this, + moderator, + this.guild, + caseID!, + options.reason ?? undefined, + options.role as BushRole + ); + return ret; } #checkIfShouldAddRole(role: BushRole | Role): true | 'user hierarchy' | 'role managed' | 'client hierarchy' { @@ -217,47 +261,66 @@ export class BushGuildMember extends GuildMember { if (!muteRole) return 'invalid mute role'; if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) return 'mute role not manageable'; + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; - // add role - const muteSuccess = await this.roles - .add(muteRole, `[Mute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) - .catch(async (e) => { - await client.console.warn('muteRoleAddError', e); - client.console.debug(e); - return false; - }); - if (!muteSuccess) return 'error giving mute role'; - - // add modlog entry - const { log: modlog } = await util.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE, - user: this, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this.guild - }); - - if (!modlog) return 'error creating modlog entry'; - - // add punishment entry so they can be unmuted later - const punishmentEntrySuccess = await util.createPunishmentEntry({ - type: 'mute', - user: this, - guild: this.guild, - duration: options.duration, - modlog: modlog.id - }); + const ret = await (async () => { + // add role + const muteSuccess = await this.roles + .add(muteRole, `[Mute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) + .catch(async (e) => { + await client.console.warn('muteRoleAddError', e); + client.console.debug(e); + return false; + }); + if (!muteSuccess) return 'error giving mute role'; - if (!punishmentEntrySuccess) return 'error creating mute entry'; + // add modlog entry + const { log: modlog } = await util.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE, + user: this, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this.guild + }); - // dm user - const dmSuccess = await this.punishDM('muted', options.reason, options.duration ?? 0); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; - if (!dmSuccess) return 'failed to dm'; + // add punishment entry so they can be unmuted later + const punishmentEntrySuccess = await util.createPunishmentEntry({ + type: 'mute', + user: this, + guild: this.guild, + duration: options.duration, + modlog: modlog.id + }); - return 'success'; + if (!punishmentEntrySuccess) return 'error creating mute entry'; + + // dm user + const dmSuccess = await this.punishDM('muted', options.reason, options.duration ?? 0); + dmSuccessEvent = dmSuccess; + + if (!dmSuccess) return 'failed to dm'; + + return 'success'; + })(); + + if (!['error giving mute role', 'error creating modlog entry', 'error creating mute entry'].includes(ret)) + client.emit( + 'bushMute', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent! + ); + return ret; } public async unmute(options: BushPunishmentOptions): Promise<UnmuteResponse> { @@ -269,110 +332,145 @@ export class BushGuildMember extends GuildMember { if (!muteRole) return 'invalid mute role'; if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) return 'mute role not manageable'; + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; - //remove role - const muteSuccess = await this.roles - .remove(muteRole, `[Unmute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) - .catch(async (e) => { - await client.console.warn('muteRoleAddError', e?.stack || e); - return false; + const ret = await (async () => { + //remove role + const muteSuccess = await this.roles + .remove(muteRole, `[Unmute] ${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) + .catch(async (e) => { + await client.console.warn('muteRoleAddError', e?.stack || e); + return false; + }); + if (!muteSuccess) return 'error removing mute role'; + + //remove modlog entry + const { log: modlog } = await util.createModLogEntry({ + type: ModLogType.UNMUTE, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild }); - if (!muteSuccess) return 'error removing mute role'; - //remove modlog entry - const { log: modlog } = await util.createModLogEntry({ - type: ModLogType.UNMUTE, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild - }); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; - if (!modlog) return 'error creating modlog entry'; + // remove mute entry + const removePunishmentEntrySuccess = await util.removePunishmentEntry({ + type: 'mute', + user: this, + guild: this.guild + }); - // remove mute entry - const removePunishmentEntrySuccess = await util.removePunishmentEntry({ - type: 'mute', - user: this, - guild: this.guild - }); + if (!removePunishmentEntrySuccess) return 'error removing mute entry'; - if (!removePunishmentEntrySuccess) return 'error removing mute entry'; + //dm user + const dmSuccess = await this.punishDM('unmuted', options.reason, undefined, false); + dmSuccessEvent = dmSuccess; - //dm user - const dmSuccess = await this.punishDM('unmuted', options.reason, undefined, false); + if (!dmSuccess) return 'failed to dm'; - if (!dmSuccess) return 'failed to dm'; + return 'success'; + })(); - return 'success'; + if (!['error removing mute role', 'error creating modlog entry', 'error removing mute entry'].includes(ret)) + client.emit('bushUnmute', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); + return ret; } public async bushKick(options: BushPunishmentOptions): Promise<KickResponse> { // checks if (!this.guild.me?.permissions.has('KICK_MEMBERS') || !this.kickable) return 'missing permissions'; + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; + const ret = await (async () => { + // dm user + const dmSuccess = await this.punishDM('kicked', options.reason); + dmSuccessEvent = dmSuccess; - // dm user - const dmSuccess = await this.punishDM('kicked', options.reason); - - // kick - const kickSuccess = await this.kick(`${moderator?.tag} | ${options.reason ?? 'No reason provided.'}`).catch(() => false); - if (!kickSuccess) return 'error kicking'; - - // add modlog entry - const { log: modlog } = await util.createModLogEntry({ - type: ModLogType.KICK, - user: this, - moderator: moderator.id, - reason: options.reason, - guild: this.guild - }); - if (!modlog) return 'error creating modlog entry'; - if (!dmSuccess) return 'failed to dm'; - return 'success'; + // kick + const kickSuccess = await this.kick(`${moderator?.tag} | ${options.reason ?? 'No reason provided.'}`).catch(() => false); + if (!kickSuccess) return 'error kicking'; + + // add modlog entry + const { log: modlog } = await util.createModLogEntry({ + type: ModLogType.KICK, + user: this, + moderator: moderator.id, + reason: options.reason, + guild: this.guild + }); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; + if (!dmSuccess) return 'failed to dm'; + return 'success'; + })(); + if (!['error kicking', 'error creating modlog entry'].includes(ret)) + client.emit('bushKick', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!); + return ret; } public async bushBan(options: BushBanOptions): Promise<BanResponse> { // checks if (!this.guild.me!.permissions.has('BAN_MEMBERS') || !this.bannable) return 'missing permissions'; + let caseID: string | undefined = undefined; + let dmSuccessEvent: boolean | undefined = undefined; const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!; + const ret = await (async () => { + // dm user + const dmSuccess = await this.punishDM('banned', options.reason, options.duration ?? 0); + dmSuccessEvent = dmSuccess; + + // ban + const banSuccess = await this.ban({ + reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, + days: options.deleteDays + }).catch(() => false); + if (!banSuccess) return 'error banning'; + + // add modlog entry + const { log: modlog } = await util.createModLogEntry({ + type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, + user: this, + moderator: moderator.id, + reason: options.reason, + duration: options.duration, + guild: this.guild + }); + if (!modlog) return 'error creating modlog entry'; + caseID = modlog.id; - // dm user - const dmSuccess = await this.punishDM('banned', options.reason, options.duration ?? 0); - - // ban - const banSuccess = await this.ban({ - reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`, - days: options.deleteDays - }).catch(() => false); - if (!banSuccess) return 'error banning'; - - // add modlog entry - const { log: modlog } = await util.createModLogEntry({ - type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN, - user: this, - moderator: moderator.id, - reason: options.reason, - duration: options.duration, - guild: this.guild - }); - if (!modlog) return 'error creating modlog entry'; - - // add punishment entry so they can be unbanned later - const punishmentEntrySuccess = await util.createPunishmentEntry({ - type: 'ban', - user: this, - guild: this.guild, - duration: options.duration, - modlog: modlog.id - }); - if (!punishmentEntrySuccess) return 'error creating ban entry'; - - if (!dmSuccess) return 'failed to dm'; - return 'success'; + // add punishment entry so they can be unbanned later + const punishmentEntrySuccess = await util.createPunishmentEntry({ + type: 'ban', + user: this, + guild: this.guild, + duration: options.duration, + modlog: modlog.id + }); + if (!punishmentEntrySuccess) return 'error creating ban entry'; + + if (!dmSuccess) return 'failed to dm'; + return 'success'; + })(); + if (!['error banning', 'error creating modlog entry', 'error creating ban entry'].includes(ret)) + client.emit( + 'bushBan', + this, + moderator, + this.guild, + options.reason ?? undefined, + caseID!, + options.duration ?? 0, + dmSuccessEvent! + ); + return ret; } public get isOwner(): boolean { |