feat: add kick, mute, and unmute commands

This commit is contained in:
Ahmad 2025-04-16 22:10:47 -04:00
parent 49d274f2be
commit 20af09b279
No known key found for this signature in database
GPG key ID: 8FD8A93530D182BF
8 changed files with 395 additions and 7 deletions

View file

@ -0,0 +1,88 @@
import { PermissionsBitField, SlashCommandBuilder } from 'discord.js';
import { updateMemberModerationHistory } from '@/db/db.js';
import { OptionsCommand } from '@/types/CommandTypes.js';
import logAction from '@/util/logging/logAction.js';
const command: OptionsCommand = {
data: new SlashCommandBuilder()
.setName('kick')
.setDescription('Kick a member from the server')
.addUserOption((option) =>
option
.setName('member')
.setDescription('The member to kick')
.setRequired(true),
)
.addStringOption((option) =>
option
.setName('reason')
.setDescription('The reason for the kick')
.setRequired(true),
),
execute: async (interaction) => {
const moderator = await interaction.guild?.members.fetch(
interaction.user.id,
);
const member = await interaction.guild?.members.fetch(
interaction.options.get('member')!.value as string,
);
const reason = interaction.options.get('reason')?.value as string;
if (
!interaction.memberPermissions?.has(
PermissionsBitField.Flags.KickMembers,
) ||
moderator!.roles.highest.position <= member!.roles.highest.position ||
!member?.kickable
) {
await interaction.reply({
content:
'You do not have permission to kick members or this member cannot be kicked.',
flags: ['Ephemeral'],
});
return;
}
try {
try {
await member.user.send(
`You have been kicked from ${interaction.guild!.name}. Reason: ${reason}. You can join back at: \nhttps://discord.gg/KRTGjxx7gY`,
);
} catch (error) {
console.error('Failed to send DM to kicked user:', error);
}
await member.kick(reason);
await updateMemberModerationHistory({
discordId: member.id,
moderatorDiscordId: interaction.user.id,
action: 'kick',
reason,
duration: '',
createdAt: new Date(),
});
await logAction({
guild: interaction.guild!,
action: 'kick',
target: member,
moderator: moderator!,
reason,
});
await interaction.reply({
content: `<@${member.id}> has been kicked. Reason: ${reason}`,
});
} catch (error) {
console.error('Kick command error:', error);
await interaction.reply({
content: 'Unable to kick member.',
flags: ['Ephemeral'],
});
}
},
};
export default command;

View file

@ -0,0 +1,115 @@
import { PermissionsBitField, SlashCommandBuilder } from 'discord.js';
import { updateMember, updateMemberModerationHistory } from '@/db/db.js';
import { parseDuration } from '@/util/helpers.js';
import { OptionsCommand } from '@/types/CommandTypes.js';
import logAction from '@/util/logging/logAction.js';
const command: OptionsCommand = {
data: new SlashCommandBuilder()
.setName('mute')
.setDescription('Timeout a member in the server')
.addUserOption((option) =>
option
.setName('member')
.setDescription('The member to timeout')
.setRequired(true),
)
.addStringOption((option) =>
option
.setName('reason')
.setDescription('The reason for the timeout')
.setRequired(true),
)
.addStringOption((option) =>
option
.setName('duration')
.setDescription(
'The duration of the timeout (ex. 5m, 1h, 1d, 1w). Max 28 days.',
)
.setRequired(true),
),
execute: async (interaction) => {
const moderator = await interaction.guild?.members.fetch(
interaction.user.id,
);
const member = await interaction.guild?.members.fetch(
interaction.options.get('member')!.value as string,
);
const reason = interaction.options.get('reason')?.value as string;
const muteDuration = interaction.options.get('duration')?.value as string;
if (
!interaction.memberPermissions?.has(
PermissionsBitField.Flags.ModerateMembers,
) ||
moderator!.roles.highest.position <= member!.roles.highest.position ||
!member?.moderatable
) {
await interaction.reply({
content:
'You do not have permission to timeout members or this member cannot be timed out.',
flags: ['Ephemeral'],
});
return;
}
try {
const durationMs = parseDuration(muteDuration);
const maxTimeout = 28 * 24 * 60 * 60 * 1000;
if (durationMs > maxTimeout) {
await interaction.reply({
content: 'Timeout duration cannot exceed 28 days.',
flags: ['Ephemeral'],
});
return;
}
await member.user.send(
`You have been timed out in ${interaction.guild!.name} for ${muteDuration}. Reason: ${reason}.`,
);
await member.timeout(durationMs, reason);
const expiresAt = new Date(Date.now() + durationMs);
await updateMemberModerationHistory({
discordId: member.id,
moderatorDiscordId: interaction.user.id,
action: 'mute',
reason,
duration: muteDuration,
createdAt: new Date(),
expiresAt,
active: true,
});
await updateMember({
discordId: member.id,
currentlyMuted: true,
});
await logAction({
guild: interaction.guild!,
action: 'mute',
target: member,
moderator: moderator!,
reason,
duration: muteDuration,
});
await interaction.reply({
content: `<@${member.id}> has been timed out for ${muteDuration}. Reason: ${reason}`,
});
} catch (error) {
console.error('Mute command error:', error);
await interaction.reply({
content: 'Unable to timeout member.',
flags: ['Ephemeral'],
});
}
},
};
export default command;

View file

@ -0,0 +1,65 @@
import { PermissionsBitField, SlashCommandBuilder } from 'discord.js';
import { executeUnmute } from '@/util/helpers.js';
import { OptionsCommand } from '@/types/CommandTypes.js';
const command: OptionsCommand = {
data: new SlashCommandBuilder()
.setName('unmute')
.setDescription('Remove a timeout from a member')
.addUserOption((option) =>
option
.setName('member')
.setDescription('The member to unmute')
.setRequired(true),
)
.addStringOption((option) =>
option
.setName('reason')
.setDescription('The reason for removing the timeout')
.setRequired(true),
),
execute: async (interaction) => {
const moderator = await interaction.guild?.members.fetch(
interaction.user.id,
);
const member = await interaction.guild?.members.fetch(
interaction.options.get('member')!.value as string,
);
const reason = interaction.options.get('reason')?.value as string;
if (
!interaction.memberPermissions?.has(
PermissionsBitField.Flags.ModerateMembers,
)
) {
await interaction.reply({
content: 'You do not have permission to unmute members.',
flags: ['Ephemeral'],
});
return;
}
try {
await executeUnmute(
interaction.client,
interaction.guild!.id,
member!.id,
reason,
moderator,
);
await interaction.reply({
content: `<@${member!.id}>'s timeout has been removed. Reason: ${reason}`,
});
} catch (error) {
console.error('Unmute command error:', error);
await interaction.reply({
content: 'Unable to unmute member.',
flags: ['Ephemeral'],
});
}
},
};
export default command;

View file

@ -54,9 +54,6 @@ const command: OptionsCommand = {
await member!.user.send(
`You have been warned in **${interaction?.guild?.name}**. Reason: **${reason}**.`,
);
await interaction.reply(
`<@${member!.user.id}> has been warned. Reason: ${reason}`,
);
await logAction({
guild: interaction.guild!,
action: 'warn',
@ -64,6 +61,9 @@ const command: OptionsCommand = {
moderator: moderator!,
reason: reason,
});
await interaction.reply(
`<@${member!.user.id}> has been warned. Reason: ${reason}`,
);
} catch (error) {
console.error(error);
await interaction.reply({