2025-10-03 18:17:43 -05:00
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" ;
2025-10-03 18:17:43 -05:00
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 ,
2025-10-03 18:17:43 -05:00
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
}
2025-10-03 18:17:43 -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
2025-10-03 18:17:43 -05:00
// Otherwise, show interactive panel
await showDeletionPanel ( message , client ) ;
} ,
} ;
2025-09-17 13:33:10 -05:00
2025-10-03 18:17:43 -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-10-03 18:17:43 -05:00
} ) ;
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -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-10-03 18:17:43 -05:00
]
} ;
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -05:00
const confirmMessage = await message . reply ( {
embeds : [ confirmEmbed ] ,
components : [ confirmRow ]
} ) ;
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -05:00
await handleConfirmationInteraction ( confirmMessage , message , client , block ) ;
}
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -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
2025-10-03 18:17:43 -05:00
if ( blocks . length === 0 ) {
await handleNoBlocks ( message ) ;
return ;
}
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -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
2025-10-03 18:17:43 -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-10-03 18:17:43 -05:00
]
} ;
}
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -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
}
2025-10-03 18:17:43 -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
2025-10-03 18:17:43 -05:00
if ( selectedBlock ) {
await handleBlockSelection ( interaction , client , selectedBlock ) ;
collector . stop ( ) ;
2025-09-20 00:00:39 -05:00
}
}
2025-10-03 18:17:43 -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." ,
2025-10-03 22:38:28 -05:00
flags : 64 // Use flags instead of ephemeral
2025-09-20 00:00:39 -05:00
} ) ;
}
2025-10-03 18:17:43 -05:00
}
} ) ;
2025-09-20 00:00:39 -05:00
2025-10-03 18:17:43 -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
} ) ;
2025-10-03 22:38:28 -05:00
finalCollector . on ( "collect" , async ( finalInteraction : MessageComponentInteraction ) = > {
2025-10-03 18:17:43 -05:00
try {
2025-10-03 22:38:28 -05:00
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 ) ;
}
2025-10-03 18:17:43 -05:00
}
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
} ) ;
2025-10-03 22:38:28 -05:00
collector . on ( "collect" , async ( interaction : MessageComponentInteraction ) = > {
2025-10-03 18:17:43 -05:00
try {
2025-10-03 22:38:28 -05:00
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 } ) ;
}
2025-09-20 00:00:39 -05:00
}
2025-10-03 18:17:43 -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
} ) ;
2025-10-03 18:17:43 -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" ) ;
}
}