diff --git a/.env.reod b/.env.reod deleted file mode 100755 index 17ff6a1..0000000 --- a/.env.reod +++ /dev/null @@ -1,49 +0,0 @@ -# Configuración de la aplicación -APP_ENV=production -APP_DEBUG=false -APP_URL=https://reod-dragon.ddns.net - -# Configuración de la base de datos -DB_HOST=10.10.4.17 -DB_PORT=3390 -DB_NAME=bot -DB_USER=nickpons666 -DB_PASS=MiPo6425@@ -DB_DIALECT=mysql - -# Configuración de JWT -JWT_SECRET=19c5020fa8207d2c3b9e82f430784667e001f1eb733848922f7bcb9be98f93c2 -JWT_ALGORITHM=HS256 -JWT_EXPIRATION=3600 - -# Configuración de Discord -DISCORD_GUILD_ID=1338327171013541999 -DISCORD_CLIENT_ID=1385790344594985061 -DISCORD_CLIENT_SECRET=hK9SNiYdenHQVxakt8Mx3RoMkZ5oOJvk -DISCORD_BOT_TOKEN=MTM4NTc5MDM0NDU5NDk4NTA2MQ.GvobiS.TRQM9dX7vDjmuGVa3Ckp6YRtGEWxdW0gBDbvCI - -# Configuración de Telegram -TELEGRAM_BOT_TOKEN=8469229183:AAEVIV5e7rjDXKNgFTX0dnCW6JWB88X4p2I -TELEGRAM_WEBHOOK_TOKEN=webhook_secure_token_12345 -TEST_ENV_LOAD=caos_cargado - -LIBRETRANSLATE_URL=http://10.10.4.17:5000 - -N8N_URL=https://n8n-dragon.ddns.net -N8N_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MWY4YjU3YS0wMTg2LTQ1NTctOWZlMC1jYWUxNjZlYzZlMTkiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzU1OTMwODM5fQ.2tLbddyhMTKplp9n-qVNiAgQCUj2YEvVASwLnNjgCt0 - -# ----------------------------------------- -# --- Configuración para la migración a n8n --- -# ----------------------------------------- -# URL base de esta aplicación, para que n8n pueda llamarla. -APP_BASE_URL=https://reod-dragon.ddns.net - -# Clave secreta para la comunicación segura entre n8n y api_handler.php. -# DEBE SER UNA CADENA LARGA Y ALEATORIA. Genera una con: openssl rand -hex 32 -INTERNAL_API_KEY="b5dda33b8eb062e06e100c98a8947c0248b6e38973dfd689e81f725af238d23c" - -# URL completa del webhook de n8n que procesa la cola de mensajes (process_queue_workflow). -# La obtienes del nodo Webhook en tu flujo de n8n. -N8N_PROCESS_QUEUE_WEBHOOK_URL="https://n8n-dragon.ddns.net/webhook/telegram-unified" -N8N_IA_WEBHOOK_URL="https://n8n-dragon.ddns.net/webhook/ia" -N8N_IA_WEBHOOK_URL_DISCORD="https://n8n-dragon.ddns.net/webhook/42e803ae-8aee-4b1c-858a-6c6d3fbb6230" \ No newline at end of file diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/0.0 b/0.0 new file mode 100644 index 0000000..e69de29 diff --git a/admin/sync_languages.php b/admin/sync_languages.php index b4c55ea..bb7321d 100755 --- a/admin/sync_languages.php +++ b/admin/sync_languages.php @@ -33,7 +33,7 @@ try { throw new Exception("La variable de entorno LIBRETRANSLATE_URL no está configurada en tu archivo .env"); } - $translator = new Translate(); + $translator = new Translate(LIBRETRANSLATE_URL); $libreLanguages = $translator->getSupportedLanguages(); if ($libreLanguages === null) { diff --git a/analisis_archivos_comunes.txt b/analisis_archivos_comunes.txt new file mode 100644 index 0000000..033bdbd --- /dev/null +++ b/analisis_archivos_comunes.txt @@ -0,0 +1,158 @@ +ANÁLISIS DE ARCHIVOS COMUNES - BOT DE DISCORD Y TELEGRAM +======================================================== + +Fecha de análisis: 8 de febrero de 2026 + +## 1. ARCHIVOS UTILIZADOS POR AMBOS BOTS (DISCORD Y TELEGRAM) + +### Configuración y Base de Datos: +├── config/config.php - Configuración central (tokens, DB, URLs) +├── includes/db.php - Conexión a base de datos compartida +├── includes/logger.php - Sistema de logging personalizado +├── includes/session_check.php - Validación de sesiones y CSRF +├── includes/activity_logger.php - Registro de actividad de usuarios +└── includes/auth.php - Funciones de autenticación + +### Sistema de Traducción: +├── includes/Translate.php - Clase de LibreTranslate +├── src/Translate.php - Clase principal de traducción +├── translate_message.php - Endpoint de procesamiento de traducciones +├── process_translation_queue.php - Worker de traducción en background +├── src/TranslationWorker.php - Worker individual de traducción +├── src/TranslationCache.php - Sistema de caché de traducciones +└── src/TranslationWorkerPool.php - Pool de workers de traducción + +### Helpers y Utilidades Compartidas: +├── common/helpers/schedule_helpers.php - Funciones de programación recurrente +├── common/helpers/sender_factory.php - Factory para crear senders específicos +├── common/helpers/converter_factory.php - Factory para conversores HTML +├── common/helpers/url_helper.php - Utilidades de URLs y seguridad +├── common/helpers/emojis.php - Manejo de emojis +├── includes/schedule_helpers.php - Helper alternativo de programación +├── includes/translation_helper.php - Helper de traducción frontend +├── includes/message_handler.php - Manejador central de mensajes +├── includes/error_handler.php - Manejo centralizado de errores +└── includes/tren_handler.php - Handler específico de trenes + +### Templates y Componentes UI: +├── templates/header.php - Cabecera HTML común +├── templates/footer.php - Footer HTML común +├── templates/admin/ - Templates de administración compartidos + +### Procesamiento y Colas: +├── process_queue.php - Procesador principal de colas de mensajes +├── includes/message_handler.php - Manejo de creación/actualización de mensajes +└── includes/scheduled_messages_table_body.php - Componente de tabla compartido + +## 2. ARCHIVOS COMUNES PARA ENVÍO DE MENSAJES DESDE create_message.php + +### Flujo Principal de Envío: +1. create_message.php (formulario) → +2. includes/message_handler.php (procesamiento) → +3. process_queue.php (ejecución) → +4. [DiscordSender|TelegramSender] (envío específico) + +### Archivos Involucrados en el Envío: + +#### create_message.php: +├── Incluye: session_check.php, db.php +├── Template: templates/header.php, footer.php +├── Acción: POST a includes/message_handler.php +└── JavaScript: Manejo de Summernote, validación, selección de destinatarios + +#### includes/message_handler.php: +├── Incluye: session_check.php, db.php, activity_logger.php, schedule_helpers.php +├── Procesa: Creación/actualización de mensajes en DB +├── Programa: Envíos inmediatos, diferidos, recurrentes +├── Dispara: process_queue.php para envíos inmediatos +└── Registra: Actividad en activity_log + +#### process_queue.php: +├── Incluye: db.php, SenderFactory, ConverterFactory +├── Consulta: Mensajes pendientes en schedules +├── Crea: Sender apropiado via SenderFactory +├── Convierte: HTML via ConverterFactory +└── Ejecuta: Envío through platform-specific sender + +#### Senders Específicos: +├── discord/DiscordSender.php - Envío a Discord API +├── src/DiscordSender.php - Versión alternativa Discord +├── src/TelegramSender.php - Envío a Telegram Bot API +└── telegram/TelegramSender.php - Versión alternativa Telegram + +#### Conversores de Contenido: +├── discord/converters/HtmlToDiscordMarkdownConverter.php +├── src/HtmlToDiscordMarkdownConverter.php +└── src/HtmlToTelegramHtmlConverter.php + +#### Tablas de Base de Datos Compartidas: +├── recipients - Destinatarios (con campo 'platform' para distinguir) +├── messages - Contenido de mensajes (compartido) +├── schedules - Programación de envíos (compartido) +├── sent_messages - Registro de mensajes enviados +├── translation_queue - Cola de traducciones +├── recurrent_messages - Plantillas de mensajes recurrentes +└── supported_languages - Configuración de idiomas + +## 3. ARQUITECTURA DE COMPONENTES COMPARTIDOS + +### Patrón Factory: +```php +// Creación de sender específico +$sender = SenderFactory::create($platform, $pdo); +$converter = ConverterFactory::create($platform); +``` + +### Flujo de Datos Compartido: +1. Usuario selecciona plataforma en create_message.php +2. message_handler.php guarda en DB con plataforma indicada +3. process_queue.php lee plataforma de DB +4. SenderFactory crea sender apropiado +5. ConverterFactory crea conversor apropiado +6. Se envía mensaje a plataforma específica + +### Configuración Compartida: +├── Database: Misma conexión para ambas plataformas +├── Translation: Mismo servicio de traducción +├── Templates: Mismo sistema de plantillas recurrentes +├── Gallery: Misma galería de imágenes +├── Scheduling: Mismo sistema de programación +└── Activity: Mismo sistema de registro de actividad + +## 4. DIFERENCIACIÓN POR PLATAFORMA + +### En Base de Datos: +├── Campo 'platform' en tabla recipients ('discord' | 'telegram') +├── Platform-specific tokens en config.php +├── IDs de plataforma en platform_id +└── Chat modes específicos por plataforma + +### En Código: +├── Directorios separados: /discord/ y /telegram/ +├── Senders específicos por plataforma +├── Conversores específicos por formato +├── Webhooks específicos por plataforma +└── Commands específicos por plataforma + +## 5. BENEFICIOS DE ESTA ARQUITECTURA + +✅ Reutilización máxima de código entre plataformas +✅ Mantenimiento centralizado de lógica común +✅ Base de datos unificada para reporting +✅ Sistema de traducción compartido +✅ Interfaz web unificada para administración +✅ Sistema de programación compartido +✅ Logging centralizado +✅ Sistema de plantillas compartido + +## 6. PATRONES DE DISEÑO IMPLEMENTADOS + +🏗️ Factory Pattern - Para senders y converters +🏗️ Strategy Pattern - Para manejo de plataformas +🏗️ Singleton Pattern - Para conexión DB +🏗️ Observer Pattern - Para logging +🏗️ Template Method - Para flujo de envío + +Esta arquitectura permite añadir nuevas plataformas fácilmente mediante la creación +de nuevos senders y converters, mientras se mantiene toda la lógica de negocio +compartida entre ambas plataformas actuales. \ No newline at end of file diff --git a/attachments b/attachments new file mode 100644 index 0000000..e69de29 diff --git a/clear_opcache.php b/clear_opcache.php old mode 100644 new mode 100755 diff --git a/content b/content new file mode 100644 index 0000000..e69de29 diff --git a/direct_check.php b/direct_check.php old mode 100644 new mode 100755 diff --git a/discord_bot.php b/discord_bot.php index e0c0309..00eaf4a 100755 --- a/discord_bot.php +++ b/discord_bot.php @@ -38,10 +38,21 @@ if (!defined('DISCORD_BOT_TOKEN') || empty(DISCORD_BOT_TOKEN)) { try { $discord = new Discord([ 'token' => DISCORD_BOT_TOKEN, +<<<<<<< HEAD 'intents' => Intents::GUILDS | Intents::GUILD_MESSAGES | Intents::DIRECT_MESSAGES | Intents::GUILD_MEMBERS | Intents::GUILD_MESSAGE_REACTIONS | Intents::MESSAGE_CONTENT, 'logger' => $logger +======= + 'intents' => Intents::GUILDS | Intents::GUILD_MESSAGES | Intents::DIRECT_MESSAGES | Intents::GUILD_MEMBERS | Intents::GUILD_MESSAGE_REACTIONS, + 'logger' => $logger, + 'loop' => \React\EventLoop\Loop::get(), + 'socket_options' => [ + 'heartbeat_interval' => 30, // Enviar heartbeat cada 30 segundos + 'reconnect_timeout' => 60, // Timeout para reconexión + ] +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 ]); + // Manejar eventos de conexión y desconexión $discord->on('ready', function (Discord $discord) { $discord->getLogger()->info("=================================================="); $discord->getLogger()->info("Bot conectado y listo para escuchar!"); @@ -49,6 +60,21 @@ try { $discord->getLogger()->info("=================================================="); }); + // Evento de reconexión + $discord->on('reconnected', function (Discord $discord) { + $logger->info("[RECONEXIÓN] Bot reconectado exitosamente"); + }); + + // Evento de desconexión + $discord->on('disconnected', function (Discord $discord, $reason) { + $logger->warning("[DESCONEXIÓN] Bot desconectado. Razón: $reason"); + }); + + // Evento de error + $discord->on('error', function ($error, Discord $discord) { + $logger->error("[ERROR DISCORD] Error en la conexión: " . $error->getMessage()); + }); + // Evento para nuevos miembros en el servidor $discord->on(Event::GUILD_MEMBER_ADD, function (Member $member, Discord $discord) use ($pdo, $logger) { $logger->info("[NUEVO MIEMBRO] Usuario {$member->user->username} ({$member->id}) se ha unido al servidor."); @@ -79,9 +105,11 @@ try { try { if (strpos($customId, 'translate_manual:') === 0) { $targetLang = substr($customId, strlen('translate_manual:')); + $userId = $interaction->user->id; $originalMessage = $interaction->message; - $channelId = $originalMessage->channel_id; + $channel = $interaction->channel; +<<<<<<< HEAD // Responder de inmediato para evitar timeout de interacción $interaction->respondWithMessage(MessageBuilder::new()->setContent('⌛ Procesando traducción...'), true); @@ -113,32 +141,124 @@ try { // Obtener bandera desde supported_languages $flag = ''; +======= + try { + // Buscar el mensaje original que se está traduciendo + // Para interacciones, el mensaje original está en el mensaje de la interacción + $originalContent = ''; + + // Primero intentar obtener el mensaje referenciado si existe + $messageReference = $originalMessage->message_reference; + if ($messageReference && $messageReference->message_id) { + $referencedMessageId = $messageReference->message_id; + $channel->messages->fetch($referencedMessageId)->done(function (Message $referencedMessage) use ($interaction, $targetLang, $logger, $discord, $userId, $pdo) { +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 try { + $originalContent = trim((string) ($referencedMessage->content ?? '')); + + if (empty($originalContent)) { + $interaction->respondWithMessage( + MessageBuilder::new()->setContent('❌ No se encontró contenido para traducir.'), + true + ); + return; + } + + // Extraer y preservar emojis, stickers y elementos de Discord + $processedContent = preserveDiscordElements($originalContent); + $translator = new Translate(LIBRETRANSLATE_URL); + $sourceLang = $translator->detectLanguage($processedContent['text']) ?? 'es'; + + if ($sourceLang === $targetLang) { + $interaction->respondWithMessage( + MessageBuilder::new()->setContent('ℹ️ El mensaje ya está en este idioma.'), + true + ); + return; + } + + $translatedText = $translator->translateText($processedContent['text'], $sourceLang, $targetLang); + + // Restaurar emojis y elementos de Discord + $finalText = restoreDiscordElements($translatedText, $processedContent['placeholders']); + + // Obtener bandera $stmt = $pdo->prepare("SELECT flag_emoji FROM supported_languages WHERE language_code = ? AND is_active = 1"); $stmt->execute([$targetLang]); $flag = $stmt->fetchColumn() ?: ''; - } catch (\Throwable $e) { /* noop */ } + + $flag = $flag !== '' ? $flag : '🏳️'; - $flag = $flag !== '' ? $flag : '🏳️'; - - // Enviar resultado como mensaje normal al canal, mencionando al usuario - $sender = new DiscordSender(DISCORD_BOT_TOKEN); - $mention = '<@' . $userId . '>'; - $content = "{$mention} {$flag} Traducción ({$sourceLang} → {$targetLang}):\n> " . $translatedText; - // Pequeña espera para que el mensaje efímero aparezca primero - usleep(300000); - $sender->sendRawMessage($channelId, $content); - if (empty($translatedText)) { - $sender = new DiscordSender(DISCORD_BOT_TOKEN); - $sender->sendRawMessage($channelId, '<@' . $userId . '> El mensaje ya está en este idioma.'); + // Responder efímeramente con la traducción + $response = "{$flag} **Traducción ({$sourceLang} → {$targetLang}):**\n\n" . $finalText; + $interaction->respondWithMessage( + MessageBuilder::new()->setContent($response), + true // Efímero - solo visible para el usuario + ); + + $logger->info("[TRADUCCIÓN EFÍMERA] Traducción {$sourceLang}→{$targetLang} enviada efímeramente"); + + } catch (\Throwable $e) { + $logger->error("[Error Traducción Manual]", ['error' => $e->getMessage()]); + $interaction->respondWithMessage( + MessageBuilder::new()->setContent('❌ Error al procesar la traducción: ' . $e->getMessage()), + true + ); + } + }); + return; // Salir después de iniciar el fetch + } else { + // Fallback: usar el contenido del mensaje original si no hay referencia + $originalContent = trim((string) ($originalMessage->content ?? '')); + + if (empty($originalContent)) { + $interaction->respondWithMessage( + MessageBuilder::new()->setContent('❌ No se encontró contenido para traducir.'), + true + ); + return; } - } catch (\Throwable $e) { - $sender = new DiscordSender(DISCORD_BOT_TOKEN); - $sender->sendRawMessage($channelId, '<@' . $userId . "> Error al traducir: " . $e->getMessage()); + + // Extraer y preservar emojis, stickers y elementos de Discord + $processedContent = preserveDiscordElements($originalContent); + $translator = new Translate(LIBRETRANSLATE_URL); + $sourceLang = $translator->detectLanguage($processedContent['text']) ?? 'es'; + + if ($sourceLang === $targetLang) { + $interaction->respondWithMessage( + MessageBuilder::new()->setContent('ℹ️ El mensaje ya está en este idioma.'), + true + ); + return; + } + + $translatedText = $translator->translateText($processedContent['text'], $sourceLang, $targetLang); + + // Restaurar emojis y elementos de Discord + $finalText = restoreDiscordElements($translatedText, $processedContent['placeholders']); + + // Obtener bandera + $stmt = $pdo->prepare("SELECT flag_emoji FROM supported_languages WHERE language_code = ? AND is_active = 1"); + $stmt->execute([$targetLang]); + $flag = $stmt->fetchColumn() ?: ''; + + $flag = $flag !== '' ? $flag : '🏳️'; + + // Responder efímeramente con la traducción + $response = "{$flag} **Traducción ({$sourceLang} → {$targetLang}):**\n\n" . $finalText; + $interaction->respondWithMessage( + MessageBuilder::new()->setContent($response), + true // Efímero - solo visible para el usuario + ); + + $logger->info("[TRADUCCIÓN EFÍMERA] Traducción {$sourceLang}→{$targetLang} enviada efímeramente"); } - } else { - $sender = new DiscordSender(DISCORD_BOT_TOKEN); - $sender->sendRawMessage($channelId, '<@' . $userId . '> No se encontró contenido para traducir.'); + } catch (\Throwable $e) { + $logger->error("[Error Interacción Manual]", ['error' => $e->getMessage()]); + $interaction->respondWithMessage( + MessageBuilder::new()->setContent('❌ Error al procesar la solicitud: ' . $e->getMessage()), + true + ); } return; // Salir después de manejar la interacción } @@ -445,6 +565,23 @@ try { } }); + // Sistema de verificación de salud periódica + $discord->on('ready', function (Discord $discord) use ($logger) { + // Programar verificación de salud cada 2 minutos + $discord->getLoop()->addPeriodicTimer(120, function() use ($discord, $logger) { + try { + // Verificar si el bot sigue conectado + if ($discord->user && $discord->user->id) { + $logger->info("[HEALTH CHECK] Bot conectado y respondiendo. Usuario: {$discord->user->username}"); + } else { + $logger->warning("[HEALTH CHECK] Bot no responde correctamente"); + } + } catch (\Throwable $e) { + $logger->error("[HEALTH CHECK] Error en verificación: " . $e->getMessage()); + } + }); + }); + $discord->run(); } catch (Throwable $e) { @@ -604,6 +741,7 @@ function handleDiscordCommand(Message $message, PDO $pdo, Logger $logger) function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger) { try { +<<<<<<< HEAD $translator = new Translate(LIBRETRANSLATE_URL); // Instanciar al inicio $messageContentOriginal = trim($message->content); @@ -623,10 +761,29 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger) $confidence = $detectionResult[0]['confidence'] ?? 0.0; if ($confidence > 0.0) { $hasTranslatableText = true; +======= + $text = $message->content; + $attachments = $message->attachments; + + // Verificar si el mensaje tiene contenido de texto + $hasTextContent = !empty(trim($text)); + + // Verificar si hay texto en los attachments (captions de imágenes/videos) + $attachmentText = ''; + if (!empty($attachments)) { + foreach ($attachments as $attachment) { + // Algunos attachments tienen propiedad 'description' o 'caption' + if (!empty($attachment->description)) { + $attachmentText .= $attachment->description . ' '; + } + if (!empty($attachment->caption)) { + $attachmentText .= $attachment->caption . ' '; +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 } } } +<<<<<<< HEAD // If no translatable text is found, do not send buttons. if (!$hasTranslatableText) { $logger->info("[TRANSLATION_BUTTONS] Mensaje de Discord #{$message->id} sin contenido de texto traducible, no se envían botones de traducción."); @@ -642,10 +799,33 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger) $activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC); // 3. Filtrar los idiomas de destino (todos los activos menos el original) +======= + // Combinar texto principal con texto de attachments + $fullText = trim($text . ' ' . $attachmentText); + $hasAnyText = !empty($fullText); + + // Verificar si el mensaje tiene solo imágenes/adjuntos sin texto + $hasOnlyAttachments = !$hasAnyText && !empty($attachments) && count($attachments) > 0; + + // Si el mensaje no tiene texto o tiene solo imágenes, no mostrar botones de traducción + if (!$hasAnyText || $hasOnlyAttachments) { + $logger->info("[TRADUCCIÓN] Mensaje #{$message->id} no tiene contenido de texto, omitiendo botones de traducción."); + return; + } + $translator = new Translate(LIBRETRANSLATE_URL); + $detectedLang = $translator->detectLanguage(strip_tags($fullText)) ?? 'es'; + + // Obtener idiomas activos disponibles + $langStmt = $pdo->query("SELECT language_code, language_name, flag_emoji FROM supported_languages WHERE is_active = 1"); + $activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC); + + // Filtrar idiomas diferentes al detectado +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 $targetLangs = array_filter($activeLangs, function($lang) use ($detectedLang) { return $lang['language_code'] !== $detectedLang; }); +<<<<<<< HEAD // 4. Si no hay idiomas a los que traducir, no hacer nada if (empty($targetLangs)) { $logger->info("[TRANSLATION_BUTTONS] No se requieren botones de traducción para el mensaje de Discord #{$message->id} desde '$detectedLang'."); @@ -653,11 +833,20 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger) } // 5. Crear botones de traducción para cada idioma destino +======= + if (empty($targetLangs)) { + $logger->info("[TRADUCCIÓN] No hay idiomas disponibles para traducir desde '$detectedLang'"); + return; + } + + // Crear botones con banderas +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 $components = []; $actionRow = ActionRow::new(); $buttonCount = 0; foreach ($targetLangs as $lang) { +<<<<<<< HEAD $button = Button::new(Button::STYLE_SECONDARY, 'translate_to_lang:' . $message->id . ':' . $lang['language_code']) ->setLabel($lang['language_name']); if (!empty($lang['flag_emoji'])) { @@ -666,15 +855,33 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger) $actionRow->addComponent($button); $buttonCount++; +======= + $button = Button::new(Button::STYLE_SECONDARY, 'translate_manual:' . $lang['language_code']) + ->setLabel($lang['language_name']); + + if (!empty($lang['flag_emoji'])) { + $button->setEmoji($lang['flag_emoji']); + } + + $actionRow->addComponent($button); + $buttonCount++; + + // Discord permite máximo 5 botones por ActionRow +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 if ($buttonCount % 5 === 0) { $components[] = $actionRow; $actionRow = ActionRow::new(); } } +<<<<<<< HEAD +======= + +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 if ($buttonCount % 5 !== 0) { $components[] = $actionRow; } +<<<<<<< HEAD // 6. Enviar mensaje del bot con botones como respuesta al mensaje original $builder = MessageBuilder::new() ->setContent('🌐 Select a language to translate to:') @@ -688,5 +895,68 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger) } catch (Throwable $e) { $logger->error("[TRANSLATION_BUTTONS] Error al procesar mensaje para botones de traducción", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]); +======= + // Enviar mensaje con botones efímeros + $builder = MessageBuilder::new() +// ->setContent("🌍 **Selecciona idioma para traducir:**") + ->setComponents($components); + + $message->reply($builder, true); // true = ephemeral (solo visible para el usuario) + + $logger->info("[TRADUCCIÓN] Botones de traducción enviados para mensaje #{$message->id}"); + + } catch (Throwable $e) { + $logger->error("[Error Traducción Discord]", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]); +>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43 } } + +/** + * Función para preservar emojis, stickers y elementos de Discord durante la traducción + */ +function preserveDiscordElements($text) { + $placeholders = []; + $processedText = $text; + + // Patrones para detectar elementos de Discord (excluyendo emojis Unicode) + $patterns = [ + // Emojis personalizados de Discord <:name:id> + '//', + // Menciones de usuarios <@id> y <@!id> + '/<@!?(\d+)>/', + // Menciones de canales <#id> + '/<#(\d+)>/', + // Menciones de roles <@&id> + '/<@&(\d+)>/', + // Stickers y GIFs animados (pueden venir como URLs especiales) + '/https?:\/\/(?:media|cdn)\.discordapp\.(?:com|net)\/(stickers|attachments)\/\S+/i' + ]; + + $index = 0; + foreach ($patterns as $pattern) { + $processedText = preg_replace_callback($pattern, function($matches) use (&$placeholders, &$index) { + $placeholder = "DISCORD_ELEMENT_{$index}"; + $placeholders[$placeholder] = $matches[0]; + $index++; + return $placeholder; + }, $processedText); + } + + return [ + 'text' => $processedText, + 'placeholders' => $placeholders + ]; +} + +/** + * Función para restaurar emojis, stickers y elementos de Discord después de la traducción + */ +function restoreDiscordElements($translatedText, $placeholders) { + $restoredText = $translatedText; + + foreach ($placeholders as $placeholder => $originalElement) { + $restoredText = str_replace($placeholder, $originalElement, $restoredText); + } + + return $restoredText; +} diff --git a/docker/bot-lastwar.yaml b/docker/bot-lastwar.yaml new file mode 100755 index 0000000..d224bc7 --- /dev/null +++ b/docker/bot-lastwar.yaml @@ -0,0 +1,86 @@ +name: bot-lastwar +services: + bot-lastwar: + image: 10.10.4.3:5000/bot-lastwar:v3 + container_name: bot_lastwar_funcionando + restart: unless-stopped + ports: + - target: 80 + published: "8086" + protocol: tcp + volumes: + - type: bind + source: /media/ZimaOS-HD/AppData/bot_lastwar/logs + target: /var/www/html/bot/logs + environment: + # --- App Configuration --- + - APP_ENV=production + - APP_ENVIRONMENT=reod + - APP_URL=https://reod-dragon.ddns.net + - INTERNAL_API_KEY=b5dda33b8eb062e06e100c98a8947c0248b6e38973dfd689e81f725af238d23c + - TEST_ENV_LOAD=caos_cargado + - TZ=America/Mexico_City + - USE_LOCALHOST=false + + # --- Database Configuration --- + - DB_DIALECT=mysql + - DB_HOST=10.10.4.17 + - DB_NAME=bot + - DB_PASS='MiPo6425@@' + - DB_PORT=3390 + - DB_USER=nickpons666 + + # --- Discord Configuration --- + - DISCORD_BOT_TOKEN=MTM4NTc5MDM0NDU5NDk4NTA2MQ.GvobiS.TRQM9dX7vDjmuGVa3Ckp6YRtGEWxdW0gBDbvCI + - DISCORD_CLIENT_ID=1385790344594985061 + - DISCORD_CLIENT_SECRET=hK9SNiYdenHQVxakt8Mx3RoMkZ5oOJvk + - DISCORD_GUILD_ID=1338327171013541999 + + # --- JWT Configuration --- + - JWT_ALGORITHM=HS256 + - JWT_EXPIRATION=3600 + - JWT_SECRET=19c5020fa8207d2c3b9e82f430784667e001f1eb733848922f7bcb9be98f93c2 + + # --- Services Configuration --- + - LIBRETRANSLATE_URL=http://10.10.4.17:5000 + - N8N_IA_WEBHOOK_URL=https://n8n-dragon.ddns.net/webhook/ia + - N8N_IA_WEBHOOK_URL_DISCORD=https://n8n-dragon.ddns.net/webhook/42e803ae-8aee-4b1c-858a-6c6d3fbb6230 + - N8N_PROCESS_QUEUE_WEBHOOK_URL=https://n8n-dragon.ddns.net/webhook/telegram-unified + - N8N_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MWY4YjU3YS0wMTg2LTQ1NTctOWZlMC1jYWUxNjZlYzZlMTkiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzU1OTMwODM5fQ.2tLbddyhMTKplp9n-qVNiAgQCUj2YEvVASwLnNjgCt0 + - N8N_URL=https://n8n-dragon.ddns.net + + # --- Telegram Configuration --- + - TELEGRAM_BOT_TOKEN=8469229183:AAEVIV5e7rjDXKNgFTX0dnCW6JWB88X4p2I + - TELEGRAM_WEBHOOK_TOKEN=webhook_secure_token_12345 + + # --- Docker Specific Settings --- + command: + - /entrypoint.sh + user: 0:0 + network_mode: bridge + privileged: false + cpu_shares: 90 + deploy: + resources: + limits: + memory: 16663138304 + reservations: + devices: [] + cap_add: [] + devices: [] + labels: + icon: https://www.ruthlessreviews.com/wp-content/uploads/2025/12/last-war-image.jpg + +x-casaos: + author: self + category: self + hostname: "" + icon: https://www.ruthlessreviews.com/wp-content/uploads/2025/12/last-war-image.jpg + index: / + is_uncontrolled: false + port_map: "8086" + scheme: http + store_app_id: bot-lastwar + title: + custom: "" + en_us: bot-lastwar diff --git a/docker/deploy.sh b/docker/deploy.sh index 376ad7e..ef34521 100755 --- a/docker/deploy.sh +++ b/docker/deploy.sh @@ -1,38 +1,68 @@ #!/bin/bash -# ============================================ -# Script para construir y subir imagen Docker -# Target: 10.10.4.3:5000/bot-lastwar:v2 -# ============================================ +# ================================================== +# Script para construir y subir una imagen Docker. +# +# Uso: +# ./deploy.sh +# +# El script te preguntará interactivamente: +# - Tag para la imagen +# - Si deseas usar caché al construir +# ================================================== set -e -# Cambiar al directorio del proyecto (carpeta padre de donde está este script) +# --- Configuración --- +REGISTRY_URL="10.10.4.3:5000" +IMAGE_NAME="bot-lastwar" + +# Preguntar por el tag si no se proporciona como argumento +if [ -n "$1" ]; then + TAG="$1" +else + read -p "Introduce el tag para la imagen [latest]: " TAG + TAG="${TAG:-latest}" +fi + +# Preguntar si quiere usar caché +read -p "¿Usar caché al construir? (s/n) [n]: " USE_CACHE +if [ "${USE_CACHE,,}" = "s" ]; then + BUILD_CACHE_FLAG="" + CACHE_STATUS="con caché" +else + BUILD_CACHE_FLAG="--no-cache" + CACHE_STATUS="sin caché" +fi +# --- Fin de la Configuración --- + +# Nombres completos de la imagen +LOCAL_IMAGE_NAME="${IMAGE_NAME}:${TAG}" +REMOTE_IMAGE_NAME="${REGISTRY_URL}/${IMAGE_NAME}:${TAG}" + +# Cambiar al directorio raíz del proyecto (un nivel arriba de este script) cd "$(dirname "$0")/.." echo "==========================================" -echo "Construyendo imagen Docker..." -echo "Directorio: $(pwd)" +echo "Directorio de trabajo: $(pwd)" +echo "Construyendo imagen: ${LOCAL_IMAGE_NAME}" +echo "Opción de build: ${CACHE_STATUS}" +echo "Destino del registry: ${REMOTE_IMAGE_NAME}" echo "==========================================" -# Construir la imagen usando el Dockerfile en docker/ -docker build -t bot-lastwar:latest -f docker/Dockerfile . +# 1. Construir la imagen Docker +echo "Paso 1: Construyendo imagen Docker ${CACHE_STATUS}..." +docker build ${BUILD_CACHE_FLAG} -t "${LOCAL_IMAGE_NAME}" -f docker/Dockerfile . + +# 2. Etiquetar la imagen para el registry +echo "Paso 2: Etiquetando imagen como ${REMOTE_IMAGE_NAME}..." +docker tag "${LOCAL_IMAGE_NAME}" "${REMOTE_IMAGE_NAME}" + +# 3. Subir la imagen al registry +echo "Paso 3: Subiendo imagen al registry..." +docker push "${REMOTE_IMAGE_NAME}" echo "==========================================" -echo "Etiquetando imagen para el registry..." -echo "==========================================" - -# Etiquetar la imagen con la versión v2 -docker tag bot-lastwar:latest 10.10.4.3:5000/bot-lastwar:v2 - -echo "==========================================" -echo "Subiendo imagen al registry..." -echo "==========================================" - -# Subir al registry -docker push 10.10.4.3:5000/bot-lastwar:v2 - -echo "==========================================" -echo "Imagen subida exitosamente:" -echo "10.10.4.3:5000/bot-lastwar:v2" +echo "✅ Imagen subida exitosamente:" +echo "${REMOTE_IMAGE_NAME}" echo "==========================================" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 702d154..087b67e 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -13,7 +13,7 @@ echo "Generando archivo .env desde variables de entorno..." # Eliminar todos los archivos .env existentes para evitar conflictos rm -f /var/www/html/bot/.env* 2>/dev/null || true -env | grep -E "^(DB_|JWT_|DISCORD_|TELEGRAM_|LIBRETRANSLATE_|N8N_|APP_|INTERNAL_API_KEY|TEST_ENV_LOAD)" > /tmp/env_vars.txt +env > /tmp/env_vars.txt # Determinar el nombre del archivo .env según el entorno if [ "$ENVIRONMENT" = "reod" ]; then @@ -39,7 +39,10 @@ fi rm -f /tmp/env_vars.txt -echo "Archivo .env generado" +echo "Archivo .env generado en ${ENV_FILE}" +# Corregir permisos para que el servidor web (www-data) pueda leerlo +chown www-data:www-data "$ENV_FILE" +chmod 644 "$ENV_FILE" if [ -f /var/www/html/bot/composer.json ]; then echo "Instalando dependencias de Composer..." @@ -55,12 +58,12 @@ touch /var/log/apache2/access.log 2>/dev/null || true chmod 666 /var/log/apache2/*.log 2>/dev/null || true chown -R www-data:www-data /var/www/html/bot/logs /var/log/apache2 2>/dev/null || true -echo "Configurando sitio Apache..." +echo "Configurando sitio Apache y limpiando caché de Opcache..." if [ "$ENVIRONMENT" = "reod" ]; then a2ensite reod-dragon.ddns.net.conf 2>/dev/null || true else a2dissite reod-dragon.ddns.net.conf 2>/dev/null || true fi -echo "Iniciando Supervisor..." +echo "Iniciando Supervisor (que gestionará Apache y los workers)..." exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf diff --git a/includes/db.php b/includes/db.php index acc58fb..2ddc552 100755 --- a/includes/db.php +++ b/includes/db.php @@ -14,11 +14,11 @@ class DatabaseConnection { private function __construct() { $this->config = [ - 'host' => $_ENV['DB_HOST'] ?? 'localhost', - 'port' => $_ENV['DB_PORT'] ?? '3306', - 'name' => $_ENV['DB_NAME'] ?? 'bot', - 'user' => $_ENV['DB_USER'] ?? 'nickpons666', - 'pass' => $_ENV['DB_PASS'] ?? 'MiPo6425@@', + 'host' => DB_HOST, + 'port' => DB_PORT, + 'name' => DB_NAME, + 'user' => DB_USER, + 'pass' => DB_PASS, 'charset' => 'utf8mb4', 'timeout' => 30, // Tiempo de espera de conexión en segundos 'reconnect_attempts' => 3, // Número de intentos de reconexión diff --git a/plan_diagnostico_docker.md b/plan_diagnostico_docker.md new file mode 100755 index 0000000..1be0e2d --- /dev/null +++ b/plan_diagnostico_docker.md @@ -0,0 +1,31 @@ +### **Plan de Diagnóstico: Problemas en el Entorno Docker** + +**Objetivo:** Identificar por qué la aplicación se comporta de manera diferente dentro de Docker, específicamente en la traducción de mensajes, el envío de imágenes y la funcionalidad de los botones. + +**Fase 1: Verificación de Configuración y Entorno** +* [x] **1. Revisar la configuración de Docker:** + * **Acción:** Analizar los archivos `docker/Dockerfile`, `docker/docker-compose.local.yml`, `docker/entrypoint.sh`. + * **Resultado:** Se confirmó que el `entrypoint.sh` genera un nuevo archivo `.env` basado en las variables de entorno pasadas al contenedor. El `docker-compose.local.yml` actual pasa muy pocas variables. + +* [x] **2. Confirmar la lógica de carga en `config.php`:** + * **Acción:** Volver a examinar `config.php`. + * **Resultado:** La lógica es correcta, pero el archivo `.env` que carga está incompleto en el contenedor. + +* [x] **3. Comparar variables de entorno (Local vs. Docker):** + * **Acción:** Listar las variables del `.env.pruebas` local y compararlas con las proporcionadas en `docker-compose.local.yml`. + * **Resultado:** Se confirmó que variables críticas (`APP_URL`, `DB_*`, `LIBRETRANSLATE_URL`, etc.) NO se están pasando al contenedor. **Esta es la causa raíz del problema.** + +**Fase 2: Análisis de Rutas, URLs y Conectividad** +* [x] **4, 5, 6. Análisis detallado de síntomas:** + * **Nota:** Se omite el análisis detallado de cada síntoma individual, ya que todos (fallo de traducción, imágenes y botones) son una consecuencia directa de la falta de variables de entorno identificada en la Fase 1. + +**Fase 3: Propuesta de Soluciones y Verificación** +* [x] **7. Proponer e implementar las correcciones:** + * **Propuesta Original:** Modificar `docker-compose.local.yml` para utilizar la directiva `env_file` y apuntar al archivo `.env.pruebas`. + * **Actualización 1:** La causa principal está en el `entrypoint.sh` y su manejo de las variables en producción. Se ha modificado `docker/entrypoint.sh` para que capture *todas* las variables de entorno proporcionadas en el `yaml` de despliegue, en lugar de usar un filtro `grep` restrictivo. + * **Actualización 2:** Se identificó que `DB_PASS` en el YAML no estaba correctamente entrecomillado, lo que causaba corrupción. Se corrigió el `docker/bot-lastwar.yaml` para añadir comillas a `DB_PASS` y mejorar la organización de las variables. + * **Actualización 3:** Se identificó un error en `includes/db.php` donde el código dependía de `$_ENV`, el cual puede no estar poblado, causando un error de conexión a la base de datos `No such file or directory`. Se modificó `includes/db.php` para usar `getenv()` en lugar de `$_ENV` para una lectura más robusta de las variables de entorno de la base de datos. + +* [ ] **8. Verificación final:** + * **Acción:** Construir la nueva imagen de Docker, desplegarla usando `bot-lastwar.yaml` y probar los escenarios que antes fallaban (traducciones, comandos con imágenes, botones). + * **Meta:** Confirmar que la aplicación funciona correctamente en el contenedor. \ No newline at end of file diff --git a/plan_separacion_plataformas.md b/plan_separacion_plataformas.md new file mode 100644 index 0000000..9e52f8e --- /dev/null +++ b/plan_separacion_plataformas.md @@ -0,0 +1,515 @@ +PLAN DE MIGRACIÓN COMPLETO - SEPARACIÓN DE PLATAFORMAS DISCORD/TELEGRAM +============================================================================ + +OPCIÓN 1: SEPARACIÓN GRADUAL CON MANTENIMIENTO DE COMPATIBILIDAD +============================================================================ + +Fecha: 8 de febrero de 2026 +Duración estimada: 15-20 días laborales +Riesgo: Alto (Mitigado con backups y pruebas) +Impacto: Muy alto (Requiere planificación cuidadosa) + +========================================== +VISIÓN GENERAL Y ESTRATEGIA +========================================== + +Objetivo: Crear dos instancias completamente independientes del sistema de bots, +manteniendo funcionalidad existente y evitando downtime crítico. + +Estrategia: +1. Crear estructura paralela sin afectar sistema actual +2. Migrar datos de forma segura +3. Implementar routing específico por plataforma +4. Validar extensivamente antes de corte +5. Realizar cambio mínimo de DNS/configuración final + +========================================== +ESTRUCTURA OBJETIVO FINAL +========================================== + +/bot/ +├── shared/ # Mínimo compartido (solo config general) +│ ├── config_basic.php # Configuración no sensible +│ └── constants.php # Constantes globales +├── discord/ +│ ├── bot.php # Bot de Discord independiente +│ ├── web/ +│ │ ├── create_message.php # Interfaz específica Discord +│ │ ├── admin/ # Admin específico Discord +│ │ └── templates/ # Templates específicos +│ ├── includes/ +│ │ ├── db.php # Conexión específica Discord +│ │ ├── DiscordSender.php # Sender específico +│ │ ├── message_handler.php # Handler específico +│ │ └── [todos los includes específicos] +│ ├── database/ +│ │ └── estructura_discord.sql # Tablas específicas Discord +│ └── logs/ +│ └── discord_*.log # Logs específicos +├── telegram/ +│ ├── bot.php # Bot de Telegram independiente +│ ├── web/ +│ │ ├── create_message.php # Interfaz específica Telegram +│ │ ├── admin/ # Admin específico Telegram +│ │ └── templates/ # Templates específicos +│ ├── includes/ +│ │ ├── db.php # Conexión específica Telegram +│ │ ├── TelegramSender.php # Sender específico +│ │ ├── message_handler.php # Handler específico +│ │ └── [todos los includes específicos] +│ ├── database/ +│ │ └── estructura_telegram.sql # Tablas específicas Telegram +│ └── logs/ +│ └── telegram_*.log # Logs específicos +└── legacy/ # Backup del sistema actual + └── [todo el código actual] + +========================================== +FASE 1: PREPARACIÓN Y ANÁLISIS (Días 1-2) +========================================== + +Objetivo: Análisis detallado y preparación del entorno + +**DÍA 1: Análisis de Dependencias** +├── **MAÑANA (4 horas):** +│ ├── Mapear todas las dependencias entre archivos +│ ├── Identificar archivos críticos compartidos +│ ├── Documentar flujo completo de datos +│ └── Crear diagrama de arquitectura actual +├── **TARDE (4 horas):** +│ ├── Análisis de base de datos actual +│ ├── Identificar tablas compartidas vs específicas +│ ├── Documentar relaciones y constraints +│ └── Estimar volumen de datos a migrar + +**DÍA 2: Planificación y Preparación** +├── **MAÑANA (4 horas):** +│ ├── Crear repositorio de backup completo +│ ├── Setup de entorno de pruebas paralelo +│ ├── Definir estrategia de rollback +│ └── Preparar scripts de validación +├── **TARDE (4 horas):** +│ ├── Crear estructura de directorios destino +│ ├── Setup de base de datos de pruebas +│ ├── Preparar scripts de migración de datos +│ └── Documentar puntos críticos de validación + +**DELIVERABLES FASE 1:** +✅ Diagrama de dependencias completo +✅ Estructura de directorios creada +✅ Entorno de pruebas funcionando +✅ Scripts de backup y rollback listos +✅ Documentación de puntos críticos + +========================================== +FASE 2: DUPLICACIÓN DE ESTRUCTURA (Días 3-5) +========================================== + +Objetivo: Crear estructura base duplicada sin modificar sistema actual + +**DÍA 3: Estructura de Directorios y Configuración** +├── **MAÑANA (4 horas):** +│ ├── Copiar estructura completa a /discord/ y /telegram/ +│ ├── Crear archivos de configuración específicos +│ ├── Setup de variables de entorno separadas +│ └── Configurar paths relativos específicos +├── **TARDE (4 horas):** +│ ├── Adaptar includes de cada plataforma +│ ├── Configurar logging específico por plataforma +│ ├── Ajustar rutas de templates y assets +│ └── Validar estructura básica + +**DÍA 4: Base de Datos Específica** +├── **MAÑANA (4 horas):** +│ ├── Exportar estructura completa de DB actual +│ ├── Crear databases discord_bot y telegram_bot +│ ├── Importar estructura a ambas BDs +│ └── Configurar usuarios y permisos específicos +├── **TARDE (4 horas):** +│ ├── Crear scripts de conexión específicos +│ ├── Adaptar archivos de configuración de DB +│ ├── Probar conexiones a ambas BDs +│ └── Validar que no haya cruces + +**DÍA 5: Adaptación Básica de Código** +├── **MAÑANA (4 horas):** +│ ├── Adaptar paths de includes en cada plataforma +│ ├── Modificar require_once para paths relativos +│ ├── Actualizar rutas de templates +│ └── Ajustar configuración de logging +├── **TARDE (4 horas):** +│ ├── Probar carga básica de archivos PHP +│ ├── Validar que no haya errores de sintaxis +│ ├── Verificar conexión a DB específica +│ └── Documentar cambios realizados + +**DELIVERABLES FASE 2:** +✅ Estructura duplicada completa +✅ Bases de datos separadas funcionando +✅ Configuración específica por plataforma +✅ Validación básica de estructura + +========================================== +FASE 3: MIGRACIÓN DE DATOS (Días 6-9) +========================================== + +Objetivo: Migrar datos de forma segura manteniendo integridad + +**DÍA 6: Scripts de Migración** +├── **MAÑANA (4 horas):** +│ ├── Crear scripts de exportación por plataforma +│ ├── Implementar filtros por platform='discord'/'telegram' +│ ├── Setup de validación de integridad referencial +│ └── Preparar scripts de rollback de datos +├── **TARDE (4 horas):** +│ ├── Test de scripts con subset de datos +│ ├── Validar integridad de datos migrados +│ ├── Verificar counts y relaciones +│ └── Optimizar rendimiento de migración + +**DÍA 7: Migración de Datos Principales** +├── **MAÑANA (4 horas):** +│ ├── Backup completo de producción +│ ├── Migrar tabla recipients (filtrada por plataforma) +│ ├── Migrar tabla messages +│ └── Migrar tabla schedules +├── **TARDE (4 horas):** +│ ├── Migrar tabla sent_messages +│ ├── Migrar tabla recurrent_messages +│ ├── Migrar tabla supported_languages +│ └── Validar integridad de relaciones + +**DÍA 8: Migración de Datos de Configuración** +├── **MAÑANA (4 horas):** +│ ├── Migrar tabla activity_log (con prefijo) +│ ├── Migrar tabla translation_queue +│ ├── Migrar tabla telegram_interactions (solo Telegram) +│ └── Migrar configuraciones específicas +├── **TARDE (4 horas):** +│ ├── Validar consistencia de datos migrados +│ ├── Verificar counts vs original +│ ├── Test de queries complejas +│ └── Documentar datos migrados + +**DÍA 9: Validación y Ajustes de Datos** +├── **MAÑANA (4 horas):** +│ ├── Ejecutar scripts de validación completa +│ ├── Verificar integridad referencial +│ ├── Test de operaciones CRUD +│ └── Validar timestamps y secuencias +├── **TARDE (4 horas):** +│ ├── Corregir anomalías encontradas +│ ├── Optimizar índices específicos +│ ├── Validar performance de queries +│ └── Preparar reporte de migración + +**DELIVERABLES FASE 3:** +✅ Datos completamente migrados a ambas BDs +✅ Validación de integridad completada +✅ Scripts de rollback de datos probados +✅ Documentación de migración + +========================================== +FASE 4: ADAPTACIÓN DE LÓGICA DE NEGOCIO (Días 10-13) +========================================== + +Objetivo: Adaptar toda la lógica para operación independiente + +**DÍA 10: Adaptación de Bot Files** +├── **MAÑANA (4 horas):** +│ ├── Adaptar discord_bot.php para BD específica +│ ├── Adaptar telegram_bot_webhook.php para BD específica +│ ├── Ajustar conexiones y paths +│ └── Configurar logging específico +├── **TARDE (4 horas):** +│ ├── Adaptar includes específicos de cada bot +│ ├── Modificar handlers de mensajes +│ ├── Ajustar sistemas de traducción +│ └── Validar funcionamiento básico + +**DÍA 11: Adaptación de Web Interface - Discord** +├── **MAÑANA (4 horas):** +│ ├── Adaptar discord/web/create_message.php +│ ├── Modificar para usar BD específica +│ ├── Ajustar recipient selection +│ └── Configurar paths específicos +├── **TARDE (4 horas):** +│ ├── Adaptar discord/web/admin/ +│ ├── Modificar todos los archivos de admin +│ ├── Ajustar templates específicos +│ └── Validar interfaz completa + +**DÍA 12: Adaptación de Web Interface - Telegram** +├── **MAÑANA (4 horas):** +│ ├── Adaptar telegram/web/create_message.php +│ ├── Modificar para usar BD específica +│ ├── Ajustar recipient selection +│ └── Configurar paths específicos +├── **TARDE (4 horas):** +│ ├── Adaptar telegram/web/admin/ +│ ├── Modificar todos los archivos de admin +│ ├── Ajustar templates específicos +│ └── Validar interfaz completa + +**DÍA 13: Adaptación de Procesos en Background** +├── **MAÑANA (4 horas):** +│ ├── Adaptar discord/process_queue.php +│ ├── Adaptar telegram/process_queue.php +│ ├── Ajustar sistemas de traducción específicos +│ └── Configurar logging independiente +├── **TARDE (4 horas):** +│ ├── Adaptar scripts de workers +│ ├── Modificar sistemas de scheduling +│ ├── Ajustar procesos de traducción +│ └── Validar procesos background + +**DELIVERABLES FASE 4:** +✅ Bots funcionando con BDs específicas +✅ Interfaces web adaptadas y funcionando +✅ Procesos background adaptados +✅ Logging específico funcionando + +========================================== +FASE 5: TESTING Y VALIDACIÓN (Días 14-16) +========================================== + +Objetivo: Validación exhaustiva antes de producción + +**DÍA 14: Testing Funcional** +├── **MAÑANA (4 horas):** +│ ├── Test completo de bot de Discord +│ ├── Validar todos los comandos +│ ├── Probar envío de mensajes +│ └── Test de sistema de traducción +├── **TARDE (4 horas):** +│ ├── Test completo de bot de Telegram +│ ├── Validar todos los comandos +│ ├── Probar envío de mensajes +│ └── Test de sistema de traducción + +**DÍA 15: Testing de Web Interface** +├── **MAÑANA (4 horas):** +│ ├── Test completo de web Discord +│ ├── Probar creación de mensajes +│ ├── Test de administración +│ └── Validar programación +├── **TARDE (4 horas):** +│ ├── Test completo de web Telegram +│ ├── Probar creación de mensajes +│ ├── Test de administración +│ └── Validar programación + +**DÍA 16: Testing de Integración y Stress** +├── **MAÑANA (4 horas):** +│ ├── Test de ambas plataformas simultáneamente +│ ├── Validar que no haya interferencia +│ ├── Test de carga concurrente +│ └── Medición de rendimiento +├── **TARDE (4 horas):** +│ ├── Test de procesos background +│ ├── Validar sistema de traducción bajo carga +│ ├── Test de recuperación de errores +│ └── Documentar resultados + +**DELIVERABLES FASE 5:** +✅ Validación funcional completa +✅ Testing de integración exitoso +✅ Métricas de性能 documentadas +✅ Checklist de validación completado + +========================================== +FASE 6: DEPLOY Y MIGRACIÓN FINAL (Días 17-18) +========================================== + +Objetivo: Migración final con mínimo impacto + +**DÍA 17: Preparación para Producción** +├── **MAÑANA (4 horas):** +│ ├── Backup final de sistema actual +│ ├── Preparar entorno de producción +│ ├── Configurar DNS y rutas +│ └── Documentar plan de corte +├── **TARDE (4 horas):** +│ ├── Configurar variables de entorno producción +│ ├── Validar conexiones a BDs finales +│ ├── Test de endpoints expuestos +│ └── Preparar monitoreo + +**DÍA 18: Migración Final** +├── **MAÑANA (2 horas) - WINDOW DE MIGRACIÓN:** +│ ├── Poner sistema actual en modo mantenimiento +│ ├── Migrar última data diferencial +│ ├── Apuntar rutas a nuevos sistemas +│ └── Iniciar nuevos servicios +├── **MAÑANA (2 horas) - VALIDACIÓN INMEDIATA:** +│ ├── Verificar bots conectados +│ ├── Test básico de funcionalidad +│ ├── Validar logs corriendo +│ └── Confirmar no hay errores críticos +├── **TARDE (4 horas) - MONITOREO INTENSIVO:** +│ ├── Monitorear rendimiento +│ ├── Validar todas las funcionalidades +│ ├── Revisar logs en tiempo real +│ └── Estar listo para rollback si es necesario + +**DELIVERABLES FASE 6:** +✅ Sistema completamente migrado +✅ Ambas plataformas funcionando independientemente +✅ Monitoreo activo implementado +✅ Plan de rollback validado + +========================================== +FASE 7: POST-MIGRACIÓN Y OPTIMIZACIÓN (Días 19-20) +========================================== + +Objetivo: Optimizar y documentar nueva arquitectura + +**DÍA 19: Optimización y Ajustes** +├── **MAÑANA (4 horas):** +│ ├── Analizar performance post-migración +│ ├── Optimizar queries específicos +│ ├── Ajustar configuración de cache +│ └── Optimizar índices de BD +├── **TARDE (4 horas):** +│ ├── Configurar monitoreo específico +│ ├── Setup de alertas personalizadas +│ ├── Optimizar procesos background +│ └── Ajustar configuración de logs + +**DÍA 20: Documentación y Handover** +├── **MAÑANA (4 horas):** +│ ├── Documentar nueva arquitectura +│ ├── Crear guías de operación +│ ├── Documentar procedimientos de backup +│ └── Preparar documentación técnica +├── **TARDE (4 horas):** +│ ├── Capacitar al equipo en nueva estructura +│ ├── Crear runbooks de operación +│ ├── Documentar puntos críticos +│ └── Cerrar proyecto exitosamente + +**DELIVERABLES FASE 7:** +✅ Sistema optimizado funcionando +✅ Documentación completa +✅ Equipo capacitado +✅ Proyecto cerrado exitosamente + +========================================== +RIESGOS CRÍTICOS Y MITIGACIÓN +========================================== + +**RIESGO 1: Pérdida de Datos During Migración** +├── Impacto: Crítico +├── Mitigación: +│ ├── Múltiples backups (antes, durante, después) +│ ├── Scripts de validación de integridad +│ ├── Test con datos de prueba primero +│ └── Rollback planificado y testado + +**RIESGO 2: Downtime Prolongado** +├── Impacto: Alto +├── Mitigación: +│ ├── Ventana de migración planificada +│ ├── Estructura paralela pre-creada +│ ├── Scripts automatizados para velocidad +│ └── Team listo para rollback inmediato + +**RIESGO 3: Regresiones Funcionales** +├── Impacto: Alto +├── Mitigación: +│ ├── Testing extensivo en ambiente aislado +│ ├── Checklist de validación detallado +│ ├── Monitoreo intensivo post-migración +│ └── Equipo de soporte listo + +**RIESGO 4: Problemas de Performance** +├── Impacto: Medio +├── Mitigación: +│ ├── Medición de baseline actual +│ ├── Optimización específica por plataforma +│ ├── Monitoreo continuo de métricas +│ └── Plan de optimización post-migración + +========================================== +REQUERIMIENTOS DE RECURSOS +========================================== + +**Personal:** +├── 1 Desarrollador Senior (Líder del proyecto) +├── 1 Desarrollador Mid (Soporte técnico) +├── 1 DBA (Para migración de datos) +└── 1 DevOps/Infraestructura (Para deploy) + +**Infraestructura:** +├── Servidor adicional para staging/paralelo +├── 2 bases de datos adicionales +├── Storage extra para backups +└── Herramientas de monitoreo + +**Software/Herramientas:** +├── Herramientas de comparación de BD +├── Scripts de migración automatizados +├── Sistema de control de versiones +└── Herramientas de testing automatizado + +========================================== +MÉTRICAS DE ÉXITO +========================================== + +**Técnicas:** +✅ 0% de pérdida de datos durante migración +✅ <30 minutos de downtime total +✅ 100% de funcionalidades validadas +✅ Performance igual o superior al sistema actual + +**Operativas:** +✅ 100% de independencia entre plataformas +✅ Capacidad de actualizar una plataforma sin afectar la otra +✅ Logs y monitoreo específico por plataforma +✅ Documentación completa y accesible + +========================================== +CHECKLIST FINAL DE VALIDACIÓN +========================================== + +**Pre-Migración:** +☐ Backup completo realizado y validado +☐ Scripts de migración testeados extensivamente +☐ Equipo completo notificado y listo +☐ Ventana de mantenimiento comunicada a usuarios +☐ Plan de rollback testado y validado + +**Post-Migración:** +☐ Ambos bots conectados y funcionando +☐ Interfaces web accesibles y funcionales +☐ Datos migrados correctamente (counts validados) +☐ Logs generándose correctamente +☐ Procesos background corriendo +☐ Monitoreo detectando anomalías +☐ Backup post-migración realizado + +**Week Post-Migración:** +☐ Performance estable +☐ Usuarios reportando normalidad +☐ Logs sin errores críticos +☐ Sistemas de traducción funcionando +☐ Programación de mensajes funcionando +☐ Documentación completada +☐ Equipo capacitado + +========================================== +CONCLUSIÓN +========================================== + +Este plan proporciona una ruta clara y estructurada para lograr la separación completa +de las plataformas Discord y Telegram con mínimo riesgo y máximo beneficio. + +La separación permitirá: +- Desarrollo independiente +- Despliegues seguros y aislados +- Especialización por plataforma +- Mayor estabilidad operativa +- Escalabilidad independiente + +El tiempo estimado (15-20 días) considera todas las validaciones necesarias +para garantizar una migración exitosa sin impacto crítico en el negocio. \ No newline at end of file diff --git a/process_translation_queue.php b/process_translation_queue.php index 0eb5509..e6ac893 100755 --- a/process_translation_queue.php +++ b/process_translation_queue.php @@ -20,7 +20,7 @@ require_once __DIR__ . '/src/DiscordSender.php'; use Discord\Parts\Embed\Embed; // Instanciar las clases necesarias -$translator = new Translate(); +$translator = new Translate(LIBRETRANSLATE_URL); $running = true; @@ -74,7 +74,7 @@ while ($running) { custom_log("[JOB #{$job['id']}] Procesando trabajo para Discord con múltiples idiomas."); $targetLangs = explode(',', $job['target_lang']); - $discordSender = new DiscordSender($_ENV['DISCORD_BOT_TOKEN']); + $discordSender = new DiscordSender(DISCORD_BOT_TOKEN); // Pre-procesar el texto para proteger las menciones de Discord $originalText = $job['text_to_translate']; @@ -128,7 +128,7 @@ while ($running) { // --- LÓGICA MEJORADA PARA TELEGRAM USANDO UN SOLO MENSAJE HTML --- custom_log("[JOB #{$job['id']}] Procesando trabajo para Telegram con múltiples idiomas."); $targetLangs = explode(',', $job['target_lang']); - $telegram = new TelegramSender($_ENV['TELEGRAM_BOT_TOKEN'], $pdo); + $telegram = new TelegramSender(TELEGRAM_BOT_TOKEN, $pdo, BOT_BASE_URL); $htmlOutput = "Traducciones:\n\n"; $translationCount = 0; @@ -137,7 +137,7 @@ while ($running) { if (empty($langCode)) continue; $translatedText = $translator->translateHtml($job['text_to_translate'], $job['source_lang'], $langCode); - + if ($translatedText && trim(strtolower($translatedText)) !== trim(strtolower($job['text_to_translate']))) { // Obtener nombre del idioma y bandera $langInfoStmt = $pdo->prepare("SELECT language_name, flag_emoji FROM supported_languages WHERE language_code = ?"); @@ -190,7 +190,6 @@ while ($running) { pcntl_signal_dispatch(); } - custom_log("--- PROCESADOR DE COLA DETENIDO ---"); /** diff --git a/run_manual_translation.php b/run_manual_translation.php index 17c1b86..7598a8f 100755 --- a/run_manual_translation.php +++ b/run_manual_translation.php @@ -68,7 +68,7 @@ try { $originalHtml = $stmt->fetchColumn(); if ($originalHtml) { - $translator = new Translate(); + $translator = new Translate(LIBRETRANSLATE_URL); $textContent = strip_tags(html_entity_decode($originalHtml)); $sourceLang = $translator->detectLanguage($textContent); @@ -78,8 +78,7 @@ try { if ($translatedHtml && $translatedHtml !== $originalHtml) { $sender = new DiscordSender(DISCORD_BOT_TOKEN); - $mention = "<@{" - . $userId . "}>; + $mention = "<@" . $userId . ">"; $finalContent = $mention . " *Traducción a {" . $targetLang . "}:*\n" . $translatedHtml; $sender->sendMessage($channelId, $finalContent); direct_log("[MANUAL_TRANSLATE_WORKER] Traducción enviada con éxito."); diff --git a/src/TranslationWorker.php b/src/TranslationWorker.php index 061f5de..2c4bad8 100755 --- a/src/TranslationWorker.php +++ b/src/TranslationWorker.php @@ -10,7 +10,7 @@ class TranslationWorker { public function __construct($workerId, $pdo) { $this->workerId = "worker_" . $workerId . "_" . getmypid(); $this->pdo = $pdo; - $this->translator = new Translate(); + $this->translator = new Translate(LIBRETRANSLATE_URL); // Configurar sleep time desde environment if (isset($_ENV['TRANSLATION_WORKER_SLEEP'])) { diff --git a/stickers b/stickers new file mode 100644 index 0000000..e69de29 diff --git a/translate_message.php b/translate_message.php index 877b94a..2e03c41 100755 --- a/translate_message.php +++ b/translate_message.php @@ -39,7 +39,7 @@ try { exit; } - $translator = new Translate(); + $translator = new Translate(LIBRETRANSLATE_URL); $translatedText = $translator->translateHtml($originalText, $sourceLang, $targetLang); if ($translatedText) { diff --git a/translate_proxy.php b/translate_proxy.php index 85b5cc1..ee52687 100755 --- a/translate_proxy.php +++ b/translate_proxy.php @@ -21,18 +21,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { // Cargar las variables de entorno try { - // Determinar el entorno desde la variable de entorno del servidor - $environment = $_SERVER['APP_ENVIRONMENT'] ?? 'pruebas'; // Usar 'pruebas' como fallback - $envFile = '.env.' . $environment; + // Determinar el entorno + $environment = getenv('APP_ENVIRONMENT') ?: 'pruebas'; - // Verificar si el archivo de entorno existe - if (!file_exists(__DIR__ . '/' . $envFile)) { - throw new \Dotenv\Exception\InvalidPathException("El archivo de entorno '{$envFile}' no se encuentra."); + // Construir el nombre del archivo de entorno correcto + if ($environment === 'reod') { + $envFile = '.env'; + } else { + $envFile = '.env.' . $environment; } - // Cargar el archivo de entorno correspondiente + // Cargar el archivo de entorno $dotenv = Dotenv\Dotenv::createImmutable(__DIR__, $envFile); $dotenv->load(); + } catch (\Dotenv\Exception\InvalidPathException $e) { http_response_code(500); $errorMessage = "Error al cargar la configuración del entorno: " . $e->getMessage();