feat: refactor AI admin panel to use plain objects for improved structure and compatibility with Discord API V2

This commit is contained in:
2025-10-03 22:39:07 -05:00
parent 204b86e706
commit 1c769289c3
2 changed files with 103 additions and 93 deletions

View File

@@ -1,5 +1,5 @@
import { CommandMessage } from "../../../core/types/commands"; import { CommandMessage } from "../../../core/types/commands";
import { ContainerBuilder, TextDisplayBuilder, SectionBuilder, ButtonBuilder, ButtonStyle, MessageFlags } from "discord.js"; import { ComponentType, ButtonStyle } from "discord-api-types/v10";
import { aiService } from "../../../core/services/AIService"; import { aiService } from "../../../core/services/AIService";
import logger from "../../../core/lib/logger"; import logger from "../../../core/lib/logger";
@@ -26,7 +26,7 @@ function formatBytesMB(bytes: number): string {
} }
/** /**
* Construir panel de administración de IA usando la API REAL de Discord.js 14.22.1 * Construir panel de administración de IA usando formato de objetos planos
*/ */
function buildAIAdminPanel() { function buildAIAdminPanel() {
const stats = aiService.getStats(); const stats = aiService.getStats();
@@ -45,53 +45,53 @@ function buildAIAdminPanel() {
const heapTotal = formatBytesMB(memoryUsage.heapTotal); const heapTotal = formatBytesMB(memoryUsage.heapTotal);
const external = formatBytesMB(memoryUsage.external); const external = formatBytesMB(memoryUsage.external);
// Crear texto de header // Construir panel usando Display Components V2 (objetos planos)
const headerText = new TextDisplayBuilder() return {
.setContent('## 🌸 Panel de Administración - Gemini-chan\n-# Gestiona el sistema de IA y monitorea estadísticas en tiempo real.'); type: ComponentType.Container,
components: [
// Crear secciones con estadísticas { type: ComponentType.TextDisplay, content: '## 🌸 Panel de Administración - Gemini-chan\n-# Gestiona el sistema de IA y monitorea estadísticas en tiempo real.' },
const statsSection1 = new SectionBuilder() {
.addTextDisplayComponents( type: ComponentType.Section,
new TextDisplayBuilder() components: [
.setContent(`🔄 **Conversaciones Activas:** ${stats.activeConversations}`) { type: ComponentType.TextDisplay, content: `🔄 **Conversaciones Activas:** ${stats.activeConversations}` }
) ],
.setButtonAccessory( accessory: {
new ButtonBuilder() type: ComponentType.Button,
.setCustomId('ai_clear_cache') custom_id: 'ai_clear_cache',
.setLabel('Limpiar Cache') label: 'Limpiar Cache',
.setEmoji('🧹') emoji: { name: '🧹' },
.setStyle(ButtonStyle.Primary) style: ButtonStyle.Primary
); }
},
const statsSection2 = new SectionBuilder() {
.addTextDisplayComponents( type: ComponentType.Section,
new TextDisplayBuilder() components: [
.setContent(`📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}`) { type: ComponentType.TextDisplay, content: `📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}` }
) ],
.setButtonAccessory( accessory: {
new ButtonBuilder() type: ComponentType.Button,
.setCustomId('ai_refresh_stats') custom_id: 'ai_refresh_stats',
.setLabel('Refrescar Stats') label: 'Refrescar Stats',
.setEmoji('🔄') emoji: { name: '🔄' },
.setStyle(ButtonStyle.Primary) style: ButtonStyle.Primary
); }
},
const configSection = new SectionBuilder() {
.addTextDisplayComponents( type: ComponentType.Section,
new TextDisplayBuilder() components: [
.setContent(`⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}`) { type: ComponentType.TextDisplay, content: `⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}` }
) ],
.setButtonAccessory( accessory: {
new ButtonBuilder() type: ComponentType.Button,
.setCustomId('ai_config') custom_id: 'ai_config',
.setLabel('Configuración') label: 'Configuración',
.setEmoji('🔧') emoji: { name: '🔧' },
.setStyle(ButtonStyle.Secondary) style: ButtonStyle.Secondary
); }
},
// Texto de memoria {
const memoryText = new TextDisplayBuilder() type: ComponentType.TextDisplay,
.setContent(`## 🧠 Uso de Memoria del Sistema IA content: `## 🧠 Uso de Memoria del Sistema IA
\`\`\` \`\`\`
┌─────────────────┬──────────────┬──────────┐ ┌─────────────────┬──────────────┬──────────┐
│ Memory Type │ Usage │ Status │ │ Memory Type │ Usage │ Status │
@@ -104,30 +104,23 @@ function buildAIAdminPanel() {
📈 Configuración: 20 req/min | 3s cooldown | 1M/8K tokens | 3 concurrent 📈 Configuración: 20 req/min | 3s cooldown | 1M/8K tokens | 3 concurrent
\`\`\` \`\`\`
🔄 Última actualización: ${ts} UTC`); 🔄 Última actualización: ${ts} UTC`
},
// Sección de reset {
const resetSection = new SectionBuilder() type: ComponentType.Section,
.addTextDisplayComponents( components: [
new TextDisplayBuilder() { type: ComponentType.TextDisplay, content: "**REINICIAR** todo el sistema de IA" }
.setContent("**REINICIAR** todo el sistema de IA") ],
) accessory: {
.setButtonAccessory( type: ComponentType.Button,
new ButtonBuilder() custom_id: 'ai_full_reset',
.setCustomId('ai_full_reset') label: 'RESET COMPLETO',
.setLabel('RESET COMPLETO') emoji: { name: '⚠️' },
.setEmoji('⚠️') style: ButtonStyle.Danger
.setStyle(ButtonStyle.Danger) }
); }
]
// Construir container principal };
const container = new ContainerBuilder()
.addTextDisplayComponents(headerText)
.addSectionComponents(statsSection1, statsSection2, configSection)
.addTextDisplayComponents(memoryText)
.addSectionComponents(resetSection);
return container;
} }
export const command: CommandMessage = { export const command: CommandMessage = {
@@ -150,15 +143,20 @@ export const command: CommandMessage = {
// Reset del sistema si se solicita // Reset del sistema si se solicita
if (action === 'reset') { if (action === 'reset') {
const resetContainer = new ContainerBuilder() const resetPanel = {
.addTextDisplayComponents( type: ComponentType.Container,
new TextDisplayBuilder() components: [
.setContent('## ✅ Sistema de IA Reiniciado\nLas estadísticas, cache y conversaciones han sido limpiados exitosamente.\n\n🔄 **Estado:** Sistema reiniciado\n⏰ **Timestamp:** ' + new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC\n👤 **Dueño:** ' + message.author.username) {
); type: ComponentType.TextDisplay,
content: '## ✅ Sistema de IA Reiniciado\nLas estadísticas, cache y conversaciones han sido limpiados exitosamente.\n\n🔄 **Estado:** Sistema reiniciado\n⏰ **Timestamp:** ' + new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC\n👤 **Dueño:** ' + message.author.username
}
]
};
await message.reply({ await message.reply({
components: [resetContainer], // @ts-ignore - Flag de componentes V2
flags: MessageFlags.IsComponentsV2 flags: 32768,
components: [resetPanel]
}); });
logger.info(`Sistema de IA reiniciado por el dueño ${message.author.username} (${message.author.id})`); logger.info(`Sistema de IA reiniciado por el dueño ${message.author.username} (${message.author.id})`);
return; return;
@@ -168,22 +166,28 @@ export const command: CommandMessage = {
const adminPanel = buildAIAdminPanel(); const adminPanel = buildAIAdminPanel();
await message.reply({ await message.reply({
components: [adminPanel], // @ts-ignore - Flag de componentes V2
flags: MessageFlags.IsComponentsV2 flags: 32768,
components: [adminPanel]
}); });
} catch (error: any) { } catch (error: any) {
logger.error('Error obteniendo estadísticas de IA:', error); logger.error('Error obteniendo estadísticas de IA:', error);
const errorContainer = new ContainerBuilder() const errorPanel = {
.addTextDisplayComponents( type: ComponentType.Container,
new TextDisplayBuilder() components: [
.setContent('## ❌ Error del Sistema\nNo se pudieron obtener las estadísticas del sistema de IA.\n\n**Error:** ' + (error.message || 'Error desconocido') + '\n**Timestamp:** ' + new Date().toISOString()) {
); type: ComponentType.TextDisplay,
content: '## ❌ Error del Sistema\nNo se pudieron obtener las estadísticas del sistema de IA.\n\n**Error:** ' + (error.message || 'Error desconocido') + '\n**Timestamp:** ' + new Date().toISOString()
}
]
};
await message.reply({ await message.reply({
components: [errorContainer], // @ts-ignore - Flag de componentes V2
flags: MessageFlags.IsComponentsV2 flags: 32768,
components: [errorPanel]
}); });
} }
} }

View File

@@ -30,10 +30,16 @@ const getGuildIcon = (g?: Guild) => {
}; };
// Construye datos de invite similares a la versión previa // Construye datos de invite similares a la versión previa
const getInviteObject = (invite?: Invite) => invite?.guild ? { const getInviteObject = (invite?: Invite) => {
// En discord.js dev, necesitamos verificar si es GuildInvite
if (invite && 'guild' in invite && invite.guild) {
return {
name: invite.guild.name, name: invite.guild.name,
icon: invite.guild.icon ? `https://cdn.discordapp.com/icons/${invite.guild.id}/${invite.guild.icon}.webp?size=256` : '' icon: invite.guild.icon ? `https://cdn.discordapp.com/icons/${invite.guild.id}/${invite.guild.icon}.webp?size=256` : ''
} : null; };
}
return null;
};
// Helper: calcula el rank dentro del servidor para un campo (weeklyPoints / monthlyPoints / totalPoints) // Helper: calcula el rank dentro del servidor para un campo (weeklyPoints / monthlyPoints / totalPoints)
async function computeRankInGuild( async function computeRankInGuild(