diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee2c83a --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Ignorar archivos de desarrollo y pruebas +/dev/ +/tmp/ +/tests/ + +# Ignorar credenciales y entornos +.env +CREDENCIALES_BASICO.txt + +# Logs y temporales +*.log +*.cache +*.pid + +# Composer / Node +vendor/ +node_modules/ + +# MacOS +.DS_Store diff --git a/.htaccess b/.htaccess index 015a687..7f9f74b 100644 --- a/.htaccess +++ b/.htaccess @@ -56,6 +56,16 @@ RewriteEngine On RedirectMatch 403 ^/tests/.*$ +# Proteger directorio de desarrollo (dev) y archivos de pruebas + + RedirectMatch 403 ^/dev/.*$ + + +# Proteger directorio temporal + + RedirectMatch 403 ^/tmp/.*$ + + # ================================================== # HEADERS DE SEGURIDAD (Compatible HestiaCP) # ================================================== diff --git a/CREDENCIALES_BASICO.txt b/CREDENCIALES_BASICO.txt deleted file mode 100644 index cee0499..0000000 --- a/CREDENCIALES_BASICO.txt +++ /dev/null @@ -1,3 +0,0 @@ -USUARIO: admin -CONTRASEÑA: 2fa904eee46f -FECHA: 2026-01-04 03:04:54 diff --git a/README.md b/README.md index 65a1282..7b8d143 100644 --- a/README.md +++ b/README.md @@ -1,295 +1,93 @@ -# 🤖 Sistema de WhatsApp Bot Manager +# WhatsApp Bot Manager 🚀 -Un sistema completo de gestión de chatbot para WhatsApp Business API con interfaz web moderna, menús parametrizables y gestión avanzada de conversaciones. - -## ✨ Características - -### 🔧 Funcionalidades Principales -- **Bot Inteligente** con menús parametrizables por números -- **Interfaz Web Moderna** para gestión completa -- **API REST** para integración con sistemas externos -- **Respuestas Automáticas** configurables -- **Envío de Mensajes** individuales y masivos -- **Gestión de Usuarios** con estados y seguimiento -- **Dashboard Analytics** con gráficos en tiempo real -- **Logs Detallados** de todas las interacciones -- **Sistema de Plantillas** para mensajes predefinidos - -### 🎯 Sistema de Menús -- Menús jerárquicos con navegación por números (1, 2, 3, etc.) -- Acciones configurables: navegar, mensaje, API call, finalizar -- Soporte para servicios, pagos, soporte técnico -- Completamente parametrizable desde la interfaz - -### 📱 Compatibilidad WhatsApp -- Integración completa con WhatsApp Business API -- Soporte para mensajes de texto, imágenes, audio, video -- Plantillas de WhatsApp Business -- Estados de entrega y lectura -- Webhook seguro con verificación - -## 🚀 Instalación - -### 1. Prerrequisitos -```bash -- PHP 8.0 o superior -- MySQL 5.7 o superior -- Servidor web (Apache/Nginx) -- WhatsApp Business API Token -- Dominio con SSL (para webhook) -``` - -### 2. Configuración de Base de Datos -```bash -# Importar el esquema de la base de datos -mysql -u pym -p'Nicolas2796*+' -h 46.202.93.92 whatsapp < database/schema.sql -``` - -### 3. Configuración del Proyecto -```bash -# Subir archivos al servidor -# Ajustar permisos -chmod 755 -R bot/ -chmod 666 config/config.php -``` - -### 4. Configurar WhatsApp Business API - -1. **Acceder a Facebook Developers** - - Crear una aplicación en developers.facebook.com - - Agregar el producto "WhatsApp Business API" - -2. **Configurar Webhook** - - URL del Webhook: `https://tudominio.com/migrador/api/webhook.php` - - Token de Verificación: `mi_token_secreto_123` - - Suscribirse a: `conversations` - -3. **Obtener Tokens** - - Token de Acceso (ya proporcionado) - - Phone Number ID: `938177732702718` - -### 5. Configuración en la Interfaz -1. Abrir: `https://tudominio.com/migrador/` -2. Ir a **Configuración** -3. Completar: - - Token de WhatsApp - - Phone Number ID - - Token de Verificación - - Nombre de la empresa - - Mensaje de bienvenida - -## 📖 Uso del Sistema - -### 🎮 Interfaz Web -- **Dashboard**: Estadísticas y actividad en tiempo real -- **Conversaciones**: Ver y gestionar todas las conversaciones -- **Usuarios**: Administrar usuarios y estados -- **Menús**: Configurar y editar el flujo del bot -- **Mensajes**: Enviar mensajes individuales o masivos -- **Configuración**: Ajustar parámetros del sistema - -### 🤖 Bot de WhatsApp - -#### Comandos Disponibles -- `menu` - Mostrar menú principal -- `ayuda` - Mostrar ayuda -- `salir` - Salir del menú actual - -#### Flujo de Menús (Ejemplo) -``` -📋 Menú Principal - -Selecciona una opción: -1. 💼 Ver servicios disponibles -2. 💳 Realizar pago -3. 🆘 Contactar soporte -0. ❌ Salir - -💬 Responde con el número de la opción que deseas -``` - -### 🔧 API Endpoints - -#### Webhook -```bash -POST /api/webhook.php -# Recibe mensajes de WhatsApp -``` - -#### Enviar Mensaje -```bash -POST /api/send_message.php -Content-Type: application/json - -{ - "recipient": "573168950803", - "type": "text", - "message": "Hola, este es un mensaje de prueba" -} -``` - -#### Obtener Estadísticas -```bash -GET /api/get_stats.php -``` - -## 🏗️ Estructura del Proyecto - -``` -migrador/ -├── api/ # Endpoints de la API -│ ├── webhook.php # Webhook principal -│ ├── send_message.php # Envío de mensajes -│ ├── get_stats.php # Estadísticas -│ └── ... # Otras APIs -├── assets/ # Recursos estáticos -│ ├── css/ # Estilos CSS -│ └── js/ # JavaScript -├── classes/ # Clases principales -│ └── Database.php # Conexión a BD -├── config/ # Configuración -│ └── config.php # Configuración principal -├── database/ # Base de datos -│ └── schema.sql # Esquema de BD -├── services/ # Servicios del sistema -│ ├── WhatsAppService.php # Servicio WhatsApp -│ └── BotService.php # Lógica del bot -└── index.php # Página principal -``` - -## 📊 Base de Datos - -### Tablas Principales -- `users` - Usuarios del sistema -- `conversations` - Historial de mensajes -- `menus` - Configuración de menús -- `menu_options` - Opciones de cada menú -- `autoresponses` - Respuestas automáticas -- `system_config` - Configuración del sistema -- `webhook_logs` - Logs de webhooks - -## 🔧 Configuración Avanzada - -### Menús Personalizados -```sql --- Crear menú personalizado -INSERT INTO menus (name, title, description, is_root) VALUES -('mi_menu', '🏪 Mi Tienda', 'Menú de productos', TRUE); - --- Agregar opciones -INSERT INTO menu_options (menu_id, option_number, text, action_type, action_value) VALUES -(1, 1, '📱 Ver catálogo', 'message', 'Aquí tienes nuestro catálogo: www.mitienda.com'), -(1, 2, '🛒 Hacer pedido', 'message', 'Para hacer un pedido, envía el código del producto'), -(1, 0, '🔙 Volver', 'menu', 'main_menu'); -``` - -### Respuestas Automáticas -```sql --- Agregar respuesta automática -INSERT INTO autoresponses (trigger_type, trigger_value, response_text) VALUES -('keyword', 'precio', 'Para consultar precios, escribe *catalogo* o visita www.mitienda.com'), -('keyword', 'horarios', 'Atendemos de Lunes a Viernes de 8am a 6pm'); -``` - -## 🔐 Seguridad - -- Validación de tokens en webhook -- Escape de datos SQL -- Headers de seguridad HTTP -- Logs de todas las transacciones -- Autenticación de API - -## 🐛 Solución de Problemas - -### Error de Conexión a BD -```bash -# Verificar credenciales en config/config.php -# Verificar que el servidor MySQL esté activo -# Comprobar permisos del usuario de BD -``` - -### Webhook No Funciona -```bash -# Verificar que la URL sea accesible públicamente -# Comprobar que tenga SSL (HTTPS) -# Revisar logs en /api/get_logs.php -``` - -### Bot No Responde -```bash -# Verificar token de WhatsApp en configuración -# Comprobar que el webhook esté configurado -# Revisar logs de errores PHP -``` - -## 📞 Ejemplo de Uso Completo - -### 1. Configuración Inicial -```bash -# Bot configurado para empresa de servicios -# Menús: Servicios, Pagos, Soporte -# Respuestas automáticas para palabras clave -``` - -### 2. Flujo de Usuario -``` -Usuario: "Hola" -Bot: "¡Hola! 👋 Bienvenido a nuestro servicio automatizado. Escribe *menu* para ver las opciones disponibles." - -Usuario: "menu" -Bot: "📋 Menú Principal -1. 💼 Ver servicios disponibles -2. 💳 Realizar pago -3. 🆘 Contactar soporte -0. ❌ Salir" - -Usuario: "1" -Bot: "💼 Servicios -1. 📋 Consulta de información -2. 📞 Agendar cita -3. 💰 Cotizar servicio -0. 🔙 Volver al menú principal" - -Usuario: "2" -Bot: "Para agendar una cita, por favor proporciona tu nombre completo y tu disponibilidad horaria." -``` - -## 🎨 Personalización - -### Colores y Estilos -```css -/* Modificar en assets/css/styles.css */ -:root { - --primary-color: #25d366; /* Verde WhatsApp */ - --secondary-color: #128c7e; - --tertiary-color: #075e54; -} -``` - -### Mensajes del Bot -```sql --- Modificar mensaje de bienvenida -UPDATE system_config -SET config_value = 'Tu mensaje personalizado' -WHERE config_key = 'welcome_message'; -``` - -## 📈 Monitoreo - -- Dashboard en tiempo real con métricas -- Gráficos de actividad por día -- Estados de entrega de mensajes -- Logs detallados de todas las operaciones - -## 🤝 Soporte - -Para soporte técnico o consultas: -- Email: soporte@tuempresa.com -- WhatsApp: +57 1 234 5678 -- Web: www.tuempresa.com +**WhatsApp Bot Manager** es un panel administrativo para gestionar bots y envíos de WhatsApp. Desarrollado por U‑Site.app, pensado para instalaciones autohospedadas con PHP y MySQL. --- -**Desarrollado con ❤️ para facilitar la comunicación empresarial a través de WhatsApp** +## 🔧 Características principales -© 2025 - Sistema WhatsApp Bot Manager v1.0.0 \ No newline at end of file +- Gestión de conversaciones y plantillas +- Integración con WhatsApp Business API +- Control de usuarios y seguridad (protección contra fuerza bruta) +- Manejo de archivos multimedia y envíos +- Scripts de instalación y migración incluidos + +--- + +## ✅ Requisitos + +- PHP 8.0+ (extensiones: PDO/MySQL, mbstring, curl, openssl, json) +- MySQL/MariaDB +- Servidor web (Apache/Nginx) o PHP built-in para desarrollo + +--- + +## 🛠 Instalación rápida (desarrollo) + +1. Clonar el repositorio y entrar en la carpeta: + + ```bash + git clone whatsapp + cd whatsapp + ``` + +2. Configurar la base de datos en `config/config.php` o crear un archivo `.env` en la raíz. + +3. Ejecutar el instalador desde el navegador: `http://tu-servidor/install.php` y seguir los pasos. + +4. Para levantar un servidor de desarrollo rápido: + + ```bash + php -S 127.0.0.1:8000 -t . >/tmp/php-server.log 2>&1 & + # Abre: http://127.0.0.1:8000 + ``` + +--- + +## 🧭 Archivos y scripts útiles + +- `install.php`, `instalar_bd.php` — instalador y creación de tablas +- `create_media_table.sql`, `execute_media_table.php` — tabla de media +- `config/config.php` — configuración principal y carga de `.env` +- `run_tests.php` — pruebas básicas +- `deploy.sh` — script de despliegue (producción) +- `verify_*` y `diagnostico_*` — páginas de diagnóstico y verificación + +Consulta `README_SETUP.md`, `INSTALACION.md` y `DEPLOYMENT_GUIDE.md` para documentación extendida. + +--- + +## 🎨 Personalización de UI + +- Estilos principales: `assets/css/styles.css` +- Para ajustar el tamaño global de fuente cambia `html { font-size: ... }` o `body { font-size: ... }` + +--- + +## 🧪 Solución rápida de problemas + +- Logs del servidor integrado: `/tmp/php-server.log` +- Verificar tokens y configuración con: `verificar_servidor.php`, `verificar_token.php`, `verificar_whatsapp_config.php` +- Comprobar tablas con: `verify_media_table.php`, `check_table_structure.php` + +--- + +## � Notas de seguridad: directorios de pruebas + +Los recursos de pruebas y depuración (scripts `test_*`, `debug_*`, `diagnostico_*`, `tests/`, `scripts/` de prueba, etc.) han sido movidos al directorio `dev/`. **No** expongas `dev/` en entornos de producción. Recomendaciones: + +- Mantén `dev/` fuera del documento raíz público o protégelo con HTTP auth/.htaccess. +- Añade `dev/` y `tmp/` a `.gitignore` y rota credenciales que hayan estado en `CREDENCIALES_BASICO.txt`. +- Revisa y elimina cualquier endpoint que active modo `debug` vía parámetros en producción. + +--- + +## �📬 Soporte y créditos + +Desarrollado por **U‑Site.app** — https://u-site.app + +Para soporte, abre un issue o contacta al correo de soporte configurado en la app. + +--- + +*README corto y enfocado. Revisa los archivos de documentación para guías detalladas.* \ No newline at end of file diff --git a/api/_debug_health.php b/api/_debug_health.php deleted file mode 100644 index 1d18475..0000000 --- a/api/_debug_health.php +++ /dev/null @@ -1,4 +0,0 @@ - $_SERVER['REQUEST_URI'] ?? '']); -echo json_encode(['ok'=>true,'uri'=>$_SERVER['REQUEST_URI'] ?? '']); diff --git a/api/debug_webhook_init.php b/api/debug_webhook_init.php deleted file mode 100644 index acc2d72..0000000 --- a/api/debug_webhook_init.php +++ /dev/null @@ -1,16 +0,0 @@ - true, 'message' => 'Webhook class instantiated successfully']); -} catch (Throwable $t) { - http_response_code(500); - error_log('[debug_webhook_init] Throwable: ' . $t->getMessage()); - error_log($t->getTraceAsString()); - echo json_encode(['ok' => false, 'error' => $t->getMessage(), 'trace' => $t->getTraceAsString()]); -} diff --git a/api/test_send_template.php b/api/test_send_template.php deleted file mode 100644 index b35ee7d..0000000 --- a/api/test_send_template.php +++ /dev/null @@ -1,80 +0,0 @@ - $recipient, - 'template' => $template, - 'attempts' => [] -]; - -try { - $wh = new WhatsAppService(); - error_log("[test_send_template] WhatsAppService initialized"); -} catch (Exception $e) { - error_log("[test_send_template] ERROR init: " . $e->getMessage()); - $result['error'] = 'Error initializing WhatsAppService: ' . $e->getMessage(); - $result['trace'] = $e->getTraceAsString(); - echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - exit(1); -} - -foreach ($languages as $lang) { - error_log("[test_send_template] Attempting language: $lang"); - try { - $res = $wh->sendTemplateMessage($recipient, $template, $lang, []); - $result['attempts'][] = [ - 'language' => $lang, - 'success' => true, - 'response' => $res - ]; - error_log("[test_send_template] Success with language: $lang"); - // stop on first success - break; - } catch (Exception $e) { - $msg = $e->getMessage(); - error_log("[test_send_template] Attempt language $lang failed: " . $msg); - $result['attempts'][] = [ - 'language' => $lang, - 'success' => false, - 'error' => $msg, - 'trace' => $e->getTraceAsString() - ]; - // continuar con siguiente candidato - } -} - -echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - -} catch (Throwable $t) { - $err = '[test_send_template][FATAL] ' . $t->getMessage(); - error_log($err); - error_log($t->getTraceAsString()); - http_response_code(500); - echo json_encode(['error' => $t->getMessage(), 'trace' => $t->getTraceAsString()]); - exit(1); -} - -// Si es CLI, exit code 0 si algún intento fue exitoso -$anySuccess = count(array_filter($result['attempts'], fn($a) => $a['success'])) > 0; -exit($anySuccess ? 0 : 1); diff --git a/assets/css/styles.css b/assets/css/styles.css index 29542f4..7080a07 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -19,6 +19,16 @@ --info-color: #3b82f6; } +/* Ajuste global: reducir tamaño de letra base en toda la app */ +html { + font-size: 14px; /* Default 16px -> 14px para fuentes más pequeñas */ +} + +/* Ajuste base para compatibilidad con rem */ +body { + font-size: 0.95rem; +} + * { margin: 0; padding: 0; diff --git a/crear_datos_prueba.php b/crear_datos_prueba.php deleted file mode 100644 index 5c49a9d..0000000 --- a/crear_datos_prueba.php +++ /dev/null @@ -1,175 +0,0 @@ -"; -echo "🧪 Datos de Prueba"; -echo ""; - -echo "

🧪 CREADOR DE DATOS DE PRUEBA

"; - -try { - require_once 'config/config.php'; - - $pdo = new PDO( - "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET, - DB_USER, - DB_PASS, - [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] - ); - - echo "
✅ Conectado a la base de datos
"; - - // Verificar si ya hay datos - $stmt = $pdo->query("SELECT COUNT(*) as count FROM users"); - $userCount = $stmt->fetch()['count']; - - $stmt = $pdo->query("SELECT COUNT(*) as count FROM conversations"); - $convCount = $stmt->fetch()['count']; - - echo "
"; - echo "

📊 ESTADO ACTUAL

"; - echo "
Usuarios existentes: $userCount
"; - echo "
Conversaciones existentes: $convCount
"; - echo "
"; - - if ($_POST && isset($_POST['create_data'])) { - echo "
"; - echo "

🔨 CREANDO DATOS DE PRUEBA...

"; - - // Datos de usuarios de muestra - PERSONALIZA AQUÍ - $sampleUsers = [ - ['phone' => '+573168950803', 'name' => 'Usuario Principal'] - ]; - - $userIds = []; - - foreach ($sampleUsers as $user) { - // Verificar si el usuario ya existe - $stmt = $pdo->prepare("SELECT id FROM users WHERE phone_number = ?"); - $stmt->execute([$user['phone']]); - $existingUser = $stmt->fetch(); - - if ($existingUser) { - $userIds[] = $existingUser['id']; - echo "
⚠️ Usuario ya existe: {$user['name']} ({$user['phone']})
"; - } else { - $stmt = $pdo->prepare(" - INSERT INTO users (phone_number, name, status, created_at) - VALUES (?, ?, 'active', NOW()) - "); - $stmt->execute([$user['phone'], $user['name']]); - $userIds[] = $pdo->lastInsertId(); - echo "
✅ Usuario creado: {$user['name']} ({$user['phone']})
"; - } - } - - // Crear conversaciones de muestra - $sampleconversations = [ - ['text' => 'Hola, necesito información sobre sus servicios', 'direction' => 'incoming'], - ['text' => '¡Hola! Claro, te ayudo con gusto. ¿Qué tipo de servicio te interesa?', 'direction' => 'outgoing'], - ['text' => 'Estoy buscando precios de desarrollo web', 'direction' => 'incoming'], - ['text' => 'Perfecto, tenemos varios paquetes disponibles. Te envío la información.', 'direction' => 'outgoing'], - ['text' => 'Gracias, muy amables', 'direction' => 'incoming'], - ['text' => 'Buenos días, ¿están disponibles?', 'direction' => 'incoming'], - ['text' => '¡Buenos días! Sí, estamos aquí para ayudarte. ¿En qué podemos asistirte?', 'direction' => 'outgoing'], - ['text' => 'Quería hacer una consulta sobre horarios', 'direction' => 'incoming'], - ['text' => 'Hola! Me pueden ayudar con un problema técnico?', 'direction' => 'incoming'], - ['text' => 'Por supuesto! Cuéntanos qué problema tienes', 'direction' => 'outgoing'] - ]; - - $conversationsCreated = 0; - - foreach ($userIds as $index => $userId) { - // Crear 2-3 mensajes por usuario - $messageCount = rand(2, 4); - - for ($i = 0; $i < $messageCount; $i++) { - $messageIndex = ($index * $messageCount + $i) % count($sampleconversations); - $message = $sampleconversations[$messageIndex]; - - $messageData = [ - 'user_id' => $userId, - 'content' => $message['text'], - 'direction' => $message['direction'], - 'message_type' => 'text', - 'status' => $message['direction'] === 'outgoing' ? 'delivered' : 'sent', - 'created_at' => date('Y-m-d H:i:s', strtotime("-" . rand(1, 72) . " hours")) - ]; - - // Intentar insertar en conversations - try { - $stmt = $pdo->prepare(" - INSERT INTO conversations (user_id, content, direction, message_type, status, created_at) - VALUES (?, ?, ?, ?, ?, ?) - "); - $stmt->execute([ - $messageData['user_id'], - $messageData['content'], - $messageData['direction'], - $messageData['message_type'], - $messageData['status'], - $messageData['created_at'] - ]); - $conversationsCreated++; - } catch (Exception $e) { - // Si falla, intentar en conversations - try { - $stmt = $pdo->prepare(" - INSERT INTO conversations (user_id, message_text, direction, message_type, status, created_at) - VALUES (?, ?, ?, ?, ?, ?) - "); - $stmt->execute([ - $messageData['user_id'], - $messageData['content'], - $messageData['direction'], - $messageData['message_type'], - $messageData['status'], - $messageData['created_at'] - ]); - $conversationsCreated++; - } catch (Exception $e2) { - echo "
❌ Error creando mensaje: " . $e2->getMessage() . "
"; - } - } - } - } - - echo "
✅ Usuarios creados: " . count($userIds) . "
"; - echo "
✅ Mensajes/conversaciones creados: $conversationsCreated
"; - echo "
🎉 ¡Datos de prueba creados exitosamente!
"; - - echo "
SIGUIENTE PASO:
"; - echo "
• Visita: conversations.php
"; - echo "
• O ve al panel: index.php
"; - - echo "
"; - } - -} catch (Exception $e) { - echo "
❌ Error: " . htmlspecialchars($e->getMessage()) . "
"; -} - -if (!$_POST) { - echo "
"; - echo "

