Update BotService.php
This commit is contained in:
+140
-16
@@ -326,28 +326,36 @@ class BotService {
|
|||||||
*/
|
*/
|
||||||
private function processMenuSelection($user, $messageText) {
|
private function processMenuSelection($user, $messageText) {
|
||||||
$phoneNumber = $user['phone_number'];
|
$phoneNumber = $user['phone_number'];
|
||||||
// Obtener el current_menu_id más reciente desde la tabla users (evita estados desincronizados)
|
$currentMenuId = null;
|
||||||
$cur = $this->db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]);
|
|
||||||
$currentMenuId = $cur ? $cur['current_menu_id'] : ($user['current_menu_id'] ?? null);
|
|
||||||
|
|
||||||
// Fallback: si usamos ConversationStateService para guardar estados, respetarlo
|
// Intentar primero el servicio de estados (si existe) — esto permite flujos que usan user_states en lugar de la columna users.current_menu_id
|
||||||
|
try {
|
||||||
|
$stateSvc = new ConversationStateService();
|
||||||
|
$currentMenuId = $stateSvc->getCurrentMenuId($phoneNumber);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// ignore if service not available
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no tenemos valor desde el servicio de estados, consultar columna users
|
||||||
if (empty($currentMenuId)) {
|
if (empty($currentMenuId)) {
|
||||||
try {
|
$cur = $this->db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]);
|
||||||
$stateSvc = new ConversationStateService();
|
$currentMenuId = $cur ? $cur['current_menu_id'] : ($user['current_menu_id'] ?? null);
|
||||||
$currentMenuId = $stateSvc->getCurrentMenuId($phoneNumber);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// ignore if service not available
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
try { error_log("[BotService] processMenuSelection - user_id={$user['id']} menu_id={$currentMenuId} received='{$messageText}'"); } catch (Throwable $t) {}
|
try { error_log("[BotService] processMenuSelection - RESOLVED menu_id={$currentMenuId} for phone={$phoneNumber} user_id={$user['id']} incoming='{$messageText}'"); } catch (Throwable $t) {}
|
||||||
|
|
||||||
// Soportar comando "atrás" o "volver" para navegar al menú padre
|
// Soportar comando "atrás" o "volver" para navegar al menú padre o al menú previo
|
||||||
$cmd = mb_strtolower(trim($messageText));
|
$cmd = mb_strtolower(trim($messageText));
|
||||||
$backCommands = ['atras', 'atrás', 'volver', 'back'];
|
$backCommands = ['atras', 'atrás', 'volver', 'back'];
|
||||||
if (in_array($cmd, $backCommands, true)) {
|
if (in_array($cmd, $backCommands, true)) {
|
||||||
$this->goToParentMenu($phoneNumber, $currentMenuId);
|
try { error_log("[BotService] processMenuSelection - BACK command detected for phone={$phoneNumber} resolved_menu={$currentMenuId}"); } catch (Throwable $t) {}
|
||||||
|
if (!empty($currentMenuId)) {
|
||||||
|
$this->goToParentMenu($phoneNumber, $currentMenuId);
|
||||||
|
} else {
|
||||||
|
// Si no hay menú actual, intentar volver al menú previo almacenado en historial
|
||||||
|
$this->goToPreviousMenu($phoneNumber);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,14 +511,38 @@ class BotService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Salir del menú actual
|
* Salir del menú actual
|
||||||
|
* Guarda el menú previo en el historial para permitir "ATRÁS" después de salir
|
||||||
*/
|
*/
|
||||||
private function exitMenu($phoneNumber) {
|
private function exitMenu($phoneNumber) {
|
||||||
|
// Obtener menú actual antes de limpiar
|
||||||
|
try {
|
||||||
|
$cur = $this->db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]);
|
||||||
|
$previousMenu = $cur ? $cur['current_menu_id'] : null;
|
||||||
|
if (!empty($previousMenu)) {
|
||||||
|
try {
|
||||||
|
$this->pushMenuToHistory($phoneNumber, $previousMenu);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[BotService] exitMenu - failed to push previous menu: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
$this->db->update(
|
$this->db->update(
|
||||||
'users',
|
'users',
|
||||||
['current_menu_id' => null, 'current_step' => 0, 'session_data' => null],
|
['current_menu_id' => null, 'current_step' => 0, 'session_data' => null],
|
||||||
'phone_number = :phone',
|
'phone_number = :phone',
|
||||||
['phone' => $phoneNumber]
|
['phone' => $phoneNumber]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Mantener el estado en ConversationStateService (no borrar el historial)
|
||||||
|
try {
|
||||||
|
$stateSvc = new ConversationStateService();
|
||||||
|
$stateSvc->setCurrentMenu($phoneNumber, null);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -671,26 +703,30 @@ class BotService {
|
|||||||
|
|
||||||
// Intentar resolver por ID numérico
|
// Intentar resolver por ID numérico
|
||||||
if (!empty($currentMenuId) && is_numeric($currentMenuId)) {
|
if (!empty($currentMenuId) && is_numeric($currentMenuId)) {
|
||||||
$menu = $this->db->fetch("SELECT id, parent_id FROM menus WHERE id = :id", ['id' => (int)$currentMenuId]);
|
$menu = $this->db->fetch("SELECT id, parent_id, name, title FROM menus WHERE id = :id", ['id' => (int)$currentMenuId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si no existe, intentar resolver por nombre (caso action_value = 'main_menu')
|
// Si no existe, intentar resolver por nombre (caso action_value = 'main_menu')
|
||||||
if (!$menu && !empty($currentMenuId)) {
|
if (!$menu && !empty($currentMenuId)) {
|
||||||
$menu = $this->db->fetch("SELECT id, parent_id FROM menus WHERE name = :name", ['name' => $currentMenuId]);
|
$menu = $this->db->fetch("SELECT id, parent_id, name, title FROM menus WHERE name = :name", ['name' => $currentMenuId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($menu) {
|
if ($menu) {
|
||||||
$parentId = $menu['parent_id'] ?? null;
|
$parentId = $menu['parent_id'] ?? null;
|
||||||
|
try { error_log("[BotService] goToParentMenu - resolved menu id={$menu['id']} name={$menu['name']} title='{$menu['title']}' parent_id=" . ($parentId ?? 'null') . " for phone={$phoneNumber}"); } catch (Throwable $t) {}
|
||||||
|
|
||||||
if (!empty($parentId)) {
|
if (!empty($parentId)) {
|
||||||
$this->showMenu($phoneNumber, $parentId);
|
$this->showMenu($phoneNumber, $parentId);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Si no hay padre, re-mostrar el menú actual (mejor UX) o fallback al menú principal
|
// Si no hay padre, re-mostrar el menú actual (mejor UX)
|
||||||
if (!empty($menu['id'])) {
|
if (!empty($menu['id'])) {
|
||||||
$this->showMenu($phoneNumber, $menu['id']);
|
$this->showMenu($phoneNumber, $menu['id']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
try { error_log("[BotService] goToParentMenu - could not resolve menu for currentMenuId='{$currentMenuId}' phone={$phoneNumber}"); } catch (Throwable $t) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback final a menú principal
|
// Fallback final a menú principal
|
||||||
@@ -703,17 +739,105 @@ class BotService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Actualizar estado del menú del usuario
|
* Actualizar estado del menú del usuario
|
||||||
|
* - Guarda el menú actual en el historial (ConversationStateService) antes de actualizar
|
||||||
*/
|
*/
|
||||||
private function updateUserMenuState($phoneNumber, $menuId) {
|
private function updateUserMenuState($phoneNumber, $menuId) {
|
||||||
|
// Obtener menú previo
|
||||||
|
try {
|
||||||
|
$cur = $this->db->fetch("SELECT current_menu_id FROM users WHERE phone_number = :phone", ['phone' => $phoneNumber]);
|
||||||
|
$previousMenu = $cur ? $cur['current_menu_id'] : null;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$previousMenu = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si hay un menú previo diferente al nuevo, empujarlo al historial
|
||||||
|
if (!empty($previousMenu) && $previousMenu != $menuId) {
|
||||||
|
try {
|
||||||
|
$this->pushMenuToHistory($phoneNumber, $previousMenu);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[BotService] failed to push menu to history: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar columna users
|
||||||
$this->db->update(
|
$this->db->update(
|
||||||
'users',
|
'users',
|
||||||
['current_menu_id' => $menuId, 'current_step' => 1],
|
['current_menu_id' => $menuId, 'current_step' => 1],
|
||||||
'phone_number = :phone',
|
'phone_number = :phone',
|
||||||
['phone' => $phoneNumber]
|
['phone' => $phoneNumber]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// También actualizar ConversationStateService por compatibilidad
|
||||||
|
try {
|
||||||
|
$stateSvc = new ConversationStateService();
|
||||||
|
$stateSvc->setCurrentMenu($phoneNumber, (int)$menuId);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// ignore if service not available
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empujar un menú al historial del usuario (se almacena en ConversationStateService.state_data.menu_history)
|
||||||
|
*/
|
||||||
|
private function pushMenuToHistory(string $phoneNumber, $menuId) {
|
||||||
|
try {
|
||||||
|
$stateSvc = new ConversationStateService();
|
||||||
|
$history = (array)$stateSvc->getStateData($phoneNumber, 'menu_history');
|
||||||
|
if (!is_array($history)) $history = [];
|
||||||
|
|
||||||
|
// Añadir al final y limitar a 10 entradas
|
||||||
|
$history[] = $menuId;
|
||||||
|
if (count($history) > 10) array_shift($history);
|
||||||
|
|
||||||
|
return $stateSvc->updateStateData($phoneNumber, ['menu_history' => $history]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[BotService] pushMenuToHistory failed: ' . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sacar el último menú del historial (LIFO) y retornarlo
|
||||||
|
*/
|
||||||
|
private function popMenuFromHistory(string $phoneNumber) {
|
||||||
|
try {
|
||||||
|
$stateSvc = new ConversationStateService();
|
||||||
|
$history = (array)$stateSvc->getStateData($phoneNumber, 'menu_history');
|
||||||
|
if (!is_array($history) || empty($history)) return null;
|
||||||
|
|
||||||
|
$last = array_pop($history);
|
||||||
|
$stateSvc->updateStateData($phoneNumber, ['menu_history' => $history]);
|
||||||
|
return $last;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[BotService] popMenuFromHistory failed: ' . $e->getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ir al menú anterior según historial. Si no hay historial, fallback a main menu.
|
||||||
|
*/
|
||||||
|
private function goToPreviousMenu($phoneNumber) {
|
||||||
|
$prev = $this->popMenuFromHistory($phoneNumber);
|
||||||
|
if (!empty($prev)) {
|
||||||
|
// Intentar mostrar por ID primero
|
||||||
|
if (is_numeric($prev)) {
|
||||||
|
$this->showMenu($phoneNumber, (int)$prev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Si no es numérico, intentar resolver por nombre
|
||||||
|
$menu = $this->db->fetch("SELECT id FROM menus WHERE name = :name", ['name' => $prev]);
|
||||||
|
if ($menu && !empty($menu['id'])) {
|
||||||
|
$this->showMenu($phoneNumber, $menu['id']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
$this->showMainMenu($phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtener estadísticas del bot
|
* Obtener estadísticas del bot
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user