import { createCommand } from "@shared/lib/utils"; import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js"; import { config } from "@/lib/config"; import { PruneService } from "@shared/modules/moderation/prune.service"; import { getConfirmationMessage, getProgressEmbed, getSuccessEmbed, getPruneErrorEmbed, getPruneWarningEmbed, getCancelledEmbed } from "@/modules/moderation/prune.view"; export const prune = createCommand({ data: new SlashCommandBuilder() .setName("prune") .setDescription("Delete messages in bulk (admin only)") .addIntegerOption(option => option .setName("amount") .setDescription(`Number of messages to delete (1-${config.moderation?.prune?.maxAmount || 100})`) .setRequired(false) .setMinValue(1) .setMaxValue(config.moderation?.prune?.maxAmount || 100) ) .addUserOption(option => option .setName("user") .setDescription("Only delete messages from this user") .setRequired(false) ) .addBooleanOption(option => option .setName("all") .setDescription("Delete all messages in the channel") .setRequired(false) ) .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), execute: async (interaction) => { await interaction.deferReply({ flags: MessageFlags.Ephemeral }); try { const amount = interaction.options.getInteger("amount"); const user = interaction.options.getUser("user"); const all = interaction.options.getBoolean("all") || false; // Validate inputs if (!amount && !all) { // Default to 10 messages } else if (amount && all) { await interaction.editReply({ embeds: [getPruneErrorEmbed("Cannot specify both `amount` and `all`. Please use one or the other.")] }); return; } const finalAmount = all ? 'all' : (amount || 10); const confirmThreshold = config.moderation.prune.confirmThreshold; // Check if confirmation is needed const needsConfirmation = all || (typeof finalAmount === 'number' && finalAmount > confirmThreshold); if (needsConfirmation) { // Estimate message count for confirmation let estimatedCount: number | undefined; if (all) { try { estimatedCount = await PruneService.estimateMessageCount(interaction.channel!); } catch { estimatedCount = undefined; } } const { embeds, components } = getConfirmationMessage(finalAmount, estimatedCount); const response = await interaction.editReply({ embeds, components }); try { const confirmation = await response.awaitMessageComponent({ filter: (i) => i.user.id === interaction.user.id, componentType: ComponentType.Button, time: 30000 }); if (confirmation.customId === "cancel_prune") { await confirmation.update({ embeds: [getCancelledEmbed()], components: [] }); return; } // User confirmed, proceed with deletion await confirmation.update({ embeds: [getProgressEmbed({ current: 0, total: estimatedCount || finalAmount as number })], components: [] }); // Execute deletion with progress callback for 'all' mode const result = await PruneService.deleteMessages( interaction.channel!, { amount: typeof finalAmount === 'number' ? finalAmount : undefined, userId: user?.id, all }, all ? async (progress) => { await interaction.editReply({ embeds: [getProgressEmbed(progress)] }); } : undefined ); // Show success await interaction.editReply({ embeds: [getSuccessEmbed(result)], components: [] }); } catch (error) { if (error instanceof Error && error.message.includes("time")) { await interaction.editReply({ embeds: [getPruneWarningEmbed("Confirmation timed out. Please try again.")], components: [] }); } else { throw error; } } } else { // No confirmation needed, proceed directly const result = await PruneService.deleteMessages( interaction.channel!, { amount: finalAmount as number, userId: user?.id, all: false } ); // Check if no messages were found if (result.deletedCount === 0) { if (user) { await interaction.editReply({ embeds: [getPruneWarningEmbed(`No messages found from **${user.username}** in the last ${finalAmount} messages.`)] }); } else { await interaction.editReply({ embeds: [getPruneWarningEmbed("No messages found to delete.")] }); } return; } await interaction.editReply({ embeds: [getSuccessEmbed(result)] }); } } catch (error) { console.error("Prune command error:", error); let errorMessage = "An unexpected error occurred while trying to delete messages."; if (error instanceof Error) { if (error.message.includes("permission")) { errorMessage = "I don't have permission to delete messages in this channel."; } else if (error.message.includes("channel type")) { errorMessage = "This command cannot be used in this type of channel."; } else { errorMessage = error.message; } } await interaction.editReply({ embeds: [getPruneErrorEmbed(errorMessage)] }); } } });