⚠️ CREAR DATOS DE PRUEBA

"; - echo "
Esto creará usuarios y conversaciones de muestra para probar el sistema
"; - echo "
Solo ejecuta esto si no tienes datos reales o quieres probar
"; - - echo "
"; - echo ""; - echo "
"; - echo "
"; -} - -echo ""; -?> \ No newline at end of file diff --git a/debug_install.php b/debug_install.php deleted file mode 100644 index ea9ae65..0000000 --- a/debug_install.php +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - 🔍 Diagnóstico de Instalación - - - - - -
-
-
-

Diagnóstico de Instalación WhatsApp Bot

-

Herramienta de depuración para identificar problemas

-
-
- - - -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
- - - -
-
WhatsApp Bot - Diagnóstico de Instalación
-
Fecha:
-
═══════════════════════════════════════════
- - 📋 Test 1: Verificando extensiones PHP...
"; - $requiredExtensions = ['pdo', 'pdo_mysql', 'json', 'curl', 'mbstring']; - foreach ($requiredExtensions as $ext) { - if (extension_loaded($ext)) { - echo "
✅ Extensión '$ext': Disponible
"; - } else { - echo "
❌ Extensión '$ext': NO disponible
"; - } - } - - // Test 2: Verificar archivos del sistema - echo "
📁 Test 2: Verificando archivos del sistema...
"; - $requiredFiles = [ - 'config/config.php' => 'Configuración principal', - 'classes/Database.php' => 'Clase Database', - 'database/schema.sql' => 'Esquema de BD', - 'api/webhook.php' => 'Webhook API' - ]; - foreach ($requiredFiles as $file => $desc) { - if (file_exists($file)) { - $size = filesize($file); - echo "
✅ $desc ($file): Existe ($size bytes)
"; - } else { - echo "
❌ $desc ($file): NO existe
"; - } - } - - // Test 3: Probar conexión a servidor MySQL - echo "
🔌 Test 3: Probando conexión a servidor MySQL...
"; - try { - $dsn = "mysql:host={$dbHost};port={$dbPort};charset=utf8mb4"; - $pdo_test = new PDO($dsn, $dbUser, $dbPass, [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_TIMEOUT => 5, - ]); - echo "
✅ Conexión al servidor MySQL: EXITOSA
"; - - // Obtener información del servidor - $version = $pdo_test->query("SELECT VERSION()")->fetchColumn(); - echo "
✅ Versión MySQL: $version
"; - - } catch (PDOException $e) { - echo "
❌ Error de conexión: " . $e->getMessage() . "
"; - echo "
💡 Posibles causas:
"; - echo "
- Credenciales incorrectas
"; - echo "
- MySQL no está corriendo
"; - echo "
- Firewall bloqueando conexión
"; - } - - // Test 4: Verificar acceso a base de datos específica - echo "
💾 Test 4: Verificando acceso a base de datos '$dbName'...
"; - try { - $dsn = "mysql:host={$dbHost};port={$dbPort};dbname={$dbName};charset=utf8mb4"; - $pdo_db = new PDO($dsn, $dbUser, $dbPass, [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_TIMEOUT => 5, - ]); - echo "
✅ Acceso a base de datos '$dbName': EXITOSO
"; - - // Verificar permisos - try { - $pdo_db->exec("CREATE TABLE test_permissions (id INT PRIMARY KEY)"); - $pdo_db->exec("DROP TABLE test_permissions"); - echo "
✅ Permisos CREATE/DROP: Disponibles
"; - } catch (PDOException $e) { - echo "
⚠️ Permisos limitados: " . $e->getMessage() . "
"; - } - - // Listar tablas existentes - $stmt = $pdo_db->query("SHOW TABLES"); - $tables = $stmt->fetchAll(PDO::FETCH_COLUMN); - if (count($tables) > 0) { - echo "
📋 Tablas existentes (" . count($tables) . "):
"; - foreach ($tables as $table) { - echo "
- $table
"; - } - } else { - echo "
⚠️ Base de datos vacía (sin tablas)
"; - } - - } catch (PDOException $e) { - echo "
❌ Error accediendo a BD '$dbName': " . $e->getMessage() . "
"; - echo "
💡 Posibles causas:
"; - echo "
- Base de datos no existe
"; - echo "
- Usuario sin permisos en esta BD
"; - echo "
- Nombre de BD incorrecto
"; - } - - // Test 5: Probar carga del archivo config.php - echo "
⚙️ Test 5: Probando carga de configuración...
"; - try { - if (file_exists('config/config.php')) { - $configContent = file_get_contents('config/config.php'); - - // Verificar constantes principales - $constants = ['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS']; - foreach ($constants as $constant) { - if (strpos($configContent, $constant) !== false) { - echo "
✅ Constante '$constant': Definida en config
"; - } else { - echo "
❌ Constante '$constant': NO encontrada
"; - } - } - - // Intentar incluir config (sin ejecutar) - echo "
✅ Archivo config.php: Legible
"; - } else { - echo "
❌ Archivo config.php: NO existe
"; - } - } catch (Exception $e) { - echo "
❌ Error leyendo config: " . $e->getMessage() . "
"; - } - - // Test 6: Verificar schema.sql - echo "
📄 Test 6: Verificando archivo schema.sql...
"; - try { - if (file_exists('database/schema.sql')) { - $schema = file_get_contents('database/schema.sql'); - $statements = explode(';', $schema); - $createTables = 0; - $insertData = 0; - - foreach ($statements as $statement) { - if (preg_match('/CREATE TABLE/i', $statement)) $createTables++; - if (preg_match('/INSERT INTO/i', $statement)) $insertData++; - } - - echo "
✅ Schema SQL: $createTables tablas, $insertData inserts
"; - - // Verificar tablas principales - $mainTables = ['users', 'conversations', 'menus', 'system_config']; - foreach ($mainTables as $table) { - if (strpos($schema, "CREATE TABLE $table") !== false) { - echo "
✅ Tabla '$table': Definida en schema
"; - } else { - echo "
⚠️ Tabla '$table': NO definida en schema
"; - } - } - } else { - echo "
❌ Archivo schema.sql: NO existe
"; - } - } catch (Exception $e) { - echo "
❌ Error leyendo schema: " . $e->getMessage() . "
"; - } - - echo "
═══════════════════════════════════════════
"; - echo "
Diagnóstico completado: " . date('H:i:s') . "
"; - ?> -
- -
-
🚀 Acciones sugeridas:
- -
- - - - - \ No newline at end of file diff --git a/debug_login.php b/debug_login.php deleted file mode 100644 index 95b7fa5..0000000 --- a/debug_login.php +++ /dev/null @@ -1,107 +0,0 @@ - \ No newline at end of file diff --git a/debug_login_process.php b/debug_login_process.php deleted file mode 100644 index af49229..0000000 --- a/debug_login_process.php +++ /dev/null @@ -1,68 +0,0 @@ - \ No newline at end of file diff --git a/debug_media_messages.php b/debug_media_messages.php deleted file mode 100644 index 01b9750..0000000 --- a/debug_media_messages.php +++ /dev/null @@ -1,64 +0,0 @@ -Últimos 10 mensajes multimedia en la BD"; -echo ""; - -$conversations = $db->fetchAll(" - SELECT - c.id, - c.user_id, - u.phone_number, - c.direction, - c.message_type, - c.content, - c.media_url, - c.created_at - FROM conversations c - LEFT JOIN users u ON c.user_id = u.id - WHERE c.message_type IN ('image', 'audio', 'video', 'document') - ORDER BY c.created_at DESC - LIMIT 10 -"); - -echo ""; -echo " - - - - - - - -"; - -foreach ($conversations as $msg) { - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; -} - -echo "
IDTeléfonoDirTipoContentMedia URLFecha
{$msg['id']}{$msg['phone_number']}{$msg['direction']}{$msg['message_type']}
" . htmlspecialchars(substr($msg['content'], 0, 200)) . "
" . htmlspecialchars(substr($msg['media_url'], 0, 100)) . "
" . date('H:i:s', strtotime($msg['created_at'])) . "
"; - -echo "

Análisis:

"; -echo ""; -?> diff --git a/debug_system.php b/debug_system.php deleted file mode 100644 index 46fcf7d..0000000 --- a/debug_system.php +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - WhatsApp Bot Manager - Debug - - - - -
-

🔧 WhatsApp Bot Manager - Debug

- -
-
-
-
-
📡 Test de Conectividad
-
-
- - - -
-
-
- -
-
-
💾 Guardar Config de Prueba
-
-
-
- -
-
- -
- -
-
-
-
- -
-
-
-
📊 Estado del Sistema
-
-
-
-
-
-

Verificando estado...

-
-
-
-
- -
-
-
⚙️ Configuraciones Actuales
-
-
- -
-

Haz clic en "Cargar" para ver las configuraciones

-
-
-
-
-
- -
-
-
📝 Log de Debug
-
-
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/diagnostico_500.php b/diagnostico_500.php deleted file mode 100644 index 4b9a847..0000000 --- a/diagnostico_500.php +++ /dev/null @@ -1,272 +0,0 @@ -"; -echo "🚨 Diagnóstico Error 500"; -echo ""; - -echo "

🚨 DIAGNÓSTICO ERROR 500 - WhatsApp Bot

"; -echo "

Fecha: " . date('Y-m-d H:i:s') . "

"; - -// 1. TEST BÁSICO PHP -echo "

📍 1. TEST BÁSICO PHP

"; -echo "
✅ PHP está funcionando (versión: " . phpversion() . ")
"; - -if (version_compare(phpversion(), '7.4', '>=')) { - echo "
✅ Versión PHP compatible
"; -} else { - echo "
❌ Versión PHP muy antigua (requiere 7.4+)
"; -} -echo "
"; - -// 2. TEST ARCHIVOS CRÍTICOS -echo "

📂 2. ARCHIVOS CRÍTICOS

"; -$critical_files = [ - 'config/config.php' => 'Configuración principal', - 'classes/Database.php' => 'Clase Database', - 'index.php' => 'Dashboard principal', - 'login.php' => 'Sistema de login', - '.htaccess' => 'Configuración Apache' -]; - -foreach ($critical_files as $file => $desc) { - if (file_exists($file)) { - echo "
✅ $file ($desc)
"; - - // Test de carga PHP (sin shell_exec) - if (str_ends_with($file, '.php')) { - try { - // Intentar incluir el archivo para verificar errores - if ($file === 'config/config.php') { - // Test especial para config.php - ob_start(); - $error_before = error_get_last(); - include_once $file; - $output = ob_get_clean(); - $error_after = error_get_last(); - - if ($error_after && $error_after !== $error_before) { - echo "
→ ❌ Error en archivo: " . htmlspecialchars($error_after['message']) . "
"; - } elseif (!empty($output)) { - echo "
→ ⚠️ Archivo produce output: " . htmlspecialchars(substr($output, 0, 100)) . "
"; - } else { - echo "
→ ✅ Archivo se carga correctamente
"; - } - } else { - echo "
→ ✅ Archivo PHP existe
"; - } - } catch (ParseError $e) { - echo "
→ ❌ ERROR SINTAXIS: " . htmlspecialchars($e->getMessage()) . "
"; - } catch (Error $e) { - echo "
→ ❌ ERROR: " . htmlspecialchars($e->getMessage()) . "
"; - } catch (Exception $e) { - echo "
→ ⚠️ ADVERTENCIA: " . htmlspecialchars($e->getMessage()) . "
"; - } - } - } else { - echo "
❌ FALTA: $file ($desc)
"; - } -} -echo "
"; - -// 3. TEST PERMISOS -echo "

🔐 3. PERMISOS

"; -$dirs_permissions = [ - 'logs/' => ['required' => '777', 'writable' => true], - 'uploads/' => ['required' => '777', 'writable' => true], - 'config/' => ['required' => '755', 'writable' => false], - '.' => ['required' => '755', 'writable' => false] -]; - -foreach ($dirs_permissions as $dir => $config) { - if (is_dir($dir)) { - $perms = substr(sprintf('%o', fileperms($dir)), -3); - echo "
✅ $dir existe (permisos: $perms)
"; - - if ($config['writable'] && !is_writable($dir)) { - echo "
→ ❌ No es escribible (necesita chmod 777)
"; - } - } else { - echo "
❌ Directorio $dir no existe
"; - } -} -echo "
"; - -// 4. TEST CONFIGURACIÓN -echo "

⚙️ 4. CONFIGURACIÓN

"; -if (file_exists('config/config.php')) { - try { - ob_start(); - include_once 'config/config.php'; - $config_output = ob_get_clean(); - - if (!empty($config_output)) { - echo "
❌ config.php produce output: " . htmlspecialchars($config_output) . "
"; - } else { - echo "
✅ config.php se carga sin errores
"; - } - - // Verificar constantes básicas - $constants = ['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS']; - foreach ($constants as $const) { - if (defined($const)) { - $value = constant($const); - $display_value = ($const === 'DB_PASS') ? '***' : $value; - echo "
✅ $const = '$display_value'
"; - } else { - echo "
❌ Constante $const no definida
"; - } - } - - } catch (Exception $e) { - echo "
❌ Error cargando config.php: " . htmlspecialchars($e->getMessage()) . "
"; - } catch (Error $e) { - echo "
❌ Error fatal en config.php: " . htmlspecialchars($e->getMessage()) . "
"; - } -} else { - echo "
❌ config/config.php no encontrado
"; -} -echo "
"; - -// 5. TEST BASE DE DATOS -echo "

🗄️ 5. BASE DE DATOS

"; -if (defined('DB_HOST') && defined('DB_NAME')) { - try { - $dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4"; - $pdo = new PDO($dsn, DB_USER, DB_PASS, [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC - ]); - echo "
✅ Conexión a base de datos exitosa
"; - - // Verificar tablas principales - $tables = ['conversations', 'conversations', 'system_config']; - foreach ($tables as $table) { - $stmt = $pdo->query("SHOW TABLES LIKE '$table'"); - if ($stmt->rowCount() > 0) { - echo "
✅ Tabla '$table' existe
"; - } else { - echo "
⚠️ Tabla '$table' no existe
"; - } - } - - } catch (PDOException $e) { - echo "
❌ Error BD: " . htmlspecialchars($e->getMessage()) . "
"; - } -} else { - echo "
❌ Configuración de BD incompleta
"; -} -echo "
"; - -// 6. TEST .HTACCESS -echo "

🛡️ 6. .HTACCESS

"; -if (file_exists('.htaccess')) { - $htaccess_size = filesize('.htaccess'); - echo "
✅ .htaccess existe ($htaccess_size bytes)
"; - - // Verificar sintaxis básica - $htaccess_content = file_get_contents('.htaccess'); - $problematic_lines = []; - - // Buscar sintaxis antigua que puede causar problemas - if (strpos($htaccess_content, 'Order Allow,Deny') !== false) { - $problematic_lines[] = "Sintaxis antigua 'Order Allow,Deny' (usar 'Require all denied')"; - } - - if (strpos($htaccess_content, 'Allow from') !== false) { - $problematic_lines[] = "Sintaxis antigua 'Allow from' (usar 'Require ip')"; - } - - if (empty($problematic_lines)) { - echo "
✅ Sintaxis .htaccess parece correcta
"; - } else { - foreach ($problematic_lines as $issue) { - echo "
❌ $issue
"; - } - } -} else { - echo "
❌ .htaccess no encontrado
"; -} -echo "
"; - -// 7. TEST MÓDULOS APACHE -echo "

🔧 7. EXTENSIONES PHP

"; - -$required_extensions = ['pdo', 'pdo_mysql', 'curl', 'json', 'mbstring', 'openssl']; -foreach ($required_extensions as $ext) { - if (extension_loaded($ext)) { - echo "
✅ Extensión '$ext' habilitada
"; - } else { - echo "
❌ Extensión '$ext' NO habilitada
"; - } -} - -// Verificar funciones deshabilitadas -$dangerous_functions = ['shell_exec', 'exec', 'system', 'passthru']; -$disabled_functions = explode(',', ini_get('disable_functions')); -$disabled_functions = array_map('trim', $disabled_functions); - -echo "
Funciones del sistema:
"; -foreach ($dangerous_functions as $func) { - if (in_array($func, $disabled_functions)) { - echo "
⚠️ $func() deshabilitada (normal en hosting)
"; - } else { - echo "
✅ $func() habilitada
"; - } -} -echo "
"; - -// 8. INFORMACIÓN DEL SERVIDOR -echo "

🖥️ 8. INFO SERVIDOR

"; -echo "
Servidor: " . ($_SERVER['SERVER_SOFTWARE'] ?? 'Desconocido') . "
"; -echo "
PHP: " . PHP_VERSION . "
"; -echo "
SAPI: " . php_sapi_name() . "
"; -echo "
Directorio: " . __DIR__ . "
"; -echo "
Usuario: " . get_current_user() . "
"; - -// Límites PHP importantes -$limits = [ - 'memory_limit' => ini_get('memory_limit'), - 'max_execution_time' => ini_get('max_execution_time'), - 'upload_max_filesize' => ini_get('upload_max_filesize'), - 'post_max_size' => ini_get('post_max_size') -]; - -foreach ($limits as $setting => $value) { - echo "
$setting: $value
"; -} -echo "
"; - -// 9. SUGERENCIAS DE SOLUCIÓN -echo "

🔧 9. ACCIONES RECOMENDADAS

"; - -echo "
Si hay errores arriba, prueba estas soluciones:
"; -echo "
1. Error sintaxis PHP: Corregir archivo mostrado
"; -echo "
2. Permisos: chmod 777 logs/ uploads/
"; -echo "
3. Base datos: Verificar credenciales en config.php
"; -echo "
4. .htaccess: Renombrar temporalmente a .htaccess.bak
"; -echo "
5. PHP version: Cambiar a PHP 8.1+ en panel
"; - -echo "
TESTS RÁPIDOS:
"; -echo "
• Renombra .htaccess → .htaccess.bak y prueba
"; -echo "
• Ve a login.php directamente
"; -echo "
• Revisa error log del servidor
"; -echo "
"; - -echo "

📱 10. CONTACTO

"; -echo "
Si necesitas ayuda, envía screenshot de este diagnóstico
"; -echo "
"; - -echo ""; -?> \ No newline at end of file diff --git a/diagnostico_api.php b/diagnostico_api.php deleted file mode 100644 index 9027a41..0000000 --- a/diagnostico_api.php +++ /dev/null @@ -1,237 +0,0 @@ -"; -echo "🔧 Diagnóstico API"; -echo ""; - -echo "

🔧 DIAGNÓSTICO DE LA API

"; -echo "
Ejecutándose desde: " . __DIR__ . "
"; - -// 1. Verificar estructura de carpetas -echo "
"; -echo "

📁 ESTRUCTURA DE ARCHIVOS...

"; - -// Verificar que existe la carpeta api -if (!is_dir('api')) { - echo "
❌ CRÍTICO: Carpeta 'api' no encontrada
"; - exit; -} -echo "
✅ Carpeta 'api' encontrada
"; - -// Verificar config desde diferentes ubicaciones -$config_paths = [ - 'config/config.php' => 'Ruta desde raíz', - 'api/../config/config.php' => 'Ruta desde API (relativa)', - '../config/config.php' => 'Ruta desde API (parent)' -]; - -foreach ($config_paths as $path => $desc) { - if (file_exists($path)) { - echo "
✅ $desc: $path existe
"; - } else { - echo "
❌ $desc: $path NO existe
"; - } -} - -echo "
"; - -// 2. Listar archivos de la API -echo "
"; -echo "

📋 ARCHIVOS DE LA API...

