false, 'error' => 'Método no permitido']); exit; } // Función auxiliar para enviar con media_id function sendMediaByIdToWhatsApp($whatsapp, $recipient, $mediaId, $mediaType, $caption, $filename, $isVoice = false) { // Pasar skipAutoSave=true para evitar guardado duplicado (lo guardamos manualmente después) // Para audio: pasar is_voice para determinar si es nota de voz o audio normal return $whatsapp->sendMediaById($recipient, $mediaId, $mediaType, $caption, $filename, true, $isVoice); } // Función auxiliar para enviar con link function sendMediaByLinkToWhatsApp($whatsapp, $recipient, $mediaUrl, $mediaType, $caption, $filename) { // Pasar skipAutoSave=true para evitar guardado duplicado (lo guardamos manualmente después) switch ($mediaType) { case 'image': return $whatsapp->sendImageMessage($recipient, $mediaUrl, $caption, true); case 'video': return $whatsapp->sendVideoMessage($recipient, $mediaUrl, $caption, true); case 'audio': return $whatsapp->sendAudioMessage($recipient, $mediaUrl, true); case 'document': return $whatsapp->sendDocumentMessage($recipient, $mediaUrl, $filename, $caption, true); default: throw new Exception('Tipo de media no soportado: ' . $mediaType); } } try { $rawInput = file_get_contents('php://input'); error_log("send_media_message.php - Raw input: " . $rawInput); smm_log("Raw input: " . $rawInput); $input = json_decode($rawInput, true); if (!$input) { error_log("send_media_message.php - JSON decode failed"); smm_log("JSON decode failed. Raw: " . $rawInput); throw new Exception('Datos no válidos - JSON inválido'); } error_log("send_media_message.php - Input decoded: " . json_encode($input)); smm_log("Input decoded: " . json_encode($input)); // Debug flag: permitir depuración remota si se pasa debug=true en query o en body $debugMode = (isset($_GET['debug']) && $_GET['debug'] === 'true') || (!empty($input['debug'])); $debugInfo = []; // Helper: hacer HEAD request para verificar accesibilidad de URL de media function headCheckUrl($url) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_NOBODY => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 10 ]); $res = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); $contentLength = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD); $err = curl_error($ch); if (version_compare(PHP_VERSION, '8.5.0', '<')) { curl_close($ch); } return [ 'http_code' => $httpCode, 'content_type' => $contentType, 'content_length' => $contentLength, 'error' => $err ]; } // Validar campos requeridos if (empty($input['recipient']) || empty($input['media_url']) || empty($input['media_type'])) { $missing = []; if (empty($input['recipient'])) $missing[] = 'recipient'; if (empty($input['media_url'])) $missing[] = 'media_url'; if (empty($input['media_type'])) $missing[] = 'media_type'; error_log("send_media_message.php - Missing fields: " . implode(', ', $missing)); throw new Exception('Faltan campos requeridos: ' . implode(', ', $missing)); } $recipient = $input['recipient']; $mediaUrl = $input['media_url']; $mediaType = $input['media_type']; // image, video, audio, document $caption = $input['caption'] ?? null; $filename = $input['filename'] ?? null; // Para audio: is_voice = true significa nota de voz (grabada desde micrófono) // is_voice = false o ausente significa audio normal (archivo subido) $isVoice = isset($input['is_voice']) ? (bool)$input['is_voice'] : false; error_log("send_media_message.php - is_voice: " . ($isVoice ? 'true' : 'false')); smm_log("is_voice: " . ($isVoice ? 'true' : 'false')); // Inicializar servicio de WhatsApp $whatsapp = new WhatsAppService(); // IMPORTANTE: Si la URL es local (mismo host) o apunta a nuestro /uploads, necesitamos subir el archivo a WhatsApp primero $isLocalUrl = false; try { $parsed = parse_url($mediaUrl); $host = $parsed['host'] ?? null; $path = $parsed['path'] ?? null; $serverHost = $_SERVER['HTTP_HOST'] ?? null; // Consider as local if domain matches server host or if path is inside /uploads if ($host && $serverHost && (strpos($host, $serverHost) !== false || strpos($serverHost, $host) !== false)) { $isLocalUrl = true; } if ($path && strpos($path, '/uploads/') === 0) { $isLocalUrl = true; } // also support localhost variants if (strpos($mediaUrl, 'localhost') !== false || strpos($mediaUrl, '127.0.0.1') !== false) { $isLocalUrl = true; } } catch (Exception $e) { // fall back to simple check if (strpos($mediaUrl, 'localhost') !== false || strpos($mediaUrl, '127.0.0.1') !== false) { $isLocalUrl = true; } } if ($isLocalUrl) { error_log("send_media_message.php - URL local detectada, subiendo archivo a WhatsApp primero"); set_time_limit(300); // Tiempo extra para archivos grandes // Obtener la ruta local del archivo $parsedUrl = parse_url($mediaUrl); $relativePath = $parsedUrl['path']; // Construir ruta absoluta usando el path completo (no solo basename) $docRoot = __DIR__ . '/..'; $localFilePath = $docRoot . $relativePath; // Si no existe, intentar con solo el basename en uploads/ if (!file_exists($localFilePath)) { $uploadDir = __DIR__ . '/../uploads/'; $localFilePath = $uploadDir . basename($relativePath); } // Último intento: buscar recursivamente en uploads/ if (!file_exists($localFilePath)) { $searchName = basename($relativePath); $found = glob(__DIR__ . '/../uploads/**/' . $searchName); if (!empty($found)) { $localFilePath = $found[0]; } } error_log("send_media_message.php - Ruta local del archivo: " . $localFilePath . " - existe: " . (file_exists($localFilePath) ? 'SI' : 'NO') . " - size: " . (file_exists($localFilePath) ? filesize($localFilePath) : 'N/A')); smm_log("Local file: {$localFilePath} exists=" . (file_exists($localFilePath) ? 'yes' : 'no') . " size=" . (file_exists($localFilePath) ? filesize($localFilePath) : 'N/A')); if (!file_exists($localFilePath)) { throw new Exception('Archivo no encontrado en el servidor: ' . $localFilePath); } // Detectar MIME type $mimeType = mime_content_type($localFilePath); error_log("send_media_message.php - MIME type detectado: " . $mimeType); // Subir archivo a WhatsApp try { $uploadResult = $whatsapp->uploadMedia($localFilePath, $mimeType); error_log("send_media_message.php - Archivo subido a WhatsApp: " . json_encode($uploadResult)); smm_log("Upload result: " . json_encode($uploadResult)); if (isset($uploadResult['id'])) { // Usar el media_id de WhatsApp en lugar de URL $mediaId = $uploadResult['id']; // Save uploaded media id for DB and debugging $uploaded_media_id = $mediaId; error_log("send_media_message.php - Media ID obtenido: " . $mediaId); smm_log("Media ID obtained: " . $mediaId); // Enviar mensaje usando media_id (pasar isVoice para audio) $response = sendMediaByIdToWhatsApp($whatsapp, $recipient, $mediaId, $mediaType, $caption, $filename, $isVoice); smm_log("Send media by id response: " . json_encode($response)); } else { smm_log("Upload did not return id: " . json_encode($uploadResult)); throw new Exception('Error al subir archivo a WhatsApp: ' . json_encode($uploadResult)); } } catch (Exception $e) { error_log("send_media_message.php - Error subiendo a WhatsApp: " . $e->getMessage()); throw new Exception('Error al subir archivo a WhatsApp: ' . $e->getMessage()); } } else { // URL pública, usar método normal con link error_log("send_media_message.php - URL pública detectada, usando link directo: " . $mediaUrl); smm_log("Public URL: " . $mediaUrl); // Chequear accesibilidad de la URL antes de pedir a WhatsApp que la descargue try { $head = headCheckUrl($mediaUrl); smm_log("HEAD check: " . json_encode($head)); if ($debugMode) $debugInfo['media_head'] = $head; if (isset($head['http_code']) && intval($head['http_code']) >= 400) { smm_log("send_media_message.php - Warning: media URL returned HTTP " . $head['http_code']); } } catch (Exception $e) { smm_log("send_media_message.php - HEAD check failed: " . $e->getMessage()); if ($debugMode) $debugInfo['media_head_error'] = $e->getMessage(); } // For audio links, prefer to download and upload to WhatsApp to avoid link expiry or CDN issues if ($mediaType === 'audio') { try { smm_log('Attempting to download remote audio for upload to WhatsApp: ' . $mediaUrl); $tmpName = uniqid('dl_', true) . '.' . (pathinfo($mediaUrl, PATHINFO_EXTENSION) ?: 'tmp'); $tmpPath = __DIR__ . '/../uploads/' . $tmpName; $ch = curl_init($mediaUrl); $fp = fopen($tmpPath, 'w'); curl_setopt_array($ch, [ CURLOPT_FILE => $fp, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 60, CURLOPT_FAILONERROR => true ]); $ok = curl_exec($ch); $err = curl_error($ch); curl_close($ch); fclose($fp); if (!$ok) { smm_log('Download failed for audio link: ' . $err); // fallback to link send $response = sendMediaByLinkToWhatsApp($whatsapp, $recipient, $mediaUrl, $mediaType, $caption, $filename); } else { smm_log('Downloaded remote audio to: ' . $tmpPath); $mimeType = mime_content_type($tmpPath) ?: 'audio/ogg'; $uploadResult = $whatsapp->uploadMedia($tmpPath, $mimeType); if (isset($uploadResult['id'])) { $uploaded_media_id = $uploadResult['id']; smm_log('Uploaded downloaded audio to WhatsApp, media_id: ' . $uploaded_media_id); $response = sendMediaByIdToWhatsApp($whatsapp, $recipient, $uploaded_media_id, $mediaType, $caption, $filename, $isVoice); } else { smm_log('Upload of downloaded audio did not return id, fallback to link.'); $response = sendMediaByLinkToWhatsApp($whatsapp, $recipient, $mediaUrl, $mediaType, $caption, $filename); } // cleanup temp file try { @unlink($tmpPath); } catch (Exception $e) { /* ignore */ } } } catch (Exception $e) { smm_log('Exception during audio download/upload: ' . $e->getMessage()); // fallback to link $response = sendMediaByLinkToWhatsApp($whatsapp, $recipient, $mediaUrl, $mediaType, $caption, $filename); } } else { $response = sendMediaByLinkToWhatsApp($whatsapp, $recipient, $mediaUrl, $mediaType, $caption, $filename); } } // Guardar respuesta en base de datos // Aceptar tanto respuestas con 'conversations' como con 'messages' $sentMessageId = null; if ($response) { if (isset($response['conversations'][0]['id'])) { $sentMessageId = $response['conversations'][0]['id']; } elseif (isset($response['messages'][0]['id'])) { $sentMessageId = $response['messages'][0]['id']; } } // Si no hay id, pero la respuesta es un string (posible JSON crudo), intentar decodificar if (!$sentMessageId && is_string($response)) { $maybe = json_decode($response, true); if (is_array($maybe)) { error_log("send_media_message.php - Decoded raw response string to array for inspection"); // Reasignar response para usar la estructura decodificada $response = $maybe; if (isset($response['conversations'][0]['id'])) { $sentMessageId = $response['conversations'][0]['id']; } elseif (isset($response['messages'][0]['id'])) { $sentMessageId = $response['messages'][0]['id']; } } } if ($response && $sentMessageId) { error_log("send_media_message.php - Respuesta de WhatsApp exitosa: " . json_encode($response)); smm_log("WhatsApp response (successful): " . json_encode($response)); if ($debugMode) $debugInfo['whatsapp_response'] = $response; // Guardar en base de datos $db = Database::getInstance(); // Obtener usuario $user = $db->fetch( "SELECT id FROM users WHERE phone_number = ?", [$recipient] ); if ($user) { // Determinar content: prioridad filename > caption > descripción por defecto $content = ''; if (!empty($filename)) { $content = $filename; } elseif (!empty($caption)) { $content = $caption; } else { // Descripción por defecto según tipo $defaultLabels = [ 'image' => '[Imagen]', 'video' => '[Video]', 'audio' => '[Audio]', 'document' => '[Documento]' ]; $content = $defaultLabels[$mediaType] ?? '[Media]'; } // Datos base para insertar // content: nombre de archivo o caption (nunca JSON) // media_url: ID de WhatsApp si está disponible, sino la URL original // local_file: ruta relativa del archivo local si existe // local_thumb: miniatura (por ahora usar mismo archivo para imágenes/videos) // Intentar determinar ruta local desde mediaUrl si no se definió antes if (!isset($localFilePath) && $mediaUrl) { try { error_log("send_media_message.php - Intentando extraer local_file desde mediaUrl: " . $mediaUrl); $parsedUrl = parse_url($mediaUrl); $path = $parsedUrl['path'] ?? ''; error_log("send_media_message.php - Path extraído: " . $path); // Si la URL apunta a /uploads/, construir ruta local if (strpos($path, '/uploads/') === 0) { $uploadDir = __DIR__ . '/../uploads/'; $filename_from_url = basename($path); $testPath = $uploadDir . $filename_from_url; error_log("send_media_message.php - Testeando archivo local: " . $testPath); if (file_exists($testPath)) { $localFilePath = $testPath; error_log("send_media_message.php - ✅ Archivo local encontrado: " . $localFilePath); } else { error_log("send_media_message.php - ❌ Archivo local NO existe: " . $testPath); } } } catch (Exception $e) { error_log("send_media_message.php - Error parseando mediaUrl: " . $e->getMessage()); } } else { error_log("send_media_message.php - localFilePath ya definido o mediaUrl vacío. localFilePath=" . (isset($localFilePath) ? $localFilePath : 'NULL') . ", mediaUrl=" . ($mediaUrl ?? 'NULL')); } $localFileRelative = (isset($localFilePath) && file_exists($localFilePath)) ? str_replace(__DIR__ . '/../', '', $localFilePath) : null; error_log("send_media_message.php - local_file relativo para BD: " . ($localFileRelative ?? 'NULL')); $conversationData = [ 'user_id' => $user['id'], 'message_id' => $sentMessageId, 'direction' => 'outgoing', 'message_type' => $mediaType, 'content' => $content, 'media_url' => isset($uploaded_media_id) ? $uploaded_media_id : $mediaUrl, 'whatsapp_media_id' => $uploaded_media_id ?? null, 'local_file' => $localFileRelative, 'local_thumb' => ($mediaType === 'image' || $mediaType === 'video') ? $localFileRelative : null, 'filename' => $filename ?? null, 'status' => 'sent', 'created_at' => date('Y-m-d H:i:s') ]; // Log detallado para debugging error_log("send_media_message.php - Guardando mensaje multimedia:"); error_log(" - content: " . $content); error_log(" - media_url: " . ($conversationData['media_url'] ?? 'null')); error_log(" - whatsapp_media_id: " . ($conversationData['whatsapp_media_id'] ?? 'null')); error_log(" - local_file: " . ($conversationData['local_file'] ?? 'null')); error_log(" - local_thumb: " . ($conversationData['local_thumb'] ?? 'null')); error_log(" - media_type: " . $mediaType); smm_log("Saving to DB - content: {$content}, media_url: " . ($conversationData['media_url'] ?? 'null') . ", local_file: " . ($conversationData['local_file'] ?? 'null') . ", local_thumb: " . ($conversationData['local_thumb'] ?? 'null')); try { $db->insert('conversations', $conversationData); } catch (Exception $dbError) { // Si falla, intentar sin filename (por compatibilidad con BD antigua) error_log("send_media_message.php - Advertencia al guardar: " . $dbError->getMessage()); } } $out = [ 'success' => true, 'message' => 'Mensaje multimedia enviado correctamente', 'media_type' => $mediaType, 'data' => $response ]; if ($debugMode) $out['debug'] = $debugInfo; echo json_encode($out); } else { error_log("send_media_message.php - Respuesta de WhatsApp sin message_id: " . json_encode($response)); // Añadir contexto adicional para debugging: guardar la respuesta cruda y tipo $raw = is_string($response) ? $response : json_encode($response); error_log("send_media_message.php - Raw response: " . substr($raw, 0, 2000)); smm_log("Raw response: " . substr($raw, 0, 2000)); throw new Exception('Error al enviar mensaje: ' . $raw); } } catch (Exception $e) { error_log("Error en send_media_message.php: " . $e->getMessage()); error_log("Stack trace: " . $e->getTraceAsString()); // Limpiar cualquier output previo antes de enviar JSON if (ob_get_length()) ob_clean(); http_response_code(500); $errOut = [ 'success' => false, 'error' => $e->getMessage(), 'file' => basename($e->getFile()), 'line' => $e->getLine() ]; if (isset($debugMode) && $debugMode) { $errOut['debug'] = $debugInfo; } echo json_encode($errOut, JSON_UNESCAPED_UNICODE); } ?>