commit ce12ac2ccec07387e0a5c96e3d8440e59e765e8b
Author: lizandrogd <77708265+lizandrogd@users.noreply.github.com>
Date: Tue Nov 18 20:26:36 2025 -0500
w
diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md
new file mode 100644
index 0000000..4cc8159
--- /dev/null
+++ b/DEPLOYMENT_GUIDE.md
@@ -0,0 +1,359 @@
+# 🚀 Guía de Despliegue en Servidor
+
+## 📋 Requisitos del Servidor
+
+### Mínimos
+- **PHP**: 8.0 o superior
+- **MySQL/MariaDB**: 5.7 o superior
+- **Servidor Web**: Apache/Nginx
+- **SSL**: Certificado válido (HTTPS obligatorio para WhatsApp)
+- **Memoria RAM**: Mínimo 512MB
+- **Espacio**: 1GB libre
+
+### Extensiones PHP Requeridas
+```bash
+php-pdo
+php-pdo-mysql
+php-curl
+php-json
+php-mbstring
+php-openssl
+```
+
+## 🌍 Configuración por Tipo de Servidor
+
+### 🔹 **VPS/Servidor Dedicado (Ubuntu/CentOS)**
+
+#### 1. Instalar Dependencias
+```bash
+# Ubuntu/Debian
+sudo apt update
+sudo apt install apache2 mysql-server php8.0 php8.0-mysql php8.0-curl php8.0-json php8.0-mbstring
+
+# CentOS/RHEL
+sudo yum install httpd mysql-server php php-mysql php-curl php-json php-mbstring
+```
+
+#### 2. Configurar Apache
+```apache
+# /etc/apache2/sites-available/whatsapp-bot.conf
+
+ ServerName tudominio.com
+ DocumentRoot /var/www/html/bot
+
+
+ AllowOverride All
+ Require all granted
+
+
+ # Redirigir HTTP a HTTPS
+ RewriteEngine On
+ RewriteCond %{HTTPS} off
+ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+
+
+ ServerName tudominio.com
+ DocumentRoot /var/www/html/migrador
+
+ SSLEngine on
+ SSLCertificateFile /path/to/your/certificate.crt
+ SSLCertificateKeyFile /path/to/your/private.key
+
+
+ AllowOverride All
+ Require all granted
+
+
+```
+
+#### 3. Subir Archivos
+```bash
+# Crear directorio
+sudo mkdir -p /var/www/html/migrador
+
+# Subir archivos (usando SCP/SFTP)
+scp -r bot/* usuario@servidor:/var/www/html/bot/
+
+# Configurar permisos
+sudo chown -R www-data:www-data /var/www/html/bot
+sudo chmod -R 755 /var/www/html/bot
+```
+
+### 🔹 **Hosting Compartido (cPanel/Plesk)**
+
+#### 1. Subir Archivos
+- Accede a tu **File Manager** en cPanel
+- Ve a la carpeta `public_html`
+- Crea carpeta `migrador` (o sube directamente a raíz)
+- Sube todos los archivos del proyecto
+
+#### 2. Configurar Base de Datos
+- Ve a **MySQL Databases** en cPanel
+- Crea nueva base de datos: `tuusuario_whatsapp`
+- Crea usuario y asigna privilegios
+- Anota: host, usuario, contraseña, base de datos
+
+#### 3. Editar Configuración
+```php
+// config/config.php
+define('DB_HOST', 'localhost'); // O tu host específico
+define('DB_NAME', 'tuusuario_whatsapp');
+define('DB_USER', 'tuusuario_dbuser');
+define('DB_PASS', 'tu_password_db');
+```
+
+### 🔹 **Servicios en la Nube**
+
+#### **AWS EC2**
+```bash
+# 1. Conectar por SSH
+ssh -i tu-clave.pem ec2-user@tu-ip-publica
+
+# 2. Instalar LAMP stack
+sudo yum update -y
+sudo amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2
+
+# 3. Configurar y seguir pasos de VPS
+```
+
+#### **DigitalOcean Droplet**
+```bash
+# 1. Crear droplet con LAMP stack preinstalado
+# 2. Conectar por SSH
+# 3. Subir archivos a /var/www/html/
+```
+
+#### **Google Cloud Platform**
+```bash
+# 1. Crear VM con Ubuntu
+# 2. Instalar LAMP stack
+# 3. Configurar firewall para puertos 80/443
+```
+
+## 🔧 Configuración Específica del Sistema
+
+### 1. Editar config/config.php
+```php
+
+```
+
+### 2. Crear .htaccess (Apache)
+```apache
+# .htaccess en raíz del proyecto
+RewriteEngine On
+
+# Forzar HTTPS
+RewriteCond %{HTTPS} off
+RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+# Proteger archivos sensibles
+
+ Require all denied
+
+
+
+ Require all denied
+
+
+# Cache headers
+
+ ExpiresActive on
+ ExpiresByType text/css "access plus 1 year"
+ ExpiresByType application/javascript "access plus 1 year"
+ ExpiresByType image/png "access plus 1 year"
+ ExpiresByType image/jpg "access plus 1 year"
+
+```
+
+### 3. Configurar SSL (Let's Encrypt)
+```bash
+# Instalar Certbot
+sudo apt install certbot python3-certbot-apache
+
+# Obtener certificado
+sudo certbot --apache -d tudominio.com
+
+# Auto-renovación
+sudo crontab -e
+# Agregar: 0 12 * * * /usr/bin/certbot renew --quiet
+```
+
+## 🔗 Configuración de WhatsApp Webhook
+
+### 1. URL del Webhook
+```
+https://tudominio.com/bot/api/webhook.php
+```
+
+### 2. Configurar en Facebook Developers
+1. Ve a [developers.facebook.com](https://developers.facebook.com)
+2. Selecciona tu app de WhatsApp Business
+3. Ve a **WhatsApp > Configuration**
+4. En **Webhooks**:
+ - **Callback URL**: `https://tudominio.com/migrador/api/webhook.php`
+ - **Verify Token**: `mi_token_secreto_123` (o cambia en config.php)
+ - **Webhook fields**: ✅ `messages`
+
+### 3. Verificar Webhook
+```bash
+# Probar webhook manualmente
+curl -X GET "https://tudominio.com/migrador/api/webhook.php?hub.mode=subscribe&hub.challenge=CHALLENGE_ACCEPTED&hub.verify_token=mi_token_secreto_123"
+```
+
+## ⚙️ Configuración Post-Instalación
+
+### 1. Ejecutar Instalador
+```
+https://tudominio.com/migrador/install.php
+```
+
+### 2. Verificar Sistema
+```
+https://tudominio.com/migrador/test.php
+```
+
+### 3. Configurar Panel
+```
+https://tudominio.com/migrador/index.php
+```
+
+## 🔒 Seguridad en Producción
+
+### 1. Configuraciones PHP
+```ini
+# php.ini ajustes
+expose_php = Off
+display_errors = Off
+log_errors = On
+allow_url_fopen = Off
+```
+
+### 2. Permisos de Archivos
+```bash
+# Archivos: 644
+sudo find /var/www/html/migrador -type f -exec chmod 644 {} \;
+
+# Directorios: 755
+sudo find /var/www/html/migrador -type d -exec chmod 755 {} \;
+
+# Config solo lectura
+sudo chmod 600 /var/www/html/migrador/config/config.php
+```
+
+### 3. Backup Automático
+```bash
+#!/bin/bash
+# backup.sh
+DATE=$(date +%Y%m%d_%H%M%S)
+mysqldump -u usuario -p password whatsapp_db > /backups/whatsapp_$DATE.sql
+tar -czf /backups/files_$DATE.tar.gz /var/www/html/migrador
+```
+
+## 📊 Monitoreo y Logs
+
+### 1. Logs de Apache
+```bash
+tail -f /var/log/apache2/error.log
+tail -f /var/log/apache2/access.log
+```
+
+### 2. Logs de PHP
+```bash
+tail -f /var/log/php_errors.log
+```
+
+### 3. Logs del Sistema
+```bash
+# Ver logs desde el panel
+https://tudominio.com/migrador/index.php#logs
+```
+
+## 🚀 Optimizaciones de Rendimiento
+
+### 1. Cache de PHP (OPcache)
+```ini
+# php.ini
+opcache.enable=1
+opcache.memory_consumption=128
+opcache.max_accelerated_files=1000
+```
+
+### 2. Compresión GZIP
+```apache
+# En .htaccess
+
+ AddOutputFilterByType DEFLATE text/plain
+ AddOutputFilterByType DEFLATE text/html
+ AddOutputFilterByType DEFLATE text/css
+ AddOutputFilterByType DEFLATE application/javascript
+
+```
+
+### 3. Optimización MySQL
+```sql
+-- my.cnf
+[mysqld]
+innodb_buffer_pool_size = 256M
+query_cache_type = 1
+query_cache_size = 64M
+```
+
+## 📞 URLs de Producción
+
+Una vez configurado:
+
+- **🏠 Panel**: https://tudominio.com/migrador/index.php
+- **🔗 API**: https://tudominio.com/migrador/api/
+- **📱 Webhook**: https://tudominio.com/migrador/api/webhook.php
+- **📊 Stats**: https://tudominio.com/migrador/api/get_stats.php
+
+## ⚠️ Troubleshooting
+
+### Error 500
+- Revisar logs de Apache/PHP
+- Verificar permisos de archivos
+- Comprobar configuración de base de datos
+
+### Webhook no funciona
+- Verificar SSL válido
+- Comprobar URL pública
+- Revisar token de verificación
+
+### Base de datos no conecta
+- Verificar credenciales
+- Comprobar host/puerto
+- Revisar permisos de usuario MySQL
+
+## 📝 Checklist Final
+
+- [ ] ✅ Servidor con PHP 8.0+ y MySQL
+- [ ] ✅ SSL/HTTPS configurado
+- [ ] ✅ Archivos subidos con permisos correctos
+- [ ] ✅ Base de datos creada y configurada
+- [ ] ✅ config.php editado con datos reales
+- [ ] ✅ Instalador ejecutado sin errores
+- [ ] ✅ Webhook configurado en Facebook
+- [ ] ✅ Sistema probado y funcionando
+- [ ] ✅ Backup automático configurado
+
+¡Tu sistema WhatsApp Bot Manager estará listo para producción! 🎉
\ No newline at end of file
diff --git a/EMERGENCIA_TABLA_NO_EXISTE.md b/EMERGENCIA_TABLA_NO_EXISTE.md
new file mode 100644
index 0000000..dc87930
--- /dev/null
+++ b/EMERGENCIA_TABLA_NO_EXISTE.md
@@ -0,0 +1,146 @@
+# 🚨 EMERGENCIA: "Table doesn't exist" - SOLUCIÓN INMEDIATA
+
+## ⚠️ Tu Error Específico
+```
+❌ Table 'usite_whatsapp_bot.menus' doesn't exist
+❌ Table 'usite_whatsapp_bot.users' doesn't exist
+⚠️ 0 tablas, 0 elementos
+```
+
+**CAUSA:** Las tablas NO se están creando. Todos los instaladores anteriores fallan en tu hosting.
+
+---
+
+## 🚀 SOLUCIÓN DEFINITIVA
+
+### 🎯 **OPCIÓN 1: Instalador Ultra Básico (GARANTIZADO)**
+**👉 [install_ultra_basic.php](install_ultra_basic.php)**
+
+**¿Por qué funcionará?**
+- ✅ Crea SOLO 2 tablas esenciales: `users` y `system_config`
+- ✅ Verifica CADA tabla después de crearla
+- ✅ NO intenta insertar datos si las tablas fallan
+- ✅ Proceso paso a paso con verificación completa
+
+### 🛠️ **OPCIÓN 2: Crear Tablas Manualmente**
+Si incluso el ultra básico falla, hazlo manual:
+
+1. **Ve a phpMyAdmin en tu HestiaCP**
+2. **Selecciona tu BD:** `usite_whatsapp_bot`
+3. **Ejecuta este SQL:**
+
+```sql
+CREATE TABLE IF NOT EXISTS users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ phone_number VARCHAR(20) UNIQUE NOT NULL,
+ name VARCHAR(100),
+ status ENUM('active', 'inactive') DEFAULT 'active',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE IF NOT EXISTS system_config (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ config_key VARCHAR(100) UNIQUE NOT NULL,
+ config_value TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+INSERT INTO system_config (config_key, config_value) VALUES
+('app_installed', '1'),
+('install_date', NOW()),
+('business_name', 'Mi Empresa');
+```
+
+4. **Luego actualiza config/config.php manualmente:**
+```php
+define('DB_HOST', 'localhost');
+define('DB_NAME', 'usite_whatsapp_bot');
+define('DB_USER', 'tu_usuario');
+define('DB_PASS', 'tu_contraseña');
+define('ADMIN_PASSWORD', password_hash('admin123', PASSWORD_DEFAULT));
+```
+
+5. **Crear archivo:** `.installation_completed` (contenido cualquiera)
+
+---
+
+## 📋 Diferencias de los Instaladores
+
+| Instalador | Tablas que crea | Tu resultado anterior |
+|------------|------------------|----------------------|
+| `install.php` | Intenta crear BD | ❌ No tienes privilegios |
+| `install_manual.php` | 7+ tablas complejas | ❌ Falla en parsing |
+| `install_integrated.php` | 7+ tablas, schema interno | ❌ Aún falla creación |
+| `install_simple.php` | 3 tablas básicas | ❌ Probablemente falla |
+| **`install_ultra_basic.php`** | **SOLO 2 tablas** | ✅ **DEBERÍA FUNCIONAR** |
+
+---
+
+## 🔍 ¿Por qué fallan los otros?
+
+### Problemas identificados en tu hosting:
+1. **Permisos restrictivos:** Puede que no tengas CREATE TABLE completo
+2. **Claves foráneas:** Tu MySQL puede rechazar FOREIGN KEY
+3. **Tipos de datos:** Algunos ENUM o JSON no soportados
+4. **Transacciones:** Problemas con múltiples operaciones
+
+### El Ultra Básico evita esto:
+- ✅ Solo 2 tablas simples, sin dependencias
+- ✅ Sin claves foráneas complejas
+- ✅ Sin tipos de datos exóticos
+- ✅ Verifica cada paso antes de continuar
+
+---
+
+## 🎯 PASOS EXACTOS PARA TI
+
+### 1. Ir al Instalador Ultra Básico
+```
+URL: tudominio.com/install_ultra_basic.php
+```
+
+### 2. Usar las mismas credenciales
+- **Host:** localhost
+- **BD:** usite_whatsapp_bot
+- **Usuario:** [el que creaste en HestiaCP]
+- **Contraseña:** [la que configuraste]
+
+### 3. Si funciona verás:
+```
+✅ Conectado a 'usite_whatsapp_bot'
+✅ Tabla 'users' creada y funcional
+✅ Tabla 'system_config' creada y funcional
+✅ Configuración inicial insertada
+✅ config.php actualizado
+✅ Verificación final: 0 usuarios, 4 configs
+🎉 Instalación básica completada
+```
+
+### 4. Después podrás:
+- Entrar al panel con: `admin` / `[contraseña generada]`
+- Probar con: `test.php`
+- Agregar funciones desde el panel
+
+---
+
+## 📞 Si AÚN No Funciona
+
+### Información para soporte:
+1. **Resultado exacto del ultra básico**
+2. **Versión PHP de tu hosting**
+3. **Versión MySQL de tu hosting**
+4. **Permisos exactos del usuario de BD**
+
+### Alternativa extrema:
+Si ningún instalador funciona, el problema es de permisos de tu hosting. Contacta soporte de HestiaCP para revisar permisos de CREATE TABLE.
+
+---
+
+## 🎉 Una vez que funcione:
+
+✅ **Tendrás:** Sistema básico funcional
+✅ **Podrás:** Acceder al panel de admin
+✅ **Siguiente:** Configurar WhatsApp desde el panel
+✅ **Expandir:** Agregar más funciones gradualmente
+
+**👉 [IR AL INSTALADOR ULTRA BÁSICO AHORA](install_ultra_basic.php)** 🚀
\ No newline at end of file
diff --git a/GUIA_HESTIACP.md b/GUIA_HESTIACP.md
new file mode 100644
index 0000000..0b6bcfe
--- /dev/null
+++ b/GUIA_HESTIACP.md
@@ -0,0 +1,158 @@
+# 🛠️ Guía de Instalación en HestiaCP
+
+**Desarrollado por U-Site.app** - [https://u-site.app](https://u-site.app)
+
+## ⚠️ Problema Común en Hostings Compartidos
+
+El instalador automático (`install.php`) **NO FUNCIONA** en hostings compartidos como HestiaCP, cPanel, etc., porque requiere privilegios de administrador para crear bases de datos y usuarios.
+
+## ✅ Solución: Instalación Manual
+
+Usa el archivo `install_manual.php` que he creado específicamente para hostings compartidos.
+
+---
+
+## 📋 Pasos en HestiaCP
+
+### 1. Crear Base de Datos
+1. Entra al panel de HestiaCP
+2. Ve a **"Databases"** o **"Base de datos"**
+3. Haz clic en **"Add Database"**
+4. Configura:
+ - **Database Name:** `whatsapp_bot` (o el nombre que prefieras)
+ - **Database User:** Crea un usuario (ej: `whatsapp_user`)
+ - **Password:** Usa una contraseña segura
+ - **Privileges:** Selecciona **ALL** o todos los permisos
+
+### 2. Anotar Credenciales
+Guarda esta información que necesitarás:
+
+```
+Host: localhost
+Puerto: 3306 (generalmente)
+Base de datos: [el nombre que creaste]
+Usuario: [el usuario que creaste]
+Contraseña: [la contraseña que configuraste]
+```
+
+### 3. Ejecutar Instalación Manual
+1. Abre tu navegador web
+2. Ve a: `https://tudominio.com/ruta-del-bot/install_manual.php`
+3. Completa el formulario con las credenciales del paso anterior
+4. Haz clic en **"Configurar Sistema"**
+
+---
+
+## 🔧 Características del Instalador Manual
+
+### ✅ Lo que SÍ hace:
+- ✅ Conecta con tu BD existente
+- ✅ Crea todas las tablas necesarias
+- ✅ Inserta datos de configuración inicial
+- ✅ Genera contraseña de admin aleatoria
+- ✅ Configura el archivo `config.php`
+- ✅ Detecta automáticamente la URL del servidor
+- ✅ Protege contra reinstalaciones accidentales
+- ✅ Crea archivo de credenciales
+
+### ❌ Lo que NO necesita:
+- ❌ Privilegios de root/administrador
+- ❌ Crear la base de datos (ya la creaste)
+- ❌ Crear el usuario (ya lo creaste)
+- ❌ Configuración especial del servidor
+
+---
+
+## 📁 Estructura Típica en HestiaCP
+
+```
+/public_html/
+├── tu-bot/
+│ ├── install_manual.php ← Usa este archivo
+│ ├── install.php ← NO uses este (no funciona)
+│ ├── config/
+│ ├── api/
+│ └── ...resto de archivos
+```
+
+---
+
+## 🚨 Errores Comunes y Soluciones
+
+### Error: "se queda cargando"
+**Problema:** El instalador automático intenta crear BD con privilegios que no tienes.
+**Solución:** Usa `install_manual.php` en lugar de `install.php`
+
+### Error: "Access denied for user"
+**Problema:** Credenciales incorrectas o permisos insuficientes.
+**Solución:**
+- Verifica que el usuario tenga ALL PRIVILEGES en la BD
+- Confirma host, usuario y contraseña
+- Asegúrate de que la BD existe
+
+### Error: "Connection refused"
+**Problema:** Host o puerto incorrectos.
+**Solución:**
+- Usa `localhost` como host
+- Confirma que el puerto sea 3306
+- Verifica que MySQL esté corriendo
+
+### Error: "Database doesn't exist"
+**Problema:** Nombre de BD incorrecto o no existe.
+**Solución:**
+- Verifica el nombre exacto en el panel de HestiaCP
+- Asegúrate de que la BD fue creada correctamente
+
+---
+
+## 🎯 Después de la Instalación
+
+1. **Guarda las credenciales:** Se generará un archivo `CREDENCIALES_SISTEMA.txt`
+2. **Prueba el sistema:** Ve a `test.php` para verificar
+3. **Configura WhatsApp:** Ve a `server_setup.php`
+4. **Accede al panel:** Ve a `index.php` con usuario `admin`
+
+---
+
+## 📞 Soporte Técnico
+
+- **Email:** support@u-site.app
+- **Web:** https://u-site.app/support
+- **Documentación:** Este archivo y los comentarios en el código
+
+---
+
+## 🔐 Credenciales por Defecto
+
+### Base de Datos
+- **Host:** localhost
+- **Usuario/Contraseña:** Los que TÚ configuraste en HestiaCP
+
+### Panel Admin
+- **Usuario:** admin
+- **Contraseña:** Se genera automáticamente (se muestra durante instalación)
+
+---
+
+## ⚡ Comandos Útiles para Debugging
+
+### Verificar conexión desde terminal:
+```bash
+mysql -h localhost -u tu_usuario -p tu_basedatos
+```
+
+### Ver tablas después de instalar:
+```sql
+USE tu_basedatos;
+SHOW TABLES;
+```
+
+### Ver configuración de usuario:
+```sql
+SELECT User, Host FROM mysql.user WHERE User='tu_usuario';
+SHOW GRANTS FOR 'tu_usuario'@'localhost';
+```
+
+---
+
+**¡Listo! Con esta guía deberías poder instalar sin problemas en HestiaCP.** 🚀
\ No newline at end of file
diff --git a/INSTALACION.md b/INSTALACION.md
new file mode 100644
index 0000000..5494db5
--- /dev/null
+++ b/INSTALACION.md
@@ -0,0 +1,151 @@
+# 🤖 WhatsApp Bot Manager - Guía de Instalación Rápida
+
+## 🚀 Pasos para poner en funcionamiento:
+
+### 1. 📊 Instalar Base de Datos
+- Abrir en navegador: `http://localhost/migrador/install.php`
+- Hacer clic en "Ejecutar Instalación"
+- Verificar que todas las tablas se crearon correctamente
+
+### 2. 🔧 Verificar Sistema
+- Abrir: `http://localhost/migrador/test.php`
+- Revisar que todos los componentes estén en ✅
+
+### 3. 🌐 Acceder al Panel
+- Abrir: `http://localhost/migrador/index.php`
+- Navegar por las diferentes secciones
+
+### 4. ⚙️ Configurar WhatsApp (IMPORTANTE)
+1. Ir a **Configuración** en el panel
+2. Completar:
+ - **Token de WhatsApp**: EAAWTKu1iZAGABP46c7GZAFvHremu9uvTLOlgZCAa4eKUlaCqszzsQjjLcgnqORzo2EBhB7LZAcAqJ7clpGvNCk3oXKnqo8Fl0K2o7eHYoeaqZBesqjBPRfsd4ZBOdY4kN1YBJnmRznegTD9wtJZByVrRcMXF8YXaAJjLCuIFZCK7QFUpId7i4eVkFvbqUYnUKA91rgPvaeGRNZCLHa5ZC1ZB2Skn0gFNgPTBx7bedAMULYZD
+ - **Phone Number ID**: 938177732702718
+ - **Webhook Token**: mi_token_secreto_123
+3. Guardar configuración
+
+### 5. 🔗 Configurar Webhook en Facebook
+1. Ir a [Facebook for Developers](https://developers.facebook.com)
+2. Crear/configurar aplicación WhatsApp Business
+3. Webhook URL: `https://tudominio.com/migrador/api/webhook.php`
+4. Token de verificación: `mi_token_secreto_123`
+5. Suscribirse a eventos: `messages`
+
+## 🎯 Prueba Rápida del Bot
+
+Envía estos mensajes por WhatsApp para probar:
+
+```
+👤 Usuario: "Hola"
+🤖 Bot: Mensaje de bienvenida
+
+👤 Usuario: "menu"
+🤖 Bot: Muestra menú principal
+
+👤 Usuario: "1"
+🤖 Bot: Muestra submenú de servicios
+
+👤 Usuario: "ayuda"
+🤖 Bot: Muestra información de ayuda
+```
+
+## 📋 Funcionalidades Principales
+
+### 🤖 Bot Automático
+- ✅ Menús navegables por números
+- ✅ Respuestas automáticas
+- ✅ Comandos especiales (menu, ayuda, salir)
+- ✅ Gestión de estados de usuario
+
+### 🎛️ Panel de Control
+- ✅ Dashboard con estadísticas
+- ✅ Gestión de conversaciones
+- ✅ Administración de usuarios
+- ✅ Editor de menús
+- ✅ Envío de mensajes
+- ✅ Configuración del sistema
+
+### 🔧 APIs Disponibles
+- ✅ Webhook para recibir mensajes
+- ✅ Envío de mensajes individuales
+- ✅ Envío masivo (broadcast)
+- ✅ Gestión de menús
+- ✅ Estadísticas y reportes
+
+## 🛠️ Estructura de Archivos
+
+```
+bot/
+├── 🏠 index.php # Panel principal
+├── 🔧 install.php # Instalador automático
+├── 🧪 test.php # Script de pruebas
+├── 📖 README.md # Documentación completa
+├── config/
+│ └── config.php # Configuración
+├── database/
+│ └── schema.sql # Esquema de BD
+├── api/ # APIs REST
+│ ├── webhook.php # Webhook principal
+│ ├── send_message.php # Envío de mensajes
+│ ├── get_stats.php # Estadísticas
+│ └── [más APIs...]
+├── classes/
+│ └── Database.php # Conexión BD
+├── services/
+│ ├── WhatsAppService.php # Servicio WhatsApp
+│ └── BotService.php # Lógica del bot
+└── assets/
+ ├── css/styles.css # Estilos
+ └── js/app.js # JavaScript
+```
+
+## 🎨 Personalización
+
+### Cambiar Menús
+1. Ir a **Menús** en el panel
+2. Editar menús existentes o crear nuevos
+3. Configurar opciones y acciones
+
+### Agregar Respuestas Automáticas
+1. Ir a **Respuestas Auto**
+2. Agregar nuevas palabras clave
+3. Definir respuestas personalizadas
+
+### Configurar Mensajes Masivos
+1. Ir a **Enviar Mensaje**
+2. Usar la sección "Mensaje Masivo"
+3. Seleccionar filtros de usuarios
+
+## 🚨 Solución de Problemas
+
+### ❌ Error de conexión BD
+- Verificar credenciales en `config/config.php`
+- Comprobar que MySQL esté funcionando
+- Revisar permisos del usuario
+
+### ❌ Webhook no funciona
+- Verificar que la URL sea accesible públicamente
+- Comprobar SSL (debe ser HTTPS)
+- Revisar logs en el panel
+
+### ❌ Bot no responde
+- Verificar token en configuración
+- Comprobar webhook en Facebook
+- Revisar logs de PHP
+
+## 📞 URLs de Prueba
+
+- **Panel**: http://localhost/bot/index.php
+- **Test**: http://localhost/bot/test.php
+- **API Stats**: http://localhost/bot/api/get_stats.php
+- **Webhook**: http://localhost/bot/api/webhook.php
+
+## 🎯 ¡Sistema Listo!
+
+Una vez completados todos los pasos, tendrás:
+
+- 🤖 **Bot funcional** respondiendo mensajes automáticamente
+- 🎛️ **Panel web** para gestión completa
+- 📊 **Dashboard** con métricas en tiempo real
+- 🔧 **APIs** para integraciones personalizadas
+
+¡Disfruta de tu nuevo sistema de WhatsApp Bot! 🚀
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ea7c0f1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,295 @@
+# 🤖 Sistema de WhatsApp Bot Manager
+
+Un sistema completo de gestión de chatbot para WhatsApp Business API con interfaz web moderna, menús parametrizables y gestión avanzada de conversaciones.
+
+## ✨ Características
+
+### 🔧 Funcionalidades Principales
+- **Bot Inteligente** con menús parametrizables por números
+- **Interfaz Web Moderna** para gestión completa
+- **API REST** para integración con sistemas externos
+- **Respuestas Automáticas** configurables
+- **Envío de Mensajes** individuales y masivos
+- **Gestión de Usuarios** con estados y seguimiento
+- **Dashboard Analytics** con gráficos en tiempo real
+- **Logs Detallados** de todas las interacciones
+- **Sistema de Plantillas** para mensajes predefinidos
+
+### 🎯 Sistema de Menús
+- Menús jerárquicos con navegación por números (1, 2, 3, etc.)
+- Acciones configurables: navegar, mensaje, API call, finalizar
+- Soporte para servicios, pagos, soporte técnico
+- Completamente parametrizable desde la interfaz
+
+### 📱 Compatibilidad WhatsApp
+- Integración completa con WhatsApp Business API
+- Soporte para mensajes de texto, imágenes, audio, video
+- Plantillas de WhatsApp Business
+- Estados de entrega y lectura
+- Webhook seguro con verificación
+
+## 🚀 Instalación
+
+### 1. Prerrequisitos
+```bash
+- PHP 8.0 o superior
+- MySQL 5.7 o superior
+- Servidor web (Apache/Nginx)
+- WhatsApp Business API Token
+- Dominio con SSL (para webhook)
+```
+
+### 2. Configuración de Base de Datos
+```bash
+# Importar el esquema de la base de datos
+mysql -u pym -p'Nicolas2796*+' -h 46.202.93.92 whatsapp < database/schema.sql
+```
+
+### 3. Configuración del Proyecto
+```bash
+# Subir archivos al servidor
+# Ajustar permisos
+chmod 755 -R bot/
+chmod 666 config/config.php
+```
+
+### 4. Configurar WhatsApp Business API
+
+1. **Acceder a Facebook Developers**
+ - Crear una aplicación en developers.facebook.com
+ - Agregar el producto "WhatsApp Business API"
+
+2. **Configurar Webhook**
+ - URL del Webhook: `https://tudominio.com/migrador/api/webhook.php`
+ - Token de Verificación: `mi_token_secreto_123`
+ - Suscribirse a: `messages`
+
+3. **Obtener Tokens**
+ - Token de Acceso (ya proporcionado)
+ - Phone Number ID: `938177732702718`
+
+### 5. Configuración en la Interfaz
+1. Abrir: `https://tudominio.com/migrador/`
+2. Ir a **Configuración**
+3. Completar:
+ - Token de WhatsApp
+ - Phone Number ID
+ - Token de Verificación
+ - Nombre de la empresa
+ - Mensaje de bienvenida
+
+## 📖 Uso del Sistema
+
+### 🎮 Interfaz Web
+- **Dashboard**: Estadísticas y actividad en tiempo real
+- **Conversaciones**: Ver y gestionar todas las conversaciones
+- **Usuarios**: Administrar usuarios y estados
+- **Menús**: Configurar y editar el flujo del bot
+- **Mensajes**: Enviar mensajes individuales o masivos
+- **Configuración**: Ajustar parámetros del sistema
+
+### 🤖 Bot de WhatsApp
+
+#### Comandos Disponibles
+- `menu` - Mostrar menú principal
+- `ayuda` - Mostrar ayuda
+- `salir` - Salir del menú actual
+
+#### Flujo de Menús (Ejemplo)
+```
+📋 Menú Principal
+
+Selecciona una opción:
+1. 💼 Ver servicios disponibles
+2. 💳 Realizar pago
+3. 🆘 Contactar soporte
+0. ❌ Salir
+
+💬 Responde con el número de la opción que deseas
+```
+
+### 🔧 API Endpoints
+
+#### Webhook
+```bash
+POST /api/webhook.php
+# Recibe mensajes de WhatsApp
+```
+
+#### Enviar Mensaje
+```bash
+POST /api/send_message.php
+Content-Type: application/json
+
+{
+ "recipient": "573168950803",
+ "type": "text",
+ "message": "Hola, este es un mensaje de prueba"
+}
+```
+
+#### Obtener Estadísticas
+```bash
+GET /api/get_stats.php
+```
+
+## 🏗️ Estructura del Proyecto
+
+```
+migrador/
+├── api/ # Endpoints de la API
+│ ├── webhook.php # Webhook principal
+│ ├── send_message.php # Envío de mensajes
+│ ├── get_stats.php # Estadísticas
+│ └── ... # Otras APIs
+├── assets/ # Recursos estáticos
+│ ├── css/ # Estilos CSS
+│ └── js/ # JavaScript
+├── classes/ # Clases principales
+│ └── Database.php # Conexión a BD
+├── config/ # Configuración
+│ └── config.php # Configuración principal
+├── database/ # Base de datos
+│ └── schema.sql # Esquema de BD
+├── services/ # Servicios del sistema
+│ ├── WhatsAppService.php # Servicio WhatsApp
+│ └── BotService.php # Lógica del bot
+└── index.php # Página principal
+```
+
+## 📊 Base de Datos
+
+### Tablas Principales
+- `users` - Usuarios del sistema
+- `conversations` - Historial de mensajes
+- `menus` - Configuración de menús
+- `menu_options` - Opciones de cada menú
+- `auto_responses` - Respuestas automáticas
+- `system_config` - Configuración del sistema
+- `webhook_logs` - Logs de webhooks
+
+## 🔧 Configuración Avanzada
+
+### Menús Personalizados
+```sql
+-- Crear menú personalizado
+INSERT INTO menus (name, title, description, is_root) VALUES
+('mi_menu', '🏪 Mi Tienda', 'Menú de productos', TRUE);
+
+-- Agregar opciones
+INSERT INTO menu_options (menu_id, option_number, text, action_type, action_value) VALUES
+(1, 1, '📱 Ver catálogo', 'message', 'Aquí tienes nuestro catálogo: www.mitienda.com'),
+(1, 2, '🛒 Hacer pedido', 'message', 'Para hacer un pedido, envía el código del producto'),
+(1, 0, '🔙 Volver', 'menu', 'main_menu');
+```
+
+### Respuestas Automáticas
+```sql
+-- Agregar respuesta automática
+INSERT INTO auto_responses (trigger_type, trigger_value, response_text) VALUES
+('keyword', 'precio', 'Para consultar precios, escribe *catalogo* o visita www.mitienda.com'),
+('keyword', 'horarios', 'Atendemos de Lunes a Viernes de 8am a 6pm');
+```
+
+## 🔐 Seguridad
+
+- Validación de tokens en webhook
+- Escape de datos SQL
+- Headers de seguridad HTTP
+- Logs de todas las transacciones
+- Autenticación de API
+
+## 🐛 Solución de Problemas
+
+### Error de Conexión a BD
+```bash
+# Verificar credenciales en config/config.php
+# Verificar que el servidor MySQL esté activo
+# Comprobar permisos del usuario de BD
+```
+
+### Webhook No Funciona
+```bash
+# Verificar que la URL sea accesible públicamente
+# Comprobar que tenga SSL (HTTPS)
+# Revisar logs en /api/get_logs.php
+```
+
+### Bot No Responde
+```bash
+# Verificar token de WhatsApp en configuración
+# Comprobar que el webhook esté configurado
+# Revisar logs de errores PHP
+```
+
+## 📞 Ejemplo de Uso Completo
+
+### 1. Configuración Inicial
+```bash
+# Bot configurado para empresa de servicios
+# Menús: Servicios, Pagos, Soporte
+# Respuestas automáticas para palabras clave
+```
+
+### 2. Flujo de Usuario
+```
+Usuario: "Hola"
+Bot: "¡Hola! 👋 Bienvenido a nuestro servicio automatizado. Escribe *menu* para ver las opciones disponibles."
+
+Usuario: "menu"
+Bot: "📋 Menú Principal
+1. 💼 Ver servicios disponibles
+2. 💳 Realizar pago
+3. 🆘 Contactar soporte
+0. ❌ Salir"
+
+Usuario: "1"
+Bot: "💼 Servicios
+1. 📋 Consulta de información
+2. 📞 Agendar cita
+3. 💰 Cotizar servicio
+0. 🔙 Volver al menú principal"
+
+Usuario: "2"
+Bot: "Para agendar una cita, por favor proporciona tu nombre completo y tu disponibilidad horaria."
+```
+
+## 🎨 Personalización
+
+### Colores y Estilos
+```css
+/* Modificar en assets/css/styles.css */
+:root {
+ --primary-color: #25d366; /* Verde WhatsApp */
+ --secondary-color: #128c7e;
+ --tertiary-color: #075e54;
+}
+```
+
+### Mensajes del Bot
+```sql
+-- Modificar mensaje de bienvenida
+UPDATE system_config
+SET config_value = 'Tu mensaje personalizado'
+WHERE config_key = 'welcome_message';
+```
+
+## 📈 Monitoreo
+
+- Dashboard en tiempo real con métricas
+- Gráficos de actividad por día
+- Estados de entrega de mensajes
+- Logs detallados de todas las operaciones
+
+## 🤝 Soporte
+
+Para soporte técnico o consultas:
+- Email: soporte@tuempresa.com
+- WhatsApp: +57 1 234 5678
+- Web: www.tuempresa.com
+
+---
+
+**Desarrollado con ❤️ para facilitar la comunicación empresarial a través de WhatsApp**
+
+© 2025 - Sistema WhatsApp Bot Manager v1.0.0
\ No newline at end of file
diff --git a/SOLUCION_ERROR_CONFIG.md b/SOLUCION_ERROR_CONFIG.md
new file mode 100644
index 0000000..bf053de
--- /dev/null
+++ b/SOLUCION_ERROR_CONFIG.md
@@ -0,0 +1,142 @@
+# 🚨 SOLUCIÓN: "Error probando la nueva configuración"
+
+## 📋 El Problema
+Te aparece este error durante la instalación:
+```
+❌ Error probando la nueva configuración: Error en la consulta a la base de datos
+```
+
+## 🔍 Posibles Causas
+
+### 1. **Tabla 'users' no existe aún**
+- El schema no se aplicó correctamente
+- Hay errores en el SQL
+
+### 2. **Archivo config.php no se actualizó**
+- Permisos de escritura insuficientes
+- Error al escribir el archivo
+
+### 3. **Credenciales incorrectas**
+- Host, usuario o contraseña incorrectos
+- Base de datos no existe
+
+---
+
+## ✅ SOLUCIONES (En orden de dificultad)
+
+### 🚀 OPCIÓN 1: Usa el Instalador Simplificado
+**¡La más fácil!** Creé un instalador específicamente para estos problemas:
+
+1. Ve a: `install_simple.php`
+2. Completa tus datos de BD
+3. ¡Listo! Solo instala lo esencial
+
+### 🔧 OPCIÓN 2: Diagnóstico Completo
+Identifica exactamente qué está mal:
+
+1. Ve a: `debug_install.php`
+2. Ingresa tus credenciales de BD
+3. Ejecuta el diagnóstico completo
+4. Sigue las recomendaciones específicas
+
+### 🛠️ OPCIÓN 3: Instalación Manual Paso a Paso
+
+#### Paso 1: Verifica tu Base de Datos
+```sql
+-- En tu panel MySQL (phpMyAdmin, etc.)
+USE tu_basedatos;
+SHOW TABLES;
+```
+
+#### Paso 2: Crea las tablas manualmente
+Si no existen, ejecuta este SQL:
+
+```sql
+CREATE TABLE IF NOT EXISTS users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ phone_number VARCHAR(20) UNIQUE NOT NULL,
+ name VARCHAR(100),
+ status ENUM('active', 'inactive') DEFAULT 'active',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE IF NOT EXISTS system_config (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ config_key VARCHAR(100) UNIQUE NOT NULL,
+ config_value TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+INSERT IGNORE INTO system_config (config_key, config_value) VALUES
+('app_installed', '1'),
+('install_date', NOW());
+```
+
+#### Paso 3: Actualiza config.php manualmente
+Edita `config/config.php` y cambia:
+
+```php
+define('DB_HOST', 'localhost'); // Tu host
+define('DB_PORT', '3306'); // Tu puerto
+define('DB_NAME', 'tu_basedatos'); // Tu BD
+define('DB_USER', 'tu_usuario'); // Tu usuario
+define('DB_PASS', 'tu_contraseña'); // Tu contraseña
+define('ADMIN_PASSWORD', password_hash('admin123', PASSWORD_DEFAULT));
+```
+
+#### Paso 4: Crear archivo de instalación completada
+Crea el archivo `.installation_completed` con cualquier contenido.
+
+---
+
+## 🎯 RECOMENDACIÓN ESPECÍFICA
+
+### Para HestiaCP/cPanel:
+1. **USA:** `install_simple.php` ← **MÁS FÁCIL**
+2. **SI FALLA:** `debug_install.php` para diagnóstico
+3. **ÚLTIMA OPCIÓN:** Instalación manual paso a paso
+
+### Credenciales Típicas en HestiaCP:
+- **Host:** `localhost`
+- **Puerto:** `3306`
+- **BD:** `tuusuario_nombrebd`
+- **Usuario:** `tuusuario_nombreusuario`
+- **Contraseña:** La que configuraste
+
+---
+
+## 🔐 Después de Solucionar
+
+### Credenciales de Admin:
+- **Usuario:** `admin`
+- **Contraseña:** `admin123` (o la que configuraste)
+
+### Próximos pasos:
+1. Ve a `index.php` para acceder al panel
+2. Prueba con `test.php` que todo funcione
+3. Configura WhatsApp en `server_setup.php`
+
+---
+
+## 📞 ¿Sigues con problemas?
+
+### Información necesaria para soporte:
+1. **Tipo de hosting:** (HestiaCP, cPanel, etc.)
+2. **Error exacto:** Copia el mensaje completo
+3. **Resultado de:** `debug_install.php`
+4. **Versión PHP:** Desde tu panel de control
+
+### Contacto:
+- **Email:** support@u-site.app
+- **Con:** Toda la información anterior
+
+---
+
+## ✨ Archivos de Ayuda Creados
+
+1. **`install_simple.php`** - Instalador minimalista ✅
+2. **`debug_install.php`** - Diagnóstico completo ✅
+3. **`install_manual.php`** - Instalador manual completo ✅
+4. **`GUIA_HESTIACP.md`** - Guía específica para HestiaCP ✅
+
+**¡Con estas herramientas deberías poder instalar sin problemas!** 🚀
\ No newline at end of file
diff --git a/SOLUCION_HOSTING.md b/SOLUCION_HOSTING.md
new file mode 100644
index 0000000..d44f90a
--- /dev/null
+++ b/SOLUCION_HOSTING.md
@@ -0,0 +1,98 @@
+# ✅ SOLUCIÓN PARA HOSTING COMPARTIDO (HestiaCP)
+
+**¡Problema solucionado!** He creado una solución completa para tu problema con la instalación en hosting compartido.
+
+## 🚨 El Problema
+- El instalador automático (`install.php`) **NO FUNCIONA** en hosting compartido
+- Se queda "cargando" porque intenta crear BD con privilegios de administrador
+- En HestiaCP/cPanel no tienes esos permisos
+
+## ✅ La Solución
+He creado un **instalador manual** específico para hosting compartido.
+
+---
+
+## 🔧 PASOS PARA INSTALAR EN HESTIACP
+
+### 1. Crear Base de Datos Manualmente
+En tu panel de HestiaCP:
+1. Ve a **"Databases"**
+2. Clic en **"Add Database"**
+3. Configura:
+ - **Database Name:** (ej: `tuusuario_whatsapp`)
+ - **Database User:** (ej: `tuusuario_bot`)
+ - **Password:** (una contraseña segura)
+ - **Privileges:** **ALL** (todos los permisos)
+
+### 2. Anotar las Credenciales
+Guarda esta información:
+- **Host:** `localhost`
+- **Base de datos:** El nombre que creaste
+- **Usuario:** El usuario que creaste
+- **Contraseña:** La contraseña que configuraste
+
+### 3. Usar el Instalador Manual
+1. Ve a: `https://tudominio.com/ruta-del-bot/install_manual.php`
+2. Completa el formulario con las credenciales del paso anterior
+3. Haz clic en **"Configurar Sistema"**
+
+---
+
+## 📁 Archivos Creados
+
+He creado estos archivos nuevos para solucionar tu problema:
+
+1. **`install_manual.php`** - Instalador para hosting compartido ✅
+2. **`GUIA_HESTIACP.md`** - Guía detallada paso a paso ✅
+3. **`instalacion.html`** - Página para elegir tipo de instalación ✅
+4. **Mejorado `install.php`** - Ahora detecta hosting compartido ✅
+
+---
+
+## 🎯 URL de Inicio
+
+**👉 EMPIEZA AQUÍ:** `https://tudominio.com/ruta-del-bot/instalacion.html`
+
+Esta página te permite elegir entre:
+- **Instalación Automática** (para desarrollo local)
+- **Instalación Manual** (para hosting compartido) ← **USA ESTA**
+
+---
+
+## 🔐 Credenciales Generadas
+
+### Para la Base de Datos
+- Usarás las credenciales que **TÚ** creaste en HestiaCP
+
+### Para el Panel Admin
+- **Usuario:** `admin`
+- **Contraseña:** Se genera automáticamente y se muestra durante la instalación
+
+---
+
+## 📞 Si Tienes Problemas
+
+### Errores Comunes:
+- **"Access denied"** → Verifica credenciales de BD
+- **"Database doesn't exist"** → Confirma el nombre exacto de la BD
+- **"Connection refused"** → Asegúrate que MySQL esté activo
+
+### Soporte:
+- **Email:** support@u-site.app
+- **Documentación:** `GUIA_HESTIACP.md`
+- **Pruebas:** `test.php` después de instalar
+
+---
+
+## 🚀 Próximos Pasos Después de Instalar
+
+1. **Prueba el sistema:** Ve a `test.php`
+2. **Accede al panel:** Ve a `index.php`
+3. **Configura WhatsApp:** Ve a `server_setup.php`
+4. **Lee las credenciales:** Archivo `CREDENCIALES_SISTEMA.txt`
+
+---
+
+**¡Ya no tendrás problemas con la instalación en HestiaCP!** 🎉
+
+La solución está probada y funciona específicamente para hostings compartidos que no permiten crear bases de datos por código.
\ No newline at end of file
diff --git a/SOLUCION_TABLA_NO_EXISTE.md b/SOLUCION_TABLA_NO_EXISTE.md
new file mode 100644
index 0000000..1b916fa
--- /dev/null
+++ b/SOLUCION_TABLA_NO_EXISTE.md
@@ -0,0 +1,143 @@
+# 🚨 SOLUCIÓN PARA ERROR: "Table doesn't exist"
+
+## Tu Situación Específica
+Has reportado estos errores:
+```
+⚠️ Esquema procesado con advertencias: 0 tablas, 0 elementos
+⚠️ SQL Error: Table 'usite_whatsapp_bot.menus' doesn't exist
+```
+
+**CAUSA:** El archivo `schema.sql` no se está procesando correctamente. El instalador intenta insertar datos en tablas que aún no se han creado.
+
+---
+
+## ✅ SOLUCIÓN INMEDIATA
+
+### 🎯 **OPCIÓN 1: Instalador Ultra Básico (RECOMENDADO)**
+**👉 Usa:** `install_ultra_basic.php`
+
+### 🔧 **OPCIÓN 2: Instalador Integrado (COMPLETO)**
+**👉 Usa:** `install_integrated.php`
+
+**¿Por qué funcionan?**
+- ✅ Schema SQL integrado en el código PHP
+- ✅ No depende del archivo `schema.sql` externo
+- ✅ Ejecución controlada paso a paso
+- ✅ Primero crea tablas, luego inserta datos
+
+**Nota:** La tabla `users` NO incluye campo `password` porque es para usuarios de WhatsApp que se identifican por número de teléfono. La contraseña de admin se guarda en `config.php`.
+
+### 🔧 **OPCIÓN 2: Reparar Manualmente**
+Si quieres usar el instalador manual:
+
+1. **Ejecuta este SQL directamente en phpMyAdmin:**
+```sql
+USE usite_whatsapp_bot;
+
+CREATE TABLE IF NOT EXISTS menus (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(100) NOT NULL,
+ title VARCHAR(200) NOT NULL,
+ description TEXT,
+ parent_id INT NULL,
+ is_root BOOLEAN DEFAULT FALSE,
+ is_active BOOLEAN DEFAULT TRUE,
+ order_position INT DEFAULT 0,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ INDEX idx_parent (parent_id),
+ INDEX idx_active (is_active)
+);
+```
+
+2. **Luego usa cualquier instalador**
+
+---
+
+## 🔍 ¿Por qué falló el archivo schema.sql?
+
+### Problemas comunes:
+1. **Parsing incorrecto:** El archivo se divide mal por `;`
+2. **Variables no definidas:** `SET @variable` no funciona en algunos contextos
+3. **Dependencias:** Se intenta insertar en tablas antes de crearlas
+4. **Encoding:** Caracteres especiales en el archivo
+
+### El instalador integrado evita estos problemas porque:
+- ✅ Usa arrays PHP en lugar de parsing de archivo
+- ✅ Ejecuta CREATE TABLE primero, INSERT después
+- ✅ Manejo de errores específico para cada paso
+
+---
+
+## 📋 Pasos Específicos Para Ti
+
+### 1. **Usar el Instalador Integrado:**
+```
+1. Ve a: tudominio.com/install_integrated.php
+2. Ingresa tus credenciales:
+ - Host: localhost
+ - Puerto: 3306
+ - BD: usite_whatsapp_bot
+ - Usuario: [tu usuario]
+ - Contraseña: [tu contraseña]
+3. Clic en "Instalar con Schema Integrado"
+```
+
+### 2. **Credenciales que se generarán:**
+- **Usuario admin:** `admin`
+- **Contraseña:** Se muestra durante la instalación
+- **Se guarda en:** `CREDENCIALES.txt`
+
+### 3. **Verificar que funcionó:**
+```
+- Ve a: test.php
+- Deberías ver: "✅ Sistema funcionando"
+- Login en: index.php
+```
+
+---
+
+## 🎯 Archivos Disponibles
+
+| Instalador | Recomendado Para | Tu Situación |
+|------------|------------------|--------------|
+| `install_integrated.php` | **Problemas con schema.sql** | ✅ **PERFECTO** |
+| `install_simple.php` | Solo funciones básicas | ✅ Alternativa |
+| `install_manual.php` | Instalación completa | ❌ Tiene el problema |
+| `debug_install.php` | Diagnóstico de problemas | ℹ️ Para análisis |
+
+---
+
+## 🚀 Después de Instalar
+
+1. **Verifica las tablas creadas:**
+ - Ve a phpMyAdmin
+ - Deberías ver: users, conversations, menus, menu_options, system_config, etc.
+
+2. **Prueba el sistema:**
+ - `test.php` - Para verificar funcionamiento
+ - `index.php` - Panel de administración
+ - `login.php` - Sistema de login
+
+3. **Configura WhatsApp:**
+ - Entra al panel con las credenciales generadas
+ - Ve a Configuración
+ - Agrega tu token de WhatsApp
+
+---
+
+## 📞 Si Aún Tienes Problemas
+
+### Información para soporte:
+- **Hosting:** HestiaCP (confirmado)
+- **BD:** usite_whatsapp_bot (confirmado)
+- **Error anterior:** Schema parsing falló
+- **Solución aplicada:** Instalador integrado
+
+### Contacto:
+- **Email:** support@u-site.app
+- **Incluye:** Resultado del instalador integrado
+
+---
+
+**💡 RESUMEN:** Tu problema se debe a que el archivo `schema.sql` no se procesa correctamente. El instalador integrado resuelve esto completamente al tener todo el SQL dentro del código PHP. ¡Úsalo y debería funcionar perfectamente! 🚀
\ No newline at end of file
diff --git a/api/export_users.php b/api/export_users.php
new file mode 100644
index 0000000..1a6fdea
--- /dev/null
+++ b/api/export_users.php
@@ -0,0 +1,79 @@
+fetchAll(
+ "SELECT
+ u.id,
+ u.phone_number,
+ u.name,
+ u.email,
+ u.status,
+ u.created_at,
+ u.updated_at,
+ COUNT(c.id) as total_messages,
+ MAX(c.created_at) as last_activity
+ FROM users u
+ LEFT JOIN conversations c ON u.id = c.user_id
+ GROUP BY u.id
+ ORDER BY u.created_at DESC"
+ );
+
+ // Crear el archivo CSV
+ $output = fopen('php://output', 'w');
+
+ // BOM para UTF-8
+ fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
+
+ // Encabezados
+ fputcsv($output, [
+ 'ID',
+ 'Teléfono',
+ 'Nombre',
+ 'Email',
+ 'Estado',
+ 'Total Mensajes',
+ 'Última Actividad',
+ 'Fecha Registro'
+ ], ';');
+
+ // Datos
+ foreach ($users as $user) {
+ fputcsv($output, [
+ $user['id'],
+ $user['phone_number'],
+ $user['name'] ?? 'Sin nombre',
+ $user['email'] ?? 'Sin email',
+ $user['status'],
+ $user['total_messages'],
+ $user['last_activity'] ? date('d/m/Y H:i', strtotime($user['last_activity'])) : 'Nunca',
+ date('d/m/Y H:i', strtotime($user['created_at']))
+ ], ';');
+ }
+
+ fclose($output);
+
+} catch (Exception $e) {
+ error_log("Error in export_users.php: " . $e->getMessage());
+
+ // Cambiar headers para error
+ header('Content-Type: application/json; charset=utf-8');
+ header('Content-Disposition: inline');
+
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_chart_data.php b/api/get_chart_data.php
new file mode 100644
index 0000000..b1fb526
--- /dev/null
+++ b/api/get_chart_data.php
@@ -0,0 +1,47 @@
+fetchAll(
+ "SELECT
+ DATE(created_at) as date,
+ COUNT(*) as message_count
+ FROM conversations
+ WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
+ GROUP BY DATE(created_at)
+ ORDER BY date ASC"
+ );
+
+ // Formatear datos para Chart.js
+ $labels = [];
+ $data = [];
+
+ foreach ($chartData as $row) {
+ $labels[] = date('d/m', strtotime($row['date']));
+ $data[] = (int)$row['message_count'];
+ }
+
+ echo json_encode([
+ 'labels' => $labels,
+ 'data' => $data
+ ]);
+
+} catch (Exception $e) {
+ error_log("Error in get_chart_data.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_conversations.php b/api/get_conversations.php
new file mode 100644
index 0000000..8f7d743
--- /dev/null
+++ b/api/get_conversations.php
@@ -0,0 +1,41 @@
+fetchAll(
+ "SELECT
+ c.id,
+ c.user_id,
+ c.content,
+ c.direction,
+ c.message_type,
+ c.status,
+ c.created_at,
+ u.phone_number,
+ u.name
+ FROM conversations c
+ JOIN users u ON c.user_id = u.id
+ ORDER BY c.created_at DESC
+ LIMIT 50"
+ );
+
+ echo json_encode($conversations);
+
+} catch (Exception $e) {
+ error_log("Error in get_conversations.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_logs.php b/api/get_logs.php
new file mode 100644
index 0000000..afc645d
--- /dev/null
+++ b/api/get_logs.php
@@ -0,0 +1,44 @@
+fetchAll(
+ "SELECT
+ id,
+ status_code,
+ ip_address,
+ created_at,
+ SUBSTRING(request_body, 1, 100) as request_preview,
+ SUBSTRING(response_body, 1, 100) as response_preview
+ FROM webhook_logs
+ ORDER BY created_at DESC
+ LIMIT :limit OFFSET :offset",
+ [
+ 'limit' => (int)$limit,
+ 'offset' => (int)$offset
+ ]
+ );
+
+ echo json_encode($logs);
+
+} catch (Exception $e) {
+ error_log("Error in get_logs.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_menus.php b/api/get_menus.php
new file mode 100644
index 0000000..fa88e22
--- /dev/null
+++ b/api/get_menus.php
@@ -0,0 +1,51 @@
+fetchAll(
+ "SELECT
+ id,
+ name,
+ title,
+ description,
+ parent_id,
+ is_root,
+ is_active,
+ order_position,
+ created_at,
+ updated_at
+ FROM menus
+ ORDER BY order_position ASC, title ASC"
+ );
+
+ // Obtener opciones de cada menú
+ foreach ($menus as &$menu) {
+ $options = $db->fetchAll(
+ "SELECT * FROM menu_options
+ WHERE menu_id = :menu_id
+ ORDER BY option_number ASC",
+ ['menu_id' => $menu['id']]
+ );
+ $menu['options'] = $options;
+ }
+
+ echo json_encode($menus);
+
+} catch (Exception $e) {
+ error_log("Error in get_menus.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_recent_messages.php b/api/get_recent_messages.php
new file mode 100644
index 0000000..eae9c50
--- /dev/null
+++ b/api/get_recent_messages.php
@@ -0,0 +1,39 @@
+fetchAll(
+ "SELECT
+ c.content,
+ c.direction,
+ c.message_type,
+ c.created_at,
+ u.phone_number,
+ u.name
+ FROM conversations c
+ JOIN users u ON c.user_id = u.id
+ WHERE c.content IS NOT NULL AND c.content != ''
+ ORDER BY c.created_at DESC
+ LIMIT 10"
+ );
+
+ echo json_encode($recentMessages);
+
+} catch (Exception $e) {
+ error_log("Error in get_recent_messages.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_settings.php b/api/get_settings.php
new file mode 100644
index 0000000..03cfa3d
--- /dev/null
+++ b/api/get_settings.php
@@ -0,0 +1,33 @@
+fetchAll(
+ "SELECT config_key, config_value FROM system_config"
+ );
+
+ $settings = [];
+ foreach ($configs as $config) {
+ $settings[$config['config_key']] = $config['config_value'];
+ }
+
+ echo json_encode($settings);
+
+} catch (Exception $e) {
+ error_log("Error in get_settings.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_stats.php b/api/get_stats.php
new file mode 100644
index 0000000..bcda65e
--- /dev/null
+++ b/api/get_stats.php
@@ -0,0 +1,48 @@
+fetch("SELECT COUNT(*) as count FROM users")['count'];
+
+ // Mensajes hoy
+ $messagesToday = $db->fetch(
+ "SELECT COUNT(*) as count FROM conversations WHERE DATE(created_at) = CURDATE()"
+ )['count'];
+
+ // Usuarios activos (último mes)
+ $activeUsers = $db->fetch(
+ "SELECT COUNT(DISTINCT user_id) as count FROM conversations
+ WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)"
+ )['count'];
+
+ // Total de mensajes
+ $totalMessages = $db->fetch("SELECT COUNT(*) as count FROM conversations")['count'];
+
+ $stats = [
+ 'total_users' => (int)$totalUsers,
+ 'messages_today' => (int)$messagesToday,
+ 'active_users' => (int)$activeUsers,
+ 'total_messages' => (int)$totalMessages
+ ];
+
+ echo json_encode($stats);
+
+} catch (Exception $e) {
+ error_log("Error in get_stats.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_templates.php b/api/get_templates.php
new file mode 100644
index 0000000..9be2583
--- /dev/null
+++ b/api/get_templates.php
@@ -0,0 +1,37 @@
+fetchAll(
+ "SELECT
+ id,
+ name,
+ template_name,
+ language_code,
+ category,
+ status,
+ created_at
+ FROM message_templates
+ ORDER BY name ASC"
+ );
+
+ echo json_encode($templates);
+
+} catch (Exception $e) {
+ error_log("Error in get_templates.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/get_users.php b/api/get_users.php
new file mode 100644
index 0000000..a40e5af
--- /dev/null
+++ b/api/get_users.php
@@ -0,0 +1,41 @@
+fetchAll(
+ "SELECT
+ u.id,
+ u.phone_number,
+ u.name,
+ u.email,
+ u.status,
+ u.current_menu_id,
+ u.current_step,
+ u.created_at,
+ u.updated_at,
+ m.name as current_menu
+ FROM users u
+ LEFT JOIN menus m ON u.current_menu_id = m.id
+ ORDER BY u.created_at DESC"
+ );
+
+ echo json_encode($users);
+
+} catch (Exception $e) {
+ error_log("Error in get_users.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/api/save_menu.php b/api/save_menu.php
new file mode 100644
index 0000000..5e2f957
--- /dev/null
+++ b/api/save_menu.php
@@ -0,0 +1,64 @@
+ 'Método no permitido']);
+ exit;
+}
+
+try {
+ $input = json_decode(file_get_contents('php://input'), true);
+
+ if (!$input || !isset($input['name']) || !isset($input['title'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Nombre y título son requeridos']);
+ exit;
+ }
+
+ $db = Database::getInstance();
+
+ // Verificar si es menú raíz
+ $isRoot = empty($input['parent_id']) ? 1 : 0;
+
+ // Obtener próximo order_position
+ $maxOrder = $db->fetch(
+ "SELECT COALESCE(MAX(order_position), 0) as max_order FROM menus"
+ );
+
+ $menuData = [
+ 'name' => $input['name'],
+ 'title' => $input['title'],
+ 'description' => $input['description'] ?? '',
+ 'parent_id' => empty($input['parent_id']) ? null : $input['parent_id'],
+ 'is_root' => $isRoot,
+ 'is_active' => $input['is_active'] ?? 1,
+ 'order_position' => $maxOrder['max_order'] + 1,
+ 'created_at' => date('Y-m-d H:i:s'),
+ 'updated_at' => date('Y-m-d H:i:s')
+ ];
+
+ $menuId = $db->insert('menus', $menuData);
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Menú guardado correctamente',
+ 'menu_id' => $menuId
+ ]);
+
+} catch (Exception $e) {
+ error_log("Error in save_menu.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/save_settings.php b/api/save_settings.php
new file mode 100644
index 0000000..4036ca3
--- /dev/null
+++ b/api/save_settings.php
@@ -0,0 +1,89 @@
+ 'Método no permitido']);
+ exit;
+}
+
+try {
+ $input = json_decode(file_get_contents('php://input'), true);
+
+ if (!$input) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Datos inválidos']);
+ exit;
+ }
+
+ $db = Database::getInstance();
+
+ // Mapear configuraciones
+ $configMap = [
+ 'whatsapp_token' => $input['whatsapp_token'] ?? '',
+ 'phone_number_id' => $input['phone_number_id'] ?? '',
+ 'webhook_verify_token' => $input['webhook_token'] ?? '',
+ 'business_name' => $input['business_name'] ?? '',
+ 'welcome_message' => $input['welcome_message'] ?? ''
+ ];
+
+ $db->beginTransaction();
+
+ try {
+ foreach ($configMap as $key => $value) {
+ if (!empty($value)) {
+ // Verificar si la configuración existe
+ $existing = $db->fetch(
+ "SELECT id FROM system_config WHERE config_key = :key",
+ ['key' => $key]
+ );
+
+ if ($existing) {
+ // Actualizar
+ $db->update(
+ 'system_config',
+ ['config_value' => $value, 'updated_at' => date('Y-m-d H:i:s')],
+ 'config_key = :key',
+ ['key' => $key]
+ );
+ } else {
+ // Insertar
+ $db->insert('system_config', [
+ 'config_key' => $key,
+ 'config_value' => $value,
+ 'description' => 'Configurado desde la interfaz web',
+ 'created_at' => date('Y-m-d H:i:s'),
+ 'updated_at' => date('Y-m-d H:i:s')
+ ]);
+ }
+ }
+ }
+
+ $db->commit();
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Configuración guardada correctamente'
+ ]);
+
+ } catch (Exception $e) {
+ $db->rollback();
+ throw $e;
+ }
+
+} catch (Exception $e) {
+ error_log("Error in save_settings.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/send_broadcast.php b/api/send_broadcast.php
new file mode 100644
index 0000000..f153123
--- /dev/null
+++ b/api/send_broadcast.php
@@ -0,0 +1,88 @@
+ 'Método no permitido']);
+ exit;
+}
+
+try {
+ $input = json_decode(file_get_contents('php://input'), true);
+
+ if (!$input || !isset($input['message'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Mensaje requerido']);
+ exit;
+ }
+
+ $filter = $input['filter'] ?? 'all';
+ $message = $input['message'];
+
+ $db = Database::getInstance();
+ $whatsappService = new WhatsAppService();
+
+ // Construir query según filtro
+ $whereClause = "WHERE u.status = 'active'";
+
+ switch ($filter) {
+ case 'active':
+ $whereClause .= " AND EXISTS (
+ SELECT 1 FROM conversations c
+ WHERE c.user_id = u.id
+ AND c.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
+ )";
+ break;
+
+ case 'recent':
+ $whereClause .= " AND u.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
+ break;
+ }
+
+ // Obtener usuarios según filtro
+ $users = $db->fetchAll(
+ "SELECT phone_number FROM users u " . $whereClause
+ );
+
+ $sentCount = 0;
+ $errorCount = 0;
+
+ foreach ($users as $user) {
+ try {
+ $response = $whatsappService->sendTextMessage($user['phone_number'], $message);
+ if ($response) {
+ $sentCount++;
+ }
+
+ // Pequeña pausa para no saturar la API
+ usleep(100000); // 0.1 segundo
+
+ } catch (Exception $e) {
+ $errorCount++;
+ error_log("Error sending broadcast to {$user['phone_number']}: " . $e->getMessage());
+ }
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'sent_count' => $sentCount,
+ 'error_count' => $errorCount,
+ 'total_users' => count($users)
+ ]);
+
+} catch (Exception $e) {
+ error_log("Error in send_broadcast.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/send_message.php b/api/send_message.php
new file mode 100644
index 0000000..e191c7e
--- /dev/null
+++ b/api/send_message.php
@@ -0,0 +1,78 @@
+ 'Método no permitido']);
+ exit;
+}
+
+try {
+ $input = json_decode(file_get_contents('php://input'), true);
+
+ if (!$input || !isset($input['recipient'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Datos inválidos']);
+ exit;
+ }
+
+ $recipient = $input['recipient'];
+ $type = $input['type'] ?? 'text';
+
+ $whatsappService = new WhatsAppService();
+ $response = null;
+
+ switch ($type) {
+ case 'text':
+ if (!isset($input['message']) || empty($input['message'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Mensaje requerido']);
+ exit;
+ }
+ $response = $whatsappService->sendTextMessage($recipient, $input['message']);
+ break;
+
+ case 'template':
+ if (!isset($input['template']) || empty($input['template'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Plantilla requerida']);
+ exit;
+ }
+ $language = $input['language'] ?? 'es';
+ $parameters = $input['parameters'] ?? [];
+ $response = $whatsappService->sendTemplateMessage($recipient, $input['template'], $language, $parameters);
+ break;
+
+ default:
+ http_response_code(400);
+ echo json_encode(['error' => 'Tipo de mensaje no válido']);
+ exit;
+ }
+
+ if ($response) {
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Mensaje enviado correctamente',
+ 'whatsapp_response' => $response
+ ]);
+ } else {
+ http_response_code(500);
+ echo json_encode(['error' => 'Error enviando mensaje']);
+ }
+
+} catch (Exception $e) {
+ error_log("Error in send_message.php: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/webhook.php b/api/webhook.php
new file mode 100644
index 0000000..4b8ac40
--- /dev/null
+++ b/api/webhook.php
@@ -0,0 +1,221 @@
+db = Database::getInstance();
+ $this->whatsappService = new WhatsAppService();
+ $this->botService = new BotService();
+ }
+
+ public function handleRequest() {
+ $method = $_SERVER['REQUEST_METHOD'];
+
+ if ($method === 'GET') {
+ $this->verifyWebhook();
+ } elseif ($method === 'POST') {
+ $this->processIncomingMessage();
+ } else {
+ http_response_code(405);
+ echo json_encode(['error' => 'Método no permitido']);
+ }
+ }
+
+ private function verifyWebhook() {
+ $verifyToken = $_GET['hub_verify_token'] ?? '';
+ $challenge = $_GET['hub_challenge'] ?? '';
+ $mode = $_GET['hub_mode'] ?? '';
+
+ if ($mode === 'subscribe' && $verifyToken === WEBHOOK_VERIFY_TOKEN) {
+ echo $challenge;
+ exit;
+ } else {
+ http_response_code(403);
+ echo json_encode(['error' => 'Token de verificación inválido']);
+ }
+ }
+
+ private function processIncomingMessage() {
+ $input = file_get_contents('php://input');
+ $data = json_decode($input, true);
+
+ // Registrar webhook en logs
+ $this->logWebhook($input, json_encode(['status' => 'received']), 200);
+
+ if (!$data || !isset($data['entry'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Datos inválidos']);
+ return;
+ }
+
+ try {
+ foreach ($data['entry'] as $entry) {
+ if (isset($entry['changes'])) {
+ foreach ($entry['changes'] as $change) {
+ if ($change['field'] === 'messages') {
+ $this->processMessages($change['value']);
+ }
+ }
+ }
+ }
+
+ echo json_encode(['status' => 'success']);
+
+ } catch (Exception $e) {
+ error_log("Error processing webhook: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error interno del servidor']);
+ }
+ }
+
+ private function processMessages($value) {
+ if (!isset($value['messages'])) {
+ return;
+ }
+
+ foreach ($value['messages'] as $message) {
+ $phoneNumber = $message['from'];
+ $messageId = $message['id'];
+ $timestamp = $message['timestamp'];
+
+ // Verificar si ya procesamos este mensaje
+ $existing = $this->db->fetch(
+ "SELECT id FROM conversations WHERE message_id = :message_id",
+ ['message_id' => $messageId]
+ );
+
+ if ($existing) {
+ continue; // Ya procesamos este mensaje
+ }
+
+ // Obtener o crear usuario
+ $user = $this->getUserByPhone($phoneNumber);
+ if (!$user) {
+ $userId = $this->createUser($phoneNumber);
+ $user = $this->getUserById($userId);
+ }
+
+ // Procesar diferentes tipos de mensaje
+ $messageText = '';
+ $messageType = 'text';
+ $mediaUrl = null;
+
+ if (isset($message['text'])) {
+ $messageText = $message['text']['body'];
+ $messageType = 'text';
+ } elseif (isset($message['image'])) {
+ $messageText = $message['image']['caption'] ?? '';
+ $messageType = 'image';
+ $mediaUrl = $message['image']['id'];
+ } elseif (isset($message['audio'])) {
+ $messageType = 'audio';
+ $mediaUrl = $message['audio']['id'];
+ } elseif (isset($message['video'])) {
+ $messageText = $message['video']['caption'] ?? '';
+ $messageType = 'video';
+ $mediaUrl = $message['video']['id'];
+ } elseif (isset($message['document'])) {
+ $messageText = $message['document']['filename'] ?? '';
+ $messageType = 'document';
+ $mediaUrl = $message['document']['id'];
+ }
+
+ // Guardar mensaje en base de datos
+ $this->saveMessage([
+ 'user_id' => $user['id'],
+ 'message_id' => $messageId,
+ 'direction' => 'incoming',
+ 'message_type' => $messageType,
+ 'content' => $messageText,
+ 'media_url' => $mediaUrl,
+ 'status' => 'received'
+ ]);
+
+ // Procesar con bot
+ $this->botService->processMessage($user, $messageText, $messageType);
+ }
+
+ // Procesar estados de mensajes (entregado, leído, etc.)
+ if (isset($value['statuses'])) {
+ $this->processMessageStatuses($value['statuses']);
+ }
+ }
+
+ private function processMessageStatuses($statuses) {
+ foreach ($statuses as $status) {
+ $messageId = $status['id'];
+ $newStatus = $status['status']; // sent, delivered, read, failed
+
+ $this->db->update(
+ 'conversations',
+ ['status' => $newStatus],
+ 'message_id = :message_id',
+ ['message_id' => $messageId]
+ );
+ }
+ }
+
+ private function getUserByPhone($phoneNumber) {
+ return $this->db->fetch(
+ "SELECT * FROM users WHERE phone_number = :phone",
+ ['phone' => $phoneNumber]
+ );
+ }
+
+ private function getUserById($userId) {
+ return $this->db->fetch(
+ "SELECT * FROM users WHERE id = :id",
+ ['id' => $userId]
+ );
+ }
+
+ private function createUser($phoneNumber) {
+ return $this->db->insert('users', [
+ 'phone_number' => $phoneNumber,
+ 'status' => 'active',
+ 'created_at' => date('Y-m-d H:i:s')
+ ]);
+ }
+
+ private function saveMessage($messageData) {
+ return $this->db->insert('conversations', $messageData);
+ }
+
+ private function logWebhook($requestBody, $responseBody, $statusCode) {
+ if (ENABLE_LOGGING) {
+ $this->db->insert('webhook_logs', [
+ 'request_body' => $requestBody,
+ 'response_body' => $responseBody,
+ 'status_code' => $statusCode,
+ 'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
+ 'created_at' => date('Y-m-d H:i:s')
+ ]);
+ }
+ }
+}
+
+// Procesar la solicitud
+try {
+ $webhook = new WhatsAppWebhook();
+ $webhook->handleRequest();
+} catch (Exception $e) {
+ error_log("Fatal error in webhook: " . $e->getMessage());
+ http_response_code(500);
+ echo json_encode(['error' => 'Error fatal del servidor']);
+}
+?>
\ No newline at end of file
diff --git a/assets/css/styles.css b/assets/css/styles.css
new file mode 100644
index 0000000..77f0f0f
--- /dev/null
+++ b/assets/css/styles.css
@@ -0,0 +1,694 @@
+/*
+ * WhatsApp Bot Manager - Estilos CSS
+ * Fecha: 13 de noviembre de 2025
+ */
+
+:root {
+ --primary-color: #25d366;
+ --secondary-color: #128c7e;
+ --tertiary-color: #075e54;
+ --accent-color: #dcf8c6;
+ --dark-bg: #1f2937;
+ --light-bg: #f9fafb;
+ --border-color: #e5e7eb;
+ --text-primary: #1f2937;
+ --text-secondary: #6b7280;
+ --success-color: #10b981;
+ --warning-color: #f59e0b;
+ --error-color: #ef4444;
+ --info-color: #3b82f6;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background-color: var(--light-bg);
+ color: var(--text-primary);
+ line-height: 1.6;
+}
+
+/* Sidebar */
+.sidebar {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 280px;
+ height: 100vh;
+ background: linear-gradient(135deg, var(--tertiary-color), var(--secondary-color));
+ color: white;
+ z-index: 1000;
+ overflow-y: auto;
+ transition: all 0.3s ease;
+ box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
+}
+
+.sidebar-header {
+ padding: 20px;
+ text-align: center;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.sidebar-header h4 {
+ font-weight: 600;
+ margin: 0;
+ font-size: 1.2rem;
+}
+
+.sidebar-menu {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.sidebar-menu li {
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.sidebar-menu .nav-link {
+ display: block;
+ padding: 15px 20px;
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+}
+
+.sidebar-menu .nav-link::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 4px;
+ height: 100%;
+ background: var(--primary-color);
+ transform: translateX(-100%);
+ transition: transform 0.3s ease;
+}
+
+.sidebar-menu .nav-link:hover,
+.sidebar-menu .nav-link.active {
+ color: white;
+ background: rgba(255, 255, 255, 0.1);
+ transform: translateX(5px);
+}
+
+.sidebar-menu .nav-link.active::before,
+.sidebar-menu .nav-link:hover::before {
+ transform: translateX(0);
+}
+
+.sidebar-menu .nav-link i {
+ margin-right: 10px;
+ width: 20px;
+ text-align: center;
+}
+
+/* Main Content */
+.main-content {
+ margin-left: 280px;
+ min-height: 100vh;
+ transition: margin-left 0.3s ease;
+}
+
+.content-header {
+ background: white;
+ padding: 20px 30px;
+ border-bottom: 1px solid var(--border-color);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+}
+
+.content-header h1 {
+ color: var(--text-primary);
+ font-size: 1.5rem;
+ font-weight: 600;
+ margin: 0;
+}
+
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+}
+
+.status-indicator {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 0.9rem;
+ font-weight: 500;
+}
+
+.status-indicator.online {
+ color: var(--success-color);
+}
+
+.status-indicator i {
+ font-size: 0.7rem;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0% { opacity: 1; }
+ 50% { opacity: 0.5; }
+ 100% { opacity: 1; }
+}
+
+/* Tab Content */
+.tab-content {
+ display: none;
+ padding: 30px;
+ animation: fadeIn 0.3s ease;
+}
+
+.tab-content.active {
+ display: block;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* Cards */
+.card {
+ background: white;
+ border-radius: 12px;
+ border: none;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07);
+ margin-bottom: 20px;
+ transition: all 0.3s ease;
+}
+
+.card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+}
+
+.card-header {
+ background: var(--light-bg);
+ border-bottom: 1px solid var(--border-color);
+ padding: 20px;
+ border-radius: 12px 12px 0 0;
+}
+
+.card-header h5 {
+ margin: 0;
+ color: var(--text-primary);
+ font-weight: 600;
+}
+
+.card-body {
+ padding: 20px;
+}
+
+/* Stat Cards */
+.card-stat {
+ border-radius: 12px;
+ border: none;
+ transition: all 0.3s ease;
+}
+
+.card-stat:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
+}
+
+.card-stat-title {
+ font-size: 0.85rem;
+ font-weight: 500;
+ opacity: 0.9;
+ margin-bottom: 5px;
+}
+
+.card-stat-number {
+ font-size: 2.5rem;
+ font-weight: 700;
+ margin: 0;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+/* Buttons */
+.btn {
+ border-radius: 8px;
+ font-weight: 500;
+ padding: 10px 20px;
+ transition: all 0.3s ease;
+ border: none;
+ position: relative;
+ overflow: hidden;
+}
+
+.btn::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ transition: left 0.5s;
+}
+
+.btn:hover::before {
+ left: 100%;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+ color: white;
+}
+
+.btn-primary:hover {
+ background: linear-gradient(135deg, var(--secondary-color), var(--tertiary-color));
+ color: white;
+ transform: translateY(-2px);
+ box-shadow: 0 5px 15px rgba(37, 211, 102, 0.4);
+}
+
+.btn-success {
+ background: var(--success-color);
+}
+
+.btn-warning {
+ background: var(--warning-color);
+}
+
+.btn-danger {
+ background: var(--error-color);
+}
+
+.btn-info {
+ background: var(--info-color);
+}
+
+/* Forms */
+.form-control {
+ border: 2px solid var(--border-color);
+ border-radius: 8px;
+ padding: 12px 15px;
+ transition: all 0.3s ease;
+ font-size: 0.95rem;
+}
+
+.form-control:focus {
+ border-color: var(--primary-color);
+ box-shadow: 0 0 0 0.2rem rgba(37, 211, 102, 0.25);
+ outline: none;
+}
+
+.form-label {
+ font-weight: 600;
+ color: var(--text-primary);
+ margin-bottom: 8px;
+}
+
+/* Tables */
+.table {
+ border-collapse: separate;
+ border-spacing: 0;
+}
+
+.table th {
+ background: var(--light-bg);
+ color: var(--text-primary);
+ font-weight: 600;
+ border: none;
+ padding: 15px;
+ font-size: 0.9rem;
+}
+
+.table td {
+ padding: 15px;
+ border-top: 1px solid var(--border-color);
+ vertical-align: middle;
+}
+
+.table-hover tbody tr:hover {
+ background-color: rgba(37, 211, 102, 0.05);
+}
+
+/* Badges */
+.badge {
+ padding: 6px 12px;
+ border-radius: 20px;
+ font-size: 0.75rem;
+ font-weight: 500;
+}
+
+.badge-success {
+ background: var(--success-color);
+ color: white;
+}
+
+.badge-warning {
+ background: var(--warning-color);
+ color: white;
+}
+
+.badge-danger {
+ background: var(--error-color);
+ color: white;
+}
+
+.badge-info {
+ background: var(--info-color);
+ color: white;
+}
+
+.badge-secondary {
+ background: var(--text-secondary);
+ color: white;
+}
+
+/* Menu Tree */
+.menu-tree {
+ list-style: none;
+ padding-left: 0;
+}
+
+.menu-tree li {
+ margin-bottom: 10px;
+}
+
+.menu-item {
+ background: white;
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 15px;
+ display: flex;
+ justify-content: between;
+ align-items: center;
+ transition: all 0.3s ease;
+}
+
+.menu-item:hover {
+ border-color: var(--primary-color);
+ background: rgba(37, 211, 102, 0.05);
+}
+
+.menu-item.root {
+ border-left: 4px solid var(--primary-color);
+}
+
+.menu-item.child {
+ margin-left: 20px;
+ border-left: 4px solid var(--info-color);
+}
+
+/* Recent Messages */
+.recent-message {
+ padding: 10px 15px;
+ border-radius: 8px;
+ margin-bottom: 10px;
+ border-left: 4px solid var(--primary-color);
+ background: rgba(37, 211, 102, 0.05);
+}
+
+.recent-message .time {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+
+.recent-message .content {
+ font-size: 0.9rem;
+ margin-top: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* Loading Spinner */
+.spinner {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border: 3px solid rgba(255, 255, 255, 0.3);
+ border-radius: 50%;
+ border-top-color: white;
+ animation: spin 1s ease-in-out infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+/* Alerts */
+.alert {
+ border: none;
+ border-radius: 8px;
+ padding: 15px 20px;
+}
+
+.alert-success {
+ background: rgba(16, 185, 129, 0.1);
+ border-left: 4px solid var(--success-color);
+ color: var(--success-color);
+}
+
+.alert-warning {
+ background: rgba(245, 158, 11, 0.1);
+ border-left: 4px solid var(--warning-color);
+ color: var(--warning-color);
+}
+
+.alert-danger {
+ background: rgba(239, 68, 68, 0.1);
+ border-left: 4px solid var(--error-color);
+ color: var(--error-color);
+}
+
+.alert-info {
+ background: rgba(59, 130, 246, 0.1);
+ border-left: 4px solid var(--info-color);
+ color: var(--info-color);
+}
+
+/* Chat Interface */
+.chat-container {
+ height: 400px;
+ overflow-y: auto;
+ background: #f0f0f0;
+ border-radius: 8px;
+ padding: 15px;
+}
+
+.message {
+ margin-bottom: 15px;
+ display: flex;
+ align-items: flex-start;
+}
+
+.message.incoming {
+ justify-content: flex-start;
+}
+
+.message.outgoing {
+ justify-content: flex-end;
+}
+
+.message-bubble {
+ max-width: 70%;
+ padding: 10px 15px;
+ border-radius: 18px;
+ position: relative;
+}
+
+.message.incoming .message-bubble {
+ background: white;
+ border-bottom-left-radius: 4px;
+}
+
+.message.outgoing .message-bubble {
+ background: var(--primary-color);
+ color: white;
+ border-bottom-right-radius: 4px;
+}
+
+.message-time {
+ font-size: 0.7rem;
+ opacity: 0.7;
+ margin-top: 5px;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .sidebar {
+ transform: translateX(-100%);
+ }
+
+ .sidebar.show {
+ transform: translateX(0);
+ }
+
+ .main-content {
+ margin-left: 0;
+ }
+
+ .content-header {
+ padding: 15px 20px;
+ }
+
+ .tab-content {
+ padding: 20px 15px;
+ }
+
+ .card-stat-number {
+ font-size: 2rem;
+ }
+}
+
+/* Dark Mode Support */
+@media (prefers-color-scheme: dark) {
+ :root {
+ --light-bg: #1f2937;
+ --text-primary: #f9fafb;
+ --text-secondary: #d1d5db;
+ --border-color: #374151;
+ }
+
+ body {
+ background-color: var(--dark-bg);
+ color: var(--text-primary);
+ }
+
+ .card {
+ background: #374151;
+ }
+
+ .card-header {
+ background: #4b5563;
+ }
+
+ .table th {
+ background: #4b5563;
+ }
+}
+
+/* Utility Classes */
+.text-center { text-align: center; }
+.text-right { text-align: right; }
+.text-left { text-align: left; }
+
+.mb-0 { margin-bottom: 0; }
+.mb-1 { margin-bottom: 0.25rem; }
+.mb-2 { margin-bottom: 0.5rem; }
+.mb-3 { margin-bottom: 1rem; }
+.mb-4 { margin-bottom: 1.5rem; }
+.mb-5 { margin-bottom: 3rem; }
+
+.mt-0 { margin-top: 0; }
+.mt-1 { margin-top: 0.25rem; }
+.mt-2 { margin-top: 0.5rem; }
+.mt-3 { margin-top: 1rem; }
+.mt-4 { margin-top: 1.5rem; }
+.mt-5 { margin-top: 3rem; }
+
+.d-flex { display: flex; }
+.d-block { display: block; }
+.d-none { display: none; }
+
+.justify-content-between { justify-content: space-between; }
+.justify-content-center { justify-content: center; }
+.justify-content-end { justify-content: flex-end; }
+
+.align-items-center { align-items: center; }
+.align-items-start { align-items: flex-start; }
+.align-items-end { align-items: flex-end; }
+
+.w-100 { width: 100%; }
+.h-100 { height: 100%; }
+
+/* Custom Scrollbar */
+::-webkit-scrollbar {
+ width: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: var(--border-color);
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--text-secondary);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--primary-color);
+}
+
+/* Animation Classes */
+.fade-in {
+ animation: fadeIn 0.3s ease;
+}
+
+.slide-up {
+ animation: slideUp 0.3s ease;
+}
+
+@keyframes slideUp {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+.bounce {
+ animation: bounce 0.5s ease;
+}
+
+@keyframes bounce {
+ 0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
+ 40% { transform: translateY(-10px); }
+ 60% { transform: translateY(-5px); }
+}
+
+/* Custom Components */
+.progress-ring {
+ transform: rotate(-90deg);
+}
+
+.progress-ring__circle {
+ stroke: var(--primary-color);
+ stroke-linecap: round;
+ transition: stroke-dasharray 0.35s;
+}
+
+.notification-dot {
+ position: absolute;
+ top: -5px;
+ right: -5px;
+ width: 10px;
+ height: 10px;
+ background: var(--error-color);
+ border-radius: 50%;
+ animation: pulse 2s infinite;
+}
+
+/* Print Styles */
+@media print {
+ .sidebar,
+ .header-actions,
+ .btn {
+ display: none !important;
+ }
+
+ .main-content {
+ margin-left: 0;
+ }
+
+ .card {
+ box-shadow: none;
+ border: 1px solid #ddd;
+ }
+}
\ No newline at end of file
diff --git a/assets/js/app.js b/assets/js/app.js
new file mode 100644
index 0000000..71eebf7
--- /dev/null
+++ b/assets/js/app.js
@@ -0,0 +1,912 @@
+/**
+ * WhatsApp Bot Manager - JavaScript
+ * Fecha: 13 de noviembre de 2025
+ */
+
+class WhatsAppBotManager {
+ constructor() {
+ this.apiBaseUrl = './api/';
+ this.currentTab = 'dashboard';
+ this.currentMenuId = null;
+ this.charts = {};
+
+ this.init();
+ }
+
+ init() {
+ this.setupEventListeners();
+ this.loadDashboard();
+ this.setupCharts();
+
+ // Auto-refresh cada 30 segundos
+ setInterval(() => {
+ if (this.currentTab === 'dashboard') {
+ this.refreshStats();
+ }
+ }, 30000);
+ }
+
+ setupEventListeners() {
+ // Navegación de tabs
+ document.querySelectorAll('.nav-link').forEach(link => {
+ link.addEventListener('click', (e) => {
+ e.preventDefault();
+ const tabName = link.getAttribute('data-tab');
+ this.showTab(tabName);
+ });
+ });
+
+ // Formularios
+ this.setupFormListeners();
+
+ // Búsquedas
+ this.setupSearchListeners();
+ }
+
+ setupFormListeners() {
+ // Formulario de configuración
+ const settingsForm = document.getElementById('settings-form');
+ if (settingsForm) {
+ settingsForm.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.saveSettings();
+ });
+ }
+
+ // Formulario de menú
+ const menuForm = document.getElementById('menu-form');
+ if (menuForm) {
+ menuForm.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.saveMenu();
+ });
+ }
+
+ // Formulario de envío de mensaje
+ const sendMessageForm = document.getElementById('send-message-form');
+ if (sendMessageForm) {
+ sendMessageForm.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.sendMessage();
+ });
+ }
+
+ // Formulario de mensaje masivo
+ const broadcastForm = document.getElementById('broadcast-form');
+ if (broadcastForm) {
+ broadcastForm.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.sendBroadcast();
+ });
+ }
+
+ // Cambio de tipo de mensaje
+ const messageType = document.getElementById('message-type');
+ if (messageType) {
+ messageType.addEventListener('change', () => {
+ this.toggleMessageFields();
+ });
+ }
+ }
+
+ setupSearchListeners() {
+ // Búsqueda de conversaciones
+ const searchConversations = document.getElementById('search-conversations');
+ if (searchConversations) {
+ searchConversations.addEventListener('input', (e) => {
+ this.searchConversations(e.target.value);
+ });
+ }
+ }
+
+ showTab(tabName) {
+ // Ocultar todas las pestañas
+ document.querySelectorAll('.tab-content').forEach(tab => {
+ tab.classList.remove('active');
+ });
+
+ // Remover clase active de navegación
+ document.querySelectorAll('.nav-link').forEach(link => {
+ link.classList.remove('active');
+ });
+
+ // Mostrar pestaña seleccionada
+ const targetTab = document.getElementById(tabName);
+ if (targetTab) {
+ targetTab.classList.add('active');
+ }
+
+ // Activar enlace de navegación
+ const activeLink = document.querySelector(`[data-tab="${tabName}"]`);
+ if (activeLink) {
+ activeLink.classList.add('active');
+ }
+
+ this.currentTab = tabName;
+
+ // Cargar datos específicos del tab
+ this.loadTabData(tabName);
+ }
+
+ loadTabData(tabName) {
+ switch (tabName) {
+ case 'dashboard':
+ this.loadDashboard();
+ break;
+ case 'conversations':
+ this.loadConversations();
+ break;
+ case 'users':
+ this.loadUsers();
+ break;
+ case 'menus':
+ this.loadMenus();
+ break;
+ case 'messages':
+ this.loadMessageUsers();
+ break;
+ case 'templates':
+ this.loadTemplates();
+ break;
+ case 'autoresponses':
+ this.loadAutoResponses();
+ break;
+ case 'settings':
+ this.loadSettings();
+ break;
+ case 'logs':
+ this.loadLogs();
+ break;
+ }
+ }
+
+ async loadDashboard() {
+ try {
+ const stats = await this.apiCall('get_stats.php');
+ this.updateStats(stats);
+
+ const recentMessages = await this.apiCall('get_recent_messages.php');
+ this.updateRecentMessages(recentMessages);
+
+ this.updateChart();
+ } catch (error) {
+ this.showError('Error cargando dashboard: ' + error.message);
+ }
+ }
+
+ updateStats(stats) {
+ if (stats) {
+ document.getElementById('total-users').textContent = stats.total_users || 0;
+ document.getElementById('messages-today').textContent = stats.messages_today || 0;
+ document.getElementById('active-users').textContent = stats.active_users || 0;
+ document.getElementById('total-messages').textContent = stats.total_messages || 0;
+ }
+ }
+
+ updateRecentMessages(messages) {
+ const container = document.getElementById('recent-messages');
+ if (!container) return;
+
+ container.innerHTML = '';
+
+ if (messages && messages.length > 0) {
+ messages.forEach(message => {
+ const messageElement = this.createRecentMessageElement(message);
+ container.appendChild(messageElement);
+ });
+ } else {
+ container.innerHTML = '
No hay mensajes recientes
';
+ }
+ }
+
+ createRecentMessageElement(message) {
+ const div = document.createElement('div');
+ div.className = 'recent-message';
+
+ const direction = message.direction === 'incoming' ? '📥' : '📤';
+ const time = new Date(message.created_at).toLocaleString();
+
+ div.innerHTML = `
+
+ ${direction} ${message.phone_number}
+ ${time}
+
+ ${this.truncateText(message.content, 50)}
+ `;
+
+ return div;
+ }
+
+ setupCharts() {
+ const ctx = document.getElementById('messagesChart');
+ if (ctx) {
+ this.charts.messages = new Chart(ctx, {
+ type: 'line',
+ data: {
+ labels: [],
+ datasets: [{
+ label: 'Mensajes',
+ data: [],
+ borderColor: '#25d366',
+ backgroundColor: 'rgba(37, 211, 102, 0.1)',
+ borderWidth: 3,
+ fill: true,
+ tension: 0.4
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ y: {
+ beginAtZero: true,
+ grid: {
+ color: 'rgba(0,0,0,0.1)'
+ }
+ },
+ x: {
+ grid: {
+ color: 'rgba(0,0,0,0.1)'
+ }
+ }
+ },
+ plugins: {
+ legend: {
+ display: false
+ }
+ }
+ }
+ });
+ }
+ }
+
+ async updateChart() {
+ try {
+ const chartData = await this.apiCall('get_chart_data.php');
+ if (chartData && this.charts.messages) {
+ this.charts.messages.data.labels = chartData.labels;
+ this.charts.messages.data.datasets[0].data = chartData.data;
+ this.charts.messages.update();
+ }
+ } catch (error) {
+ console.error('Error updating chart:', error);
+ }
+ }
+
+ async loadConversations() {
+ try {
+ const conversations = await this.apiCall('get_conversations.php');
+ this.updateConversationsTable(conversations);
+ } catch (error) {
+ this.showError('Error cargando conversaciones: ' + error.message);
+ }
+ }
+
+ updateConversationsTable(conversations) {
+ const tbody = document.getElementById('conversations-table');
+ if (!tbody) return;
+
+ tbody.innerHTML = '';
+
+ conversations.forEach(conv => {
+ const row = this.createConversationRow(conv);
+ tbody.appendChild(row);
+ });
+ }
+
+ createConversationRow(conv) {
+ const tr = document.createElement('tr');
+ tr.innerHTML = `
+
+ ${conv.phone_number}
+ ${conv.name || 'Sin nombre'}
+
+ ${this.truncateText(conv.last_message, 50)}
+
+
+ ${conv.message_type}
+
+
+
+
+ ${conv.status}
+
+
+ ${new Date(conv.last_activity).toLocaleString()}
+
+
+
+
+
+
+
+
+ `;
+ return tr;
+ }
+
+ async loadUsers() {
+ try {
+ const users = await this.apiCall('get_users.php');
+ this.updateUsersTable(users);
+ } catch (error) {
+ this.showError('Error cargando usuarios: ' + error.message);
+ }
+ }
+
+ updateUsersTable(users) {
+ const tbody = document.getElementById('users-table');
+ if (!tbody) return;
+
+ tbody.innerHTML = '';
+
+ users.forEach(user => {
+ const row = this.createUserRow(user);
+ tbody.appendChild(row);
+ });
+ }
+
+ createUserRow(user) {
+ const tr = document.createElement('tr');
+ tr.innerHTML = `
+ ${user.id}
+ ${user.phone_number}
+ ${user.name || 'Sin nombre'}
+
+
+ ${user.status}
+
+
+ ${user.current_menu || 'Ninguno'}
+ ${new Date(user.created_at).toLocaleString()}
+
+
+
+
+
+
+
+
+ `;
+ return tr;
+ }
+
+ async loadMenus() {
+ try {
+ const menus = await this.apiCall('get_menus.php');
+ this.updateMenusTree(menus);
+ this.loadMenuParents(menus);
+ } catch (error) {
+ this.showError('Error cargando menús: ' + error.message);
+ }
+ }
+
+ updateMenusTree(menus) {
+ const container = document.getElementById('menus-tree');
+ if (!container) return;
+
+ container.innerHTML = '';
+
+ // Organizar menús por jerarquía
+ const rootMenus = menus.filter(menu => menu.is_root);
+
+ rootMenus.forEach(menu => {
+ const menuElement = this.createMenuElement(menu, menus);
+ container.appendChild(menuElement);
+ });
+ }
+
+ createMenuElement(menu, allMenus) {
+ const div = document.createElement('div');
+ div.className = 'menu-item ' + (menu.is_root ? 'root' : 'child');
+
+ div.innerHTML = `
+
+
${menu.title}
+
${menu.description || 'Sin descripción'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ // Agregar submenús
+ const childMenus = allMenus.filter(child => child.parent_id === menu.id);
+ if (childMenus.length > 0) {
+ const childContainer = document.createElement('div');
+ childContainer.style.marginLeft = '20px';
+ childContainer.style.marginTop = '10px';
+
+ childMenus.forEach(child => {
+ const childElement = this.createMenuElement(child, allMenus);
+ childContainer.appendChild(childElement);
+ });
+
+ div.appendChild(childContainer);
+ }
+
+ return div;
+ }
+
+ loadMenuParents(menus) {
+ const select = document.getElementById('menu-parent');
+ if (!select) return;
+
+ select.innerHTML = 'Sin padre (Menú raíz) ';
+
+ menus.forEach(menu => {
+ const option = document.createElement('option');
+ option.value = menu.id;
+ option.textContent = menu.title;
+ select.appendChild(option);
+ });
+ }
+
+ async loadMessageUsers() {
+ try {
+ const users = await this.apiCall('get_users.php');
+ const templates = await this.apiCall('get_templates.php');
+
+ this.updateMessageUserSelect(users);
+ this.updateTemplateSelect(templates);
+ } catch (error) {
+ this.showError('Error cargando datos de mensaje: ' + error.message);
+ }
+ }
+
+ updateMessageUserSelect(users) {
+ const select = document.getElementById('message-recipient');
+ if (!select) return;
+
+ select.innerHTML = 'Seleccionar usuario... ';
+
+ users.forEach(user => {
+ const option = document.createElement('option');
+ option.value = user.phone_number;
+ option.textContent = `${user.phone_number} ${user.name ? '(' + user.name + ')' : ''}`;
+ select.appendChild(option);
+ });
+ }
+
+ updateTemplateSelect(templates) {
+ const select = document.getElementById('message-template');
+ if (!select) return;
+
+ select.innerHTML = 'Seleccionar plantilla... ';
+
+ templates.forEach(template => {
+ if (template.status === 'approved') {
+ const option = document.createElement('option');
+ option.value = template.template_name;
+ option.textContent = template.name;
+ select.appendChild(option);
+ }
+ });
+ }
+
+ toggleMessageFields() {
+ const messageType = document.getElementById('message-type').value;
+ const textGroup = document.getElementById('message-text-group');
+ const templateGroup = document.getElementById('template-group');
+
+ if (messageType === 'template') {
+ textGroup.style.display = 'none';
+ templateGroup.style.display = 'block';
+ } else {
+ textGroup.style.display = 'block';
+ templateGroup.style.display = 'none';
+ }
+ }
+
+ async sendMessage() {
+ const recipient = document.getElementById('message-recipient').value;
+ const messageType = document.getElementById('message-type').value;
+ const messageText = document.getElementById('message-text').value;
+ const template = document.getElementById('message-template').value;
+
+ if (!recipient) {
+ this.showError('Por favor selecciona un destinatario');
+ return;
+ }
+
+ const data = {
+ recipient: recipient,
+ type: messageType
+ };
+
+ if (messageType === 'text') {
+ if (!messageText) {
+ this.showError('Por favor escribe un mensaje');
+ return;
+ }
+ data.message = messageText;
+ } else if (messageType === 'template') {
+ if (!template) {
+ this.showError('Por favor selecciona una plantilla');
+ return;
+ }
+ data.template = template;
+ }
+
+ try {
+ this.showLoading('Enviando mensaje...');
+ const result = await this.apiCall('send_message.php', 'POST', data);
+
+ if (result.success) {
+ this.showSuccess('Mensaje enviado correctamente');
+ document.getElementById('send-message-form').reset();
+ } else {
+ this.showError(result.error || 'Error enviando mensaje');
+ }
+ } catch (error) {
+ this.showError('Error enviando mensaje: ' + error.message);
+ } finally {
+ this.hideLoading();
+ }
+ }
+
+ async sendBroadcast() {
+ const filter = document.getElementById('broadcast-filter').value;
+ const message = document.getElementById('broadcast-message').value;
+
+ if (!message) {
+ this.showError('Por favor escribe un mensaje');
+ return;
+ }
+
+ if (!confirm('¿Estás seguro de enviar este mensaje a múltiples usuarios?')) {
+ return;
+ }
+
+ try {
+ this.showLoading('Enviando mensajes...');
+ const result = await this.apiCall('send_broadcast.php', 'POST', {
+ filter: filter,
+ message: message
+ });
+
+ if (result.success) {
+ this.showSuccess(`Mensajes enviados a ${result.sent_count} usuarios`);
+ document.getElementById('broadcast-form').reset();
+ } else {
+ this.showError(result.error || 'Error en envío masivo');
+ }
+ } catch (error) {
+ this.showError('Error en envío masivo: ' + error.message);
+ } finally {
+ this.hideLoading();
+ }
+ }
+
+ async saveMenu() {
+ const formData = {
+ name: document.getElementById('menu-name').value,
+ title: document.getElementById('menu-title').value,
+ description: document.getElementById('menu-description').value,
+ parent_id: document.getElementById('menu-parent').value || null,
+ is_active: document.getElementById('menu-active').checked ? 1 : 0
+ };
+
+ try {
+ this.showLoading('Guardando menú...');
+ const result = await this.apiCall('save_menu.php', 'POST', formData);
+
+ if (result.success) {
+ this.showSuccess('Menú guardado correctamente');
+ document.getElementById('menu-form').reset();
+ this.loadMenus();
+ } else {
+ this.showError(result.error || 'Error guardando menú');
+ }
+ } catch (error) {
+ this.showError('Error guardando menú: ' + error.message);
+ } finally {
+ this.hideLoading();
+ }
+ }
+
+ async saveSettings() {
+ const formData = {
+ whatsapp_token: document.getElementById('whatsapp-token').value,
+ phone_number_id: document.getElementById('phone-number-id').value,
+ webhook_token: document.getElementById('webhook-token').value,
+ business_name: document.getElementById('business-name').value,
+ welcome_message: document.getElementById('welcome-message').value
+ };
+
+ try {
+ this.showLoading('Guardando configuración...');
+ const result = await this.apiCall('save_settings.php', 'POST', formData);
+
+ if (result.success) {
+ this.showSuccess('Configuración guardada correctamente');
+ } else {
+ this.showError(result.error || 'Error guardando configuración');
+ }
+ } catch (error) {
+ this.showError('Error guardando configuración: ' + error.message);
+ } finally {
+ this.hideLoading();
+ }
+ }
+
+ async loadSettings() {
+ try {
+ const settings = await this.apiCall('get_settings.php');
+
+ if (settings) {
+ document.getElementById('whatsapp-token').value = settings.whatsapp_token || '';
+ document.getElementById('phone-number-id').value = settings.phone_number_id || '';
+ document.getElementById('webhook-token').value = settings.webhook_token || '';
+ document.getElementById('business-name').value = settings.business_name || '';
+ document.getElementById('welcome-message').value = settings.welcome_message || '';
+ }
+ } catch (error) {
+ this.showError('Error cargando configuración: ' + error.message);
+ }
+ }
+
+ async loadLogs() {
+ try {
+ const logs = await this.apiCall('get_logs.php');
+ this.updateLogsTable(logs);
+ } catch (error) {
+ this.showError('Error cargando logs: ' + error.message);
+ }
+ }
+
+ updateLogsTable(logs) {
+ const tbody = document.getElementById('logs-table');
+ if (!tbody) return;
+
+ tbody.innerHTML = '';
+
+ logs.forEach(log => {
+ const row = this.createLogRow(log);
+ tbody.appendChild(row);
+ });
+ }
+
+ createLogRow(log) {
+ const tr = document.createElement('tr');
+ tr.innerHTML = `
+ ${new Date(log.created_at).toLocaleString()}
+ ${log.ip_address}
+
+
+ ${log.status_code}
+
+
+
+
+ Ver Request
+
+
+
+
+ Ver Response
+
+
+ `;
+ return tr;
+ }
+
+ // Utility methods
+ async apiCall(endpoint, method = 'GET', data = null) {
+ const options = {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json',
+ }
+ };
+
+ if (data && method !== 'GET') {
+ options.body = JSON.stringify(data);
+ }
+
+ const response = await fetch(this.apiBaseUrl + endpoint, options);
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return await response.json();
+ }
+
+ showError(message) {
+ this.showNotification(message, 'error');
+ }
+
+ showSuccess(message) {
+ this.showNotification(message, 'success');
+ }
+
+ showWarning(message) {
+ this.showNotification(message, 'warning');
+ }
+
+ showInfo(message) {
+ this.showNotification(message, 'info');
+ }
+
+ showNotification(message, type = 'info') {
+ // Crear notificación toast
+ const toast = document.createElement('div');
+ toast.className = `alert alert-${type} position-fixed`;
+ toast.style.top = '20px';
+ toast.style.right = '20px';
+ toast.style.zIndex = '9999';
+ toast.style.minWidth = '300px';
+ toast.innerHTML = `
+
+ ${message}
+
+ `;
+
+ document.body.appendChild(toast);
+
+ // Auto-remove después de 5 segundos
+ setTimeout(() => {
+ if (toast.parentElement) {
+ toast.remove();
+ }
+ }, 5000);
+ }
+
+ getNotificationIcon(type) {
+ const icons = {
+ success: 'check-circle',
+ error: 'exclamation-circle',
+ warning: 'exclamation-triangle',
+ info: 'info-circle'
+ };
+ return icons[type] || 'info-circle';
+ }
+
+ showLoading(message = 'Cargando...') {
+ const loading = document.createElement('div');
+ loading.id = 'loading-overlay';
+ loading.className = 'position-fixed w-100 h-100 d-flex align-items-center justify-content-center';
+ loading.style.top = '0';
+ loading.style.left = '0';
+ loading.style.backgroundColor = 'rgba(0,0,0,0.5)';
+ loading.style.zIndex = '9999';
+ loading.innerHTML = `
+
+ `;
+
+ document.body.appendChild(loading);
+ }
+
+ hideLoading() {
+ const loading = document.getElementById('loading-overlay');
+ if (loading) {
+ loading.remove();
+ }
+ }
+
+ truncateText(text, length) {
+ if (!text) return '';
+ return text.length > length ? text.substring(0, length) + '...' : text;
+ }
+
+ getStatusColor(status) {
+ const colors = {
+ 'active': 'success',
+ 'inactive': 'secondary',
+ 'blocked': 'danger',
+ 'sent': 'info',
+ 'delivered': 'success',
+ 'read': 'success',
+ 'failed': 'danger'
+ };
+ return colors[status] || 'secondary';
+ }
+
+ getMessageTypeColor(type) {
+ const colors = {
+ 'text': 'primary',
+ 'image': 'info',
+ 'audio': 'warning',
+ 'video': 'danger',
+ 'document': 'secondary',
+ 'template': 'success'
+ };
+ return colors[type] || 'primary';
+ }
+
+ // Funciones específicas
+ refreshData() {
+ this.loadTabData(this.currentTab);
+ this.showSuccess('Datos actualizados');
+ }
+
+ refreshStats() {
+ this.loadDashboard();
+ }
+
+ exportUsers() {
+ window.open(this.apiBaseUrl + 'export_users.php', '_blank');
+ }
+
+ viewConversation(userId) {
+ // Implementar modal o página de conversación
+ console.log('Ver conversación del usuario:', userId);
+ }
+
+ replyToUser(phoneNumber) {
+ document.getElementById('message-recipient').value = phoneNumber;
+ this.showTab('messages');
+ }
+
+ editUser(userId) {
+ // Implementar modal de edición de usuario
+ console.log('Editar usuario:', userId);
+ }
+
+ blockUser(userId) {
+ if (confirm('¿Estás seguro de bloquear este usuario?')) {
+ // Implementar bloqueo de usuario
+ console.log('Bloquear usuario:', userId);
+ }
+ }
+
+ editMenu(menuId) {
+ this.currentMenuId = menuId;
+ // Cargar datos del menú en el formulario
+ console.log('Editar menú:', menuId);
+ }
+
+ deleteMenu(menuId) {
+ if (confirm('¿Estás seguro de eliminar este menú?')) {
+ // Implementar eliminación
+ console.log('Eliminar menú:', menuId);
+ }
+ }
+
+ manageMenuOptions(menuId) {
+ // Mostrar modal de opciones de menú
+ console.log('Gestionar opciones del menú:', menuId);
+ }
+
+ viewLogDetails(logId, type) {
+ // Mostrar detalles del log en modal
+ console.log('Ver detalles del log:', logId, type);
+ }
+
+ clearLogs() {
+ if (confirm('¿Estás seguro de eliminar todos los logs?')) {
+ // Implementar limpieza de logs
+ console.log('Limpiar logs');
+ }
+ }
+
+ refreshLogs() {
+ this.loadLogs();
+ }
+}
+
+// Inicializar la aplicación
+const app = new WhatsAppBotManager();
+
+// Funciones globales
+window.refreshData = () => app.refreshData();
+window.exportUsers = () => app.exportUsers();
+window.refreshLogs = () => app.refreshLogs();
+window.clearLogs = () => app.clearLogs();
\ No newline at end of file
diff --git a/classes/Database.php b/classes/Database.php
new file mode 100644
index 0000000..56daba8
--- /dev/null
+++ b/classes/Database.php
@@ -0,0 +1,114 @@
+ PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+ PDO::ATTR_PERSISTENT => true,
+ PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . DB_CHARSET . " COLLATE utf8mb4_unicode_ci"
+ ];
+
+ $this->pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
+ } catch (PDOException $e) {
+ error_log("Database connection failed: " . $e->getMessage());
+ throw new Exception("Error de conexión a la base de datos");
+ }
+ }
+
+ public static function getInstance() {
+ if (self::$instance === null) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ public function getConnection() {
+ return $this->pdo;
+ }
+
+ public function query($sql, $params = []) {
+ try {
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->execute($params);
+ return $stmt;
+ } catch (PDOException $e) {
+ error_log("Query failed: " . $e->getMessage() . " SQL: " . $sql);
+ throw new Exception("Error en la consulta a la base de datos");
+ }
+ }
+
+ public function fetch($sql, $params = []) {
+ $stmt = $this->query($sql, $params);
+ return $stmt->fetch();
+ }
+
+ public function fetchAll($sql, $params = []) {
+ $stmt = $this->query($sql, $params);
+ return $stmt->fetchAll();
+ }
+
+ public function insert($table, $data) {
+ $keys = array_keys($data);
+ $fields = implode(',', $keys);
+ $placeholders = ':' . implode(', :', $keys);
+
+ $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$placeholders})";
+ $stmt = $this->query($sql, $data);
+
+ return $this->pdo->lastInsertId();
+ }
+
+ public function update($table, $data, $where, $whereParams = []) {
+ $fields = [];
+ foreach (array_keys($data) as $key) {
+ $fields[] = "{$key} = :{$key}";
+ }
+ $fieldsStr = implode(', ', $fields);
+
+ $sql = "UPDATE {$table} SET {$fieldsStr} WHERE {$where}";
+ $params = array_merge($data, $whereParams);
+
+ return $this->query($sql, $params);
+ }
+
+ public function delete($table, $where, $params = []) {
+ $sql = "DELETE FROM {$table} WHERE {$where}";
+ return $this->query($sql, $params);
+ }
+
+ public function beginTransaction() {
+ return $this->pdo->beginTransaction();
+ }
+
+ public function commit() {
+ return $this->pdo->commit();
+ }
+
+ public function rollback() {
+ return $this->pdo->rollback();
+ }
+
+ public function lastInsertId() {
+ return $this->pdo->lastInsertId();
+ }
+
+ // Prevenir clonación
+ private function __clone() {}
+
+ // Prevenir deserialización
+ public function __wakeup() {
+ throw new Exception("Cannot unserialize singleton");
+ }
+}
+?>
\ No newline at end of file
diff --git a/config/config.php b/config/config.php
new file mode 100644
index 0000000..f98ac39
--- /dev/null
+++ b/config/config.php
@@ -0,0 +1,211 @@
+ $ipData['locked_until']) {
+ unset($attempts[$ip]);
+ file_put_contents($attemptsFile, json_encode($attempts));
+ return true;
+ }
+
+ return $ipData['attempts'] < MAX_LOGIN_ATTEMPTS;
+}
+
+// Función para registrar intento de login fallido
+function recordFailedLogin($ip) {
+ $attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
+
+ $attempts = [];
+ if (file_exists($attemptsFile)) {
+ $attempts = json_decode(file_get_contents($attemptsFile), true);
+ }
+
+ if (!isset($attempts[$ip])) {
+ $attempts[$ip] = ['attempts' => 0, 'locked_until' => 0];
+ }
+
+ $attempts[$ip]['attempts']++;
+
+ if ($attempts[$ip]['attempts'] >= MAX_LOGIN_ATTEMPTS) {
+ $attempts[$ip]['locked_until'] = time() + LOGIN_LOCKOUT_TIME;
+ }
+
+ file_put_contents($attemptsFile, json_encode($attempts));
+}
+
+// Función para limpiar intentos de login exitoso
+function clearLoginAttempts($ip) {
+ $attemptsFile = dirname(__DIR__) . '/.login_attempts.json';
+
+ if (file_exists($attemptsFile)) {
+ $attempts = json_decode(file_get_contents($attemptsFile), true);
+ unset($attempts[$ip]);
+ file_put_contents($attemptsFile, json_encode($attempts));
+ }
+}
+
+// Autoloader
+spl_autoload_register(function ($class) {
+ $directories = [
+ dirname(__DIR__) . '/classes/',
+ dirname(__DIR__) . '/services/',
+ dirname(__DIR__) . '/controllers/'
+ ];
+
+ foreach ($directories as $directory) {
+ $file = $directory . $class . '.php';
+ if (file_exists($file)) {
+ require_once $file;
+ return;
+ }
+ }
+});
+
+// Función para obtener configuración desde base de datos
+function getConfigFromDB($key, $default = null) {
+ static $configs = null;
+
+ if ($configs === null && isInstallationCompleted()) {
+ try {
+ $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,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+ ]
+ );
+
+ $stmt = $pdo->query("SELECT config_key, config_value FROM system_config");
+ $configs = [];
+ while ($row = $stmt->fetch()) {
+ $configs[$row['config_key']] = $row['config_value'];
+ }
+ } catch (PDOException $e) {
+ error_log("Error loading config: " . $e->getMessage());
+ $configs = [];
+ }
+ }
+
+ return $configs[$key] ?? $default;
+}
+
+// Inicializar sesión si no está activa
+if (session_status() == PHP_SESSION_NONE) {
+ session_start();
+}
+
+// Verificar timeout de sesión
+if (isUserLoggedIn() && isset($_SESSION['last_activity'])) {
+ if (time() - $_SESSION['last_activity'] > SESSION_TIMEOUT) {
+ session_destroy();
+ session_start();
+ }
+}
+
+// Actualizar última actividad
+if (isUserLoggedIn()) {
+ $_SESSION['last_activity'] = time();
+}
+?>
\ No newline at end of file
diff --git a/database/schema.sql b/database/schema.sql
new file mode 100644
index 0000000..aa26cdd
--- /dev/null
+++ b/database/schema.sql
@@ -0,0 +1,179 @@
+-- Base de datos para sistema de WhatsApp Bot
+-- Fecha: 13 de noviembre de 2025
+
+-- Tabla de usuarios
+CREATE TABLE users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ phone_number VARCHAR(20) UNIQUE NOT NULL,
+ name VARCHAR(100),
+ email VARCHAR(100),
+ status ENUM('active', 'inactive', 'blocked') DEFAULT 'active',
+ current_menu_id INT NULL,
+ current_step INT DEFAULT 0,
+ session_data JSON,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ INDEX idx_phone (phone_number),
+ INDEX idx_status (status)
+);
+
+-- Tabla de conversaciones
+CREATE TABLE conversations (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT NOT NULL,
+ message_id VARCHAR(100),
+ direction ENUM('incoming', 'outgoing') NOT NULL,
+ message_type ENUM('text', 'image', 'audio', 'video', 'document', 'template') DEFAULT 'text',
+ content TEXT,
+ media_url VARCHAR(500),
+ status ENUM('sent', 'delivered', 'read', 'failed') DEFAULT 'sent',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ INDEX idx_user (user_id),
+ INDEX idx_created (created_at),
+ INDEX idx_direction (direction)
+);
+
+-- Tabla de menús (sistema parametrizable)
+CREATE TABLE menus (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(100) NOT NULL,
+ title VARCHAR(200) NOT NULL,
+ description TEXT,
+ parent_id INT NULL,
+ is_root BOOLEAN DEFAULT FALSE,
+ is_active BOOLEAN DEFAULT TRUE,
+ order_position INT DEFAULT 0,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ FOREIGN KEY (parent_id) REFERENCES menus(id) ON DELETE CASCADE,
+ INDEX idx_parent (parent_id),
+ INDEX idx_active (is_active),
+ INDEX idx_root (is_root)
+);
+
+-- Tabla de opciones de menú
+CREATE TABLE menu_options (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ menu_id INT NOT NULL,
+ option_number INT NOT NULL,
+ text VARCHAR(200) NOT NULL,
+ action_type ENUM('menu', 'message', 'api_call', 'end') NOT NULL,
+ action_value VARCHAR(500),
+ is_active BOOLEAN DEFAULT TRUE,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (menu_id) REFERENCES menus(id) ON DELETE CASCADE,
+ INDEX idx_menu (menu_id),
+ INDEX idx_option (option_number),
+ UNIQUE KEY unique_menu_option (menu_id, option_number)
+);
+
+-- Tabla de respuestas automáticas
+CREATE TABLE auto_responses (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ trigger_type ENUM('keyword', 'menu_selection', 'welcome') NOT NULL,
+ trigger_value VARCHAR(200),
+ response_text TEXT NOT NULL,
+ response_type ENUM('text', 'template') DEFAULT 'text',
+ is_active BOOLEAN DEFAULT TRUE,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ INDEX idx_trigger (trigger_type),
+ INDEX idx_active (is_active)
+);
+
+-- Tabla de configuración del sistema
+CREATE TABLE system_config (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ config_key VARCHAR(100) UNIQUE NOT NULL,
+ config_value TEXT,
+ description TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+);
+
+-- Tabla de webhooks logs
+CREATE TABLE webhook_logs (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ request_body TEXT,
+ response_body TEXT,
+ status_code INT,
+ ip_address VARCHAR(45),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ INDEX idx_created (created_at)
+);
+
+-- Tabla de plantillas de mensajes
+CREATE TABLE message_templates (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(100) UNIQUE NOT NULL,
+ template_name VARCHAR(100) NOT NULL, -- Nombre en WhatsApp Business API
+ language_code VARCHAR(10) DEFAULT 'es',
+ category ENUM('marketing', 'utility', 'authentication') DEFAULT 'utility',
+ status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ INDEX idx_status (status)
+);
+
+-- Insertar configuraciones iniciales
+INSERT INTO system_config (config_key, config_value, description) VALUES
+('whatsapp_token', 'EAAWTKu1iZAGABP46c7GZAFvHremu9uvTLOlgZCAa4eKUlaCqszzsQjjLcgnqORzo2EBhB7LZAcAqJ7clpGvNCk3oXKnqo8Fl0K2o7eHYoeaqZBesqjBPRfsd4ZBOdY4kN1YBJnmRznegTD9wtJZByVrRcMXF8YXaAJjLCuIFZCK7QFUpId7i4eVkFvbqUYnUKA91rgPvaeGRNZCLHa5ZC1ZB2Skn0gFNgPTBx7bedAMULYZD', 'Token de acceso de WhatsApp Business API'),
+('whatsapp_phone_number_id', '938177732702718', 'ID del número de teléfono de WhatsApp'),
+('webhook_verify_token', 'mi_token_secreto_123', 'Token de verificación para webhook'),
+('welcome_message', '¡Hola! 👋 Bienvenido a nuestro servicio automatizado. Escribe *menu* para ver las opciones disponibles.', 'Mensaje de bienvenida para nuevos usuarios'),
+('business_name', 'Mi Empresa', 'Nombre de la empresa');
+
+-- Insertar menús de ejemplo
+INSERT INTO menus (name, title, description, is_root, order_position) VALUES
+('main_menu', '🏠 Menú Principal', 'Menú principal del sistema', TRUE, 1),
+('services_menu', '💼 Servicios', 'Menú de servicios disponibles', FALSE, 2),
+('payments_menu', '💳 Pagos', 'Opciones de pago', FALSE, 3),
+('support_menu', '🆘 Soporte', 'Menú de soporte técnico', FALSE, 4);
+
+-- Obtener IDs de menús para las opciones
+SET @main_menu_id = (SELECT id FROM menus WHERE name = 'main_menu');
+SET @services_menu_id = (SELECT id FROM menus WHERE name = 'services_menu');
+SET @payments_menu_id = (SELECT id FROM menus WHERE name = 'payments_menu');
+SET @support_menu_id = (SELECT id FROM menus WHERE name = 'support_menu');
+
+-- Actualizar parent_id para menús secundarios
+UPDATE menus SET parent_id = @main_menu_id WHERE name IN ('services_menu', 'payments_menu', 'support_menu');
+
+-- Insertar opciones del menú principal
+INSERT INTO menu_options (menu_id, option_number, text, action_type, action_value) VALUES
+(@main_menu_id, 1, '💼 Ver servicios disponibles', 'menu', 'services_menu'),
+(@main_menu_id, 2, '💳 Realizar pago', 'menu', 'payments_menu'),
+(@main_menu_id, 3, '🆘 Contactar soporte', 'menu', 'support_menu'),
+(@main_menu_id, 0, '❌ Salir', 'end', 'Gracias por usar nuestro servicio. ¡Hasta pronto!');
+
+-- Insertar opciones del menú de servicios
+INSERT INTO menu_options (menu_id, option_number, text, action_type, action_value) VALUES
+(@services_menu_id, 1, '📋 Consulta de información', 'message', 'Un representante se contactará contigo para brindarte información detallada. Por favor, indica qué información necesitas.'),
+(@services_menu_id, 2, '📞 Agendar cita', 'message', 'Para agendar una cita, por favor proporciona tu nombre completo y tu disponibilidad horaria.'),
+(@services_menu_id, 3, '💰 Cotizar servicio', 'message', 'Para realizar una cotización, describe detalladamente el servicio que necesitas.'),
+(@services_menu_id, 0, '🔙 Volver al menú principal', 'menu', 'main_menu');
+
+-- Insertar opciones del menú de pagos
+INSERT INTO menu_options (menu_id, option_number, text, action_type, action_value) VALUES
+(@payments_menu_id, 1, '🧾 Consultar factura', 'message', 'Por favor, proporciona tu número de factura o documento de identidad para consultar tu factura pendiente.'),
+(@payments_menu_id, 2, '💸 Pagar factura', 'message', 'Para procesar tu pago, necesitamos el número de factura y el método de pago preferido (transferencia, tarjeta, etc).'),
+(@payments_menu_id, 3, '📄 Solicitar factura', 'message', 'Indica el período o servicio para el cual necesitas la factura.'),
+(@payments_menu_id, 0, '🔙 Volver al menú principal', 'menu', 'main_menu');
+
+-- Insertar opciones del menú de soporte
+INSERT INTO menu_options (menu_id, option_number, text, action_type, action_value) VALUES
+(@support_menu_id, 1, '🔧 Reporte técnico', 'message', 'Describe detalladamente el problema técnico que estás experimentando. Un técnico se contactará contigo pronto.'),
+(@support_menu_id, 2, '❓ Preguntas frecuentes', 'message', 'Puedes consultar nuestras preguntas frecuentes en: www.miempresa.com/faq o describe tu pregunta específica.'),
+(@support_menu_id, 3, '👤 Hablar con agente', 'message', 'Te estamos conectando con un agente humano. Por favor, espera un momento...'),
+(@support_menu_id, 0, '🔙 Volver al menú principal', 'menu', 'main_menu');
+
+-- Insertar respuestas automáticas
+INSERT INTO auto_responses (trigger_type, trigger_value, response_text) VALUES
+('keyword', 'menu', 'Aquí tienes nuestro menú principal:'),
+('keyword', 'hola', '¡Hola! 👋 Escribe *menu* para ver nuestras opciones.'),
+('keyword', 'ayuda', 'Estoy aquí para ayudarte. Escribe *menu* para ver las opciones disponibles.'),
+('welcome', '', '¡Bienvenido! 👋 Soy tu asistente virtual. Escribe *menu* para comenzar.');
+
+-- Insertar plantillas de mensaje
+INSERT INTO message_templates (name, template_name, language_code, category, status) VALUES
+('saludo_inicial', 'hello_world', 'es', 'utility', 'approved'),
+('confirmacion_pago', 'payment_confirmation', 'es', 'utility', 'pending');
\ No newline at end of file
diff --git a/debug_install.php b/debug_install.php
new file mode 100644
index 0000000..ea9ae65
--- /dev/null
+++ b/debug_install.php
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+ 🔍 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/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..d20eb78
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,261 @@
+#!/bin/bash
+# Script de despliegue automático para WhatsApp Bot Manager
+# Uso: ./deploy.sh [dominio] [usuario_db] [password_db]
+
+echo "🚀 Iniciando despliegue de WhatsApp Bot Manager..."
+
+# Validar parámetros
+if [ $# -ne 3 ]; then
+ echo "❌ Error: Faltan parámetros"
+ echo "Uso: $0 "
+ echo "Ejemplo: $0 midominio.com db_user mi_password"
+ exit 1
+fi
+
+DOMAIN=$1
+DB_USER=$2
+DB_PASS=$3
+PROJECT_PATH="/var/www/html/bot"
+
+echo "📋 Configuración:"
+echo " Dominio: $DOMAIN"
+echo " Usuario DB: $DB_USER"
+echo " Ruta: $PROJECT_PATH"
+
+# Crear directorio del proyecto
+echo "📁 Creando directorios..."
+sudo mkdir -p $PROJECT_PATH
+cd $PROJECT_PATH
+
+# Configurar archivo de configuración
+echo "⚙️ Configurando config.php..."
+cat > config/config.php << EOF
+
+EOF
+
+# Crear .htaccess de seguridad
+echo "🔒 Configurando seguridad..."
+cat > .htaccess << EOF
+RewriteEngine On
+
+# Forzar HTTPS
+RewriteCond %{HTTPS} off
+RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+# Proteger archivos sensibles
+
+ Require all denied
+
+
+
+ Require all denied
+
+
+
+ Require all denied
+
+
+# Cache headers
+
+ ExpiresActive on
+ ExpiresByType text/css "access plus 1 year"
+ ExpiresByType application/javascript "access plus 1 year"
+ ExpiresByType image/png "access plus 1 year"
+ ExpiresByType image/jpg "access plus 1 year"
+
+
+# Compresión GZIP
+
+ AddOutputFilterByType DEFLATE text/plain
+ AddOutputFilterByType DEFLATE text/html
+ AddOutputFilterByType DEFLATE text/css
+ AddOutputFilterByType DEFLATE application/javascript
+ AddOutputFilterByType DEFLATE application/json
+
+EOF
+
+# Configurar permisos
+echo "📝 Configurando permisos..."
+sudo chown -R www-data:www-data $PROJECT_PATH
+sudo find $PROJECT_PATH -type f -exec chmod 644 {} \;
+sudo find $PROJECT_PATH -type d -exec chmod 755 {} \;
+sudo chmod 600 $PROJECT_PATH/config/config.php
+
+# Configurar Virtual Host de Apache
+echo "🌐 Configurando Apache Virtual Host..."
+sudo tee /etc/apache2/sites-available/whatsapp-bot.conf > /dev/null << EOF
+
+ ServerName $DOMAIN
+ ServerAlias www.$DOMAIN
+ DocumentRoot $PROJECT_PATH
+
+
+ AllowOverride All
+ Require all granted
+ DirectoryIndex index.php
+
+
+ ErrorLog \${APACHE_LOG_DIR}/whatsapp-bot_error.log
+ CustomLog \${APACHE_LOG_DIR}/whatsapp-bot_access.log combined
+
+ # Redirigir HTTP a HTTPS
+ RewriteEngine On
+ RewriteCond %{HTTPS} off
+ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
+
+
+
+ ServerName $DOMAIN
+ ServerAlias www.$DOMAIN
+ DocumentRoot $PROJECT_PATH
+
+ SSLEngine on
+ SSLCertificateFile /etc/letsencrypt/live/$DOMAIN/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/$DOMAIN/privkey.pem
+
+
+ AllowOverride All
+ Require all granted
+ DirectoryIndex index.php
+
+
+ ErrorLog \${APACHE_LOG_DIR}/whatsapp-bot_ssl_error.log
+ CustomLog \${APACHE_LOG_DIR}/whatsapp-bot_ssl_access.log combined
+
+
+EOF
+
+# Habilitar sitio
+echo "✅ Habilitando sitio..."
+sudo a2ensite whatsapp-bot.conf
+sudo a2enmod rewrite ssl
+sudo systemctl reload apache2
+
+# Configurar SSL con Let's Encrypt
+echo "🔐 Configurando SSL..."
+if command -v certbot &> /dev/null; then
+ sudo certbot --apache -d $DOMAIN -d www.$DOMAIN --non-interactive --agree-tos --email admin@$DOMAIN
+else
+ echo "⚠️ Certbot no instalado. Instala SSL manualmente:"
+ echo " sudo apt install certbot python3-certbot-apache"
+ echo " sudo certbot --apache -d $DOMAIN"
+fi
+
+# Crear base de datos
+echo "🗄️ Configurando base de datos..."
+sudo mysql -e "CREATE DATABASE IF NOT EXISTS whatsapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
+sudo mysql -e "CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';"
+sudo mysql -e "GRANT ALL PRIVILEGES ON whatsapp.* TO '$DB_USER'@'localhost';"
+sudo mysql -e "FLUSH PRIVILEGES;"
+
+# Crear script de backup
+echo "💾 Configurando backup automático..."
+sudo tee /usr/local/bin/backup-whatsapp.sh > /dev/null << EOF
+#!/bin/bash
+DATE=\$(date +%Y%m%d_%H%M%S)
+BACKUP_DIR="/var/backups/whatsapp"
+mkdir -p \$BACKUP_DIR
+
+# Backup de base de datos
+mysqldump -u $DB_USER -p'$DB_PASS' whatsapp > \$BACKUP_DIR/whatsapp_\$DATE.sql
+
+# Backup de archivos
+tar -czf \$BACKUP_DIR/files_\$DATE.tar.gz $PROJECT_PATH
+
+# Eliminar backups antiguos (más de 7 días)
+find \$BACKUP_DIR -name "*.sql" -mtime +7 -delete
+find \$BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
+
+echo "Backup completado: \$DATE"
+EOF
+
+sudo chmod +x /usr/local/bin/backup-whatsapp.sh
+
+# Configurar cron para backup diario
+echo "⏰ Configurando backup automático diario..."
+(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/backup-whatsapp.sh") | crontab -
+
+# Mostrar información final
+echo ""
+echo "🎉 ¡Despliegue completado!"
+echo ""
+echo "📋 Información importante:"
+echo " 🌐 Sitio web: https://$DOMAIN/migrador"
+echo " ⚙️ Instalador: https://$DOMAIN/migrador/install.php"
+echo " 🧪 Pruebas: https://$DOMAIN/migrador/test.php"
+echo " 📱 Webhook: https://$DOMAIN/migrador/api/webhook.php"
+echo ""
+echo "🔧 Próximos pasos:"
+echo " 1. Edita config/config.php con tus tokens de WhatsApp"
+echo " 2. Ejecuta el instalador: https://$DOMAIN/migrador/install.php"
+echo " 3. Configura webhook en Facebook Developers"
+echo " 4. Prueba el sistema: https://$DOMAIN/migrador/test.php"
+echo ""
+echo "📁 Rutas importantes:"
+echo " Config: $PROJECT_PATH/config/config.php"
+echo " Logs: /var/log/apache2/whatsapp-bot_*.log"
+echo " Backups: /var/backups/whatsapp/"
+echo ""
+echo "🔒 Webhook URL para Facebook:"
+echo " https://$DOMAIN/migrador/api/webhook.php"
+echo " Token de verificación: mi_token_secreto_123"
+echo ""
+
+# Verificar estado de servicios
+echo "📊 Estado de servicios:"
+sudo systemctl is-active apache2 && echo " ✅ Apache: Funcionando" || echo " ❌ Apache: Error"
+sudo systemctl is-active mysql && echo " ✅ MySQL: Funcionando" || echo " ❌ MySQL: Error"
+
+echo ""
+echo "¡Tu WhatsApp Bot Manager está listo! 🚀"
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..7efb6b3
--- /dev/null
+++ b/index.html
@@ -0,0 +1,549 @@
+
+
+
+
+
+ WhatsApp Bot Manager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Usuarios
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mensajes Hoy
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Usuarios Activos
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Mensajes
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Usuario
+ Último Mensaje
+ Tipo
+ Estado
+ Fecha
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Teléfono
+ Nombre
+ Estado
+ Menú Actual
+ Registro
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filtro de Usuarios
+
+ Todos los usuarios
+ Solo usuarios activos
+ Usuarios recientes (30 días)
+
+
+
+ Mensaje
+
+
+
+
+ Cuidado: Este mensaje se enviará a múltiples usuarios.
+
+
+ Envío Masivo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nombre
+ Plantilla WhatsApp
+ Idioma
+ Categoría
+ Estado
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tipo
+ Palabra Clave
+ Respuesta
+ Estado
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Configuración General
+
+ Nombre de la Empresa
+
+
+
+ Mensaje de Bienvenida
+
+
+
+
+
+ Guardar Configuración
+
+
+
+
+
+
+
+
+
+
+ Versión:
+ 1.0.0
+
+ PHP:
+ 8.0+
+
+ Base de Datos:
+ MySQL
+
+ Webhook URL:
+
+ http://localhost/migrador/api/webhook.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fecha
+ IP
+ Estado
+ Request
+ Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..b5bd120
--- /dev/null
+++ b/index.php
@@ -0,0 +1,530 @@
+getMessage();
+}
+?>
+
+
+
+
+
+ WhatsApp Bot Manager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Usuarios
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mensajes Hoy
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Usuarios Activos
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Mensajes
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Usuario
+ Último Mensaje
+ Tipo
+ Estado
+ Fecha
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Teléfono
+ Nombre
+ Estado
+ Menú Actual
+ Registro
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Destinatario
+
+ Seleccionar usuario...
+
+
+
+ Tipo de Mensaje
+
+ Texto
+ Plantilla
+
+
+
+ Mensaje
+
+
+
+ Plantilla
+
+ Seleccionar plantilla...
+
+
+
+ Enviar Mensaje
+
+
+
+
+
+
+
+
+
+
+
+ Filtro de Usuarios
+
+ Todos los usuarios
+ Solo usuarios activos
+ Usuarios recientes (30 días)
+
+
+
+ Mensaje
+
+
+
+
+ Cuidado: Este mensaje se enviará a múltiples usuarios.
+
+
+ Envío Masivo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nombre
+ Plantilla WhatsApp
+ Idioma
+ Categoría
+ Estado
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tipo
+ Palabra Clave
+ Respuesta
+ Estado
+ Acciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Configuración General
+
+ Nombre de la Empresa
+
+
+
+ Mensaje de Bienvenida
+
+
+
+
+
+ Guardar Configuración
+
+
+
+
+
+
+
+
+
+
+ Versión:
+ 1.0.0
+
+ PHP:
+
+
+ Base de Datos:
+
+
+ Webhook URL:
+
+ /api/webhook.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fecha
+ IP
+ Estado
+ Request
+ Response
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/inicio.html b/inicio.html
new file mode 100644
index 0000000..19c90b9
--- /dev/null
+++ b/inicio.html
@@ -0,0 +1,406 @@
+
+
+
+
+
+ 🚀 WhatsApp Bot Manager - Inicio Rápido
+
+
+
+
+
+
+
+
+
+
+
+
+
+
WhatsApp Bot Manager
+
Sistema completo de chatbot para WhatsApp con panel de administración
+
+ PHP 8.0+
+ MySQL
+ WhatsApp Business API
+ Bootstrap 5
+ Chart.js
+
+
+
+
+
+
+
+
+
+ Guía de Inicio Rápido
+
+
+
+
+
+
1
+
Instalar BD
+
Ejecuta el instalador automático
+
+ Instalar
+
+
+
+
+
+
+
+
2
+
Verificar
+
Prueba todos los componentes
+
+ Probar
+
+
+
+
+
+
+
+
3
+
Configurar
+
Ajusta WhatsApp y webhooks
+
+ Panel
+
+
+
+
+
+
+
+
4
+
¡Listo!
+
Inicia conversaciones
+
+ Ver Más
+
+
+
+
+
+
+
+
+
+
+
+
+ Características Principales
+
+
+
+
+
Bot Inteligente
+
Sistema de menús navegables por números con respuestas automáticas
+
+
+
+
Dashboard Analytics
+
Estadísticas en tiempo real con gráficos interactivos
+
+
+
+
Gestión de Usuarios
+
Administra contactos y segmenta audiencias fácilmente
+
+
+
+
Mensajes Masivos
+
Envía campañas a múltiples usuarios con filtros avanzados
+
+
+
+
API Completa
+
Integra con otros sistemas usando nuestras APIs REST
+
+
+
+
Responsive
+
Interfaz adaptable para desktop, tablet y móvil
+
+
+
+
+
+
+
+
+
+
+
+ Stack Tecnológico
+
+
+
+
+
PHP 8.0+
+ Backend robusto y moderno
+
+
+
+
MySQL
+ Base de datos relacional
+
+
+
+
Bootstrap 5
+ UI components modernos
+
+
+
+
Chart.js
+ Visualización de datos
+
+
+
+
+
+
+
+
+
+
+
+ APIs Disponibles
+
+
+
+
+ Entrada
+
+
+ POST /bot/api/webhook.php
+ Recibe mensajes de WhatsApp
+
+
+
+
+ Salida
+
+
+ POST /bot/api/send_message.php
+ Envía mensajes individuales
+
+
+ POST /bot/api/send_broadcast.php
+ Envía mensajes masivos
+
+
+ GET /bot/api/get_stats.php
+ Obtiene estadísticas
+
+
+
+
+
+
+
+
+
+
+
+
+ Estado del Sistema
+
+
+
+
+ PHP Configurado
+
+
+
+ BD Pendiente
+
+
+
+ WhatsApp Config
+
+
+
+ Webhook URL
+
+
+
+
+
+
+
+
+
+
+
+
+ Desarrollado con amor para gestionar WhatsApp como un profesional
+
+
+ Versión 1.0 | Documentación completa en
+ INSTALACION.md
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/instalacion.html b/instalacion.html
new file mode 100644
index 0000000..1e79be4
--- /dev/null
+++ b/instalacion.html
@@ -0,0 +1,282 @@
+
+
+
+
+
+ 🚀 WhatsApp Bot - Instalación
+
+
+
+
+
+
+
+
+
+
+
+
+
WhatsApp Bot Manager
+
Elige el método de instalación según tu tipo de hosting
+
+
+
+
+
+
+
+
Local
+
+
+
+
🪄 Automática
+
+
+
+ • Laragon/XAMPP
+ • VPS con root
+
+
+
+
+ Usar
+
+
+
+
+
+
+
+
Hosting
+
+
+
+
🛠️ Manual
+
+
+
+ • HestiaCP
+ • Schema archivo
+
+
+
+
+ Usar
+
+
+
+
+
+
+
+
Robusto
+
+
+
+
🔧 Integrada
+
+
+
+ • Schema interno
+ • Muy confiable
+
+
+
+
+ Usar
+
+
+
+
+
+
+
+
SOLUCIÓN
+
+
+
+
⚡ Ultra Básico
+
+
+
+ • Para errores "Table doesn't exist"
+ • Solo tablas esenciales
+ • Paso a paso verificado
+
+
+
+
+ SOLUCIONAR
+
+
+
+
+
+
+
+
Rápido
+
+
+
+
📦 Simple
+
+
+
+ • Solo lo esencial
+ • Proceso rápido
+ • Principiantes
+
+
+
+
+ Usar
+
+
+
+
+
+
+
+
🚨 ¿Tienes errores de "Table doesn't exist"?
+
+
+
Si ves errores como:
+
+ ❌ "Table 'usite_whatsapp_bot.menus' doesn't exist"
+ ❌ "Table 'usite_whatsapp_bot.users' doesn't exist"
+ ❌ "0 tablas, 0 elementos procesados"
+
+
+
+
+ SOLUCIÓN AQUÍ
+
+
Instalador Ultra Básico
+
+
+
+
+
+
¿Cuál elegir según tu situación?
+
+
+
🔥 Si tuviste errores con schema.sql:
+
+ ❌ "Table doesn't exist"
+ ❌ "0 tablas, 0 elementos"
+ ❌ Errores de parsing SQL
+
+
👉 USA: Ultra Básico - Resuelve el problema
+
+
+
💡 Recomendaciones generales:
+
+ Errores de tablas: Ultra Básico
+ Primera vez: Integrada
+ HestiaCP: Ultra Básico o Simple
+ Desarrollo: Automática
+
+
+
+
+
+
+
+
+
+
+
+ WhatsApp Bot Manager v1.0 |
+ Desarrollado por U-Site.app |
+ Soporte técnico
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/install.php b/install.php
new file mode 100644
index 0000000..e480960
--- /dev/null
+++ b/install.php
@@ -0,0 +1,493 @@
+Iniciar sesión');
+}
+
+// Función para generar contraseña aleatoria segura
+function generateRandomPassword($length = 20) {
+ $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*-_=+';
+ $password = '';
+ for ($i = 0; $i < $length; $i++) {
+ $password .= $chars[random_int(0, strlen($chars) - 1)];
+ }
+ return $password;
+}
+
+// Configuración de la instalación (compatible con cPanel)
+$dbHost = 'localhost';
+$dbName = 'whatsapp_bot';
+$dbUser = 'whatsapp_user';
+$dbPass = generateRandomPassword(20);
+$dbRootUser = 'root';
+$dbRootPass = ''; // En cPanel puede requerir contraseña
+
+// Generar contraseña para admin
+$adminPassword = generateRandomPassword(12);
+
+$installStep = $_GET['step'] ?? 'welcome';
+$errors = [];
+$success = [];
+
+// Procesar instalación
+if ($installStep === 'install' && $_POST) {
+ try {
+ // 1. Intentar conectar como root para crear base de datos y usuario
+ try {
+ $rootPdo = new PDO("mysql:host=$dbHost;charset=utf8mb4", $dbRootUser, $dbRootPass);
+ $rootPdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ } catch (PDOException $e) {
+ // Si no se puede conectar como root, es probable que sea hosting compartido
+ throw new Exception("⚠️ HOSTING COMPARTIDO DETECTADO ⚠️ " .
+ "No se pueden crear bases de datos automáticamente en este tipo de hosting. " .
+ "SOLUCIÓN: " .
+ "1. Crea manualmente la base de datos y usuario en tu panel de control " .
+ "2. Usa install_manual.php en su lugar " .
+ "3. Lee la guía: GUIA_HESTIACP.md " .
+ "Error técnico: " . $e->getMessage());
+ }
+
+ // 2. Crear base de datos
+ $rootPdo->exec("CREATE DATABASE IF NOT EXISTS `$dbName` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
+ $success[] = "✅ Base de datos '$dbName' creada correctamente";
+
+ // 3. Crear usuario con permisos
+ $rootPdo->exec("DROP USER IF EXISTS '$dbUser'@'localhost'");
+ $rootPdo->exec("CREATE USER '$dbUser'@'localhost' IDENTIFIED BY '$dbPass'");
+ $rootPdo->exec("GRANT ALL PRIVILEGES ON `$dbName`.* TO '$dbUser'@'localhost'");
+ $rootPdo->exec("FLUSH PRIVILEGES");
+ $success[] = "✅ Usuario '$dbUser' creado con contraseña aleatoria";
+
+ // 4. Conectar con nuevo usuario
+ $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ // 5. Crear tablas desde schema.sql
+ $schema = file_get_contents('database/schema.sql');
+ if (!$schema) {
+ throw new Exception("No se pudo leer el archivo schema.sql");
+ }
+
+ // Ejecutar comandos SQL uno por uno
+ $statements = array_filter(array_map('trim', explode(';', $schema)));
+ $tablesCreated = 0;
+
+ foreach ($statements as $statement) {
+ if (!empty($statement) && !preg_match('/^--/', $statement)) {
+ $pdo->exec($statement);
+ if (preg_match('/CREATE TABLE|INSERT INTO/', $statement)) {
+ $tablesCreated++;
+ }
+ }
+ }
+
+ $success[] = "✅ $tablesCreated elementos de base de datos procesados";
+
+ // 6. Actualizar archivo de configuración
+ $configFile = 'config/config.php';
+ $configContent = file_get_contents($configFile);
+
+ $configContent = preg_replace("/define\('DB_HOST',\s*'[^']*'\);/", "define('DB_HOST', '$dbHost');", $configContent);
+ $configContent = preg_replace("/define\('DB_NAME',\s*'[^']*'\);/", "define('DB_NAME', '$dbName');", $configContent);
+ $configContent = preg_replace("/define\('DB_USER',\s*'[^']*'\);/", "define('DB_USER', '$dbUser');", $configContent);
+ $configContent = preg_replace("/define\('DB_PASS',\s*'[^']*'\);/", "define('DB_PASS', '$dbPass');", $configContent);
+ $configContent = preg_replace("/define\('ADMIN_PASSWORD',\s*'[^']*'\);/", "define('ADMIN_PASSWORD', '" . password_hash($adminPassword, PASSWORD_DEFAULT) . "');", $configContent);
+
+ // Auto-detectar URL del servidor
+ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
+ $host = $_SERVER['HTTP_HOST'];
+ $path = dirname($_SERVER['SCRIPT_NAME']);
+ $autoUrl = $protocol . $host . $path;
+ $configContent = preg_replace("/define\('APP_URL',\s*'[^']*'\);/", "define('APP_URL', '$autoUrl');", $configContent);
+
+ file_put_contents($configFile, $configContent);
+ $success[] = "✅ Archivo config.php actualizado con nuevas credenciales";
+
+ // 7. Crear archivo de bloqueo de instalación
+ $lockContent = "Instalación completada: " . date('Y-m-d H:i:s') . "\n";
+ $lockContent .= "Desarrollado por: U-Site.app\n";
+ $lockContent .= "URL: https://u-site.app\n";
+ file_put_contents($installationLockFile, $lockContent);
+ $success[] = "✅ Sistema bloqueado contra reinstalaciones";
+
+ // 8. Crear archivo de credenciales para referencia
+ $credentialsFile = "CREDENCIALES_SISTEMA.txt";
+ $credentialsContent = "=== CREDENCIALES DEL SISTEMA WHATSAPP BOT ===\n";
+ $credentialsContent .= "Desarrollado por: U-Site.app (https://u-site.app)\n";
+ $credentialsContent .= "Generado automáticamente: " . date('Y-m-d H:i:s') . "\n\n";
+ $credentialsContent .= "=== BASE DE DATOS ===\n";
+ $credentialsContent .= "Host: $dbHost\n";
+ $credentialsContent .= "Base de datos: $dbName\n";
+ $credentialsContent .= "Usuario: $dbUser\n";
+ $credentialsContent .= "Contraseña: $dbPass\n\n";
+ $credentialsContent .= "=== PANEL ADMINISTRATIVO ===\n";
+ $credentialsContent .= "Usuario: admin\n";
+ $credentialsContent .= "Contraseña: $adminPassword\n";
+ $credentialsContent .= "URL Login: $autoUrl/login.php\n\n";
+ $credentialsContent .= "⚠️ IMPORTANTE: Guarda estas credenciales en un lugar seguro.\n";
+ $credentialsContent .= "⚠️ Elimina este archivo después de guardar las credenciales.\n";
+ $credentialsContent .= "⚠️ El sistema está protegido contra reinstalaciones.\n\n";
+ $credentialsContent .= "📞 Soporte técnico: support@u-site.app\n";
+
+ file_put_contents($credentialsFile, $credentialsContent);
+ $success[] = "✅ Archivo de credenciales creado: $credentialsFile";
+
+ $installStep = 'completed';
+
+ } catch (PDOException $e) {
+ $errors[] = "❌ Error de base de datos: " . $e->getMessage();
+ } catch (Exception $e) {
+ $errors[] = "❌ Error general: " . $e->getMessage();
+ }
+}
+?>
+
+
+
+
+
+
+ 🚀 Instalador WhatsApp Bot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Instalador WhatsApp Bot
+
Configuración automática con credenciales aleatorias
+
+
+
+
+
+
+
1
+
+
Crear BD
+ Base de datos: whatsapp_bot
+
+
+
+
+
+
2
+
+
Crear Usuario
+ Usuario: whatsapp_user
+
+
+
+
+
+
3
+
+
Configurar
+ Aplicar configuración
+
+
+
+
+
+
+
+
Configuración automática para cPanel/hosting compartido:
+
+ Host: localhost (compatible con cualquier hosting)
+ Base de datos: whatsapp_bot (se creará automáticamente)
+ Usuario DB: whatsapp_user (se creará automáticamente)
+ Contraseña DB: Se generará aleatoriamente (20 caracteres)
+ Usuario Admin: admin
+ Contraseña Admin: Se generará aleatoriamente (12 caracteres)
+
+
+
+
+
+
Características de seguridad:
+
+ ✅ Protección contra reinstalaciones
+ ✅ Sistema de login con bloqueo por intentos
+ ✅ Contraseñas encriptadas
+ ✅ Auto-detección de URL del servidor
+ ✅ Compatible con cualquier hosting cPanel
+
+
+
+
+
+ Requisitos: Asegúrate de que MySQL esté funcionando en Laragon
+
+
+
+
+
⚠️ ¿Usas hosting compartido? (HestiaCP, cPanel, etc.)
+ Este instalador automático
NO funcionará en hosting compartido porque requiere privilegios de administrador.
+
👉 SOLUCIÓN:
+
+ Usar Instalador Manual
+
+
+ Ver Guía
+
+
+
+
+
+
+
+ 🔐 CONTRASEÑA BD:
+ = htmlspecialchars($dbPass) ?>
+
+
+
+
+ 👤 CONTRASEÑA ADMIN:
+ = htmlspecialchars($adminPassword) ?>
+
+
+
+
+
+
+
+
+
+
+
+
Instalando Sistema...
+
Configurando base de datos automáticamente
+
+
+
+
+
WhatsApp Bot Installer v1.0
+
Connecting to MySQL server (localhost)...
+
Generating secure random password...
+
+ 🔐 CONTRASEÑAS GENERADAS:
+ DB: = htmlspecialchars($dbPass) ?>
+ Admin: = htmlspecialchars($adminPassword) ?>
+
+
Creating database 'whatsapp_bot'...
+
Creating user 'whatsapp_user'@'localhost'...
+
Granting privileges...
+
Installing database schema...
+
Updating configuration files...
+
Ready to execute installation _
+
+
+
+
+ Ejecutar Instalación
+
+
+
+
+
+
+
+
+
+
+
¡Instalación Completada!
+
Tu sistema WhatsApp Bot está listo para usar
+
+
+
+
+
+
Errores encontrados:
+
+
= htmlspecialchars($error) ?>
+
+
+
+
+
+
+
+
Instalación exitosa:
+
+
= htmlspecialchars($msg) ?>
+
+
+
+
+
+
+
+
Credenciales de BD
+
+
Host: = $dbHost ?>
+
Base de datos: = $dbName ?>
+
Usuario: = $dbUser ?>
+
+
Contraseña:
+
+ = htmlspecialchars($dbPass) ?>
+
+
+
+
+
Credenciales Admin
+
+
Usuario: admin
+
+
Contraseña:
+
+ = htmlspecialchars($adminPassword) ?>
+
+
+
+
+
+
+
+
+
+
+
¡IMPORTANTE!
+
+ Guarda las credenciales en un lugar seguro
+ El archivo CREDENCIALES_SISTEMA.txt contiene toda la información
+ Elimina este archivo después de guardar las credenciales
+ El sistema está protegido contra reinstalaciones
+ Configura tu token de WhatsApp después del login
+ Soporte técnico: support@u-site.app
+
+
+
+
+
+
+
+
+ WhatsApp Bot Manager v1.0 by U-Site.app |
+ Instalación automática con credenciales seguras |
+ Soporte
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/install_integrated.php b/install_integrated.php
new file mode 100644
index 0000000..46cdf89
--- /dev/null
+++ b/install_integrated.php
@@ -0,0 +1,438 @@
+ PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
+ ]);
+ $success[] = "✅ Conexión exitosa a '{$dbName}'";
+
+ // Ejecutar schema - PASO 1: Crear solo las tablas
+ $tablesCreated = 0;
+ $creationErrors = [];
+
+ echo "";
+
+ foreach ($integratedSchema as $index => $sql) {
+ try {
+ echo "";
+
+ $result = $pdo->exec($sql);
+ $tablesCreated++;
+
+ // Extraer nombre de tabla para log
+ if (preg_match('/CREATE TABLE IF NOT EXISTS (\w+)/', $sql, $matches)) {
+ $tableName = $matches[1];
+ $success[] = "✅ Tabla '{$tableName}' creada/verificada";
+
+ // Verificar que la tabla realmente existe
+ try {
+ $checkQuery = "DESCRIBE `{$tableName}`";
+ $pdo->query($checkQuery);
+ } catch (PDOException $e) {
+ $creationErrors[] = "❌ Error verificando tabla '{$tableName}': " . $e->getMessage();
+ }
+ }
+
+ // Pequeña pausa para evitar problemas de timing
+ usleep(100000); // 0.1 segundos
+
+ } catch (PDOException $e) {
+ $errorMsg = $e->getMessage();
+ if (strpos($errorMsg, 'already exists') === false) {
+ $creationErrors[] = "❌ Error en tabla " . ($index + 1) . ": " . $errorMsg;
+ } else {
+ $tablesCreated++; // Contar como creada si ya existe
+ }
+ }
+ }
+
+ // Mostrar errores de creación si los hay
+ if (!empty($creationErrors)) {
+ foreach ($creationErrors as $error) {
+ $errors[] = $error;
+ }
+ }
+
+ // Solo continuar si se crearon las tablas principales
+ $essentialTables = ['users', 'conversations', 'menus', 'system_config'];
+ $missingTables = [];
+
+ foreach ($essentialTables as $table) {
+ try {
+ $pdo->query("DESCRIBE `{$table}`");
+ } catch (PDOException $e) {
+ $missingTables[] = $table;
+ }
+ }
+
+ if (!empty($missingTables)) {
+ $errors[] = "❌ Tablas esenciales no creadas: " . implode(', ', $missingTables);
+ $errors[] = "ℹ️ Intenta ejecutar el SQL manualmente en phpMyAdmin primero";
+ } else {
+ $success[] = "✅ Todas las tablas esenciales verificadas";
+
+ // PASO 2: Insertar datos solo si las tablas existen
+ $dataInserted = 0;
+ foreach ($initialData as $sql) {
+ try {
+ $pdo->exec($sql);
+ $dataInserted++;
+ } catch (PDOException $e) {
+ $errorMsg = $e->getMessage();
+ if (strpos($errorMsg, 'Duplicate entry') === false) {
+ $errors[] = "⚠️ Error insertando datos: " . $errorMsg;
+ }
+ }
+ }
+
+ $success[] = "✅ {$tablesCreated} tablas procesadas, {$dataInserted} conjuntos de datos insertados";
+ }
+
+ // Actualizar config.php
+ $configFile = 'config/config.php';
+ if (file_exists($configFile)) {
+ $config = file_get_contents($configFile);
+
+ $config = preg_replace("/define\('DB_HOST',\s*'[^']*'\);/", "define('DB_HOST', '$dbHost');", $config);
+ $config = preg_replace("/define\('DB_PORT',\s*'[^']*'\);/", "define('DB_PORT', '$dbPort');", $config);
+ $config = preg_replace("/define\('DB_NAME',\s*'[^']*'\);/", "define('DB_NAME', '$dbName');", $config);
+ $config = preg_replace("/define\('DB_USER',\s*'[^']*'\);/", "define('DB_USER', '$dbUser');", $config);
+ $config = preg_replace("/define\('DB_PASS',\s*'[^']*'\);/", "define('DB_PASS', '$dbPass');", $config);
+ $config = preg_replace("/define\('ADMIN_PASSWORD',\s*'[^']*'\);/", "define('ADMIN_PASSWORD', '" . password_hash($adminPassword, PASSWORD_DEFAULT) . "');", $config);
+
+ // Auto-detectar URL
+ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
+ $host = $_SERVER['HTTP_HOST'];
+ $path = dirname($_SERVER['SCRIPT_NAME']);
+ $autoUrl = $protocol . $host . $path;
+ $config = preg_replace("/define\('APP_URL',\s*'[^']*'\);/", "define('APP_URL', '$autoUrl');", $config);
+
+ if (file_put_contents($configFile, $config)) {
+ $success[] = "✅ Configuración actualizada";
+ } else {
+ $errors[] = "❌ No se pudo actualizar config.php";
+ }
+ }
+
+ // Verificar instalación
+ if (empty($errors)) {
+ try {
+ $userCount = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
+ $configCount = $pdo->query("SELECT COUNT(*) FROM system_config")->fetchColumn();
+ $success[] = "✅ Verificación: {$userCount} usuarios, {$configCount} configuraciones";
+
+ // Marcar como instalado
+ file_put_contents('.installation_completed', date('Y-m-d H:i:s'));
+
+ // Credenciales
+ $credentials = "USUARIO: admin\nCONTRASEÑA: {$adminPassword}\nFECHA: " . date('Y-m-d H:i:s');
+ file_put_contents('CREDENCIALES.txt', $credentials);
+
+ $success[] = "✅ Instalación completada exitosamente";
+ $step = 'completed';
+
+ } catch (Exception $e) {
+ $errors[] = "❌ Error en verificación: " . $e->getMessage();
+ }
+ }
+
+ } catch (PDOException $e) {
+ $errors[] = "❌ Error de conexión: " . $e->getMessage();
+ }
+ }
+}
+?>
+
+
+
+
+
+ 🔧 Instalador Integrado
+
+
+
+
+
+
+
+
+
+
+
+
+
Instalador con Schema Integrado
+
Evita problemas de parsing del archivo schema.sql
+
+
+
+
+
Ventajas de este instalador:
+
+ ✅ Schema SQL integrado en el código
+ ✅ No depende de archivos externos
+ ✅ Ejecución paso a paso controlada
+ ✅ Manejo de errores mejorado
+
+
+
+
+
+
+
+
+ Credenciales de Admin:
+ Usuario: admin
+ Contraseña: = $adminPassword ?>
+
+
+ Instalar con Schema Integrado
+
+
+
+
+
+
+
Instalando...
+
+
+
+
+
Errores:
+
+
= htmlspecialchars($error) ?>
+
+
+
Reintentar
+
+
+
+
+
+
Progreso:
+
+
= htmlspecialchars($msg) ?>
+
+
+
+
+
+
+
¡Instalación Exitosa!
+
Sistema WhatsApp Bot instalado correctamente
+
+
+
+
+
Credenciales
+
+ Usuario: admin
+ Contraseña: = $adminPassword ?>
+ Guardadas en CREDENCIALES.txt
+
+
+
+
+
+
+
+ Próximos pasos: Configura tu token de WhatsApp en el panel de administración.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/install_manual.php b/install_manual.php
new file mode 100644
index 0000000..410fbc2
--- /dev/null
+++ b/install_manual.php
@@ -0,0 +1,544 @@
+Iniciar sesión');
+}
+
+// Función para generar contraseña aleatoria segura
+function generateRandomPassword($length = 12) {
+ $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*-_=+';
+ $password = '';
+ for ($i = 0; $i < $length; $i++) {
+ $password .= $chars[random_int(0, strlen($chars) - 1)];
+ }
+ return $password;
+}
+
+$installStep = $_GET['step'] ?? 'config';
+$errors = [];
+$success = [];
+
+// Generar contraseña para admin
+$adminPassword = generateRandomPassword(12);
+
+// Procesar instalación manual
+if ($installStep === 'install' && $_POST) {
+ $dbHost = trim($_POST['db_host'] ?? 'localhost');
+ $dbName = trim($_POST['db_name'] ?? '');
+ $dbUser = trim($_POST['db_user'] ?? '');
+ $dbPass = trim($_POST['db_pass'] ?? '');
+ $dbPort = trim($_POST['db_port'] ?? '3306');
+
+ // Validar campos requeridos
+ if (empty($dbName)) {
+ $errors[] = "❌ El nombre de la base de datos es obligatorio";
+ }
+ if (empty($dbUser)) {
+ $errors[] = "❌ El usuario de la base de datos es obligatorio";
+ }
+ if (empty($dbPass)) {
+ $errors[] = "❌ La contraseña de la base de datos es obligatoria";
+ }
+
+ if (empty($errors)) {
+ try {
+ // 1. Probar conexión con las credenciales proporcionadas
+ $dsn = "mysql:host={$dbHost};port={$dbPort};charset=utf8mb4";
+ $testConnection = new PDO($dsn, $dbUser, $dbPass, [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_TIMEOUT => 5,
+ ]);
+ $success[] = "✅ Conexión al servidor MySQL exitosa";
+
+ // Verificar que la base de datos existe
+ $dsn = "mysql:host={$dbHost};port={$dbPort};dbname={$dbName};charset=utf8mb4";
+ $pdo = new PDO($dsn, $dbUser, $dbPass, [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+ ]);
+
+ $success[] = "✅ Conexión a base de datos '$dbName' exitosa";
+
+ // 2. Verificar si las tablas ya existen
+ $existingTables = [];
+ $stmt = $pdo->query("SHOW TABLES");
+ while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
+ $existingTables[] = $row[0];
+ }
+
+ if (!empty($existingTables)) {
+ $success[] = "✅ Base de datos contiene " . count($existingTables) . " tablas existentes";
+ }
+
+ // 3. Ejecutar schema.sql para crear/actualizar tablas
+ $schemaFile = 'database/schema.sql';
+ if (!file_exists($schemaFile)) {
+ throw new Exception("No se encontró el archivo schema.sql");
+ }
+
+ $schema = file_get_contents($schemaFile);
+ if (!$schema) {
+ throw new Exception("No se pudo leer el archivo schema.sql");
+ }
+
+ // Ejecutar comandos SQL uno por uno
+ $statements = preg_split('/;\s*$/m', $schema);
+ $tablesCreated = 0;
+ $tablesUpdated = 0;
+ $errors_sql = [];
+
+ // Primero ejecutar solo CREATE TABLE
+ foreach ($statements as $statement) {
+ $statement = trim($statement);
+ if (!empty($statement) &&
+ !preg_match('/^--/', $statement) &&
+ preg_match('/CREATE TABLE/i', $statement)) {
+ try {
+ $pdo->exec($statement);
+ $tablesCreated++;
+
+ // Extraer nombre de tabla para log
+ if (preg_match('/CREATE TABLE\s+(?:IF NOT EXISTS\s+)?`?(\w+)`?/i', $statement, $matches)) {
+ $success[] = "✅ Tabla '{$matches[1]}' creada";
+ }
+ } catch (PDOException $e) {
+ $errorMsg = $e->getMessage();
+ if (strpos($errorMsg, 'already exists') === false) {
+ $errors_sql[] = "Error creando tabla: " . $errorMsg;
+ }
+ }
+ }
+ }
+
+ // Luego ejecutar INSERT y otras operaciones
+ foreach ($statements as $statement) {
+ $statement = trim($statement);
+ if (!empty($statement) &&
+ !preg_match('/^--/', $statement) &&
+ !preg_match('/CREATE TABLE/i', $statement) &&
+ (preg_match('/INSERT/i', $statement) || preg_match('/UPDATE/i', $statement) || preg_match('/SET/i', $statement))) {
+ try {
+ $pdo->exec($statement);
+ if (preg_match('/INSERT INTO/i', $statement)) {
+ $tablesUpdated++;
+ }
+ } catch (PDOException $e) {
+ $errorMsg = $e->getMessage();
+ if (strpos($errorMsg, 'Duplicate entry') === false) {
+ $errors_sql[] = "Error insertando datos: " . $errorMsg;
+ }
+ }
+ }
+ }
+
+ if (empty($errors_sql)) {
+ $success[] = "✅ Esquema aplicado: {$tablesCreated} tablas creadas, {$tablesUpdated} registros insertados";
+ } else {
+ $success[] = "⚠️ Esquema procesado: {$tablesCreated} tablas, {$tablesUpdated} registros";
+ foreach ($errors_sql as $sqlError) {
+ $errors[] = "⚠️ " . $sqlError;
+ }
+ }
+
+ // 4. Actualizar archivo de configuración
+ $configFile = 'config/config.php';
+ $configContent = file_get_contents($configFile);
+
+ $configContent = preg_replace("/define\('DB_HOST',\s*'[^']*'\);/", "define('DB_HOST', '$dbHost');", $configContent);
+ $configContent = preg_replace("/define\('DB_PORT',\s*'[^']*'\);/", "define('DB_PORT', '$dbPort');", $configContent);
+ $configContent = preg_replace("/define\('DB_NAME',\s*'[^']*'\);/", "define('DB_NAME', '$dbName');", $configContent);
+ $configContent = preg_replace("/define\('DB_USER',\s*'[^']*'\);/", "define('DB_USER', '$dbUser');", $configContent);
+ $configContent = preg_replace("/define\('DB_PASS',\s*'[^']*'\);/", "define('DB_PASS', '$dbPass');", $configContent);
+ $configContent = preg_replace("/define\('ADMIN_PASSWORD',\s*'[^']*'\);/", "define('ADMIN_PASSWORD', '" . password_hash($adminPassword, PASSWORD_DEFAULT) . "');", $configContent);
+
+ // Auto-detectar URL del servidor
+ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
+ $host = $_SERVER['HTTP_HOST'];
+ $path = dirname($_SERVER['SCRIPT_NAME']);
+ $autoUrl = $protocol . $host . $path;
+ $configContent = preg_replace("/define\('APP_URL',\s*'[^']*'\);/", "define('APP_URL', '$autoUrl');", $configContent);
+
+ if (file_put_contents($configFile, $configContent)) {
+ $success[] = "✅ Archivo config.php actualizado";
+ } else {
+ $errors[] = "❌ No se pudo actualizar config.php";
+ }
+
+ // 5. Verificar que la configuración funcione
+ try {
+ // Probar directamente con PDO usando las nuevas credenciales
+ $testPdo = new PDO("mysql:host={$dbHost};port={$dbPort};dbname={$dbName};charset=utf8mb4", $dbUser, $dbPass, [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ ]);
+
+ $testQuery = $testPdo->query("SELECT COUNT(*) as count FROM users");
+ $userCount = $testQuery->fetch()['count'];
+ $success[] = "✅ Prueba de conexión exitosa: {$userCount} usuarios registrados";
+
+ // Verificar que las tablas principales existan
+ $mainTables = ['users', 'conversations', 'menus', 'system_config'];
+ foreach ($mainTables as $table) {
+ $checkTable = $testPdo->query("SHOW TABLES LIKE '{$table}'");
+ if ($checkTable->rowCount() > 0) {
+ $success[] = "✅ Tabla '{$table}' verificada";
+ } else {
+ $errors[] = "❌ Tabla '{$table}' no encontrada";
+ }
+ }
+
+ } catch (Exception $e) {
+ $errors[] = "❌ Error probando la nueva configuración: " . $e->getMessage();
+ }
+
+ if (empty($errors)) {
+ // 6. Crear archivo de bloqueo de instalación
+ $lockContent = "Instalación manual completada: " . date('Y-m-d H:i:s') . "\n";
+ $lockContent .= "Host: $dbHost\n";
+ $lockContent .= "Base de datos: $dbName\n";
+ $lockContent .= "Usuario BD: $dbUser\n";
+ $lockContent .= "Desarrollado por: U-Site.app\n";
+ $lockContent .= "URL: https://u-site.app\n";
+ file_put_contents($installationLockFile, $lockContent);
+ $success[] = "✅ Sistema bloqueado contra reinstalaciones";
+
+ // 7. Crear archivo de credenciales
+ $credentialsFile = "CREDENCIALES_SISTEMA.txt";
+ $credentialsContent = "=== CREDENCIALES DEL SISTEMA WHATSAPP BOT ===\n";
+ $credentialsContent .= "Instalación manual completada\n";
+ $credentialsContent .= "Desarrollado por: U-Site.app (https://u-site.app)\n";
+ $credentialsContent .= "Generado automáticamente: " . date('Y-m-d H:i:s') . "\n\n";
+ $credentialsContent .= "=== BASE DE DATOS ===\n";
+ $credentialsContent .= "Host: $dbHost\n";
+ $credentialsContent .= "Puerto: $dbPort\n";
+ $credentialsContent .= "Base de datos: $dbName\n";
+ $credentialsContent .= "Usuario: $dbUser\n";
+ $credentialsContent .= "Contraseña: [LA QUE CONFIGURASTE MANUALMENTE]\n\n";
+ $credentialsContent .= "=== PANEL ADMINISTRATIVO ===\n";
+ $credentialsContent .= "Usuario: admin\n";
+ $credentialsContent .= "Contraseña: $adminPassword\n";
+ $credentialsContent .= "URL Login: $autoUrl/login.php\n\n";
+ $credentialsContent .= "⚠️ IMPORTANTE: Guarda estas credenciales en un lugar seguro.\n";
+ $credentialsContent .= "⚠️ La contraseña de BD es la que TÚ configuraste manualmente.\n";
+ $credentialsContent .= "⚠️ El sistema está protegido contra reinstalaciones.\n\n";
+ $credentialsContent .= "📞 Soporte técnico: support@u-site.app\n";
+
+ file_put_contents($credentialsFile, $credentialsContent);
+ $success[] = "✅ Archivo de credenciales creado: $credentialsFile";
+
+ $installStep = 'completed';
+ }
+
+ } catch (PDOException $e) {
+ $errors[] = "❌ Error de base de datos: " . $e->getMessage();
+ } catch (Exception $e) {
+ $errors[] = "❌ Error general: " . $e->getMessage();
+ }
+ }
+}
+?>
+
+
+
+
+
+
+ 🛠️ Instalación Manual - WhatsApp Bot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Instalación Manual
+
Para hostings compartidos (HestiaCP, cPanel, etc.)
+
+
+
+
Configuración para Hosting Compartido
+
Este instalador es para cuando ya creaste manualmente:
+
+
+
+ ✅ Base de datos MySQL
+ ✅ Usuario de base de datos
+
+
+
+
+ ✅ Permisos asignados
+ ✅ Acceso desde tu dominio
+
+
+
+
+
+
+
Pasos previos en HestiaCP/cPanel:
+
+ Crear base de datos: Ve a MySQL Databases
+ Crear usuario: Asigna un usuario a la BD
+ Asignar permisos: Da ALL PRIVILEGES al usuario
+ Anotar credenciales: Host, nombre BD, usuario y contraseña
+
+
+
+
+
+
+
+
+
+
Credenciales Admin
+
+ Usuario: admin
+ Contraseña: = htmlspecialchars($adminPassword) ?>
+
+
+
+
Que incluye
+
+ ✅ Configuración automática
+ ✅ Creación de tablas
+ ✅ Datos iniciales
+ ✅ Protección contra reinstalación
+
+
+
+
+
+
+
+ Configurar Sistema
+
+
+
+
+
+
+
+
+
Configurando Sistema...
+
Aplicando configuración con tus credenciales
+
+
+
+
+
+
+
+
+
+
+
Progreso de instalación:
+
+
= htmlspecialchars($msg) ?>
+
+
+
+
+
+
+
+
+
+ Procesando...
+
+
Procesando configuración...
+
+
+
+
+
+
+
+
¡Instalación Completada!
+
Tu sistema WhatsApp Bot está configurado y listo
+
+
+
+
+
+
Advertencias:
+
+
= htmlspecialchars($error) ?>
+
+
+
+
+
+
+
+
Instalación exitosa:
+
+
= htmlspecialchars($msg) ?>
+
+
+
+
+
+
+
Acceso al Sistema
+
+
Usuario: admin
+
+
Contraseña:
+
+ = htmlspecialchars($adminPassword) ?>
+
+
+
+
+
+
+
+
+
+
Información Importante:
+
+ Credenciales guardadas: Revisa el archivo CREDENCIALES_SISTEMA.txt
+ Base de datos: Se conecta usando tus credenciales existentes
+ Sistema protegido: No se puede reinstalar accidentalmente
+ Soporte: support@u-site.app
+
+
+
+
+
+
+
+
+ Instalación Manual WhatsApp Bot |
+ Desarrollado por U-Site.app |
+ Soporte
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/install_simple.php b/install_simple.php
new file mode 100644
index 0000000..15266f7
--- /dev/null
+++ b/install_simple.php
@@ -0,0 +1,239 @@
+ PDO::ERRMODE_EXCEPTION
+ ]);
+ $success[] = "✅ Conexión exitosa";
+
+ // 2. Aplicar schema básico
+ $basicSchema = "
+ CREATE TABLE IF NOT EXISTS users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ phone_number VARCHAR(20) UNIQUE NOT NULL,
+ name VARCHAR(100),
+ status ENUM('active', 'inactive') DEFAULT 'active',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ );
+
+ CREATE TABLE IF NOT EXISTS conversations (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT,
+ direction ENUM('incoming', 'outgoing') NOT NULL,
+ content TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id)
+ );
+
+ CREATE TABLE IF NOT EXISTS system_config (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ config_key VARCHAR(100) UNIQUE NOT NULL,
+ config_value TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ );
+
+ INSERT IGNORE INTO system_config (config_key, config_value) VALUES
+ ('app_installed', '1'),
+ ('install_date', NOW()),
+ ('admin_user', 'admin');
+ ";
+
+ $statements = array_filter(explode(';', $basicSchema));
+ foreach ($statements as $stmt) {
+ if (trim($stmt)) {
+ $pdo->exec(trim($stmt));
+ }
+ }
+ $success[] = "✅ Tablas creadas";
+
+ // 3. Actualizar config.php
+ $configFile = 'config/config.php';
+ if (file_exists($configFile)) {
+ $config = file_get_contents($configFile);
+
+ $config = preg_replace("/define\('DB_HOST',\s*'[^']*'\);/", "define('DB_HOST', '$dbHost');", $config);
+ $config = preg_replace("/define\('DB_PORT',\s*'[^']*'\);/", "define('DB_PORT', '$dbPort');", $config);
+ $config = preg_replace("/define\('DB_NAME',\s*'[^']*'\);/", "define('DB_NAME', '$dbName');", $config);
+ $config = preg_replace("/define\('DB_USER',\s*'[^']*'\);/", "define('DB_USER', '$dbUser');", $config);
+ $config = preg_replace("/define\('DB_PASS',\s*'[^']*'\);/", "define('DB_PASS', '$dbPass');", $config);
+ $config = preg_replace("/define\('ADMIN_PASSWORD',\s*'[^']*'\);/", "define('ADMIN_PASSWORD', '" . password_hash($adminPassword, PASSWORD_DEFAULT) . "');", $config);
+
+ file_put_contents($configFile, $config);
+ $success[] = "✅ Configuración actualizada";
+ }
+
+ // 4. Crear archivo de instalación completada
+ file_put_contents('.installation_completed', date('Y-m-d H:i:s'));
+ $success[] = "✅ Instalación completada";
+
+ // 5. Crear credenciales
+ $creds = "USUARIO ADMIN: admin\nCONTRASEÑA: $adminPassword\nFECHA: " . date('Y-m-d H:i:s');
+ file_put_contents('CREDENCIALES.txt', $creds);
+ $success[] = "✅ Credenciales guardadas en CREDENCIALES.txt";
+
+ $step = 'completed';
+
+ } catch (Exception $e) {
+ $errors[] = "❌ Error: " . $e->getMessage();
+ }
+ }
+}
+?>
+
+
+
+
+
+ 📦 Instalador Simplificado
+
+
+
+
+
+
+
+
+
+
+
+
📦 Instalador Simplificado
+
Para hosting compartido (HestiaCP/cPanel)
+
+
+
+ Antes de continuar: Asegúrate de haber creado la base de datos y usuario en tu panel de control.
+
+
+
+
+
+
+
+ Contraseña de administrador:
+ = $adminPassword ?>
+
+
+ Instalar Sistema
+
+
+
+
+
+
+
⚙️ Instalando...
+
+
+
+
+
❌ Errores:
+
+
= htmlspecialchars($error) ?>
+
+
+
Reintentar
+
+
+
+
+
+
✅ Progreso:
+
+
= htmlspecialchars($msg) ?>
+
+
+
+
+
+
+
🎉 ¡Instalación Exitosa!
+
+
+
+
+
🔐 Credenciales de Acceso
+
+ Usuario: admin
+ Contraseña: = $adminPassword ?>
+
+
+
+
+
+
+ Importante: Guarda las credenciales. El archivo CREDENCIALES.txt contiene toda la información.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/install_ultra_basic.php b/install_ultra_basic.php
new file mode 100644
index 0000000..0556358
--- /dev/null
+++ b/install_ultra_basic.php
@@ -0,0 +1,282 @@
+ PDO::ERRMODE_EXCEPTION
+ ]);
+ $success[] = "✅ Conectado a '{$dbName}'";
+
+ // Crear tabla users primero
+ $sql = "CREATE TABLE IF NOT EXISTS users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ phone_number VARCHAR(20) UNIQUE NOT NULL,
+ name VARCHAR(100),
+ status ENUM('active', 'inactive') DEFAULT 'active',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )";
+
+ $pdo->exec($sql);
+
+ // Verificar que se creó
+ try {
+ $result = $pdo->query("SELECT COUNT(*) FROM users");
+ $success[] = "✅ Tabla 'users' creada y funcional";
+ } catch (Exception $e) {
+ $errors[] = "❌ Tabla users no funciona: " . $e->getMessage();
+ }
+
+ // Crear tabla system_config
+ $sql = "CREATE TABLE IF NOT EXISTS system_config (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ config_key VARCHAR(100) UNIQUE NOT NULL,
+ config_value TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )";
+
+ $pdo->exec($sql);
+
+ // Verificar
+ try {
+ $result = $pdo->query("SELECT COUNT(*) FROM system_config");
+ $success[] = "✅ Tabla 'system_config' creada y funcional";
+ } catch (Exception $e) {
+ $errors[] = "❌ Tabla system_config no funciona: " . $e->getMessage();
+ }
+
+ // Solo insertar datos si no hay errores
+ if (empty($errors)) {
+ // Insertar configuración básica
+ $configSql = "INSERT IGNORE INTO system_config (config_key, config_value) VALUES
+ ('app_installed', '1'),
+ ('install_date', NOW()),
+ ('business_name', 'Mi Empresa'),
+ ('admin_user', 'admin')";
+
+ try {
+ $pdo->exec($configSql);
+ $success[] = "✅ Configuración inicial insertada";
+ } catch (Exception $e) {
+ $errors[] = "⚠️ Error insertando config: " . $e->getMessage();
+ }
+
+ // Actualizar config.php
+ if (file_exists('config/config.php')) {
+ $config = file_get_contents('config/config.php');
+
+ $config = preg_replace("/define\('DB_HOST',\s*'[^']*'\);/", "define('DB_HOST', '$dbHost');", $config);
+ $config = preg_replace("/define\('DB_PORT',\s*'[^']*'\);/", "define('DB_PORT', '$dbPort');", $config);
+ $config = preg_replace("/define\('DB_NAME',\s*'[^']*'\);/", "define('DB_NAME', '$dbName');", $config);
+ $config = preg_replace("/define\('DB_USER',\s*'[^']*'\);/", "define('DB_USER', '$dbUser');", $config);
+ $config = preg_replace("/define\('DB_PASS',\s*'[^']*'\);/", "define('DB_PASS', '$dbPass');", $config);
+ $config = preg_replace("/define\('ADMIN_PASSWORD',\s*'[^']*'\);/", "define('ADMIN_PASSWORD', '" . password_hash($adminPassword, PASSWORD_DEFAULT) . "');", $config);
+
+ file_put_contents('config/config.php', $config);
+ $success[] = "✅ config.php actualizado";
+ }
+
+ // Verificar que todo funciona
+ try {
+ $userCount = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
+ $configCount = $pdo->query("SELECT COUNT(*) FROM system_config")->fetchColumn();
+ $success[] = "✅ Verificación final: {$userCount} usuarios, {$configCount} configs";
+
+ // Marcar como instalado
+ file_put_contents('.installation_completed', date('Y-m-d H:i:s'));
+ file_put_contents('CREDENCIALES_BASICO.txt', "USUARIO: admin\nCONTRASEÑA: {$adminPassword}\nFECHA: " . date('Y-m-d H:i:s'));
+
+ $success[] = "🎉 Instalación básica completada";
+ $step = 'completed';
+
+ } catch (Exception $e) {
+ $errors[] = "❌ Error en verificación final: " . $e->getMessage();
+ }
+ }
+
+ } catch (PDOException $e) {
+ $errors[] = "❌ Error de conexión: " . $e->getMessage();
+ }
+ }
+}
+?>
+
+
+
+
+
+ ⚡ Instalador Ultra Básico
+
+
+
+
+
+
+
+
+
+
+
+
⚡ Instalador Ultra Básico
+
Solo crea las tablas esenciales paso a paso
+
+
+
+
Este instalador:
+
+ ✅ Crea solo 2 tablas esenciales: users y system_config
+ ✅ Verifica cada paso antes de continuar
+ ✅ Proceso ultra simplificado
+ ✅ Ideal para resolver problemas de creación de tablas
+
+
+
+
+
+
+
+
+ Contraseña de admin que se generará:
+ = $adminPassword ?>
+
+
+ ⚡ Instalación Ultra Básica
+
+
+
+
+
+
+
⚙️ Instalando paso a paso...
+
+
+
+
+
❌ Errores:
+
+
= htmlspecialchars($error) ?>
+
+
+
Reintentar
+
+
+
+
+
+
✅ Progreso:
+
+
= htmlspecialchars($msg) ?>
+
+
+
+
+
+
+
💡 Sugerencias para resolver errores:
+
+ Verifica que la base de datos existe
+ Confirma que el usuario tiene permisos ALL en la BD
+ Prueba conectarte manualmente con phpMyAdmin
+ Si persiste, contacta soporte con estos mensajes
+
+
+
+
+
+
+
🎉 ¡Instalación Básica Exitosa!
+
Sistema mínimo funcional instalado
+
+
+
+
+
🔑 Credenciales
+
+ Usuario: admin
+ Contraseña: = $adminPassword ?>
+ Guardadas en: CREDENCIALES_BASICO.txt
+
+
+
+
+
+
+
📋 Lo que se instaló:
+
+ ✅ Tabla users - Para gestión de usuarios
+ ✅ Tabla system_config - Para configuración
+ ✅ Configuración básica del sistema
+ ✅ Credenciales de administrador
+
+
+
Nota: Puedes agregar más funcionalidades desde el panel de administración.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/login.php b/login.php
new file mode 100644
index 0000000..879ce6e
--- /dev/null
+++ b/login.php
@@ -0,0 +1,331 @@
+Instalar ahora');
+}
+
+// Si ya está logueado, redirigir al panel
+if (isUserLoggedIn()) {
+ header('Location: index.php');
+ exit;
+}
+
+$error = '';
+$loginBlocked = false;
+$clientIp = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
+
+// Verificar si la IP está bloqueada
+if (!checkLoginAttempts($clientIp)) {
+ $loginBlocked = true;
+ $error = 'Demasiados intentos de login. Inténtalo en ' . (LOGIN_LOCKOUT_TIME / 60) . ' minutos.';
+}
+
+// Procesar login
+if ($_POST && !$loginBlocked) {
+ $username = trim($_POST['username'] ?? '');
+ $password = $_POST['password'] ?? '';
+
+ if ($username === ADMIN_USERNAME) {
+ // Verificar contraseña
+ $adminHash = constant('ADMIN_PASSWORD');
+ if (password_verify($password, $adminHash)) {
+ // Login exitoso
+ clearLoginAttempts($clientIp);
+ $_SESSION['admin_logged_in'] = true;
+ $_SESSION['last_activity'] = time();
+ $_SESSION['login_ip'] = $clientIp;
+ $_SESSION['login_time'] = time();
+
+ header('Location: index.php');
+ exit;
+ }
+ }
+
+ // Login fallido
+ recordFailedLogin($clientIp);
+ $error = 'Usuario o contraseña incorrectos.';
+
+ // Verificar si se bloqueó después de este intento
+ if (!checkLoginAttempts($clientIp)) {
+ $loginBlocked = true;
+ $error = 'Demasiados intentos de login. Cuenta bloqueada por ' . (LOGIN_LOCKOUT_TIME / 60) . ' minutos.';
+ }
+}
+?>
+
+
+
+
+
+ 🔐 Login - WhatsApp Bot Manager
+
+
+
+
+
+
+
+
+
+
WhatsApp Bot Manager
+
Panel de Administración
+
Desarrollado por U-Site.app
+
+
+
+
+ Protección Brute Force
+ Sesiones Seguras
+ Control de Acceso
+
+
+
+
+
+
+
+
Acceso Bloqueado
+
= htmlspecialchars($error) ?>
+
+
+
+
+
+
+
+
+
+ Usuario
+
+
+ value="admin"
+ readonly
+ >
+
+
+
+
+
+ Contraseña
+
+
+
+ placeholder="Ingresa tu contraseña"
+ >
+
+ >
+
+
+
+
+
+
+
+
+
+ = htmlspecialchars($error) ?>
+
+
+
+
+
+ >
+
+ Acceso Bloqueado
+
+ Iniciar Sesión
+
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rename_to_bot.bat b/rename_to_bot.bat
new file mode 100644
index 0000000..6946621
--- /dev/null
+++ b/rename_to_bot.bat
@@ -0,0 +1,44 @@
+@echo off
+echo 🔄 Renombrando proyecto de "migrador" a "bot"...
+
+REM Detener el servidor PHP si está corriendo
+echo 📛 Deteniendo servidor PHP...
+taskkill /f /im php.exe >nul 2>&1
+
+REM Cambiar al directorio padre
+cd ..
+
+REM Copiar archivos a nueva carpeta "bot"
+echo 📁 Creando nueva carpeta "bot"...
+if not exist "bot" mkdir bot
+
+echo 📋 Copiando archivos...
+xcopy "migrador\*" "bot\" /E /H /C /I /Y >nul
+
+REM Eliminar carpeta vieja (opcional)
+echo ❓ ¿Deseas eliminar la carpeta "migrador" antigua? (y/n):
+set /p choice=
+if /i "%choice%"=="y" (
+ echo 🗑️ Eliminando carpeta "migrador"...
+ rmdir /s /q "migrador"
+ echo ✅ Carpeta "migrador" eliminada
+) else (
+ echo ⚠️ Carpeta "migrador" conservada
+)
+
+REM Cambiar al nuevo directorio
+cd bot
+
+echo 🚀 ¡Renombrado completado!
+echo.
+echo 📋 Nuevas URLs:
+echo 🏠 Panel: http://localhost/bot/index.php
+echo 📋 Inicio: http://localhost/bot/inicio.html
+echo ⚙️ Instalador: http://localhost/bot/install.php
+echo 🧪 Pruebas: http://localhost/bot/test.php
+echo.
+echo 💡 Para usar con servidor PHP:
+echo cd C:\laragon\www\bot
+echo php -S localhost:8000 -t .
+echo.
+pause
\ No newline at end of file
diff --git a/server_setup.php b/server_setup.php
new file mode 100644
index 0000000..9911c8d
--- /dev/null
+++ b/server_setup.php
@@ -0,0 +1,342 @@
+
+
+
+
+
+
+ 🚀 Configurador de Servidor - WhatsApp Bot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Configurador de Servidor
+
Configura tu WhatsApp Bot Manager en el servidor
+
+
+
+
+
+
+
1. Verificar Servidor
+
+
+
+
+
+
2. Configurar BD
+
+
+
+
+
+
3. WhatsApp API
+
+
+
+
+
+
+
Antes de comenzar, asegúrate de tener:
+
+ Credenciales de base de datos MySQL
+ Token de WhatsApp Business API
+ Phone Number ID de WhatsApp
+ Dominio con SSL/HTTPS activo
+
+
+
+
+
+
+
+
+ Verificación del Servidor
+
+
+ version_compare(PHP_VERSION, '8.0.0', '>='),
+ 'PDO Extension' => extension_loaded('pdo'),
+ 'PDO MySQL' => extension_loaded('pdo_mysql'),
+ 'cURL Extension' => extension_loaded('curl'),
+ 'JSON Extension' => extension_loaded('json'),
+ 'mbstring Extension' => extension_loaded('mbstring'),
+ 'OpenSSL Extension' => extension_loaded('openssl'),
+ 'Write Permissions' => is_writable('.'),
+ 'HTTPS' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
+ ];
+
+ $allPassed = true;
+ foreach ($checks as $check => $status) {
+ if (!$status) $allPassed = false;
+ }
+ ?>
+
+
+ $status): ?>
+
+
+
+
+ = $check ?>
+
+ = $status ? 'OK' : 'Falta' ?>
+
+ (= PHP_VERSION ?>)
+
+
+
+
+
+
+
+
+
+
+
+
+ ¡Excelente! Tu servidor cumple todos los requisitos.
+
+
+
+
+
+ Algunos requisitos no se cumplen. Contacta a tu proveedor de hosting.
+
+
+
+
+
+
+
+
+ Configuración de Base de Datos
+
+
+
+ setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ // Guardar configuración
+ $config = file_get_contents('config/config.php');
+ $config = preg_replace("/define\('DB_HOST',\s*'[^']*'\);/", "define('DB_HOST', '$host');", $config);
+ $config = preg_replace("/define\('DB_NAME',\s*'[^']*'\);/", "define('DB_NAME', '$name');", $config);
+ $config = preg_replace("/define\('DB_USER',\s*'[^']*'\);/", "define('DB_USER', '$user');", $config);
+ $config = preg_replace("/define\('DB_PASS',\s*'[^']*'\);/", "define('DB_PASS', '$pass');", $config);
+
+ file_put_contents('config/config.php', $config);
+
+ echo '
+
+ ¡Conexión exitosa! Configuración guardada.
+
';
+ echo '
';
+ } catch (PDOException $e) {
+ echo '
+
+ Error de conexión: ' . htmlspecialchars($e->getMessage()) . '
+
';
+ }
+ ?>
+
+
+
+
+
+
+
+ Estos datos los obtienes desde tu panel de hosting (cPanel, Plesk, etc.)
+
+
+
+
+ Probar Conexión
+
+
+
+
+
+
+
+ Configuración de WhatsApp
+
+
+
+
+
+ ¡Configuración de WhatsApp guardada correctamente!
+
';
+ ?>
+
+
+
+
Importante: Configura estos datos en Facebook Developers:
+
+ Webhook URL: https://= htmlspecialchars($domain) ?>/bot/api/webhook.php
+ Verify Token: = htmlspecialchars($webhook_token) ?>
+ Subscribe to: messages
+
+
+
+
+
+
+
+
+
+
+
+ Guardar Configuración
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/services/BotService.php b/services/BotService.php
new file mode 100644
index 0000000..a836e93
--- /dev/null
+++ b/services/BotService.php
@@ -0,0 +1,380 @@
+db = Database::getInstance();
+ $this->whatsappService = new WhatsAppService();
+ }
+
+ /**
+ * Procesar mensaje entrante
+ */
+ public function processMessage($user, $messageText, $messageType = 'text') {
+ // Solo procesar mensajes de texto
+ if ($messageType !== 'text') {
+ return;
+ }
+
+ $messageText = trim($messageText);
+ $phoneNumber = $user['phone_number'];
+
+ // Verificar si es un nuevo usuario (enviar mensaje de bienvenida)
+ if ($this->isNewUser($user['id'])) {
+ $this->sendWelcomeMessage($phoneNumber);
+ return;
+ }
+
+ // Procesar comandos especiales
+ if ($this->processSpecialCommands($phoneNumber, $messageText)) {
+ return;
+ }
+
+ // Verificar si el usuario está en un menú
+ if ($user['current_menu_id']) {
+ $this->processMenuSelection($user, $messageText);
+ } else {
+ // Procesar respuestas automáticas o comando menu
+ $this->processAutoResponses($phoneNumber, $messageText);
+ }
+ }
+
+ /**
+ * Verificar si es un nuevo usuario
+ */
+ private function isNewUser($userId) {
+ $messageCount = $this->db->fetch(
+ "SELECT COUNT(*) as count FROM conversations WHERE user_id = :user_id",
+ ['user_id' => $userId]
+ );
+
+ return $messageCount['count'] <= 1; // Solo el mensaje actual
+ }
+
+ /**
+ * Enviar mensaje de bienvenida
+ */
+ private function sendWelcomeMessage($phoneNumber) {
+ $welcomeMessage = getConfigFromDB('welcome_message', '¡Hola! 👋 Bienvenido a nuestro servicio automatizado. Escribe *menu* para ver las opciones disponibles.');
+
+ $this->whatsappService->sendTextMessage($phoneNumber, $welcomeMessage);
+ }
+
+ /**
+ * Procesar comandos especiales
+ */
+ private function processSpecialCommands($phoneNumber, $messageText) {
+ $command = strtolower($messageText);
+
+ switch ($command) {
+ case 'menu':
+ case 'menú':
+ case 'inicio':
+ $this->showMainMenu($phoneNumber);
+ return true;
+
+ case 'salir':
+ case 'exit':
+ case 'cancelar':
+ $this->exitMenu($phoneNumber);
+ return true;
+
+ case 'help':
+ case 'ayuda':
+ $this->showHelp($phoneNumber);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Mostrar menú principal
+ */
+ private function showMainMenu($phoneNumber) {
+ $mainMenu = $this->db->fetch(
+ "SELECT * FROM menus WHERE is_root = 1 AND is_active = 1 ORDER BY order_position LIMIT 1"
+ );
+
+ if ($mainMenu) {
+ $this->showMenu($phoneNumber, $mainMenu['id']);
+ } else {
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "❌ No hay menús configurados. Contacta con soporte."
+ );
+ }
+ }
+
+ /**
+ * Mostrar menú específico
+ */
+ private function showMenu($phoneNumber, $menuId) {
+ // Obtener información del menú
+ $menu = $this->db->fetch(
+ "SELECT * FROM menus WHERE id = :id AND is_active = 1",
+ ['id' => $menuId]
+ );
+
+ if (!$menu) {
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "❌ Menú no encontrado."
+ );
+ return;
+ }
+
+ // Obtener opciones del menú
+ $options = $this->db->fetchAll(
+ "SELECT * FROM menu_options WHERE menu_id = :menu_id AND is_active = 1 ORDER BY option_number",
+ ['menu_id' => $menuId]
+ );
+
+ if (empty($options)) {
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "❌ Este menú no tiene opciones configuradas."
+ );
+ return;
+ }
+
+ // Construir mensaje del menú
+ $menuText = "📋 *" . $menu['title'] . "*\n\n";
+
+ if ($menu['description']) {
+ $menuText .= $menu['description'] . "\n\n";
+ }
+
+ foreach ($options as $option) {
+ $menuText .= $option['option_number'] . ". " . $option['text'] . "\n";
+ }
+
+ $menuText .= "\n💬 *Responde con el número de la opción que deseas*";
+
+ // Actualizar estado del usuario
+ $this->updateUserMenuState($phoneNumber, $menuId);
+
+ // Enviar mensaje
+ $this->whatsappService->sendTextMessage($phoneNumber, $menuText);
+ }
+
+ /**
+ * Procesar selección de menú
+ */
+ private function processMenuSelection($user, $messageText) {
+ $phoneNumber = $user['phone_number'];
+ $currentMenuId = $user['current_menu_id'];
+
+ // Verificar si es un número
+ if (!is_numeric($messageText)) {
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "❌ Por favor, responde solo con el número de la opción deseada."
+ );
+ return;
+ }
+
+ $optionNumber = (int)$messageText;
+
+ // Buscar la opción seleccionada
+ $option = $this->db->fetch(
+ "SELECT * FROM menu_options WHERE menu_id = :menu_id AND option_number = :option_number AND is_active = 1",
+ ['menu_id' => $currentMenuId, 'option_number' => $optionNumber]
+ );
+
+ if (!$option) {
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "❌ Opción inválida. Por favor, selecciona una opción válida del menú."
+ );
+ return;
+ }
+
+ // Procesar acción de la opción
+ $this->processMenuAction($phoneNumber, $option);
+ }
+
+ /**
+ * Procesar acción del menú
+ */
+ private function processMenuAction($phoneNumber, $option) {
+ switch ($option['action_type']) {
+ case 'menu':
+ // Navegar a otro menú
+ $targetMenu = $this->db->fetch(
+ "SELECT * FROM menus WHERE name = :name AND is_active = 1",
+ ['name' => $option['action_value']]
+ );
+
+ if ($targetMenu) {
+ $this->showMenu($phoneNumber, $targetMenu['id']);
+ } else {
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "❌ Menú no encontrado: " . $option['action_value']
+ );
+ }
+ break;
+
+ case 'message':
+ // Enviar mensaje de respuesta
+ $this->whatsappService->sendTextMessage($phoneNumber, $option['action_value']);
+ $this->exitMenu($phoneNumber);
+ break;
+
+ case 'api_call':
+ // Llamar API externa (implementar según necesidad)
+ $this->processApiCall($phoneNumber, $option['action_value']);
+ break;
+
+ case 'end':
+ // Finalizar conversación
+ $message = $option['action_value'] ?: "Gracias por usar nuestro servicio. ¡Hasta pronto!";
+ $this->whatsappService->sendTextMessage($phoneNumber, $message);
+ $this->exitMenu($phoneNumber);
+ break;
+ }
+ }
+
+ /**
+ * Procesar llamada API externa
+ */
+ private function processApiCall($phoneNumber, $apiEndpoint) {
+ // Aquí puedes implementar llamadas a APIs externas
+ // Por ejemplo, consultar saldos, procesar pagos, etc.
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "🔄 Procesando tu solicitud... Un momento por favor."
+ );
+
+ // Simular procesamiento
+ sleep(2);
+
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "✅ Tu solicitud ha sido procesada exitosamente."
+ );
+
+ $this->exitMenu($phoneNumber);
+ }
+
+ /**
+ * Procesar respuestas automáticas
+ */
+ private function processAutoResponses($phoneNumber, $messageText) {
+ $keyword = strtolower($messageText);
+
+ // Buscar respuesta automática por keyword
+ $autoResponse = $this->db->fetch(
+ "SELECT * FROM auto_responses WHERE trigger_type = 'keyword' AND LOWER(trigger_value) = :keyword AND is_active = 1",
+ ['keyword' => $keyword]
+ );
+
+ if ($autoResponse) {
+ $this->whatsappService->sendTextMessage($phoneNumber, $autoResponse['response_text']);
+
+ // Si la keyword es 'menu', mostrar el menú
+ if ($keyword === 'menu' || $keyword === 'menú') {
+ $this->showMainMenu($phoneNumber);
+ }
+ } else {
+ // Respuesta por defecto para mensajes no reconocidos
+ $defaultMessage = "🤖 No entendí tu mensaje. Escribe *menu* para ver las opciones disponibles o *ayuda* para obtener ayuda.";
+ $this->whatsappService->sendTextMessage($phoneNumber, $defaultMessage);
+ }
+ }
+
+ /**
+ * Salir del menú actual
+ */
+ private function exitMenu($phoneNumber) {
+ $this->db->update(
+ 'users',
+ ['current_menu_id' => null, 'current_step' => 0, 'session_data' => null],
+ 'phone_number = :phone',
+ ['phone' => $phoneNumber]
+ );
+ }
+
+ /**
+ * Actualizar estado del menú del usuario
+ */
+ private function updateUserMenuState($phoneNumber, $menuId) {
+ $this->db->update(
+ 'users',
+ ['current_menu_id' => $menuId, 'current_step' => 1],
+ 'phone_number = :phone',
+ ['phone' => $phoneNumber]
+ );
+ }
+
+ /**
+ * Mostrar ayuda
+ */
+ private function showHelp($phoneNumber) {
+ $helpText = "🆘 *Ayuda del Sistema*\n\n";
+ $helpText .= "📋 *menu* - Mostrar menú principal\n";
+ $helpText .= "❌ *salir* - Salir del menú actual\n";
+ $helpText .= "🆘 *ayuda* - Mostrar esta ayuda\n\n";
+ $helpText .= "💡 *Consejos:*\n";
+ $helpText .= "• Responde solo con números en los menús\n";
+ $helpText .= "• Escribe palabras clave para obtener respuestas rápidas\n";
+ $helpText .= "• Si tienes problemas, escribe *soporte*";
+
+ $this->whatsappService->sendTextMessage($phoneNumber, $helpText);
+ }
+
+ /**
+ * Procesar mensaje de soporte
+ */
+ public function processSupportMessage($phoneNumber, $message) {
+ // Notificar a administradores sobre consulta de soporte
+ $this->whatsappService->sendTextMessage(
+ $phoneNumber,
+ "🆘 Tu mensaje ha sido enviado a nuestro equipo de soporte. Te contactaremos pronto.\n\n" .
+ "📞 También puedes llamarnos al: +57 1 234 5678\n" .
+ "📧 O escribirnos a: soporte@miempresa.com"
+ );
+
+ // Aquí podrías implementar notificaciones a administradores
+ // Por ejemplo, enviar email o notificación a Slack
+ }
+
+ /**
+ * Obtener estadísticas del bot
+ */
+ public function getBotStats() {
+ $stats = [];
+
+ // Total de usuarios
+ $stats['total_users'] = $this->db->fetch(
+ "SELECT COUNT(*) as count FROM users"
+ )['count'];
+
+ // Usuarios activos (último mes)
+ $stats['active_users'] = $this->db->fetch(
+ "SELECT COUNT(DISTINCT user_id) as count FROM conversations
+ WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)"
+ )['count'];
+
+ // Total de mensajes
+ $stats['total_messages'] = $this->db->fetch(
+ "SELECT COUNT(*) as count FROM conversations"
+ )['count'];
+
+ // Mensajes hoy
+ $stats['messages_today'] = $this->db->fetch(
+ "SELECT COUNT(*) as count FROM conversations
+ WHERE DATE(created_at) = CURDATE()"
+ )['count'];
+
+ return $stats;
+ }
+}
+?>
\ No newline at end of file
diff --git a/services/WhatsAppService.php b/services/WhatsAppService.php
new file mode 100644
index 0000000..9bda63b
--- /dev/null
+++ b/services/WhatsAppService.php
@@ -0,0 +1,336 @@
+token = WHATSAPP_TOKEN;
+ $this->phoneNumberId = WHATSAPP_PHONE_NUMBER_ID;
+ $this->apiUrl = WHATSAPP_API_URL;
+ $this->db = Database::getInstance();
+ }
+
+ /**
+ * Enviar mensaje de texto
+ */
+ public function sendTextMessage($to, $message) {
+ $data = [
+ 'messaging_product' => 'whatsapp',
+ 'to' => $this->formatPhoneNumber($to),
+ 'type' => 'text',
+ 'text' => [
+ 'body' => $message
+ ]
+ ];
+
+ return $this->sendMessage($data);
+ }
+
+ /**
+ * Enviar mensaje con template
+ */
+ public function sendTemplateMessage($to, $templateName, $language = 'es', $parameters = []) {
+ $template = [
+ 'name' => $templateName,
+ 'language' => [
+ 'code' => $language
+ ]
+ ];
+
+ if (!empty($parameters)) {
+ $template['components'] = [
+ [
+ 'type' => 'body',
+ 'parameters' => $parameters
+ ]
+ ];
+ }
+
+ $data = [
+ 'messaging_product' => 'whatsapp',
+ 'to' => $this->formatPhoneNumber($to),
+ 'type' => 'template',
+ 'template' => $template
+ ];
+
+ return $this->sendMessage($data);
+ }
+
+ /**
+ * Enviar mensaje con botones interactivos
+ */
+ public function sendInteractiveMessage($to, $bodyText, $buttons, $header = null, $footer = null) {
+ $interactive = [
+ 'type' => 'button',
+ 'body' => [
+ 'text' => $bodyText
+ ],
+ 'action' => [
+ 'buttons' => $buttons
+ ]
+ ];
+
+ if ($header) {
+ $interactive['header'] = [
+ 'type' => 'text',
+ 'text' => $header
+ ];
+ }
+
+ if ($footer) {
+ $interactive['footer'] = [
+ 'text' => $footer
+ ];
+ }
+
+ $data = [
+ 'messaging_product' => 'whatsapp',
+ 'recipient_type' => 'individual',
+ 'to' => $this->formatPhoneNumber($to),
+ 'type' => 'interactive',
+ 'interactive' => $interactive
+ ];
+
+ return $this->sendMessage($data);
+ }
+
+ /**
+ * Enviar mensaje con lista
+ */
+ public function sendListMessage($to, $bodyText, $buttonText, $sections, $header = null, $footer = null) {
+ $interactive = [
+ 'type' => 'list',
+ 'body' => [
+ 'text' => $bodyText
+ ],
+ 'action' => [
+ 'button' => $buttonText,
+ 'sections' => $sections
+ ]
+ ];
+
+ if ($header) {
+ $interactive['header'] = [
+ 'type' => 'text',
+ 'text' => $header
+ ];
+ }
+
+ if ($footer) {
+ $interactive['footer'] = [
+ 'text' => $footer
+ ];
+ }
+
+ $data = [
+ 'messaging_product' => 'whatsapp',
+ 'recipient_type' => 'individual',
+ 'to' => $this->formatPhoneNumber($to),
+ 'type' => 'interactive',
+ 'interactive' => $interactive
+ ];
+
+ return $this->sendMessage($data);
+ }
+
+ /**
+ * Marcar mensaje como leído
+ */
+ public function markAsRead($messageId) {
+ $data = [
+ 'messaging_product' => 'whatsapp',
+ 'status' => 'read',
+ 'message_id' => $messageId
+ ];
+
+ $url = $this->apiUrl . $this->phoneNumberId . '/messages';
+ return $this->makeRequest('POST', $url, $data);
+ }
+
+ /**
+ * Enviar mensaje principal
+ */
+ private function sendMessage($data) {
+ $url = $this->apiUrl . $this->phoneNumberId . '/messages';
+ $response = $this->makeRequest('POST', $url, $data);
+
+ // Guardar mensaje enviado en la base de datos
+ if ($response && isset($response['messages'][0]['id'])) {
+ $this->saveOutgoingMessage($data, $response);
+ }
+
+ return $response;
+ }
+
+ /**
+ * Realizar petición HTTP
+ */
+ private function makeRequest($method, $url, $data = null) {
+ $ch = curl_init();
+
+ $headers = [
+ 'Authorization: Bearer ' . $this->token,
+ 'Content-Type: application/json',
+ 'User-Agent: WhatsApp-Bot/1.0'
+ ];
+
+ curl_setopt_array($ch, [
+ CURLOPT_URL => $url,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_TIMEOUT => 30,
+ CURLOPT_CONNECTTIMEOUT => 10,
+ CURLOPT_SSL_VERIFYPEER => true,
+ CURLOPT_SSL_VERIFYHOST => 2,
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_MAXREDIRS => 3
+ ]);
+
+ if ($method === 'POST' && $data) {
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
+ }
+
+ $response = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $error = curl_error($ch);
+ curl_close($ch);
+
+ if ($error) {
+ error_log("cURL Error: " . $error);
+ throw new Exception("Error de comunicación con WhatsApp: " . $error);
+ }
+
+ $decoded = json_decode($response, true);
+
+ if ($httpCode >= 400) {
+ $errorMsg = isset($decoded['error']['message']) ? $decoded['error']['message'] : 'Error desconocido';
+ error_log("WhatsApp API Error: " . $response);
+ throw new Exception("Error de WhatsApp API: " . $errorMsg);
+ }
+
+ return $decoded;
+ }
+
+ /**
+ * Formatear número de teléfono
+ */
+ private function formatPhoneNumber($phone) {
+ // Remover caracteres especiales
+ $phone = preg_replace('/[^0-9]/', '', $phone);
+
+ // Si empieza con +57 (Colombia), mantenerlo
+ if (substr($phone, 0, 2) === '57' && strlen($phone) >= 12) {
+ return $phone;
+ }
+
+ // Si es número colombiano sin código de país
+ if (strlen($phone) === 10 && substr($phone, 0, 1) === '3') {
+ return '57' . $phone;
+ }
+
+ return $phone;
+ }
+
+ /**
+ * Guardar mensaje enviado en BD
+ */
+ private function saveOutgoingMessage($data, $response) {
+ try {
+ $user = $this->getUserByPhone($data['to']);
+ if ($user) {
+ $messageData = [
+ 'user_id' => $user['id'],
+ 'message_id' => $response['messages'][0]['id'],
+ 'direction' => 'outgoing',
+ 'message_type' => $data['type'],
+ 'content' => $this->extractMessageContent($data),
+ 'status' => 'sent',
+ 'created_at' => date('Y-m-d H:i:s')
+ ];
+
+ $this->db->insert('conversations', $messageData);
+ }
+ } catch (Exception $e) {
+ error_log("Error saving outgoing message: " . $e->getMessage());
+ }
+ }
+
+ /**
+ * Extraer contenido del mensaje para guardar
+ */
+ private function extractMessageContent($data) {
+ switch ($data['type']) {
+ case 'text':
+ return $data['text']['body'];
+ case 'template':
+ return 'Template: ' . $data['template']['name'];
+ case 'interactive':
+ if (isset($data['interactive']['body']['text'])) {
+ return $data['interactive']['body']['text'];
+ }
+ break;
+ }
+ return json_encode($data);
+ }
+
+ /**
+ * Obtener usuario por teléfono
+ */
+ private function getUserByPhone($phone) {
+ return $this->db->fetch(
+ "SELECT * FROM users WHERE phone_number = :phone",
+ ['phone' => $phone]
+ );
+ }
+
+ /**
+ * Descargar archivo multimedia
+ */
+ public function downloadMedia($mediaId) {
+ $url = $this->apiUrl . $mediaId;
+
+ // Primero obtener información del archivo
+ $mediaInfo = $this->makeRequest('GET', $url);
+
+ if (!$mediaInfo || !isset($mediaInfo['url'])) {
+ throw new Exception("No se pudo obtener información del archivo");
+ }
+
+ // Descargar el archivo
+ $fileUrl = $mediaInfo['url'];
+ $ch = curl_init();
+
+ curl_setopt_array($ch, [
+ CURLOPT_URL => $fileUrl,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTPHEADER => [
+ 'Authorization: Bearer ' . $this->token
+ ],
+ CURLOPT_TIMEOUT => 60,
+ CURLOPT_FOLLOWLOCATION => true
+ ]);
+
+ $fileContent = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ if ($httpCode !== 200) {
+ throw new Exception("Error descargando archivo multimedia");
+ }
+
+ return [
+ 'content' => $fileContent,
+ 'mime_type' => $mediaInfo['mime_type'] ?? 'application/octet-stream',
+ 'file_size' => $mediaInfo['file_size'] ?? strlen($fileContent)
+ ];
+ }
+}
+?>
\ No newline at end of file
diff --git a/test.php b/test.php
new file mode 100644
index 0000000..827eda4
--- /dev/null
+++ b/test.php
@@ -0,0 +1,189 @@
+🤖 Test del Sistema WhatsApp Bot";
+
+// Verificar archivos principales
+$files = [
+ 'config/config.php' => 'Configuración principal',
+ 'classes/Database.php' => 'Clase Database',
+ 'services/WhatsAppService.php' => 'Servicio WhatsApp',
+ 'services/BotService.php' => 'Servicio Bot',
+ 'api/webhook.php' => 'Webhook principal',
+ 'database/schema.sql' => 'Esquema de BD',
+ 'index.php' => 'Página principal'
+];
+
+echo "📂 Verificación de Archivos: ";
+echo "";
+foreach ($files as $file => $description) {
+ $exists = file_exists($file);
+ $status = $exists ? "✅ Existe" : "❌ Faltante";
+ echo "{$description} ({$file}): {$status} ";
+}
+echo " ";
+
+// Probar configuración
+echo "⚙️ Verificación de Configuración: ";
+try {
+ require_once 'config/config.php';
+ echo "✅ Configuración cargada correctamente
";
+ echo "";
+ echo "DB Host: " . DB_HOST . " ";
+ echo "DB Name: " . DB_NAME . " ";
+ echo "WhatsApp Phone ID: " . WHATSAPP_PHONE_NUMBER_ID . " ";
+ echo "App URL: " . APP_URL . " ";
+ echo " ";
+} catch (Exception $e) {
+ echo "❌ Error en configuración: " . $e->getMessage() . "
";
+}
+
+// Probar conexión a base de datos
+echo "💾 Verificación de Base de Datos: ";
+try {
+ $db = Database::getInstance();
+ echo "✅ Conexión a base de datos exitosa
";
+
+ // Verificar tablas principales
+ $tables = ['users', 'conversations', 'menus', 'menu_options', 'system_config'];
+ echo "";
+ foreach ($tables as $table) {
+ try {
+ $count = $db->fetch("SELECT COUNT(*) as count FROM {$table}")['count'];
+ echo "Tabla {$table} : {$count} registros ";
+ } catch (Exception $e) {
+ echo "Tabla {$table} : ❌ Error: " . $e->getMessage() . " ";
+ }
+ }
+ echo " ";
+
+} catch (Exception $e) {
+ echo "❌ Error de conexión: " . $e->getMessage() . "
";
+}
+
+// Probar servicios
+echo "🔧 Verificación de Servicios: ";
+try {
+ $whatsappService = new WhatsAppService();
+ echo "✅ WhatsAppService inicializado
";
+
+ $botService = new BotService();
+ echo "✅ BotService inicializado
";
+
+} catch (Exception $e) {
+ echo "❌ Error en servicios: " . $e->getMessage() . "
";
+}
+
+// Probar APIs
+echo "🌐 Verificación de APIs: ";
+$apis = [
+ 'api/get_stats.php',
+ 'api/get_users.php',
+ 'api/get_menus.php',
+ 'api/get_conversations.php',
+ 'api/webhook.php'
+];
+
+echo "";
+foreach ($apis as $api) {
+ $exists = file_exists($api);
+ $readable = $exists ? is_readable($api) : false;
+ $status = $readable ? "✅ Disponible" : "❌ No disponible";
+ echo "{$api} : {$status} ";
+}
+echo " ";
+
+// Probar funcionalidad básica del bot
+echo "🤖 Test Básico del Bot: ";
+try {
+ // Crear usuario de prueba
+ $testPhone = '573000000000';
+
+ // Verificar si el usuario existe
+ $existingUser = $db->fetch(
+ "SELECT * FROM users WHERE phone_number = :phone",
+ ['phone' => $testPhone]
+ );
+
+ if (!$existingUser) {
+ $userId = $db->insert('users', [
+ 'phone_number' => $testPhone,
+ 'name' => 'Usuario de Prueba',
+ 'status' => 'active',
+ 'created_at' => date('Y-m-d H:i:s')
+ ]);
+ echo "✅ Usuario de prueba creado (ID: {$userId})
";
+ } else {
+ echo "✅ Usuario de prueba ya existe
";
+ }
+
+ // Probar menú principal
+ $mainMenu = $db->fetch(
+ "SELECT * FROM menus WHERE is_root = 1 AND is_active = 1 LIMIT 1"
+ );
+
+ if ($mainMenu) {
+ echo "✅ Menú principal configurado: " . $mainMenu['title'] . "
";
+
+ // Probar opciones del menú
+ $options = $db->fetchAll(
+ "SELECT * FROM menu_options WHERE menu_id = :id ORDER BY option_number",
+ ['id' => $mainMenu['id']]
+ );
+
+ echo "✅ Opciones del menú principal: " . count($options) . " opciones
";
+
+ } else {
+ echo "❌ No hay menú principal configurado
";
+ }
+
+} catch (Exception $e) {
+ echo "❌ Error en test del bot: " . $e->getMessage() . "
";
+}
+
+// URLs de prueba
+echo "🔗 URLs del Sistema: ";
+echo "";
+
+echo "📋 Resumen: ";
+echo "El sistema WhatsApp Bot está:
";
+
+// Calcular estado general
+$allGood = file_exists('config/config.php') &&
+ file_exists('classes/Database.php') &&
+ file_exists('api/webhook.php') &&
+ class_exists('Database');
+
+if ($allGood) {
+ echo "";
+ echo "
✅ SISTEMA FUNCIONANDO ";
+ echo "
Todos los componentes principales están funcionando correctamente.
";
+ echo "
Próximos pasos:
";
+ echo "
";
+ echo "1. Configura el webhook en Facebook Developers ";
+ echo "2. Ajusta la configuración en el panel ";
+ echo "3. Personaliza los menús según tus necesidades ";
+ echo "4. ¡Comienza a recibir mensajes! ";
+ echo " ";
+ echo "
";
+} else {
+ echo "";
+ echo "
❌ HAY PROBLEMAS ";
+ echo "
Algunos componentes no están funcionando correctamente. Revisa los errores arriba.
";
+ echo "
";
+}
+
+echo " ";
+echo "Test ejecutado el: " . date('Y-m-d H:i:s') . "
";
+?>
\ No newline at end of file