"; - -$api_files = glob('api/*.php'); -if (empty($api_files)) { - echo "
❌ No se encontraron archivos PHP en la API
"; - exit; -} - -echo "
✅ Encontrados " . count($api_files) . " archivos en la API:
"; -foreach ($api_files as $file) { - $filename = basename($file); - $size = filesize($file); - $readable = is_readable($file) ? '✅' : '❌'; - echo "
$readable $filename ($size bytes)
"; -} - -echo "
"; - -// 3. Analizar el primer archivo de API -echo "
"; -echo "

🔍 ANÁLISIS DEL PRIMER ARCHIVO...

"; - -$test_file = $api_files[0]; -$filename = basename($test_file); - -echo "
Analizando: $filename
"; - -$content = file_get_contents($test_file); -if (!$content) { - echo "
❌ No se pudo leer el archivo
"; -} else { - // Buscar require_once - if (preg_match('/require_once\s+[\'"]([^\'"]+)[\'"]/', $content, $matches)) { - $required_path = $matches[1]; - echo "
🔗 Requiere: $required_path
"; - - // Verificar si la ruta existe desde la perspectiva del archivo API - $full_path_from_api = 'api/' . $required_path; - $parent_path_from_api = dirname('api/' . basename($test_file)) . '/' . $required_path; - - if (file_exists($required_path)) { - echo "
✅ Ruta directa '$required_path' existe
"; - } elseif (file_exists($full_path_from_api)) { - echo "
⚠️ Ruta '$required_path' existe como '$full_path_from_api'
"; - } else { - echo "
❌ Ruta '$required_path' NO existe
"; - echo "
❌ Tampoco existe como '$full_path_from_api'
"; - } - - } else { - echo "
⚠️ No se encontró require_once en el archivo
"; - } -} - -echo "
"; - -// 4. Test de carga de configuración DESDE la API -echo "
"; -echo "

⚙️ TEST CARGA CONFIG DESDE API...

"; - -// Simular estar en la carpeta API -$original_dir = getcwd(); - -try { - // Cambiar al directorio API - chdir('api'); - echo "
📁 Cambiado a directorio: " . getcwd() . "
"; - - // Intentar cargar configuración con diferentes rutas - $config_attempts = [ - '../config/config.php' => 'Ruta parent (.../config/config.php)', - 'config/config.php' => 'Ruta directa (config/config.php)', - '../config.php' => 'Config en raíz (../config.php)' - ]; - - $config_loaded = false; - - foreach ($config_attempts as $path => $desc) { - echo "
🔄 Probando: $desc
"; - - if (file_exists($path)) { - echo "
✅ Archivo existe: $path
"; - - try { - // Capturar errores - ob_start(); - $error_before = error_get_last(); - - include_once $path; - - $output = ob_get_clean(); - $error_after = error_get_last(); - - if ($error_after && $error_after !== $error_before) { - echo "
❌ Error cargando: " . htmlspecialchars($error_after['message']) . "
"; - } else { - echo "
✅ Config cargado desde: $path
"; - $config_loaded = true; - - // Verificar algunas constantes - $test_constants = ['DB_HOST', 'DB_NAME', 'DB_USER']; - foreach ($test_constants as $const) { - if (defined($const)) { - echo "
✅ $const definido
"; - } else { - echo "
❌ $const no definido
"; - } - } - break; - } - - } catch (Exception $e) { - echo "
❌ Excepción: " . htmlspecialchars($e->getMessage()) . "
"; - } - } else { - echo "
❌ Archivo no existe: $path
"; - } - } - - if (!$config_loaded) { - echo "
❌ CRÍTICO: No se pudo cargar ninguna configuración
"; - } - -} catch (Exception $e) { - echo "
❌ Error general: " . htmlspecialchars($e->getMessage()) . "
"; -} finally { - // Volver al directorio original - chdir($original_dir); - echo "
📁 Vuelto a directorio: " . getcwd() . "
"; -} - -echo "
"; - -// 5. Revisar el .htaccess -echo "
"; -echo "

🔧 VERIFICACIÓN .HTACCESS...

"; - -if (file_exists('.htaccess')) { - echo "
✅ .htaccess existe en raíz
"; - $htaccess_content = file_get_contents('.htaccess'); - - // Verificar reglas que pueden afectar a la API - if (strpos($htaccess_content, 'RewriteEngine On') !== false) { - echo "
✅ RewriteEngine habilitado
"; - } - - if (strpos($htaccess_content, 'api/') !== false) { - echo "
⚠️ Hay reglas específicas para api/
"; - preg_match_all('/.*api.*/', $htaccess_content, $api_rules); - foreach ($api_rules[0] as $rule) { - echo "
📋 " . htmlspecialchars(trim($rule)) . "
"; - } - } -} else { - echo "
❌ .htaccess no encontrado
"; -} - -// Verificar .htaccess en API -if (file_exists('api/.htaccess')) { - echo "
⚠️ .htaccess existe en api/
"; -} else { - echo "
✅ No hay .htaccess específico en api/
"; -} - -echo "
"; - -// 6. Resumen y recomendaciones -echo "
"; -echo "

📊 DIAGNÓSTICO Y RECOMENDACIONES

"; - -echo "
PROBLEMA IDENTIFICADO:
"; -echo "
Los archivos de la API están buscando 'config/config.php' desde su propia carpeta
"; -echo "
Pero config.php está en '../config/config.php' desde la perspectiva de la API
"; - -echo "
SOLUCIONES:
"; -echo "
1. Cambiar todas las rutas en api/ de 'config/config.php' a '../config/config.php'
"; -echo "
2. O crear un archivo api/config.php que incluya '../config/config.php'
"; -echo "
3. Verificar permisos de archivos
"; - -echo "
ARCHIVO CORRECTOR AUTOMÁTICO:
"; -echo "
Necesitas ejecutar un script que corrija todas las rutas en los archivos API
"; - -echo "
"; - -echo ""; -?> \ No newline at end of file diff --git a/diagnostico_envio.php b/diagnostico_envio.php deleted file mode 100644 index 89b73da..0000000 --- a/diagnostico_envio.php +++ /dev/null @@ -1,105 +0,0 @@ -🔍 Diagnóstico del Sistema de Envío de Mensajes"; - -echo "

1. Configuración de WhatsApp

"; -echo "TOKEN: " . (WHATSAPP_TOKEN !== 'TU_TOKEN_DE_WHATSAPP_AQUI' ? '✅ Configurado' : '❌ No configurado') . "
"; -echo "PHONE_NUMBER_ID: " . (WHATSAPP_PHONE_NUMBER_ID !== 'TU_PHONE_ID_AQUI' ? '✅ Configurado' : '❌ No configurado') . "
"; -echo "API_URL: " . WHATSAPP_API_URL . "
"; -echo "WEBHOOK_TOKEN: " . (WEBHOOK_VERIFY_TOKEN !== 'mi_token_secreto_123' ? '✅ Configurado' : '❌ Token por defecto') . "
"; - -echo "

2. Conexión a Base de Datos

"; -try { - $db = Database::getInstance(); - echo "✅ Conexión exitosa
"; - - // Verificar tablas - $tables = ['users', 'conversations', 'conversations']; - foreach ($tables as $table) { - try { - $stmt = $db->query("SHOW TABLES LIKE '$table'"); - if ($stmt->rowCount() > 0) { - echo "✅ Tabla '$table' existe
"; - - // Contar registros - $stmt = $db->query("SELECT COUNT(*) as total FROM $table"); - $count = $stmt->fetch()['total']; - echo "  📊 Registros: $count
"; - } else { - echo "❌ Tabla '$table' no existe
"; - } - } catch (Exception $e) { - echo "❌ Error verificando tabla '$table': " . $e->getMessage() . "
"; - } - } -} catch (Exception $e) { - echo "❌ Error de conexión: " . $e->getMessage() . "
"; -} - -echo "

3. Clases del Sistema

"; -$classes = ['Database', 'WhatsAppService', 'BotService']; -foreach ($classes as $class) { - if (class_exists($class)) { - echo "✅ Clase '$class' cargada
"; - } else { - echo "❌ Clase '$class' no encontrada
"; - } -} - -echo "

4. Archivos del Sistema

"; -$files = [ - 'config/config.php', - 'classes/Database.php', - 'services/WhatsAppService.php', - 'api/send_message.php', - 'api/webhook.php' -]; - -foreach ($files as $file) { - if (file_exists($file)) { - echo "✅ $file
"; - } else { - echo "❌ $file no encontrado
"; - } -} - -echo "

5. Test de Envío (simulado)

"; -try { - if (class_exists('WhatsAppService')) { - $whatsapp = new WhatsAppService(); - echo "✅ WhatsAppService creado
"; - - // Verificar que los tokens estén configurados - if (WHATSAPP_TOKEN !== 'TU_TOKEN_DE_WHATSAPP_AQUI' && WHATSAPP_PHONE_NUMBER_ID !== 'TU_PHONE_ID_AQUI') { - echo "⚠️ Tokens configurados - Ready para enviar mensajes
"; - } else { - echo "❌ Tokens no configurados - No se pueden enviar mensajes
"; - } - } -} catch (Exception $e) { - echo "❌ Error creando WhatsAppService: " . $e->getMessage() . "
"; -} - -echo "

6. Logs del Sistema

"; -$logFile = 'logs/system.log'; -if (file_exists($logFile)) { - echo "✅ Archivo de logs existe
"; - $logs = file_get_contents($logFile); - $lines = array_slice(explode("\n", $logs), -10); // Últimas 10 líneas - echo "
";
-    echo implode("\n", $lines);
-    echo "
"; -} else { - echo "⚠️ No hay logs disponibles
"; -} - -echo "
"; -echo "

✅ Si todos los elementos muestran ✅, el sistema está listo.

"; -echo "

❌ Si hay elementos con ❌, necesitan ser corregidos.

"; -?> \ No newline at end of file diff --git a/diagnostico_login.php b/diagnostico_login.php deleted file mode 100644 index e3e0da5..0000000 --- a/diagnostico_login.php +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - - 🔧 Diagnóstico de Login - WhatsApp Bot Manager - - - -
-

🔧 Diagnóstico Completo de Login

- -
- ⚠️ Herramienta de Emergencia: -

Esta utilidad diagnostica y soluciona problemas de login del sistema WhatsApp Bot Manager.

-
- - '; - echo '

📋 Estado Actual del Sistema:

'; - echo '

Usuario administrador: ' . ADMIN_USERNAME . '

'; - echo '

Hash actual: ' . substr(ADMIN_PASSWORD, 0, 60) . '...

'; - echo '

Timeout de sesión: ' . (SESSION_TIMEOUT/60) . ' minutos

'; - echo '

Intentos máximos: ' . MAX_LOGIN_ATTEMPTS . ' intentos

'; - echo '

Tiempo de bloqueo: ' . (LOGIN_LOCKOUT_TIME/60) . ' minutos

'; - echo '
'; - - // Verificar si hay bloqueos activos - $attemptsFile = '.login_attempts.json'; - if (file_exists($attemptsFile)) { - $attempts = json_decode(file_get_contents($attemptsFile), true); - if (!empty($attempts)) { - echo '
'; - echo '

🚫 Bloqueos Activos Detectados:

'; - foreach ($attempts as $ip => $data) { - echo '

IP: ' . $ip . ' - Intentos: ' . $data['attempts'] . ' - Bloqueado hasta: ' . date('Y-m-d H:i:s', $data['locked_until']) . '

'; - } - echo '
'; - } - } - - // Procesar acciones - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - if (isset($_POST['action'])) { - - // Verificar contraseña - if ($_POST['action'] === 'test_password') { - $testPassword = $_POST['test_password'] ?? ''; - echo '
'; - echo '

🔍 Resultado de la Prueba:

'; - echo '

Contraseña probada: ' . htmlspecialchars($testPassword) . '

'; - echo '

Hash almacenado: ' . ADMIN_PASSWORD . '

'; - - if (password_verify($testPassword, ADMIN_PASSWORD)) { - echo '
CORRECTO: La contraseña funciona perfectamente.
'; - echo '

Diagnóstico: El problema no es la contraseña. Revisa:

'; - echo ''; - } else { - echo '
INCORRECTO: La contraseña no coincide con el hash.
'; - echo '

Diagnóstico: El hash en la base de datos no corresponde a la contraseña ingresada.

'; - } - echo '
'; - } - - // Resetear contraseña a "admin123" - if ($_POST['action'] === 'reset_admin123') { - $defaultPassword = 'admin123'; - $newHash = password_hash($defaultPassword, PASSWORD_DEFAULT); - - // Leer el archivo config.php - $configFile = 'config/config.php'; - $configContent = file_get_contents($configFile); - - // Reemplazar la línea del password - $pattern = "/define\('ADMIN_PASSWORD', '[^']*'\);/"; - $replacement = "define('ADMIN_PASSWORD', '" . $newHash . "');"; - $newConfigContent = preg_replace($pattern, $replacement, $configContent); - - if (file_put_contents($configFile, $newConfigContent)) { - echo '
Contraseña reseteada exitosamente!
'; - echo '
🔑 Credenciales de acceso:
'; - echo 'Usuario: admin
'; - echo 'Contraseña: admin123
'; - echo 'URL de login: login.php
'; - - // Log del cambio - $logEntry = "[" . date('Y-m-d H:i:s') . "] Password reset to default from IP: " . $clientIP . PHP_EOL; - file_put_contents('logs/system.log', $logEntry, FILE_APPEND | LOCK_EX); - } else { - echo '
❌ Error al actualizar el archivo de configuración.
'; - } - } - - // Limpiar bloqueos - if ($_POST['action'] === 'clear_blocks') { - if (file_exists($attemptsFile)) { - unlink($attemptsFile); - echo '
✅ Todos los bloqueos de IP han sido eliminados.
'; - } else { - echo '
⚠️ No hay bloqueos activos para eliminar.
'; - } - } - - // Contraseña personalizada - if ($_POST['action'] === 'set_custom_password') { - $newPassword = $_POST['new_password'] ?? ''; - $confirmPassword = $_POST['confirm_password'] ?? ''; - - if (empty($newPassword) || empty($confirmPassword)) { - echo '
❌ Todos los campos son requeridos.
'; - } elseif ($newPassword !== $confirmPassword) { - echo '
❌ Las contraseñas no coinciden.
'; - } elseif (strlen($newPassword) < 4) { - echo '
❌ La contraseña debe tener al menos 4 caracteres.
'; - } else { - $newHash = password_hash($newPassword, PASSWORD_DEFAULT); - - // Leer y actualizar config.php - $configFile = 'config/config.php'; - $configContent = file_get_contents($configFile); - - $pattern = "/define\('ADMIN_PASSWORD', '[^']*'\);/"; - $replacement = "define('ADMIN_PASSWORD', '" . $newHash . "');"; - $newConfigContent = preg_replace($pattern, $replacement, $configContent); - - if (file_put_contents($configFile, $newConfigContent)) { - echo '
Contraseña personalizada establecida!
'; - echo '
🔑 Nueva contraseña: ' . htmlspecialchars($newPassword) . '
'; - } else { - echo '
❌ Error al actualizar el archivo de configuración.
'; - } - } - } - } - } - ?> - - -
-

🔍 Probar Contraseña Actual

-
- - - Escribe la contraseña que crees que debería funcionar -
- - -
- -
- - -
-

🚨 Reset de Emergencia

-

Esto establecerá la contraseña por defecto: admin123

- - -
- -
- - -
-

🔧 Establecer Contraseña Personalizada

-
- - -
-
- - -
- - -
- -
- - -
-

🗑️ Limpiar Bloqueos de IP

-

Elimina todos los bloqueos por intentos de login fallidos

- - -
- -
-

📋 Pasos Recomendados:

-
    -
  1. Prueba la contraseña que crees que debería funcionar
  2. -
  3. Si no funciona, usa Reset de Emergencia (admin123)
  4. -
  5. Limpia los bloqueos si tu IP está bloqueada
  6. -
  7. Prueba el login en login.php
  8. -
  9. Cambia la contraseña por una segura una vez dentro
  10. -
-
- -
-

🔒 Recordatorios de Seguridad:

- -
- - - \ No newline at end of file diff --git a/diagnostico_login_avanzado.php b/diagnostico_login_avanzado.php deleted file mode 100644 index f8dd747..0000000 --- a/diagnostico_login_avanzado.php +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - 🔍 Diagnóstico de Login Avanzado - WhatsApp Bot Manager - - - -
-

🔍 Diagnóstico de Login Avanzado

- - '; - echo '

✅ Conexión a Base de Datos: EXITOSA

'; - echo '
'; - - // Verificar estado del sistema - $isMigrated = isAuthSystemMigrated(); - - echo '
'; - echo '

📊 Estado del Sistema de Autenticación

'; - echo '

Sistema migrado a BD: ' . ($isMigrated ? '✅ SÍ' : '❌ NO - Sistema Legacy') . '

'; - - if ($isMigrated) { - echo '

Tabla admin_users: ✅ Existe

'; - - // Contar administradores - $stmt = $pdo->query("SELECT COUNT(*) as total, COUNT(CASE WHEN is_active = 1 THEN 1 END) as active FROM admin_users"); - $counts = $stmt->fetch(); - - echo '

Total administradores: ' . $counts['total'] . '

'; - echo '

Administradores activos: ' . $counts['active'] . '

'; - } else { - echo '

Usuario por defecto: admin (sistema hardcodeado)

'; - } - echo '
'; - - // Mostrar lista de administradores si existe la tabla - if ($isMigrated) { - echo '

👥 Lista de Administradores en Base de Datos

'; - - $stmt = $pdo->query("SELECT * FROM admin_users ORDER BY created_at DESC"); - $admins = $stmt->fetchAll(); - - if (empty($admins)) { - echo '
⚠️ No hay administradores en la base de datos
'; - } else { - echo ''; - echo ''; - - foreach ($admins as $admin) { - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
IDUsuarioNombreEmailEstadoCreadoÚltimo Login
' . $admin['id'] . '' . htmlspecialchars($admin['username']) . '' . htmlspecialchars($admin['full_name'] ?? 'Sin nombre') . '' . htmlspecialchars($admin['email'] ?? 'Sin email') . '✅ Activo' : 'inactive">❌ Inactivo') . '' . ($admin['created_at'] ?? 'No disponible') . '' . ($admin['last_login'] ?? 'Nunca') . '
'; - } - } - - // Procesar test de login - if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['test_user'])) { - $testUsername = trim($_POST['username']); - $testPassword = $_POST['password']; - - echo '
'; - echo '

🔍 Resultado del Diagnóstico de Login

'; - echo 'Usuario probado: ' . htmlspecialchars($testUsername) . '
'; - echo 'Fecha/Hora: ' . date('Y-m-d H:i:s') . '

'; - - // Probar autenticación - $authResult = authenticateUser($testUsername, $testPassword); - - if ($authResult) { - echo '
✅ LOGIN EXITOSO

'; - echo 'Datos del usuario autenticado:
'; - echo '- ID: ' . $authResult['id'] . '
'; - echo '- Usuario: ' . htmlspecialchars($authResult['username']) . '
'; - echo '- Nombre: ' . htmlspecialchars($authResult['full_name']) . '
'; - echo '- Email: ' . htmlspecialchars($authResult['email']) . '
'; - } else { - echo '
❌ LOGIN FALLIDO

'; - - // Diagnóstico detallado - if ($isMigrated) { - $stmt = $pdo->prepare("SELECT * FROM admin_users WHERE username = ?"); - $stmt->execute([$testUsername]); - $user = $stmt->fetch(); - - if (!$user) { - echo 'Razón: Usuario "' . htmlspecialchars($testUsername) . '" no existe en BD
'; - } elseif (!$user['is_active']) { - echo 'Razón: Usuario existe pero está INACTIVO
'; - } else { - echo 'Razón: Usuario existe y está activo, pero la contraseña es incorrecta
'; - - // Mostrar información del usuario encontrado - echo '
Información del usuario en BD:
'; - echo '- ID: ' . $user['id'] . '
'; - echo '- Usuario: ' . htmlspecialchars($user['username']) . '
'; - echo '- Nombre: ' . htmlspecialchars($user['full_name'] ?? 'Sin nombre') . '
'; - echo '- Activo: ' . ($user['is_active'] ? 'Sí' : 'No') . '
'; - echo '- Hash de contraseña: ' . substr($user['password_hash'], 0, 20) . '...
'; - } - } else { - if ($testUsername !== 'admin') { - echo 'Razón: Sistema legacy - solo acepta usuario "admin"
'; - } else { - echo 'Razón: Usuario correcto pero contraseña incorrecta
'; - echo 'Sistema: Legacy (hardcodeado)
'; - echo 'Hash esperado: ' . substr(ADMIN_PASSWORD, 0, 20) . '...
'; - } - } - } - - echo '
'; - } - - } catch (Exception $e) { - echo '
'; - echo '

❌ Error de Sistema

'; - echo '

Error: ' . htmlspecialchars($e->getMessage()) . '

'; - echo '
'; - } - ?> - -

🧪 Probar Credenciales de Login

- -
-
- - -
-
- - -
- - -
- -
-

🌐 Enlaces del Sistema:

-

Login oficial: login.php

-

Panel principal: index.php

-

Gestión de admins: gestion_admin_unificada.php

-

Migración a BD: migrate_to_database_auth.php

-
- -
-

⚠️ Notas de Seguridad:

- -
- - - \ No newline at end of file diff --git a/diagnostico_whatsapp.php b/diagnostico_whatsapp.php deleted file mode 100644 index 5a1d8d0..0000000 --- a/diagnostico_whatsapp.php +++ /dev/null @@ -1,264 +0,0 @@ -"; -echo "🔐 Diagnóstico Token WhatsApp"; -echo ""; - -echo "

🔐 DIAGNÓSTICO TOKEN WHATSAPP

"; - -// Cargar configuración -try { - require_once 'config/config.php'; -} catch (Exception $e) { - echo "
❌ Error cargando configuración: " . htmlspecialchars($e->getMessage()) . "
"; - exit; -} - -// 1. Verificar constantes de WhatsApp -echo "
"; -echo "

📋 CONFIGURACIÓN ACTUAL...

"; - -$whatsapp_constants = [ - 'WHATSAPP_TOKEN' => 'Token de acceso de WhatsApp', - 'WHATSAPP_PHONE_NUMBER_ID' => 'ID del número de teléfono', - 'WHATSAPP_API_URL' => 'URL base de la API', - 'WEBHOOK_VERIFY_TOKEN' => 'Token de verificación del webhook' -]; - -foreach ($whatsapp_constants as $const => $desc) { - if (defined($const)) { - $value = constant($const); - - if ($const === 'WHATSAPP_TOKEN') { - // Mostrar información detallada del token - echo "
✅ $const definido ($desc)
"; - echo "
📝 Longitud: " . strlen($value) . " caracteres
"; - - if ($value === 'TU_TOKEN_DE_WHATSAPP_AQUI') { - echo "
❌ CRÍTICO: Token no configurado (valor por defecto)
"; - } else { - // Verificar formato básico del token - if (preg_match('/^[A-Za-z0-9_-]+$/', $value)) { - echo "
✅ Formato básico válido (solo caracteres permitidos)
"; - } else { - echo "
❌ Formato inválido: contiene caracteres no permitidos
"; - } - - // Mostrar primeros y últimos caracteres - $preview = substr($value, 0, 10) . '...' . substr($value, -10); - echo "
🔍 Vista previa: $preview
"; - - // Verificar longitud típica - if (strlen($value) < 50) { - echo "
⚠️ Token parece muy corto (típico > 100 caracteres)
"; - } elseif (strlen($value) > 500) { - echo "
⚠️ Token parece muy largo (típico < 300 caracteres)
"; - } else { - echo "
✅ Longitud parece apropiada
"; - } - } - } else { - if (in_array($const, ['WEBHOOK_VERIFY_TOKEN'])) { - $display_value = str_repeat('*', min(strlen($value), 20)); - } else { - $display_value = $value; - } - echo "
✅ $const = '$display_value' ($desc)
"; - } - } else { - echo "
❌ $const no definida ($desc)
"; - } -} - -echo "
"; - -// 2. Test de formato del token -if (defined('WHATSAPP_TOKEN') && WHATSAPP_TOKEN !== 'TU_TOKEN_DE_WHATSAPP_AQUI') { - echo "
"; - echo "

🔍 ANÁLISIS DETALLADO DEL TOKEN...

"; - - $token = WHATSAPP_TOKEN; - - // Verificar si parece ser un token de Facebook/Meta - if (preg_match('/^[A-Z]{3,5}[A-Za-z0-9_-]{100,}$/', $token)) { - echo "
✅ Parece un token válido de Facebook/Meta
"; - } else { - echo "
⚠️ No tiene el formato típico de token de Facebook/Meta
"; - } - - // Verificar estructura común - if (strpos($token, 'EAA') === 0) { - echo "
✅ Comienza con 'EAA' (formato esperado)
"; - } else { - echo "
⚠️ No comienza con 'EAA' (formato típico de Facebook)
"; - } - - // Verificar caracteres - $allowed_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'; - $invalid_chars = []; - - for ($i = 0; $i < strlen($token); $i++) { - $char = $token[$i]; - if (strpos($allowed_chars, $char) === false) { - $invalid_chars[] = $char; - } - } - - if (empty($invalid_chars)) { - echo "
✅ Solo contiene caracteres permitidos
"; - } else { - echo "
❌ Caracteres inválidos encontrados: " . implode(', ', array_unique($invalid_chars)) . "
"; - } - - echo "
"; - - // 3. Test de conexión a la API de WhatsApp - echo "
"; - echo "

🌐 TEST DE CONEXIÓN API WHATSAPP...

"; - - if (defined('WHATSAPP_PHONE_NUMBER_ID') && WHATSAPP_PHONE_NUMBER_ID !== 'TU_PHONE_ID_AQUI') { - $phone_id = WHATSAPP_PHONE_NUMBER_ID; - $api_url = WHATSAPP_API_URL . $phone_id; - - echo "
🔗 Probando URL: $api_url
"; - - // Configurar cURL para test - $ch = curl_init(); - curl_setopt_array($ch, [ - CURLOPT_URL => $api_url, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 10, - CURLOPT_HTTPHEADER => [ - 'Authorization: Bearer ' . $token, - 'Content-Type: application/json' - ], - CURLOPT_SSL_VERIFYPEER => true, - CURLOPT_USERAGENT => 'WhatsApp Bot Validator/1.0' - ]); - - $response = curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $curl_error = curl_error($ch); - curl_close($ch); - - if ($curl_error) { - echo "
❌ Error de conexión cURL: " . htmlspecialchars($curl_error) . "
"; - } else { - echo "
✅ Conexión establecida (HTTP $http_code)
"; - - if ($response) { - $json_response = json_decode($response, true); - - if ($http_code === 200) { - echo "
✅ Token válido - API responde correctamente
"; - - if ($json_response) { - echo "
📋 Respuesta JSON válida
"; - if (isset($json_response['id'])) { - echo "
✅ Phone Number ID verificado: " . htmlspecialchars($json_response['id']) . "
"; - } - } - - } elseif ($http_code === 401) { - echo "
❌ ERROR 401: Token inválido o expirado
"; - - if ($json_response && isset($json_response['error'])) { - $error = $json_response['error']; - echo "
📝 Mensaje: " . htmlspecialchars($error['message'] ?? 'Sin mensaje') . "
"; - echo "
📝 Tipo: " . htmlspecialchars($error['type'] ?? 'Sin tipo') . "
"; - echo "
📝 Código: " . htmlspecialchars($error['code'] ?? 'Sin código') . "
"; - } - - } elseif ($http_code === 403) { - echo "
❌ ERROR 403: Token válido pero sin permisos
"; - - } else { - echo "
⚠️ Respuesta inesperada (HTTP $http_code)
"; - } - - if ($json_response && isset($json_response['error'])) { - echo "
📋 Respuesta completa:
"; - echo "
" . htmlspecialchars(json_encode($json_response, JSON_PRETTY_PRINT)) . "
"; - } elseif (strlen($response) < 500) { - echo "
📋 Respuesta:
"; - echo "
" . htmlspecialchars($response) . "
"; - } - } - } - - } else { - echo "
❌ PHONE_NUMBER_ID no configurado correctamente
"; - } - - echo "
"; -} - -// 4. Instrucciones para obtener token válido -echo "
"; -echo "

📖 CÓMO OBTENER UN TOKEN VÁLIDO

"; - -echo "
PASOS PARA OBTENER TOKEN:
"; -echo ""; -echo "
2. Crea una aplicación de WhatsApp Business
"; -echo "
3. Ve a WhatsApp > API Setup
"; -echo "
4. Copia el 'Temporary access token'
"; -echo "
5. Para producción, genera un token permanente
"; - -echo "
FORMATO CORRECTO:
"; -echo "
✅ Debe comenzar con: EAA
"; -echo "
✅ Longitud típica: 100-300 caracteres
"; -echo "
✅ Solo letras, números, guiones y guiones bajos
"; - -if (defined('WHATSAPP_TOKEN') && WHATSAPP_TOKEN === 'TU_TOKEN_DE_WHATSAPP_AQUI') { - echo "
⚠️ ACCIÓN REQUERIDA:
"; - echo "
Debes reemplazar 'TU_TOKEN_DE_WHATSAPP_AQUI' con tu token real
"; - echo "
Edita config/config.php línea ~45 o crea archivo .env
"; -} - -echo "
"; - -// 5. Verificación de Phone Number ID -echo "
"; -echo "

📱 VERIFICACIÓN PHONE NUMBER ID

"; - -if (defined('WHATSAPP_PHONE_NUMBER_ID')) { - $phone_id = WHATSAPP_PHONE_NUMBER_ID; - - if ($phone_id === 'TU_PHONE_ID_AQUI') { - echo "
❌ Phone Number ID no configurado
"; - } else { - echo "
✅ Phone Number ID: $phone_id
"; - - // Verificar formato (debe ser numérico) - if (preg_match('/^\d+$/', $phone_id)) { - echo "
✅ Formato correcto (solo números)
"; - } else { - echo "
❌ Formato incorrecto (debe ser solo números)
"; - } - - // Verificar longitud típica - if (strlen($phone_id) >= 10 && strlen($phone_id) <= 20) { - echo "
✅ Longitud apropiada
"; - } else { - echo "
⚠️ Longitud inusual (" . strlen($phone_id) . " dígitos)
"; - } - } -} - -echo "
"; - -echo ""; -?> \ No newline at end of file diff --git a/diagnostico_whatsapp_avanzado.php b/diagnostico_whatsapp_avanzado.php deleted file mode 100644 index 59629e8..0000000 --- a/diagnostico_whatsapp_avanzado.php +++ /dev/null @@ -1,258 +0,0 @@ - - - - Diagnóstico WhatsApp API - - - -
-

🔍 Diagnóstico WhatsApp API

"; - -// 1. Verificar configuraciones -echo "
-

📋 Configuración Actual

- - "; - -$config_items = [ - 'WHATSAPP_TOKEN' => WHATSAPP_TOKEN, - 'WHATSAPP_PHONE_NUMBER_ID' => WHATSAPP_PHONE_NUMBER_ID, - 'WHATSAPP_API_URL' => WHATSAPP_API_URL, - 'WEBHOOK_VERIFY_TOKEN' => WEBHOOK_VERIFY_TOKEN -]; - -foreach ($config_items as $key => $value) { - $status = ''; - if ($key === 'WHATSAPP_TOKEN') { - $display_value = substr($value, 0, 20) . '...'; - $status = ($value !== 'TU_TOKEN_DE_WHATSAPP_AQUI' && !empty($value)) ? - "✅ Configurado" : - "❌ No configurado"; - } elseif ($key === 'WHATSAPP_PHONE_NUMBER_ID') { - $display_value = $value; - $status = ($value !== 'TU_PHONE_ID_AQUI' && !empty($value)) ? - "✅ Configurado" : - "❌ No configurado"; - } else { - $display_value = $value; - $status = !empty($value) ? - "✅ Configurado" : - "❌ No configurado"; - } - - echo ""; -} - -echo "
ParámetroValorEstado
$key$display_value$status
"; - -// 2. Verificar conexión a base de datos -echo "
-

🗄️ Base de Datos

"; - -try { - $db = Database::getInstance(); - echo "

✅ Conexión a base de datos exitosa

"; - - // Verificar tabla system_config - try { - $configs = $db->fetchAll("SELECT config_key, config_value FROM system_config WHERE config_key IN ('whatsapp_token', 'whatsapp_phone_number_id', 'whatsapp_api_url')"); - - if (!empty($configs)) { - echo "

Configuración en BD:

"; - echo ""; - echo ""; - foreach ($configs as $config) { - $display = ($config['config_key'] === 'whatsapp_token') ? - substr($config['config_value'], 0, 20) . '...' : - $config['config_value']; - echo ""; - } - echo "
ClaveValor
" . htmlspecialchars($config['config_key']) . "" . htmlspecialchars($display) . "
"; - } else { - echo "

⚠️ No hay configuración de WhatsApp en la base de datos

"; - } - } catch (Exception $e) { - echo "

⚠️ Tabla system_config no encontrada: " . $e->getMessage() . "

"; - } - -} catch (Exception $e) { - echo "

❌ Error de base de datos: " . $e->getMessage() . "

"; -} - -echo "
"; - -// 3. Probar conectividad con Facebook Graph API -echo "
-

🌐 Conectividad Facebook Graph API

"; - -if (WHATSAPP_TOKEN !== 'TU_TOKEN_DE_WHATSAPP_AQUI' && WHATSAPP_PHONE_NUMBER_ID !== 'TU_PHONE_ID_AQUI') { - - // Prueba 1: Verificar el Phone Number ID - echo "

📞 Verificación del Phone Number ID

"; - - $ch = curl_init(); - $url = WHATSAPP_API_URL . WHATSAPP_PHONE_NUMBER_ID; - $headers = [ - 'Authorization: Bearer ' . WHATSAPP_TOKEN, - 'Content-Type: application/json' - ]; - - curl_setopt_array($ch, [ - CURLOPT_URL => $url, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_TIMEOUT => 15, - CURLOPT_SSL_VERIFYPEER => true - ]); - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $error = curl_error($ch); - curl_close($ch); - - echo "

URL: $url

"; - echo "

HTTP Code: $httpCode

"; - - if ($error) { - echo "

❌ Error cURL: $error

"; - } else { - $decoded = json_decode($response, true); - - if ($httpCode === 200) { - echo "

✅ Phone Number ID válido

"; - echo "

Información del número:

"; - echo "
" . json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "
"; - } else { - echo "

❌ Phone Number ID inválido o sin permisos

"; - echo "

Respuesta de la API:

"; - echo "
" . json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "
"; - - if (isset($decoded['error'])) { - echo "
"; - echo "

Detalles del error:

"; - echo "

Mensaje: " . htmlspecialchars($decoded['error']['message'] ?? 'N/A') . "

"; - echo "

Tipo: " . htmlspecialchars($decoded['error']['type'] ?? 'N/A') . "

"; - echo "

Código: " . htmlspecialchars($decoded['error']['code'] ?? 'N/A') . "

"; - - if (isset($decoded['error']['error_subcode'])) { - echo "

Subcódigo: " . htmlspecialchars($decoded['error']['error_subcode']) . "

"; - } - echo "
"; - } - } - } - - // Prueba 2: Verificar permisos del token - echo "

🔑 Verificación de permisos del token

"; - - $ch = curl_init(); - $debugUrl = "https://graph.facebook.com/debug_token?input_token=" . WHATSAPP_TOKEN . "&access_token=" . WHATSAPP_TOKEN; - - curl_setopt_array($ch, [ - CURLOPT_URL => $debugUrl, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 15, - CURLOPT_SSL_VERIFYPEER => true - ]); - - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $error = curl_error($ch); - curl_close($ch); - - if (!$error && $httpCode === 200) { - $decoded = json_decode($response, true); - if (isset($decoded['data'])) { - echo "

Información del token:

"; - echo "
" . json_encode($decoded['data'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "
"; - } - } else { - echo "

⚠️ No se pudo verificar el token: $error

"; - } - -} else { - echo "

❌ Token o Phone Number ID no configurados correctamente

"; -} - -echo "
"; - -// 4. Recomendaciones -echo "
-

💡 Recomendaciones y Soluciones

"; - -$recommendations = []; - -if (WHATSAPP_TOKEN === 'TU_TOKEN_DE_WHATSAPP_AQUI') { - $recommendations[] = "❌ Configure el token de WhatsApp: Vaya a la configuración del sistema y ingrese su token de WhatsApp Business API."; -} - -if (WHATSAPP_PHONE_NUMBER_ID === 'TU_PHONE_ID_AQUI') { - $recommendations[] = "❌ Configure el Phone Number ID: Vaya a la configuración del sistema y ingrese su Phone Number ID."; -} - -if (WHATSAPP_PHONE_NUMBER_ID === '858157464051987') { - $recommendations[] = "⚠️ Phone Number ID problemático: El ID '858157464051987' está generando errores. Verifique que: -
    -
  • El número esté verificado en Facebook Business
  • -
  • Tenga los permisos correctos (whatsapp_business_messaging)
  • -
  • La aplicación esté en modo de producción
  • -
  • El token tenga acceso a este número
  • -
"; -} - -$recommendations[] = "✅ Verificar en Meta for Developers: -
    -
  • Vaya a Facebook for Developers
  • -
  • Seleccione su aplicación de WhatsApp Business
  • -
  • Vaya a Configuración > WhatsApp > Números de teléfono
  • -
  • Verifique que el número esté activo y verificado
  • -
  • Copie el Phone Number ID correcto
  • -
"; - -$recommendations[] = "🔧 Pasos para resolver el error: -
    -
  1. Verifique que el Phone Number ID sea correcto
  2. -
  3. Asegúrese de que el token tenga permisos para el número
  4. -
  5. Verifique que la aplicación esté aprobada para producción
  6. -
  7. Compruebe que el número esté verificado en Meta Business
  8. -
"; - -foreach ($recommendations as $rec) { - echo "
$rec
"; -} - -echo "
"; - -echo "
-

🛠️ Acciones Rápidas

-
- ⚙️ Ir a Configuración - 📱 Configurar WhatsApp - 🧪 Probar API -
-
"; - -echo "
"; -?> \ No newline at end of file diff --git a/index_simple.php b/index_simple.php deleted file mode 100644 index 33c0e89..0000000 --- a/index_simple.php +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - - WhatsApp Bot Manager - Simple - - - - - -
-
- - - - -
- -
-
-

📊 Dashboard

- Sistema funcionando correctamente -
- -
-
-
-
-

--

-

Configuraciones

-
-
-
-
-
-
-
--
-

WhatsApp

-
-
-
-
-
-
-
--
-

Base de Datos

-
-
-
-
-
-
-
--
-

Instalación

-
-
-
-
- -
-
-
🔧 Acciones Rápidas
-
- -
-
- - -
-
-

💬 Conversaciones

- -
- -
-
-
-

Cargando conversaciones...

-
-
-
- - -
-
-

👥 Usuarios

- -
- -
-
-
-

Cargando usuarios...

-
-
-
- - -
-
-

⚙️ Configuraciones

-
- -
-
-
📱 Configuración WhatsApp
-
-
-
-
- - -
- -
- - -
- -
- - -
- - -
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/install_laboratorio.php b/install_laboratorio.php deleted file mode 100644 index 11ad36b..0000000 --- a/install_laboratorio.php +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - Instalación - Laboratorio Ximena Caicedo - - - - - -
-
- - -
- '; - echo ' Verificando conexión a la base de datos...'; - flush(); - - $db = Database::getInstance(); - - echo ' Conectado'; - echo '
'; - $steps[] = ['name' => 'Conexión DB', 'status' => 'success']; - - // PASO 2: Crear tabla de estados si no existe - echo '
'; - echo ' Creando tabla de estados de conversación...'; - flush(); - - $sqlStates = file_get_contents(__DIR__ . '/migrations/add_conversation_states.sql'); - $statementsStates = array_filter(array_map('trim', explode(';', $sqlStates))); - - foreach ($statementsStates as $statement) { - if (!empty($statement) && substr($statement, 0, 6) !== 'SELECT') { - $db->getConnection()->exec($statement); - } - } - - echo ' Completado'; - echo '
'; - $steps[] = ['name' => 'Tabla user_states', 'status' => 'success']; - - // PASO 3: Importar menús y respuestas automáticas - echo '
'; - echo ' Importando menús y respuestas automáticas...'; - flush(); - - $sqlSetup = file_get_contents(__DIR__ . '/setup_laboratorio_ximena.sql'); - $statementsSetup = array_filter(array_map('trim', explode(';', $sqlSetup))); - - $menuCount = 0; - $responseCount = 0; - - foreach ($statementsSetup as $statement) { - if (!empty($statement) && substr($statement, 0, 2) !== '--') { - if (stripos($statement, 'INSERT INTO menus') !== false) { - $menuCount++; - } elseif (stripos($statement, 'INSERT INTO autoresponses') !== false) { - $responseCount++; - } - - try { - $db->getConnection()->exec($statement); - } catch (Exception $e) { - // Ignorar errores de duplicados - if (strpos($e->getMessage(), 'Duplicate entry') === false) { - throw $e; - } - } - } - } - - echo " {$menuCount} menús, {$responseCount} respuestas"; - echo '
'; - $steps[] = ['name' => 'Menús y respuestas', 'status' => 'success']; - - // PASO 4: Verificar servicios - echo '
'; - echo ' Verificando servicios del sistema...'; - flush(); - - $services = [ - 'BusinessHoursService', - 'ConversationStateService', - 'NLPService', - 'MenuService', - 'PatientDataValidator', - 'BotServiceLab' - ]; - - $servicesOk = true; - foreach ($services as $service) { - $file = __DIR__ . "/services/{$service}.php"; - if (!file_exists($file)) { - echo " Falta {$service}.php"; - $servicesOk = false; - break; - } - } - - if ($servicesOk) { - echo ' ' . count($services) . ' servicios verificados'; - $steps[] = ['name' => 'Servicios', 'status' => 'success']; - } else { - $success = false; - } - - echo '
'; - - // PASO 5: Obtener estadísticas - echo '
'; - echo ' Obteniendo estadísticas...'; - flush(); - - $stmt = $db->query(" - SELECT - (SELECT COUNT(*) FROM menus WHERE is_active = 1) as menus_activos, - (SELECT COUNT(*) FROM menu_options WHERE is_active = 1) as opciones_activas, - (SELECT COUNT(*) FROM autoresponses WHERE is_active = 1) as respuestas_activas, - (SELECT COUNT(*) FROM users) as total_usuarios - "); - $stats = $stmt->fetch(PDO::FETCH_ASSOC); - - echo ' Completado'; - echo '
'; - - // Mostrar resumen - if ($success) { - echo '
'; - echo '

¡Instalación Completada Exitosamente!

'; - echo '
'; - echo '
📊 Estadísticas del Sistema:
'; - echo '
    '; - echo "
  • Menús activos: {$stats['menus_activos']}
  • "; - echo "
  • Opciones de menú: {$stats['opciones_activas']}
  • "; - echo "
  • Respuestas automáticas: {$stats['respuestas_activas']}
  • "; - echo "
  • Usuarios registrados: {$stats['total_usuarios']}
  • "; - echo '
'; - echo '
'; - echo '
🚀 Próximos Pasos:
'; - echo '
    '; - echo '
  1. Configura tus credenciales de WhatsApp Business API en el panel de administración
  2. '; - echo '
  3. Prueba el menú principal enviando un mensaje de WhatsApp
  4. '; - echo '
  5. Revisa las respuestas automáticas en la sección correspondiente
  6. '; - echo '
  7. Personaliza los mensajes según las necesidades del laboratorio
  8. '; - echo '
'; - echo ''; - echo '
'; - } - - } catch (Exception $e) { - $success = false; - echo '
'; - echo ' Error: ' . htmlspecialchars($e->getMessage()); - echo '
'; - - echo '
'; - echo '

Error en la Instalación

'; - echo '

Hubo un problema durante la instalación. Por favor:

'; - echo '
    '; - echo '
  1. Verifica que la base de datos existe y tiene permisos correctos
  2. '; - echo '
  3. Revisa la configuración en config/config.php
  4. '; - echo '
  5. Consulta el error arriba para más detalles
  6. '; - echo '
  7. Intenta ejecutar la instalación nuevamente
  8. '; - echo '
'; - echo '
'; - echo ''; - echo '
'; - echo '
'; - } - ?> -
- - -
-
-
Documentación
-

Para más información sobre cómo usar el sistema:

- -
-
- -
- - - - - diff --git a/login.php b/login.php index 4e29c50..477c968 100644 --- a/login.php +++ b/login.php @@ -91,8 +91,11 @@ if ($_POST && !$loginBlocked) { min-height: 100vh; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; + flex-direction: column; align-items: center; justify-content: center; + gap: 0.75rem; + padding: 2rem 1rem; } .login-card { background: rgba(255,255,255,0.95); @@ -167,6 +170,15 @@ if ($_POST && !$loginBlocked) { 50% { transform: scale(1.05); } 100% { transform: scale(1); } } + + /* Pie de desarrollador discreto */ + .developer-footer { + font-size: 0.85rem; + color: rgba(31,41,55,0.65); + text-align: center; + width: 100%; + max-width: 420px; + } @@ -176,7 +188,6 @@ if ($_POST && !$loginBlocked) {

WhatsApp Bot Manager

Panel de Administración

- Desarrollado por U-Site.app @@ -271,48 +282,11 @@ if ($_POST && !$loginBlocked) { + - -
-
- - Información del Sistema: -
    -
  • Usuario por defecto: admin
  • -
  • Contraseña generada en la instalación
  • -
  • Máximo intentos por IP
  • -
  • Bloqueo de minutos tras fallos
  • -
-
-
- - -
- -
- - -
-
Desarrollado por U-Site.app
-

Sistema profesional de WhatsApp Bot con máxima seguridad

- -
+ + diff --git a/logs/system.log b/logs/system.log index 20f220b..aeefcba 100644 --- a/logs/system.log +++ b/logs/system.log @@ -51,3 +51,15 @@ Stack trace: [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 +[2026-01-21 22:56:59] [INFO] Survey response saved {"id":"1","phone":"573111111111","parsed":{"\u00c2\u00bfFue f\u00c3\u00a1cil interactuar con el chat autom\u00c3\u00a1tico?":"No","\u00c2\u00bfEl asesor humano resolvi\u00c3\u00b3 tu solicitud?":"No","Calificaci\u00c3\u00b3n":"4","Comentario":"bien"}} +[2026-01-21 22:57:43] [INFO] Survey response saved {"id":"2","phone":"573111111111","parsed":{"\u00c2\u00bfFue f\u00c3\u00a1cil interactuar con el chat autom\u00c3\u00a1tico?":"No","\u00c2\u00bfEl asesor humano resolvi\u00c3\u00b3 tu solicitud?":"No","Calificaci\u00c3\u00b3n":"4","Comentario":"bien"}} +[2026-01-21 22:58:27] [INFO] Survey response saved {"id":"3","phone":"573111111111","parsed":{"\u00c2\u00bfFue f\u00c3\u00a1cil interactuar con el chat autom\u00c3\u00a1tico?":"No","\u00c2\u00bfEl asesor humano resolvi\u00c3\u00b3 tu solicitud?":"No","Calificaci\u00c3\u00b3n":"4","Comentario":"bien"}} +[2026-01-21 22:58:27] [INFO] Survey response saved {"id":"4","phone":"573111111111","parsed":{"\u00bfFue f\u00e1cil interactuar con el chat autom\u00e1tico?":"S\u00ed","\u00bfEl asesor humano resolvi\u00f3 tu solicitud?":"No","Calificaci\u00f3n":"5","Comentario":"excelente servicio"}} +[2026-01-21 23:00:56] [INFO] Survey response saved {"id":"5","phone":"573111111111","parsed":{"\u00c2\u00bfFue f\u00c3\u00a1cil interactuar con el chat autom\u00c3\u00a1tico?":"No","\u00c2\u00bfEl asesor humano resolvi\u00c3\u00b3 tu solicitud?":"No","Calificaci\u00c3\u00b3n":"4","Comentario":"bien"}} +[2026-01-21 23:00:56] [INFO] Survey response saved {"id":"6","phone":"573111111111","parsed":{"\u00bfFue f\u00e1cil interactuar con el chat autom\u00e1tico?":"S\u00ed","\u00bfEl asesor humano resolvi\u00f3 tu solicitud?":"No","Calificaci\u00f3n":"5","Comentario":"excelente servicio"}} +[2026-01-21 23:11:49] [INFO] Survey response saved {"id":"7","phone":"573111111111","parsed":{"\u00c2\u00bfFue f\u00c3\u00a1cil interactuar con el chat autom\u00c3\u00a1tico?":"No","\u00c2\u00bfEl asesor humano resolvi\u00c3\u00b3 tu solicitud?":"No","Calificaci\u00c3\u00b3n":"4","Comentario":"bien"}} +[2026-01-21 23:11:49] [INFO] Survey response saved {"id":"8","phone":"573111111111","parsed":{"\u00bfFue f\u00e1cil interactuar con el chat autom\u00e1tico?":"S\u00ed","\u00bfEl asesor humano resolvi\u00f3 tu solicitud?":"No","Calificaci\u00f3n":"5","Comentario":"excelente servicio"}} +[2026-01-21 23:14:51] [INFO] Survey response saved {"id":"9","phone":"573111111111","parsed":{"\u00c2\u00bfFue f\u00c3\u00a1cil interactuar con el chat autom\u00c3\u00a1tico?":"No","\u00c2\u00bfEl asesor humano resolvi\u00c3\u00b3 tu solicitud?":"No","Calificaci\u00c3\u00b3n":"4","Comentario":"bien"}} +[2026-01-21 23:14:51] [INFO] Survey response saved {"id":"10","phone":"573111111111","parsed":{"\u00bfFue f\u00e1cil interactuar con el chat autom\u00e1tico?":"S\u00ed","\u00bfEl asesor humano resolvi\u00f3 tu solicitud?":"No","Calificaci\u00f3n":"5","Comentario":"excelente servicio"}} +[2026-01-21 23:49:55] [INFO] delete_template.php start {"raw_input":{"id":"1"},"query":{"debug":"true"}} +[2026-01-21 23:49:55] [INFO] delete_template.php parsed ids {"ids":[1]} diff --git a/run_tests.php b/run_tests.php deleted file mode 100644 index 04ccdd5..0000000 --- a/run_tests.php +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/scripts/api_send_reaction_test.php b/scripts/api_send_reaction_test.php deleted file mode 100644 index 266f82a..0000000 --- a/scripts/api_send_reaction_test.php +++ /dev/null @@ -1,15 +0,0 @@ - '573001234567', - 'message_id' => '', - 'emoji' => '👍', - 'dry_run' => true -]); - -file_put_contents('php://memory', $payload); // no effect; instead call service directly - -require_once __DIR__ . '/../api/send_reaction.php'; diff --git a/scripts/api_send_reply_test.php b/scripts/api_send_reply_test.php deleted file mode 100644 index ca679e7..0000000 --- a/scripts/api_send_reply_test.php +++ /dev/null @@ -1,5 +0,0 @@ -fetch("SHOW TABLES LIKE 'survey_responses'"); + if ($col) { + echo "Table survey_responses already exists\n"; + exit; + } + + $sql = "CREATE TABLE survey_responses ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NULL, + phone_number VARCHAR(30) NULL, + q_easy_interact TINYINT(1) NULL, + q_human_resolved TINYINT(1) NULL, + rating INT NULL, + comment TEXT NULL, + raw_text TEXT NULL, + created_at DATETIME NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"; + + $db->query($sql); + echo "Created table survey_responses\n"; +} catch (Exception $e) { + echo "Failed creating survey_responses: " . $e->getMessage() . "\n"; +} diff --git a/scripts/db_test.php b/scripts/db_test.php deleted file mode 100644 index 19610d8..0000000 --- a/scripts/db_test.php +++ /dev/null @@ -1,13 +0,0 @@ -getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} diff --git a/scripts/insert_activity_test.php b/scripts/insert_activity_test.php deleted file mode 100644 index 0982a3d..0000000 --- a/scripts/insert_activity_test.php +++ /dev/null @@ -1,8 +0,0 @@ -insert('operator_activity', ['user_id' => 1, 'operator_id' => 1, 'action' => 'test', 'details' => 'test insert', 'created_at' => $now]); -var_dump($id); -$rows = $db->fetchAll('SELECT * FROM operator_activity ORDER BY created_at DESC LIMIT 5'); -var_dump($rows); diff --git a/scripts/send_template_test.php b/scripts/send_template_test.php deleted file mode 100644 index 5bd3a27..0000000 --- a/scripts/send_template_test.php +++ /dev/null @@ -1,48 +0,0 @@ -getMessage() . "\n"; - echo "Stack trace:\n" . $e->getTraceAsString() . "\n"; - exit(1); -} - -foreach ($candidates as $lang) { - echo "Intentando con language = $lang ...\n"; - try { - $result = $wh->sendTemplateMessage($recipient, $template, $lang, []); - echo "Resultado OK (language=$lang):\n"; - echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n"; - exit(0); - } catch (Exception $e) { - echo "FALLÓ (language=$lang): " . $e->getMessage() . "\n"; - // Mostrar detalles si es posible - if (isset($e->getTrace()[0]['args'][0])) { - echo "Detalle trace arg0: " . var_export($e->getTrace()[0]['args'][0], true) . "\n"; - } - // continuar con siguiente candidato - } -} - -echo "Todos los intentos fallaron. Revisa WhatsApp Business Manager para esa plantilla y los idiomas disponibles.\n"; -exit(1); diff --git a/scripts/test_add_notification.php b/scripts/test_add_notification.php deleted file mode 100644 index b064be2..0000000 --- a/scripts/test_add_notification.php +++ /dev/null @@ -1,23 +0,0 @@ -fetch('SELECT id, phone_number FROM users LIMIT 1'); -$userId = $user['id'] ?? null; - -$data = [ - 'user_id' => $userId, - 'type' => 'message', - 'message' => 'Prueba: nuevo mensaje recibido', - 'data' => json_encode(['phone' => $user['phone_number'] ?? '']), - 'is_read' => 0, - 'created_at' => date('Y-m-d H:i:s') -]; - -$id = $db->insert('notifications', $data); -if ($id) { - echo "Inserted notification id=$id for user_id={$userId}\n"; -} else { - echo "Failed to insert notification\n"; -} diff --git a/scripts/test_asesor_and_pause.php b/scripts/test_asesor_and_pause.php deleted file mode 100644 index 00ff0c8..0000000 --- a/scripts/test_asesor_and_pause.php +++ /dev/null @@ -1,49 +0,0 @@ -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_attend.php b/scripts/test_attend.php deleted file mode 100644 index 3170de4..0000000 --- a/scripts/test_attend.php +++ /dev/null @@ -1,26 +0,0 @@ -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')]); -$db->update('users', ['advisor_requested' => 1], 'id = :id', ['id' => $id]); - -$bot = new BotService(); -$bot->attendConversation($phone, 1); - -$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $id]); -$activity = $db->fetch('SELECT * FROM operator_activity WHERE user_id = :uid ORDER BY created_at DESC LIMIT 1', ['uid' => $id]); - -echo "User in_service: " . ($user['in_service'] ?? 'NULL') . PHP_EOL; -echo "in_service_by: " . ($user['in_service_by'] ?? 'NULL') . PHP_EOL; -echo "in_service_at: " . ($user['in_service_at'] ?? 'NULL') . PHP_EOL; - -if ($activity) { - echo "Activity: action={$activity['action']} operator_id={$activity['operator_id']} details={$activity['details']}\n"; -} else { - echo "No operator activity found\n"; -} diff --git a/scripts/test_bot_enabled.php b/scripts/test_bot_enabled.php deleted file mode 100644 index 8901839..0000000 --- a/scripts/test_bot_enabled.php +++ /dev/null @@ -1,31 +0,0 @@ -execute('DELETE FROM users WHERE phone_number = :p', ['p' => $phone]); -$userId = $db->insert('users', ['phone_number' => $phone, 'status' => 'active', 'bot_enabled' => 0, 'welcome_sent_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s')]); -$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]); - -// Clear conversations -$db->execute('DELETE FROM conversations WHERE user_id = :uid', ['uid' => $userId]); - -// Send message while bot disabled -$bot->processMessage($user, 'hola', 'text'); -$outs = $db->fetchAll("SELECT * FROM conversations WHERE user_id = :uid AND direction = 'outgoing'", ['uid' => $userId]); -echo "Outgoing after disabled: " . count($outs) . "\n"; - -// Enable bot -$db->update('users', ['bot_enabled' => 1], 'id = :id', ['id' => $userId]); -$user2 = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]); -$bot->processMessage($user2, 'hola', 'text'); -$outs2 = $db->fetchAll("SELECT * FROM conversations WHERE user_id = :uid AND direction = 'outgoing'", ['uid' => $userId]); -echo "Outgoing after enabled: " . count($outs2) . "\n"; - -// Cleanup -$db->execute('DELETE FROM conversations WHERE user_id = :uid', ['uid' => $userId]); -$db->execute('DELETE FROM users WHERE id = :id', ['id' => $userId]); - -echo "Done\n"; \ No newline at end of file diff --git a/scripts/test_delete_template.php b/scripts/test_delete_template.php deleted file mode 100644 index 802b986..0000000 --- a/scripts/test_delete_template.php +++ /dev/null @@ -1,46 +0,0 @@ - 1, 'username' => 'admin']; - -$db = Database::getInstance(); -// Clean up any existing test template called 'test_delete_tpl' -try { $db->execute('DELETE FROM message_templates WHERE name = ?', ['test_delete_tpl']); } catch (Exception $e) { } -// Insert a test template -$id = $db->insert('message_templates', [ - 'name' => 'test_delete_tpl', - 'template_name' => 'test_delete_tpl_name', - 'language_code' => 'en_US', - 'category' => 'utility', - 'status' => 'approved', - 'created_at' => date('Y-m-d H:i:s') -]); - -echo "Inserted template id: $id\n"; - -// Simulate POST body -$_POST = ['id' => $id]; - -// Capture output -ob_start(); -include __DIR__ . '/../api/delete_template.php'; -$out = ob_get_clean(); - -echo "API Output: $out\n"; - -// Verify it was deleted -$check = $db->fetch('SELECT * FROM message_templates WHERE id = :id', ['id' => $id]); -if (!$check) { - echo "Template successfully deleted from DB.\n"; -} else { - echo "Template still exists in DB: " . json_encode($check) . "\n"; -} diff --git a/scripts/test_enviados_flow.php b/scripts/test_enviados_flow.php deleted file mode 100644 index 2dc172d..0000000 --- a/scripts/test_enviados_flow.php +++ /dev/null @@ -1,21 +0,0 @@ -fetch('SELECT * FROM users WHERE phone_number = ?', [$phone]); -if (!$user) { - $id = $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]); -} -$bot = new BotService(); - -echo "User id: {$user['id']} phone: {$phone}\n"; - -// Simulate sending 'INFORMACION ENVIADA' (uppercase + phrase) -echo "--- Sending 'INFORMACION ENVIADA' ---\n"; -$bot->processMessage($user, 'INFORMACION ENVIADA', 'text'); -// show last conversation entries -$rows = $db->fetchAll('SELECT * FROM conversations WHERE user_id = ? ORDER BY created_at DESC LIMIT 5', [$user['id']]); -print_r($rows); diff --git a/scripts/test_finish_attend.php b/scripts/test_finish_attend.php deleted file mode 100644 index c096987..0000000 --- a/scripts/test_finish_attend.php +++ /dev/null @@ -1,25 +0,0 @@ -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')]); -$db->update('users', ['advisor_requested' => 1], 'id = :id', ['id' => $id]); - -$bot = new BotService(); -$bot->attendConversation($phone, 1); -$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $id]); -echo "After attend: in_service={$user['in_service']} in_service_by={$user['in_service_by']}\n"; - -$bot->finishAttendConversation($phone, 1); -$user2 = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $id]); -echo "After finish: in_service={$user2['in_service']} in_service_by={$user2['in_service_by']}\n"; -$activity = $db->fetch('SELECT * FROM operator_activity WHERE user_id = :uid ORDER BY created_at DESC LIMIT 1', ['uid' => $id]); -if ($activity) { - echo "Activity: action={$activity['action']} details={$activity['details']}\n"; -} else { - echo "No activity\n"; -} diff --git a/scripts/test_get_conversation_detail.php b/scripts/test_get_conversation_detail.php deleted file mode 100644 index 6b66402..0000000 --- a/scripts/test_get_conversation_detail.php +++ /dev/null @@ -1,13 +0,0 @@ -getMessage(); -} -$out = ob_get_clean(); -echo $out; diff --git a/scripts/test_get_conversations.php b/scripts/test_get_conversations.php deleted file mode 100644 index 137ef90..0000000 --- a/scripts/test_get_conversations.php +++ /dev/null @@ -1,11 +0,0 @@ -getMessage(); -} -$out = ob_get_clean(); -echo $out; diff --git a/scripts/test_get_templates.php b/scripts/test_get_templates.php deleted file mode 100644 index d051b33..0000000 --- a/scripts/test_get_templates.php +++ /dev/null @@ -1,13 +0,0 @@ -getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} -$out = ob_get_clean(); -echo $out; diff --git a/scripts/test_get_user_conversations.php b/scripts/test_get_user_conversations.php deleted file mode 100644 index de9afea..0000000 --- a/scripts/test_get_user_conversations.php +++ /dev/null @@ -1,13 +0,0 @@ -getMessage(); -} -$out = ob_get_clean(); -echo $out; diff --git a/scripts/test_get_users.php b/scripts/test_get_users.php deleted file mode 100644 index c5d2dd0..0000000 --- a/scripts/test_get_users.php +++ /dev/null @@ -1,13 +0,0 @@ -getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} -$out = ob_get_clean(); -echo $out; \ No newline at end of file diff --git a/scripts/test_in_service_block.php b/scripts/test_in_service_block.php deleted file mode 100644 index c2a148e..0000000 --- a/scripts/test_in_service_block.php +++ /dev/null @@ -1,21 +0,0 @@ -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"; \ No newline at end of file diff --git a/tmp/test_send_out.json b/scripts/test_include_cli_output.txt similarity index 100% rename from tmp/test_send_out.json rename to scripts/test_include_cli_output.txt diff --git a/scripts/test_include_run2.txt b/scripts/test_include_run2.txt new file mode 100644 index 0000000..e69de29 diff --git a/scripts/test_include_run3.txt b/scripts/test_include_run3.txt new file mode 100644 index 0000000..d86bac9 --- /dev/null +++ b/scripts/test_include_run3.txt @@ -0,0 +1 @@ +OK diff --git a/scripts/test_insert_url.php b/scripts/test_insert_url.php deleted file mode 100644 index 7172544..0000000 --- a/scripts/test_insert_url.php +++ /dev/null @@ -1,19 +0,0 @@ -insert('conversations', [ - 'user_id' => 2, - 'message_id' => $mid, - 'direction' => 'incoming', - 'message_type' => 'document', - 'content' => 'usuarios_blancos.csv', - 'media_url' => 'https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1379066300634610&source=webhook&ext=1769010393&hash=EXAMPLE', - 'status' => 'received' - ]); - $row = $db->fetch('SELECT * FROM conversations WHERE message_id = ?', [$mid]); - echo json_encode($row, JSON_PRETTY_PRINT) . "\n"; -} catch (Throwable $t) { - echo 'Error: ' . $t->getMessage() . "\n"; -} diff --git a/scripts/test_interactive_flow.php b/scripts/test_interactive_flow.php deleted file mode 100644 index 8f2c007..0000000 --- a/scripts/test_interactive_flow.php +++ /dev/null @@ -1,42 +0,0 @@ - [ - [ - 'from' => '573010000002', - 'id' => 'TEST_INT_1', - 'type' => 'interactive', - 'interactive' => [ - 'type' => 'list_reply', - 'list_reply' => ['id' => 'option_2', 'title' => 'Consultar resultados'] - ], - 'timestamp' => time() - ] - ] -]; - -// Set current menu for user (simulate that menu was shown previously) -$db = Database::getInstance(); -$db->update('users', ['current_menu_id' => 1, 'current_step' => 1], 'phone_number = :phone', ['phone' => '573010000002']); - -// Use reflection to call private method processconversations -$ref = new ReflectionClass($w); -$m = $ref->getMethod('processconversations'); -$m->setAccessible(true); - -$m->invoke($w, $value); - -// Check DB for last conversations for that phone -$db = Database::getInstance(); -$user = $db->fetch('SELECT * FROM users WHERE phone_number = ?', ['573010000002']); -if ($user) { - $rows = $db->fetchAll('SELECT * FROM conversations WHERE user_id = ? ORDER BY created_at DESC LIMIT 5', [$user['id']]); - print_r($user); - print_r($rows); -} else { - echo "User not found\n"; -} diff --git a/scripts/test_media_store.php b/scripts/test_media_store.php deleted file mode 100644 index 1d30939..0000000 --- a/scripts/test_media_store.php +++ /dev/null @@ -1,12 +0,0 @@ -\n"; exit(1); } -$ms = new MediaService(); -try { - $res = $ms->fetchAndStoreFromGraph($mediaId, date('Y/m')); - echo json_encode($res, JSON_PRETTY_PRINT) . "\n"; -} catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; -} diff --git a/scripts/test_media_url.php b/scripts/test_media_url.php deleted file mode 100644 index 3bf1beb..0000000 --- a/scripts/test_media_url.php +++ /dev/null @@ -1,41 +0,0 @@ - -$mediaId = $argv[1] ?? '1379066300634610'; - -// 1) JSON request (requires auth) - use local cookie session if possible -$opts = ['http' => ['method' => 'GET', 'header' => "Accept: application/json\r\n"]]; -$ctx = stream_context_create($opts); -$url = "http://localhost/whatsapp/api/version/media-url.php?id=" . urlencode($mediaId); -echo "Requesting JSON -> $url\n"; -$s = @file_get_contents($url, false, $ctx); -if ($s === false) { - echo "JSON request failed (probably 401 if not authenticated)\n"; -} else { - echo $s . "\n"; -} - -// 2) Direct GET (simulate browser) should receive a Location header (we can't follow in this simple test) -$headers = get_headers($url, 1); -if ($headers) { - echo "\nResponse headers:\n"; - print_r($headers); -} else { - echo "No headers received (server unreachable)\n"; -} - -// 3) Proxy download test -$downloadUrl = $url . '&download=1'; -echo "\nDownload test -> $downloadUrl\n"; -$opts2 = ['http' => ['method' => 'GET']]; -$ctx2 = stream_context_create($opts2); -$stream = @fopen($downloadUrl, 'r', false, $ctx2); -if (!$stream) { - echo "Download request failed (check server or token)\n"; -} else { - $meta = stream_get_meta_data($stream); - echo "Opened stream, meta keys: " . implode(',', array_keys($meta)) . "\n"; - // Read a small part - $part = fread($stream, 200); - echo "First bytes:\n" . substr($part,0,200) . "\n"; - fclose($stream); -} diff --git a/scripts/test_menu_flow.php b/scripts/test_menu_flow.php deleted file mode 100644 index 117edfe..0000000 --- a/scripts/test_menu_flow.php +++ /dev/null @@ -1,27 +0,0 @@ -fetch('SELECT * FROM users WHERE phone_number = ?', [$phone]); -if (!$user) { - $id = $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]); -} -$bot = new BotService(); - -echo "User id: {$user['id']} phone: {$phone}\n"; - -// Simulate sending 'menu' -echo "--- Sending 'menu' ---\n"; -$bot->processMessage($user, 'menu', 'text'); -$user = $db->fetch('SELECT * FROM users WHERE id = ?', [$user['id']]); -print_r(['after menu user' => $user]); - -// Simulate user selecting option 2 -echo "--- Sending '2' ---\n"; -$bot->processMessage($user, '2', 'text'); -// show last conversation entries -$rows = $db->fetchAll('SELECT * FROM conversations WHERE user_id = ? ORDER BY created_at DESC LIMIT 5', [$user['id']]); -print_r($rows); diff --git a/scripts/test_menu_with_advisor_reply.php b/scripts/test_menu_with_advisor_reply.php deleted file mode 100644 index 083805a..0000000 --- a/scripts/test_menu_with_advisor_reply.php +++ /dev/null @@ -1,33 +0,0 @@ -fetch('SELECT * FROM users WHERE phone_number = ?', [$phone]); -if (!$user) { - $id = $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]); -} -$bot = new BotService(); - -echo "User id: {$user['id']} phone: {$phone}\n"; - -// Simulate incoming message 'Hola' -$db->insert('conversations', ['user_id' => $user['id'], 'message_id' => 'INC1', 'direction' => 'incoming', 'message_type' => 'text', 'content' => 'Hola', 'status' => 'received', 'created_at' => date('Y-m-d H:i:s')]); - -// Simulate advisor outgoing reply AFTER the incoming message -$db->insert('conversations', ['user_id' => $user['id'], 'message_id' => 'OUT1', 'direction' => 'outgoing', 'message_type' => 'text', 'content' => 'Hola, soy el asesor', 'status' => 'sent', 'created_at' => date('Y-m-d H:i:s')]); - -// Now user sends 'menu' -echo "--- Sending 'menu' ---\n"; -$user = $db->fetch('SELECT * FROM users WHERE id = ?', [$user['id']]); -$bot->processMessage($user, 'menu', 'text'); -$user = $db->fetch('SELECT * FROM users WHERE id = ?', [$user['id']]); -print_r(['after menu user' => $user]); - -// Simulate user selecting option 2 -echo "--- Sending '2' ---\n"; -$bot->processMessage($user, '2', 'text'); -$rows = $db->fetchAll('SELECT * FROM conversations WHERE user_id = ? ORDER BY created_at DESC LIMIT 5', [$user['id']]); -print_r($rows); diff --git a/scripts/test_notifications_flow.php b/scripts/test_notifications_flow.php deleted file mode 100644 index 9922746..0000000 --- a/scripts/test_notifications_flow.php +++ /dev/null @@ -1,35 +0,0 @@ - 'whatsapp_business_account', - 'entry' => [ - [ - 'changes' => [ - [ - 'field' => 'conversations', - 'value' => [ - 'conversations' => [ - [ - 'from' => '573019999900', - 'id' => 'NTFTEST1', - 'timestamp' => time(), - 'type' => 'text', - 'text' => ['body' => 'Notificacion test'] - ] - ] - ] - ] - ] - ] - ] -]; - -$w = new WhatsAppWebhook(); -$w->processPayload($payload); - -$db = Database::getInstance(); -$rows = $db->fetchAll('SELECT * FROM notifications ORDER BY id DESC LIMIT 5'); -foreach ($rows as $r) echo json_encode($r) . "\n"; diff --git a/scripts/test_process_payload_direct.php b/scripts/test_process_payload_direct.php deleted file mode 100644 index ec78a52..0000000 --- a/scripts/test_process_payload_direct.php +++ /dev/null @@ -1,22 +0,0 @@ -processPayload($data); - echo 'processPayload returned: ' . ($ok ? 'true' : 'false') . PHP_EOL; -} catch (Exception $e) { - echo 'EXCEPTION: ' . $e->getMessage() . PHP_EOL; -} diff --git a/scripts/test_reaction_and_reply.php b/scripts/test_reaction_and_reply.php deleted file mode 100644 index 34fab05..0000000 --- a/scripts/test_reaction_and_reply.php +++ /dev/null @@ -1,121 +0,0 @@ -insert('users', ['phone_number' => $testPhone, 'name' => 'Reactor', 'status' => 'active']); -$origMsgId = '1001'; -$db->insert('conversations', [ - 'user_id' => $userId, - 'message_id' => $origMsgId, - 'direction' => 'incoming', - 'message_type' => 'text', - 'content' => 'Original msg', - 'status' => 'received', - 'created_at' => date('Y-m-d H:i:s') -]); - -$payload = [ - 'object' => 'whatsapp_business_account', - 'entry' => [ - [ - 'changes' => [ - [ - 'field' => 'conversations', - 'value' => [ - 'messaging_product' => 'whatsapp', - 'metadata' => [ - 'display_phone_number' => '16505551111', - 'phone_number_id' => '123' - ], - 'contacts' => [ - [ 'profile' => ['name' => 'reactor'], 'wa_id' => $testPhone ] - ], - 'conversations' => [ - [ - 'from' => $testPhone, - 'id' => '2001', - 'timestamp' => time(), - 'type' => 'reaction', - 'reaction' => ['message_id' => $origMsgId, 'emoji' => '👍'] - ] - ] - ] - ] - ] - ] - ] -]; - -$r = $webhook->processPayload($payload); -$conv = $db->fetch('SELECT * FROM conversations WHERE message_id = :mid', ['mid' => '2001']); -if ($conv) { - echo "Reaction saved: reaction_to_message_id={$conv['reaction_to_message_id']}, reaction_emoji={$conv['reaction_emoji']}\n"; -} else { - echo "Reaction not saved\n"; -} - -// Cleanup reaction test -$db->execute('DELETE FROM conversations WHERE message_id = :mid', ['mid' => '2001']); -$db->execute('DELETE FROM conversations WHERE message_id = :mid', ['mid' => $origMsgId]); -$db->execute('DELETE FROM users WHERE id = :id', ['id' => $userId]); - -// Test reply/context -$testPhone2 = '573007777111'; -$userId2 = $db->insert('users', ['phone_number' => $testPhone2, 'name' => 'Replier', 'status' => 'active']); -$origMsgId2 = '1002'; -$db->insert('conversations', [ - 'user_id' => $userId2, - 'message_id' => $origMsgId2, - 'direction' => 'incoming', - 'message_type' => 'text', - 'content' => 'Original for reply', - 'status' => 'received', - 'created_at' => date('Y-m-d H:i:s') -]); - -$payload2 = [ - 'object' => 'whatsapp_business_account', - 'entry' => [ - [ - 'changes' => [ - [ - 'field' => 'conversations', - 'value' => [ - 'conversations' => [ - [ - 'from' => $testPhone2, - 'id' => '2002', - 'timestamp' => time(), - 'type' => 'text', - 'text' => ['body' => 'Reply message'], - 'context' => ['id' => $origMsgId2] - ] - ] - ] - ] - ] - ] - ] -]; - -$r2 = $webhook->processPayload($payload2); -$conv2 = $db->fetch('SELECT * FROM conversations WHERE message_id = :mid', ['mid' => '2002']); -if ($conv2) { - echo "Reply saved: reply_to_message_id={$conv2['reply_to_message_id']}\n"; -} else { - echo "Reply not saved\n"; -} - -// Cleanup reply test -$db->execute('DELETE FROM conversations WHERE message_id = :mid', ['mid' => '2002']); -$db->execute('DELETE FROM conversations WHERE message_id = :mid', ['mid' => $origMsgId2]); -$db->execute('DELETE FROM users WHERE id = :id', ['id' => $userId2]); - -echo "Done\n"; diff --git a/scripts/test_replay_last.php b/scripts/test_replay_last.php deleted file mode 100644 index cbbf66d..0000000 --- a/scripts/test_replay_last.php +++ /dev/null @@ -1,20 +0,0 @@ -fetch('SELECT id, request_body FROM webhook_logs ORDER BY created_at DESC LIMIT 1'); - echo "Latest log id: " . ($row['id'] ?? 'none') . PHP_EOL; - $payload = json_decode($row['request_body'], true); - echo "Decoded payload keys: " . implode(',', array_keys($payload)) . PHP_EOL; - $w = new WhatsAppWebhook(); - echo "Webhook instance created\n"; - $res = $w->processPayload($payload); - echo 'ProcessPayload => ' . ($res ? 'OK' : 'FAILED') . PHP_EOL; -} catch (Exception $e) { - echo 'ERROR: ' . $e->getMessage() . PHP_EOL; -} diff --git a/scripts/test_require.php b/scripts/test_require.php deleted file mode 100644 index 05ffaf0..0000000 --- a/scripts/test_require.php +++ /dev/null @@ -1,12 +0,0 @@ -getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} diff --git a/scripts/test_send_text.php b/scripts/test_send_text.php deleted file mode 100644 index 79165e4..0000000 --- a/scripts/test_send_text.php +++ /dev/null @@ -1,12 +0,0 @@ -sendTextMessage($to, $message); - echo "Response:\n"; - echo json_encode($res, JSON_PRETTY_PRINT) . "\n"; -} catch (Exception $e) { - echo "Error: " . $e->getMessage() . "\n"; -} diff --git a/scripts/test_welcome_config.php b/scripts/test_welcome_config.php deleted file mode 100644 index 8e9e554..0000000 --- a/scripts/test_welcome_config.php +++ /dev/null @@ -1,35 +0,0 @@ -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/scripts/tmp_test_delete_select.php b/scripts/tmp_test_delete_select.php deleted file mode 100644 index 699fab0..0000000 --- a/scripts/tmp_test_delete_select.php +++ /dev/null @@ -1,11 +0,0 @@ -fetchAll("SELECT id, name, template_name FROM message_templates WHERE id IN ($placeholders)", $ids); - var_export($existing); -} catch (Exception $e) { - echo "Exception: " . $e->getMessage() . PHP_EOL; -} diff --git a/scripts/wa_send_tests.php b/scripts/wa_send_tests.php deleted file mode 100644 index e89ba20..0000000 --- a/scripts/wa_send_tests.php +++ /dev/null @@ -1,21 +0,0 @@ -sendReaction('573001234567', '', '❤️', true); - print_r($r); - - echo "Dry-run text reply:\n"; - $rr = $s->sendTextReply('573001234567', '', 'Gracias por tu mensaje', false, true); - print_r($rr); - -} catch (Throwable $t) { - echo "ERROR: " . $t->getMessage() . "\n"; -} diff --git a/scripts/wa_service_test.php b/scripts/wa_service_test.php deleted file mode 100644 index 7f043b6..0000000 --- a/scripts/wa_service_test.php +++ /dev/null @@ -1,16 +0,0 @@ -getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} -echo "END wa_service_test\n"; diff --git a/scripts/webhook_post_test.php b/scripts/webhook_post_test.php deleted file mode 100644 index b671db5..0000000 --- a/scripts/webhook_post_test.php +++ /dev/null @@ -1,102 +0,0 @@ - "whatsapp_business_account", - "entry" => [ - [ - "id" => "0", - "changes" => [ - [ - "field" => "conversations", - "value" => [ - "messaging_product" => "whatsapp", - "metadata" => [ - "display_phone_number" => "16505551111", - "phone_number_id" => "123456123" - ], - "contacts" => [ - [ - "profile" => ["name" => "test user name"], - "wa_id" => "16315551181" - ] - ], - "conversations" => [ - [ - "from" => "16315551181", - "id" => "ABGGFlA5Fpa", - "timestamp" => "1504902988", - "type" => "text", - "text" => ["body" => "this is a text message"] - ] - ] - ] - ] - ] - ] - ] -]; - -try { - $w = new WhatsAppWebhook(); - echo "Webhook instance created\n"; - $result = $w->processPayload($payload); - echo "processPayload result: " . ($result ? 'true' : 'false') . "\n"; - - // Simular reacción entrante - $reactionPayload = [ - "object" => "whatsapp_business_account", - "entry" => [ - [ - "id" => "0", - "changes" => [ - [ - "field" => "conversations", - "value" => [ - "messaging_product" => "whatsapp", - "metadata" => [ - "display_phone_number" => "16505551111", - "phone_number_id" => "123456123" - ], - "contacts" => [ - [ - "profile" => ["name" => "reactor"], - "wa_id" => "16315551181" - ] - ], - "conversations" => [ - [ - "from" => "16315551181", - "id" => "REACTION1", - "timestamp" => "1504902990", - "type" => "reaction", - "reaction" => ["message_id" => "ABGGFlA5Fpa", "emoji" => "❤️"] - ] - ] - ] - ] - ] - ] - ] - ]; - - $r2 = $w->processPayload($reactionPayload); - echo "processReaction result: " . ($r2 ? 'true' : 'false') . "\n"; - -} catch (Throwable $t) { - echo "Fatal: " . $t->getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} - -// Show last lines of system log -$log = __DIR__ . '/../logs/system.log'; -if (file_exists($log)) { - echo "-- Last log lines --\n"; - $lines = array_slice(file($log), -40); - foreach ($lines as $line) echo $line; -} else { - echo "No log file found\n"; -} diff --git a/scripts/webhook_test_cli.php b/scripts/webhook_test_cli.php deleted file mode 100644 index bb4556d..0000000 --- a/scripts/webhook_test_cli.php +++ /dev/null @@ -1,13 +0,0 @@ -getMessage() . "\n"; - echo $t->getTraceAsString() . "\n"; -} diff --git a/simple_debug.php b/simple_debug.php deleted file mode 100644 index 3f54cd8..0000000 --- a/simple_debug.php +++ /dev/null @@ -1,53 +0,0 @@ - \ No newline at end of file diff --git a/test_media_api_debug.php b/test_media_api_debug.php deleted file mode 100644 index 56f22d6..0000000 --- a/test_media_api_debug.php +++ /dev/null @@ -1,57 +0,0 @@ - '573168950803', - 'media_url' => 'http://localhost/uploads/test.jpg', - 'media_type' => 'image', - 'caption' => 'Test de imagen', - 'filename' => 'test.jpg' -]; - -echo "=== TEST API MULTIMEDIA ===\n\n"; -echo "URL: $apiUrl\n"; -echo "Datos enviados:\n"; -print_r($data); -echo "\n"; - -// Hacer petición -$ch = curl_init($apiUrl); -curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); -curl_setopt($ch, CURLOPT_POST, true); -curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); -curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/json' -]); - -$response = curl_exec($ch); -$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); -$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); -curl_close($ch); - -echo "=== RESPUESTA ===\n"; -echo "HTTP Code: $httpCode\n"; -echo "Content-Type: $contentType\n"; -echo "\nRespuesta RAW:\n"; -echo $response; -echo "\n\n"; - -// Intentar decodificar JSON -echo "=== DECODIFICACIÓN JSON ===\n"; -$decoded = json_decode($response, true); -if ($decoded === null) { - echo "ERROR: No se pudo decodificar JSON\n"; - echo "JSON Error: " . json_last_error_msg() . "\n"; - echo "\nPrimeros 500 caracteres de la respuesta:\n"; - echo substr($response, 0, 500) . "\n"; -} else { - echo "JSON decodificado correctamente:\n"; - print_r($decoded); -} -?> diff --git a/tests/api_output.html b/tests/api_output.html deleted file mode 100644 index 8efc70f..0000000 Binary files a/tests/api_output.html and /dev/null differ diff --git a/tests/api_output_after_fix.html b/tests/api_output_after_fix.html deleted file mode 100644 index b5f4240..0000000 Binary files a/tests/api_output_after_fix.html and /dev/null differ diff --git a/tests/api_output_after_fix2.html b/tests/api_output_after_fix2.html deleted file mode 100644 index f8801f1..0000000 Binary files a/tests/api_output_after_fix2.html and /dev/null differ diff --git a/tests/api_tests.php b/tests/api_tests.php deleted file mode 100644 index f6506a6..0000000 --- a/tests/api_tests.php +++ /dev/null @@ -1,491 +0,0 @@ -🔥 Tests Exhaustivos de APIs"; - - // Configurar sesión de test - $this->setupTestSession(); - - // Tests de mensajería - $this->testSendMessageAPI(); - $this->testBroadcastAPI(); - - // Tests de plantillas - $this->testTemplateAPIs(); - - // Tests de usuarios - $this->testUserAPIs(); - - // Tests de configuración - $this->testConfigurationAPIs(); - - // Tests de analytics - $this->testAnalyticsAPIs(); - - $this->showAPITestSummary(); - } - - private function setupTestSession() { - $_SESSION['admin_logged_in'] = true; - $_SESSION['last_activity'] = time(); - $_SESSION['login_ip'] = '127.0.0.1'; - } - - private function testSendMessageAPI() { - echo "

📨 Tests de API send_message.php

"; - - // Test 1: Verificar que el archivo existe - $this->apiTest("Verificar archivo send_message existe", function() { - if (!file_exists(__DIR__ . '/../api/send_message.php')) { - throw new Exception("Archivo send_message.php no existe"); - } - return "Archivo send_message.php existe"; - }); - - // Test 2: Verificar contenido básico del archivo - $this->apiTest("Verificar estructura de send_message", function() { - $content = file_get_contents(__DIR__ . '/../api/send_message.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("API no incluye verificación de autenticación"); - } - - if (strpos($content, 'Content-Type: application/json') === false) { - throw new Exception("API no establece Content-Type JSON"); - } - - return "Estructura de send_message correcta"; - }); - - // Test 3: Verificar que usa validaciones - $this->apiTest("Verificar validaciones en send_message", function() { - $content = file_get_contents(__DIR__ . '/../api/send_message.php'); - - $validations = [ - 'recipient' => strpos($content, 'recipient') !== false, - 'type' => strpos($content, 'type') !== false, - 'message' => strpos($content, 'message') !== false - ]; - - foreach ($validations as $field => $found) { - if (!$found) { - throw new Exception("Campo '$field' no está siendo validado"); - } - } - - return "Validaciones básicas presentes"; - }); - - // Test 4: Verificar integración con WhatsApp - $this->apiTest("Verificar integración WhatsApp", function() { - $content = file_get_contents(__DIR__ . '/../api/send_message.php'); - - if (strpos($content, 'WhatsAppService') === false && - strpos($content, 'sendMessage') === false) { - throw new Exception("No integra con servicio de WhatsApp"); - } - - return "Integración WhatsApp presente"; - }); - } - - private function testBroadcastAPI() { - echo "

📢 Tests de API send_broadcast.php

"; - - $this->apiTest("Verificar archivo send_broadcast existe", function() { - if (!file_exists(__DIR__ . '/../api/send_broadcast.php')) { - throw new Exception("Archivo send_broadcast.php no existe"); - } - return "Archivo de broadcast existe"; - }); - - // Test de contenido del archivo - $this->apiTest("Verificar estructura de broadcast", function() { - $content = file_get_contents(__DIR__ . '/../api/send_broadcast.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("API no requiere autenticación"); - } - - if (strpos($content, 'recipients') === false && strpos($content, 'users') === false) { - throw new Exception("No maneja lista de destinatarios"); - } - - return "Estructura de broadcast correcta"; - }); - } - - private function testTemplateAPIs() { - echo "

📝 Tests de APIs de Plantillas

"; - - // Test get_templates.php - $this->apiTest("API get_templates existe y estructura", function() { - if (!file_exists(__DIR__ . '/../api/get_templates.php')) { - throw new Exception("get_templates.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/get_templates.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("get_templates no requiere autenticación"); - } - - if (strpos($content, 'message_templates') === false) { - throw new Exception("No consulta tabla message_templates"); - } - - return "API get_templates correcta"; - }); - - // Test save_template.php - $this->apiTest("API save_template existe y validaciones", function() { - if (!file_exists(__DIR__ . '/../api/save_template.php')) { - throw new Exception("save_template.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/save_template.php'); - - $validations = [ - 'autenticación' => strpos($content, 'requireAuthentication()') !== false, - 'name' => strpos($content, 'name') !== false, - 'template_name' => strpos($content, 'template_name') !== false, - 'language_code' => strpos($content, 'language_code') !== false - ]; - - foreach ($validations as $check => $found) { - if (!$found) { - throw new Exception("Validación '$check' faltante"); - } - } - - return "API save_template completa"; - }); - - // Test update_template_status.php - $this->apiTest("API update_template_status existe", function() { - if (!file_exists(__DIR__ . '/../api/update_template_status.php')) { - throw new Exception("update_template_status.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/update_template_status.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("update_template_status no requiere autenticación"); - } - - if (strpos($content, 'status') === false) { - throw new Exception("No maneja campo status"); - } - - return "API update_template_status correcta"; - }); - - // Test de integración con base de datos - $this->apiTest("Verificar tablas de plantillas", function() { - $db = Database::getInstance(); - - // Verificar que la tabla existe - $result = $db->fetchAll("SHOW TABLES LIKE 'message_templates'"); - $tables = $result ? $result : []; - - if (empty($tables)) { - throw new Exception("Tabla message_templates no existe"); - } - - // Verificar estructura de la tabla - $columns = $db->fetchAll("DESCRIBE message_templates"); - if (!$columns || empty($columns)) { - throw new Exception("No se puede obtener estructura de message_templates"); - } - - $requiredColumns = ['id', 'name', 'template_name', 'status', 'created_at']; - - $existingColumns = array_map(function($col) { - return $col['Field']; - }, $columns); - - foreach ($requiredColumns as $required) { - if (!in_array($required, $existingColumns)) { - throw new Exception("Columna '$required' faltante en message_templates"); - } - } - - return "Estructura de tabla message_templates correcta"; - }); - } - - private function testUserAPIs() { - echo "

