feat(bots/discord/commands): add purge and role-preset commands

This commit is contained in:
PalmDevs
2024-06-24 20:48:04 +07:00
parent 83c314ef5f
commit fb01ce5740
2 changed files with 157 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
import { EmbedBuilder, GuildChannel, SlashCommandBuilder } from 'discord.js'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { config } from '$/context'
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
import type { Command } from '..'
export default {
data: new SlashCommandBuilder()
.setName('purge')
.setDescription('Purge messages from a channel')
.addIntegerOption(option =>
option.setName('amount').setDescription('The amount of messages to remove').setMaxValue(100).setMinValue(1),
)
.addUserOption(option =>
option.setName('user').setDescription('The user to remove messages from (needs `until`)'),
)
.addStringOption(option =>
option.setName('until').setDescription('The message ID to remove messages until (overrides `amount`)'),
)
.toJSON(),
memberRequirements: {
roles: config.moderation?.roles ?? [],
},
global: false,
async execute({ logger }, interaction) {
const amount = interaction.options.getInteger('amount')
const user = interaction.options.getUser('user')
const until = interaction.options.getString('until')
if (!amount && !until)
throw new CommandError(CommandErrorType.MissingArgument, 'Either `amount` or `until` must be provided.')
const channel = interaction.channel!
if (!(channel.isTextBased() && channel instanceof GuildChannel))
throw new CommandError(CommandErrorType.InvalidChannel, 'The supplied channel is not a text channel.')
const embed = applyCommonEmbedStyles(
new EmbedBuilder({
title: 'Purging messages',
description: 'Accumulating messages...',
}),
true,
true,
true,
)
const msgsPromise = channel.messages.fetch(until ? { after: until } : { limit: amount! })
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch())
const messages = (
user ? (await msgsPromise).filter(msg => msg.author.id === user.id) : await msgsPromise
).filter(msg => msg.id !== reply.id)
await channel.bulkDelete(messages, true)
logger.info(
`Moderator ${interaction.user.tag} (${interaction.user.id}) purged ${messages.size} messages in #${channel.name} (${channel.id})`,
)
await reply.edit({
embeds: [
embed.setTitle('Purged messages').setDescription(null).addFields({
name: 'Deleted messages',
value: messages.size.toString(),
}),
],
})
},
} satisfies Command

View File

@@ -0,0 +1,84 @@
import { SlashCommandBuilder } from 'discord.js'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { sendPresetReplyAndLogs } from '$/utils/discord/moderation'
import { applyRolePreset, removeRolePreset } from '$/utils/discord/rolePresets'
import { parseDuration } from '$/utils/duration'
import type { Command } from '..'
export default {
data: new SlashCommandBuilder()
.setName('role-preset')
.setDescription('Manage role presets for a member')
.addStringOption(option =>
option
.setName('action')
.setRequired(true)
.setDescription('The action to perform')
.addChoices([
{ name: 'apply', value: 'apply' },
{ name: 'remove', value: 'remove' },
]),
)
.addUserOption(option => option.setName('member').setRequired(true).setDescription('The member to manage'))
.addStringOption(option =>
option.setName('preset').setRequired(true).setDescription('The preset to apply or remove'),
)
.addStringOption(option =>
option.setName('duration').setDescription('The duration to apply the preset for (only for apply action)'),
)
.toJSON(),
memberRequirements: {
roles: ['955220417969262612', '973886585294704640'],
},
global: false,
async execute({ logger }, interaction) {
const action = interaction.options.getString('action', true) as 'apply' | 'remove'
const user = interaction.options.getUser('member', true)
const preset = interaction.options.getString('preset', true)
const duration = interaction.options.getString('duration')
let expires: number | null | undefined = undefined
const moderator = await interaction.guild!.members.fetch(interaction.user.id)
const member = await interaction.guild!.members.fetch(user.id)
if (!member)
throw new CommandError(
CommandErrorType.InvalidUser,
'The provided member is not in the server or does not exist.',
)
if (member.manageable)
throw new CommandError(CommandErrorType.Generic, 'This user cannot be managed by the bot.')
if (action === 'apply') {
const durationMs = duration ? parseDuration(duration) : null
if (Number.isInteger(durationMs) && durationMs! < 1)
throw new CommandError(
CommandErrorType.InvalidDuration,
'The duration must be at least 1 millisecond long.',
)
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0)
throw new CommandError(
CommandErrorType.InvalidUser,
'You cannot apply a role preset to a user with a role equal to or higher than yours.',
)
expires = durationMs ? Date.now() + durationMs : null
await applyRolePreset(member, preset, expires)
logger.info(
`Moderator ${interaction.user.tag} (${interaction.user.id}) applied role preset ${preset} to ${user.id} until ${expires}`,
)
} else if (action === 'remove') {
await removeRolePreset(member, preset)
logger.info(
`Moderator ${interaction.user.tag} (${interaction.user.id}) removed role preset ${preset} from ${user.id}`,
)
}
await sendPresetReplyAndLogs(action, interaction, user, preset, expires)
},
} satisfies Command