Compare commits
2 Commits
26414094d4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7953a56501 | |||
| bc77082c20 |
49
.env.reod
49
.env.reod
@@ -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"
|
|
||||||
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
@@ -33,7 +33,7 @@ try {
|
|||||||
throw new Exception("La variable de entorno LIBRETRANSLATE_URL no está configurada en tu archivo .env");
|
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();
|
$libreLanguages = $translator->getSupportedLanguages();
|
||||||
|
|
||||||
if ($libreLanguages === null) {
|
if ($libreLanguages === null) {
|
||||||
|
|||||||
158
analisis_archivos_comunes.txt
Normal file
158
analisis_archivos_comunes.txt
Normal file
@@ -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.
|
||||||
0
attachments
Normal file
0
attachments
Normal file
0
clear_opcache.php
Normal file → Executable file
0
clear_opcache.php
Normal file → Executable file
@@ -7,45 +7,37 @@ require_once __DIR__ . '/../vendor/autoload.php';
|
|||||||
// Verificar si estamos en un contenedor Docker
|
// Verificar si estamos en un contenedor Docker
|
||||||
$is_docker = (getenv('DOCKER_CONTAINER') === '1') || ($_SERVER['DOCKER_CONTAINER'] ?? null) === '1';
|
$is_docker = (getenv('DOCKER_CONTAINER') === '1') || ($_SERVER['DOCKER_CONTAINER'] ?? null) === '1';
|
||||||
|
|
||||||
if ($is_docker) {
|
// Entorno: cargar archivo .env según APP_ENVIRONMENT
|
||||||
// Docker: cargar archivo .env creado por el entrypoint
|
$env = getenv('APP_ENVIRONMENT') ?: ($_SERVER['APP_ENVIRONMENT'] ?? 'pruebas');
|
||||||
$dotenv = null;
|
|
||||||
if (file_exists(dirname(__DIR__) . '/.env')) {
|
if ($env === 'reod') {
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
|
|
||||||
try {
|
|
||||||
$dotenv->load();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
die('Error al cargar el archivo de entorno en Docker: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Entorno local: cargar archivo .env según APP_ENVIRONMENT
|
|
||||||
$env = getenv('APP_ENVIRONMENT') ?: ($_SERVER['APP_ENVIRONMENT'] ?? 'pruebas');
|
|
||||||
$envFile = '.env';
|
$envFile = '.env';
|
||||||
if ($env) {
|
} else {
|
||||||
$envFile = '.env.' . $env;
|
$envFile = '.env.' . $env;
|
||||||
}
|
|
||||||
|
|
||||||
$dotenv = null;
|
|
||||||
if (file_exists(dirname(__DIR__) . '/' . $envFile)) {
|
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__), $envFile);
|
|
||||||
} elseif (file_exists(dirname(__DIR__) . '/.env')) {
|
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($dotenv) {
|
|
||||||
try {
|
|
||||||
$dotenv->load();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
die('Error al cargar el archivo de entorno: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
$dotenv->required([
|
|
||||||
'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS',
|
|
||||||
'JWT_SECRET', 'APP_URL'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dotenv = null;
|
||||||
|
if (file_exists(dirname(__DIR__) . '/' . $envFile)) {
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__), $envFile);
|
||||||
|
} elseif (file_exists(dirname(__DIR__) . '/.env')) {
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dotenv) {
|
||||||
|
try {
|
||||||
|
$dotenv->load();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
die('Error al cargar el archivo de entorno: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
// ... el resto de la configuración ...
|
||||||
|
// Aquí es donde definimos las variables obligatorias
|
||||||
|
$dotenv->required([
|
||||||
|
'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS',
|
||||||
|
'JWT_SECRET', 'APP_URL'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Environment Configuration
|
// Environment Configuration
|
||||||
define('ENVIRONMENT', $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'production');
|
define('ENVIRONMENT', $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'production');
|
||||||
|
|
||||||
@@ -54,7 +46,9 @@ $is_cli = (php_sapi_name() === 'cli' || defined('STDIN'));
|
|||||||
|
|
||||||
// Helper function to get env vars
|
// Helper function to get env vars
|
||||||
function getEnvVar($name, $default = null) {
|
function getEnvVar($name, $default = null) {
|
||||||
return $_ENV[$name] ?? $_SERVER[$name] ?? getenv($name) ?? $default;
|
// Priorizamos getenv() para las variables cargadas por Dotenv,
|
||||||
|
// ya que sabemos que funciona y $_ENV puede estar corrupto o contener Array en algunos entornos Docker.
|
||||||
|
return getenv($name) ?: ($_ENV[$name] ?? $_SERVER[$name] ?? $default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configurar la URL base y el protocolo
|
// Configurar la URL base y el protocolo
|
||||||
|
|||||||
0
direct_check.php
Normal file → Executable file
0
direct_check.php
Normal file → Executable file
224
discord_bot.php
224
discord_bot.php
@@ -38,6 +38,10 @@ if (!defined('DISCORD_BOT_TOKEN') || empty(DISCORD_BOT_TOKEN)) {
|
|||||||
try {
|
try {
|
||||||
$discord = new Discord([
|
$discord = new Discord([
|
||||||
'token' => DISCORD_BOT_TOKEN,
|
'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,
|
'intents' => Intents::GUILDS | Intents::GUILD_MESSAGES | Intents::DIRECT_MESSAGES | Intents::GUILD_MEMBERS | Intents::GUILD_MESSAGE_REACTIONS,
|
||||||
'logger' => $logger,
|
'logger' => $logger,
|
||||||
'loop' => \React\EventLoop\Loop::get(),
|
'loop' => \React\EventLoop\Loop::get(),
|
||||||
@@ -45,6 +49,7 @@ try {
|
|||||||
'heartbeat_interval' => 30, // Enviar heartbeat cada 30 segundos
|
'heartbeat_interval' => 30, // Enviar heartbeat cada 30 segundos
|
||||||
'reconnect_timeout' => 60, // Timeout para reconexión
|
'reconnect_timeout' => 60, // Timeout para reconexión
|
||||||
]
|
]
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Manejar eventos de conexión y desconexión
|
// Manejar eventos de conexión y desconexión
|
||||||
@@ -104,6 +109,39 @@ try {
|
|||||||
$originalMessage = $interaction->message;
|
$originalMessage = $interaction->message;
|
||||||
$channel = $interaction->channel;
|
$channel = $interaction->channel;
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
// Responder de inmediato para evitar timeout de interacción
|
||||||
|
$interaction->respondWithMessage(MessageBuilder::new()->setContent('⌛ Procesando traducción...'), true);
|
||||||
|
|
||||||
|
// Extraer contenido del mensaje: primero content plano, luego embeds como fallback
|
||||||
|
$originalContent = trim((string) ($originalMessage->content ?? ''));
|
||||||
|
if ($originalContent === '' && count($originalMessage->embeds) > 0) {
|
||||||
|
foreach ($originalMessage->embeds as $embed) {
|
||||||
|
$originalContent .= trim((string) ($embed->description ?? '')) . "\n";
|
||||||
|
}
|
||||||
|
$originalContent = trim($originalContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($originalContent)) {
|
||||||
|
try {
|
||||||
|
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
|
$sourceLang = $translator->detectLanguage($originalContent) ?? 'es';
|
||||||
|
if ($sourceLang === $targetLang) {
|
||||||
|
// Fallback: muchas plantillas están en ES; intenta forzar ES como origen
|
||||||
|
$fallbackSrc = 'es';
|
||||||
|
if ($fallbackSrc !== $targetLang) {
|
||||||
|
$translatedText = $translator->translateText($originalContent, $fallbackSrc, $targetLang);
|
||||||
|
$sourceLang = $fallbackSrc;
|
||||||
|
} else {
|
||||||
|
$translatedText = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$translatedText = $translator->translateText($originalContent, $sourceLang, $targetLang);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener bandera desde supported_languages
|
||||||
|
$flag = '';
|
||||||
|
=======
|
||||||
try {
|
try {
|
||||||
// Buscar el mensaje original que se está traduciendo
|
// Buscar el mensaje original que se está traduciendo
|
||||||
// Para interacciones, el mensaje original está en el mensaje de la interacción
|
// Para interacciones, el mensaje original está en el mensaje de la interacción
|
||||||
@@ -114,6 +152,7 @@ try {
|
|||||||
if ($messageReference && $messageReference->message_id) {
|
if ($messageReference && $messageReference->message_id) {
|
||||||
$referencedMessageId = $messageReference->message_id;
|
$referencedMessageId = $messageReference->message_id;
|
||||||
$channel->messages->fetch($referencedMessageId)->done(function (Message $referencedMessage) use ($interaction, $targetLang, $logger, $discord, $userId, $pdo) {
|
$channel->messages->fetch($referencedMessageId)->done(function (Message $referencedMessage) use ($interaction, $targetLang, $logger, $discord, $userId, $pdo) {
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
try {
|
try {
|
||||||
$originalContent = trim((string) ($referencedMessage->content ?? ''));
|
$originalContent = trim((string) ($referencedMessage->content ?? ''));
|
||||||
|
|
||||||
@@ -224,6 +263,111 @@ try {
|
|||||||
return; // Salir después de manejar la interacción
|
return; // Salir después de manejar la interacción
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MANEJAR TRADUCCIÓN A IDIOMA ESPECÍFICO (desde botones de idiomas)
|
||||||
|
if (strpos($customId, 'translate_to_lang:') === 0) {
|
||||||
|
// Extraer el ID del mensaje original y el idioma destino del custom_id
|
||||||
|
$parts = explode(':', $customId);
|
||||||
|
if (count($parts) < 3) {
|
||||||
|
$interaction->respondWithMessage(MessageBuilder::new()->setContent("❌ Error: Formato de botón de traducción inválido."), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$originalMessageId = $parts[1];
|
||||||
|
$targetLang = $parts[2];
|
||||||
|
|
||||||
|
// 1. Deferir la respuesta de forma efímera para que el usuario sepa que se está procesando
|
||||||
|
$interaction->acknowledge(true)->done(function() use ($originalMessageId, $targetLang, $interaction, $discord, $pdo, $logger, $userId) {
|
||||||
|
// 2. Obtener el mensaje original usando su ID
|
||||||
|
$interaction->channel->messages->fetch($originalMessageId)->done(function ($originalMessage) use ($originalMessageId, $interaction, $discord, $pdo, $logger, $userId, $targetLang) {
|
||||||
|
$originalContent = (string) ($originalMessage->content ?? '');
|
||||||
|
|
||||||
|
$emojiMap = [];
|
||||||
|
preg_match_all('/<a?:(\w+):(\d+)>/', $originalContent, $matches, PREG_OFFSET_CAPTURE);
|
||||||
|
foreach ($matches[0] as $match) {
|
||||||
|
$emojiMap[$match[1]] = $match[0];
|
||||||
|
}
|
||||||
|
$contentForTranslation = preg_replace('/<a?:(\w+):(\d+)>/', '', $originalContent);
|
||||||
|
$contentForTranslation = trim($contentForTranslation);
|
||||||
|
|
||||||
|
if (empty($contentForTranslation)) {
|
||||||
|
$interaction->editOriginalResponse(MessageBuilder::new()->setContent("⚠️ El mensaje no contiene texto traducible."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
|
$detectionResultForSource = $translator->detectLanguage($contentForTranslation);
|
||||||
|
$sourceLang = $detectionResultForSource[0]['language'] ?? 'es';
|
||||||
|
|
||||||
|
if ($sourceLang === $targetLang) {
|
||||||
|
$interaction->editOriginalResponse(MessageBuilder::new()->setContent("⚠️ El mensaje ya está en el idioma seleccionado ({$targetLang})."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$translatedText = $translator->translateText($contentForTranslation, $sourceLang, $targetLang);
|
||||||
|
|
||||||
|
if (!empty($translatedText)) {
|
||||||
|
foreach ($emojiMap as $emojiTag) {
|
||||||
|
$translatedText .= ' ' . $emojiTag;
|
||||||
|
}
|
||||||
|
$translatedText = trim($translatedText);
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT flag_emoji, language_name FROM supported_languages WHERE language_code = ? AND is_active = 1");
|
||||||
|
$stmt->execute([$targetLang]);
|
||||||
|
$langInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$flag = $langInfo['flag_emoji'] ?? '🏳️';
|
||||||
|
$langName = $langInfo['language_name'] ?? strtoupper($targetLang);
|
||||||
|
|
||||||
|
$embed = new Embed($discord);
|
||||||
|
$embed->setTitle("{$flag} Traducción a {$langName}");
|
||||||
|
$embed->setDescription($translatedText);
|
||||||
|
$embed->setColor("#5865F2");
|
||||||
|
$embed->setFooter("Traducido de {$sourceLang} • Solo tú puedes ver esto");
|
||||||
|
|
||||||
|
$imageUrl = null;
|
||||||
|
if ($originalMessage->attachments !== null && count($originalMessage->attachments) > 0) {
|
||||||
|
$firstAttachment = $originalMessage->attachments->first();
|
||||||
|
if ($firstAttachment && isset($firstAttachment->url)) {
|
||||||
|
$contentType = $firstAttachment->content_type ?? '';
|
||||||
|
if (strpos($contentType, 'image/') === 0 || strpos($contentType, 'video/') === 0) {
|
||||||
|
$imageUrl = $firstAttachment->url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$imageUrl && $originalMessage->stickers !== null && count($originalMessage->stickers) > 0) {
|
||||||
|
$firstSticker = $originalMessage->stickers->first();
|
||||||
|
if ($firstSticker && isset($firstSticker->image_url)) {
|
||||||
|
$imageUrl = $firstSticker->image_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($imageUrl) {
|
||||||
|
$embed->setImage($imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Editar la respuesta original con el resultado
|
||||||
|
$builder = MessageBuilder::new()->addEmbed($embed);
|
||||||
|
$interaction->sendFollowUpMessage($builder, true);
|
||||||
|
|
||||||
|
$logger->info("[TRANSLATION_BUTTON] Usuario {$userId} tradujo mensaje #{$originalMessageId} de {$sourceLang} a {$targetLang}");
|
||||||
|
} else {
|
||||||
|
$interaction->editOriginalResponse(MessageBuilder::new()->setContent("⚠️ No se pudo traducir el mensaje."));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$logger->error("[TRANSLATION_BUTTON] Error al traducir", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
||||||
|
$interaction->editOriginalResponse(MessageBuilder::new()->setContent("❌ Error al traducir: " . $e->getMessage()));
|
||||||
|
}
|
||||||
|
}, function ($error) use ($interaction, $logger) {
|
||||||
|
$logger->error("[TRANSLATION_BUTTON] Error al obtener mensaje original", ['error' => $error->getMessage()]);
|
||||||
|
$interaction->editOriginalResponse(MessageBuilder::new()->setContent("❌ No se pudo obtener el mensaje original."));
|
||||||
|
});
|
||||||
|
}, function ($error) use ($logger, $interaction) {
|
||||||
|
$logger->error("[TRANSLATION_BUTTON] Error al reconocer (defer) la interacción.", ['error' => $error->getMessage()]);
|
||||||
|
// If even deferring fails, we can't edit the response.
|
||||||
|
});
|
||||||
|
return; // Finalizar para no procesar otros ifs
|
||||||
|
}
|
||||||
|
|
||||||
// Traducción de plantillas completas (comandos #)
|
// Traducción de plantillas completas (comandos #)
|
||||||
if (strpos($customId, 'translate_template:') === 0) {
|
if (strpos($customId, 'translate_template:') === 0) {
|
||||||
$payload = substr($customId, strlen('translate_template:'));
|
$payload = substr($customId, strlen('translate_template:'));
|
||||||
@@ -597,6 +741,27 @@ function handleDiscordCommand(Message $message, PDO $pdo, Logger $logger)
|
|||||||
function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
<<<<<<< HEAD
|
||||||
|
$translator = new Translate(LIBRETRANSLATE_URL); // Instanciar al inicio
|
||||||
|
|
||||||
|
$messageContentOriginal = trim($message->content);
|
||||||
|
|
||||||
|
// Determine if there is translatable text content based on LibreTranslate confidence
|
||||||
|
$hasTranslatableText = false;
|
||||||
|
$contentForDetection = ''; // Initialize outside if block for broader scope
|
||||||
|
|
||||||
|
if (!empty($messageContentOriginal)) { // Only try to detect if message has any content
|
||||||
|
// Prepare content for language detection: remove custom Discord emojis
|
||||||
|
$contentForDetection = preg_replace('/<a?:(\w+):(\d+)>/u', '', $messageContentOriginal);
|
||||||
|
// Removed: $contentForDetection = strip_tags($contentForDetection); // This was the culprit
|
||||||
|
$contentForDetection = trim($contentForDetection);
|
||||||
|
|
||||||
|
if (!empty($contentForDetection)) { // Only if there's something left to detect
|
||||||
|
$detectionResult = $translator->detectLanguage($contentForDetection);
|
||||||
|
$confidence = $detectionResult[0]['confidence'] ?? 0.0;
|
||||||
|
if ($confidence > 0.0) {
|
||||||
|
$hasTranslatableText = true;
|
||||||
|
=======
|
||||||
$text = $message->content;
|
$text = $message->content;
|
||||||
$attachments = $message->attachments;
|
$attachments = $message->attachments;
|
||||||
|
|
||||||
@@ -613,10 +778,28 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
|||||||
}
|
}
|
||||||
if (!empty($attachment->caption)) {
|
if (!empty($attachment->caption)) {
|
||||||
$attachmentText .= $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.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Detectar idioma original (using the content prepared for detection, which we know has text)
|
||||||
|
$finalDetectionResult = $translator->detectLanguage($contentForDetection); // This will be the full array or null
|
||||||
|
$detectedLang = $finalDetectionResult[0]['language'] ?? 'es'; // Correctly extract language
|
||||||
|
|
||||||
|
// 2. Obtener todos los idiomas activos con información completa
|
||||||
|
$langStmt = $pdo->query("SELECT language_code, language_name, flag_emoji FROM supported_languages WHERE is_active = 1 ORDER BY language_name ASC");
|
||||||
|
$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
|
// Combinar texto principal con texto de attachments
|
||||||
$fullText = trim($text . ' ' . $attachmentText);
|
$fullText = trim($text . ' ' . $attachmentText);
|
||||||
$hasAnyText = !empty($fullText);
|
$hasAnyText = !empty($fullText);
|
||||||
@@ -637,21 +820,42 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
|||||||
$activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC);
|
$activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
// Filtrar idiomas diferentes al detectado
|
// Filtrar idiomas diferentes al detectado
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
$targetLangs = array_filter($activeLangs, function($lang) use ($detectedLang) {
|
$targetLangs = array_filter($activeLangs, function($lang) use ($detectedLang) {
|
||||||
return $lang['language_code'] !== $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'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Crear botones de traducción para cada idioma destino
|
||||||
|
=======
|
||||||
if (empty($targetLangs)) {
|
if (empty($targetLangs)) {
|
||||||
$logger->info("[TRADUCCIÓN] No hay idiomas disponibles para traducir desde '$detectedLang'");
|
$logger->info("[TRADUCCIÓN] No hay idiomas disponibles para traducir desde '$detectedLang'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear botones con banderas
|
// Crear botones con banderas
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
$components = [];
|
$components = [];
|
||||||
$actionRow = ActionRow::new();
|
$actionRow = ActionRow::new();
|
||||||
$buttonCount = 0;
|
$buttonCount = 0;
|
||||||
|
|
||||||
foreach ($targetLangs as $lang) {
|
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'])) {
|
||||||
|
$button->setEmoji($lang['flag_emoji']);
|
||||||
|
}
|
||||||
|
$actionRow->addComponent($button);
|
||||||
|
$buttonCount++;
|
||||||
|
|
||||||
|
=======
|
||||||
$button = Button::new(Button::STYLE_SECONDARY, 'translate_manual:' . $lang['language_code'])
|
$button = Button::new(Button::STYLE_SECONDARY, 'translate_manual:' . $lang['language_code'])
|
||||||
->setLabel($lang['language_name']);
|
->setLabel($lang['language_name']);
|
||||||
|
|
||||||
@@ -663,16 +867,35 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
|||||||
$buttonCount++;
|
$buttonCount++;
|
||||||
|
|
||||||
// Discord permite máximo 5 botones por ActionRow
|
// Discord permite máximo 5 botones por ActionRow
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
if ($buttonCount % 5 === 0) {
|
if ($buttonCount % 5 === 0) {
|
||||||
$components[] = $actionRow;
|
$components[] = $actionRow;
|
||||||
$actionRow = ActionRow::new();
|
$actionRow = ActionRow::new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
if ($buttonCount % 5 !== 0) {
|
if ($buttonCount % 5 !== 0) {
|
||||||
$components[] = $actionRow;
|
$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:')
|
||||||
|
->setComponents($components);
|
||||||
|
|
||||||
|
$message->reply($builder)->done(function () use ($logger, $message, $detectedLang) {
|
||||||
|
$logger->info("[TRANSLATION_BUTTONS] Botones de traducción enviados para mensaje #{$message->id} (idioma detectado: $detectedLang)");
|
||||||
|
}, function ($error) use ($logger, $message) {
|
||||||
|
$logger->error("[TRANSLATION_BUTTONS] Error al enviar botones para mensaje #{$message->id}", ['error' => $error->getMessage()]);
|
||||||
|
});
|
||||||
|
|
||||||
|
} 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
|
// Enviar mensaje con botones efímeros
|
||||||
$builder = MessageBuilder::new()
|
$builder = MessageBuilder::new()
|
||||||
// ->setContent("🌍 **Selecciona idioma para traducir:**")
|
// ->setContent("🌍 **Selecciona idioma para traducir:**")
|
||||||
@@ -684,6 +907,7 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
|||||||
|
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$logger->error("[Error Traducción Discord]", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
$logger->error("[Error Traducción Discord]", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
||||||
|
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
86
docker/bot-lastwar.yaml
Executable file
86
docker/bot-lastwar.yaml
Executable file
@@ -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
|
||||||
@@ -13,7 +13,7 @@ echo "Generando archivo .env desde variables de entorno..."
|
|||||||
# Eliminar todos los archivos .env existentes para evitar conflictos
|
# Eliminar todos los archivos .env existentes para evitar conflictos
|
||||||
rm -f /var/www/html/bot/.env* 2>/dev/null || true
|
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
|
# Determinar el nombre del archivo .env según el entorno
|
||||||
if [ "$ENVIRONMENT" = "reod" ]; then
|
if [ "$ENVIRONMENT" = "reod" ]; then
|
||||||
@@ -39,7 +39,10 @@ fi
|
|||||||
|
|
||||||
rm -f /tmp/env_vars.txt
|
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
|
if [ -f /var/www/html/bot/composer.json ]; then
|
||||||
echo "Instalando dependencias de Composer..."
|
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
|
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
|
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
|
if [ "$ENVIRONMENT" = "reod" ]; then
|
||||||
a2ensite reod-dragon.ddns.net.conf 2>/dev/null || true
|
a2ensite reod-dragon.ddns.net.conf 2>/dev/null || true
|
||||||
else
|
else
|
||||||
a2dissite reod-dragon.ddns.net.conf 2>/dev/null || true
|
a2dissite reod-dragon.ddns.net.conf 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Iniciando Supervisor..."
|
echo "Iniciando Supervisor (que gestionará Apache y los workers)..."
|
||||||
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ class DatabaseConnection {
|
|||||||
|
|
||||||
private function __construct() {
|
private function __construct() {
|
||||||
$this->config = [
|
$this->config = [
|
||||||
'host' => $_ENV['DB_HOST'] ?? 'localhost',
|
'host' => DB_HOST,
|
||||||
'port' => $_ENV['DB_PORT'] ?? '3306',
|
'port' => DB_PORT,
|
||||||
'name' => $_ENV['DB_NAME'] ?? 'bot',
|
'name' => DB_NAME,
|
||||||
'user' => $_ENV['DB_USER'] ?? 'nickpons666',
|
'user' => DB_USER,
|
||||||
'pass' => $_ENV['DB_PASS'] ?? 'MiPo6425@@',
|
'pass' => DB_PASS,
|
||||||
'charset' => 'utf8mb4',
|
'charset' => 'utf8mb4',
|
||||||
'timeout' => 30, // Tiempo de espera de conexión en segundos
|
'timeout' => 30, // Tiempo de espera de conexión en segundos
|
||||||
'reconnect_attempts' => 3, // Número de intentos de reconexión
|
'reconnect_attempts' => 3, // Número de intentos de reconexión
|
||||||
|
|||||||
31
plan_diagnostico_docker.md
Executable file
31
plan_diagnostico_docker.md
Executable file
@@ -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.
|
||||||
515
plan_separacion_plataformas.md
Normal file
515
plan_separacion_plataformas.md
Normal file
@@ -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.
|
||||||
@@ -20,7 +20,7 @@ require_once __DIR__ . '/src/DiscordSender.php';
|
|||||||
use Discord\Parts\Embed\Embed;
|
use Discord\Parts\Embed\Embed;
|
||||||
|
|
||||||
// Instanciar las clases necesarias
|
// Instanciar las clases necesarias
|
||||||
$translator = new Translate();
|
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
|
|
||||||
$running = true;
|
$running = true;
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ while ($running) {
|
|||||||
custom_log("[JOB #{$job['id']}] Procesando trabajo para Discord con múltiples idiomas.");
|
custom_log("[JOB #{$job['id']}] Procesando trabajo para Discord con múltiples idiomas.");
|
||||||
$targetLangs = explode(',', $job['target_lang']);
|
$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
|
// Pre-procesar el texto para proteger las menciones de Discord
|
||||||
$originalText = $job['text_to_translate'];
|
$originalText = $job['text_to_translate'];
|
||||||
@@ -128,7 +128,7 @@ while ($running) {
|
|||||||
// --- LÓGICA MEJORADA PARA TELEGRAM USANDO UN SOLO MENSAJE HTML ---
|
// --- LÓGICA MEJORADA PARA TELEGRAM USANDO UN SOLO MENSAJE HTML ---
|
||||||
custom_log("[JOB #{$job['id']}] Procesando trabajo para Telegram con múltiples idiomas.");
|
custom_log("[JOB #{$job['id']}] Procesando trabajo para Telegram con múltiples idiomas.");
|
||||||
$targetLangs = explode(',', $job['target_lang']);
|
$targetLangs = explode(',', $job['target_lang']);
|
||||||
$telegram = new TelegramSender($_ENV['TELEGRAM_BOT_TOKEN'], $pdo);
|
$telegram = new TelegramSender(TELEGRAM_BOT_TOKEN, $pdo, BOT_BASE_URL);
|
||||||
|
|
||||||
$htmlOutput = "<b>Traducciones:</b>\n\n";
|
$htmlOutput = "<b>Traducciones:</b>\n\n";
|
||||||
$translationCount = 0;
|
$translationCount = 0;
|
||||||
@@ -137,7 +137,7 @@ while ($running) {
|
|||||||
if (empty($langCode)) continue;
|
if (empty($langCode)) continue;
|
||||||
|
|
||||||
$translatedText = $translator->translateHtml($job['text_to_translate'], $job['source_lang'], $langCode);
|
$translatedText = $translator->translateHtml($job['text_to_translate'], $job['source_lang'], $langCode);
|
||||||
|
|
||||||
if ($translatedText && trim(strtolower($translatedText)) !== trim(strtolower($job['text_to_translate']))) {
|
if ($translatedText && trim(strtolower($translatedText)) !== trim(strtolower($job['text_to_translate']))) {
|
||||||
// Obtener nombre del idioma y bandera
|
// Obtener nombre del idioma y bandera
|
||||||
$langInfoStmt = $pdo->prepare("SELECT language_name, flag_emoji FROM supported_languages WHERE language_code = ?");
|
$langInfoStmt = $pdo->prepare("SELECT language_name, flag_emoji FROM supported_languages WHERE language_code = ?");
|
||||||
@@ -190,7 +190,6 @@ while ($running) {
|
|||||||
|
|
||||||
pcntl_signal_dispatch();
|
pcntl_signal_dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_log("--- PROCESADOR DE COLA DETENIDO ---");
|
custom_log("--- PROCESADOR DE COLA DETENIDO ---");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ try {
|
|||||||
$originalHtml = $stmt->fetchColumn();
|
$originalHtml = $stmt->fetchColumn();
|
||||||
|
|
||||||
if ($originalHtml) {
|
if ($originalHtml) {
|
||||||
$translator = new Translate();
|
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
$textContent = strip_tags(html_entity_decode($originalHtml));
|
$textContent = strip_tags(html_entity_decode($originalHtml));
|
||||||
$sourceLang = $translator->detectLanguage($textContent);
|
$sourceLang = $translator->detectLanguage($textContent);
|
||||||
|
|
||||||
@@ -78,8 +78,7 @@ try {
|
|||||||
|
|
||||||
if ($translatedHtml && $translatedHtml !== $originalHtml) {
|
if ($translatedHtml && $translatedHtml !== $originalHtml) {
|
||||||
$sender = new DiscordSender(DISCORD_BOT_TOKEN);
|
$sender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||||
$mention = "<@{"
|
$mention = "<@" . $userId . ">";
|
||||||
. $userId . "}>;
|
|
||||||
$finalContent = $mention . " *Traducción a {" . $targetLang . "}:*\n" . $translatedHtml;
|
$finalContent = $mention . " *Traducción a {" . $targetLang . "}:*\n" . $translatedHtml;
|
||||||
$sender->sendMessage($channelId, $finalContent);
|
$sender->sendMessage($channelId, $finalContent);
|
||||||
direct_log("[MANUAL_TRANSLATE_WORKER] Traducción enviada con éxito.");
|
direct_log("[MANUAL_TRANSLATE_WORKER] Traducción enviada con éxito.");
|
||||||
|
|||||||
@@ -4,21 +4,22 @@ class Translate
|
|||||||
{
|
{
|
||||||
private $apiUrl;
|
private $apiUrl;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct(string $apiUrl)
|
||||||
{
|
{
|
||||||
$this->apiUrl = rtrim($_ENV['LIBRETRANSLATE_URL'], '/');
|
$this->apiUrl = rtrim($apiUrl, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function detectLanguage($text)
|
public function detectLanguage($text)
|
||||||
{
|
{
|
||||||
if (empty(trim($text))) {
|
if (empty(trim($text))) {
|
||||||
return null;
|
return null; // Or return an empty array indicating no detection
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->request('/detect', ['q' => $text]);
|
$response = $this->request('/detect', ['q' => $text]);
|
||||||
|
|
||||||
|
// Return the full response array if detection was successful
|
||||||
if (isset($response[0]['language'])) {
|
if (isset($response[0]['language'])) {
|
||||||
return $response[0]['language'];
|
return $response; // Return the array, e.g., [{"confidence":90.0,"language":"es"}]
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class TranslationWorker {
|
|||||||
public function __construct($workerId, $pdo) {
|
public function __construct($workerId, $pdo) {
|
||||||
$this->workerId = "worker_" . $workerId . "_" . getmypid();
|
$this->workerId = "worker_" . $workerId . "_" . getmypid();
|
||||||
$this->pdo = $pdo;
|
$this->pdo = $pdo;
|
||||||
$this->translator = new Translate();
|
$this->translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
|
|
||||||
// Configurar sleep time desde environment
|
// Configurar sleep time desde environment
|
||||||
if (isset($_ENV['TRANSLATION_WORKER_SLEEP'])) {
|
if (isset($_ENV['TRANSLATION_WORKER_SLEEP'])) {
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ class TelegramSender
|
|||||||
private $botToken;
|
private $botToken;
|
||||||
private $apiUrl = 'https://api.telegram.org/bot';
|
private $apiUrl = 'https://api.telegram.org/bot';
|
||||||
private $pdo;
|
private $pdo;
|
||||||
|
private $baseUrl; // Nueva propiedad para almacenar BOT_BASE_URL
|
||||||
|
|
||||||
public function __construct($botToken, $pdo)
|
public function __construct($botToken, $pdo, string $baseUrl)
|
||||||
{
|
{
|
||||||
$this->botToken = $botToken;
|
$this->botToken = $botToken;
|
||||||
$this->pdo = $pdo;
|
$this->pdo = $pdo;
|
||||||
|
$this->baseUrl = rtrim($baseUrl, '/'); // Asegurarse de que no tenga una barra al final
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMessage($chatId, $content, $options = [], $addTranslateButton = false, $messageLanguage = 'es', $originalFullContent = null)
|
public function sendMessage($chatId, $content, $options = [], $addTranslateButton = false, $messageLanguage = 'es', $originalFullContent = null)
|
||||||
@@ -302,7 +304,7 @@ class TelegramSender
|
|||||||
|
|
||||||
// Convertir rutas relativas a absolutas si es necesario
|
// Convertir rutas relativas a absolutas si es necesario
|
||||||
if (!preg_match('/^https?:\/\//', $image_url)) {
|
if (!preg_match('/^https?:\/\//', $image_url)) {
|
||||||
$base_url = rtrim($_ENV['APP_URL'], '/');
|
$base_url = $this->baseUrl;
|
||||||
$image_url = $base_url . '/' . ltrim($image_url, '/');
|
$image_url = $base_url . '/' . ltrim($image_url, '/');
|
||||||
custom_log("[DEBUG] URL de imagen convertida: " . $image_url);
|
custom_log("[DEBUG] URL de imagen convertida: " . $image_url);
|
||||||
|
|
||||||
@@ -605,7 +607,7 @@ class TelegramSender
|
|||||||
|
|
||||||
// Si la URL es relativa, intentar convertirla a absoluta
|
// Si la URL es relativa, intentar convertirla a absoluta
|
||||||
if (!preg_match('/^https?:\/\//i', $url)) {
|
if (!preg_match('/^https?:\/\//i', $url)) {
|
||||||
$baseUrl = rtrim($_ENV['APP_URL'], '/');
|
$baseUrl = $this->baseUrl;
|
||||||
$absoluteUrl = $baseUrl . '/' . ltrim($url, '/');
|
$absoluteUrl = $baseUrl . '/' . ltrim($url, '/');
|
||||||
custom_log("${logPrefix} URL relativa detectada, convirtiendo a absoluta: $absoluteUrl");
|
custom_log("${logPrefix} URL relativa detectada, convirtiendo a absoluta: $absoluteUrl");
|
||||||
$url = $absoluteUrl;
|
$url = $absoluteUrl;
|
||||||
@@ -712,7 +714,7 @@ class TelegramSender
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^https?:\/\//', $url)) {
|
if (!preg_match('/^https?:\/\//', $url)) {
|
||||||
$baseUrl = rtrim($_ENV['APP_URL'], '/');
|
$baseUrl = $this->baseUrl;
|
||||||
$url = $baseUrl . '/' . ltrim($url, '/');
|
$url = $baseUrl . '/' . ltrim($url, '/');
|
||||||
custom_log("[DEBUG] isValidImageUrl: URL convertida a absoluta: " . $url);
|
custom_log("[DEBUG] isValidImageUrl: URL convertida a absoluta: " . $url);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ ini_set('log_errors', 1);
|
|||||||
|
|
||||||
// Verificación de autenticación
|
// Verificación de autenticación
|
||||||
$authToken = $_GET['auth_token'] ?? '';
|
$authToken = $_GET['auth_token'] ?? '';
|
||||||
$expectedToken = $_ENV['TELEGRAM_WEBHOOK_TOKEN'] ?? '';
|
$expectedToken = TELEGRAM_WEBHOOK_TOKEN;
|
||||||
if (!empty($expectedToken) && $authToken !== $expectedToken) {
|
if (!empty($expectedToken) && $authToken !== $expectedToken) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
custom_log("Acceso no autorizado: token inválido.");
|
custom_log("Acceso no autorizado: token inválido.");
|
||||||
@@ -30,7 +30,7 @@ if (!empty($expectedToken) && $authToken !== $expectedToken) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verificar token del bot
|
// Verificar token del bot
|
||||||
$botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? '';
|
$botToken = TELEGRAM_BOT_TOKEN;
|
||||||
if (empty($botToken)) {
|
if (empty($botToken)) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
custom_log("Token de bot no configurado.");
|
custom_log("Token de bot no configurado.");
|
||||||
@@ -49,8 +49,8 @@ if (!$update) {
|
|||||||
custom_log("Update recibido: " . json_encode($update, JSON_PRETTY_PRINT));
|
custom_log("Update recibido: " . json_encode($update, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$telegram = new TelegramSender($botToken, $pdo);
|
$telegram = new TelegramSender(TELEGRAM_BOT_TOKEN, $pdo, BOT_BASE_URL);
|
||||||
$translator = new Translate();
|
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
$commandLocker = new CommandLocker($pdo);
|
$commandLocker = new CommandLocker($pdo);
|
||||||
|
|
||||||
$message = $update['message'] ?? $update['channel_post'] ?? null;
|
$message = $update['message'] ?? $update['channel_post'] ?? null;
|
||||||
@@ -248,7 +248,8 @@ try {
|
|||||||
|
|
||||||
if (!empty($originalText)) {
|
if (!empty($originalText)) {
|
||||||
try {
|
try {
|
||||||
$sourceLang = $translator->detectLanguage($originalText);
|
$detectionResult = $translator->detectLanguage($originalText);
|
||||||
|
$sourceLang = $detectionResult[0]['language'] ?? null;
|
||||||
if ($sourceLang && $sourceLang !== $targetLang) {
|
if ($sourceLang && $sourceLang !== $targetLang) {
|
||||||
$translatedText = $translator->translateHtml($originalText, $sourceLang, $targetLang);
|
$translatedText = $translator->translateHtml($originalText, $sourceLang, $targetLang);
|
||||||
$telegram->answerCallbackQuery($callbackId, ['text' => $translatedText, 'show_alert' => true]);
|
$telegram->answerCallbackQuery($callbackId, ['text' => $translatedText, 'show_alert' => true]);
|
||||||
@@ -279,7 +280,8 @@ try {
|
|||||||
if ($originalContent) {
|
if ($originalContent) {
|
||||||
// Detectar idioma real del contenido y aplicar fallback si coincide con el destino
|
// Detectar idioma real del contenido y aplicar fallback si coincide con el destino
|
||||||
$plain = strip_tags(html_entity_decode($originalContent, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
|
$plain = strip_tags(html_entity_decode($originalContent, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
|
||||||
$sourceLang = $translator->detectLanguage($plain) ?? 'es';
|
$detectionResult = $translator->detectLanguage($plain);
|
||||||
|
$sourceLang = $detectionResult[0]['language'] ?? 'es';
|
||||||
if ($sourceLang === $targetLang) {
|
if ($sourceLang === $targetLang) {
|
||||||
$fallbackSrc = 'es';
|
$fallbackSrc = 'es';
|
||||||
if ($fallbackSrc !== $targetLang) {
|
if ($fallbackSrc !== $targetLang) {
|
||||||
@@ -351,7 +353,8 @@ try {
|
|||||||
|
|
||||||
function handleCommand($pdo, $telegram, $commandLocker, $translator, $text, $from, $chatId, $messageId) {
|
function handleCommand($pdo, $telegram, $commandLocker, $translator, $text, $from, $chatId, $messageId) {
|
||||||
$userId = $from['id'];
|
$userId = $from['id'];
|
||||||
$detectedLang = $translator->detectLanguage($text) ?? 'es';
|
$detectionResult = $translator->detectLanguage($text);
|
||||||
|
$detectedLang = $detectionResult[0]['language'] ?? 'es';
|
||||||
|
|
||||||
if (strpos($text, '/setlang') === 0 || strpos($text, '/setlanguage') === 0) {
|
if (strpos($text, '/setlang') === 0 || strpos($text, '/setlanguage') === 0) {
|
||||||
$parts = explode(' ', $text, 2);
|
$parts = explode(' ', $text, 2);
|
||||||
@@ -459,7 +462,8 @@ function handleCommand($pdo, $telegram, $commandLocker, $translator, $text, $fro
|
|||||||
$converter = new HtmlToTelegramHtmlConverter();
|
$converter = new HtmlToTelegramHtmlConverter();
|
||||||
$content = $converter->convert($content);
|
$content = $converter->convert($content);
|
||||||
|
|
||||||
$detectedLang = $translator->detectLanguage(strip_tags($content)) ?? 'es';
|
$detectionResult = $translator->detectLanguage(strip_tags($content));
|
||||||
|
$detectedLang = $detectionResult[0]['language'] ?? 'es';
|
||||||
if ($detectedLang !== 'es') {
|
if ($detectedLang !== 'es') {
|
||||||
$translatedContent = $translator->translateHtml($content, $detectedLang, 'es');
|
$translatedContent = $translator->translateHtml($content, $detectedLang, 'es');
|
||||||
if ($translatedContent) $content = $translatedContent;
|
if ($translatedContent) $content = $translatedContent;
|
||||||
@@ -520,7 +524,8 @@ function handleRegularMessage($pdo, $telegram, $commandLocker, $translator, $tex
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Detectar el idioma del mensaje entrante
|
// 1. Detectar el idioma del mensaje entrante
|
||||||
$detectedLang = $translator->detectLanguage(strip_tags($text)) ?? 'es';
|
$detectionResult = $translator->detectLanguage(strip_tags($text));
|
||||||
|
$detectedLang = $detectionResult[0]['language'] ?? 'es';
|
||||||
|
|
||||||
// 2. Guardar la interacción original en la base de datos
|
// 2. Guardar la interacción original en la base de datos
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ try {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$translator = new Translate();
|
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||||
$translatedText = $translator->translateHtml($originalText, $sourceLang, $targetLang);
|
$translatedText = $translator->translateHtml($originalText, $sourceLang, $targetLang);
|
||||||
|
|
||||||
if ($translatedText) {
|
if ($translatedText) {
|
||||||
|
|||||||
@@ -21,18 +21,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|||||||
|
|
||||||
// Cargar las variables de entorno
|
// Cargar las variables de entorno
|
||||||
try {
|
try {
|
||||||
// Determinar el entorno desde la variable de entorno del servidor
|
// Determinar el entorno
|
||||||
$environment = $_SERVER['APP_ENVIRONMENT'] ?? 'pruebas'; // Usar 'pruebas' como fallback
|
$environment = getenv('APP_ENVIRONMENT') ?: 'pruebas';
|
||||||
$envFile = '.env.' . $environment;
|
|
||||||
|
|
||||||
// Verificar si el archivo de entorno existe
|
// Construir el nombre del archivo de entorno correcto
|
||||||
if (!file_exists(__DIR__ . '/' . $envFile)) {
|
if ($environment === 'reod') {
|
||||||
throw new \Dotenv\Exception\InvalidPathException("El archivo de entorno '{$envFile}' no se encuentra.");
|
$envFile = '.env';
|
||||||
|
} else {
|
||||||
|
$envFile = '.env.' . $environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cargar el archivo de entorno correspondiente
|
// Cargar el archivo de entorno
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, $envFile);
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, $envFile);
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
|
||||||
} catch (\Dotenv\Exception\InvalidPathException $e) {
|
} catch (\Dotenv\Exception\InvalidPathException $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
$errorMessage = "Error al cargar la configuración del entorno: " . $e->getMessage();
|
$errorMessage = "Error al cargar la configuración del entorno: " . $e->getMessage();
|
||||||
|
|||||||
Reference in New Issue
Block a user