👥 Tests de APIs de Usuarios

"; - - // Test get_users.php - $this->apiTest("API get_users existe y estructura", function() { - if (!file_exists(__DIR__ . '/../api/get_users.php')) { - throw new Exception("get_users.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/get_users.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("get_users no requiere autenticación"); - } - - if (strpos($content, 'users') === false && strpos($content, 'conversations') === false) { - throw new Exception("No consulta tablas de usuarios"); - } - - return "API get_users correcta"; - }); - - // Test export_users.php - $this->apiTest("API export_users existe", function() { - if (!file_exists(__DIR__ . '/../api/export_users.php')) { - throw new Exception("export_users.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/export_users.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("export_users no requiere autenticación"); - } - - if (strpos($content, 'CSV') === false && strpos($content, 'header') === false) { - throw new Exception("No implementa exportación CSV"); - } - - return "API export_users correcta"; - }); - - // Verificar tabla de usuarios - $this->apiTest("Verificar estructura tabla usuarios", function() { - $db = Database::getInstance(); - - // Verificar tabla conversations (usuarios) - $result = $db->fetchAll("SHOW TABLES LIKE 'conversations'"); - if (!$result || empty($result)) { - throw new Exception("Tabla conversations no existe"); - } - - return "Estructura de usuarios correcta"; - }); - } - - private function testConfigurationAPIs() { - echo "

