Refactor el comando de eliminación de bloques para mejorar la legibilidad y la gestión de interacciones, permitiendo la selección múltiple de bloques y simplificando la lógica de confirmación.
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
Message,
|
Message,
|
||||||
ButtonInteraction,
|
ButtonInteraction,
|
||||||
StringSelectMenuInteraction,
|
StringSelectMenuInteraction,
|
||||||
MessageComponentInteraction,
|
MessageComponentInteraction,
|
||||||
ComponentType,
|
ComponentType,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
APIEmbed
|
APIEmbed,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
import type Amayo from "../../../core/client";
|
import type Amayo from "../../../core/client";
|
||||||
@@ -13,405 +13,502 @@ import type { JsonValue } from "@prisma/client/runtime/library";
|
|||||||
import { hasManageGuildOrStaff } from "../../../core/lib/permissions";
|
import { hasManageGuildOrStaff } from "../../../core/lib/permissions";
|
||||||
|
|
||||||
interface BlockItem {
|
interface BlockItem {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActionRowBuilder {
|
interface ActionRowBuilder {
|
||||||
type: ComponentType.ActionRow;
|
type: ComponentType.ActionRow;
|
||||||
components: any[];
|
components: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: "eliminar-bloque",
|
name: "eliminar-bloque",
|
||||||
type: "message",
|
type: "message",
|
||||||
aliases: ["bloque-eliminar", "bloque-embed", "blockdelete"],
|
aliases: ["bloque-eliminar", "bloque-embed", "blockdelete"],
|
||||||
cooldown: 10,
|
cooldown: 10,
|
||||||
description: "Elimina bloques DisplayComponents del servidor",
|
description: "Elimina bloques DisplayComponents del servidor",
|
||||||
category: "Creacion",
|
category: "Creacion",
|
||||||
usage: "eliminar-bloque [nombre_bloque]",
|
usage: "eliminar-bloque [nombre_bloque]",
|
||||||
run: async (message: Message, args: string[], client: Amayo): Promise<void> => {
|
run: async (
|
||||||
const allowed = await hasManageGuildOrStaff(message.member, message.guildId!, client.prisma);
|
message: Message,
|
||||||
if (!allowed) {
|
args: string[],
|
||||||
await message.reply("❌ No tienes permisos de ManageGuild ni rol de staff.");
|
client: Amayo
|
||||||
return;
|
): Promise<void> => {
|
||||||
}
|
const allowed = await hasManageGuildOrStaff(
|
||||||
|
message.member,
|
||||||
// If specific block name provided, handle direct deletion
|
message.guildId!,
|
||||||
if (args.length > 0) {
|
client.prisma
|
||||||
const blockName = args.join(" ").trim();
|
);
|
||||||
await handleDirectDeletion(message, client, blockName);
|
if (!allowed) {
|
||||||
return;
|
await message.reply(
|
||||||
}
|
"❌ No tienes permisos de ManageGuild ni rol de staff."
|
||||||
|
);
|
||||||
// Otherwise, show interactive panel
|
return;
|
||||||
await showDeletionPanel(message, client);
|
}
|
||||||
},
|
await showDeletionPanel(message, client);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleDirectDeletion(
|
async function showDeletionPanel(
|
||||||
message: Message,
|
message: Message,
|
||||||
client: Amayo,
|
client: Amayo
|
||||||
blockName: string
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const block = await client.prisma.blockV2Config.findFirst({
|
const blocks = await fetchBlocks(client, message.guildId!);
|
||||||
where: {
|
|
||||||
guildId: message.guildId!,
|
|
||||||
name: blockName
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!block) {
|
if (blocks.length === 0) {
|
||||||
await message.reply(`❌ No se encontró un bloque llamado \`${blockName}\`.`);
|
await handleNoBlocks(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show confirmation for direct deletion
|
const deleteEmbed = createDeletionEmbed(blocks);
|
||||||
const confirmEmbed: APIEmbed = {
|
const actionRow = createBlockSelectRow(blocks);
|
||||||
color: 0xff6b35,
|
const cancelRow = createCancelRow();
|
||||||
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 = {
|
const panelMessage = await message.reply({
|
||||||
type: ComponentType.ActionRow,
|
embeds: [deleteEmbed],
|
||||||
components: [
|
components: [actionRow, cancelRow],
|
||||||
{
|
});
|
||||||
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({
|
await handlePanelInteractions(panelMessage, message, client, blocks);
|
||||||
embeds: [confirmEmbed],
|
|
||||||
components: [confirmRow]
|
|
||||||
});
|
|
||||||
|
|
||||||
await handleConfirmationInteraction(confirmMessage, message, client, block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showDeletionPanel(message: Message, client: Amayo): Promise<void> {
|
async function fetchBlocks(
|
||||||
const blocks = await fetchBlocks(client, message.guildId!);
|
client: Amayo,
|
||||||
|
guildId: string
|
||||||
if (blocks.length === 0) {
|
): Promise<BlockItem[]> {
|
||||||
await handleNoBlocks(message);
|
return await client.prisma.blockV2Config.findMany({
|
||||||
return;
|
where: { guildId },
|
||||||
}
|
select: { name: true, id: true },
|
||||||
|
orderBy: { name: "asc" },
|
||||||
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> {
|
async function handleNoBlocks(message: Message): Promise<void> {
|
||||||
const noBlocksEmbed: APIEmbed = {
|
const noBlocksEmbed: APIEmbed = {
|
||||||
color: 0xf04747,
|
color: 0xf04747,
|
||||||
title: "🗂️ Panel de Eliminación de Bloques",
|
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`.",
|
description:
|
||||||
footer: { text: "Sistema de gestión de bloques • Amayo Bot" }
|
"📭 **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({
|
await message.reply({
|
||||||
embeds: [noBlocksEmbed]
|
embeds: [noBlocksEmbed],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDeletionEmbed(blocks: BlockItem[]): APIEmbed {
|
function createDeletionEmbed(blocks: BlockItem[]): APIEmbed {
|
||||||
return {
|
return {
|
||||||
color: 0xff6b35,
|
color: 0xff6b35,
|
||||||
title: "🗑️ Panel de Eliminación de Bloques",
|
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:`,
|
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" }
|
footer: { text: "Selecciona un bloque para eliminar • Timeout: 5 minutos" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBlockSelectRow(blocks: BlockItem[]): ActionRowBuilder {
|
function createBlockSelectRow(blocks: BlockItem[]): ActionRowBuilder {
|
||||||
const selectOptions = blocks.slice(0, 25).map((block, index) => ({
|
const selectOptions = blocks.slice(0, 25).map((block, index) => ({
|
||||||
label: block.name,
|
label: block.name,
|
||||||
value: block.id, // Use ID instead of name for better uniqueness
|
value: block.id, // Use ID instead of name for better uniqueness
|
||||||
description: `ID: ${block.id.slice(-8)}`,
|
description: `ID: ${block.id.slice(-8)}`,
|
||||||
emoji: index < 10 ? { name: `${index + 1}️⃣` } : { name: "📄" }
|
emoji: index < 10 ? { name: `${index + 1}️⃣` } : { name: "📄" },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: ComponentType.ActionRow,
|
type: ComponentType.ActionRow,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
type: ComponentType.StringSelect,
|
type: ComponentType.StringSelect,
|
||||||
custom_id: "delete_block_select",
|
custom_id: "delete_block_select",
|
||||||
placeholder: "🗑️ Selecciona un bloque para eliminar...",
|
placeholder: "🗑️ Selecciona uno o varios bloques para eliminar...",
|
||||||
min_values: 1,
|
min_values: 1,
|
||||||
max_values: 1,
|
// Allow multi-select up to how many options we provided (max 25)
|
||||||
options: selectOptions
|
max_values: Math.min(25, blocks.length),
|
||||||
}
|
options: selectOptions,
|
||||||
]
|
},
|
||||||
};
|
],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCancelRow(): ActionRowBuilder {
|
function createCancelRow(): ActionRowBuilder {
|
||||||
return {
|
return {
|
||||||
type: ComponentType.ActionRow,
|
type: ComponentType.ActionRow,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
style: ButtonStyle.Danger,
|
style: ButtonStyle.Danger,
|
||||||
label: "❌ Cancelar",
|
label: "❌ Cancelar",
|
||||||
custom_id: "cancel_delete"
|
custom_id: "cancel_delete",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handlePanelInteractions(
|
async function handlePanelInteractions(
|
||||||
panelMessage: Message,
|
panelMessage: Message,
|
||||||
originalMessage: Message,
|
originalMessage: Message,
|
||||||
client: Amayo,
|
client: Amayo,
|
||||||
blocks: BlockItem[]
|
blocks: BlockItem[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const collector = panelMessage.createMessageComponentCollector({
|
const collector = panelMessage.createMessageComponentCollector({
|
||||||
time: 300000, // 5 minutes
|
time: 300000, // 5 minutes
|
||||||
filter: (interaction: MessageComponentInteraction) => interaction.user.id === originalMessage.author.id
|
filter: (interaction: MessageComponentInteraction) =>
|
||||||
});
|
interaction.user.id === originalMessage.author.id,
|
||||||
|
});
|
||||||
|
|
||||||
collector.on("collect", async (interaction: MessageComponentInteraction) => {
|
collector.on("collect", async (interaction: MessageComponentInteraction) => {
|
||||||
try {
|
try {
|
||||||
if (interaction.isButton() && interaction.customId === "cancel_delete") {
|
if (interaction.isButton() && interaction.customId === "cancel_delete") {
|
||||||
await handleCancellation(interaction);
|
await handleCancellation(interaction);
|
||||||
collector.stop();
|
collector.stop();
|
||||||
} else if (interaction.isStringSelectMenu() && interaction.customId === "delete_block_select") {
|
} else if (
|
||||||
const selectedBlockId = interaction.values[0];
|
interaction.isStringSelectMenu() &&
|
||||||
const selectedBlock = blocks.find(b => b.id === selectedBlockId);
|
interaction.customId === "delete_block_select"
|
||||||
|
) {
|
||||||
|
const selectedIds = interaction.values;
|
||||||
|
const selectedBlocks = blocks.filter((b) => selectedIds.includes(b.id));
|
||||||
|
|
||||||
if (selectedBlock) {
|
if (selectedBlocks.length === 1) {
|
||||||
await handleBlockSelection(interaction, client, selectedBlock);
|
await handleBlockSelection(interaction, client, selectedBlocks[0]);
|
||||||
collector.stop();
|
collector.stop();
|
||||||
}
|
} else if (selectedBlocks.length > 1) {
|
||||||
}
|
// Confirm batch deletion
|
||||||
} catch (error) {
|
await handleMultipleBlockSelection(
|
||||||
console.error("Error handling deletion interaction:", error);
|
interaction,
|
||||||
if (!interaction.replied && !interaction.deferred) {
|
client,
|
||||||
await interaction.reply({
|
selectedBlocks
|
||||||
content: "❌ Ocurrió un error al procesar la interacción.",
|
);
|
||||||
flags: 64 // Use flags instead of ephemeral
|
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) => {
|
collector.on("end", async (collected, reason) => {
|
||||||
if (reason === "time") {
|
if (reason === "time") {
|
||||||
await handlePanelTimeout(panelMessage);
|
await handlePanelTimeout(panelMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCancellation(interaction: ButtonInteraction): Promise<void> {
|
async function handleCancellation(
|
||||||
const canceledEmbed: APIEmbed = {
|
interaction: ButtonInteraction
|
||||||
color: 0x36393f,
|
): Promise<void> {
|
||||||
title: "❌ Operación Cancelada",
|
const canceledEmbed: APIEmbed = {
|
||||||
description: "La eliminación de bloques ha sido cancelada.\nNingún bloque fue eliminado.",
|
color: 0x36393f,
|
||||||
footer: { text: "Operación cancelada por el usuario" }
|
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({
|
await interaction.update({
|
||||||
embeds: [canceledEmbed],
|
embeds: [canceledEmbed],
|
||||||
components: []
|
components: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleBlockSelection(
|
async function handleBlockSelection(
|
||||||
interaction: StringSelectMenuInteraction,
|
interaction: StringSelectMenuInteraction,
|
||||||
client: Amayo,
|
client: Amayo,
|
||||||
selectedBlock: BlockItem
|
selectedBlock: BlockItem
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const confirmEmbed: APIEmbed = {
|
const confirmEmbed: APIEmbed = {
|
||||||
color: 0xff4444,
|
color: 0xff4444,
|
||||||
title: "⚠️ Confirmar Eliminación",
|
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.**`,
|
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" }
|
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 handleMultipleBlockSelection(
|
||||||
|
interaction: StringSelectMenuInteraction,
|
||||||
|
client: Amayo,
|
||||||
|
selectedBlocks: BlockItem[]
|
||||||
|
): Promise<void> {
|
||||||
|
const namesList = selectedBlocks
|
||||||
|
.map(
|
||||||
|
(b) => `• ${b.name} (
|
||||||
|
ID: ${b.id.slice(-8)})`
|
||||||
|
)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const confirmEmbed: APIEmbed = {
|
||||||
|
color: 0xff4444,
|
||||||
|
title: `⚠️ Confirmar Eliminación de ${selectedBlocks.length} bloques`,
|
||||||
|
description: `¿Estás seguro de que quieres **eliminar permanentemente** los siguientes bloques?\n\n${namesList}\n\n❗ **Esta acción NO se puede deshacer.**`,
|
||||||
|
footer: { text: "Confirma la eliminación usando los botones" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmRow: ActionRowBuilder = {
|
||||||
|
type: ComponentType.ActionRow,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: ComponentType.Button,
|
||||||
|
style: ButtonStyle.Danger,
|
||||||
|
label: `🗑️ ELIMINAR ${selectedBlocks.length}`,
|
||||||
|
custom_id: `confirm_batch_delete_${selectedBlocks
|
||||||
|
.map((b) => b.id)
|
||||||
|
.join("_")}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ComponentType.Button,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "❌ Cancelar",
|
||||||
|
custom_id: "cancel_delete_final",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [confirmEmbed],
|
||||||
|
components: [confirmRow],
|
||||||
|
});
|
||||||
|
|
||||||
|
const finalCollector = interaction.message.createMessageComponentCollector({
|
||||||
|
time: 60000,
|
||||||
|
filter: (i: MessageComponentInteraction) =>
|
||||||
|
i.user.id === interaction.user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
finalCollector.on(
|
||||||
|
"collect",
|
||||||
|
async (finalInteraction: MessageComponentInteraction) => {
|
||||||
|
try {
|
||||||
|
if (!finalInteraction.isButton()) return;
|
||||||
|
if (finalInteraction.customId === "cancel_delete_final") {
|
||||||
|
await handleCancellation(finalInteraction);
|
||||||
|
} else if (
|
||||||
|
finalInteraction.customId.startsWith("confirm_batch_delete_")
|
||||||
|
) {
|
||||||
|
await executeBlocksDeletion(finalInteraction, client, selectedBlocks);
|
||||||
|
}
|
||||||
|
finalCollector.stop();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error in batch final confirmation:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
finalCollector.on("end", async (_collected, reason) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
await handleConfirmationTimeout(interaction.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeBlocksDeletion(
|
||||||
|
interaction: ButtonInteraction,
|
||||||
|
client: Amayo,
|
||||||
|
blocks: BlockItem[]
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Delete many by IDs inside a transaction
|
||||||
|
const ids = blocks.map((b) => b.id);
|
||||||
|
await client.prisma.blockV2Config.deleteMany({
|
||||||
|
where: { id: { in: ids } },
|
||||||
|
});
|
||||||
|
|
||||||
|
const successEmbed: APIEmbed = {
|
||||||
|
color: 0x57f287,
|
||||||
|
title: "✅ Bloques Eliminados",
|
||||||
|
description: `Se eliminaron ${blocks.length} bloque(s) correctamente.`,
|
||||||
|
footer: { text: "Operación completada" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmRow: ActionRowBuilder = {
|
await interaction.update({ embeds: [successEmbed], components: [] });
|
||||||
type: ComponentType.ActionRow,
|
} catch (error) {
|
||||||
components: [
|
console.error("Error deleting blocks batch:", error);
|
||||||
{
|
const errorEmbed: APIEmbed = {
|
||||||
type: ComponentType.Button,
|
color: 0xf04747,
|
||||||
style: ButtonStyle.Danger,
|
title: "❌ Error al eliminar bloques",
|
||||||
label: "🗑️ SÍ, ELIMINAR",
|
description:
|
||||||
custom_id: `confirm_delete_${selectedBlock.id}`
|
"Ocurrió un error al eliminar los bloques seleccionados. Intenta de nuevo más tarde.",
|
||||||
},
|
footer: { text: "Error en la eliminación" },
|
||||||
{
|
|
||||||
type: ComponentType.Button,
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
label: "❌ Cancelar",
|
|
||||||
custom_id: "cancel_delete_final"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
await interaction.update({
|
await interaction.update({ embeds: [errorEmbed], components: [] });
|
||||||
embeds: [confirmEmbed],
|
} catch {}
|
||||||
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(
|
async function handleConfirmationInteraction(
|
||||||
confirmMessage: Message,
|
confirmMessage: Message,
|
||||||
originalMessage: Message,
|
originalMessage: Message,
|
||||||
client: Amayo,
|
client: Amayo,
|
||||||
block: any
|
block: any
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const collector = confirmMessage.createMessageComponentCollector({
|
const collector = confirmMessage.createMessageComponentCollector({
|
||||||
time: 60000, // 1 minute
|
time: 60000, // 1 minute
|
||||||
filter: (interaction: MessageComponentInteraction) => interaction.user.id === originalMessage.author.id
|
filter: (interaction: MessageComponentInteraction) =>
|
||||||
});
|
interaction.user.id === originalMessage.author.id,
|
||||||
|
});
|
||||||
|
|
||||||
collector.on("collect", async (interaction: MessageComponentInteraction) => {
|
collector.on("collect", async (interaction: MessageComponentInteraction) => {
|
||||||
try {
|
try {
|
||||||
if (interaction.isButton()) {
|
if (interaction.isButton()) {
|
||||||
if (interaction.customId === "cancel_delete") {
|
if (interaction.customId === "cancel_delete") {
|
||||||
await handleCancellation(interaction);
|
await handleCancellation(interaction);
|
||||||
} else if (interaction.customId === `confirm_delete_${block.id}`) {
|
} else if (interaction.customId === `confirm_delete_${block.id}`) {
|
||||||
await executeBlockDeletion(interaction, client, { name: block.name, id: block.id });
|
await executeBlockDeletion(interaction, client, {
|
||||||
}
|
name: block.name,
|
||||||
}
|
id: block.id,
|
||||||
collector.stop();
|
});
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in confirmation interaction:", error);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
collector.stop();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in confirmation interaction:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
collector.on("end", async (collected, reason) => {
|
collector.on("end", async (collected, reason) => {
|
||||||
if (reason === "time") {
|
if (reason === "time") {
|
||||||
await handleConfirmationTimeout(confirmMessage);
|
await handleConfirmationTimeout(confirmMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeBlockDeletion(
|
async function executeBlockDeletion(
|
||||||
interaction: ButtonInteraction,
|
interaction: ButtonInteraction,
|
||||||
client: Amayo,
|
client: Amayo,
|
||||||
block: BlockItem
|
block: BlockItem
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Delete the block from database
|
// Delete the block from database
|
||||||
await client.prisma.blockV2Config.delete({
|
await client.prisma.blockV2Config.delete({
|
||||||
where: { id: block.id }
|
where: { id: block.id },
|
||||||
});
|
});
|
||||||
|
|
||||||
const successEmbed: APIEmbed = {
|
const successEmbed: APIEmbed = {
|
||||||
color: 0x57f287,
|
color: 0x57f287,
|
||||||
title: "✅ Bloque Eliminado",
|
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}\``,
|
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" }
|
footer: { text: "Bloque eliminado permanentemente" },
|
||||||
};
|
};
|
||||||
|
|
||||||
await interaction.update({
|
await interaction.update({
|
||||||
embeds: [successEmbed],
|
embeds: [successEmbed],
|
||||||
components: []
|
components: [],
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting block:", error);
|
||||||
|
|
||||||
} catch (error) {
|
const errorEmbed: APIEmbed = {
|
||||||
console.error("Error deleting block:", error);
|
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" },
|
||||||
|
};
|
||||||
|
|
||||||
const errorEmbed: APIEmbed = {
|
await interaction.update({
|
||||||
color: 0xf04747,
|
embeds: [errorEmbed],
|
||||||
title: "❌ Error al Eliminar",
|
components: [],
|
||||||
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> {
|
async function handlePanelTimeout(panelMessage: Message): Promise<void> {
|
||||||
const timeoutEmbed: APIEmbed = {
|
const timeoutEmbed: APIEmbed = {
|
||||||
color: 0x36393f,
|
color: 0x36393f,
|
||||||
title: "⏰ Panel Expirado",
|
title: "⏰ Panel Expirado",
|
||||||
description: "El panel de eliminación ha expirado por inactividad.\n\nUsa `!eliminar-embed` para abrir un nuevo panel.",
|
description:
|
||||||
footer: { text: "Panel expirado por inactividad" }
|
"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 {
|
try {
|
||||||
await panelMessage.edit({
|
await panelMessage.edit({
|
||||||
embeds: [timeoutEmbed],
|
embeds: [timeoutEmbed],
|
||||||
components: []
|
components: [],
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Could not edit message on timeout, likely deleted");
|
console.log("Could not edit message on timeout, likely deleted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleConfirmationTimeout(confirmMessage: Message): Promise<void> {
|
async function handleConfirmationTimeout(
|
||||||
const timeoutEmbed: APIEmbed = {
|
confirmMessage: Message
|
||||||
color: 0x36393f,
|
): Promise<void> {
|
||||||
title: "⏰ Confirmación Expirada",
|
const timeoutEmbed: APIEmbed = {
|
||||||
description: "La confirmación ha expirado por inactividad.\nLa eliminación ha sido cancelada.",
|
color: 0x36393f,
|
||||||
footer: { text: "Confirmación expirada" }
|
title: "⏰ Confirmación Expirada",
|
||||||
};
|
description:
|
||||||
|
"La confirmación ha expirado por inactividad.\nLa eliminación ha sido cancelada.",
|
||||||
|
footer: { text: "Confirmación expirada" },
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await confirmMessage.edit({
|
await confirmMessage.edit({
|
||||||
embeds: [timeoutEmbed],
|
embeds: [timeoutEmbed],
|
||||||
components: []
|
components: [],
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Could not edit confirmation message on timeout");
|
console.log("Could not edit confirmation message on timeout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user