From ce12ac2ccec07387e0a5c96e3d8440e59e765e8b Mon Sep 17 00:00:00 2001 From: lizandrogd <77708265+lizandrogd@users.noreply.github.com> Date: Tue, 18 Nov 2025 20:26:36 -0500 Subject: [PATCH] w --- DEPLOYMENT_GUIDE.md | 359 +++++++++++++ EMERGENCIA_TABLA_NO_EXISTE.md | 146 ++++++ GUIA_HESTIACP.md | 158 ++++++ INSTALACION.md | 151 ++++++ README.md | 295 +++++++++++ SOLUCION_ERROR_CONFIG.md | 142 ++++++ SOLUCION_HOSTING.md | 98 ++++ SOLUCION_TABLA_NO_EXISTE.md | 143 ++++++ api/export_users.php | 79 +++ api/get_chart_data.php | 47 ++ api/get_conversations.php | 41 ++ api/get_logs.php | 44 ++ api/get_menus.php | 51 ++ api/get_recent_messages.php | 39 ++ api/get_settings.php | 33 ++ api/get_stats.php | 48 ++ api/get_templates.php | 37 ++ api/get_users.php | 41 ++ api/save_menu.php | 64 +++ api/save_settings.php | 89 ++++ api/send_broadcast.php | 88 ++++ api/send_message.php | 78 +++ api/webhook.php | 221 ++++++++ assets/css/styles.css | 694 ++++++++++++++++++++++++++ assets/js/app.js | 912 ++++++++++++++++++++++++++++++++++ classes/Database.php | 114 +++++ config/config.php | 211 ++++++++ database/schema.sql | 179 +++++++ debug_install.php | 269 ++++++++++ deploy.sh | 261 ++++++++++ index.html | 549 ++++++++++++++++++++ index.php | 530 ++++++++++++++++++++ inicio.html | 406 +++++++++++++++ instalacion.html | 282 +++++++++++ install.php | 493 ++++++++++++++++++ install_integrated.php | 438 ++++++++++++++++ install_manual.php | 544 ++++++++++++++++++++ install_simple.php | 239 +++++++++ install_ultra_basic.php | 282 +++++++++++ login.php | 331 ++++++++++++ rename_to_bot.bat | 44 ++ server_setup.php | 342 +++++++++++++ services/BotService.php | 380 ++++++++++++++ services/WhatsAppService.php | 336 +++++++++++++ test.php | 189 +++++++ 45 files changed, 10517 insertions(+) create mode 100644 DEPLOYMENT_GUIDE.md create mode 100644 EMERGENCIA_TABLA_NO_EXISTE.md create mode 100644 GUIA_HESTIACP.md create mode 100644 INSTALACION.md create mode 100644 README.md create mode 100644 SOLUCION_ERROR_CONFIG.md create mode 100644 SOLUCION_HOSTING.md create mode 100644 SOLUCION_TABLA_NO_EXISTE.md create mode 100644 api/export_users.php create mode 100644 api/get_chart_data.php create mode 100644 api/get_conversations.php create mode 100644 api/get_logs.php create mode 100644 api/get_menus.php create mode 100644 api/get_recent_messages.php create mode 100644 api/get_settings.php create mode 100644 api/get_stats.php create mode 100644 api/get_templates.php create mode 100644 api/get_users.php create mode 100644 api/save_menu.php create mode 100644 api/save_settings.php create mode 100644 api/send_broadcast.php create mode 100644 api/send_message.php create mode 100644 api/webhook.php create mode 100644 assets/css/styles.css create mode 100644 assets/js/app.js create mode 100644 classes/Database.php create mode 100644 config/config.php create mode 100644 database/schema.sql create mode 100644 debug_install.php create mode 100644 deploy.sh create mode 100644 index.html create mode 100644 index.php create mode 100644 inicio.html create mode 100644 instalacion.html create mode 100644 install.php create mode 100644 install_integrated.php create mode 100644 install_manual.php create mode 100644 install_simple.php create mode 100644 install_ultra_basic.php create mode 100644 login.php create mode 100644 rename_to_bot.bat create mode 100644 server_setup.php create mode 100644 services/BotService.php create mode 100644 services/WhatsAppService.php create mode 100644 test.php 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'} +
+ + ${menu.is_active ? 'Activo' : 'Inactivo'} + +
+
+
+ + + +
+ `; + + // 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 = ''; + + 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 = ''; + + 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 = ''; + + 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} + + + + + + + + + `; + 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 = ` +
+
+
${message}
+
+ `; + + 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:
+
═══════════════════════════════════════════
+ + 📋 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 + + + + + + + + + +
+ +
+

Sistema de Gestión WhatsApp Bot

+
+ + En línea + + +
+
+ + +
+
+
+
+
+
+
+
Total Usuarios
+

-

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Mensajes Hoy
+

-

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Usuarios Activos
+

-

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Total Mensajes
+

-