⚙️ Tests de APIs de Configuración

"; - - // Test get_system_config.php - $this->apiTest("API get_system_config existe", function() { - if (!file_exists(__DIR__ . '/../api/get_system_config.php')) { - throw new Exception("get_system_config.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/get_system_config.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("get_system_config no requiere autenticación"); - } - - return "API get_system_config correcta"; - }); - - // Test save_system_config.php - $this->apiTest("API save_system_config existe", function() { - if (!file_exists(__DIR__ . '/../api/save_system_config.php')) { - throw new Exception("save_system_config.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/save_system_config.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("save_system_config no requiere autenticación"); - } - - $fields = ['business_name', 'whatsapp_token', 'phone_number_id']; - foreach ($fields as $field) { - if (strpos($content, $field) === false) { - throw new Exception("Campo '$field' no está siendo procesado"); - } - } - - return "API save_system_config completa"; - }); - - // Verificar tabla system_config - $this->apiTest("Verificar tabla configuración", function() { - $db = Database::getInstance(); - - $result = $db->fetchAll("SHOW TABLES LIKE 'system_config'"); - if (!$result || empty($result)) { - throw new Exception("Tabla system_config no existe"); - } - - return "Tabla de configuración existe"; - }); - } - - private function testAnalyticsAPIs() { - echo "

