fix(bots/discord/commands): refactor and add checks

This commit is contained in:
PalmDevs
2024-06-24 20:50:46 +07:00
parent fb01ce5740
commit a2bf3eade9
7 changed files with 78 additions and 68 deletions

View File

@@ -1,5 +1,6 @@
import { SlashCommandBuilder, type TextBasedChannel } from 'discord.js' import { SlashCommandBuilder, type TextBasedChannel } from 'discord.js'
import { config } from '$/context'
import type { Command } from '..' import type { Command } from '..'
export default { export default {
@@ -16,7 +17,7 @@ export default {
.toJSON(), .toJSON(),
memberRequirements: { memberRequirements: {
roles: ['955220417969262612', '973886585294704640'], roles: config.moderation?.roles ?? [],
}, },
global: false, global: false,

View File

@@ -2,8 +2,10 @@ import { SlashCommandBuilder } from 'discord.js'
import type { Command } from '..' import type { Command } from '..'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { config } from '$/context' import { config } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from '$/utils/discord/embeds' import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
import { parseDuration } from '$/utils/duration' import { parseDuration } from '$/utils/duration'
export default { export default {
@@ -23,27 +25,34 @@ export default {
global: false, global: false,
async execute({ config, logger }, interaction) { async execute({ logger }, interaction) {
const user = interaction.options.getUser('member', true) const user = interaction.options.getUser('user', true)
const reason = interaction.options.getString('reason') ?? undefined const reason = interaction.options.getString('reason') ?? 'No reason provided'
const dmd = interaction.options.getString('dmd') const dmd = interaction.options.getString('dmd')
const member = await interaction.guild!.members.fetch(user.id)
const moderator = await interaction.guild!.members.fetch(interaction.user.id)
if (member.bannable) throw new CommandError(CommandErrorType.Generic, 'This user cannot be banned by the bot.')
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0)
throw new CommandError(
CommandErrorType.InvalidUser,
'You cannot ban a user with a role equal to or higher than yours.',
)
const dms = Math.floor(dmd ? parseDuration(dmd) : 0 / 1000) const dms = Math.floor(dmd ? parseDuration(dmd) : 0 / 1000)
await interaction.guild!.members.ban(user, { await interaction.guild!.members.ban(user, {
reason: `Banned by moderator ${interaction.user.tag} (${interaction.user.id}): ${reason}`, reason: `Banned by moderator ${interaction.user.tag} (${interaction.user.id}): ${reason}`,
deleteMessageSeconds: dms, deleteMessageSeconds: dms,
}) })
const embed = createModerationActionEmbed('Banned', user, interaction.user, reason ?? 'No reason provided') await sendModerationReplyAndLogs(
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch()) interaction,
createModerationActionEmbed('Banned', user, interaction.user, reason),
const logConfig = config.moderation?.log )
if (logConfig) { logger.info(
const channel = await interaction.guild!.channels.fetch(logConfig.thread ?? logConfig.channel) `${interaction.user.tag} (${interaction.user.id}) banned ${user.tag} (${user.id}) because ${reason}, deleting their messages sent in the previous ${dms}s`,
if (!channel || !channel.isTextBased()) )
return void logger.warn('The moderation log channel does not exist, skipping logging')
await channel.send({ embeds: [applyReferenceToModerationActionEmbed(embed, reply.url)] })
}
}, },
} satisfies Command } satisfies Command

View File

@@ -5,7 +5,8 @@ import { applyRolePreset } from '$/utils/discord/rolePresets'
import type { Command } from '..' import type { Command } from '..'
import { config } from '$/context' import { config } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from '$/utils/discord/embeds' import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
import { parseDuration } from '$/utils/duration' import { parseDuration } from '$/utils/duration'
export default { export default {
@@ -23,9 +24,9 @@ export default {
global: false, global: false,
async execute({ config, logger }, interaction) { async execute({ logger }, interaction) {
const user = interaction.options.getUser('member', true) const user = interaction.options.getUser('member', true)
const reason = interaction.options.getString('reason') const reason = interaction.options.getString('reason') ?? 'No reason provided'
const duration = interaction.options.getString('duration') const duration = interaction.options.getString('duration')
const durationMs = duration ? parseDuration(duration) : null const durationMs = duration ? parseDuration(duration) : null
@@ -35,6 +36,8 @@ export default {
'The duration must be at least 1 millisecond long.', 'The duration must be at least 1 millisecond long.',
) )
const expires = durationMs ? Date.now() + durationMs : null
const moderator = await interaction.guild!.members.fetch(interaction.user.id)
const member = await interaction.guild!.members.fetch(user.id) const member = await interaction.guild!.members.fetch(user.id)
if (!member) if (!member)
throw new CommandError( throw new CommandError(
@@ -42,25 +45,23 @@ export default {
'The provided member is not in the server or does not exist.', 'The provided member is not in the server or does not exist.',
) )
await applyRolePreset(member, 'mute', durationMs ? Date.now() + durationMs : null) if (member.manageable)
throw new CommandError(CommandErrorType.Generic, 'This user cannot be managed by the bot.')
const embed = createModerationActionEmbed( if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0)
'Muted', throw new CommandError(
user, CommandErrorType.InvalidUser,
interaction.user, 'You cannot mute a user with a role equal to or higher than yours.',
reason ?? 'No reason provided', )
durationMs,
await applyRolePreset(member, 'mute', durationMs ? Date.now() + durationMs : null)
await sendModerationReplyAndLogs(
interaction,
createModerationActionEmbed('Muted', user, interaction.user, reason, durationMs),
) )
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch()) logger.info(
`Moderator ${interaction.user.tag} (${interaction.user.id}) muted ${user.tag} (${user.id}) until ${expires} because ${reason}`,
const logConfig = config.moderation?.log )
if (logConfig) {
const channel = await interaction.guild!.channels.fetch(logConfig.thread ?? logConfig.channel)
if (!channel || !channel.isTextBased())
return void logger.warn('The moderation log channel does not exist, skipping logging')
await channel.send({ embeds: [applyReferenceToModerationActionEmbed(embed, reply.url)] })
}
}, },
} satisfies Command } satisfies Command

View File

@@ -48,12 +48,18 @@ export default {
logger.info(`Setting slowmode to ${duration}ms on ${channel.id}`) logger.info(`Setting slowmode to ${duration}ms on ${channel.id}`)
await channel.setRateLimitPerUser( await channel.setRateLimitPerUser(duration / 1000, `Set by ${interaction.user.tag} (${interaction.user.id})`)
duration / 1000,
`Slowmode set by @${interaction.user.username} (${interaction.user.id})`,
)
await interaction.reply({ await interaction.reply({
embeds: [createSuccessEmbed(`Slowmode set to ${durationToString(duration)} on ${channel.toString()}`)], embeds: [
createSuccessEmbed(
`Slowmode ${duration ? `set to ${durationToString(duration)}` : 'removed'} on ${channel.toString()}`,
),
],
}) })
logger.info(
`${interaction.user.tag} (${interaction.user.id}) set the slowmode on ${channel.name} (${channel.id}) to ${duration}ms`,
)
}, },
} satisfies Command } satisfies Command

