prepare("UPDATE recipients SET chat_mode = ? WHERE platform_id = ? AND platform = 'telegram'"); $stmt->execute([$newMode, $userId]); $responseText = $newMode === 'bot' ? "🤖 Modo cambiado a 'Platicar con bot'. Ahora puedes usar los comandos normales como /comandos." : "🧠 Modo cambiado a 'Usar IA'. Todo lo que escribas será procesado por la IA.\n\nEscribe /agente para volver a este menú."; $telegram->answerCallbackQuery($callbackQuery['id']); $telegram->editMessageText($chatId, $callbackQuery['message']['message_id'], $responseText); custom_log("[MODO AGENTE] Usuario $userId cambió al modo: $newMode"); exit('ok'); } } $stmt = $pdo->prepare("SELECT chat_mode FROM recipients WHERE platform_id = ? AND platform = 'telegram'"); $stmt->execute([$userId]); $userChatMode = $stmt->fetchColumn(); if ($userChatMode === false) { custom_log("[NUEVO USUARIO] Usuario $userId no encontrado. Iniciando proceso de bienvenida."); // 1. Obtener la configuración del mensaje de bienvenida $configStmt = $pdo->query("SELECT * FROM telegram_bot_messages WHERE id = 1"); $welcomeConfig = $configStmt->fetch(PDO::FETCH_ASSOC); // Verificar si el registro de usuarios está activo if ($welcomeConfig && $welcomeConfig['register_users']) { // 2. Registrar al nuevo usuario $from = $message['from']; $userName = trim(($from['first_name'] ?? '') . ' ' . ($from['last_name'] ?? '')); $languageCode = $from['language_code'] ?? 'es'; $insertStmt = $pdo->prepare( "INSERT INTO recipients (platform_id, name, type, platform, language_code, chat_mode) VALUES (?, ?, 'user', 'telegram', ?, 'agent') ON DUPLICATE KEY UPDATE name = VALUES(name), language_code = VALUES(language_code)" ); $insertStmt->execute([$userId, $userName, $languageCode]); custom_log("[NUEVO USUARIO] Usuario $userId ($userName) registrado con modo 'agent'."); // 3. Enviar el mensaje de bienvenida si está activo if ($welcomeConfig['is_active']) { $messageText = str_replace('{user_name}', htmlspecialchars($userName), $welcomeConfig['message_text']); $keyboard = [ 'inline_keyboard' => [ [ ['text' => $welcomeConfig['button_text'], 'url' => $welcomeConfig['group_invite_link']] ] ] ]; $telegram->sendMessage($chatId, $messageText, ['reply_markup' => json_encode($keyboard)]); custom_log("[NUEVO USUARIO] Mensaje de bienvenida enviado a $userId."); } } else { custom_log("[NUEVO USUARIO] El registro de usuarios está desactivado en la configuración. No se realiza ninguna acción."); } // Salir después de gestionar al nuevo usuario. La interacción ha terminado. exit('ok'); } custom_log("[MODO AGENTE] Modo actual del usuario $userId: $userChatMode"); if (isset($message['text']) && trim($message['text']) === '/agente') { $stmt = $pdo->prepare("UPDATE recipients SET chat_mode = 'agent' WHERE platform_id = ? AND platform = 'telegram'"); $stmt->execute([$userId]); $userChatMode = 'agent'; custom_log("[MODO AGENTE] Usuario $userId usó /agente. Reseteando a modo 'agent'."); } switch ($userChatMode) { case 'agent': $keyboard = [ 'inline_keyboard' => [ [ ['text' => '🤖 Platicar con bot', 'callback_data' => 'platicar_bot'], ['text' => '🧠 Usar IA', 'callback_data' => 'usar_ia'] ] ] ]; $telegram->sendMessage($chatId, "👋 Hola! ¿Cómo quieres interactuar?", ['reply_markup' => json_encode($keyboard)]); exit('ok'); case 'ia': custom_log("[DEBUG] Entrando en el caso 'ia'."); $n8nWebhookUrl = $_ENV['N8N_IA_WEBHOOK_URL'] ?? null; custom_log("[DEBUG] URL de n8n leída desde .env: " . ($n8nWebhookUrl ? $n8nWebhookUrl : 'NO ENCONTRADA')); if ($n8nWebhookUrl && isset($message['text'])) { $postData = [ 'chat_id' => $chatId, 'user_id' => $userId, 'message' => $message['text'], 'name' => trim(($message['from']['first_name'] ?? '') . ' ' . ($message['from']['last_name'] ?? '')) ]; custom_log("[DEBUG] Preparando para enviar a n8n. Datos: " . json_encode($postData)); $ch = curl_init($n8nWebhookUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData)); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $response = curl_exec($ch); $curlError = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($curlError) { custom_log("[ERROR] Error en cURL al llamar a n8n: " . $curlError); } else { custom_log("[DEBUG] Llamada a n8n completada. Código HTTP: $httpCode. Respuesta: " . $response); } custom_log("[MODO AGENTE] Mensaje de $userId reenviado a n8n (modo IA)."); } else { if (!$n8nWebhookUrl) { custom_log("[ERROR] La variable N8N_IA_WEBHOOK_URL no está configurada en el .env."); } if (!isset($message['text'])) { custom_log("[DEBUG] No se reenvió a n8n porque no era un mensaje de texto."); } } exit('ok'); case 'bot': break; } } $isPrivateChatInBotMode = ($isPrivateChat && isset($userChatMode) && $userChatMode === 'bot'); if (!$isPrivateChat || $isPrivateChatInBotMode) { custom_log("Ejecutando lógica original. Razón: " . (!$isPrivateChat ? "No es chat privado" : "Chat privado en modo BOT")); if (isset($message['new_chat_members'])) { foreach ($message['new_chat_members'] as $newMember) { if (isset($newMember['is_bot']) && $newMember['is_bot']) continue; $userId = $newMember['id']; $userName = trim(($newMember['first_name'] ?? '') . ' ' . ($newMember['last_name'] ?? '')); $languageCode = $newMember['language_code'] ?? 'es'; $stmt = $pdo->prepare("INSERT INTO recipients (platform_id, name, type, platform, language_code) VALUES (?, ?, 'user', 'telegram', ?) ON DUPLICATE KEY UPDATE name = VALUES(name), language_code = VALUES(language_code)"); $stmt->execute([$userId, $userName, $languageCode]); custom_log("Nuevo miembro $userName ($userId) registrado/actualizado."); } exit(); } if ($callbackQuery) { $callbackId = $callbackQuery['id']; $data = $callbackQuery['data']; $message = $callbackQuery['message']; $chatId = $message['chat']['id']; $messageId = $message['message_id']; custom_log("[CALLBACK] Recibido callback original: " . $data); if (strpos($data, 'translate_manual:') === 0) { $targetLang = substr($data, strlen('translate_manual:')); $originalMessage = $callbackQuery['message']; $originalText = $originalMessage['text'] ?? $originalMessage['caption'] ?? ''; if (!empty($originalText)) { try { $sourceLang = $translator->detectLanguage($originalText); if ($sourceLang && $sourceLang !== $targetLang) { $translatedText = $translator->translateHtml($originalText, $sourceLang, $targetLang); $telegram->answerCallbackQuery($callbackId, ['text' => $translatedText, 'show_alert' => true]); } else { $telegram->answerCallbackQuery($callbackId, ['text' => 'El mensaje ya está en este idioma o no se pudo detectar el idioma original.', 'show_alert' => false]); } } catch (Exception $e) { custom_log("[CALLBACK] Excepción en traducción manual: " . $e->getMessage()); $telegram->answerCallbackQuery($callbackId, ['text' => '⚠️ Error interno al traducir.']); } } else { $telegram->answerCallbackQuery($callbackId, ['text' => 'No se encontró texto para traducir.']); } exit('ok'); } if (strpos($data, 'translate:') === 0) { $parts = explode(':', $data); if (count($parts) === 3) { $originalMessageId = $parts[1]; $targetLang = $parts[2]; try { $stmt = $pdo->prepare("SELECT message_text FROM telegram_interactions WHERE telegram_message_id = ? AND chat_id = ? ORDER BY id DESC LIMIT 1"); $stmt->execute([$originalMessageId, $chatId]); $originalContent = $stmt->fetchColumn(); if ($originalContent) { // 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')); $sourceLang = $translator->detectLanguage($plain) ?? 'es'; if ($sourceLang === $targetLang) { $fallbackSrc = 'es'; if ($fallbackSrc !== $targetLang) { $sourceLang = $fallbackSrc; } } $translatedText = $translator->translateHtml($originalContent, $sourceLang, $targetLang); if ($translatedText) { $telegram->sendMessage($chatId, $translatedText, ['parse_mode' => 'HTML'], false, $targetLang); // Conservar los botones para permitir traducir a otros idiomas $telegram->answerCallbackQuery($callbackId, ['text' => '✅ Traducción enviada']); } else { $telegram->answerCallbackQuery($callbackId, ['text' => '⚠️ Error al traducir.']); } } else { $telegram->answerCallbackQuery($callbackId, ['text' => '⚠️ No se encontró el texto original.']); } } catch (Exception $e) { custom_log("[CALLBACK] Excepción al traducir: " . $e->getMessage()); $telegram->answerCallbackQuery($callbackId, ['text' => '⚠️ Error interno.']); } } $telegram->answerCallbackQuery($callbackId); exit(); } } if (isset($message['text'])) { $text = trim($message['text']); $from = $message['from'] ?? null; $userId = $from['id'] ?? null; $messageId = $message['message_id'] ?? null; if (!$userId) { custom_log("No se pudo determinar el ID de usuario para la lógica original."); exit(); } $isCommand = (strpos($text, '/') === 0) || (strpos($text, '#') === 0); if ($isCommand) { handleCommand($pdo, $telegram, $commandLocker, $translator, $text, $from, $chatId, $messageId); } else { handleRegularMessage($pdo, $telegram, $commandLocker, $translator, $text, $from, $chatId, $messageId); } } } http_response_code(200); exit('ok'); } catch (Exception $e) { custom_log("Error al procesar la actualización de Telegram: " . $e->getMessage()); http_response_code(500); exit('Error interno del servidor'); } function handleCommand($pdo, $telegram, $commandLocker, $translator, $text, $from, $chatId, $messageId) { $userId = $from['id']; $detectedLang = $translator->detectLanguage($text) ?? 'es'; if (strpos($text, '/setlang') === 0 || strpos($text, '/setlanguage') === 0) { $parts = explode(' ', $text, 2); if (count($parts) < 2 || strlen(trim($parts[1])) !== 2) { $telegram->sendMessage($chatId, "❌ Formato incorrecto. Usa: /setlang es", [], true); return; } $newLangCode = strtolower(trim($parts[1])); $userName = trim(($from['first_name'] ?? '') . ' ' . ($from['last_name'] ?? '')); $stmt = $pdo->prepare("SELECT id FROM recipients WHERE platform_id = ? AND platform = 'telegram'"); $stmt->execute([$userId]); $userExists = $stmt->fetch(); if ($userExists) { $stmt = $pdo->prepare("UPDATE recipients SET language_code = ?, name = ? WHERE platform_id = ? AND platform = 'telegram'"); $stmt->execute([$newLangCode, $userName, $userId]); } else { $stmt = $pdo->prepare("INSERT INTO recipients (platform_id, name, type, platform, language_code) VALUES (?, ?, 'user', 'telegram', ?)"); $stmt->execute([$userId, $userName, $newLangCode]); } $telegram->sendMessage($chatId, "✅ Tu idioma ha sido establecido a '" . strtoupper($newLangCode) . "'.", [], true, $detectedLang); } elseif (strpos($text, '/bienvenida') === 0) { custom_log("[COMANDO] Usuario $userId solicitó el mensaje de bienvenida."); $configStmt = $pdo->query("SELECT * FROM telegram_bot_messages WHERE id = 1"); $welcomeConfig = $configStmt->fetch(PDO::FETCH_ASSOC); if ($welcomeConfig && $welcomeConfig['is_active']) { $userName = trim(($from['first_name'] ?? '') . ' ' . ($from['last_name'] ?? '')); $messageText = str_replace('{user_name}', htmlspecialchars($userName), $welcomeConfig['message_text']); $keyboard = [ 'inline_keyboard' => [ [ ['text' => $welcomeConfig['button_text'], 'url' => $welcomeConfig['group_invite_link']] ] ] ]; $telegram->sendMessage($chatId, $messageText, ['reply_markup' => json_encode($keyboard)]); custom_log("[COMANDO] Mensaje de bienvenida enviado a $userId por solicitud."); } else { custom_log("[COMANDO] /bienvenida solicitado, pero el mensaje está inactivo."); $telegram->sendMessage($chatId, "ℹ️ La función de bienvenida no está activa en este momento.", [], true, $detectedLang); } } elseif (strpos($text, '/comandos') === 0) { $stmt = $pdo->query("SELECT telegram_command, name FROM recurrent_messages WHERE telegram_command IS NOT NULL AND telegram_command != '' ORDER BY name ASC"); $commands = $stmt->fetchAll(PDO::FETCH_ASSOC); $response = ""; if (empty($commands)) { $response = "ℹ️ No hay comandos personalizados disponibles."; } else { $response = "📋 LISTA DE COMANDOS DISPONIBLES\n\n"; foreach ($commands as $index => $cmd) { $command = htmlspecialchars(trim($cmd['telegram_command'])); if (strpos($command, '#') !== 0) $command = '#' . $command; $name = htmlspecialchars(trim($cmd['name'])); $response .= ($index + 1) . ". " . $command . " - " . $name . "\n"; } $response .= "\nℹ️ Escribe el comando para usarlo."; } $stmt = $pdo->prepare("SELECT language_code FROM recipients WHERE platform_id = ? AND platform = 'telegram'"); $stmt->execute([$userId]); $userLang = $stmt->fetchColumn(); if (!$userLang || $userLang === 'es') { $converter = new HtmlToTelegramHtmlConverter(); $convertedContent = $converter->convert($response); $telegram->sendMessage($chatId, $convertedContent, ['parse_mode' => 'HTML'], true, 'es'); } else { $plainText = strip_tags(str_replace(['
', '

'], "\n", $response)); $plainText = html_entity_decode($plainText, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $telegram->sendMessage($chatId, $plainText, ['parse_mode' => 'HTML'], true, 'es'); } } elseif (strpos($text, '#') === 0) { $command = ltrim($text, '#'); if ($commandLocker->isCommandProcessing($command, $chatId, 'command')) { $telegram->sendMessage($chatId, "El comando `#{$command}` ya está siendo procesado. Por favor, espera un momento.", [], true, 'es'); return; } $lockId = $commandLocker->acquireLock($command, $chatId, 'command', ['user_id' => $userId, 'original_message' => $text]); if ($lockId === false) { $telegram->sendMessage($chatId, "No se pudo procesar el comando. Inténtalo de nuevo.", [], true, 'es'); return; } try { $stmt = $pdo->prepare("SELECT message_content FROM recurrent_messages WHERE telegram_command = ?"); $stmt->execute([$command]); $template = $stmt->fetch(PDO::FETCH_ASSOC); if ($template) { $content = $template['message_content']; $converter = new HtmlToTelegramHtmlConverter(); $content = $converter->convert($content); $detectedLang = $translator->detectLanguage(strip_tags($content)) ?? 'es'; if ($detectedLang !== 'es') { $translatedContent = $translator->translateHtml($content, $detectedLang, 'es'); if ($translatedContent) $content = $translatedContent; } usleep(500000); $result = $telegram->sendMessage($chatId, $content, ['parse_mode' => 'HTML'], true, 'es'); // TelegramSender::sendMessage devuelve un array de partes enviadas con message_id $sentMessageId = null; if (is_array($result)) { // Tomar el último message_id exitoso for ($i = count($result) - 1; $i >= 0; $i--) { if (isset($result[$i]['success']) && $result[$i]['success'] && isset($result[$i]['message_id'])) { $sentMessageId = $result[$i]['message_id']; break; } } } elseif (isset($result['result']['message_id'])) { $sentMessageId = $result['result']['message_id']; } if (!empty($sentMessageId)) { $commandLocker->releaseLock($lockId, $sentMessageId); // Añadir botones de traducción dinámicos $langStmt = $pdo->query("SELECT language_code, language_name, flag_emoji FROM supported_languages WHERE is_active = 1"); $activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC); if (count($activeLangs) > 1) { $keyboard = []; foreach ($activeLangs as $lang) { $keyboard[] = ['text' => ($lang['flag_emoji'] ?? '') . ' ' . $lang['language_name'], 'callback_data' => 'translate:' . $sentMessageId . ':' . $lang['language_code']]; } // Agrupar botones en filas de a 2 $inline_keyboard = array_chunk($keyboard, 2); $replyMarkup = json_encode(['inline_keyboard' => $inline_keyboard]); $telegram->editMessageReplyMarkup($chatId, $sentMessageId, $replyMarkup); } } else { $commandLocker->failLock($lockId, 'Error al enviar el mensaje'); $telegram->sendMessage($chatId, "Ocurrió un error al procesar el comando.", [], true, 'es'); } } else { $commandLocker->releaseLock($lockId); $telegram->sendMessage($chatId, "El comando `#{$command}` no fue encontrado.", [], true, $detectedLang); } } catch (Exception $e) { if (isset($lockId)) $commandLocker->failLock($lockId, 'Excepción: ' . $e->getMessage()); custom_log("Error al procesar comando #{$command}: " . $e->getMessage()); $telegram->sendMessage($chatId, "Ocurrió un error inesperado.", [], true, 'es'); } } } function handleRegularMessage($pdo, $telegram, $commandLocker, $translator, $text, $from, $chatId, $messageId) { $userId = $from['id']; try { // 1. Detectar el idioma del mensaje entrante $detectedLang = $translator->detectLanguage(strip_tags($text)) ?? 'es'; // 2. Guardar la interacción original en la base de datos try { $stmt = $pdo->prepare("INSERT INTO telegram_interactions (chat_id, message_text, direction, language_code) VALUES (?, ?, 'in', ?)"); $stmt->execute([$chatId, $text, $detectedLang]); } catch (PDOException $e) { custom_log("[ERROR] No se pudo guardar el mensaje entrante en telegram_interactions: " . $e->getMessage()); // No detenemos la ejecución, la traducción es más importante. } // 3. Obtener idiomas activos y encolar traducciones $langStmt = $pdo->query("SELECT language_code FROM supported_languages WHERE is_active = 1"); $activeLangs = $langStmt->fetchAll(PDO::FETCH_COLUMN); if (in_array($detectedLang, $activeLangs)) { foreach ($activeLangs as $targetLang) { if ($detectedLang === $targetLang) { continue; // No traducir al mismo idioma } $sql = "INSERT INTO translation_queue (message_id, chat_id, user_id, text_to_translate, source_lang, target_lang, platform) VALUES (?, ?, ?, ?, ?, ?, 'telegram')"; $stmt = $pdo->prepare($sql); $stmt->execute([ $messageId, $chatId, $userId, $text, $detectedLang, $targetLang ]); custom_log("[QUEUE] Mensaje #$messageId encolado para traducción de '$detectedLang' a '$targetLang'."); } } } catch (Exception $e) { custom_log("[ERROR] Error en handleRegularMessage al encolar traducción: " . $e->getMessage()); } }