📊 Tests de APIs de Analytics

"; - - // Test get_stats.php - $this->apiTest("API get_stats existe y estructura", function() { - if (!file_exists(__DIR__ . '/../api/get_stats.php')) { - throw new Exception("get_stats.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/get_stats.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("get_stats no requiere autenticación"); - } - - $stats = ['total_users', 'conversations_today', 'active_users', 'total_conversations']; - foreach ($stats as $stat) { - if (strpos($content, $stat) === false) { - throw new Exception("Estadística '$stat' no está implementada"); - } - } - - return "API get_stats completa"; - }); - - // Test get_chart_data.php - $this->apiTest("API get_chart_data existe", function() { - if (!file_exists(__DIR__ . '/../api/get_chart_data.php')) { - throw new Exception("get_chart_data.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/get_chart_data.php'); - - if (strpos($content, 'requireAuthentication()') === false) { - throw new Exception("get_chart_data no requiere autenticación"); - } - - return "API get_chart_data correcta"; - }); - - // Test get_recent_messages.php - $this->apiTest("API get_recent_messages existe", function() { - if (!file_exists(__DIR__ . '/../api/get_recent_messages.php')) { - throw new Exception("get_recent_messages.php no existe"); - } - - $content = file_get_contents(__DIR__ . '/../api/get_recent_messages.php'); - - if (strpos($content, 'conversations') === false) { - throw new Exception("No consulta mensajes"); - } - - return "API get_recent_messages correcta"; - }); - - // Test de integridad de datos - $this->apiTest("Verificar tablas de analytics", function() { - $db = Database::getInstance(); - - $tables = ['conversations', 'conversations']; - foreach ($tables as $table) { - $result = $db->fetchAll("SHOW TABLES LIKE '$table'"); - if (!$result || empty($result)) { - throw new Exception("Tabla '$table' necesaria para analytics no existe"); - } - } - - return "Tablas para analytics existen"; - }); - } - - private function callAPI($endpoint, $method = 'GET', $data = null) { - // Configurar el entorno para la llamada - $_SERVER['REQUEST_METHOD'] = $method; - - if ($method === 'POST' && $data) { - $_POST = $data; - // Simular php://input para APIs que lo usan - $GLOBALS['HTTP_RAW_POST_DATA'] = json_encode($data); - } - - ob_start(); - - try { - // Cambiar al directorio padre temporalmente para que las rutas relativas funcionen - $originalDir = getcwd(); - chdir(__DIR__ . '/..'); - - include "api/$endpoint"; - - // Restaurar directorio original - chdir($originalDir); - - $output = ob_get_clean(); - - // Intentar decodificar como JSON - $decoded = json_decode($output, true); - if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) { - // Si no es JSON válido, retornar el output raw - return ['raw_output' => $output]; - } - - return $decoded; - - } catch (Exception $e) { - ob_end_clean(); - // Restaurar directorio en caso de excepción - if (isset($originalDir)) { - chdir($originalDir); - } - throw $e; - } finally { - // Restaurar directorio y limpiar variables globales - if (isset($originalDir)) { - chdir($originalDir); - } - unset($_POST); - unset($GLOBALS['HTTP_RAW_POST_DATA']); - } - } - - private function apiTest($name, $callable) { - try { - $result = $callable(); - echo "
$name: $result
"; - $this->testResults[] = ['name' => $name, 'status' => 'passed', 'message' => $result]; - } catch (Exception $e) { - echo "
$name: " . $e->getMessage() . "
"; - $this->testResults[] = ['name' => $name, 'status' => 'failed', 'message' => $e->getMessage()]; - } - } - - private function showAPITestSummary() { - $total = count($this->testResults); - $passed = count(array_filter($this->testResults, function($r) { return $r['status'] === 'passed'; })); - $failed = $total - $passed; - $successRate = $total > 0 ? ($passed / $total) * 100 : 0; - - $alertClass = $successRate === 100.0 ? 'success' : ($successRate >= 80 ? 'warning' : 'danger'); - - echo "
"; - echo "

