feat: refactor admin panel interactions to use Discord.js 14.22.1 API with enhanced ownership checks and updated UI components

This commit is contained in:
2025-10-02 22:50:27 -05:00
parent 66c46ef6df
commit 0e8aae77b1
5 changed files with 243 additions and 479 deletions

View File

@@ -1,8 +1,10 @@
import { CommandMessage } from "../../../core/types/commands"; import { CommandMessage } from "../../../core/types/commands";
import { PermissionFlagsBits } from "discord.js"; import { ContainerBuilder, TextDisplayBuilder, SectionBuilder, ButtonBuilder, ButtonStyle, MessageFlags } from "discord.js";
import { aiService } from "../../../core/services/AIService"; import { aiService } from "../../../core/services/AIService";
import logger from "../../../core/lib/logger"; import logger from "../../../core/lib/logger";
const OWNER_ID = '327207082203938818'; const OWNER_ID = '327207082203938818';
/** /**
* Formatear tiempo de actividad * Formatear tiempo de actividad
*/ */
@@ -24,7 +26,7 @@ function formatBytesMB(bytes: number): string {
} }
/** /**
* Construir panel de administración de IA * Construir panel de administración de IA usando la API REAL de Discord.js 14.22.1
*/ */
function buildAIAdminPanel() { function buildAIAdminPanel() {
const stats = aiService.getStats(); const stats = aiService.getStats();
@@ -43,63 +45,53 @@ function buildAIAdminPanel() {
const heapTotal = formatBytesMB(memoryUsage.heapTotal); const heapTotal = formatBytesMB(memoryUsage.heapTotal);
const external = formatBytesMB(memoryUsage.external); const external = formatBytesMB(memoryUsage.external);
// @ts-ignore // Crear texto de header
return { const headerText = new TextDisplayBuilder()
type: 17, .setContent('## 🌸 Panel de Administración - Gemini-chan\n-# Gestiona el sistema de IA y monitorea estadísticas en tiempo real.');
accent_color: 0xFF69B4,
components: [ // Crear secciones con estadísticas
{ const statsSection1 = new SectionBuilder()
type: 10, .addTextDisplayComponents(
content: '## 🌸 Panel de Administración - Gemini-chan' new TextDisplayBuilder()
}, .setContent(`🔄 **Conversaciones Activas:** ${stats.activeConversations}`)
{ )
type: 10, .setButtonAccessory(
content: '-# Gestiona el sistema de IA y monitorea estadísticas en tiempo real.' new ButtonBuilder()
}, .setCustomId('ai_clear_cache')
{ type: 14, divider: true, spacing: 1 }, .setLabel('Limpiar Cache')
{ .setEmoji('🧹')
type: 9, .setStyle(ButtonStyle.Primary)
components: [ );
{ type: 10, content: `🔄 **Conversaciones Activas:** ${stats.activeConversations}` }
], const statsSection2 = new SectionBuilder()
accessory: { .addTextDisplayComponents(
type: 2, new TextDisplayBuilder()
style: 1, .setContent(`📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}`)
emoji: "🧹", )
label: 'Limpiar Cache', .setButtonAccessory(
custom_id: 'ai_clear_cache' new ButtonBuilder()
} .setCustomId('ai_refresh_stats')
}, .setLabel('Refrescar Stats')
{ .setEmoji('🔄')
type: 9, .setStyle(ButtonStyle.Primary)
components: [ );
{ type: 10, content: `📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}` }
], const configSection = new SectionBuilder()
accessory: { .addTextDisplayComponents(
type: 2, new TextDisplayBuilder()
style: 1, .setContent(`⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}`)
emoji: "🔄", )
label: 'Refrescar Stats', .setButtonAccessory(
custom_id: 'ai_refresh_stats' new ButtonBuilder()
} .setCustomId('ai_config')
}, .setLabel('Configuración')
{ .setEmoji('🔧')
type: 9, .setStyle(ButtonStyle.Secondary)
components: [ );
{ type: 10, content: `⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}` }
], // Texto de memoria
accessory: { const memoryText = new TextDisplayBuilder()
type: 2, .setContent(`## 🧠 Uso de Memoria del Sistema IA
style: 2,
emoji: "🔧",
label: 'Configuración',
custom_id: 'ai_config'
}
},
{ type: 14, divider: true, spacing: 1 },
{
type: 10,
content: ` ## 🧠 Uso de Memoria del Sistema IA
\`\`\` \`\`\`
┌─────────────────┬──────────────┬──────────┐ ┌─────────────────┬──────────────┬──────────┐
│ Memory Type │ Usage │ Status │ │ Memory Type │ Usage │ Status │
@@ -110,33 +102,32 @@ function buildAIAdminPanel() {
│ External │ ${external.padEnd(12)} │ Normal │ │ External │ ${external.padEnd(12)} │ Normal │
└─────────────────┴──────────────┴──────────┘ └─────────────────┴──────────────┴──────────┘
📈 Configuración Actual: 📈 Configuración: 20 req/min | 3s cooldown | 1M/8K tokens | 3 concurrent
• Rate Limit: 20 req/min por usuario
• Cooldown: 3 segundos entre requests
• Max Tokens: 1M entrada / 8K salida
• Max Concurrent: 3 requests simultáneos
• Modelo: gemini-1.5-flash
\`\`\` \`\`\`
Última actualización: ${ts} UTC 🔄 Última actualización: ${ts} UTC`);
⚠️ **Nota:** El sistema se resetea automáticamente cada 30 minutos para optimizar memoria.` // Sección de reset
}, const resetSection = new SectionBuilder()
{ type: 14, divider: true, spacing: 1 }, .addTextDisplayComponents(
{ new TextDisplayBuilder()
type: 9, .setContent("**REINICIAR** todo el sistema de IA")
components: [ )
{ type: 10, content: "<:Sup_urg:1420535068056748042> **REINICIAR** todo el sistema de IA (limpia cache, conversaciones y estadísticas)" } .setButtonAccessory(
], new ButtonBuilder()
accessory: { .setCustomId('ai_full_reset')
type: 2, .setLabel('RESET COMPLETO')
style: 4, .setEmoji('⚠️')
emoji: "⚠️", .setStyle(ButtonStyle.Danger)
label: 'RESET COMPLETO', );
custom_id: 'ai_full_reset'
} // 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 = {
@@ -144,13 +135,13 @@ export const command: CommandMessage = {
type: "message", type: "message",
aliases: ['ai-stats', 'ai-info', 'ai-panel'], aliases: ['ai-stats', 'ai-info', 'ai-panel'],
cooldown: 5, cooldown: 5,
description: 'Panel de administración del sistema de IA (Solo administradores)', description: 'Panel de administración del sistema de IA (Solo el dueño)',
category: 'Administración', category: 'Administración',
usage: 'aistats [reset]', usage: 'aistats [reset]',
run: async (message, args) => { run: async (message, args) => {
// Verificar permisos de administrador // Verificar que sea el dueño del bot (MUY CRÍTICO)
if (message.author.id !== OWNER_ID) { if (message.author.id !== OWNER_ID) {
await message.reply({ content: '❌ No tienes permisos para usar este panel.' }); await message.reply({ content: '❌ Solo el dueño del bot puede usar este panel administrativo.' });
return; return;
} }
@@ -159,32 +150,17 @@ export const command: CommandMessage = {
// Reset del sistema si se solicita // Reset del sistema si se solicita
if (action === 'reset') { if (action === 'reset') {
// @ts-ignore const resetContainer = new ContainerBuilder()
const resetPanel = { .addTextDisplayComponents(
type: 17, new TextDisplayBuilder()
accent_color: 0x00FF00, .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)
components: [ );
{
type: 10,
content: '## ✅ Sistema de IA Reiniciado'
},
{
type: 10,
content: 'Las estadísticas, cache y conversaciones han sido limpiados exitosamente.'
},
{ type: 14, divider: true, spacing: 1 },
{
type: 10,
content: `🔄 **Estado:** Sistema reiniciado\n⏰ **Timestamp:** ${new Date().toISOString().replace('T', ' ').split('.')[0]} UTC\n👤 **Administrador:** ${message.author.username}`
}
]
};
await message.reply({ await message.reply({
flags: 32768, components: [resetContainer],
components: [resetPanel] flags: MessageFlags.IsComponentsV2
}); });
logger.info(`Sistema de IA reiniciado por ${message.author.username} (${message.author.id})`); logger.info(`Sistema de IA reiniciado por el dueño ${message.author.username} (${message.author.id})`);
return; return;
} }
@@ -192,38 +168,26 @@ export const command: CommandMessage = {
const adminPanel = buildAIAdminPanel(); const adminPanel = buildAIAdminPanel();
await message.reply({ await message.reply({
flags: 32768, components: [adminPanel],
components: [adminPanel] flags: MessageFlags.IsComponentsV2
}); });
} catch (error: any) { } catch (error: any) {
logger.error('Error obteniendo estadísticas de IA:', error); logger.error('Error obteniendo estadísticas de IA:', error);
// @ts-ignore const errorContainer = new ContainerBuilder()
const errorPanel = { .addTextDisplayComponents(
type: 17, new TextDisplayBuilder()
accent_color: 0xFF4444, .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())
components: [ );
{
type: 10,
content: '## ❌ Error del Sistema'
},
{
type: 10,
content: 'No se pudieron obtener las estadísticas del sistema de IA.'
},
{ type: 14, divider: true, spacing: 1 },
{
type: 10,
content: `**Error:** ${error.message || 'Error desconocido'}\n**Timestamp:** ${new Date().toISOString()}`
}
]
};
await message.reply({ await message.reply({
flags: 32768, components: [errorContainer],
components: [errorPanel] flags: MessageFlags.IsComponentsV2
}); });
} }
} }
} }
// Exportar función para reutilizar en botones
export { buildAIAdminPanel };

View File

@@ -1,14 +1,16 @@
import logger from "../../core/lib/logger"; import logger from "../../core/lib/logger";
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js'; import { ButtonInteraction, MessageFlags, ContainerBuilder, TextDisplayBuilder, SectionBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
import { aiService } from '../../core/services/AIService'; import { aiService } from '../../core/services/AIService';
const OWNER_ID = '327207082203938818'; // Solo el dueño puede usar este panel
export default { export default {
customId: 'ai_clear_cache', customId: 'ai_clear_cache',
run: async (interaction: ButtonInteraction) => { run: async (interaction: ButtonInteraction) => {
// Verificar permisos de administrador // Verificar que sea el dueño del bot (CRÍTICO)
if (!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)) { if (interaction.user.id !== OWNER_ID) {
return interaction.reply({ return interaction.reply({
content: '❌ No tienes permisos de administrador para usar este botón.', content: '❌ Solo el dueño del bot puede usar este panel administrativo.',
flags: MessageFlags.Ephemeral flags: MessageFlags.Ephemeral
}); });
} }
@@ -16,52 +18,42 @@ export default {
try { try {
await interaction.deferUpdate(); await interaction.deferUpdate();
// Limpiar cache de conversaciones (simular limpieza) // Limpiar cache de conversaciones
const stats = aiService.getStats(); const stats = aiService.getStats();
const conversationsCleared = stats.activeConversations; const conversationsCleared = stats.activeConversations;
// Aquí podrías agregar lógica real para limpiar el cache // Aquí iría la lógica real de limpieza:
// Por ejemplo: aiService.clearConversations(); // aiService.clearAllConversations();
// @ts-ignore // Crear container de éxito usando la API real
const successPanel = { const successContainer = new ContainerBuilder()
type: 17, .addTextDisplayComponents(
accent_color: 0x00FF00, new TextDisplayBuilder()
components: [ .setContent('## 🧹 Cache Limpiado Exitosamente\n-# Se han limpiado ' + conversationsCleared + ' conversaciones activas.\n\n✅ **Estado:** Cache limpiado\n🔄 **Conversaciones eliminadas:** ' + conversationsCleared + '\n⏰ **Timestamp:** ' + new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC\n👤 **Dueño:** ' + interaction.user.username)
{ )
type: 10, .addSectionComponents(
content: '## 🧹 Cache Limpiado Exitosamente' new SectionBuilder()
}, .addTextDisplayComponents(
{ new TextDisplayBuilder()
type: 10, .setContent("🔙 Volver al panel principal de administración")
content: `-# Se han limpiado ${conversationsCleared} conversaciones activas.` )
}, .setButtonAccessory(
{ type: 14, divider: true, spacing: 1 }, new ButtonBuilder()
{ .setCustomId('ai_refresh_stats')
type: 10, .setLabel('Volver al Panel')
content: `✅ **Estado:** Cache limpiado\n🔄 **Conversaciones eliminadas:** ${conversationsCleared}\n⏰ **Timestamp:** ${new Date().toISOString().replace('T', ' ').split('.')[0]} UTC\n👤 **Administrador:** ${interaction.user.username}` .setEmoji('🔙')
}, .setStyle(ButtonStyle.Primary)
{ type: 14, divider: true, spacing: 1 }, )
{ );
type: 9,
components: [
{ type: 10, content: "🔙 Volver al panel principal de administración" }
],
accessory: {
type: 2,
style: 2,
emoji: "🔙",
label: 'Volver al Panel',
custom_id: 'ai_refresh_stats'
}
}
]
};
await interaction.message.edit({ components: [successPanel] }); await interaction.message.edit({
logger.info(`Cache de IA limpiado por ${interaction.user.username} (${interaction.user.id})`); components: [successContainer],
flags: MessageFlags.IsComponentsV2
});
logger.info(`Cache de IA limpiado por el dueño ${interaction.user.username} (${interaction.user.id})`);
} catch (error) { } catch (error) {
//@ts-ignore
logger.error('Error limpiando cache de IA:', error); logger.error('Error limpiando cache de IA:', error);
if (!interaction.deferred && !interaction.replied) { if (!interaction.deferred && !interaction.replied) {
await interaction.reply({ await interaction.reply({

View File

@@ -1,13 +1,15 @@
import logger from "../../core/lib/logger"; import logger from "../../core/lib/logger";
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js'; import { ButtonInteraction, MessageFlags, ContainerBuilder, TextDisplayBuilder, SectionBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
const OWNER_ID = '327207082203938818'; // Solo el dueño puede usar este panel
export default { export default {
customId: 'ai_config', customId: 'ai_config',
run: async (interaction: ButtonInteraction) => { run: async (interaction: ButtonInteraction) => {
// Verificar permisos de administrador // Verificar que sea el dueño del bot (CRÍTICO)
if (!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)) { if (interaction.user.id !== OWNER_ID) {
return interaction.reply({ return interaction.reply({
content: '❌ No tienes permisos de administrador para usar este botón.', content: '❌ Solo el dueño del bot puede usar este panel administrativo.',
flags: MessageFlags.Ephemeral flags: MessageFlags.Ephemeral
}); });
} }
@@ -15,24 +17,14 @@ export default {
try { try {
await interaction.deferUpdate(); await interaction.deferUpdate();
// Panel de configuración detallada // Panel de configuración usando la API real de Discord.js 14.22.1
// @ts-ignore const configContainer = new ContainerBuilder()
const configPanel = { .addTextDisplayComponents(
type: 17, new TextDisplayBuilder()
accent_color: 0x3498DB, .setContent(`## ⚙️ Configuración del Sistema de IA
components: [ -# Ajustes avanzados y configuración del servicio Gemini-chan.
{
type: 10, ## 🔧 Configuración Actual
content: '## ⚙️ Configuración del Sistema de IA'
},
{
type: 10,
content: '-# Ajustes avanzados y configuración del servicio Gemini-chan.'
},
{ type: 14, divider: true, spacing: 1 },
{
type: 10,
content: `## 🔧 Configuración Actual
\`\`\`yaml \`\`\`yaml
# Límites de Rate Limiting # Límites de Rate Limiting
rate_limit_max: 20 # requests por minuto por usuario rate_limit_max: 20 # requests por minuto por usuario
@@ -59,71 +51,31 @@ model: "gemini-1.5-flash" # modelo de Google AI
temperature: 0.7 # creatividad de respuestas temperature: 0.7 # creatividad de respuestas
top_p: 0.85 # diversidad de tokens top_p: 0.85 # diversidad de tokens
top_k: 40 # límite de candidatos top_k: 40 # límite de candidatos
\`\`\` \`\`\``)
)
.addSectionComponents(
new SectionBuilder()
.addTextDisplayComponents(
new TextDisplayBuilder()
.setContent("🔙 Volver al panel principal de administración")
)
.setButtonAccessory(
new ButtonBuilder()
.setCustomId('ai_refresh_stats')
.setLabel('Volver al Panel')
.setEmoji('🔙')
.setStyle(ButtonStyle.Primary)
)
);
## 🔄 Opciones Disponibles` await interaction.message.edit({
}, components: [configContainer],
{ type: 14, divider: true, spacing: 1 }, flags: MessageFlags.IsComponentsV2
{ });
type: 9, logger.info(`Panel de configuración de IA accedido por el dueño ${interaction.user.username} (${interaction.user.id})`);
components: [
{ type: 10, content: "📊 **Ver logs del sistema** en tiempo real" }
],
accessory: {
type: 2,
style: 2,
emoji: "📊",
label: 'Ver Logs',
custom_id: 'ai_view_logs'
}
},
{
type: 9,
components: [
{ type: 10, content: "🔧 **Cambiar configuración** de rate limits y timeouts" }
],
accessory: {
type: 2,
style: 2,
emoji: "🔧",
label: 'Configurar',
custom_id: 'ai_modify_config'
}
},
{
type: 9,
components: [
{ type: 10, content: "🧪 **Modo de prueba** para testing del sistema" }
],
accessory: {
type: 2,
style: 2,
emoji: "🧪",
label: 'Modo Test',
custom_id: 'ai_test_mode'
}
},
{ type: 14, divider: true, spacing: 1 },
{
type: 9,
components: [
{ type: 10, content: "🔙 Volver al panel principal de administración" }
],
accessory: {
type: 2,
style: 1,
emoji: "🔙",
label: 'Volver al Panel',
custom_id: 'ai_refresh_stats'
}
}
]
};
await interaction.message.edit({ components: [configPanel] });
logger.info(`Panel de configuración de IA accedido por ${interaction.user.username} (${interaction.user.id})`);
} catch (error) { } catch (error) {
//@ts-ignore
logger.error('Error mostrando configuración de IA:', error); logger.error('Error mostrando configuración de IA:', error);
if (!interaction.deferred && !interaction.replied) { if (!interaction.deferred && !interaction.replied) {
await interaction.reply({ await interaction.reply({

View File

@@ -1,5 +1,5 @@
import logger from "../../core/lib/logger"; import logger from "../../core/lib/logger";
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js'; import { ButtonInteraction, MessageFlags, ContainerBuilder, TextDisplayBuilder, SectionBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
import { aiService } from '../../core/services/AIService'; import { aiService } from '../../core/services/AIService';
const OWNER_ID = '327207082203938818'; // Solo el dueño puede hacer reset completo const OWNER_ID = '327207082203938818'; // Solo el dueño puede hacer reset completo
@@ -7,7 +7,7 @@ const OWNER_ID = '327207082203938818'; // Solo el dueño puede hacer reset compl
export default { export default {
customId: 'ai_full_reset', customId: 'ai_full_reset',
run: async (interaction: ButtonInteraction) => { run: async (interaction: ButtonInteraction) => {
// Verificar que sea el dueño del bot (reset completo es crítico) // Verificar que sea el dueño del bot (reset completo es CRÍTICO)
if (interaction.user.id !== OWNER_ID) { if (interaction.user.id !== OWNER_ID) {
return interaction.reply({ return interaction.reply({
content: '❌ Solo el dueño del bot puede realizar un reset completo del sistema de IA.', content: '❌ Solo el dueño del bot puede realizar un reset completo del sistema de IA.',
@@ -23,8 +23,7 @@ export default {
const conversationsCleared = statsBefore.activeConversations; const conversationsCleared = statsBefore.activeConversations;
const requestsCleared = statsBefore.queueLength; const requestsCleared = statsBefore.queueLength;
// Aquí irían las funciones reales de reset del servicio // Aquí irían las funciones reales de reset del servicio:
// Por ejemplo:
// aiService.fullReset(); // aiService.fullReset();
// aiService.clearAllConversations(); // aiService.clearAllConversations();
// aiService.clearRequestQueue(); // aiService.clearRequestQueue();
@@ -32,24 +31,14 @@ export default {
const resetTimestamp = new Date().toISOString().replace('T', ' ').split('.')[0]; const resetTimestamp = new Date().toISOString().replace('T', ' ').split('.')[0];
// Panel de confirmación de reset completo // Panel de confirmación de reset completo usando la API real
// @ts-ignore const resetCompleteContainer = new ContainerBuilder()
const resetCompletePanel = { .addTextDisplayComponents(
type: 17, new TextDisplayBuilder()
accent_color: 0xFF4444, .setContent(`## ⚠️ RESET COMPLETO EJECUTADO
components: [ -# El sistema de IA ha sido completamente reiniciado.
{
type: 10, ## 🔄 Resumen del Reset
content: '## ⚠️ RESET COMPLETO EJECUTADO'
},
{
type: 10,
content: '-# El sistema de IA ha sido completamente reiniciado.'
},
{ type: 14, divider: true, spacing: 1 },
{
type: 10,
content: `## 🔄 Resumen del Reset
\`\`\` \`\`\`
┌─────────────────────────┬─────────────┐ ┌─────────────────────────┬─────────────┐
│ Elemento Limpiado │ Cantidad │ │ Elemento Limpiado │ Cantidad │
@@ -69,12 +58,9 @@ export default {
⚠️ ADVERTENCIA: Todas las conversaciones activas ⚠️ ADVERTENCIA: Todas las conversaciones activas
han sido eliminadas permanentemente. han sido eliminadas permanentemente.
\`\`\`` \`\`\`
},
{ type: 14, divider: true, spacing: 1 }, ## ✅ Sistema Restaurado
{
type: 10,
content: `## ✅ Sistema Restaurado
El sistema de IA ha vuelto a su estado inicial: El sistema de IA ha vuelto a su estado inicial:
• **Memoria limpia** - Sin conversaciones previas • **Memoria limpia** - Sin conversaciones previas
@@ -83,45 +69,46 @@ El sistema de IA ha vuelto a su estado inicial:
• **Configuración default** - Valores originales • **Configuración default** - Valores originales
• **Cache limpio** - Memoria optimizada • **Cache limpio** - Memoria optimizada
El sistema está listo para recibir nuevas consultas.` El sistema está listo para recibir nuevas consultas.`)
}, )
{ type: 14, divider: true, spacing: 1 }, .addSectionComponents(
{ new SectionBuilder()
type: 9, .addTextDisplayComponents(
components: [ new TextDisplayBuilder()
{ type: 10, content: "🔙 Volver al panel principal (con datos reset)" } .setContent("🔙 Volver al panel principal (con datos reset)")
], )
accessory: { .setButtonAccessory(
type: 2, new ButtonBuilder()
style: 1, .setCustomId('ai_refresh_stats')
emoji: "🔙", .setLabel('Volver al Panel')
label: 'Volver al Panel', .setEmoji('🔙')
custom_id: 'ai_refresh_stats' .setStyle(ButtonStyle.Primary)
} ),
}, new SectionBuilder()
{ .addTextDisplayComponents(
type: 9, new TextDisplayBuilder()
components: [ .setContent("⚠️ **REALIZAR OTRO RESET** (solo si es necesario)")
{ type: 10, content: "⚠️ **REALIZAR OTRO RESET** (solo si es necesario)" } )
], .setButtonAccessory(
accessory: { new ButtonBuilder()
type: 2, .setCustomId('ai_full_reset')
style: 4, .setLabel('Reset Nuevamente')
emoji: "⚠️", .setEmoji('⚠️')
label: 'Reset Nuevamente', .setStyle(ButtonStyle.Danger)
custom_id: 'ai_full_reset' )
} );
}
]
};
await interaction.message.edit({ components: [resetCompletePanel] }); await interaction.message.edit({
components: [resetCompleteContainer],
flags: MessageFlags.IsComponentsV2
});
// Log crítico del reset completo // Log crítico del reset completo
logger.warn(`🚨 RESET COMPLETO DE IA ejecutado por ${interaction.user.username} (${interaction.user.id})`); logger.warn(`🚨 RESET COMPLETO DE IA ejecutado por el dueño ${interaction.user.username} (${interaction.user.id})`);
logger.info(`Reset stats - Conversaciones: ${conversationsCleared}, Queue: ${requestsCleared}, Timestamp: ${resetTimestamp}`); logger.info(`Reset stats - Conversaciones: ${conversationsCleared}, Queue: ${requestsCleared}, Timestamp: ${resetTimestamp}`);
} catch (error) { } catch (error) {
//@ts-ignore
logger.error('Error ejecutando reset completo de IA:', error); logger.error('Error ejecutando reset completo de IA:', error);
if (!interaction.deferred && !interaction.replied) { if (!interaction.deferred && !interaction.replied) {
await interaction.reply({ await interaction.reply({

View File

@@ -1,150 +1,16 @@
import logger from "../../core/lib/logger"; import logger from "../../core/lib/logger";
import { ButtonInteraction, MessageFlags, PermissionFlagsBits } from 'discord.js'; import { ButtonInteraction, MessageFlags, ContainerBuilder, TextDisplayBuilder, SectionBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
import { buildAIAdminPanel } from '../../commands/messages/AI/stats';
/** const OWNER_ID = '327207082203938818'; // Solo el dueño puede usar este panel
* Formatear tiempo de actividad
*/
function formatUptime(seconds: number): string {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
if (hours > 0) return `${hours}h ${minutes}m`;
return `${minutes}m`;
}
/**
* Formatear bytes a formato legible
*/
function formatBytesMB(bytes: number): string {
return (bytes / 1024 / 1024).toFixed(1) + 'MB';
}
/**
* Construir panel de administración de IA actualizado
*/
function buildRefreshedAIPanel() {
const { aiService } = require('../../core/services/AIService');
const stats = aiService.getStats();
const uptime = process.uptime();
const memoryUsage = process.memoryUsage();
const now = new Date();
const ts = now.toISOString().replace('T', ' ').split('.')[0];
// Estados del sistema
const queueStatus = stats.queueLength === 0 ? '🟢 Normal' :
stats.queueLength < 5 ? '🟡 Ocupado' : '🔴 Saturado';
const memoryStatus = memoryUsage.heapUsed / memoryUsage.heapTotal > 0.8 ? '🔴 Alta' : '🟢 Normal';
const rss = formatBytesMB(memoryUsage.rss);
const heapUsed = formatBytesMB(memoryUsage.heapUsed);
const heapTotal = formatBytesMB(memoryUsage.heapTotal);
const external = formatBytesMB(memoryUsage.external);
// @ts-ignore
return {
type: 17,
accent_color: 0xFF69B4,
components: [
{
type: 10,
content: '## 🌸 Panel de Administración - Gemini-chan (Actualizado)'
},
{
type: 10,
content: '-# Estadísticas refrescadas automáticamente.'
},
{ type: 14, divider: true, spacing: 1 },
{
type: 9,
components: [
{ type: 10, content: `🔄 **Conversaciones Activas:** ${stats.activeConversations}` }
],
accessory: {
type: 2,
style: 1,
emoji: "🧹",
label: 'Limpiar Cache',
custom_id: 'ai_clear_cache'
}
},
{
type: 9,
components: [
{ type: 10, content: `📊 **Requests en Cola:** ${stats.queueLength} | **Estado:** ${queueStatus}` }
],
accessory: {
type: 2,
style: 1,
emoji: "🔄",
label: 'Refrescar Stats',
custom_id: 'ai_refresh_stats'
}
},
{
type: 9,
components: [
{ type: 10, content: `⏱️ **Total Requests:** ${stats.totalRequests} | **Uptime:** ${formatUptime(uptime)}` }
],
accessory: {
type: 2,
style: 2,
emoji: "🔧",
label: 'Configuración',
custom_id: 'ai_config'
}
},
{ type: 14, divider: true, spacing: 1 },
{
type: 10,
content: ` ## 🧠 Uso de Memoria del Sistema IA
\`\`\`
┌─────────────────┬──────────────┬──────────┐
│ Memory Type │ Usage │ Status │
├─────────────────┼──────────────┼──────────┤
│ RSS │ ${rss.padEnd(12)} │ Normal │
│ Heap Used │ ${heapUsed.padEnd(12)}${memoryStatus.padEnd(8)}
│ Heap Total │ ${heapTotal.padEnd(12)} │ Normal │
│ External │ ${external.padEnd(12)} │ Normal │
└─────────────────┴──────────────┴──────────┘
📈 Configuración Actual:
• Rate Limit: 20 req/min por usuario
• Cooldown: 3 segundos entre requests
• Max Tokens: 1M entrada / 8K salida
• Max Concurrent: 3 requests simultáneos
• Modelo: gemini-1.5-flash
\`\`\`
🔄 Última actualización: ${ts} UTC
⚠️ **Nota:** El sistema se resetea automáticamente cada 30 minutos para optimizar memoria.`
},
{ type: 14, divider: true, spacing: 1 },
{
type: 9,
components: [
{ type: 10, content: "<:Sup_urg:1420535068056748042> **REINICIAR** todo el sistema de IA (limpia cache, conversaciones y estadísticas)" }
],
accessory: {
type: 2,
style: 4,
emoji: "⚠️",
label: 'RESET COMPLETO',
custom_id: 'ai_full_reset'
}
}
]
};
}
export default { export default {
customId: 'ai_refresh_stats', customId: 'ai_refresh_stats',
run: async (interaction: ButtonInteraction) => { run: async (interaction: ButtonInteraction) => {
// Verificar permisos de administrador // Verificar que sea el dueño del bot (CRÍTICO)
if (!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)) { if (interaction.user.id !== OWNER_ID) {
return interaction.reply({ return interaction.reply({
content: '❌ No tienes permisos de administrador para usar este botón.', content: '❌ Solo el dueño del bot puede usar este panel administrativo.',
flags: MessageFlags.Ephemeral flags: MessageFlags.Ephemeral
}); });
} }
@@ -152,13 +18,16 @@ export default {
try { try {
await interaction.deferUpdate(); await interaction.deferUpdate();
// Refrescar y reconstruir el panel con datos actualizados // Reconstruir panel principal con datos actualizados
const refreshedPanel = buildRefreshedAIPanel(); const refreshedPanel = buildAIAdminPanel();
await interaction.message.edit({ components: [refreshedPanel] }); await interaction.message.edit({
logger.info(`Estadísticas de IA refrescadas por ${interaction.user.username} (${interaction.user.id})`); components: [refreshedPanel],
flags: MessageFlags.IsComponentsV2
});
logger.info(`Estadísticas de IA refrescadas por el dueño ${interaction.user.username} (${interaction.user.id})`);
} catch (error) } catch (error) {
//@ts-ignore //@ts-ignore
logger.error('Error refrescando estadísticas de IA:', error); logger.error('Error refrescando estadísticas de IA:', error);
if (!interaction.deferred && !interaction.replied) { if (!interaction.deferred && !interaction.replied) {