Files
amayo/src/commands/messages/alliaces/deleteDisplayComponent.ts

412 lines
14 KiB
TypeScript
Raw Normal View History

import {
Message,
ButtonInteraction,
StringSelectMenuInteraction,
MessageComponentInteraction,
ComponentType,
ButtonStyle,
APIEmbed
} from "discord.js";
2025-09-17 13:33:10 -05:00
import { CommandMessage } from "../../../core/types/commands";
import type Amayo from "../../../core/client";
import type { JsonValue } from "@prisma/client/runtime/library";
interface BlockItem {
name: string;
id: string;
}
interface ActionRowBuilder {
type: ComponentType.ActionRow;
components: any[];
}
2025-09-17 13:33:10 -05:00
export const command: CommandMessage = {
2025-09-20 00:00:39 -05:00
name: "eliminar-embed",
2025-09-17 13:33:10 -05:00
type: "message",
2025-09-20 00:00:39 -05:00
aliases: ["embed-eliminar", "borrar-embed", "embeddelete"],
2025-09-17 13:33:10 -05:00
cooldown: 10,
description: "Elimina bloques DisplayComponents del servidor",
category: "Alianzas",
usage: "eliminar-embed [nombre_bloque]",
run: async (message: Message, args: string[], client: Amayo): Promise<void> => {
2025-09-17 13:33:10 -05:00
if (!message.member?.permissions.has("Administrator")) {
2025-09-20 00:00:39 -05:00
await message.reply("❌ No tienes permisos de Administrador.");
return;
2025-09-17 13:33:10 -05:00
}
// If specific block name provided, handle direct deletion
if (args.length > 0) {
const blockName = args.join(" ").trim();
await handleDirectDeletion(message, client, blockName);
return;
}
2025-09-17 13:33:10 -05:00
// Otherwise, show interactive panel
await showDeletionPanel(message, client);
},
};
2025-09-17 13:33:10 -05:00
async function handleDirectDeletion(
message: Message,
client: Amayo,
blockName: string
): Promise<void> {
const block = await client.prisma.blockV2Config.findFirst({
where: {
guildId: message.guildId!,
name: blockName
2025-09-17 13:33:10 -05:00
}
});
2025-09-20 00:00:39 -05:00
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"
2025-09-20 00:00:39 -05:00
}
]
};
2025-09-20 00:00:39 -05:00
const confirmMessage = await message.reply({
embeds: [confirmEmbed],
components: [confirmRow]
});
2025-09-20 00:00:39 -05:00
await handleConfirmationInteraction(confirmMessage, message, client, block);
}
2025-09-20 00:00:39 -05:00
async function showDeletionPanel(message: Message, client: Amayo): Promise<void> {
const blocks = await fetchBlocks(client, message.guildId!);
2025-09-20 00:00:39 -05:00
if (blocks.length === 0) {
await handleNoBlocks(message);
return;
}
2025-09-20 00:00:39 -05:00
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<BlockItem[]> {
return await client.prisma.blockV2Config.findMany({
where: { guildId },
select: { name: true, id: true },
orderBy: { name: 'asc' }
});
}
async function handleNoBlocks(message: Message): Promise<void> {
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: "📄" }
}));
2025-09-20 00:00:39 -05:00
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
2025-09-20 00:00:39 -05:00
}
]
};
}
2025-09-20 00:00:39 -05:00
function createCancelRow(): ActionRowBuilder {
return {
type: ComponentType.ActionRow,
components: [
{
type: ComponentType.Button,
style: ButtonStyle.Danger,
label: "❌ Cancelar",
custom_id: "cancel_delete"
2025-09-20 00:00:39 -05:00
}
]
};
}
async function handlePanelInteractions(
panelMessage: Message,
originalMessage: Message,
client: Amayo,
blocks: BlockItem[]
): Promise<void> {
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);
2025-09-20 00:00:39 -05:00
if (selectedBlock) {
await handleBlockSelection(interaction, client, selectedBlock);
collector.stop();
2025-09-20 00:00:39 -05:00
}
}
} 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.",
ephemeral: true
2025-09-20 00:00:39 -05:00
});
}
}
});
2025-09-20 00:00:39 -05:00
collector.on("end", async (collected, reason) => {
if (reason === "time") {
await handlePanelTimeout(panelMessage);
}
});
}
async function handleCancellation(interaction: ButtonInteraction): Promise<void> {
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<void> {
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: ButtonInteraction) => {
try {
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<void> {
const collector = confirmMessage.createMessageComponentCollector({
time: 60000, // 1 minute
filter: (interaction: MessageComponentInteraction) => interaction.user.id === originalMessage.author.id
});
collector.on("collect", async (interaction: ButtonInteraction) => {
try {
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 });
2025-09-20 00:00:39 -05:00
}
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<void> {
try {
// Delete the block from database
await client.prisma.blockV2Config.delete({
where: { id: block.id }
2025-09-20 00:00:39 -05:00
});
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<void> {
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<void> {
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");
}
}