Resumen Tests de APIs

"; - echo "

Total de tests API: $total | "; - echo "Pasaron: $passed | "; - echo "Fallaron: $failed | "; - echo "Éxito: " . number_format($successRate, 1) . "%

"; - echo "
"; - } -} - -// No ejecutar si es incluido por el runner principal -if (basename($_SERVER['PHP_SELF']) === 'api_tests.php') { - $configPath = __DIR__ . '/../config/config.php'; - if (file_exists($configPath)) { - require_once $configPath; - } - $suite = new APITestSuite(); - $suite->runAllAPITests(); -} -?> \ No newline at end of file diff --git a/tests/output.html b/tests/output.html deleted file mode 100644 index 43ee9e9..0000000 Binary files a/tests/output.html and /dev/null differ diff --git a/tests/output_after_fix.html b/tests/output_after_fix.html deleted file mode 100644 index 5ef683a..0000000 Binary files a/tests/output_after_fix.html and /dev/null differ diff --git a/tests/output_final.html b/tests/output_final.html deleted file mode 100644 index 50e807f..0000000 Binary files a/tests/output_final.html and /dev/null differ diff --git a/tests/run_all_tests.php b/tests/run_all_tests.php deleted file mode 100644 index dbd4613..0000000 --- a/tests/run_all_tests.php +++ /dev/null @@ -1,644 +0,0 @@ -startTime = microtime(true); - } - - public function runAllTests() { - echo $this->getHtmlHeader(); - - echo "

🧪 Suite de Tests Completa - WhatsApp Bot

"; - - echo "
"; - echo "
Suite de Tests Comprensiva
"; - echo "

Esta suite ejecuta más de 50 tests cubriendo todas las funcionalidades críticas:

"; - echo "
    "; - echo "
  • Infraestructura: Archivos, configuración, base de datos
  • "; - echo "
  • APIs: Todas las 17 APIs del sistema
  • "; - echo "
  • Servicios: WhatsAppService, BotService, Webhook
  • "; - echo "
  • Seguridad: Autenticación, autorización, validación
  • "; - echo "
  • Plantillas: Sistema completo implementado
  • "; - echo "
  • Integración: Flujos end-to-end
  • "; - echo "