+
+
+ +
+
+
+
+
+
+ + +
+
+
+
+
Actividad Reciente
+
+
+ +
+
+
+
+
+
+
Últimos Mensajes
+
+
+
+ +
+
+
+
+
+
+ + +
+
+
+
Conversaciones
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
UsuarioÚltimo MensajeTipoEstadoFechaAcciones
+
+
+
+
+ + +
+
+
+
Gestión de Usuarios
+ +
+
+
+ + + + + + + + + + + + + + + +
IDTeléfonoNombreEstadoMenú ActualRegistroAcciones
+
+
+
+
+ + + + + +
+
+
+
+
+
Enviar Mensaje
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+
+
+
+
Mensaje Masivo
+
+
+
+
+ + +
+
+ + +
+
+ + Cuidado: Este mensaje se enviará a múltiples usuarios. +
+ +
+
+
+
+
+
+ + +
+
+
+
Plantillas de Mensaje
+ +
+
+
+ + + + + + + + + + + + + + +
NombrePlantilla WhatsAppIdiomaCategoríaEstadoAcciones
+
+
+
+
+ + +
+
+
+
Respuestas Automáticas
+ +
+
+
+ + + + + + + + + + + + + +
TipoPalabra ClaveRespuestaEstadoAcciones
+
+
+
+
+ + +
+
+
+
+
+
Configuración del Sistema
+
+
+
+
+
WhatsApp Business API
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
Configuración General
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
Información del Sistema
+
+
+
+
Versión:
+
1.0.0
+ +
PHP:
+
8.0+
+ +
Base de Datos:
+
MySQL
+ +
Webhook URL:
+
+ http://localhost/migrador/api/webhook.php +
+
+
+
+
+
+
+ + +
+
+
+
Logs del Sistema
+
+ + +
+
+
+
+ + + + + + + + + + + + + +
FechaIPEstadoRequestResponse
+
+
+
+
+
+ + + + + + + + + + + \ 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 + + + + + + + + + +
+ +
+

Sistema de Gestión WhatsApp Bot

+
+ + En línea + + +
+
+ + +
+
+
+
+
+
+
+
Total Usuarios
+

-

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Mensajes Hoy
+

-

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Usuarios Activos
+

-

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Total Mensajes
+

-

+
+
+ +
+
+
+
+
+
+ + +
+
+
+
+
Actividad Reciente
+
+
+ +
+
+
+
+
+
+
Últimos Mensajes
+
+
+
+ +
+
+
+
+
+
+ + +
+
+
+
Conversaciones
+
+ +
+
+
+
+ + + + + + + + + + + + + + +
UsuarioÚltimo MensajeTipoEstadoFechaAcciones
+
+
+
+
+ + +
+
+
+
Gestión de Usuarios
+ +
+
+
+ + + + + + + + + + + + + + + +
IDTeléfonoNombreEstadoMenú ActualRegistroAcciones
+
+
+
+
+ + + + + +
+
+
+
+
+
Enviar Mensaje
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+
+
+
+
Mensaje Masivo
+
+
+
+
+ + +
+
+ + +
+
+ + Cuidado: Este mensaje se enviará a múltiples usuarios. +
+ +
+
+
+
+
+
+ + +
+
+
+
Plantillas de Mensaje
+ +
+
+
+ + + + + + + + + + + + + + +
NombrePlantilla WhatsAppIdiomaCategoríaEstadoAcciones
+
+
+
+
+ + +
+
+
+
Respuestas Automáticas
+ +
+
+
+ + + + + + + + + + + + + +
TipoPalabra ClaveRespuestaEstadoAcciones
+
+
+
+
+ + +
+
+
+
+
+
Configuración del Sistema
+
+
+
+
+
WhatsApp Business API
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
Configuración General
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
Información del Sistema
+
+
+
+
Versión:
+
1.0.0
+ +
PHP:
+
+ +
Base de Datos:
+
+ +
Webhook URL:
+
+ /api/webhook.php +
+
+
+
+
+
+
+ + +
+
+
+
Logs del Sistema
+
+ + +
+
+
+
+ + + + + + + + + + + + + +
FechaIPEstadoRequestResponse
+
+
+
+
+
+ + + + + + + \ 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

+
+ + Desarrollado por U-Site.app + +
+
+ +
+ +
+
+ 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

+
+ + Desarrollado por U-Site.app | + Soporte técnico + +
+
+ +
+
+
+ 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:
+ +
+
+
+
+ 👤 CONTRASEÑA ADMIN:
+ +
+
+
+
+ + + + + +
+ +

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:
+ Admin: +
+
Creating database 'whatsapp_bot'...
+
Creating user 'whatsapp_user'@'localhost'...
+
Granting privileges...
+
Installing database schema...
+
Updating configuration files...
+
Ready to execute installation _
+
+ +
+ +
+
+ + + + + +
+ +

¡Instalación Completada!

+

Tu sistema WhatsApp Bot está listo para usar

