diff --git a/api/get_conversation_detail.php b/api/get_conversation_detail.php index 6d64a35..e0e1c80 100644 --- a/api/get_conversation_detail.php +++ b/api/get_conversation_detail.php @@ -30,7 +30,7 @@ try { // Obtener información del usuario $user = $db->fetch( - "SELECT id, phone_number, name FROM users WHERE id = ?", + "SELECT id, phone_number, name, in_service, in_service_by, in_service_at FROM users WHERE id = ?", [$user_id] ); @@ -39,6 +39,15 @@ try { echo json_encode(['error' => 'Usuario no encontrado']); exit; } + + // Si la conversación está siendo atendida, resolver el nombre del operador + $inServiceByName = null; + if (!empty($user['in_service_by'])) { + $admin = $db->fetch("SELECT id, full_name, username FROM admin_users WHERE id = ?", [$user['in_service_by']]); + if ($admin) { + $inServiceByName = $admin['full_name'] ?: $admin['username']; + } + } // Obtener todos los mensajes de la conversación $conversations = $db->fetchAll( @@ -122,7 +131,11 @@ try { 'user' => [ 'id' => intval($user['id']), 'phone_number' => $user['phone_number'], - 'name' => $user['name'] ?? $user['phone_number'] + 'name' => $user['name'] ?? $user['phone_number'], + 'in_service' => !empty($user['in_service']) ? true : false, + 'in_service_by' => isset($user['in_service_by']) ? intval($user['in_service_by']) : null, + 'in_service_at' => $user['in_service_at'] ?? null, + 'in_service_by_name' => $inServiceByName ], 'conversations' => $conversations, 'total_conversations' => count($conversations) diff --git a/api/version/media-url_debug.log b/api/version/media-url_debug.log index 9df10b8..c4484b0 100644 --- a/api/version/media-url_debug.log +++ b/api/version/media-url_debug.log @@ -44,3 +44,21 @@ [2026-01-21 14:40:09] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] [2026-01-21 14:40:09] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 [2026-01-21 14:40:10] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769024699&hash=ARlXiE4qXDbAzqzvCutrIryD_tdYaN1gjuIZWoBHDbilqQ +[2026-01-21 21:35:15] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] +[2026-01-21 21:35:15] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 +[2026-01-21 21:35:16] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769049615&hash=ARl0ssx82h2xALShS68EX09cnPLDRp3xDrMkXLHxNpYdmQ +[2026-01-21 21:39:36] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] +[2026-01-21 21:39:36] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 +[2026-01-21 21:39:37] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769049877&hash=ARkt4u_w991l5uoAEPQPa_zaMV4_P5fD4HAa6ClILfS5ZA +[2026-01-21 21:55:37] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] +[2026-01-21 21:55:37] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 +[2026-01-21 21:55:37] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769050837&hash=ARn_VZzaTBKkCf7WJMaJHiVu3cYe_pxGrq1Ix9YvppbNBA +[2026-01-21 21:59:29] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] +[2026-01-21 21:59:29] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 +[2026-01-21 21:59:30] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769051070&hash=ARn9gwkRZ-Tfpv4cSUtMh9pebsJNis-s5tEQeIhxh7iPAA +[2026-01-21 22:00:11] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] +[2026-01-21 22:00:11] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 +[2026-01-21 22:00:11] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769051111&hash=ARnFCx7vV54UV2Z2LJnp3JMsxseZMInCxX8bM-xz0mM5AA +[2026-01-21 22:05:03] Request: GET /api/version/media-url.php?id=3932473397057815 GET:{"id":"3932473397057815"} POST:[] +[2026-01-21 22:05:03] Graph API request to https://graph.facebook.com/v22.0/3932473397057815 +[2026-01-21 22:05:03] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3932473397057815&source=getMedia&ext=1769051403&hash=ARm7CptuN3CH6WN5V1RmuW6rK9iIipE_KE1-FLbj3RQuHQ diff --git a/api/version/media-url_error.log b/api/version/media-url_error.log index 45b6ec2..6ed5eb5 100644 --- a/api/version/media-url_error.log +++ b/api/version/media-url_error.log @@ -2,3 +2,11 @@ [21-Jan-2026 14:31:30 America/Bogota] PHP Warning: preg_match(): Unknown modifier '&' in C:\laragon\www\whatsapp\api\version\media-url.php on line 131 [21-Jan-2026 14:33:32 America/Bogota] PHP Warning: preg_match(): Unknown modifier '&' in C:\laragon\www\whatsapp\api\version\media-url.php on line 131 [21-Jan-2026 14:35:45 America/Bogota] PHP Warning: preg_match(): Unknown modifier '&' in C:\laragon\www\whatsapp\api\version\media-url.php on line 131 +[21-Jan-2026 21:35:16 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 203 +[21-Jan-2026 21:35:17 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 522 +[21-Jan-2026 21:39:37 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 203 +[21-Jan-2026 21:39:37 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 522 +[21-Jan-2026 21:55:37 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 203 +[21-Jan-2026 21:55:38 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 522 +[21-Jan-2026 21:59:30 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 203 +[21-Jan-2026 21:59:30 America/Bogota] PHP Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /Users/lizandro/Documents/GitHub/whatsapp/api/version/media-url.php on line 522 diff --git a/chat_window.php b/chat_window.php index 129aa39..2bc4385 100644 --- a/chat_window.php +++ b/chat_window.php @@ -357,6 +357,10 @@ if (empty($user_id)) {
+ + @@ -563,6 +567,9 @@ if (empty($user_id)) { document.getElementById('userAvatar').innerHTML = `${(currentUser.name || 'U').charAt(0).toUpperCase()}`; + // Estado de atención + renderAttendControls(); + // Cargar mensajes displayconversations(response.conversations); @@ -1060,6 +1067,51 @@ if (empty($user_id)) { wrapper.style.display = (wrapper.style.display === 'block') ? 'none' : 'block'; } } + + // ===== Controles de atención (Atender / Finalizar) ===== + function renderAttendControls() { + const btn = document.getElementById('attendBtn'); + const status = document.getElementById('attendStatus'); + if (!btn || !status || !currentUser) return; + + if (currentUser.in_service) { + btn.innerHTML = ' Finalizar'; + btn.classList.remove('btn-outline-light'); + btn.classList.add('btn-light','text-dark'); + status.innerHTML = `Atendido por ${currentUser.in_service_by_name || 'un asesor'}`; + } else { + btn.innerHTML = ' Atender'; + btn.classList.remove('btn-light','text-dark'); + btn.classList.add('btn-outline-light'); + status.innerHTML = ''; + } + } + + async function toggleAttend() { + if (!currentUser) return; + try { + if (currentUser.in_service) { + const resp = await whatsappManager.apiCall('finish_attend.php', { body: { user_id: userId } }); + if (resp && resp.success) { + showAlert('Finalizada la atención', 'success'); + await loadUserData(); + } else { + throw new Error(resp.error || 'Error finalizando'); + } + } else { + const resp = await whatsappManager.apiCall('attend.php', { body: { user_id: userId } }); + if (resp && resp.success) { + showAlert('Atención iniciada', 'success'); + await loadUserData(); + } else { + throw new Error(resp.error || 'Error iniciando atención'); + } + } + } catch (err) { + console.error('Error toggling attend:', err); + showAlert('Error al cambiar estado de atención: ' + err.message, 'danger'); + } + } function showError(message) { showAlert(message, 'danger'); diff --git a/classes/Database.php b/classes/Database.php index 40f9ba5..f106c52 100644 --- a/classes/Database.php +++ b/classes/Database.php @@ -16,7 +16,7 @@ class Database { PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_PERSISTENT => true, - PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . DB_CHARSET . " COLLATE utf8mb4_unicode_ci" + (class_exists('Pdo\Mysql') ? \Pdo\Mysql::ATTR_INIT_COMMAND : PDO::MYSQL_ATTR_INIT_COMMAND) => "SET NAMES " . DB_CHARSET . " COLLATE utf8mb4_unicode_ci" ]; $this->pdo = new PDO($dsn, DB_USER, DB_PASS, $options); diff --git a/logs/system.log b/logs/system.log index 30b6d48..20f220b 100644 --- a/logs/system.log +++ b/logs/system.log @@ -32,3 +32,22 @@ Stack trace: [2026-01-21 15:16:53] [INFO] Cleared advisor_requested for user 142 after outgoing message [2026-01-21 15:16:56] [INFO] Cleared advisor_requested for user 142 after outgoing message [2026-01-21 15:16:57] [INFO] Bot paused for 5 minutes for 573010000003 +[2026-01-21 21:55:46] [INFO] Cleared advisor_requested for user 135 after outgoing message +[2026-01-21 21:55:46] [INFO] Operator 2 attending conversation for 573168950803 +[2026-01-21 21:56:00] [INFO] Cleared advisor_requested for user 135 after outgoing message +[2026-01-21 21:56:00] [INFO] Operator 2 attending conversation for 573168950803 +[2026-01-21 21:59:34] [WARN] Failed to send survey template: Error de WhatsApp API: (#131009) Parameter value is not valid +[2026-01-21 21:59:34] [INFO] Operator 2 finished attending for 573168950803, survey=encuesta_satisfaccin +[2026-01-21 21:59:38] [WARN] Failed to send survey template: Error de WhatsApp API: (#131009) Parameter value is not valid +[2026-01-21 21:59:39] [INFO] Operator 2 finished attending for 573168950803, survey=encuesta_satisfaccin +[2026-01-21 22:00:23] [INFO] Cleared advisor_requested for user 135 after outgoing message +[2026-01-21 22:00:23] [INFO] Operator 2 attending conversation for 573168950803 +[2026-01-21 22:03:38] [INFO] Cleared advisor_requested for user 163 after outgoing message +[2026-01-21 22:03:38] [INFO] Operator 1 attending conversation for 573000000999 +[2026-01-21 22:04:01] [INFO] Cleared advisor_requested for user 164 after outgoing message +[2026-01-21 22:04:02] [INFO] Operator 1 attending conversation for 573000000999 +[2026-01-21 22:05:30] [WARN] Failed to send survey template: Error de WhatsApp API: (#131009) Parameter value is not valid +[2026-01-21 22:05:31] [INFO] Operator 2 finished attending for 573168950803, survey=encuesta_satisfaccin +[2026-01-21 22:06:42] [INFO] Cleared advisor_requested for user 135 after outgoing message +[2026-01-21 22:06:43] [INFO] Operator 2 attending conversation for 573168950803 +[2026-01-21 22:07:15] [INFO] Cleared advisor_requested for user 135 after outgoing message diff --git a/scripts/test_in_service_block.php b/scripts/test_in_service_block.php new file mode 100644 index 0000000..c2a148e --- /dev/null +++ b/scripts/test_in_service_block.php @@ -0,0 +1,21 @@ +fetch('SELECT id, phone_number FROM users LIMIT 1'); +if (!$user) { + echo "No users found\n"; + exit(1); +} + +// Mark user as in_service +$db->update('users', ['in_service' => 1, 'in_service_by' => 1, 'in_service_at' => date('Y-m-d H:i:s')], 'id = :id', ['id' => $user['id']]); + +// Fetch stale user array (simulate that caller has an old copy without in_service) +$staleUser = ['id' => $user['id'], 'phone_number' => $user['phone_number']]; + +$bot = new BotService(); +$bot->processMessage($staleUser, 'hola', 'text'); + +echo "processMessage invoked (should have skipped bot processing if in_service)\n"; \ No newline at end of file diff --git a/services/BotService.php b/services/BotService.php index 7f638cd..658a205 100644 --- a/services/BotService.php +++ b/services/BotService.php @@ -25,6 +25,26 @@ class BotService { $messageText = trim($messageText); $phoneNumber = $user['phone_number']; + // Comprobar en la base de datos el estado más reciente de 'in_service' para evitar condiciones de carrera + try { + $fresh = $this->db->fetch("SELECT in_service, in_service_by FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]); + if (!empty($fresh['in_service'])) { + try { + $userId = $user['id'] ?? 'unknown'; + error_log("[BotService] processMessage - user_id={$userId} is currently in_service (DB), skipping bot processing"); + } catch (Throwable $t) { + // ignore logging errors + } + return; + } + // actualizar el array $user con valores frescos por si se usan más adelante + $user['in_service'] = !empty($fresh['in_service']); + $user['in_service_by'] = $fresh['in_service_by'] ?? null; + } catch (Exception $e) { + error_log('[BotService] processMessage - failed to refresh in_service: ' . $e->getMessage()); + // En caso de fallo al consultar, continuar con lógica previa (no bloquear) + } + // DEBUG: log basic context try { $userId = $user['id'] ?? 'unknown'; diff --git a/services/WhatsAppService.php b/services/WhatsAppService.php index 84f1d20..eab1576 100644 --- a/services/WhatsAppService.php +++ b/services/WhatsAppService.php @@ -465,7 +465,10 @@ class WhatsAppService $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); - curl_close($ch); + // curl_close is deprecated in PHP 8.5+ (has no effect). Avoid calling it on newer versions. + if (version_compare(PHP_VERSION, '8.5.0', '<')) { + curl_close($ch); + } if ($error) { error_log("cURL Error: " . $error);