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 { config } from '$/context'
import type { Command } from '..'
export default {
@@ -16,7 +17,7 @@ export default {
.toJSON(),
memberRequirements: {
roles: ['955220417969262612', '973886585294704640'],
roles: config.moderation?.roles ?? [],
},
global: false,

View File

@@ -2,8 +2,10 @@ import { SlashCommandBuilder } from 'discord.js'
import type { Command } from '..'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
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'
export default {
@@ -23,27 +25,34 @@ export default {
global: false,
async execute({ config, logger }, interaction) {
const user = interaction.options.getUser('member', true)
const reason = interaction.options.getString('reason') ?? undefined
async execute({ logger }, interaction) {
const user = interaction.options.getUser('user', true)
const reason = interaction.options.getString('reason') ?? 'No reason provided'
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)
await interaction.guild!.members.ban(user, {
reason: `Banned by moderator ${interaction.user.tag} (${interaction.user.id}): ${reason}`,
deleteMessageSeconds: dms,
})
const embed = createModerationActionEmbed('Banned', user, interaction.user, reason ?? 'No reason provided')
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch())
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)] })
}
await sendModerationReplyAndLogs(
interaction,
createModerationActionEmbed('Banned', user, interaction.user, reason),
)
logger.info(
`${interaction.user.tag} (${interaction.user.id}) banned ${user.tag} (${user.id}) because ${reason}, deleting their messages sent in the previous ${dms}s`,
)
},
} satisfies Command

View File

@@ -5,7 +5,8 @@ import { applyRolePreset } from '$/utils/discord/rolePresets'
import type { Command } from '..'
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'
export default {
@@ -23,9 +24,9 @@ export default {
global: false,
async execute({ config, logger }, interaction) {
async execute({ logger }, interaction) {
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 durationMs = duration ? parseDuration(duration) : null
@@ -35,6 +36,8 @@ export default {
'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)
if (!member)
throw new CommandError(
@@ -42,25 +45,23 @@ export default {
'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(
'Muted',
user,
interaction.user,
reason ?? 'No reason provided',
durationMs,
if (moderator.roles.highest.comparePositionTo(member.roles.highest) <= 0)
throw new CommandError(
CommandErrorType.InvalidUser,
'You cannot mute a user with a role equal to or higher than yours.',
)
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())
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)] })
}
logger.info(
`Moderator ${interaction.user.tag} (${interaction.user.id}) muted ${user.tag} (${user.id}) until ${expires} because ${reason}`,
)
},
} satisfies Command

View File

@@ -48,12 +48,18 @@ export default {
logger.info(`Setting slowmode to ${duration}ms on ${channel.id}`)
await channel.setRateLimitPerUser(
duration / 1000,
`Slowmode set by @${interaction.user.username} (${interaction.user.id})`,
)
await channel.setRateLimitPerUser(duration / 1000, `Set by ${interaction.user.tag} (${interaction.user.id})`)
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

View File

@@ -3,7 +3,8 @@ import { SlashCommandBuilder } from 'discord.js'
import type { Command } from '..'
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 {
data: new SlashCommandBuilder()
@@ -18,24 +19,15 @@ export default {
global: false,
async execute({ config, logger }, interaction) {
const user = interaction.options.getUser('member', true)
async execute({ logger }, interaction) {
const user = interaction.options.getUser('user', true)
await interaction.guild!.members.unban(
user,
`Unbanned by moderator ${interaction.user.tag} (${interaction.user.id})`,
)
const embed = createModerationActionEmbed('Unbanned', user, interaction.user)
const reply = await interaction.reply({ embeds: [embed] }).then(it => it.fetch())
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)] })
}
await sendModerationReplyAndLogs(interaction, createModerationActionEmbed('Unbanned', user, interaction.user))
logger.info(`${interaction.user.tag} (${interaction.user.id}) unbanned ${user.tag} (${user.id})`)
},
} satisfies Command

View File

@@ -2,8 +2,11 @@ import { SlashCommandBuilder } from 'discord.js'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
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 { and, eq } from 'drizzle-orm'
import type { Command } from '..'
export default {
@@ -19,7 +22,7 @@ export default {
global: false,
async execute({ config, logger }, interaction) {
async execute({ logger, database }, interaction) {
const user = interaction.options.getUser('member', true)
const member = await interaction.guild!.members.fetch(user.id)
if (!member)
@@ -28,18 +31,16 @@ export default {
'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')
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())
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)] })
}
logger.info(`Moderator ${interaction.user.tag} (${interaction.user.id}) unmuted ${user.tag} (${user.id})`)
},
} satisfies Command