Bot Discord - Commit completo con todos los cambios
This commit is contained in:
235
process_translation_queue.php
Executable file
235
process_translation_queue.php
Executable file
@@ -0,0 +1,235 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user