From 3f6e723facca5ee81555fd2de932d43abc9cb873 Mon Sep 17 00:00:00 2001 From: lizandrogd <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:33:11 -0500 Subject: [PATCH] up --- api/webhook.php | 78 ++------------------ classes/MediaService.php | 31 ++++++++ migrations/20260121_media_queue_add_cols.sql | 4 + scripts/enqueue_missing_media.php | 22 ++++++ scripts/media_worker.php | 18 ++++- scripts/show_media_queue.php | 5 ++ 6 files changed, 84 insertions(+), 74 deletions(-) create mode 100644 migrations/20260121_media_queue_add_cols.sql create mode 100644 scripts/enqueue_missing_media.php create mode 100644 scripts/show_media_queue.php diff --git a/api/webhook.php b/api/webhook.php index 09184f8..c6bc5eb 100644 --- a/api/webhook.php +++ b/api/webhook.php @@ -165,54 +165,7 @@ class WhatsAppWebhook { $mimeType = $message['image']['mime_type'] ?? null; $filename = $message['image']['filename'] ?? null; - // Intentar descargar y almacenar imagen localmente - try { - $ms = new MediaService(); - $res = null; - if ($mediaUrl && preg_match('#^https?://#i', $mediaUrl)) { - // Si venía URL directa, descargar desde URL - $content = $ms->downloadUrl($mediaUrl); - if ($content !== false) { - $subdir = date('Y/m'); - $dir = 'uploads/media/' . $subdir; - if (!is_dir(__DIR__ . '/../' . $dir)) mkdir(__DIR__ . '/../' . $dir, 0755, true); - $ext = $ms->extensionFromMime($mimeType) ?: pathinfo(parse_url($mediaUrl, PHP_URL_PATH), PATHINFO_EXTENSION) ?: 'jpg'; - $fname = uniqid('m_') . '.' . $ext; - $path = __DIR__ . '/../' . $dir . '/' . $fname; - file_put_contents($path, $content); - $res = ['local_file' => $dir . '/' . $fname]; - // crear thumb - if ($ms->isImageMime($mimeType)) { - $thumbPath = __DIR__ . '/../' . $dir . '/thumb_' . $fname . '.jpg'; - if ($ms->createThumbnail($path, $thumbPath)) { - $res['local_thumb'] = $dir . '/thumb_' . $fname . '.jpg'; - } - } - } - } elseif ($mediaUrl) { - // mediaUrl probablemente es media id -> usar fetchAndStoreFromGraph - $res = $ms->fetchAndStoreFromGraph($mediaUrl, date('Y/m')); - } - if ($res) { - $localFile = $res['local_file'] ?? null; - $localThumb = $res['local_thumb'] ?? null; - } - } catch (Exception $e) { - error_log('Media download failed: ' . $e->getMessage()); - // Encolar para reintento si tenemos un media id (no descargar documentos aquí) - try { - if ($mediaUrl && !preg_match('#^https?://#i', $mediaUrl)) { - $this->db->insert('media_queue', [ - 'media_id' => $mediaUrl, - 'subdir' => date('Y/m'), - 'status' => 'pending', - 'created_at' => date('Y-m-d H:i:s') - ]); - } - } catch (Exception $e2) { - error_log('Failed to enqueue media download: ' . $e2->getMessage()); - } - } + } elseif (isset($message['audio'])) { $messageType = 'audio'; @@ -220,18 +173,7 @@ class WhatsAppWebhook { $mimeType = $message['audio']['mime_type'] ?? null; $filename = $message['audio']['filename'] ?? null; - // Descargar audio y guardar local (opcional) - try { - $ms = new MediaService(); - if ($mediaUrl && !preg_match('#^https?://#i', $mediaUrl)) { - $res = $ms->fetchAndStoreFromGraph($mediaUrl, date('Y/m')); - if ($res) { - $localFile = $res['local_file'] ?? null; - } - } - } catch (Exception $e) { - error_log('Audio download failed: ' . $e->getMessage()); - } + } elseif (isset($message['video'])) { $messageText = $message['video']['caption'] ?? ''; @@ -240,18 +182,7 @@ class WhatsAppWebhook { $mimeType = $message['video']['mime_type'] ?? null; $filename = $message['video']['filename'] ?? null; - // Descargar video y guardar local (opcional) - try { - $ms = new MediaService(); - if ($mediaUrl && !preg_match('#^https?://#i', $mediaUrl)) { - $res = $ms->fetchAndStoreFromGraph($mediaUrl, date('Y/m')); - if ($res) { - $localFile = $res['local_file'] ?? null; - } - } - } catch (Exception $e) { - error_log('Video download failed: ' . $e->getMessage()); - } + } elseif (isset($message['document'])) { $messageText = $message['document']['filename'] ?? ''; @@ -305,7 +236,8 @@ class WhatsAppWebhook { // Filtrar campos según columnas existentes para evitar errores en esquemas antiguos $saveData = $this->filterColumns('conversations', $saveData); - $this->saveMessage($saveData); + // Guardar mensaje y conservar el id de la conversación para posibles encolamientos + $conversationId = $this->saveMessage($saveData); // Crear notificación para UI (nuevo mensaje entrante) try { diff --git a/classes/MediaService.php b/classes/MediaService.php index 2be4d42..702f910 100644 --- a/classes/MediaService.php +++ b/classes/MediaService.php @@ -72,6 +72,37 @@ class MediaService { return $result; } + // Nuevo: descargar y almacenar desde una URL directa + public function fetchAndStoreFromUrl($url, $subdir = '') { + $content = $this->downloadUrl($url); + if ($content === false) { + $content = $this->downloadUrl($url, ['Referer: https://graph.facebook.com']); + } + if ($content === false) throw new Exception('Failed to download media content from url ' . $url); + + // intentar inferir mime + $mime = null; + $ext = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION) ?: 'bin'; + $dir = $this->uploadsDir . ($subdir ? '/' . trim($subdir, '/') : ''); + if (!is_dir($dir)) mkdir($dir, 0755, true); + $filename = uniqid('m_') . '.' . $ext; + $fullPath = $dir . '/' . $filename; + file_put_contents($fullPath, $content); + + $result = ['local_file' => 'uploads/media' . ($subdir ? '/' . trim($subdir, '/') : '') . '/' . $filename, 'mime_type' => $mime, 'size' => filesize($fullPath)]; + + // Si parece imagen, intentar crear thumb + if (preg_match('#jpe?g|png|gif#i', $ext)) { + $thumbPath = $dir . '/thumb_' . $filename . '.jpg'; + $thumbRel = dirname($result['local_file']) . '/thumb_' . $filename . '.jpg'; + if ($this->createThumbnail($fullPath, $thumbPath)) { + $result['local_thumb'] = $thumbRel; + } + } + + return $result; + } + public function downloadUrl($url, $extraHeaders = []) { // Incluir Authorization Bearer si está configurado (necesario para algunos endpoints) $authHeader = []; diff --git a/migrations/20260121_media_queue_add_cols.sql b/migrations/20260121_media_queue_add_cols.sql new file mode 100644 index 0000000..ca49983 --- /dev/null +++ b/migrations/20260121_media_queue_add_cols.sql @@ -0,0 +1,4 @@ +-- Add conversation reference and media_url to media_queue +ALTER TABLE media_queue + ADD COLUMN conversation_id INT NULL DEFAULT NULL, + ADD COLUMN media_url VARCHAR(1000) NULL DEFAULT NULL; diff --git a/scripts/enqueue_missing_media.php b/scripts/enqueue_missing_media.php new file mode 100644 index 0000000..e2603a0 --- /dev/null +++ b/scripts/enqueue_missing_media.php @@ -0,0 +1,22 @@ +fetchAll("SELECT id, message_type, media_url, filename, message_id, created_at FROM conversations WHERE (message_type IN ('image','video','audio')) AND (local_file IS NULL OR local_file = '') AND (media_url IS NOT NULL AND media_url != '') ORDER BY created_at DESC LIMIT 200"); +$inserted = 0; +foreach ($rows as $r) { + $media = $r['media_url']; + $data = ['conversation_id' => $r['id'], 'subdir' => date('Y/m'), 'status' => 'pending', 'created_at' => date('Y-m-d H:i:s')]; + if (preg_match('#^https?://#i', $media)) { + $data['media_url'] = $media; + } else { + // assume media id + $data['media_id'] = $media; + } + try { + $db->insert('media_queue', $data); + $inserted++; + } catch (Exception $e) { + // ignore duplicates + } +} +echo "Inserted: $inserted jobs\n"; \ No newline at end of file diff --git a/scripts/media_worker.php b/scripts/media_worker.php index ec1a97f..12e244f 100644 --- a/scripts/media_worker.php +++ b/scripts/media_worker.php @@ -10,9 +10,25 @@ $rows = $db->fetchAll('SELECT * FROM media_queue WHERE status = ? ORDER BY creat $ms = new MediaService(); foreach ($rows as $r) { try { - $res = $ms->fetchAndStoreFromGraph($r['media_id'], $r['subdir'] ?? date('Y/m')); + $res = null; + if (!empty($r['media_id'])) { + $res = $ms->fetchAndStoreFromGraph($r['media_id'], $r['subdir'] ?? date('Y/m')); + } elseif (!empty($r['media_url'])) { + $res = $ms->fetchAndStoreFromUrl($r['media_url'], $r['subdir'] ?? date('Y/m')); + } + if ($res) { + // Actualizar fila media_queue $db->update('media_queue', ['status' => 'done', 'result' => json_encode($res)], 'id = :id', ['id' => $r['id']]); + // Actualizar conversations si tenemos conversation_id + if (!empty($r['conversation_id'])) { + $update = []; + if (!empty($res['local_file'])) $update['local_file'] = $res['local_file']; + if (!empty($res['local_thumb'])) $update['local_thumb'] = $res['local_thumb']; + if (!empty($update)) { + $db->update('conversations', $update, 'id = :id', ['id' => $r['conversation_id']]); + } + } } } catch (Exception $e) { $db->update('media_queue', ['status' => 'failed', 'result' => json_encode(['error' => $e->getMessage()])], 'id = :id', ['id' => $r['id']]); diff --git a/scripts/show_media_queue.php b/scripts/show_media_queue.php new file mode 100644 index 0000000..44f640f --- /dev/null +++ b/scripts/show_media_queue.php @@ -0,0 +1,5 @@ +fetchAll('SELECT id,media_id,media_url,conversation_id,status,created_at FROM media_queue ORDER BY created_at ASC LIMIT 100'); +echo json_encode($rows, JSON_PRETTY_PRINT) . "\n"; \ No newline at end of file