import { Message, ButtonInteraction, StringSelectMenuInteraction, MessageComponentInteraction, ComponentType, ButtonStyle, APIEmbed } from "discord.js"; import { CommandMessage } from "../../../core/types/commands"; import type Amayo from "../../../core/client"; import type { JsonValue } from "@prisma/client/runtime/library"; import { hasManageGuildOrStaff } from "../../../core/lib/permissions"; interface BlockItem { name: string; id: string; } interface ActionRowBuilder { type: ComponentType.ActionRow; components: any[]; } export const command: CommandMessage = { name: "eliminar-bloque", type: "message", aliases: ["bloque-eliminar", "bloque-embed", "blockdelete"], cooldown: 10, description: "Elimina bloques DisplayComponents del servidor", category: "Creacion", usage: "eliminar-bloque [nombre_bloque]", run: async (message: Message, args: string[], client: Amayo): Promise => { const allowed = await hasManageGuildOrStaff(message.member, message.guildId!, client.prisma); if (!allowed) { await message.reply("❌ No tienes permisos de ManageGuild ni rol de staff."); return; } // If specific block name provided, handle direct deletion if (args.length > 0) { const blockName = args.join(" ").trim(); await handleDirectDeletion(message, client, blockName); return; } // Otherwise, show interactive panel await showDeletionPanel(message, client); }, }; async function handleDirectDeletion( message: Message, client: Amayo, blockName: string ): Promise { const block = await client.prisma.blockV2Config.findFirst({ where: { guildId: message.guildId!, name: blockName } }); if (!block) { await message.reply(`❌ No se encontró un bloque llamado \`${blockName}\`.`); return; } // Show confirmation for direct deletion const confirmEmbed: APIEmbed = { color: 0xff6b35, title: "⚠️ Confirmar Eliminación", description: `¿Estás seguro de que quieres eliminar el bloque \`${blockName}\`?\n\n**Esta acción es irreversible.**`, footer: { text: "Confirma la eliminación usando los botones" } }; const confirmRow: ActionRowBuilder = { type: ComponentType.ActionRow, components: [ { type: ComponentType.Button, style: ButtonStyle.Danger, label: "🗑️ Confirmar Eliminación", custom_id: `confirm_delete_${block.id}` }, { type: ComponentType.Button, style: ButtonStyle.Secondary, label: "❌ Cancelar", custom_id: "cancel_delete" } ] }; const confirmMessage = await message.reply({ embeds: [confirmEmbed], components: [confirmRow] }); await handleConfirmationInteraction(confirmMessage, message, client, block); } async function showDeletionPanel(message: Message, client: Amayo): Promise { const blocks = await fetchBlocks(client, message.guildId!); if (blocks.length === 0) { await handleNoBlocks(message); return; } const deleteEmbed = createDeletionEmbed(blocks); const actionRow = createBlockSelectRow(blocks); const cancelRow = createCancelRow(); const panelMessage = await message.reply({ embeds: [deleteEmbed], components: [actionRow, cancelRow] }); await handlePanelInteractions(panelMessage, message, client, blocks); } async function fetchBlocks(client: Amayo, guildId: string): Promise { return await client.prisma.blockV2Config.findMany({ where: { guildId }, select: { name: true, id: true }, orderBy: { name: 'asc' } }); } async function handleNoBlocks(message: Message): Promise { const noBlocksEmbed: APIEmbed = { color: 0xf04747, title: "🗂️ Panel de Eliminación de Bloques", description: "📭 **No hay bloques disponibles**\n\nNo se encontraron bloques para eliminar en este servidor.\n\nPuedes crear nuevos bloques usando `!crear-embed`.", footer: { text: "Sistema de gestión de bloques • Amayo Bot" } }; await message.reply({ embeds: [noBlocksEmbed] }); } function createDeletionEmbed(blocks: BlockItem[]): APIEmbed { return { color: 0xff6b35, title: "🗑️ Panel de Eliminación de Bloques", description: `📊 **${blocks.length} bloque(s) encontrado(s)**\n\n⚠️ **ADVERTENCIA:** La eliminación es permanente e irreversible.\n\nSelecciona el bloque que deseas eliminar del menú de abajo:`, footer: { text: "Selecciona un bloque para eliminar • Timeout: 5 minutos" } }; } function createBlockSelectRow(blocks: BlockItem[]): ActionRowBuilder { const selectOptions = blocks.slice(0, 25).map((block, index) => ({ label: block.name, value: block.id, // Use ID instead of name for better uniqueness description: `ID: ${block.id.slice(-8)}`, emoji: index < 10 ? { name: `${index + 1}️⃣` } : { name: "📄" } })); return { type: ComponentType.ActionRow, components: [ { type: ComponentType.StringSelect, custom_id: "delete_block_select", placeholder: "🗑️ Selecciona un bloque para eliminar...", min_values: 1, max_values: 1, options: selectOptions } ] }; } function createCancelRow(): ActionRowBuilder { return { type: ComponentType.ActionRow, components: [ { type: ComponentType.Button, style: ButtonStyle.Danger, label: "❌ Cancelar", custom_id: "cancel_delete" } ] }; } async function handlePanelInteractions( panelMessage: Message, originalMessage: Message, client: Amayo, blocks: BlockItem[] ): Promise { const collector = panelMessage.createMessageComponentCollector({ time: 300000, // 5 minutes filter: (interaction: MessageComponentInteraction) => interaction.user.id === originalMessage.author.id }); collector.on("collect", async (interaction: MessageComponentInteraction) => { try { if (interaction.isButton() && interaction.customId === "cancel_delete") { await handleCancellation(interaction); collector.stop(); } else if (interaction.isStringSelectMenu() && interaction.customId === "delete_block_select") { const selectedBlockId = interaction.values[0]; const selectedBlock = blocks.find(b => b.id === selectedBlockId); if (selectedBlock) { await handleBlockSelection(interaction, client, selectedBlock); collector.stop(); } } } catch (error) { console.error("Error handling deletion interaction:", error); if (!interaction.replied && !interaction.deferred) { await interaction.reply({ content: "❌ Ocurrió un error al procesar la interacción.", flags: 64 // Use flags instead of ephemeral }); } } }); collector.on("end", async (collected, reason) => { if (reason === "time") { await handlePanelTimeout(panelMessage); } }); } async function handleCancellation(interaction: ButtonInteraction): Promise { const canceledEmbed: APIEmbed = { color: 0x36393f, title: "❌ Operación Cancelada", description: "La eliminación de bloques ha sido cancelada.\nNingún bloque fue eliminado.", footer: { text: "Operación cancelada por el usuario" } }; await interaction.update({ embeds: [canceledEmbed], components: [] }); } async function handleBlockSelection( interaction: StringSelectMenuInteraction, client: Amayo, selectedBlock: BlockItem ): Promise { const confirmEmbed: APIEmbed = { color: 0xff4444, title: "⚠️ Confirmar Eliminación", description: `¿Estás seguro de que quieres **eliminar permanentemente** el bloque?\n\n📄 **Nombre:** \`${selectedBlock.name}\`\n🔑 **ID:** \`${selectedBlock.id}\`\n\n❗ **Esta acción NO se puede deshacer.**`, footer: { text: "Confirma tu decisión usando los botones" } }; const confirmRow: ActionRowBuilder = { type: ComponentType.ActionRow, components: [ { type: ComponentType.Button, style: ButtonStyle.Danger, label: "🗑️ SÍ, ELIMINAR", custom_id: `confirm_delete_${selectedBlock.id}` }, { type: ComponentType.Button, style: ButtonStyle.Secondary, label: "❌ Cancelar", custom_id: "cancel_delete_final" } ] }; await interaction.update({ embeds: [confirmEmbed], components: [confirmRow] }); // Handle final confirmation const finalCollector = interaction.message.createMessageComponentCollector({ time: 60000, // 1 minute for final confirmation filter: (i: MessageComponentInteraction) => i.user.id === interaction.user.id }); finalCollector.on("collect", async (finalInteraction: MessageComponentInteraction) => { try { if (finalInteraction.isButton()) { if (finalInteraction.customId === "cancel_delete_final") { await handleCancellation(finalInteraction); } else if (finalInteraction.customId === `confirm_delete_${selectedBlock.id}`) { await executeBlockDeletion(finalInteraction, client, selectedBlock); } } finalCollector.stop(); } catch (error) { console.error("Error in final confirmation:", error); } }); finalCollector.on("end", async (collected, reason) => { if (reason === "time") { await handleConfirmationTimeout(interaction.message); } }); } async function handleConfirmationInteraction( confirmMessage: Message, originalMessage: Message, client: Amayo, block: any ): Promise { const collector = confirmMessage.createMessageComponentCollector({ time: 60000, // 1 minute filter: (interaction: MessageComponentInteraction) => interaction.user.id === originalMessage.author.id }); collector.on("collect", async (interaction: MessageComponentInteraction) => { try { if (interaction.isButton()) { if (interaction.customId === "cancel_delete") { await handleCancellation(interaction); } else if (interaction.customId === `confirm_delete_${block.id}`) { await executeBlockDeletion(interaction, client, { name: block.name, id: block.id }); } } collector.stop(); } catch (error) { console.error("Error in confirmation interaction:", error); } }); collector.on("end", async (collected, reason) => { if (reason === "time") { await handleConfirmationTimeout(confirmMessage); } }); } async function executeBlockDeletion( interaction: ButtonInteraction, client: Amayo, block: BlockItem ): Promise { try { // Delete the block from database await client.prisma.blockV2Config.delete({ where: { id: block.id } }); const successEmbed: APIEmbed = { color: 0x57f287, title: "✅ Bloque Eliminado", description: `El bloque \`${block.name}\` ha sido eliminado exitosamente.\n\n🗑️ **Operación completada**\n📄 **Bloque:** \`${block.name}\`\n🔑 **ID:** \`${block.id}\``, footer: { text: "Bloque eliminado permanentemente" } }; await interaction.update({ embeds: [successEmbed], components: [] }); } catch (error) { console.error("Error deleting block:", error); const errorEmbed: APIEmbed = { color: 0xf04747, title: "❌ Error al Eliminar", description: `No se pudo eliminar el bloque \`${block.name}\`.\n\nPor favor, inténtalo de nuevo más tarde.`, footer: { text: "Error en la eliminación" } }; await interaction.update({ embeds: [errorEmbed], components: [] }); } } async function handlePanelTimeout(panelMessage: Message): Promise { const timeoutEmbed: APIEmbed = { color: 0x36393f, title: "⏰ Panel Expirado", description: "El panel de eliminación ha expirado por inactividad.\n\nUsa `!eliminar-embed` para abrir un nuevo panel.", footer: { text: "Panel expirado por inactividad" } }; try { await panelMessage.edit({ embeds: [timeoutEmbed], components: [] }); } catch (error) { console.log("Could not edit message on timeout, likely deleted"); } } async function handleConfirmationTimeout(confirmMessage: Message): Promise { const timeoutEmbed: APIEmbed = { color: 0x36393f, title: "⏰ Confirmación Expirada", description: "La confirmación ha expirado por inactividad.\nLa eliminación ha sido cancelada.", footer: { text: "Confirmación expirada" } }; try { await confirmMessage.edit({ embeds: [timeoutEmbed], components: [] }); } catch (error) { console.log("Could not edit confirmation message on timeout"); } }