aboutsummaryrefslogtreecommitdiff
path: root/src/lib/common
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2022-07-06 22:10:58 +0200
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2022-07-06 22:10:58 +0200
commitadbb5e939cebcf4c0479d66162377957f2a845af (patch)
treee69b237c79fac2701f162fce6076ec1f85f617a9 /src/lib/common
parent22b3a8af49dc16bf5d8f9c3ce48b4770505ea33b (diff)
downloadtanzanite-adbb5e939cebcf4c0479d66162377957f2a845af.tar.gz
tanzanite-adbb5e939cebcf4c0479d66162377957f2a845af.tar.bz2
tanzanite-adbb5e939cebcf4c0479d66162377957f2a845af.zip
feat(automod): unmute button
Diffstat (limited to 'src/lib/common')
-rw-r--r--src/lib/common/AutoMod.ts148
-rw-r--r--src/lib/common/util/Moderation.ts24
2 files changed, 123 insertions, 49 deletions
diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts
index 970fecd..21bcb00 100644
--- a/src/lib/common/AutoMod.ts
+++ b/src/lib/common/AutoMod.ts
@@ -1,4 +1,4 @@
-import { banResponse, colors, emojis, format, formatError, Moderation } from '#lib';
+import { colors, emojis, format, formatError, Moderation, unmuteResponse } from '#lib';
import assert from 'assert';
import chalk from 'chalk';
import {
@@ -10,8 +10,10 @@ import {
PermissionFlagsBits,
type ButtonInteraction,
type Message,
+ type Snowflake,
type TextChannel
} from 'discord.js';
+import UnmuteCommand from '../../commands/moderation/unmute.js';
/**
* Handles auto moderation functionality.
@@ -165,21 +167,14 @@ export class AutoMod {
.setDescription(
`**User:** ${this.message.author} (${this.message.author.tag})\n**Sent From:** <#${this.message.channel.id}> [Jump to context](${this.message.url})`
)
- .addFields([
- { name: 'Message Content', value: `${await this.message.client.utils.codeblock(this.message.content, 1024)}` }
- ])
+ .addFields({
+ name: 'Message Content',
+ value: `${await this.message.client.utils.codeblock(this.message.content, 1024)}`
+ })
.setColor(color)
.setTimestamp()
],
- components: [
- new ActionRowBuilder<ButtonBuilder>().addComponents([
- new ButtonBuilder({
- style: ButtonStyle.Danger,
- label: 'Ban User',
- customId: `automod;ban;${this.message.author.id};everyone mention and scam phrase`
- })
- ])
- ]
+ components: [this.buttons(this.message.author.id, 'everyone mention and scam phrase')]
});
}
}
@@ -332,28 +327,33 @@ export class AutoMod {
this.message.channel.id
}> [Jump to context](${this.message.url})\n**Blacklisted Words:** ${offenses.map((o) => `\`${o.match}\``).join(', ')}`
)
- .addFields([
- { name: 'Message Content', value: `${await this.message.client.utils.codeblock(this.message.content, 1024)}` }
- ])
+ .addFields({
+ name: 'Message Content',
+ value: `${await this.message.client.utils.codeblock(this.message.content, 1024)}`
+ })
.setColor(color)
.setTimestamp()
.setAuthor({ name: this.message.author.tag, url: this.message.author.displayAvatarURL() })
],
- components:
- highestOffence.severity >= 2
- ? [
- new ActionRowBuilder<ButtonBuilder>().addComponents([
- new ButtonBuilder({
- style: ButtonStyle.Danger,
- label: 'Ban User',
- customId: `automod;ban;${this.message.author.id};${highestOffence.reason}`
- })
- ])
- ]
- : undefined
+ components: highestOffence.severity >= 2 ? [this.buttons(this.message.author.id, highestOffence.reason)] : undefined
});
}
+ private buttons(userId: Snowflake, reason: string): ActionRowBuilder<ButtonBuilder> {
+ return new ActionRowBuilder<ButtonBuilder>().addComponents(
+ new ButtonBuilder({
+ style: ButtonStyle.Danger,
+ label: 'Ban User',
+ customId: `automod;ban;${userId};${reason}`
+ }),
+ new ButtonBuilder({
+ style: ButtonStyle.Success,
+ label: 'Unmute User',
+ customId: `automod;unmute;${userId}`
+ })
+ );
+ }
+
/**
* Handles the ban button in the automod log.
* @param interaction The button interaction.
@@ -364,23 +364,31 @@ export class AutoMod {
content: `${emojis.error} You are missing the **Ban Members** permission.`,
ephemeral: true
});
- const [action, userId, reason] = interaction.customId.replace('automod;', '').split(';');
- switch (action) {
- case 'ban': {
- const victim = await interaction.guild!.members.fetch(userId).catch(() => null);
- const moderator =
- interaction.member instanceof GuildMember
- ? interaction.member
- : await interaction.guild!.members.fetch(interaction.user.id);
+ const [action, userId, reason] = interaction.customId.replace('automod;', '').split(';') as [
+ 'ban' | 'unmute',
+ string,
+ string
+ ];
- const check = victim ? await Moderation.permissionCheck(moderator, victim, 'ban', true) : true;
+ if ((['ban', 'unmute'] as const).includes(action)) throw new TypeError(`Invalid automod button action: ${action}`);
+
+ const victim = await interaction.guild!.members.fetch(userId).catch(() => null);
+ const moderator =
+ interaction.member instanceof GuildMember
+ ? interaction.member
+ : await interaction.guild!.members.fetch(interaction.user.id);
- if (check !== true)
+ switch (action) {
+ case 'ban': {
+ if (!interaction.guild?.members.me?.permissions.has('BanMembers'))
return interaction.reply({
- content: check,
+ content: `${emojis.error} I do not have permission to ${action} members.`,
ephemeral: true
});
+ const check = victim ? await Moderation.permissionCheck(moderator, victim, 'ban', true) : true;
+ if (check !== true) return interaction.reply({ content: check, ephemeral: true });
+
const result = await interaction.guild?.bushBan({
user: userId,
reason,
@@ -389,21 +397,65 @@ export class AutoMod {
});
const victimUserFormatted = (await interaction.client.utils.resolveNonCachedUser(userId))?.tag ?? userId;
- if (result === banResponse.SUCCESS)
- return interaction.reply({
- content: `${emojis.success} Successfully banned **${victimUserFormatted}**.`,
- ephemeral: true
- });
- else if (result === banResponse.DM_ERROR)
+
+ const content = (() => {
+ if (result === unmuteResponse.SUCCESS) {
+ return `${emojis.success} Successfully banned ${format.input(victimUserFormatted)}.`;
+ } else if (result === unmuteResponse.DM_ERROR) {
+ return `${emojis.warn} Banned ${format.input(victimUserFormatted)} however I could not send them a dm.`;
+ } else {
+ return `${emojis.error} Could not ban ${format.input(victimUserFormatted)}: \`${result}\` .`;
+ }
+ })();
+
+ return interaction.reply({
+ content: content,
+ ephemeral: true
+ });
+ }
+
+ case 'unmute': {
+ if (!victim)
return interaction.reply({
- content: `${emojis.warn} Banned ${victimUserFormatted} however I could not send them a dm.`,
+ content: `${emojis.error} Cannot find member, they may have left the server.`,
ephemeral: true
});
- else
+
+ if (!interaction.guild)
return interaction.reply({
- content: `${emojis.error} Could not ban **${victimUserFormatted}**: \`${result}\` .`,
+ content: `${emojis.error} This is weird, I don't seem to be in the server...`,
ephemeral: true
});
+
+ const check = await Moderation.permissionCheck(moderator, victim, 'unmute', true);
+ if (check !== true) return interaction.reply({ content: check, ephemeral: true });
+
+ const check2 = await Moderation.checkMutePermissions(interaction.guild);
+ if (check2 !== true)
+ return interaction.reply({ content: UnmuteCommand.formatCode('/', victim!, check2), ephemeral: true });
+
+ const result = await victim.bushUnmute({
+ reason,
+ moderator: interaction.member as GuildMember,
+ evidence: (interaction.message as Message).url ?? undefined
+ });
+
+ const victimUserFormatted = victim.user.tag;
+
+ const content = (() => {
+ if (result === unmuteResponse.SUCCESS) {
+ return `${emojis.success} Successfully unmuted ${format.input(victimUserFormatted)}.`;
+ } else if (result === unmuteResponse.DM_ERROR) {
+ return `${emojis.warn} Unmuted ${format.input(victimUserFormatted)} however I could not send them a dm.`;
+ } else {
+ return `${emojis.error} Could not unmute ${format.input(victimUserFormatted)}: \`${result}\` .`;
+ }
+ })();
+
+ return interaction.reply({
+ content: content,
+ ephemeral: true
+ });
}
}
}
diff --git a/src/lib/common/util/Moderation.ts b/src/lib/common/util/Moderation.ts
index cb6b4db..fc01602 100644
--- a/src/lib/common/util/Moderation.ts
+++ b/src/lib/common/util/Moderation.ts
@@ -1,13 +1,16 @@
import {
ActivePunishment,
ActivePunishmentType,
+ baseMuteResponse,
colors,
emojis,
format,
Guild as GuildDB,
humanizeDuration,
ModLog,
- type ModLogType
+ permissionsResponse,
+ type ModLogType,
+ type ValueOf
} from '#lib';
import assert from 'assert';
import {
@@ -119,6 +122,25 @@ export async function permissionCheck(
}
/**
+ * Performs permission checks that are required in order to (un)mute a member.
+ * @param guild The guild to check the mute permissions in.
+ * @returns A {@link MuteResponse} or true if nothing failed.
+ */
+export async function checkMutePermissions(
+ guild: Guild
+): Promise<ValueOf<typeof baseMuteResponse> | ValueOf<typeof permissionsResponse> | true> {
+ if (!guild.members.me!.permissions.has('ManageRoles')) return permissionsResponse.MISSING_PERMISSIONS;
+ const muteRoleID = await guild.getSetting('muteRole');
+ if (!muteRoleID) return baseMuteResponse.NO_MUTE_ROLE;
+ const muteRole = guild.roles.cache.get(muteRoleID);
+ if (!muteRole) return baseMuteResponse.MUTE_ROLE_INVALID;
+ if (muteRole.position >= guild.members.me!.roles.highest.position || muteRole.managed)
+ return baseMuteResponse.MUTE_ROLE_NOT_MANAGEABLE;
+
+ return true;
+}
+
+/**
* Creates a modlog entry for a punishment.
* @param options Options for creating a modlog entry.
* @param getCaseNumber Whether or not to get the case number of the entry.