+
+ + + +
+
Errores encontrados:
+ +
+ +
+ + + + +
+
Instalación exitosa:
+ +
+ +
+ + + +
+
+
Credenciales de BD
+
+
Host:
+
Base de datos:
+
Usuario:
+
+ Contraseña:
+
+ +
+
+
+ +
Credenciales Admin
+
+
Usuario: admin
+
+ Contraseña:
+
+ +
+
+
URL Login:
+ /login.php +
+
+
+ +
+ +
+ + ¡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
  • +
+
+ +
+
+
+ + +
+
+ + +
+
+ + + Debe existir previamente +
+
+ + +
+
+ + +
+
+ +
+
+ Credenciales de Admin:
+ Usuario: admin
+ Contraseña: +
+ +
+
+ + +
+

Instalando...

+
+ + +
+
Errores:
+ +
+ +
+ Reintentar +
+ + + +
+
Progreso:
+ +
+ +
+ + + +
+

¡Instalación Exitosa!

+

Sistema WhatsApp Bot instalado correctamente

+
+ +
+
+
Credenciales
+
+ Usuario: admin
+ Contraseña:
+ 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:
+
    +
  1. Crear base de datos: Ve a MySQL Databases
  2. +
  3. Crear usuario: Asigna un usuario a la BD
  4. +
  5. Asignar permisos: Da ALL PRIVILEGES al usuario
  6. +
  7. Anotar credenciales: Host, nombre BD, usuario y contraseña
  8. +
+
+ +
+
+
+ + + Generalmente 'localhost' en hosting compartido +
+
+ + + Puerto estándar de MySQL +
+
+ + + Nombre exacto de la base de datos creada +
+
+ + + Usuario asignado a la base de datos +
+
+ + + Contraseña del usuario de base de datos +
+
+ +
+
+
+
Credenciales Admin
+
+ Usuario: admin
+ Contraseña: +
+
+
+
Que incluye
+
    +
  • ✅ Configuración automática
  • +
  • ✅ Creación de tablas
  • +
  • ✅ Datos iniciales
  • +
  • ✅ Protección contra reinstalación
  • +
+
+
+
+ +
+ +
+
+ + + +
+ +

Configurando Sistema...

+

Aplicando configuración con tus credenciales

+
+ + + +
+
Errores encontrados:
+ +
+ +
+ + Volver a intentar + +
+ + + + +
+
Progreso de instalación:
+ +
+ +
+ + + + + +
+
+ Procesando... +
+

Procesando configuración...

+
+ + + + +
+ +

¡Instalación Completada!

+

Tu sistema WhatsApp Bot está configurado y listo

+
+ + + +
+
Advertencias:
+ +
+ +
+ + + + +
+
Instalación exitosa:
+ +
+ +
+ + +
+
+
Acceso al Sistema
+
+
Usuario: admin
+
+ Contraseña:
+
+ +
+
+ +
+
+ +
+ +
+
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:
+ +
+ +
+
+ + +
+

⚙️ Instalando...

+
+ + +
+
❌ Errores:
+ +
+ +
+ Reintentar +
+ + + +
+
✅ Progreso:
+ +
+ +
+ + + +
+

🎉 ¡Instalación Exitosa!

+
+ +
+
+
🔐 Credenciales de Acceso
+
+ Usuario: admin
+ Contraseña: +
+
+
+
🚀 Próximos Pasos
+ +
+
+ +
+ 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á:
+ +
+ +
+
+ + +
+

⚙️ Instalando paso a paso...

+
+ + +
+
❌ Errores:
+ +
+ +
+ Reintentar +
+ + + +
+
✅ Progreso:
+ +
+ +
+ + + +
+
💡 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:
+ Guardadas en: CREDENCIALES_BASICO.txt +
+
+
+
🚀 Siguiente
+ +
+
+ +
+
📋 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 + + + + + + + + + + + \ 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): ?> +
+
+ +
+
+ + + + () + + +
+
+
+ +
+ +
+ +
+ + ¡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()) . ' +
'; + } + ?> + + +
+
+
+ + + Generalmente: localhost +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + Estos datos los obtienes desde tu panel de hosting (cPanel, Plesk, etc.) +
+ +
+ +
+
+ + + +

+ Configuración de WhatsApp +

+ + + + + ¡Configuración de WhatsApp guardada correctamente! +
'; + ?> + +
+ + Importante: Configura estos datos en Facebook Developers: +
    +
  • Webhook URL: https:///bot/api/webhook.php
  • +
  • Verify Token:
  • +
  • Subscribe to: messages
  • +
+
+ + + + +
+
+
+ + + Obténlo desde Facebook for Developers +
+
+ + +
+
+ + +
+
+ + + Usado para verificar el webhook +
+
+ +
+ +
+
+ + + + +
+ + +
+ + WhatsApp Bot Manager v1.0 | + Guía Completa + +
+
+
+
+
+ + + \ 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