From edfa5f68d9c7ce7bb780752afbce4884d732ecc9 Mon Sep 17 00:00:00 2001 From: lizandrogd <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 21 Jan 2026 02:34:23 -0500 Subject: [PATCH] up --- api/webhook.php | 15 ++++- logs/system.log | 5 ++ scripts/add_user_bot_paused_until.php | 15 +++++ scripts/add_user_on_hold.php | 15 +++++ scripts/add_user_welcome_sent_at.php | 15 +++++ scripts/test_asesor_and_pause.php | 49 +++++++++++++++++ scripts/test_welcome_config.php | 35 ++++++++++++ services/BotService.php | 79 +++++++++++++++++++++++++-- 8 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 scripts/add_user_bot_paused_until.php create mode 100644 scripts/add_user_on_hold.php create mode 100644 scripts/add_user_welcome_sent_at.php create mode 100644 scripts/test_asesor_and_pause.php create mode 100644 scripts/test_welcome_config.php diff --git a/api/webhook.php b/api/webhook.php index 494a962..5a3406f 100644 --- a/api/webhook.php +++ b/api/webhook.php @@ -227,12 +227,23 @@ class WhatsAppWebhook { ); } - private function createUser($phoneNumber) { - return $this->db->insert('users', [ + private function createUser($phoneNumber, $sendWelcome = true) { + $id = $this->db->insert('users', [ 'phone_number' => $phoneNumber, 'status' => 'active', 'created_at' => date('Y-m-d H:i:s') ]); + + if ($sendWelcome && $id) { + try { + // Enviar mensaje de bienvenida usando el BotService (que consulta system_config) + $this->botService->sendWelcomeMessage($phoneNumber); + } catch (Throwable $t) { + error_log('Failed to send welcome message: ' . $t->getMessage()); + } + } + + return $id; } private function saveMessage($messageData) { diff --git a/logs/system.log b/logs/system.log index 8d0b8aa..1e635b0 100644 --- a/logs/system.log +++ b/logs/system.log @@ -18,3 +18,8 @@ Stack trace: [20-Jan-2026 23:54:02 America/Bogota] [webhook] __construct start [20-Jan-2026 23:54:03 America/Bogota] [webhook] __construct end [20-Jan-2026 23:54:03 America/Bogota] [webhook] handleRequest start, method=GET +[2026-01-21 02:30:13] [INFO] Advisor requested for 573010000123 +[2026-01-21 02:31:08] [INFO] Advisor requested for 573010000123 +[2026-01-21 02:31:42] [INFO] Advisor requested for 573010000123 +[2026-01-21 02:31:44] [INFO] Advisor released hold for 573010000123 +[2026-01-21 02:31:49] [INFO] Bot paused for 4 hours for 573010000123 diff --git a/scripts/add_user_bot_paused_until.php b/scripts/add_user_bot_paused_until.php new file mode 100644 index 0000000..579b4fc --- /dev/null +++ b/scripts/add_user_bot_paused_until.php @@ -0,0 +1,15 @@ +fetch("SHOW COLUMNS FROM users LIKE 'bot_paused_until'"); +if ($col) { + echo "Column bot_paused_until already exists\n"; + exit(0); +} +try { + $db->query("ALTER TABLE users ADD COLUMN bot_paused_until DATETIME NULL AFTER on_hold"); + echo "Added column bot_paused_until\n"; +} catch (Exception $e) { + echo "Failed to add bot_paused_until: " . $e->getMessage() . "\n"; + exit(1); +} diff --git a/scripts/add_user_on_hold.php b/scripts/add_user_on_hold.php new file mode 100644 index 0000000..2e949ad --- /dev/null +++ b/scripts/add_user_on_hold.php @@ -0,0 +1,15 @@ +fetch("SHOW COLUMNS FROM users LIKE 'on_hold'"); +if ($col) { + echo "Column on_hold already exists\n"; + exit(0); +} +try { + $db->query("ALTER TABLE users ADD COLUMN on_hold TINYINT(1) NULL DEFAULT 0 AFTER status"); + echo "Added column on_hold\n"; +} catch (Exception $e) { + echo "Failed to add on_hold: " . $e->getMessage() . "\n"; + exit(1); +} diff --git a/scripts/add_user_welcome_sent_at.php b/scripts/add_user_welcome_sent_at.php new file mode 100644 index 0000000..33f10e3 --- /dev/null +++ b/scripts/add_user_welcome_sent_at.php @@ -0,0 +1,15 @@ +fetch("SHOW COLUMNS FROM users LIKE 'welcome_sent_at'"); +if ($col) { + echo "Column welcome_sent_at already exists\n"; + exit(0); +} +try { + $db->query("ALTER TABLE users ADD COLUMN welcome_sent_at DATETIME NULL AFTER created_at"); + echo "Added column welcome_sent_at\n"; +} catch (Exception $e) { + echo "Failed to add welcome_sent_at: " . $e->getMessage() . "\n"; + exit(1); +} diff --git a/scripts/test_asesor_and_pause.php b/scripts/test_asesor_and_pause.php new file mode 100644 index 0000000..00ff0c8 --- /dev/null +++ b/scripts/test_asesor_and_pause.php @@ -0,0 +1,49 @@ +execute('DELETE FROM users WHERE phone_number = :p', ['p' => $phone]); +$userId = $db->insert('users', ['phone_number' => $phone, 'status' => 'active', 'created_at' => date('Y-m-d H:i:s')]); +$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]); + +// Simular comando asesor +$bot->processMessage($user, 'asesor', 'text'); +// Intentar también llamar directamente putOnHold para comprobar +$bot->putOnHold($phone); +$user2 = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]); +echo "User after putOnHold: " . json_encode($user2) . "\n"; +if ($user2['on_hold']) { + echo "ON_HOLD set correctly for $phone\n"; +} else { + echo "ON_HOLD NOT SET!\n"; +} + +// Simular finalizar conversación via menu action (invoke processMenuAction indirectly) +// Crear menu option end test: insert temporary menu and option +$menuId = $db->insert('menus', ['name'=>'test_menu_end','title'=>'Test End','is_root'=>0,'is_active'=>1,'created_at'=>date('Y-m-d H:i:s')]); +$optId = $db->insert('menu_options', ['menu_id'=>$menuId,'option_number'=>9,'text'=>'Terminar','action_type'=>'end','action_value'=>'Gracias, cerrando.','is_active'=>1,'created_at'=>date('Y-m-d H:i:s')]); +// Liberar hold y luego establecer menu para probar pause +$bot->releaseHold($phone); +$db->update('users', ['current_menu_id'=>$menuId], 'id = :id', ['id' => $userId]); + +// Simular selection '9' - recargar usuario para que tenga el current_menu_id +$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]); +$bot->processMessage($user, '9', 'text'); +$user3 = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]); +if (!empty($user3['bot_paused_until'])) { + echo "Bot paused until: {$user3['bot_paused_until']}\n"; +} else { + echo "Bot pause NOT SET!\n"; +} + +// Cleanup +$db->execute('DELETE FROM menu_options WHERE menu_id = :mid', ['mid' => $menuId]); +$db->execute('DELETE FROM menus WHERE id = :id', ['id' => $menuId]); +$db->execute('DELETE FROM users WHERE id = :id', ['id' => $userId]); + +echo "Done\n"; \ No newline at end of file diff --git a/scripts/test_welcome_config.php b/scripts/test_welcome_config.php new file mode 100644 index 0000000..8e9e554 --- /dev/null +++ b/scripts/test_welcome_config.php @@ -0,0 +1,35 @@ +execute('DELETE FROM users WHERE phone_number = :p', ['p' => $phone]); +$id = $db->insert('users', ['phone_number' => $phone, 'status' => 'active', 'created_at' => date('Y-m-d H:i:s')]); + +// Ensure welcome_message config exists +$message = getConfigFromDB('welcome_message', null); +if (!$message) { + $m = 'Bienvenido a nuestro servicio (mensaje por defecto)'; + // Insert config if missing + try { + $db->insert('system_config', ['config_key' => 'welcome_message', 'config_value' => $m, 'created_at' => date('Y-m-d H:i:s')]); + echo "Inserted welcome_message config\n"; + } catch (Exception $e) { + // ignore + } +} + +$bot->sendWelcomeMessage($phone); +$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $id]); +if (!empty($user['welcome_sent_at'])) { + echo "welcome_sent_at recorded: {$user['welcome_sent_at']}\n"; +} else { + echo "welcome_sent_at NOT set\n"; +} + +// cleanup +$db->execute('DELETE FROM users WHERE id = :id', ['id' => $id]); + +echo "Done\n"; \ No newline at end of file diff --git a/services/BotService.php b/services/BotService.php index 8f00e3a..45aedda 100644 --- a/services/BotService.php +++ b/services/BotService.php @@ -35,13 +35,29 @@ class BotService { if ($this->processSpecialCommands($phoneNumber, $messageText)) { return; } + + // Si el usuario está en espera por un asesor, no procesar + if (!empty($user['on_hold']) && $user['on_hold']) { + try { + $this->whatsappService->sendTextMessage($phoneNumber, "⚠️ Has sido puesto en espera por un asesor. En breve te contactarán."); + } catch (Exception $e) { + // ignore + } + return; + } + + // Si el bot está pausado por bot_paused_until, no responder automáticamente + if (!empty($user['bot_paused_until']) && strtotime($user['bot_paused_until']) > time()) { + return; + } + // Si el asesor ya respondió después del último mensaje del usuario, frenar al bot $lastIncoming = $this->db->fetch("SELECT created_at FROM conversations WHERE user_id = :uid AND direction = 'incoming' ORDER BY created_at DESC LIMIT 1", ['uid' => $user['id']]); $lastOutgoing = $this->db->fetch("SELECT created_at FROM conversations WHERE user_id = :uid AND direction = 'outgoing' ORDER BY created_at DESC LIMIT 1", ['uid' => $user['id']]); if ($lastOutgoing && $lastIncoming && strtotime($lastOutgoing['created_at']) >= strtotime($lastIncoming['created_at'])) { // El asesor ya respondió, no enviar respuestas automáticas return; - } + } // Verificar si el usuario está en un menú if ($user['current_menu_id']) { $this->processMenuSelection($user, $messageText); @@ -59,17 +75,29 @@ class BotService { "SELECT COUNT(*) as count FROM conversations WHERE user_id = :user_id", ['user_id' => $userId] ); - - return $messageCount['count'] <= 1; // Solo el mensaje actual + // Evitar reenviar welcome si ya se envió + $user = $this->db->fetch("SELECT welcome_sent_at FROM users WHERE id = :id", ['id' => $userId]); + $welcomeSent = !empty($user['welcome_sent_at']); + return ($messageCount['count'] <= 1) && !$welcomeSent; // Solo el mensaje actual y si no se ha enviado welcome } /** * Enviar mensaje de bienvenida */ - private function sendWelcomeMessage($phoneNumber) { + public function sendWelcomeMessage($phoneNumber) { + $enabled = getConfigFromDB('welcome_enabled', '1'); $welcomeMessage = getConfigFromDB('welcome_message', '¡Hola! 👋 Bienvenido a nuestro servicio automatizado. Escribe *menu* para ver las opciones disponibles.'); + if (!$enabled || !$welcomeMessage) return; + $this->whatsappService->sendTextMessage($phoneNumber, $welcomeMessage); + + // Registrar que se envió el welcome + try { + $this->db->update('users', ['welcome_sent_at' => date('Y-m-d H:i:s')], 'phone_number = :phone', ['phone' => $phoneNumber]); + } catch (Exception $e) { + error_log('Failed to set welcome_sent_at for ' . $phoneNumber . ': ' . $e->getMessage()); + } } /** @@ -93,7 +121,8 @@ class BotService { case 'asesor': case 'ayuda': - $this->showHelp($phoneNumber); + // Poner la conversación en espera y notificar al usuario + $this->putOnHold($phoneNumber); return true; } @@ -244,6 +273,8 @@ class BotService { $message = $option['action_value'] ?: "Gracias por usar nuestro servicio. ¡Hasta pronto!"; $this->whatsappService->sendTextMessage($phoneNumber, $message); $this->exitMenu($phoneNumber); + // Pausar el bot para esta conversación por 4 horas + $this->pauseConversationForHours($phoneNumber, 4); break; } } @@ -307,7 +338,43 @@ class BotService { ['phone' => $phoneNumber] ); } - + + /** + * Poner la conversación en pausa por 4 horas (llamada desde opciones 'end') + */ + private function pauseConversationForHours($phoneNumber, $hours = 4) { + try { + $pausedUntil = date('Y-m-d H:i:s', strtotime("+{$hours} hours")); + $this->db->update('users', ['bot_paused_until' => $pausedUntil], 'phone_number = :phone', ['phone' => $phoneNumber]); + if (function_exists('writeLog')) writeLog('INFO', "Bot paused for {$hours} hours for {$phoneNumber}"); + } catch (Exception $e) { + error_log('pauseConversationForHours failed: ' . $e->getMessage()); + } + } + + /** + * Poner conversación en espera (asesor solicitado) + */ + public function putOnHold($phoneNumber) { + try { + $this->db->update('users', ['on_hold' => 1], 'phone_number = :phone', ['phone' => $phoneNumber]); + $this->whatsappService->sendTextMessage($phoneNumber, "🔔 Se ha solicitado un asesor. Te hemos puesto en espera. Te contactaremos en breve."); + if (function_exists('writeLog')) writeLog('INFO', "Advisor requested for $phoneNumber"); + } catch (Exception $e) { + error_log('putOnHold failed: ' . $e->getMessage()); + } + } + + public function releaseHold($phoneNumber) { + try { + $this->db->update('users', ['on_hold' => 0], 'phone_number = :phone', ['phone' => $phoneNumber]); + $this->whatsappService->sendTextMessage($phoneNumber, "🔔 Tu conversación ha sido retomada por el equipo."); + if (function_exists('writeLog')) writeLog('INFO', "Advisor released hold for $phoneNumber"); + } catch (Exception $e) { + error_log('releaseHold failed: ' . $e->getMessage()); + } + } + /** * Actualizar estado del menú del usuario */