false, 'error' => 'No autenticado'], 401); } // Verificar permiso if (!hasPermission('send_messages', 'telegram')) { jsonResponse(['success' => false, 'error' => 'No tienes permiso para enviar mensajes de Telegram.'], 403); } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { jsonResponse(['success' => false, 'error' => 'Método no permitido'], 405); } $input = json_decode(file_get_contents('php://input'), true); $destinatariosInput = $input['destinatario_id'] ?? []; // Puede ser un string o un array $contenido = $input['contenido'] ?? ''; $tipoEnvio = $input['tipo_envio'] ?? 'inmediato'; // 'inmediato', 'programado', 'recurrente' // Asegurarse de que $destinatariosInput sea siempre un array if (!is_array($destinatariosInput)) { $destinatariosInput = [$destinatariosInput]; } $destinatarios = array_filter(array_map('trim', $destinatariosInput)); // Limpiar y filtrar vacíos if (empty($destinatarios) || empty($contenido)) { jsonResponse(['success' => false, 'error' => 'Faltan datos requeridos: destinatario(s) y contenido'], 400); } try { $db = getDB(); $botToken = $_ENV['TELEGRAM_BOT_TOKEN'] ?? getenv('TELEGRAM_BOT_TOKEN'); if (!$botToken) { throw new Exception("Token de bot de Telegram no configurado."); } // Procesar contenido HTML para extraer imágenes y texto $dom = new DOMDocument(); libxml_use_internal_errors(true); $dom->loadHTML('' . $contenido, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); $imageUrl = null; $images = $dom->getElementsByTagName('img'); if ($images->length > 0) { // Telegram Bot API generalmente solo permite una imagen por mensaje (en sendPhoto) // Tomamos la primera imagen encontrada $imageUrl = $images->item(0)->getAttribute('src'); } // Limpiar texto para Telegram (puede usar Markdown, pero primero quitamos HTML tags) $cleanContent = strip_tags($contenido); // Quitar tags HTML $cleanContent = html_entity_decode($cleanContent, ENT_QUOTES, 'UTF-8'); // Decodificar entidades HTML $cleanContent = trim($cleanContent); $allResults = []; foreach ($destinatarios as $destinatarioId) { $messageStatus = 'pendiente'; // Estado por defecto para programados/recurrentes si no es inmediato $telegramMessageId = null; $errorMessage = null; if ($tipoEnvio === 'inmediato') { $telegramApiUrl = "https://api.telegram.org/bot{$botToken}/"; $postFields = [ 'chat_id' => $destinatarioId, ]; if ($imageUrl) { // Si hay imagen, usar sendPhoto $postFields['photo'] = $imageUrl; // El caption del sendPhoto soporta HTML/Markdown, por simplicidad usamos el texto limpio $postFields['caption'] = $cleanContent; $method = 'sendPhoto'; } else { // Si no hay imagen, usar sendMessage $postFields['text'] = $cleanContent; $method = 'sendMessage'; } $ch = curl_init($telegramApiUrl . $method); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postFields)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); $responseJson = json_decode($response, true); if ($curlError) { $errorMessage = "Error cURL: " . $curlError; } elseif ($httpCode !== 200 || !($responseJson['ok'] ?? false)) { $errorMessage = $responseJson['description'] ?? 'Error desconocido de Telegram API'; } else { $messageStatus = 'enviado'; $telegramMessageId = $responseJson['result']['message_id'] ?? null; } } // 3. Guardar en base de datos (Historial) para CADA destinatario $stmt = $db->prepare(" INSERT INTO mensajes_telegram (usuario_id, chat_id, contenido, estado, mensaje_telegram_id, fecha_envio, tipo_envio) VALUES (?, ?, ?, ?, ?, ?, ?) "); $scheduledSendTime = null; if ($tipoEnvio === 'programado' && isset($input['fecha_envio'])) { // Convertir la fecha local a UTC para guardar en DB $datetime = new DateTime($input['fecha_envio'], new DateTimeZone($_ENV['TIME_ZONE_ENVIOS'] ?? 'UTC')); $datetime->setTimezone(new DateTimeZone('UTC')); $scheduledSendTime = $datetime->format('Y-m-d H:i:s'); } elseif ($tipoEnvio === 'inmediato') { $scheduledSendTime = date('Y-m-d H:i:s'); } $finalMessageStatus = ($tipoEnvio === 'inmediato' && $messageStatus === 'fallido') ? 'fallido' : $messageStatus; $stmt->execute([ $userData->userId, $destinatarioId, $contenido, // Guardamos el original con HTML/formato para referencia $finalMessageStatus, $telegramMessageId, $scheduledSendTime, $tipoEnvio ]); $messageDbId = $db->lastInsertId(); // Si es recurrente, guardar también en la tabla recurrentes_telegram if ($tipoEnvio === 'recurrente') { // Validar y obtener hora de envío $horaEnvio = $input['hora_envio'] ?? '09:00:00'; if (!preg_match('/^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/', $horaEnvio) && !preg_match('/^([01]\d|2[0-3]):([0-5]\d)$/', $horaEnvio)) { $horaEnvio = '09:00:00'; // Valor por defecto si es inválido } if (strlen($horaEnvio) === 5) { // Si viene sin segundos, añadir $horaEnvio .= ':00'; } $nextSendTime = null; // Esto debería ser calculado por un demonio de scheduling $stmtRecur = $db->prepare(" INSERT INTO recurrentes_telegram (mensaje_id, frecuencia, hora_envio, dia_semana, dia_mes, activo, proximo_envio) VALUES (?, ?, ?, ?, ?, 1, ?) "); $stmtRecur->execute([ $messageDbId, $input['frecuencia'] ?? 'diario', $horaEnvio, $input['dia_semana'] ?? null, $input['dia_mes'] ?? null, $nextSendTime // Será calculado por el demonio ]); } $allResults[] = [ 'destinatario_id' => $destinatarioId, 'message_db_id' => $messageDbId, 'status' => $finalMessageStatus, 'error' => $errorMessage, 'telegram_message_id' => $telegramMessageId, 'tipo_envio' => $tipoEnvio ]; if ($finalMessageStatus === 'enviado') { logToFile('telegram/messages.log', "Mensaje enviado a {$destinatarioId} por {$userData->username}"); } elseif ($finalMessageStatus === 'pendiente') { logToFile('telegram/messages.log', "Mensaje {$tipoEnvio} guardado para {$destinatarioId} por {$userData->username}"); } else { logToFile('telegram/errors.log', "Error enviando mensaje a {$destinatarioId}: {$errorMessage}", 'ERROR'); } } // Determinar el éxito general de la operación $overallSuccess = array_reduce($allResults, function($carry, $item) { // Considerar éxito si al menos un mensaje fue enviado o está pendiente return $carry || ($item['status'] === 'enviado' || $item['status'] === 'pendiente'); }, false); jsonResponse([ 'success' => $overallSuccess, 'message' => 'Procesamiento de mensajes completado.', 'details' => $allResults, 'overall_status' => $overallSuccess ? 'partial_success_or_pending' : 'all_failed' ]); } catch (Exception $e) { logToFile('telegram/errors.log', "Error general en el envío de mensajes: " . $e->getMessage(), 'ERROR'); jsonResponse(['success' => false, 'error' => $e->getMessage()], 500); }