aboutsummaryrefslogtreecommitdiff
path: root/src/lib/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/extensions')
-rw-r--r--src/lib/extensions/discord-akairo/BushClient.ts10
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts6
-rw-r--r--src/lib/extensions/discord.js/BushClientEvents.d.ts19
-rw-r--r--src/lib/extensions/discord.js/BushGuild.ts62
-rw-r--r--src/lib/extensions/discord.js/BushGuildMember.ts176
5 files changed, 212 insertions, 61 deletions
diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts
index 706b52a..8cbc6fe 100644
--- a/src/lib/extensions/discord-akairo/BushClient.ts
+++ b/src/lib/extensions/discord-akairo/BushClient.ts
@@ -449,19 +449,19 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re
export interface BushClient extends EventEmitter, PatchedElements {
on<K extends keyof BushClientEvents>(event: K, listener: (...args: BushClientEvents[K]) => Awaitable<void>): this;
- on<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, listener: (...args: any[]) => Awaitable<void>): this;
+ // on<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, listener: (...args: any[]) => Awaitable<void>): this;
once<K extends keyof BushClientEvents>(event: K, listener: (...args: BushClientEvents[K]) => Awaitable<void>): this;
- once<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, listener: (...args: any[]) => Awaitable<void>): this;
+ // once<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, listener: (...args: any[]) => Awaitable<void>): this;
emit<K extends keyof BushClientEvents>(event: K, ...args: BushClientEvents[K]): boolean;
- emit<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, ...args: unknown[]): boolean;
+ // emit<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, ...args: unknown[]): boolean;
off<K extends keyof BushClientEvents>(event: K, listener: (...args: BushClientEvents[K]) => Awaitable<void>): this;
- off<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, listener: (...args: any[]) => Awaitable<void>): this;
+ // off<S extends string | symbol>(event: Exclude<S, keyof BushClientEvents>, listener: (...args: any[]) => Awaitable<void>): this;
removeAllListeners<K extends keyof BushClientEvents>(event?: K): this;
- removeAllListeners<S extends string | symbol>(event?: Exclude<S, keyof BushClientEvents>): this;
+ // removeAllListeners<S extends string | symbol>(event?: Exclude<S, keyof BushClientEvents>): this;
}
export interface BushStats {
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index f20a53e..30cc83f 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -515,11 +515,11 @@ export class BushClientUtil extends ClientUtil {
// in the beginning of the argument
let contentWithoutTime = ` ${content}`;
- for (const unit in BushConstants.TimeUnits) {
- const regex = BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].match;
+ for (const unit in BushConstants.timeUnits) {
+ const regex = BushConstants.timeUnits[unit as keyof typeof BushConstants.timeUnits].match;
const match = regex.exec(contentWithoutTime);
const value = Number(match?.groups?.[unit]);
- if (!isNaN(value)) duration! += value * BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].value;
+ if (!isNaN(value)) duration! += value * BushConstants.timeUnits[unit as keyof typeof BushConstants.timeUnits].value;
if (remove) contentWithoutTime = contentWithoutTime.replace(regex, '');
}
diff --git a/src/lib/extensions/discord.js/BushClientEvents.d.ts b/src/lib/extensions/discord.js/BushClientEvents.d.ts
index b779991..6dc94a1 100644
--- a/src/lib/extensions/discord.js/BushClientEvents.d.ts
+++ b/src/lib/extensions/discord.js/BushClientEvents.d.ts
@@ -227,6 +227,25 @@ export interface BushClientEvents extends AkairoClientEvents {
channel: BushTextChannel | BushNewsChannel | BushThreadChannel,
messages: Collection<Snowflake, BushMessage>
];
+ bushRemoveTimeout: [
+ victim: BushGuildMember,
+ moderator: BushUser,
+ guild: BushGuild,
+ reason: string | undefined,
+ caseID: string,
+ dmSuccess: boolean,
+ evidence?: string
+ ];
+ bushTimeout: [
+ victim: BushGuildMember,
+ moderator: BushUser,
+ guild: BushGuild,
+ reason: string | undefined,
+ caseID: string,
+ duration: number,
+ dmSuccess: boolean,
+ evidence?: string
+ ];
bushUnban: [
victim: BushUser,
moderator: BushUser,
diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts
index b67c71b..d182be4 100644
--- a/src/lib/extensions/discord.js/BushGuild.ts
+++ b/src/lib/extensions/discord.js/BushGuild.ts
@@ -130,6 +130,29 @@ export class BushGuild extends Guild {
}
/**
+ * Sends a message to the guild's specified logging channel
+ * @param logType The corresponding channel that the message will be sent to
+ * @param message The parameters for {@link BushTextChannel.send}
+ */
+ public async sendLogChannel(logType: GuildLogType, message: string | MessagePayload | MessageOptions) {
+ const logChannel = await this.getLogChannel(logType);
+ if (!logChannel || logChannel.type !== 'GUILD_TEXT') return;
+ if (!logChannel.permissionsFor(this.me!.id)?.has(['VIEW_CHANNEL', 'SEND_MESSAGES', 'EMBED_LINKS'])) return;
+
+ return await logChannel.send(message).catch(() => null);
+ }
+
+ /**
+ * Sends a formatted error message in a guild's error log channel
+ * @param title The title of the error embed
+ * @param message The description of the error embed
+ */
+ public async error(title: string, message: string) {
+ void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>'));
+ void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: util.colors.error }] });
+ }
+
+ /**
* Bans a user, dms them, creates a mod log entry, and creates a punishment entry.
* @param options Options for banning the user.
* @returns A string status message of the ban.
@@ -143,7 +166,7 @@ export class BushGuild extends Guild {
const moderator = (await util.resolveNonCachedUser(options.moderator!)) ?? client.user!;
const ret = await (async () => {
- await this.members.cache.get(user.id)?.punishDM('banned', options.reason, options.duration ?? 0);
+ await this.members.cache.get(user.id)?.bushPunishDM('banned', options.reason, options.duration ?? 0);
// ban
const banSuccess = await this.bans
@@ -252,29 +275,6 @@ export class BushGuild extends Guild {
}
/**
- * Sends a message to the guild's specified logging channel
- * @param logType The corresponding channel that the message will be sent to
- * @param message The parameters for {@link BushTextChannel.send}
- */
- public async sendLogChannel(logType: GuildLogType, message: string | MessagePayload | MessageOptions) {
- const logChannel = await this.getLogChannel(logType);
- if (!logChannel || logChannel.type !== 'GUILD_TEXT') return;
- if (!logChannel.permissionsFor(this.me!.id)?.has(['VIEW_CHANNEL', 'SEND_MESSAGES', 'EMBED_LINKS'])) return;
-
- return await logChannel.send(message).catch(() => null);
- }
-
- /**
- * Sends a formatted error message in a guild's error log channel
- * @param title The title of the error embed
- * @param message The description of the error embed
- */
- public async error(title: string, message: string) {
- void client.console.info(_.camelCase(title), message.replace(/\*\*(.*?)\*\*/g, '<<$1>>'));
- void this.sendLogChannel('error', { embeds: [{ title: title, description: message, color: util.colors.error }] });
- }
-
- /**
* Denies send permissions in specified channels
* @param options The options for locking down the guild
*/
@@ -336,7 +336,7 @@ export class BushGuild extends Guild {
/**
* Options for unbanning a user
*/
-interface BushUnbanOptions {
+export interface BushUnbanOptions {
/**
* The user to unban
*/
@@ -356,7 +356,7 @@ interface BushUnbanOptions {
/**
* Options for banning a user
*/
-interface BushBanOptions {
+export interface BushBanOptions {
/**
* The user to ban
*/
@@ -388,22 +388,22 @@ interface BushBanOptions {
evidence?: string;
}
-type PunishmentResponse = 'success' | 'missing permissions' | 'error creating modlog entry';
+export type PunishmentResponse = 'success' | 'missing permissions' | 'error creating modlog entry';
/**
* Response returned when banning a user
*/
-type BanResponse = PunishmentResponse | 'error banning' | 'error creating ban entry';
+export type BanResponse = PunishmentResponse | 'error banning' | 'error creating ban entry';
/**
* Response returned when unbanning a user
*/
-type UnbanResponse = PunishmentResponse | 'user not banned' | 'error unbanning' | 'error removing ban entry';
+export type UnbanResponse = PunishmentResponse | 'user not banned' | 'error unbanning' | 'error removing ban entry';
/**
* Options for locking down channel(s)
*/
-interface LockdownOptions {
+export interface LockdownOptions {
/**
* The moderator responsible for the lockdown
*/
@@ -433,7 +433,7 @@ interface LockdownOptions {
/**
* Response returned when locking down a channel
*/
-type LockdownResponse =
+export type LockdownResponse =
| `success: ${number}`
| 'all not chosen and no channel specified'
| 'no channels configured'
diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts
index ac8fb54..ffca507 100644
--- a/src/lib/extensions/discord.js/BushGuildMember.ts
+++ b/src/lib/extensions/discord.js/BushGuildMember.ts
@@ -35,7 +35,7 @@ export class BushGuildMember extends GuildMember {
* @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 punishDM(punishment: string, reason?: string | null, duration?: number, sendFooter = true): Promise<boolean> {
+ 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
@@ -56,12 +56,12 @@ export class BushGuildMember extends GuildMember {
* @returns An object with the result of the warning, and the case number of the warn.
* @emits {@link BushClientEvents.bushWarn}
*/
- public async warn(options: BushPunishmentOptions): Promise<{ result: WarnResponse | null; caseNum: number | null }> {
+ public async bushWarn(options: BushPunishmentOptions): Promise<{ result: WarnResponse; caseNum: number | null }> {
let caseID: string | undefined = undefined;
let dmSuccessEvent: boolean | undefined = undefined;
const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
- const ret = await (async () => {
+ const ret = await (async (): Promise<{ result: WarnResponse; caseNum: number | null }> => {
// add modlog entry
const result = await Moderation.createModLogEntry(
{
@@ -78,7 +78,7 @@ export class BushGuildMember extends GuildMember {
if (!result || !result.log) return { result: 'error creating modlog entry', caseNum: null };
// dm user
- const dmSuccess = await this.punishDM('warned', options.reason);
+ const dmSuccess = await this.bushPunishDM('warned', options.reason);
dmSuccessEvent = dmSuccess;
if (!dmSuccess) return { result: 'failed to dm', caseNum: result.caseNum };
@@ -86,7 +86,7 @@ export class BushGuildMember extends GuildMember {
})();
if (!(['error creating modlog entry'] as const).includes(ret.result))
client.emit('bushWarn', this, moderator, this.guild, options.reason ?? undefined, caseID!, dmSuccessEvent!);
- return ret as { result: WarnResponse | null; caseNum: number | null };
+ return ret;
}
/**
@@ -95,7 +95,7 @@ export class BushGuildMember extends GuildMember {
* @returns A status message for adding the add.
* @emits {@link BushClientEvents.bushPunishRole}
*/
- public async addRole(options: AddRoleOptions): Promise<AddRoleResponse> {
+ public async bushAddRole(options: AddRoleOptions): Promise<AddRoleResponse> {
const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator);
if (ifShouldAddRole !== true) return ifShouldAddRole;
@@ -159,7 +159,7 @@ export class BushGuildMember extends GuildMember {
* @returns A status message for removing the role.
* @emits {@link BushClientEvents.bushPunishRoleRemove}
*/
- public async removeRole(options: RemoveRoleOptions): Promise<RemoveRoleResponse> {
+ public async bushRemoveRole(options: RemoveRoleOptions): Promise<RemoveRoleResponse> {
const ifShouldAddRole = this.#checkIfShouldAddRole(options.role, options.moderator);
if (ifShouldAddRole !== true) return ifShouldAddRole;
@@ -240,7 +240,7 @@ export class BushGuildMember extends GuildMember {
* @returns A status message for muting the user.
* @emits {@link BushClientEvents.bushMute}
*/
- public async mute(options: BushTimedPunishmentOptions): Promise<MuteResponse> {
+ public async bushMute(options: BushTimedPunishmentOptions): Promise<MuteResponse> {
// checks
if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return 'missing permissions';
const muteRoleID = await this.guild.getSetting('muteRole');
@@ -290,7 +290,7 @@ export class BushGuildMember extends GuildMember {
if (!punishmentEntrySuccess) return 'error creating mute entry';
// dm user
- const dmSuccess = await this.punishDM('muted', options.reason, options.duration ?? 0);
+ const dmSuccess = await this.bushPunishDM('muted', options.reason, options.duration ?? 0);
dmSuccessEvent = dmSuccess;
if (!dmSuccess) return 'failed to dm';
@@ -319,7 +319,7 @@ export class BushGuildMember extends GuildMember {
* @returns A status message for unmuting the user.
* @emits {@link BushClientEvents.bushUnmute}
*/
- public async unmute(options: BushPunishmentOptions): Promise<UnmuteResponse> {
+ public async bushUnmute(options: BushPunishmentOptions): Promise<UnmuteResponse> {
// checks
if (!this.guild.me!.permissions.has('MANAGE_ROLES')) return 'missing permissions';
const muteRoleID = await this.guild.getSetting('muteRole');
@@ -365,7 +365,7 @@ export class BushGuildMember extends GuildMember {
if (!removePunishmentEntrySuccess) return 'error removing mute entry';
// dm user
- const dmSuccess = await this.punishDM('unmuted', options.reason, undefined, false);
+ const dmSuccess = await this.bushPunishDM('unmuted', options.reason, undefined, false);
dmSuccessEvent = dmSuccess;
if (!dmSuccess) return 'failed to dm';
@@ -402,7 +402,7 @@ export class BushGuildMember extends GuildMember {
const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
const ret = await (async () => {
// dm user
- const dmSuccess = await this.punishDM('kicked', options.reason);
+ const dmSuccess = await this.bushPunishDM('kicked', options.reason);
dmSuccessEvent = dmSuccess;
// kick
@@ -452,7 +452,7 @@ export class BushGuildMember extends GuildMember {
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);
+ const dmSuccess = await this.bushPunishDM('banned', options.reason, options.duration ?? 0);
dmSuccessEvent = dmSuccess;
// ban
@@ -507,7 +507,7 @@ export class BushGuildMember extends GuildMember {
* Prevents a user from speaking in a channel.
* @param options Options for blocking the user.
*/
- public async block(options: BlockOptions): Promise<BlockResponse> {
+ public async bushBlock(options: BlockOptions): Promise<BlockResponse> {
const _channel = this.guild.channels.resolve(options.channel);
if (!_channel || (!_channel.isText() && !_channel.isThread())) return 'invalid channel';
const channel = _channel as BushGuildTextBasedChannel;
@@ -588,7 +588,7 @@ export class BushGuildMember extends GuildMember {
* Allows a user to speak in a channel.
* @param options Options for unblocking the user.
*/
- public async unblock(options: UnblockOptions): Promise<UnblockResponse> {
+ public async bushUnblock(options: UnblockOptions): Promise<UnblockResponse> {
const _channel = this.guild.channels.resolve(options.channel);
if (!_channel || (!_channel.isText() && !_channel.isThread())) return 'invalid channel';
const channel = _channel as BushGuildTextBasedChannel;
@@ -659,6 +659,122 @@ export class BushGuildMember extends GuildMember {
}
/**
+ * Mutes a user using discord's timeout feature.
+ * @param options Options for timing out the user.
+ */
+ public async bushTimeout(options: BushTimeoutOptions): Promise<TimeoutResponse> {
+ // checks
+ if (!this.guild.me!.permissions.has('MODERATE_MEMBERS')) return 'missing permissions';
+
+ const twentyEightDays = client.consts.timeUnits.days.value * 28;
+ if (options.duration > twentyEightDays) return 'duration too long';
+
+ let caseID: string | undefined = undefined;
+ let dmSuccessEvent: boolean | undefined = undefined;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
+
+ const ret = await (async () => {
+ // timeout
+ const timeoutSuccess = await this.timeout(
+ options.duration,
+ `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`
+ ).catch(() => false);
+ if (!timeoutSuccess) return 'error timing out';
+
+ // add modlog entry
+ const { log: modlog } = await Moderation.createModLogEntry({
+ type: ModLogType.TIMEOUT,
+ user: this,
+ moderator: moderator.id,
+ reason: options.reason,
+ duration: options.duration,
+ guild: this.guild,
+ evidence: options.evidence
+ });
+
+ if (!modlog) return 'error creating modlog entry';
+ caseID = modlog.id;
+
+ // dm user
+ const dmSuccess = await this.bushPunishDM('timed out', options.reason, options.duration);
+ dmSuccessEvent = dmSuccess;
+
+ if (!dmSuccess) return 'failed to dm';
+
+ return 'success';
+ })();
+
+ if (!(['error timing out', 'error creating modlog entry'] as const).includes(ret))
+ client.emit(
+ 'bushTimeout',
+ this,
+ moderator,
+ this.guild,
+ options.reason ?? undefined,
+ caseID!,
+ options.duration ?? 0,
+ dmSuccessEvent!,
+ options.evidence
+ );
+ return ret;
+ }
+
+ /**
+ * Removes a timeout from a user.
+ * @param options Options for removing the timeout.
+ */
+ public async bushRemoveTimeout(options: BushPunishmentOptions): Promise<RemoveTimeoutResponse> {
+ // checks
+ if (!this.guild.me!.permissions.has('MODERATE_MEMBERS')) 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 () => {
+ // remove timeout
+ const timeoutSuccess = await this.timeout(null, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`).catch(
+ () => false
+ );
+ if (!timeoutSuccess) return 'error removing timeout';
+
+ // add modlog entry
+ const { log: modlog } = await Moderation.createModLogEntry({
+ type: ModLogType.REMOVE_TIMEOUT,
+ user: this,
+ moderator: moderator.id,
+ reason: options.reason,
+ guild: this.guild,
+ evidence: options.evidence
+ });
+
+ if (!modlog) return 'error creating modlog entry';
+ caseID = modlog.id;
+
+ // dm user
+ const dmSuccess = await this.bushPunishDM('un timed out', options.reason);
+ dmSuccessEvent = dmSuccess;
+
+ if (!dmSuccess) return 'failed to dm';
+
+ return 'success';
+ })();
+
+ if (!(['error removing timeout', 'error creating modlog entry'] as const).includes(ret))
+ client.emit(
+ 'bushRemoveTimeout',
+ this,
+ moderator,
+ this.guild,
+ options.reason ?? undefined,
+ caseID!,
+ dmSuccessEvent!,
+ options.evidence
+ );
+ return ret;
+ }
+
+ /**
* Whether or not the user is an owner of the bot.
*/
public isOwner(): boolean {
@@ -763,6 +879,16 @@ export interface UnblockOptions extends BushPunishmentOptions {
channel: BushGuildTextChannelResolvable | BushThreadChannelResolvable;
}
+/**
+ * Punishment options for punishments that can be temporary.
+ */
+export interface BushTimeoutOptions extends BushPunishmentOptions {
+ /**
+ * The duration of the punishment.
+ */
+ duration: number;
+}
+
export type PunishmentResponse = 'success' | 'error creating modlog entry' | 'failed to dm';
/**
@@ -774,7 +900,7 @@ export type WarnResponse = PunishmentResponse;
* Response returned when adding a role to a user.
*/
export type AddRoleResponse =
- | PunishmentResponse
+ | Exclude<PunishmentResponse, 'failed to dm'>
| 'user hierarchy'
| 'role managed'
| 'client hierarchy'
@@ -785,7 +911,7 @@ export type AddRoleResponse =
* Response returned when removing a role from a user.
*/
export type RemoveRoleResponse =
- | PunishmentResponse
+ | Exclude<PunishmentResponse, 'failed to dm'>
| 'user hierarchy'
| 'role managed'
| 'client hierarchy'
@@ -846,11 +972,17 @@ export type UnblockResponse =
| 'missing permissions'
| 'error unblocking';
-export type PartialBushGuildMember = Partialize<
- BushGuildMember,
- 'joinedAt' | 'joinedTimestamp',
- 'warn' | 'addRole' | 'removeRole' | 'mute' | 'unmute' | 'bushKick' | 'bushBan' | 'isOwner' | 'isSuperUser' | 'block'
->;
+/**
+ * Response returned when timing out a user.
+ */
+export type TimeoutResponse = PunishmentResponse | 'missing permissions' | 'duration too long' | 'error timing out';
+
+/**
+ * Response returned when removing a timeout from a user.
+ */
+export type RemoveTimeoutResponse = PunishmentResponse | 'missing permissions' | 'duration too long' | 'error removing timeout';
+
+export type PartialBushGuildMember = Partialize<BushGuildMember, 'joinedAt' | 'joinedTimestamp'>;
/**
* @typedef {BushClientEvents} VSCodePleaseDontRemove