aboutsummaryrefslogtreecommitdiff
path: root/src/listeners/member-custom
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-12-29 22:14:04 -0500
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-12-29 22:14:04 -0500
commitf0a9f894575871d498447c5de2b5f0f826b117b7 (patch)
treee3dc03b24ef7197699bc74ad47ebcbdc9cb19c1b /src/listeners/member-custom
parent1182f63498c856111762e2766d6cdddf063f2a97 (diff)
downloadtanzanite-f0a9f894575871d498447c5de2b5f0f826b117b7.tar.gz
tanzanite-f0a9f894575871d498447c5de2b5f0f826b117b7.tar.bz2
tanzanite-f0a9f894575871d498447c5de2b5f0f826b117b7.zip
events
Diffstat (limited to 'src/listeners/member-custom')
-rw-r--r--src/listeners/member-custom/bushBan.ts32
-rw-r--r--src/listeners/member-custom/bushBlock.ts36
-rw-r--r--src/listeners/member-custom/bushKick.ts31
-rw-r--r--src/listeners/member-custom/bushLevelUpdate.ts28
-rw-r--r--src/listeners/member-custom/bushMute.ts32
-rw-r--r--src/listeners/member-custom/bushPunishRole.ts31
-rw-r--r--src/listeners/member-custom/bushPunishRoleRemove.ts32
-rw-r--r--src/listeners/member-custom/bushPurge.ts40
-rw-r--r--src/listeners/member-custom/bushUnban.ts31
-rw-r--r--src/listeners/member-custom/bushUnblock.ts32
-rw-r--r--src/listeners/member-custom/bushUnmute.ts31
-rw-r--r--src/listeners/member-custom/bushUpdateModlog.ts33
-rw-r--r--src/listeners/member-custom/bushUpdateSettings.ts33
-rw-r--r--src/listeners/member-custom/bushWarn.ts31
14 files changed, 453 insertions, 0 deletions
diff --git a/src/listeners/member-custom/bushBan.ts b/src/listeners/member-custom/bushBan.ts
new file mode 100644
index 0000000..1243071
--- /dev/null
+++ b/src/listeners/member-custom/bushBan.ts
@@ -0,0 +1,32 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushBanListener extends BushListener {
+ public constructor() {
+ super('bushBan', {
+ emitter: 'client',
+ event: 'bushBan',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushBan']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.RED)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${duration ? 'Temp Ban' : 'Perm Ban'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (duration) logEmbed.addField('**Duration**', util.humanizeDuration(duration));
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushBlock.ts b/src/listeners/member-custom/bushBlock.ts
new file mode 100644
index 0000000..8e8adb6
--- /dev/null
+++ b/src/listeners/member-custom/bushBlock.ts
@@ -0,0 +1,36 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushBlockListener extends BushListener {
+ public constructor() {
+ super('bushBlock', {
+ emitter: 'client',
+ event: 'bushBlock',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(
+ ...[victim, moderator, guild, reason, caseID, duration, dmSuccess, channel]: BushClientEvents['bushBlock']
+ ) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.PURPLE)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${duration ? 'Temp Block' : 'Perm Block'}`)
+ .addField('**Channel**', `<#${channel.id}>`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+
+ if (duration) logEmbed.addField('**Duration**', `${util.humanizeDuration(duration) || duration}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushKick.ts b/src/listeners/member-custom/bushKick.ts
new file mode 100644
index 0000000..26e9617
--- /dev/null
+++ b/src/listeners/member-custom/bushKick.ts
@@ -0,0 +1,31 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushKickListener extends BushListener {
+ public constructor() {
+ super('bushKick', {
+ emitter: 'client',
+ event: 'bushKick',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushKick']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.RED)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Kick'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushLevelUpdate.ts b/src/listeners/member-custom/bushLevelUpdate.ts
new file mode 100644
index 0000000..18b0fc2
--- /dev/null
+++ b/src/listeners/member-custom/bushLevelUpdate.ts
@@ -0,0 +1,28 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { type TextChannel } from 'discord.js';
+
+export default class BushLevelUpdateListener extends BushListener {
+ public constructor() {
+ super('bushLevelUpdate', {
+ emitter: 'client',
+ event: 'bushLevelUpdate',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[member, _oldLevel, newLevel, _currentXp, message]: BushClientEvents['bushLevelUpdate']) {
+ if (await message.guild.hasFeature('sendLevelUpMessages')) {
+ void (async () => {
+ const channel = ((await message.guild.channels
+ .fetch((await message.guild.getSetting('levelUpChannel')) ?? message.channelId)
+ .catch(() => null)) ?? message.channel) as TextChannel;
+
+ const success = await channel
+ .send(`${util.format.input(member.user.tag)} leveled up to level ${util.format.input(`${newLevel}`)}.`)
+ .catch(() => null);
+
+ if (!success) await client.console.warn('bushLevelUpdate', `Could not send level up message in ${message.guild}`);
+ })();
+ }
+ }
+}
diff --git a/src/listeners/member-custom/bushMute.ts b/src/listeners/member-custom/bushMute.ts
new file mode 100644
index 0000000..a8637fd
--- /dev/null
+++ b/src/listeners/member-custom/bushMute.ts
@@ -0,0 +1,32 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushMuteListener extends BushListener {
+ public constructor() {
+ super('bushMute', {
+ emitter: 'client',
+ event: 'bushMute',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, duration, dmSuccess]: BushClientEvents['bushMute']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.ORANGE)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${duration ? 'Temp Mute' : 'Perm Mute'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (duration) logEmbed.addField('**Duration**', `${util.humanizeDuration(duration) || duration}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushPunishRole.ts b/src/listeners/member-custom/bushPunishRole.ts
new file mode 100644
index 0000000..731403b
--- /dev/null
+++ b/src/listeners/member-custom/bushPunishRole.ts
@@ -0,0 +1,31 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushPunishRoleListener extends BushListener {
+ public constructor() {
+ super('bushPunishRole', {
+ emitter: 'client',
+ event: 'bushPunishRole',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, duration]: BushClientEvents['bushPunishRole']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.YELLOW)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${duration ? 'Temp Punishment Role' : 'Perm Punishment Role'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (duration) logEmbed.addField('**Duration**', util.humanizeDuration(duration));
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushPunishRoleRemove.ts b/src/listeners/member-custom/bushPunishRoleRemove.ts
new file mode 100644
index 0000000..7d88ec8
--- /dev/null
+++ b/src/listeners/member-custom/bushPunishRoleRemove.ts
@@ -0,0 +1,32 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushPunishRoleRemoveListener extends BushListener {
+ public constructor() {
+ super('bushPunishRoleRemove', {
+ emitter: 'client',
+ event: 'bushPunishRoleRemove',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, role]: BushClientEvents['bushPunishRoleRemove']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.GREEN)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Remove Punishment Role'}`)
+ .addField('**Role**', `${role}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushPurge.ts b/src/listeners/member-custom/bushPurge.ts
new file mode 100644
index 0000000..8a1e0c5
--- /dev/null
+++ b/src/listeners/member-custom/bushPurge.ts
@@ -0,0 +1,40 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { MessageEmbed } from 'discord.js';
+
+export default class BushPurgeListener extends BushListener {
+ public constructor() {
+ super('bushPurge', {
+ emitter: 'client',
+ event: 'bushPurge',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[moderator, guild, channel, messages]: BushClientEvents['bushPurge']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+
+ const mappedMessages = messages.map((m) => ({
+ id: m.id,
+ author: `${m.author.tag} (${m.id})`,
+ content: m.content,
+ embeds: m.embeds,
+ attachments: [...m.attachments.values()]
+ }));
+ const haste = await util.inspectCleanRedactHaste(mappedMessages);
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.DARK_PURPLE)
+ .setTimestamp()
+ .setFooter({ text: `${messages.size.toLocaleString()} Messages` })
+ .setAuthor({ name: moderator.tag, iconURL: moderator.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Purge'}`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ .addField('**Channel**', `<#${channel.id}> (${channel.name})`)
+ .addField(
+ '**Messages**',
+ `${haste.url ? `[haste](${haste.url})${haste.error ? `- ${haste.error}` : ''}` : `${util.emojis.error} ${haste.error}`}`
+ );
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushUnban.ts b/src/listeners/member-custom/bushUnban.ts
new file mode 100644
index 0000000..e7024ef
--- /dev/null
+++ b/src/listeners/member-custom/bushUnban.ts
@@ -0,0 +1,31 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushUnbanListener extends BushListener {
+ public constructor() {
+ super('bushUnban', {
+ emitter: 'client',
+ event: 'bushUnban',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushUnban']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.GREEN)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Unban'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushUnblock.ts b/src/listeners/member-custom/bushUnblock.ts
new file mode 100644
index 0000000..e313025
--- /dev/null
+++ b/src/listeners/member-custom/bushUnblock.ts
@@ -0,0 +1,32 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushUnblockListener extends BushListener {
+ public constructor() {
+ super('bushUnblock', {
+ emitter: 'client',
+ event: 'bushUnblock',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess, channel]: BushClientEvents['bushUnblock']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.GREEN)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Unblock'}`)
+ .addField('**Channel**', `<#${channel.id}>`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushUnmute.ts b/src/listeners/member-custom/bushUnmute.ts
new file mode 100644
index 0000000..4fa808f
--- /dev/null
+++ b/src/listeners/member-custom/bushUnmute.ts
@@ -0,0 +1,31 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushUnmuteListener extends BushListener {
+ public constructor() {
+ super('bushUnmute', {
+ emitter: 'client',
+ event: 'bushUnmute',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushUnmute']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.GREEN)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Unmute'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushUpdateModlog.ts b/src/listeners/member-custom/bushUpdateModlog.ts
new file mode 100644
index 0000000..46910b9
--- /dev/null
+++ b/src/listeners/member-custom/bushUpdateModlog.ts
@@ -0,0 +1,33 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { MessageEmbed } from 'discord.js';
+
+export default class BushUpdateModlogListener extends BushListener {
+ public constructor() {
+ super('bushUpdateModlog', {
+ emitter: 'client',
+ event: 'bushUpdateModlog',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[moderator, modlogID, key, oldModlog, newModlog]: BushClientEvents['bushUpdateModlog']) {
+ const logChannel = await moderator.guild.getLogChannel('moderation');
+ if (!logChannel) return;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.BLURPLE)
+ .setTimestamp()
+ .setAuthor({
+ name: moderator.user.tag,
+ iconURL: moderator.user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined
+ })
+ .addField('**Action**', `${'Update Modlog'}`)
+ .addField('**Moderator**', `${moderator} (${moderator.user.tag})`)
+ .addField('**ModLog Changed**', modlogID)
+ .addField('**Value Changed**', key)
+ .addField('**Old Value**', await util.inspectCleanRedactCodeblock(oldModlog, undefined, undefined, 1024))
+ .addField('**New Value**', await util.inspectCleanRedactCodeblock(newModlog, undefined, undefined, 1024));
+
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushUpdateSettings.ts b/src/listeners/member-custom/bushUpdateSettings.ts
new file mode 100644
index 0000000..6a9c3d8
--- /dev/null
+++ b/src/listeners/member-custom/bushUpdateSettings.ts
@@ -0,0 +1,33 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { MessageEmbed } from 'discord.js';
+
+export default class BushUpdateSettingsListener extends BushListener {
+ public constructor() {
+ super('bushUpdateSettings', {
+ emitter: 'client',
+ event: 'bushUpdateSettings',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[setting, guild, oldSettings, newSettings, moderator]: BushClientEvents['bushUpdateSettings']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+
+ const logEmbed = new MessageEmbed().setColor(util.colors.discord.BLURPLE).setTimestamp();
+
+ if (moderator)
+ logEmbed.setAuthor({
+ name: moderator.user.tag,
+ iconURL: moderator.user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined
+ });
+ logEmbed.addField('**Action**', `${'Update Settings'}`);
+ if (moderator) logEmbed.addField('**Moderator**', `${moderator} (${moderator.user.tag})`);
+ logEmbed
+ .addField('**Setting Changed**', setting)
+ .addField('**Old Value**', await util.inspectCleanRedactCodeblock(oldSettings, 'js', undefined, 1024))
+ .addField('**New Value**', await util.inspectCleanRedactCodeblock(newSettings, 'js', undefined, 1024));
+
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}
diff --git a/src/listeners/member-custom/bushWarn.ts b/src/listeners/member-custom/bushWarn.ts
new file mode 100644
index 0000000..dba9fd8
--- /dev/null
+++ b/src/listeners/member-custom/bushWarn.ts
@@ -0,0 +1,31 @@
+import { BushListener, type BushClientEvents } from '#lib';
+import { GuildMember, MessageEmbed } from 'discord.js';
+
+export default class BushWarnListener extends BushListener {
+ public constructor() {
+ super('bushWarn', {
+ emitter: 'client',
+ event: 'bushWarn',
+ category: 'member-custom'
+ });
+ }
+
+ public override async exec(...[victim, moderator, guild, reason, caseID, dmSuccess]: BushClientEvents['bushWarn']) {
+ const logChannel = await guild.getLogChannel('moderation');
+ if (!logChannel) return;
+ const user = victim instanceof GuildMember ? victim.user : victim;
+
+ const logEmbed = new MessageEmbed()
+ .setColor(util.colors.discord.YELLOW)
+ .setTimestamp()
+ .setFooter({ text: `CaseID: ${caseID}` })
+ .setAuthor({ name: user.tag, iconURL: user.avatarURL({ dynamic: true, format: 'png', size: 4096 }) ?? undefined })
+ .addField('**Action**', `${'Warn'}`)
+ .addField('**User**', `${user} (${user.tag})`)
+ .addField('**Moderator**', `${moderator} (${moderator.tag})`)
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ .addField('**Reason**', `${reason || '[No Reason Provided]'}`);
+ if (dmSuccess === false) logEmbed.addField('**Additional Info**', 'Could not dm user.');
+ return await logChannel.send({ embeds: [logEmbed] });
+ }
+}