Update BotService.php
This commit is contained in:
+145
-21
@@ -326,28 +326,36 @@ class BotService {
|
||||
*/
|
||||
private function processMenuSelection($user, $messageText) {
|
||||
$phoneNumber = $user['phone_number'];
|
||||
// Obtener el current_menu_id más reciente desde la tabla users (evita estados desincronizados)
|
||||
$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
|
||||
if (empty($currentMenuId)) {
|
||||
try {
|
||||
$stateSvc = new ConversationStateService();
|
||||
$currentMenuId = $stateSvc->getCurrentMenuId($phoneNumber);
|
||||
} catch (Exception $e) {
|
||||
// ignore if service not available
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
try { error_log("[BotService] processMenuSelection - user_id={$user['id']} menu_id={$currentMenuId} received='{$messageText}'"); } catch (Throwable $t) {}
|
||||
$currentMenuId = null;
|
||||
|
||||
// Soportar comando "atrás" o "volver" para navegar al menú padre
|
||||
// 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)) {
|
||||
$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);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
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 o al menú previo
|
||||
$cmd = mb_strtolower(trim($messageText));
|
||||
$backCommands = ['atras', 'atrás', 'volver', 'back'];
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -503,14 +511,38 @@ class BotService {
|
||||
|
||||
/**
|
||||
* Salir del menú actual
|
||||
* Guarda el menú previo en el historial para permitir "ATRÁS" después de salir
|
||||
*/
|
||||
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(
|
||||
'users',
|
||||
['current_menu_id' => null, 'current_step' => 0, 'session_data' => null],
|
||||
'phone_number = :phone',
|
||||
['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
|
||||
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')
|
||||
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) {
|
||||
$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)) {
|
||||
$this->showMenu($phoneNumber, $parentId);
|
||||
return;
|
||||
} 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'])) {
|
||||
$this->showMenu($phoneNumber, $menu['id']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try { error_log("[BotService] goToParentMenu - could not resolve menu for currentMenuId='{$currentMenuId}' phone={$phoneNumber}"); } catch (Throwable $t) {}
|
||||
}
|
||||
|
||||
// Fallback final a menú principal
|
||||
@@ -703,17 +739,105 @@ class BotService {
|
||||
|
||||
/**
|
||||
* Actualizar estado del menú del usuario
|
||||
* - Guarda el menú actual en el historial (ConversationStateService) antes de actualizar
|
||||
*/
|
||||
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(
|
||||
'users',
|
||||
['current_menu_id' => $menuId, 'current_step' => 1],
|
||||
'phone_number = :phone',
|
||||
['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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user