diff --git a/src/commands/messages/settings-server/settings.ts b/src/commands/messages/settings-server/settings.ts index 0a7d223..c9647aa 100644 --- a/src/commands/messages/settings-server/settings.ts +++ b/src/commands/messages/settings-server/settings.ts @@ -3,381 +3,664 @@ import { CommandMessage } from "../../../core/types/commands"; import { ComponentType, TextInputStyle } from "discord-api-types/v10"; import { hasManageGuildOrStaff } from "../../../core/lib/permissions"; import { aiService } from "../../../core/services/AIService"; +import { invalidateGuildCache } from "../../../core/database/guildCache"; function toStringArray(input: unknown): string[] { - if (!Array.isArray(input)) return []; - return (input as unknown[]).filter((v): v is string => typeof v === 'string'); + if (!Array.isArray(input)) return []; + return (input as unknown[]).filter((v): v is string => typeof v === "string"); } export const command: CommandMessage = { - name: 'configuracion', - type: "message", - aliases: ['config', 'ajustes', 'settings'], - cooldown: 5, - description: 'Abre el panel de configuración del servidor (prefix, staff y más).', - category: 'Configuración', - usage: 'configuracion', - run: async (message, args, client) => { - const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, client.prisma); - if (!allowed) { - await message.reply("❌ No tienes permisos de ManageGuild ni rol de staff."); - return; + name: "configuracion", + type: "message", + aliases: ["config", "ajustes", "settings"], + cooldown: 5, + description: + "Abre el panel de configuración del servidor (prefix, staff y más).", + category: "Configuración", + usage: "configuracion", + run: async (message, args, client) => { + const allowed = await hasManageGuildOrStaff( + message.member, + message.guild!.id, + client.prisma + ); + if (!allowed) { + await message.reply( + "❌ No tienes permisos de ManageGuild ni rol de staff." + ); + return; + } + + const server = await client.prisma.guild.findFirst({ + where: { id: message.guild!.id }, + }); + + const currentPrefix = server?.prefix || "!"; + const staffRoles: string[] = toStringArray(server?.staff); + const staffDisplay = staffRoles.length + ? staffRoles.map((id) => `<@&${id}>`).join(", ") + : "Sin staff configurado"; + const aiRolePrompt = server?.aiRolePrompt ?? null; + const aiPreview = aiRolePrompt + ? aiRolePrompt.length > 80 + ? aiRolePrompt.slice(0, 77) + "…" + : aiRolePrompt + : "No configurado"; + + // Panel de configuración usando DisplayComponents + const settingsPanel = { + type: 17, + accent_color: 6178018, // Color del ejemplo + components: [ + { + type: 10, + content: "### <:invisible:1418684224441028608> 梅,panel admin,📢\n", + }, + { type: 14, spacing: 1, divider: false }, + { type: 10, content: "Configuracion del Servidor:" }, + { + type: 9, + components: [ + { + type: 10, + content: `**Prefix:**<:invisible:1418684224441028608>\`${currentPrefix}\``, + }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "⚙️" }, + custom_id: "open_prefix_modal", + label: "Cambiar", + }, + }, + { type: 14, divider: false }, + { + type: 9, + components: [ + { type: 10, content: `**Staff (roles):** ${staffDisplay}` }, + ], + accessory: { + type: 2, + style: 2, // Secondary + emoji: { name: "🛡️" }, + custom_id: "open_staff_modal", + label: "Configurar", + }, + }, + { type: 14, divider: false }, + { + type: 9, + components: [ + { type: 10, content: `**AI Role Prompt:** ${aiPreview}` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "🧠" }, + custom_id: "open_ai_role_modal", + label: "Configurar", + }, + }, + { type: 14, divider: false }, + ], + }; + + const panelMessage = await message.reply({ + flags: 32768, // Components v2 + components: [settingsPanel], + }); + + const collector = panelMessage.createMessageComponentCollector({ + time: 300000, // 5 minutos + filter: (i: any) => i.user.id === message.author.id, + }); + + collector.on("collect", async (interaction: any) => { + if (interaction.customId === "open_prefix_modal") { + // Crear y mostrar modal para cambiar prefix (formato consistente con otros modales) + const prefixModal = { + title: "⚙️ Configurar Prefix del Servidor", + customId: "prefix_settings_modal", + components: [ + { + type: ComponentType.Label, + label: "Nuevo Prefix", + component: { + type: ComponentType.TextInput, + customId: "new_prefix_input", + style: TextInputStyle.Short, + placeholder: `Prefix actual: ${currentPrefix}`, + required: true, + maxLength: 10, + minLength: 1, + value: currentPrefix, + }, + }, + { + type: ComponentType.Label, + label: "Motivo (opcional)", + component: { + type: ComponentType.TextInput, + customId: "prefix_description", + style: TextInputStyle.Paragraph, + placeholder: "Ej: evitar conflictos con otros bots...", + required: false, + maxLength: 200, + }, + }, + ], + } as const; + + try { + await interaction.showModal(prefixModal); + } catch (err) { + try { + await interaction.reply({ + content: "❌ No se pudo abrir el modal de prefix.", + flags: 64, + }); + } catch {} + return; } - const server = await client.prisma.guild.findFirst({ - where: { id: message.guild!.id } - }); + try { + const modalInteraction = await interaction.awaitModalSubmit({ + time: 300000, + filter: (modalInt: any) => + modalInt.customId === "prefix_settings_modal" && + modalInt.user.id === message.author.id, + }); - const currentPrefix = server?.prefix || "!"; - const staffRoles: string[] = toStringArray(server?.staff); - const staffDisplay = staffRoles.length - ? staffRoles.map((id) => `<@&${id}>`).join(', ') - : 'Sin staff configurado'; - const aiRolePrompt = server?.aiRolePrompt ?? null; - const aiPreview = aiRolePrompt ? (aiRolePrompt.length > 80 ? aiRolePrompt.slice(0, 77) + '…' : aiRolePrompt) : 'No configurado'; + const newPrefix = + modalInteraction.components.getTextInputValue("new_prefix_input"); + const description = + modalInteraction.components.getTextInputValue( + "prefix_description" + ) || "Sin descripción"; - // Panel de configuración usando DisplayComponents - const settingsPanel = { - type: 17, - accent_color: 6178018, // Color del ejemplo - components: [ - { type: 10, content: "### <:invisible:1418684224441028608> 梅,panel admin,📢\n" }, + if (!newPrefix || newPrefix.length > 10) { + await modalInteraction.reply({ + content: + "❌ **Error:** El prefix debe tener entre 1 y 10 caracteres.", + flags: 64, + }); + return; + } + + try { + await client.prisma.guild.upsert({ + where: { id: message.guild!.id }, + create: { + id: message.guild!.id, + name: message.guild!.name, + prefix: newPrefix, + }, + update: { prefix: newPrefix, name: message.guild!.name }, + }); + + // Invalidar el caché del guild para reflejar el cambio + await invalidateGuildCache(message.guild!.id); + + const successPanel = { + type: 17, + accent_color: 3066993, + components: [ + { + type: 10, + content: "### ✅ **Prefix Actualizado Exitosamente**", + }, + { type: 14, spacing: 2, divider: true }, + { + type: 9, + components: [ + { + type: 10, + content: `**Prefix anterior:** \`${currentPrefix}\`\n**Prefix nuevo:** \`${newPrefix}\`\n\n**Motivo:** ${description}`, + }, + ], + accessory: { + type: 2, + style: 3, + label: "✓ Listo", + custom_id: "prefix_confirmed", + emoji: { name: "✅" }, + }, + }, { type: 14, spacing: 1, divider: false }, - { type: 10, content: "Configuracion del Servidor:" }, { - type: 9, - components: [ { type: 10, content: `**Prefix:**<:invisible:1418684224441028608>\`${currentPrefix}\`` } ], - accessory: { - type: 2, - style: 2, - emoji: { name: "⚙️" }, - custom_id: "open_prefix_modal", - label: "Cambiar" - } + type: 10, + content: + "🚀 **¡Listo!** Ahora puedes usar los comandos con el nuevo prefix.\n\n💡 **Ejemplo:** `" + + newPrefix + + "help`, `" + + newPrefix + + "embedlist`", }, - { type: 14, divider: false }, + ], + }; + + const backToSettingsRow = { + type: 1, + components: [ { - type: 9, - components: [ { type: 10, content: `**Staff (roles):** ${staffDisplay}` } ], - accessory: { - type: 2, - style: 2, // Secondary - emoji: { name: "🛡️" }, - custom_id: "open_staff_modal", - label: "Configurar" - } + type: 2, + style: 2, + label: "↩️ Volver a Configuración", + custom_id: "back_to_settings", }, - { type: 14, divider: false }, + ], + }; + + await modalInteraction.update({ + components: [successPanel, backToSettingsRow], + }); + } catch (error) { + const errorPanel = { + type: 17, + accent_color: 15548997, + components: [ + { type: 10, content: "### ❌ **Error al Actualizar Prefix**" }, + { type: 14, spacing: 2, divider: true }, { - type: 9, - components: [ { type: 10, content: `**AI Role Prompt:** ${aiPreview}` } ], - accessory: { - type: 2, - style: 2, - emoji: { name: "🧠" }, - custom_id: "open_ai_role_modal", - label: "Configurar" - } + type: 10, + content: `**Error:** No se pudo actualizar el prefix a \`${newPrefix}\`\n\n**Posibles causas:**\n• Error de conexión con la base de datos\n• Prefix contiene caracteres no válidos\n• Permisos insuficientes\n\n🔄 **Solución:** Intenta nuevamente con un prefix diferente.`, }, - { type: 14, divider: false } - ] + ], + }; + + const retryRow = { + type: 1, + components: [ + { + type: 2, + style: 2, + label: "🔄 Reintentar", + custom_id: "open_prefix_modal", + }, + { + type: 2, + style: 4, + label: "❌ Cancelar", + custom_id: "cancel_prefix_change", + }, + ], + }; + + await modalInteraction.update({ + components: [errorPanel, retryRow], + }); + } + } catch (error: any) { + logger.info( + "Modal timeout o error:", + error?.message || String(error) + ); + } + } + + if (interaction.customId === "open_staff_modal") { + // Modal para seleccionar hasta 3 roles de staff + const staffModal = { + title: "🛡️ Configurar Roles de Staff", + customId: "staff_roles_modal", + components: [ + { + type: ComponentType.Label, + label: "Selecciona hasta 3 roles de staff", + component: { + type: ComponentType.RoleSelect, + customId: "staff_roles", + required: false, + minValues: 0, + maxValues: 3, + placeholder: "Roles de staff...", + }, + }, + ], + } as const; + + await interaction.showModal(staffModal); + + try { + const modalInteraction = await interaction.awaitModalSubmit({ + time: 300000, + }); + const selected = + modalInteraction.components.getSelectedRoles("staff_roles"); + //@ts-ignore + const roleIds: string[] = selected + ? Array.from(selected.keys()).slice(0, 3) + : []; + + await client.prisma.guild.upsert({ + where: { id: message.guild!.id }, + create: { + id: message.guild!.id, + name: message.guild!.name, + staff: roleIds, + }, + update: { staff: roleIds, name: message.guild!.name }, + }); + + // Invalidar el caché del guild para reflejar el cambio + await invalidateGuildCache(message.guild!.id); + + const updatedDisplay = roleIds.length + ? roleIds.map((id) => `<@&${id}>`).join(", ") + : "Sin staff configurado"; + + const successPanel = { + type: 17, + accent_color: 3066993, + components: [ + { type: 10, content: "### ✅ **Staff Actualizado**" }, + { type: 14, spacing: 2, divider: true }, + { + type: 10, + content: `**Nuevos roles de staff:** ${updatedDisplay}`, + }, + ], + }; + + const backRow = { + type: 1, + components: [ + { + type: 2, + style: 2, + label: "↩️ Volver a Configuración", + custom_id: "back_to_settings", + }, + ], + }; + await modalInteraction.update({ + components: [successPanel, backRow], + }); + } catch (error) { + // timeout o error + } + } + + if (interaction.customId === "open_ai_role_modal") { + const currentServer = await client.prisma.guild.findFirst({ + where: { id: message.guild!.id }, + }); + const currentAiPrompt = currentServer?.aiRolePrompt ?? ""; + const aiModal = { + title: "🧠 Configurar AI Role Prompt", + customId: "ai_role_prompt_modal", + components: [ + { + type: ComponentType.Label, + label: "Prompt de rol (opcional)", + component: { + type: ComponentType.TextInput, + customId: "ai_role_prompt_input", + style: TextInputStyle.Paragraph, + required: false, + placeholder: + "Ej: Eres un asistente amistoso del servidor, responde en español, evita spoilers...", + maxLength: 1500, + value: currentAiPrompt.slice(0, 1500), + }, + }, + ], + } as const; + + try { + await interaction.showModal(aiModal); + } catch (err) { + try { + await interaction.reply({ + content: "❌ No se pudo abrir el modal de AI.", + flags: 64, + }); + } catch {} + return; + } + + try { + const modalInteraction = await interaction.awaitModalSubmit({ + time: 300000, + filter: (m: any) => + m.customId === "ai_role_prompt_modal" && + m.user.id === message.author.id, + }); + + const newPromptRaw = + modalInteraction.components.getTextInputValue( + "ai_role_prompt_input" + ) ?? ""; + const newPrompt = newPromptRaw.trim(); + const toSave: string | null = newPrompt.length > 0 ? newPrompt : null; + + await client.prisma.guild.upsert({ + where: { id: message.guild!.id }, + create: { + id: message.guild!.id, + name: message.guild!.name, + aiRolePrompt: toSave, + }, + update: { aiRolePrompt: toSave, name: message.guild!.name }, + }); + + // Invalida el cache del servicio para reflejar cambios al instante + aiService.invalidateGuildConfig(message.guild!.id); + + // Invalidar el caché del guild también + await invalidateGuildCache(message.guild!.id); + + const preview = toSave + ? toSave.length > 200 + ? toSave.slice(0, 197) + "…" + : toSave + : "Prompt eliminado (sin configuración)"; + + const successPanel = { + type: 17, + accent_color: 3066993, + components: [ + { type: 10, content: "### ✅ **AI Role Prompt Actualizado**" }, + { type: 14, spacing: 2, divider: true }, + { type: 10, content: `**Nuevo valor:**\n${preview}` }, + ], + }; + const backRow = { + type: 1, + components: [ + { + type: 2, + style: 2, + label: "↩️ Volver a Configuración", + custom_id: "back_to_settings", + }, + ], + }; + + await modalInteraction.update({ + components: [successPanel, backRow], + }); + } catch (e) { + // timeout o cancelado + } + } + + // Manejar botones adicionales + if (interaction.customId === "back_to_settings") { + const updatedServer = await client.prisma.guild.findFirst({ + where: { id: message.guild!.id }, + }); + const newCurrentPrefix = updatedServer?.prefix || "!"; + const staffRoles2: string[] = toStringArray(updatedServer?.staff); + const staffDisplay2 = staffRoles2.length + ? staffRoles2.map((id) => `<@&${id}>`).join(", ") + : "Sin staff configurado"; + const aiRolePrompt2 = updatedServer?.aiRolePrompt ?? null; + const aiPreview2 = aiRolePrompt2 + ? aiRolePrompt2.length > 80 + ? aiRolePrompt2.slice(0, 77) + "…" + : aiRolePrompt2 + : "No configurado"; + + const updatedSettingsPanel = { + type: 17, + accent_color: 6178018, + components: [ + { + type: 10, + content: + "### <:invisible:1418684224441028608> 梅,panel admin,📢\n", + }, + { type: 14, spacing: 1, divider: false }, + { type: 10, content: "Configuracion del Servidor:" }, + { + type: 9, + components: [ + { type: 10, content: `**Prefix:** \`${newCurrentPrefix}\`` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "⚙️" }, + custom_id: "open_prefix_modal", + label: "Cambiar", + }, + }, + { type: 14, divider: false }, + { + type: 9, + components: [ + { type: 10, content: `**Staff (roles):** ${staffDisplay2}` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "🛡️" }, + custom_id: "open_staff_modal", + label: "Configurar", + }, + }, + { type: 14, divider: false }, + { + type: 9, + components: [ + { type: 10, content: `**AI Role Prompt:** ${aiPreview2}` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "🧠" }, + custom_id: "open_ai_role_modal", + label: "Configurar", + }, + }, + { type: 14, divider: false }, + ], }; - const panelMessage = await message.reply({ - flags: 32768, // Components v2 - components: [settingsPanel] + await interaction.update({ components: [updatedSettingsPanel] }); + } + + if (interaction.customId === "cancel_prefix_change") { + // Volver al panel original + const updatedServer = await client.prisma.guild.findFirst({ + where: { id: message.guild!.id }, }); + const staffRoles3: string[] = toStringArray(updatedServer?.staff); + const staffDisplay3 = staffRoles3.length + ? staffRoles3.map((id) => `<@&${id}>`).join(", ") + : "Sin staff configurado"; + const aiRolePrompt3 = updatedServer?.aiRolePrompt ?? null; + const aiPreview3 = aiRolePrompt3 + ? aiRolePrompt3.length > 80 + ? aiRolePrompt3.slice(0, 77) + "…" + : aiRolePrompt3 + : "No configurado"; - const collector = panelMessage.createMessageComponentCollector({ - time: 300000, // 5 minutos - filter: (i: any) => i.user.id === message.author.id - }); + const originalPanel = { + type: 17, + accent_color: 6178018, + components: [ + { + type: 10, + content: + "### <:invisible:1418684224441028608> 梅,panel admin,📢\n", + }, + { type: 14, spacing: 1, divider: false }, + { type: 10, content: "Configuracion del Servidor:" }, + { + type: 9, + components: [ + { type: 10, content: `**Prefix:** \`${currentPrefix}\`` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "⚙️" }, + custom_id: "open_prefix_modal", + label: "Cambiar", + }, + }, + { type: 14, divider: false }, + { + type: 9, + components: [ + { type: 10, content: `**Staff (roles):** ${staffDisplay3}` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "🛡️" }, + custom_id: "open_staff_modal", + label: "Configurar", + }, + }, + { type: 14, divider: false }, + { + type: 9, + components: [ + { type: 10, content: `**AI Role Prompt:** ${aiPreview3}` }, + ], + accessory: { + type: 2, + style: 2, + emoji: { name: "🧠" }, + custom_id: "open_ai_role_modal", + label: "Configurar", + }, + }, + { type: 14, divider: false }, + ], + }; - collector.on("collect", async (interaction: any) => { - if (interaction.customId === "open_prefix_modal") { - // Crear y mostrar modal para cambiar prefix (formato consistente con otros modales) - const prefixModal = { - title: "⚙️ Configurar Prefix del Servidor", - customId: "prefix_settings_modal", - components: [ - { - type: ComponentType.Label, - label: "Nuevo Prefix", - component: { - type: ComponentType.TextInput, - customId: "new_prefix_input", - style: TextInputStyle.Short, - placeholder: `Prefix actual: ${currentPrefix}`, - required: true, - maxLength: 10, - minLength: 1, - value: currentPrefix - } - }, - { - type: ComponentType.Label, - label: "Motivo (opcional)", - component: { - type: ComponentType.TextInput, - customId: "prefix_description", - style: TextInputStyle.Paragraph, - placeholder: "Ej: evitar conflictos con otros bots...", - required: false, - maxLength: 200 - } - } - ] - } as const; + await interaction.update({ components: [originalPanel] }); + } + }); - try { - await interaction.showModal(prefixModal); - } catch (err) { - try { await interaction.reply({ content: '❌ No se pudo abrir el modal de prefix.', flags: 64 }); } catch {} - return; - } + collector.on("end", async (_: any, reason: string) => { + if (reason === "time") { + const timeoutPanel = { + type: 17, + accent_color: 6178018, + components: [ + { type: 10, content: "### ⏰ **Panel Expirado**" }, + { type: 14, spacing: 1, divider: true }, + { + type: 10, + content: + "El panel de configuración ha expirado por inactividad.\n\nUsa `!settings` para abrir un nuevo panel.", + }, + ], + }; - try { - const modalInteraction = await interaction.awaitModalSubmit({ - time: 300000, - filter: (modalInt: any) => modalInt.customId === "prefix_settings_modal" && modalInt.user.id === message.author.id - }); - - const newPrefix = modalInteraction.components.getTextInputValue("new_prefix_input"); - const description = modalInteraction.components.getTextInputValue("prefix_description") || "Sin descripción"; - - if (!newPrefix || newPrefix.length > 10) { - await modalInteraction.reply({ content: "❌ **Error:** El prefix debe tener entre 1 y 10 caracteres.", flags: 64 }); - return; - } - - try { - await client.prisma.guild.upsert({ - where: { id: message.guild!.id }, - create: { id: message.guild!.id, name: message.guild!.name, prefix: newPrefix }, - update: { prefix: newPrefix, name: message.guild!.name } - }); - - const successPanel = { - type: 17, - accent_color: 3066993, - components: [ - { type: 10, content: "### ✅ **Prefix Actualizado Exitosamente**" }, - { type: 14, spacing: 2, divider: true }, - { type: 9, components: [ { type: 10, content: `**Prefix anterior:** \`${currentPrefix}\`\n**Prefix nuevo:** \`${newPrefix}\`\n\n**Motivo:** ${description}` } ], accessory: { type: 2, style: 3, label: "✓ Listo", custom_id: "prefix_confirmed", emoji: { name: "✅" } } }, - { type: 14, spacing: 1, divider: false }, - { type: 10, content: "🚀 **¡Listo!** Ahora puedes usar los comandos con el nuevo prefix.\n\n💡 **Ejemplo:** `" + newPrefix + "help`, `" + newPrefix + "embedlist`" } - ] - }; - - const backToSettingsRow = { type: 1, components: [ { type: 2, style: 2, label: "↩️ Volver a Configuración", custom_id: "back_to_settings" } ] }; - - await modalInteraction.update({ components: [successPanel, backToSettingsRow] }); - - } catch (error) { - const errorPanel = { - type: 17, - accent_color: 15548997, - components: [ - { type: 10, content: "### ❌ **Error al Actualizar Prefix**" }, - { type: 14, spacing: 2, divider: true }, - { type: 10, content: `**Error:** No se pudo actualizar el prefix a \`${newPrefix}\`\n\n**Posibles causas:**\n• Error de conexión con la base de datos\n• Prefix contiene caracteres no válidos\n• Permisos insuficientes\n\n🔄 **Solución:** Intenta nuevamente con un prefix diferente.` } - ] - }; - - const retryRow = { type: 1, components: [ { type: 2, style: 2, label: "🔄 Reintentar", custom_id: "open_prefix_modal" }, { type: 2, style: 4, label: "❌ Cancelar", custom_id: "cancel_prefix_change" } ] }; - - await modalInteraction.update({ components: [errorPanel, retryRow] }); - } - } catch (error: any) { - logger.info("Modal timeout o error:", error?.message || String(error)); - } - } - - if (interaction.customId === "open_staff_modal") { - // Modal para seleccionar hasta 3 roles de staff - const staffModal = { - title: "🛡️ Configurar Roles de Staff", - customId: "staff_roles_modal", - components: [ - { type: ComponentType.Label, label: "Selecciona hasta 3 roles de staff", component: { type: ComponentType.RoleSelect, customId: "staff_roles", required: false, minValues: 0, maxValues: 3, placeholder: "Roles de staff..." } } - ] - } as const; - - await interaction.showModal(staffModal); - - try { - const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 }); - const selected = modalInteraction.components.getSelectedRoles('staff_roles'); - //@ts-ignore - const roleIds: string[] = selected ? Array.from(selected.keys()).slice(0, 3) : []; - - await client.prisma.guild.upsert({ - where: { id: message.guild!.id }, - create: { id: message.guild!.id, name: message.guild!.name, staff: roleIds }, - update: { staff: roleIds, name: message.guild!.name } - }); - - const updatedDisplay = roleIds.length ? roleIds.map((id) => `<@&${id}>`).join(', ') : 'Sin staff configurado'; - - const successPanel = { - type: 17, - accent_color: 3066993, - components: [ - { type: 10, content: "### ✅ **Staff Actualizado**" }, - { type: 14, spacing: 2, divider: true }, - { type: 10, content: `**Nuevos roles de staff:** ${updatedDisplay}` } - ] - }; - - const backRow = { type: 1, components: [ { type: 2, style: 2, label: '↩️ Volver a Configuración', custom_id: 'back_to_settings' } ] }; - await modalInteraction.update({ components: [successPanel, backRow] }); - } catch (error) { - // timeout o error - } - } - - if (interaction.customId === "open_ai_role_modal") { - const currentServer = await client.prisma.guild.findFirst({ where: { id: message.guild!.id } }); - const currentAiPrompt = currentServer?.aiRolePrompt ?? ''; - const aiModal = { - title: "🧠 Configurar AI Role Prompt", - customId: "ai_role_prompt_modal", - components: [ - { - type: ComponentType.Label, - label: "Prompt de rol (opcional)", - component: { - type: ComponentType.TextInput, - customId: "ai_role_prompt_input", - style: TextInputStyle.Paragraph, - required: false, - placeholder: "Ej: Eres un asistente amistoso del servidor, responde en español, evita spoilers...", - maxLength: 1500, - value: currentAiPrompt.slice(0, 1500) - } - } - ] - } as const; - - try { - await interaction.showModal(aiModal); - } catch (err) { - try { await interaction.reply({ content: '❌ No se pudo abrir el modal de AI.', flags: 64 }); } catch {} - return; - } - - try { - const modalInteraction = await interaction.awaitModalSubmit({ - time: 300000, - filter: (m: any) => m.customId === 'ai_role_prompt_modal' && m.user.id === message.author.id - }); - - const newPromptRaw = modalInteraction.components.getTextInputValue('ai_role_prompt_input') ?? ''; - const newPrompt = newPromptRaw.trim(); - const toSave: string | null = newPrompt.length > 0 ? newPrompt : null; - - await client.prisma.guild.upsert({ - where: { id: message.guild!.id }, - create: { id: message.guild!.id, name: message.guild!.name, aiRolePrompt: toSave }, - update: { aiRolePrompt: toSave, name: message.guild!.name } - }); - - // Invalida el cache del servicio para reflejar cambios al instante - aiService.invalidateGuildConfig(message.guild!.id); - - const preview = toSave ? (toSave.length > 200 ? toSave.slice(0, 197) + '…' : toSave) : 'Prompt eliminado (sin configuración)'; - - const successPanel = { - type: 17, - accent_color: 3066993, - components: [ - { type: 10, content: "### ✅ **AI Role Prompt Actualizado**" }, - { type: 14, spacing: 2, divider: true }, - { type: 10, content: `**Nuevo valor:**\n${preview}` } - ] - }; - const backRow = { type: 1, components: [ { type: 2, style: 2, label: '↩️ Volver a Configuración', custom_id: 'back_to_settings' } ] }; - - await modalInteraction.update({ components: [successPanel, backRow] }); - } catch (e) { - // timeout o cancelado - } - } - - // Manejar botones adicionales - if (interaction.customId === "back_to_settings") { - const updatedServer = await client.prisma.guild.findFirst({ where: { id: message.guild!.id } }); - const newCurrentPrefix = updatedServer?.prefix || "!"; - const staffRoles2: string[] = toStringArray(updatedServer?.staff); - const staffDisplay2 = staffRoles2.length ? staffRoles2.map((id) => `<@&${id}>`).join(', ') : 'Sin staff configurado'; - const aiRolePrompt2 = updatedServer?.aiRolePrompt ?? null; - const aiPreview2 = aiRolePrompt2 ? (aiRolePrompt2.length > 80 ? aiRolePrompt2.slice(0, 77) + '…' : aiRolePrompt2) : 'No configurado'; - - const updatedSettingsPanel = { - type: 17, - accent_color: 6178018, - components: [ - { type: 10, content: "### <:invisible:1418684224441028608> 梅,panel admin,📢\n" }, - { type: 14, spacing: 1, divider: false }, - { type: 10, content: "Configuracion del Servidor:" }, - { type: 9, components: [ { type: 10, content: `**Prefix:** \`${newCurrentPrefix}\`` } ], accessory: { type: 2, style: 2, emoji: { name: "⚙️" }, custom_id: "open_prefix_modal", label: "Cambiar" } }, - { type: 14, divider: false }, - { type: 9, components: [ { type: 10, content: `**Staff (roles):** ${staffDisplay2}` } ], accessory: { type: 2, style: 2, emoji: { name: "🛡️" }, custom_id: "open_staff_modal", label: "Configurar" } }, - { type: 14, divider: false }, - { type: 9, components: [ { type: 10, content: `**AI Role Prompt:** ${aiPreview2}` } ], accessory: { type: 2, style: 2, emoji: { name: "🧠" }, custom_id: "open_ai_role_modal", label: "Configurar" } }, - { type: 14, divider: false } - ] - }; - - await interaction.update({ components: [updatedSettingsPanel] }); - } - - if (interaction.customId === "cancel_prefix_change") { - // Volver al panel original - const updatedServer = await client.prisma.guild.findFirst({ where: { id: message.guild!.id } }); - const staffRoles3: string[] = toStringArray(updatedServer?.staff); - const staffDisplay3 = staffRoles3.length ? staffRoles3.map((id) => `<@&${id}>`).join(', ') : 'Sin staff configurado'; - const aiRolePrompt3 = updatedServer?.aiRolePrompt ?? null; - const aiPreview3 = aiRolePrompt3 ? (aiRolePrompt3.length > 80 ? aiRolePrompt3.slice(0, 77) + '…' : aiRolePrompt3) : 'No configurado'; - - const originalPanel = { - type: 17, - accent_color: 6178018, - components: [ - { type: 10, content: "### <:invisible:1418684224441028608> 梅,panel admin,📢\n" }, - { type: 14, spacing: 1, divider: false }, - { type: 10, content: "Configuracion del Servidor:" }, - { type: 9, components: [ { type: 10, content: `**Prefix:** \`${currentPrefix}\`` } ], accessory: { type: 2, style: 2, emoji: { name: "⚙️" }, custom_id: "open_prefix_modal", label: "Cambiar" } }, - { type: 14, divider: false }, - { type: 9, components: [ { type: 10, content: `**Staff (roles):** ${staffDisplay3}` } ], accessory: { type: 2, style: 2, emoji: { name: "🛡️" }, custom_id: "open_staff_modal", label: "Configurar" } }, - { type: 14, divider: false }, - { type: 9, components: [ { type: 10, content: `**AI Role Prompt:** ${aiPreview3}` } ], accessory: { type: 2, style: 2, emoji: { name: "🧠" }, custom_id: "open_ai_role_modal", label: "Configurar" } }, - { type: 14, divider: false } - ] - }; - - await interaction.update({ components: [originalPanel] }); - } - }); - - collector.on("end", async (_: any, reason: string) => { - if (reason === "time") { - const timeoutPanel = { - type: 17, - accent_color: 6178018, - components: [ - { type: 10, content: "### ⏰ **Panel Expirado**" }, - { type: 14, spacing: 1, divider: true }, - { type: 10, content: "El panel de configuración ha expirado por inactividad.\n\nUsa `!settings` para abrir un nuevo panel." } - ] - }; - - try { - await panelMessage.edit({ components: [timeoutPanel] }); - } catch (error) { - // Mensaje eliminado o error de edición - } - } - }); - } + try { + await panelMessage.edit({ components: [timeoutPanel] }); + } catch (error) { + // Mensaje eliminado o error de edición + } + } + }); + }, };