This commit is contained in:
Lizandro Guarnizo
2026-01-21 22:11:12 -05:00
parent 2b92e47624
commit 31dbc8aa86
9 changed files with 158 additions and 4 deletions
+15 -2
View File
@@ -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)
+18
View File
@@ -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
+8
View File
@@ -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
+52
View File
@@ -357,6 +357,10 @@ if (empty($user_id)) {
</div>
</div>
<div class="d-flex align-items-center">
<button id="attendBtn" class="btn btn-outline-light btn-sm me-2" onclick="toggleAttend()" title="Atender/Finalizar">
<i class="fas fa-user-check"></i> Atender
</button>
<small id="attendStatus" class="text-white me-3" style="font-size:0.9rem;"></small>
<button class="btn btn-outline-light btn-sm me-2" onclick="refreshChat()">
<i class="fas fa-sync-alt"></i>
</button>
@@ -563,6 +567,9 @@ if (empty($user_id)) {
document.getElementById('userAvatar').innerHTML =
`<span>${(currentUser.name || 'U').charAt(0).toUpperCase()}</span>`;
// 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 = '<i class="fas fa-user-times"></i> 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 = '<i class="fas fa-user-check"></i> 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');
+1 -1
View File
@@ -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);
+19
View File
@@ -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
+21
View File
@@ -0,0 +1,21 @@
<?php
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../services/BotService.php';
$db = Database::getInstance();
$user = $db->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";
+20
View File
@@ -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';
+4 -1
View File
@@ -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);