This commit completes the merge process, incorporating remote changes that conflicted with local modifications. It also stages and commits all remaining modified and untracked files as per the user's instruction to 'upload everything without exception'.
235 lines
10 KiB
PHP
Executable File
235 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(LIBRETRANSLATE_URL);
|
|
|
|
$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(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(TELEGRAM_BOT_TOKEN, $pdo, BOT_BASE_URL);
|
|
|
|
$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;
|
|
}
|
|
|