From 09b50a2a99902f4b200915d1947e8c62f1852b44 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 24 Jan 2026 02:40:33 -0500 Subject: [PATCH] up --- scripts/test_menu_backflow.php | 26 +++++++ services/BotService.php | 130 +++++++++++++++++++++++++++++---- 2 files changed, 142 insertions(+), 14 deletions(-) diff --git a/scripts/test_menu_backflow.php b/scripts/test_menu_backflow.php index baf9aed..025f69d 100644 --- a/scripts/test_menu_backflow.php +++ b/scripts/test_menu_backflow.php @@ -93,6 +93,32 @@ print_r($stateSvc->getState($phone)); echo "menu_history: " . json_encode($stateSvc->getStateData($phone, 'menu_history')) . "\n"; print_r($db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phone])); +// 3b) En menu informacion_examenes, enviar '6' para volver al menú principal (según la config) +echo "\n3b) En menu informacion_examenes, enviar '6' para volver al menú principal\n"; +$bot->processMessage($user, '6'); +print_r($stateSvc->getState($phone)); +echo "menu_history: " . json_encode($stateSvc->getStateData($phone, 'menu_history')) . "\n"; +print_r($db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phone])); + +// 4) Enviar 'ATRAS' (para comparar comportamiento) +echo "\n4) Enviar 'ATRAS'\n"; +$bot->processMessage($user, 'ATRAS'); +print_r($stateSvc->getState($phone)); +echo "menu_history: " . json_encode($stateSvc->getStateData($phone, 'menu_history')) . "\n"; +print_r($db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phone])); + +// 5) Probar si reconoce otros comandos/números: enviar '1' y una palabra cualquiera y 'ASESOR' y 'MENU' +echo "\n5) Probar entradas posteriores: enviar '1', 'hola', 'ASESOR', 'MENU'\n"; +$bot->processMessage($user, '1'); +$bot->processMessage($user, 'hola'); +$bot->processMessage($user, 'ASESOR'); +$bot->processMessage($user, 'MENU'); +print_r($stateSvc->getState($phone)); +echo "menu_history: " . json_encode($stateSvc->getStateData($phone, 'menu_history')) . "\n"; +print_r($db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phone])); + +echo "\nTest script finished.\n"; + // 4) Enviar ATRAS echo "\n4) Enviar 'ATRAS'\n"; $bot->processMessage($user, 'ATRAS'); diff --git a/services/BotService.php b/services/BotService.php index 394dd60..71c8737 100644 --- a/services/BotService.php +++ b/services/BotService.php @@ -126,13 +126,94 @@ class BotService { $now = time(); $until = !empty($user['bot_paused_until']) ? strtotime($user['bot_paused_until']) : null; if ($until && $until > $now) { + // Permitir *navegación explícita* durante la espera: MENU, ATRÁS, números (opciones) + $normalized = mb_strtolower(trim($messageText)); + $isNav = in_array($normalized, ['menu','menú','inicio','atras','atrás','volver','back'], true) || is_numeric($messageText); try { $this->whatsappService->sendTextMessage($phoneNumber, "🔔 Su solicitud de asesor está pendiente. Un miembro del equipo le atenderá en breve. Si no hay respuesta, podrá usar *MENU* después de " . date('g:i a', $until) . "."); } catch (Exception $e) { // ignore } - error_log("[BotService] processMessage - returning early because advisor_requested active for user {$user['id']}"); - return; + if ($isNav) { + try { error_log("[BotService] processMessage - advisor_requested active but navigation input allowed for user {$user['id']} message='{$messageText}'"); } catch (Throwable $t) {} + + // Manejar la navegación inmediatamente (no esperar al flujo normal) + $normalized = mb_strtolower(trim($messageText)); + // MENU + if (in_array($normalized, ['menu','menú','inicio'], true)) { + $this->showMainMenu($phoneNumber); + return; + } + // ATRÁS + if (in_array($normalized, ['atras','atrás','volver','back'], true)) { + // Si hay menú actual, intentar ir al padre, si no, usar historial + try { + $cur = $this->db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]); + $currentMenuId = $cur ? $cur['current_menu_id'] : null; + } catch (Exception $e) { + $currentMenuId = null; + } + + if (!empty($currentMenuId)) { + $this->goToParentMenu($phoneNumber, $currentMenuId); + } else { + $this->goToPreviousMenu($phoneNumber); + } + return; + } + + // Si es número, intentar procesarlo contra current_menu_id o historial + if (is_numeric(trim($messageText))) { + try { + $stateSvc2 = new ConversationStateService(); + $resolvedMenuId2 = $stateSvc2->getCurrentMenuId($phoneNumber); + } catch (Exception $e) { + $resolvedMenuId2 = null; + } + + if (empty($resolvedMenuId2)) { + try { + $cur = $this->db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]); + $resolvedMenuId2 = $cur ? $cur['current_menu_id'] : null; + } catch (Exception $e) { + $resolvedMenuId2 = null; + } + } + + // Si no hay menú activo, intentar con historial + if (empty($resolvedMenuId2)) { + try { + $hist = (array)$stateSvc2->getStateData($phoneNumber, 'menu_history'); + if (!empty($hist)) $resolvedMenuId2 = end($hist); + } catch (Exception $e) { + $resolvedMenuId2 = null; + } + } + + if (!empty($resolvedMenuId2)) { + // Asegurar que la BD refleje el menu activo para que processMenuSelection lo resuelva correctamente + try { + $this->db->update('users', ['current_menu_id' => $resolvedMenuId2, 'current_step' => 1], 'phone_number = :phone', ['phone' => $phoneNumber]); + try { + $stateSvc2->setCurrentMenu($phoneNumber, (int)$resolvedMenuId2); + } catch (Exception $e) { + // ignore + } + } catch (Exception $e) { + error_log('[BotService] failed to set current_menu_id before processing numeric selection: ' . $e->getMessage()); + } + + $user['current_menu_id'] = $resolvedMenuId2; + $this->processMenuSelection($user, $messageText); + return; + } + } + + // Si llegamos aquí no pudimos resolver navegación; permitir que el flujo normal lo trate + } else { + error_log("[BotService] processMessage - returning early because advisor_requested active for user {$user['id']}"); + return; + } } else { // expiró la espera: limpiar flag try { @@ -191,19 +272,40 @@ class BotService { // Verificar si el usuario está en un menú if (!empty($resolvedMenuId)) { $this->processMenuSelection($user, $messageText); - } else { - // No usar las "respuestas rápidas" (autoresponses) automáticamente desde el bot. - // Estas respuestas solo se mostrarán en la UI del chat como "respuestas rápidas". - // Si un asesor ya respondió recientemente, no enviar respuestas automáticas. - if (empty($advisorReplied)) { - $this->sendDefaultNoMatch($phoneNumber); - } else { - // Opcional: informar que el asesor ya respondió y no podemos enviar automáticas - try { - //$this->whatsappService->sendTextMessage($phoneNumber, "🔕 Un asesor ya ha intervenido en la conversación, por favor espera su respuesta o utiliza *MENU* para ver opciones."); - } catch (Exception $e) { - // ignore + return; + } + + // Si no hay menú actual pero el usuario envía un número, intentar interpretar el número + // contra el último menú en el historial (por ejemplo: user vio opciones, eligió end y luego envía '6' para volver) + if (is_numeric(trim($messageText))) { + try { + $stateSvc = new ConversationStateService(); + $history = (array)$stateSvc->getStateData($phoneNumber, 'menu_history'); + if (!empty($history)) { + $lastMenu = end($history); + try { error_log("[BotService] interpreting numeric input '{$messageText}' against lastMenu={$lastMenu} for phone={$phoneNumber}"); } catch (Throwable $t) {} + // simular que estamos en ese menú para procesar la selección + $user['current_menu_id'] = $lastMenu; + $this->processMenuSelection($user, $messageText); + return; } + } catch (Exception $e) { + // ignore and fallthrough to default + error_log('[BotService] failed to check menu_history: ' . $e->getMessage()); + } + } + + // No usar las "respuestas rápidas" (autoresponses) automáticamente desde el bot. + // Estas respuestas solo se mostrarán en la UI del chat como "respuestas rápidas". + // Si un asesor ya respondió recientemente, no enviar respuestas automáticas. + if (empty($advisorReplied)) { + $this->sendDefaultNoMatch($phoneNumber); + } else { + // Opcional: informar que el asesor ya respondió y no podemos enviar automáticas + try { + //$this->whatsappService->sendTextMessage($phoneNumber, "🔕 Un asesor ya ha intervenido en la conversación, por favor espera su respuesta o utiliza *MENU* para ver opciones."); + } catch (Exception $e) { + // ignore } } }