"; - echo "
"; - - // Tests de infraestructura - $this->runInfrastructureTests(); - - // Tests de servicios - $this->runServiceTests(); - - // Tests específicos incluidos - $this->runSpecializedTests(); - - // Tests de APIs - $this->runAPITests(); - - // Tests de webhook - $this->runWebhookTests(); - - // Tests de seguridad - $this->runSecurityTests(); - - // Tests de plantillas - $this->runTemplateTests(); - - // Tests de base de datos - $this->runDatabaseTests(); - - $this->showSummary(); - echo $this->getHtmlFooter(); - } - - private function runSpecializedTests() { - $this->showTestSection("🔬 Tests Especializados"); - - $this->test("Ejecutar Suite de APIs", function() { - ob_start(); - - require_once 'tests/api_tests.php'; - $apiSuite = new APITestSuite(); - $apiSuite->runAllAPITests(); - - $output = ob_get_clean(); - - // Contar tests exitosos en la salida - $successCount = substr_count($output, 'alert-success'); - $errorCount = substr_count($output, 'alert-danger'); - - if ($errorCount > $successCount) { - throw new Exception("APIs: $errorCount errores, $successCount éxitos"); - } - - return "Suite de APIs: $successCount tests pasaron, $errorCount fallaron"; - }); - - $this->test("Ejecutar Suite de Servicios", function() { - ob_start(); - - require_once 'tests/service_tests.php'; - $serviceSuite = new ServiceTestSuite(); - $serviceSuite->runAllServiceTests(); - - $output = ob_get_clean(); - - $successCount = substr_count($output, 'alert-success'); - $errorCount = substr_count($output, 'alert-danger'); - - return "Suite de Servicios: $successCount tests pasaron, $errorCount fallaron"; - }); - - $this->test("Ejecutar Suite de Webhook", function() { - ob_start(); - - require_once 'tests/webhook_tests.php'; - $webhookSuite = new WebhookTestSuite(); - $webhookSuite->runAllWebhookTests(); - - $output = ob_get_clean(); - - $successCount = substr_count($output, 'alert-success'); - $errorCount = substr_count($output, 'alert-danger'); - - return "Suite de Webhook: $successCount tests pasaron, $errorCount fallaron"; - }); - - $this->test("Ejecutar Suite de Seguridad", function() { - ob_start(); - - require_once 'tests/security_tests.php'; - $securitySuite = new SecurityTestSuite(); - $securitySuite->runAllSecurityTests(); - - $output = ob_get_clean(); - - $successCount = substr_count($output, 'alert-success'); - $warningCount = substr_count($output, 'alert-warning'); - $errorCount = substr_count($output, 'alert-danger'); - - if ($warningCount > 0) { - return "Suite de Seguridad: $successCount OK, $warningCount advertencias, $errorCount errores"; - } - - return "Suite de Seguridad: $successCount tests pasaron, $errorCount fallaron"; - }); - - $this->test("Ejecutar Suite de Plantillas", function() { - ob_start(); - - require_once 'tests/template_tests.php'; - $templateSuite = new TemplateTestSuite(); - $templateSuite->runAllTemplateTests(); - - $output = ob_get_clean(); - - $successCount = substr_count($output, 'alert-success'); - $errorCount = substr_count($output, 'alert-danger'); - - return "Suite de Plantillas: $successCount tests pasaron, $errorCount fallaron"; - }); - } - - private function runInfrastructureTests() { - $this->showTestSection("🏗️ Tests de Infraestructura"); - - $this->test("Verificar archivos principales", function() { - $required = [ - __DIR__ . '/../config/config.php', - __DIR__ . '/../classes/Database.php', - __DIR__ . '/../services/WhatsAppService.php', - __DIR__ . '/../services/BotService.php', - __DIR__ . '/../api/webhook.php', - __DIR__ . '/../index.php' - ]; - - foreach ($required as $file) { - if (!file_exists($file)) { - throw new Exception("Archivo faltante: $file"); - } - } - return "Todos los archivos principales existen"; - }); - - $this->test("Verificar configuración", function() { - if (!defined('DB_HOST') || !defined('WHATSAPP_TOKEN')) { - throw new Exception("Constantes de configuración faltantes"); - } - return "Configuración cargada correctamente"; - }); - - $this->test("Conexión a base de datos", function() { - $db = Database::getInstance(); - $result = $db->fetch("SELECT 1 as test"); - if ($result['test'] !== 1) { - throw new Exception("Test de conexión fallido"); - } - return "Conexión DB exitosa"; - }); - } - - private function runServiceTests() { - $this->showTestSection("🔧 Tests de Servicios"); - - $this->test("Inicialización WhatsAppService", function() { - $service = new WhatsAppService(); - if (!$service) { - throw new Exception("No se pudo inicializar WhatsAppService"); - } - return "WhatsAppService inicializado correctamente"; - }); - - $this->test("Inicialización BotService", function() { - $service = new BotService(); - if (!$service) { - throw new Exception("No se pudo inicializar BotService"); - } - return "BotService inicializado correctamente"; - }); - - $this->test("Formateo de número de teléfono", function() { - $service = new WhatsAppService(); - $reflection = new ReflectionClass($service); - $method = $reflection->getMethod('formatPhoneNumber'); - $method->setAccessible(true); - - $result1 = $method->invokeArgs($service, ['573001234567']); - $result2 = $method->invokeArgs($service, ['+573001234567']); - - if ($result1 !== '573001234567' || $result2 !== '573001234567') { - throw new Exception("Formateo incorrecto: $result1, $result2"); - } - return "Formateo de números funciona correctamente"; - }); - } - - private function runAPITests() { - $this->showTestSection("🌐 Tests de APIs"); - - // Test API de estadísticas - $this->test("API get_stats.php", function() { - $result = $this->makeAPICall('get_stats.php'); - if (!isset($result['total_users'])) { - throw new Exception("Estructura de respuesta incorrecta"); - } - return "API de estadísticas responde correctamente"; - }); - - // Test API de usuarios - $this->test("API get_users.php", function() { - $result = $this->makeAPICall('get_users.php'); - if (!is_array($result)) { - throw new Exception("Respuesta debe ser un array"); - } - return "API de usuarios responde correctamente"; - }); - - // Test API de plantillas - $this->test("API get_templates.php", function() { - $result = $this->makeAPICall('get_templates.php'); - if (!is_array($result)) { - throw new Exception("Respuesta debe ser un array"); - } - return "API de plantillas responde correctamente"; - }); - - // Test creación de plantilla - $this->test("API save_template.php", function() { - $testData = [ - 'name' => 'Test Template ' . time(), - 'template_name' => 'test_template_' . time(), - 'language_code' => 'es', - 'category' => 'utility' - ]; - - $result = $this->makeAPICall('save_template.php', 'POST', $testData); - if (!$result['success']) { - throw new Exception("Error creando plantilla: " . ($result['error'] ?? 'Unknown')); - } - return "API de creación de plantillas funciona"; - }); - } - - private function runWebhookTests() { - $this->showTestSection("🔗 Tests de Webhook"); - - $this->test("Webhook verification", function() { - $testUrl = '/api/webhook.php?hub_mode=subscribe&hub_verify_token=' . WEBHOOK_VERIFY_TOKEN . '&hub_challenge=test123'; - - // Simular petición GET de verificación - $_GET = [ - 'hub_mode' => 'subscribe', - 'hub_verify_token' => WEBHOOK_VERIFY_TOKEN, - 'hub_challenge' => 'test123' - ]; - - ob_start(); - $webhook = new WhatsAppWebhook(); - $webhook->handleRequest(); - $output = ob_get_clean(); - - if ($output !== 'test123') { - throw new Exception("Verificación de webhook fallida: $output"); - } - return "Webhook verification funcionando"; - }); - - $this->test("Webhook procesamiento de mensaje", function() { - // Test básico de estructura de webhook - $testPayload = [ - 'object' => 'whatsapp_business_account', - 'entry' => [ - [ - 'changes' => [ - [ - 'value' => [ - 'conversations' => [ - [ - 'from' => '573001234567', - 'text' => ['body' => 'test message'], - 'timestamp' => time() - ] - ] - ] - ] - ] - ] - ] - ]; - - // Esto requeriría más setup para simular completamente - return "Estructura de webhook reconocida (test básico)"; - }); - } - - private function runSecurityTests() { - $this->showTestSection("🔒 Tests de Seguridad"); - - $this->test("Verificar autenticación requerida", function() { - // Simular usuario no logueado - unset($_SESSION['admin_logged_in']); - - if (isUserLoggedIn()) { - throw new Exception("Función de autenticación no funciona correctamente"); - } - return "Verificación de autenticación funciona"; - }); - - $this->test("Verificar función requireAuthentication", function() { - unset($_SESSION['admin_logged_in']); - - ob_start(); - try { - requireAuthentication(); - $output = ob_get_clean(); - throw new Exception("requireAuthentication no bloqueó usuario no autenticado"); - } catch (Exception $e) { - $output = ob_get_clean(); - // Si hay output, significa que la función funcionó - if (empty($output)) { - throw new Exception("requireAuthentication no generó respuesta JSON"); - } - } - - return "Función requireAuthentication protege correctamente"; - }); - - $this->test("Validación de tokens", function() { - if (empty(WHATSAPP_TOKEN) || WHATSAPP_TOKEN === 'TU_TOKEN_DE_WHATSAPP_AQUI') { - throw new Exception("Token de WhatsApp no configurado"); - } - - if (empty(WEBHOOK_VERIFY_TOKEN) || WEBHOOK_VERIFY_TOKEN === 'mi_token_secreto_123') { - return "⚠️ Token de webhook usando valor por defecto (cambiar en producción)"; - } - - return "Tokens configurados correctamente"; - }); - } - - private function runTemplateTests() { - $this->showTestSection("📝 Tests de Plantillas"); - - $this->test("Verificar tabla message_templates", function() { - $db = Database::getInstance(); - $tables = $db->fetchAll("SHOW TABLES LIKE 'message_templates'"); - if (empty($tables)) { - throw new Exception("Tabla message_templates no existe"); - } - return "Tabla message_templates existe"; - }); - - $this->test("Insertar plantilla de prueba", function() { - $db = Database::getInstance(); - $testName = 'test_template_' . time(); - - $id = $db->insert('message_templates', [ - 'name' => $testName, - 'template_name' => $testName, - 'language_code' => 'es', - 'category' => 'utility', - 'status' => 'pending' - ]); - - if (!$id) { - throw new Exception("No se pudo insertar plantilla de prueba"); - } - - // Limpiar - $db->execute("DELETE FROM message_templates WHERE id = :id", ['id' => $id]); - - return "Inserción y eliminación de plantillas funciona"; - }); - - $this->test("Estados de plantillas", function() { - $db = Database::getInstance(); - $validStatuses = ['pending', 'approved', 'rejected']; - - // Verificar que la columna acepta solo estos valores - $columns = $db->fetchAll("DESCRIBE message_templates"); - $statusColumn = null; - - foreach ($columns as $column) { - if ($column['Field'] === 'status') { - $statusColumn = $column; - break; - } - } - - if (!$statusColumn || strpos($statusColumn['Type'], 'enum') === false) { - throw new Exception("Columna status no es ENUM o no existe"); - } - - return "Estados de plantillas correctamente definidos"; - }); - } - - private function runDatabaseTests() { - $this->showTestSection("💾 Tests de Base de Datos"); - - $this->test("Verificar todas las tablas principales", function() { - $db = Database::getInstance(); - $requiredTables = [ - 'users', 'conversations', 'menus', 'menu_options', - 'system_config', 'autoresponses', 'message_templates', - 'webhook_logs' - ]; - - $existingTables = []; - foreach ($requiredTables as $table) { - $result = $db->fetchAll("SHOW TABLES LIKE '$table'"); - if (empty($result)) { - throw new Exception("Tabla faltante: $table"); - } - $existingTables[] = $table; - } - - return "Todas las tablas principales existen: " . implode(', ', $existingTables); - }); - - $this->test("CRUD básico en tabla users", function() { - $db = Database::getInstance(); - $testPhone = '573999' . rand(100000, 999999); - - // Create - $id = $db->insert('users', [ - 'phone_number' => $testPhone, - 'name' => 'Test User', - 'status' => 'active' - ]); - - if (!$id) throw new Exception("Insert fallido"); - - // Read - $user = $db->fetch("SELECT * FROM users WHERE id = :id", ['id' => $id]); - if ($user['phone_number'] !== $testPhone) { - throw new Exception("Select fallido"); - } - - // Update - $updated = $db->update('users', ['name' => 'Updated User'], ['id' => $id]); - if (!$updated) throw new Exception("Update fallido"); - - // Delete - $deleted = $db->execute("DELETE FROM users WHERE id = :id", ['id' => $id]); - if (!$deleted) throw new Exception("Delete fallido"); - - return "CRUD completo funciona correctamente"; - }); - - $this->test("Integridad referencial", function() { - $db = Database::getInstance(); - - // Verificar que las foreign keys están definidas - $foreignKeys = $db->fetchAll(" - SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - "); - - if (empty($foreignKeys)) { - return "⚠️ Sin foreign keys definidas (recomendado para integridad)"; - } - - return "Integridad referencial configurada: " . count($foreignKeys) . " FK"; - }); - } - - private function test($name, $callable) { - $this->totalTests++; - - try { - $result = $callable(); - $this->passedTests++; - $this->showTestResult($name, true, $result); - } catch (Exception $e) { - $this->failedTests++; - $this->showTestResult($name, false, $e->getMessage()); - } - } - - private function makeAPICall($endpoint, $method = 'GET', $data = null) { - // Simular sesión de administrador para tests - $_SESSION['admin_logged_in'] = true; - $_SESSION['last_activity'] = time(); - - $url = "api/$endpoint"; - - if ($method === 'GET' && $data) { - $url .= '?' . http_build_query($data); - } - - $context = stream_context_create([ - 'http' => [ - 'method' => $method, - 'header' => 'Content-Type: application/json', - 'content' => $data ? json_encode($data) : null - ] - ]); - - // Para tests, incluir el archivo directamente - ob_start(); - - if ($method === 'POST' && $data) { - // Simular POST data - $_POST = $data; - file_put_contents('php://input', json_encode($data)); - } - - try { - include $url; - $output = ob_get_clean(); - return json_decode($output, true); - } catch (Exception $e) { - ob_get_clean(); - throw $e; - } - } - - private function showTestSection($title) { - echo "
"; - echo "
$title
"; - echo "
"; - } - - private function showTestResult($name, $passed, $message) { - $class = $passed ? 'success' : 'danger'; - $icon = $passed ? 'check-circle' : 'times-circle'; - - echo "
"; - echo " "; - echo "$name: $message"; - echo "
"; - - if (!$passed) { - echo "
"; // Cerrar card si hay error - } - } - - private function showSummary() { - echo ""; // Cerrar última card - - $successRate = $this->totalTests > 0 ? ($this->passedTests / $this->totalTests) * 100 : 0; - $duration = round((microtime(true) - $this->startTime) * 1000, 2); - - $alertClass = $successRate === 100.0 ? 'success' : ($successRate >= 80 ? 'warning' : 'danger'); - - echo "
"; - echo "

Resumen de Tests

"; - echo "
"; - echo "
Total: {$this->totalTests}
"; - echo "
Pasaron: {$this->passedTests}
"; - echo "
Fallaron: {$this->failedTests}
"; - echo "
Éxito: " . number_format($successRate, 1) . "%
"; - echo "
"; - echo "

Tiempo de ejecución: {$duration}ms

"; - - if ($successRate === 100.0) { - echo "

🎉 ¡Todos los tests pasaron! El sistema está funcionando correctamente.

"; - } else if ($successRate >= 80) { - echo "

⚠️ La mayoría de tests pasaron, pero hay algunas fallas que requieren atención.

"; - } else { - echo "

🚨 Múltiples fallas detectadas. El sistema requiere correcciones urgentes.

"; - } - echo "
"; - } - - private function getHtmlHeader() { - return ' - - - - - - Test Suite - WhatsApp Bot - - - - -
- '; - } - - private function getHtmlFooter() { - return ' - -
-

Suite de tests ejecutada el: ' . date('Y-m-d H:i:s') . '

-
- - - '; - } -} - -// Ejecutar todos los tests -$runner = new TestRunner(); -$runner->runAllTests(); -?> \ No newline at end of file diff --git a/tests/security_tests.php b/tests/security_tests.php deleted file mode 100644 index 06c0f91..0000000 --- a/tests/security_tests.php +++ /dev/null @@ -1,575 +0,0 @@ -db = Database::getInstance(); - $this->originalSession = $_SESSION ?? []; - } - - public function runAllSecurityTests() { - echo "

🔒 Tests Exhaustivos de Seguridad

"; - - $this->testAuthentication(); - $this->testAuthorization(); - $this->testSessionSecurity(); - $this->testInputValidation(); - $this->testSQLInjectionPrevention(); - $this->testXSSPrevention(); - $this->testCSRFProtection(); - $this->testRateLimiting(); - - $_SESSION = $this->originalSession; - } - - private function testAuthentication() { - echo "

🔐 Tests de Autenticación

"; - - $this->securityTest("Función isUserLoggedIn - usuario no logueado", function() { - unset($_SESSION['admin_logged_in']); - - if (isUserLoggedIn()) { - throw new Exception("isUserLoggedIn retorna true sin sesión activa"); - } - - return "Usuario no logueado detectado correctamente"; - }); - - $this->securityTest("Función isUserLoggedIn - usuario logueado", function() { - $_SESSION['admin_logged_in'] = true; - $_SESSION['last_activity'] = time(); - - if (!isUserLoggedIn()) { - throw new Exception("isUserLoggedIn retorna false con sesión activa"); - } - - return "Usuario logueado detectado correctamente"; - }); - - $this->securityTest("Función requireAuthentication bloquea acceso", function() { - unset($_SESSION['admin_logged_in']); - - ob_start(); - - try { - requireAuthentication(); - $output = ob_get_clean(); - throw new Exception("requireAuthentication no bloqueó usuario no autenticado"); - - } catch (Exception $e) { - $output = ob_get_clean(); - - // Verificar que hay salida JSON de error - $decoded = json_decode($output, true); - - if (!$decoded || !isset($decoded['success']) || $decoded['success'] !== false) { - throw new Exception("requireAuthentication no retorna JSON de error apropiado"); - } - - return "requireAuthentication bloquea correctamente usuarios no autenticados"; - } - }); - - $this->securityTest("Timeout de sesión funciona", function() { - $_SESSION['admin_logged_in'] = true; - $_SESSION['last_activity'] = time() - (SESSION_TIMEOUT + 100); // Sesión expirada - - // Simular verificación de timeout (normalmente en config.php) - if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > SESSION_TIMEOUT) { - unset($_SESSION['admin_logged_in']); - } - - if (isUserLoggedIn()) { - throw new Exception("Sesión expirada no fue invalidada"); - } - - return "Timeout de sesión funciona correctamente"; - }); - - $this->securityTest("Verificación de contraseña hash", function() { - $testPassword = 'test123'; - $hash = password_hash($testPassword, PASSWORD_DEFAULT); - - if (!password_verify($testPassword, $hash)) { - throw new Exception("Verificación de hash falló"); - } - - if (password_verify('wrong_password', $hash)) { - throw new Exception("Hash acepta contraseña incorrecta"); - } - - return "Sistema de hash de contraseñas funciona correctamente"; - }); - } - - private function testAuthorization() { - echo "

👤 Tests de Autorización

"; - - $this->securityTest("APIs requieren autenticación", function() { - $protectedAPIs = [ - 'get_stats.php', 'get_users.php', 'send_message.php', - 'save_template.php', 'get_templates.php', 'save_system_config.php' - ]; - - unset($_SESSION['admin_logged_in']); - - foreach ($protectedAPIs as $api) { - ob_start(); - - try { - // Cambiar al directorio padre temporalmente - $originalDir = getcwd(); - chdir(__DIR__ . '/..'); - - include "api/$api"; - - // Restaurar directorio original - chdir($originalDir); - - $output = ob_get_clean(); - - // Si no retorna error 401, falla el test - if (strpos($output, '"success":false') === false) { - throw new Exception("API $api no requiere autenticación"); - } - - } catch (Exception $e) { - ob_end_clean(); - - // Restaurar directorio en caso de excepción - if (isset($originalDir)) { - chdir($originalDir); - } - - // Si hay excepción por no estar autenticado, está bien - if (strpos($e->getMessage(), 'autenticación') === false && - strpos($e->getMessage(), 'autorizado') === false) { - throw $e; - } - } - } - - return "APIs críticas requieren autenticación: " . count($protectedAPIs) . " APIs protegidas"; - }); - - $this->securityTest("Panel principal redirige sin login", function() { - unset($_SESSION['admin_logged_in']); - - // Simular acceso a index.php - ob_start(); - - try { - // El index.php ahora debería redirigir - $_SERVER['REQUEST_URI'] = '/index.php'; - - // Verificar que la verificación está implementada - $indexContent = file_get_contents('index.php'); - - if (strpos($indexContent, 'isUserLoggedIn()') === false) { - throw new Exception("index.php no verifica autenticación"); - } - - return "Panel principal protegido con verificación de login"; - - } finally { - ob_end_clean(); - } - }); - } - - private function testSessionSecurity() { - echo "

🛡️ Tests de Seguridad de Sesión

"; - - $this->securityTest("Configuración de sesión segura", function() { - $issues = []; - - // Verificar configuración de cookies - if (!ini_get('session.cookie_httponly')) { - $issues[] = "session.cookie_httponly debería estar habilitado"; - } - - if (ini_get('session.cookie_secure') && !isset($_SERVER['HTTPS'])) { - $issues[] = "session.cookie_secure habilitado sin HTTPS"; - } - - if (ini_get('session.use_only_cookies') != 1) { - $issues[] = "session.use_only_cookies debería estar habilitado"; - } - - if (!empty($issues)) { - return "⚠️ Mejoras recomendadas: " . implode(', ', $issues); - } - - return "Configuración de sesión es apropiada"; - }); - - $this->securityTest("Regeneración de ID de sesión", function() { - session_start(); - $oldId = session_id(); - - session_regenerate_id(true); - $newId = session_id(); - - if ($oldId === $newId) { - throw new Exception("ID de sesión no se regeneró"); - } - - return "Regeneración de ID de sesión funciona"; - }); - - $this->securityTest("Validación de IP de sesión", function() { - $_SESSION['admin_logged_in'] = true; - $_SESSION['login_ip'] = '192.168.1.1'; - - $currentIp = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; - - // En un sistema más seguro, esto debería validarse - if (isset($_SESSION['login_ip']) && $_SESSION['login_ip'] !== $currentIp) { - // Esto es opcional, pero es una buena práctica - return "⚠️ Validación de IP no implementada (feature de seguridad adicional)"; - } - - return "Validación de IP de sesión (básica)"; - }); - } - - private function testInputValidation() { - echo "

✅ Tests de Validación de Entrada

"; - - $this->securityTest("Validación de número de teléfono", function() { - $validNumbers = ['+573001234567', '573001234567', '57 300 123 4567']; - $invalidNumbers = ['123', 'abc', '', '+1', '++573001234567', '', - '"; DROP TABLE users; --', - '../../../etc/passwd', - '', - 'javascript:alert(1)' - ]; - - foreach ($maliciousInputs as $input) { - // Test crear plantilla con entrada maliciosa - try { - $_SESSION['admin_logged_in'] = true; - - $testData = [ - 'name' => $input, - 'template_name' => 'safe_name', - 'language_code' => 'es', - 'category' => 'utility' - ]; - - ob_start(); - $_POST = $testData; - $GLOBALS['HTTP_RAW_POST_DATA'] = json_encode($testData); - - include 'api/save_template.php'; - - $output = ob_get_clean(); - $result = json_decode($output, true); - - // Si se creó exitosamente, verificar que se sanitizó - if (isset($result['success']) && $result['success']) { - $created = $this->db->fetch( - "SELECT * FROM message_templates WHERE template_name = 'safe_name' ORDER BY id DESC LIMIT 1" - ); - - if ($created && $created['name'] === $input) { - // Limpiar - $this->db->execute("DELETE FROM message_templates WHERE id = :id", ['id' => $created['id']]); - throw new Exception("Entrada maliciosa '$input' no fue sanitizada"); - } - - if ($created) { - $this->db->execute("DELETE FROM message_templates WHERE id = :id", ['id' => $created['id']]); - } - } - - } catch (Exception $e) { - ob_end_clean(); - // Si rechaza entrada maliciosa, está bien - continue; - } finally { - unset($_POST); - unset($GLOBALS['HTTP_RAW_POST_DATA']); - } - } - - return "Entradas maliciosas son filtradas o rechazadas"; - }); - } - - private function testSQLInjectionPrevention() { - echo "

💉 Tests de Prevención de Inyección SQL

"; - - $this->securityTest("Prepared statements en Database class", function() { - $db = Database::getInstance(); - - // Test con entrada potencialmente maliciosa - $maliciousId = "1'; DROP TABLE users; --"; - - try { - $result = $db->fetch( - "SELECT * FROM users WHERE id = :id", - ['id' => $maliciousId] - ); - - // Verificar que la tabla users sigue existiendo - $tableCheck = $db->fetchAll("SHOW TABLES LIKE 'users'"); - - if (empty($tableCheck)) { - throw new Exception("Tabla eliminada - posible inyección SQL exitosa"); - } - - return "Prepared statements previenen inyección SQL básica"; - - } catch (Exception $e) { - // Si hay error por tipo de dato incorrecto, está bien - if (strpos($e->getMessage(), 'DROP') !== false) { - throw new Exception("Inyección SQL detectada en error"); - } - - return "Prepared statements rechazan entrada maliciosa"; - } - }); - - $this->securityTest("Validación en búsquedas de usuario", function() { - $maliciousPhone = "'; SELECT password FROM admin_users; --"; - - try { - $webhook = new WhatsAppWebhook(); - $reflection = new ReflectionClass($webhook); - $method = $reflection->getMethod('getUserByPhone'); - $method->setAccessible(true); - - $result = $method->invokeArgs($webhook, [$maliciousPhone]); - - // No debería retornar datos de otras tablas - if (is_array($result) && isset($result['password'])) { - throw new Exception("Posible inyección SQL - datos no esperados retornados"); - } - - return "Búsqueda de usuarios resistente a inyección SQL"; - - } catch (Exception $e) { - // Error esperado con entrada maliciosa - return "Búsqueda de usuarios rechaza entrada maliciosa"; - } - }); - } - - private function testXSSPrevention() { - echo "

🚫 Tests de Prevención XSS

"; - - $this->securityTest("Escape de salida en APIs", function() { - $xssPayloads = [ - '', - '">', - 'javascript:alert(1)', - '\'-alert(1)-\'', - '<script>alert(1)</script>' - ]; - - // Test con API que retorna datos del usuario - $_SESSION['admin_logged_in'] = true; - - foreach ($xssPayloads as $payload) { - // Crear usuario con payload XSS - $userId = $this->db->insert('users', [ - 'phone_number' => '573000000001', - 'name' => $payload, - 'status' => 'active' - ]); - - if ($userId) { - ob_start(); - include 'api/get_users.php'; - $output = ob_get_clean(); - - $data = json_decode($output, true); - - if (is_array($data)) { - foreach ($data as $user) { - if (isset($user['name']) && $user['name'] === $payload) { - // Limpiar - $this->db->execute("DELETE FROM users WHERE id = :id", ['id' => $userId]); - - // En JSON, esto es relativamente seguro, pero verificar - if (strpos($payload, '', // XSS - 'SELECT * FROM users', // SQL-like - 'template with \x00 null byte' - ]; - - foreach ($invalidNames as $name) { - try { - $id = $this->db->insert('message_templates', [ - 'name' => $name, - 'template_name' => 'test_validation_' . time(), - 'language_code' => 'es', - 'status' => 'pending' - ]); - - if ($id) { - $this->testTemplateIds[] = $id; - - // Verificar que se sanitizó o rechazó apropiadamente - $saved = $this->db->fetch( - "SELECT name FROM message_templates WHERE id = :id", - ['id' => $id] - ); - - if ($saved['name'] === $name && ( - strpos($name, ' - - \ No newline at end of file