View File

@@ -3,7 +3,8 @@ import { SlashCommandBuilder } from 'discord.js'
import type { Command } from '..' import type { Command } from '..'
import { config } from '$/context' import { config } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from '$/utils/discord/embeds' import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
export default { export default {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@@ -18,24 +19,15 @@ export default {
global: false, global: false,
async execute({ config, logger }, interaction) { async execute({ logger }, interaction) {
const user = interaction.options.getUser('member', true) const user = interaction.options.getUser('user', true)
await interaction.guild!.members.unban( await interaction.guild!.members.unban(
user, user,
`Unbanned by moderator ${interaction.user.tag} (${interaction.user.id})`, `Unbanned by moderator ${interaction.user.tag} (${interaction.user.id})`,
) )
const embed = createModerationActionEmbed('Unbanned', user, interaction.user) await sendModerationReplyAndLogs(interaction, createModerationActionEmbed('Unbanned', user, interaction.user))
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch()) logger.info(`${interaction.user.tag} (${interaction.user.id}) unbanned ${user.tag} (${user.id})`)
const logConfig = config.moderation?.log
if (logConfig) {
const channel = await interaction.guild!.channels.fetch(logConfig.thread ?? logConfig.channel)
if (!channel || !channel.isTextBased())
return void logger.warn('The moderation log channel does not exist, skipping logging')
await channel.send({ embeds: [applyReferenceToModerationActionEmbed(embed, reply.url)] })
}
}, },
} satisfies Command } satisfies Command

View File

@@ -2,8 +2,11 @@ import { SlashCommandBuilder } from 'discord.js'
import CommandError, { CommandErrorType } from '$/classes/CommandError' import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { config } from '$/context' import { config } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from '$/utils/discord/embeds' import { appliedPresets } from '$/database/schemas'
import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
import { removeRolePreset } from '$/utils/discord/rolePresets' import { removeRolePreset } from '$/utils/discord/rolePresets'
import { and, eq } from 'drizzle-orm'
import type { Command } from '..' import type { Command } from '..'
export default { export default {
@@ -19,7 +22,7 @@ export default {
global: false, global: false,
async execute({ config, logger }, interaction) { async execute({ logger, database }, interaction) {
const user = interaction.options.getUser('member', true) const user = interaction.options.getUser('member', true)
const member = await interaction.guild!.members.fetch(user.id) const member = await interaction.guild!.members.fetch(user.id)
if (!member) if (!member)
@@ -28,18 +31,16 @@ export default {
'The provided member is not in the server or does not exist.', 'The provided member is not in the server or does not exist.',
) )
if (
!(await database.query.appliedPresets.findFirst({
where: and(eq(appliedPresets.memberId, member.id), eq(appliedPresets.preset, 'mute')),
}))
)
throw new CommandError(CommandErrorType.Generic, 'This user is not muted.')
await removeRolePreset(member, 'mute') await removeRolePreset(member, 'mute')
const embed = createModerationActionEmbed('Unmuted', user, interaction.user) await sendModerationReplyAndLogs(interaction, createModerationActionEmbed('Unmuted', user, interaction.user))
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch()) logger.info(`Moderator ${interaction.user.tag} (${interaction.user.id}) unmuted ${user.tag} (${user.id})`)
const logConfig = config.moderation?.log
if (logConfig) {
const channel = await interaction.guild!.channels.fetch(logConfig.thread ?? logConfig.channel)
if (!channel || !channel.isTextBased())
return void logger.warn('The moderation log channel does not exist, skipping logging')
await channel.send({ embeds: [applyReferenceToModerationActionEmbed(embed, reply.url)] })
}
}, },
} satisfies Command } satisfies Command