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 "
";
- echo "
";
-
- 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 "";
-?>
\ 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: = date('Y-m-d H:i:s') ?>
-
═══════════════════════════════════════════
-
- 📋 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 "
- ID
- Teléfono
- Dir
- Tipo
- Content
- Media URL
- Fecha
- ";
-
-foreach ($conversations as $msg) {
- echo "";
- echo "{$msg['id']} ";
- echo "{$msg['phone_number']} ";
- echo "{$msg['direction']} ";
- echo "{$msg['message_type']} ";
- echo "" . htmlspecialchars(substr($msg['content'], 0, 200)) . " ";
- echo "" . htmlspecialchars(substr($msg['media_url'], 0, 100)) . " ";
- echo "" . date('H:i:s', strtotime($msg['created_at'])) . " ";
- echo " ";
-}
-
-echo "
";
-
-echo "Análisis: ";
-echo "";
-echo "Si 'Content' tiene JSON → Problema: se guardó la respuesta de WhatsApp en lugar del caption ";
-echo "Si 'Media URL' está vacía o tiene JSON → Problema: no se guardó correctamente la URL ";
-echo "Si 'Media URL' tiene un ID (sin http) → Problema: es el media_id de WhatsApp, no la URL local ";
-echo "Correcto: Media URL debería tener http://localhost/uploads/... ";
-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 API Simple
-
Test Configuraciones
-
Test WhatsApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Guardar Test
-
-
-
-
-
-
-
-
-
-
-
-
-
Verificando estado...
-
-
-
-
-
-
-
-
-
🔄 Cargar
-
-
Haz clic en "Cargar" para ver las configuraciones
-
-
-
-
-
-
-
-
-
-
-
-
\ 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 '
';
- echo '¿Estás usando el usuario correcto? (' . ADMIN_USERNAME . ') ';
- echo '¿Tu IP está bloqueada por intentos fallidos? ';
- echo '¿Hay espacios extra en la contraseña? ';
- 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.
';
- }
- }
- }
- }
- }
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
📋 Pasos Recomendados:
-
- Prueba la contraseña que crees que debería funcionar
- Si no funciona, usa Reset de Emergencia (admin123)
- Limpia los bloqueos si tu IP está bloqueada
- Prueba el login en login.php
- Cambia la contraseña por una segura una vez dentro
-
-
-
-
-
🔒 Recordatorios de Seguridad:
-
- Elimina este archivo después de usarlo
- Usa siempre contraseñas seguras en producción
- No dejes archivos de diagnóstico en servidores públicos
-
-
-
-
-
\ 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 'ID Usuario Nombre Email Estado Creado Último Login ';
-
- foreach ($admins as $admin) {
- echo '';
- echo '' . $admin['id'] . ' ';
- echo '' . htmlspecialchars($admin['username']) . ' ';
- echo '' . htmlspecialchars($admin['full_name'] ?? 'Sin nombre') . ' ';
- echo '' . htmlspecialchars($admin['email'] ?? 'Sin email') . ' ';
- echo '✅ Activo' : 'inactive">❌ Inactivo') . ' ';
- echo '' . ($admin['created_at'] ?? 'No disponible') . ' ';
- echo '' . ($admin['last_login'] ?? 'Nunca') . ' ';
- echo ' ';
- }
- echo '
';
- }
- }
-
- // 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
-
-
-
-
-
-
-
⚠️ Notas de Seguridad:
-
- Esta herramienta solo funciona desde localhost por seguridad
- Elimínala en producción
- Los errores de autenticación se registran en logs del sistema
- Si el sistema está migrado, solo usuarios en la tabla admin_users pueden loguearse
-
-
-
-
-
\ 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
-
- Parámetro Valor Estado ";
-
-$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 "$key $display_value $status ";
-}
-
-echo "
";
-
-// 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 "Clave Valor ";
- foreach ($configs as $config) {
- $display = ($config['config_key'] === 'whatsapp_token') ?
- substr($config['config_value'], 0, 20) . '...' :
- $config['config_value'];
- echo "" . htmlspecialchars($config['config_key']) . " " . htmlspecialchars($display) . " ";
- }
- echo "
";
- } 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:
-
- Verifique que el Phone Number ID sea correcto
- Asegúrese de que el token tenga permisos para el número
- Verifique que la aplicación esté aprobada para producción
- Compruebe que el número esté verificado en Meta Business
- ";
-
-foreach ($recommendations as $rec) {
- echo "
$rec
";
-}
-
-echo "
";
-
-echo "
-
🛠️ Acciones Rápidas
-
-
";
-
-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
-
-
-
-
-
-
-
-
-
-
-
💬 Conversaciones
- 🔄 Actualizar
-
-
-
-
-
-
Cargando conversaciones...
-
-
-
-
-
-
-
-
👥 Usuarios
- 🔄 Actualizar
-
-
-
-
-
-
Cargando usuarios...
-
-
-
-
-
-
-
-
⚙️ Configuraciones
-
-
-
-
-
-
-
-
-
-
-
-
\ 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
-
-
-
-
-
-
-
-
-
-
Laboratorio Ximena Caicedo
-
Instalación del Sistema de Chatbot WhatsApp
-
-
-
- ';
- 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 'Configura tus credenciales de WhatsApp Business API en el panel de administración ';
- echo 'Prueba el menú principal enviando un mensaje de WhatsApp ';
- echo 'Revisa las respuestas automáticas en la sección correspondiente ';
- echo 'Personaliza los mensajes según las necesidades del laboratorio ';
- 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 'Verifica que la base de datos existe y tiene permisos correctos ';
- echo 'Revisa la configuración en config/config.php ';
- echo 'Consulta el error arriba para más detalles ';
- echo 'Intenta ejecutar la instalación nuevamente ';
- echo ' ';
- echo '
';
- echo '';
- echo ' Reintentar Instalación';
- 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 = MAX_LOGIN_ATTEMPTS ?> intentos por IP
- Bloqueo de = LOGIN_LOCKOUT_TIME / 60 ?> 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 "";
- 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, '
-
-