import logger from "../../../core/lib/logger"; import { CommandMessage } from "../../../core/types/commands"; import { TextChannel, DMChannel, NewsChannel, ThreadChannel, EmbedBuilder } from "discord.js"; import { aiService } from "../../../core/services/AIService"; /** * Dividir texto de forma inteligente preservando markdown */ function smartChunkText(text: string, maxLength: number): string[] { if (text.length <= maxLength) return [text]; const chunks: string[] = []; let currentChunk = ''; const lines = text.split('\n'); for (const line of lines) { // Si agregar esta línea excede el límite if (currentChunk.length + line.length + 1 > maxLength) { if (currentChunk) { chunks.push(currentChunk.trim()); currentChunk = ''; } // Si la línea misma es muy larga, dividirla por palabras if (line.length > maxLength) { const words = line.split(' '); let wordChunk = ''; for (const word of words) { if (wordChunk.length + word.length + 1 > maxLength) { if (wordChunk) { chunks.push(wordChunk.trim()); wordChunk = ''; } } wordChunk += (wordChunk ? ' ' : '') + word; } if (wordChunk) { currentChunk = wordChunk; } } else { currentChunk = line; } } else { currentChunk += (currentChunk ? '\n' : '') + line; } } if (currentChunk) { chunks.push(currentChunk.trim()); } return chunks; } export const command: CommandMessage = { name: 'ai', type: "message", aliases: ['chat', 'gemini'], cooldown: 2, // Reducido porque el servicio maneja su propio rate limiting description: 'Chatea con la IA (Gemini) de forma estable y escalable.', category: 'IA', usage: 'ai ', run: async (message, args) => { // Validaciones básicas if (!args || args.length === 0) { const helpEmbed = new EmbedBuilder() .setColor(0xFF69B4) .setTitle('❌ Error: Mensaje requerido') .setDescription( '**Uso:** `ai `\n' + '**Ejemplo:** `ai ¿Cómo funciona JavaScript?`\n' + '**Límite:** 4000 caracteres máximo' ) .setFooter({ text: 'AI Chat mejorado con Gemini 2.5 Flash' }); await message.reply({ embeds: [helpEmbed] }); return; } const prompt = args.join(' '); const userId = message.author.id; const guildId = message.guild?.id; // Verificar tipo de canal const channel = message.channel as TextChannel | DMChannel | NewsChannel | ThreadChannel; if (!channel || !('send' in channel)) { await message.reply({ content: "❌ **Error:** Este comando no se puede usar en este tipo de canal." }); return; } // Indicador de escritura mejorado const typingInterval = setInterval(() => { channel.sendTyping().catch(() => {}); }, 5000); try { // Usar el servicio mejorado con manejo de prioridad const priority = message.member?.permissions.has('Administrator') ? 'high' : 'normal'; const aiResponse = await aiService.processAIRequest( userId, prompt, guildId, priority ); // Crear embed de respuesta mejorado const embed = new EmbedBuilder() .setColor(0xFF69B4) .setTitle('🌸 Gemini-chan') .setDescription(aiResponse) .setFooter({ text: `Solicitado por ${message.author.username}`, iconURL: message.author.displayAvatarURL({ forceStatic: false }) }) .setTimestamp(); // Manejar respuestas largas de forma inteligente if (aiResponse.length > 4000) { // Dividir en chunks preservando markdown const chunks = smartChunkText(aiResponse, 4000); for (let i = 0; i < chunks.length && i < 3; i++) { const chunkEmbed = new EmbedBuilder() .setColor(0xFF69B4) .setTitle(i === 0 ? '🌸 Gemini-chan' : `🌸 Gemini-chan (${i + 1}/${chunks.length})`) .setDescription(chunks[i]) .setFooter({ text: `Solicitado por ${message.author.username} | Parte ${i + 1}`, iconURL: message.author.displayAvatarURL({ forceStatic: false }) }) .setTimestamp(); if (i === 0) { await message.reply({ embeds: [chunkEmbed] }); } else { await channel.send({ embeds: [chunkEmbed] }); // Pausa entre mensajes para evitar rate limits await new Promise(resolve => setTimeout(resolve, 1000)); } } if (chunks.length > 3) { await channel.send({ content: "⚠️ **Nota:** La respuesta fue truncada. Intenta preguntas más específicas." }); } } else { await message.reply({ embeds: [embed] }); } // Log para monitoreo (solo en desarrollo) if (process.env.NODE_ENV === 'development') { const stats = aiService.getStats(); logger.info(`AI Request completado - Usuario: ${userId}, Queue: ${stats.queueLength}, Conversaciones activas: ${stats.activeConversations}`); } } catch (error: any) { logger.error(`Error en comando AI para usuario ${userId}:`, error); // Crear embed de error informativo const errorEmbed = new EmbedBuilder() .setColor(0xFF4444) .setTitle('❌ Error del Servicio de IA') .setDescription(error.message || 'Error desconocido del servicio') .addFields({ name: '💡 Consejos', value: '• Verifica que tu mensaje no sea demasiado largo\n' + '• Espera unos segundos entre consultas\n' + '• Evita contenido inapropiado' }) .setFooter({ text: 'Si el problema persiste, contacta a un administrador' }) .setTimestamp(); await message.reply({ embeds: [errorEmbed] }); } finally { // Limpiar indicador de escritura clearInterval(typingInterval); } } }