Files
sistema_funcionando_lastwar/process_translation_queue.php

236 lines
10 KiB
PHP
Executable File

<?php
// process_translation_queue.php
// Permitir que el script se ejecute indefinidamente
set_time_limit(0);
require_once __DIR__ . '/includes/logger.php';
custom_log("--- INICIANDO PROCESADOR DE COLA DE TRADUCCIÓN ---");
// Cargar configuración y dependencias
require_once __DIR__ . '/config/config.php';
require_once __DIR__ . '/vendor/autoload.php'; // Asegurarse de que el autoloader de Composer esté cargado
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/src/TelegramSender.php';
require_once __DIR__ . '/src/Translate.php';
require_once __DIR__ . '/src/DiscordSender.php';
// Usar la clase Embed de DiscordPHP
use Discord\Parts\Embed\Embed;
// Instanciar las clases necesarias
$translator = new Translate();
$running = true;
// Manejador de señales para una terminación controlada
pcntl_async_signals(true);
pcntl_signal(SIGINT, function($sig) use (&$running) {
custom_log("Señal SIGINT recibida. Terminando el worker...");
$running = false;
});
pcntl_signal(SIGTERM, function($sig) use (&$running) {
custom_log("Señal SIGTERM recibida. Terminando el worker...");
$running = false;
});
while ($running) {
$job = null;
try {
// Iniciar transacción
$pdo->beginTransaction();
// Obtener un trabajo pendiente y bloquear la fila
$stmt = $pdo->prepare("SELECT * FROM translation_queue WHERE status = 'pending' AND attempts < 5 ORDER BY created_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED");
$stmt->execute();
$job = $stmt->fetch(PDO::FETCH_ASSOC);
if ($job) {
// Marcar el trabajo como 'processing'
$updateStmt = $pdo->prepare("UPDATE translation_queue SET status = 'processing', attempts = attempts + 1, processed_at = NOW() WHERE id = ?");
$updateStmt->execute([$job['id']]);
custom_log("[JOB #{$job['id']}] Trabajo obtenido. Marcado como 'processing'. Intento #" . ($job['attempts'] + 1));
}
// Confirmar la transacción
$pdo->commit();
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
custom_log("Error al obtener trabajo de la cola: " . $e->getMessage());
sleep(5); // Esperar antes de reintentar
continue;
}
if ($job) {
try {
$success = false;
if ($job['platform'] === 'discord') {
// --- LÓGICA MEJORADA PARA DISCORD USANDO UN ARRAY ---
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']);
// Pre-procesar el texto para proteger las menciones de Discord
$originalText = $job['text_to_translate'];
$textToTranslateClean = handleDiscordMentions($originalText, 'to_placeholder');
// Construir el embed como un array simple
$embedData = [
'title' => 'Traducciones',
'color' => 0x4A90E2, // Un color azul
'timestamp' => date('c'),
'fields' => []
];
$translationCount = 0;
foreach ($targetLangs as $langCode) {
if (empty($langCode)) continue;
$translatedText = $translator->translateHtml($textToTranslateClean, $job['source_lang'], $langCode);
if ($translatedText && trim(strtolower($translatedText)) !== trim(strtolower($textToTranslateClean))) {
// Post-procesar para restaurar las menciones de Discord
// Primero, decodificar cualquier entidad HTML que el traductor haya podido introducir
$decodedText = html_entity_decode($translatedText, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$restoredText = handleDiscordMentions($decodedText, 'from_placeholder');
// Obtener nombre del idioma y bandera
$langInfoStmt = $pdo->prepare("SELECT language_name, flag_emoji FROM supported_languages WHERE language_code = ?");
$langInfoStmt->execute([$langCode]);
$langInfo = $langInfoStmt->fetch(PDO::FETCH_ASSOC);
$fieldName = ($langInfo['flag_emoji'] ?? '🏳️') . ' ' . ($langInfo['language_name'] ?? strtoupper($langCode));
$embedData['fields'][] = ['name' => $fieldName, 'value' => $restoredText];
$translationCount++;
}
}
if ($translationCount > 0) {
custom_log("[JOB #{$job['id']}] Enviando embed con {$translationCount} traducciones.");
$response = $discordSender->sendEmbedData($job['chat_id'], $embedData);
if ($response) {
$success = true;
}
} else {
// Si no hubo traducciones válidas, consideramos el trabajo completado para no reintentarlo.
custom_log("[JOB #{$job['id']}] No se generaron traducciones válidas. Marcando como completado.");
$success = true;
}
} else {
// --- 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);
$htmlOutput = "<b>Traducciones:</b>\n\n";
$translationCount = 0;
foreach ($targetLangs as $langCode) {
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 = ?");
$langInfoStmt->execute([$langCode]);
$langInfo = $langInfoStmt->fetch(PDO::FETCH_ASSOC);
$lineTitle = ($langInfo['flag_emoji'] ?? '🏳️') . ' <b>' . ($langInfo['language_name'] ?? strtoupper($langCode)) . '</b>';
// Append to the HTML string
$htmlOutput .= $lineTitle . "\n" . $translatedText . "\n\n";
$translationCount++;
}
}
if ($translationCount > 0) {
custom_log("[JOB #{$job['id']}] Enviando mensaje HTML con {$translationCount} traducciones a Telegram.");
$response = $telegram->sendMessage($job['chat_id'], trim($htmlOutput), [
'parse_mode' => 'HTML',
'reply_to_message_id' => $job['message_id']
]);
if ($response) {
$success = true;
}
} else {
custom_log("[JOB #{$job['id']}] No se generaron traducciones válidas para Telegram. Marcando como completado.");
$success = true;
}
}
// Marcar estado final del trabajo
if ($success) {
$finalStmt = $pdo->prepare("UPDATE translation_queue SET status = 'completed' WHERE id = ?");
$finalStmt->execute([$job['id']]);
custom_log("[JOB #{$job['id']}] Trabajo completado exitosamente.");
} else {
throw new Exception("El envío a la plataforma '{$job['platform']}' falló o no se generaron traducciones.");
}
} catch (Exception $e) {
$errorMessage = $e->getMessage();
custom_log("[JOB #{$job['id']}] Error al procesar el trabajo: " . $errorMessage);
$failStmt = $pdo->prepare("UPDATE translation_queue SET status = 'failed', error_message = ? WHERE id = ?");
$failStmt->execute([$errorMessage, $job['id']]);
}
} else {
// Si no hay trabajos, esperar
custom_log("No hay trabajos pendientes. Esperando...");
sleep(2);
}
pcntl_signal_dispatch();
}
custom_log("--- PROCESADOR DE COLA DETENIDO ---");
/**
* Maneja las menciones de Discord, reemplazándolas por placeholders antes de la traducción
* y restaurándolas después.
*
* @param string $text El texto a procesar.
* @param string $direction 'to_placeholder' para reemplazar menciones por placeholders, 'from_placeholder' para restaurarlas.
* @return string El texto procesado.
*/
function handleDiscordMentions($text, $direction = 'to_placeholder') {
static $mentionMap = [];
if ($direction === 'to_placeholder') {
$mentionMap = [];
$placeholderCounter = 0;
// Simplificado para incluir usuarios, roles y canales
$pattern = '/<(@!?\d+|@&\d+|#\d+)>/';
return preg_replace_callback($pattern, function($matches) use (&$mentionMap, &$placeholderCounter) {
$originalMention = $matches[0]; // Ej: <@12345>
$placeholder = "MENTION{$placeholderCounter}"; // Ej: MENTION0
$mentionMap[$placeholder] = $originalMention;
$placeholderCounter++;
return '<span class="notranslate">' . $placeholder . '</span>';
}, $text);
} elseif ($direction === 'from_placeholder') {
// Decodificar entidades HTML primero
$decodedText = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// Iterar sobre el mapa y reemplazar cada placeholder
foreach ($mentionMap as $placeholder => $originalMention) {
// El patrón busca el placeholder, permitiendo espacios y sin ser sensible a mayúsculas/minúsculas
$patt = '/<span class="notranslate">\s*' . preg_quote($placeholder, '/') . '\s*<\/span>/i';
$decodedText = preg_replace($patt, $originalMention, $decodedText, 1);
}
return $decodedText;
}
return $text;
}