This commit is contained in:
Lizandro Guarnizo
2026-01-28 02:44:28 -05:00
parent 6ebf5c9a22
commit e9aecc6bd2
6 changed files with 130 additions and 70 deletions
+7 -1
View File
@@ -49,6 +49,9 @@ LABEL maintainer="U-Site.app"
LABEL description="WhatsApp Bot Manager with Queue Processing" LABEL description="WhatsApp Bot Manager with Queue Processing"
LABEL version="2.0" LABEL version="2.0"
# Configurar zona horaria Colombia
ENV TZ=America/Bogota
# Instalar dependencias runtime # Instalar dependencias runtime
RUN apk add --no-cache \ RUN apk add --no-cache \
bash \ bash \
@@ -64,7 +67,10 @@ RUN apk add --no-cache \
mysql-client \ mysql-client \
redis \ redis \
ffmpeg \ ffmpeg \
openssl openssl \
tzdata \
&& cp /usr/share/zoneinfo/America/Bogota /etc/localtime \
&& echo "America/Bogota" > /etc/timezone
# Copiar extensiones PHP desde builder # Copiar extensiones PHP desde builder
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/ COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
+7 -1
View File
@@ -1,6 +1,9 @@
# Dockerfile para workers dedicados (sin Nginx) # Dockerfile para workers dedicados (sin Nginx)
FROM php:8.2-cli-alpine FROM php:8.2-cli-alpine
# Configurar zona horaria Colombia
ENV TZ=America/Bogota
# Instalar dependencias # Instalar dependencias
RUN apk add --no-cache \ RUN apk add --no-cache \
bash \ bash \
@@ -13,7 +16,10 @@ RUN apk add --no-cache \
oniguruma-dev \ oniguruma-dev \
mysql-client \ mysql-client \
redis \ redis \
zlib-dev zlib-dev \
tzdata \
&& cp /usr/share/zoneinfo/America/Bogota /etc/localtime \
&& echo "America/Bogota" > /etc/timezone
# Instalar extensiones PHP # Instalar extensiones PHP
RUN docker-php-ext-install -j$(nproc) \ RUN docker-php-ext-install -j$(nproc) \
+95 -58
View File
@@ -125,67 +125,104 @@ try {
// Revisar nuevos mensajes en BD cada 3 segundos // Revisar nuevos mensajes en BD cada 3 segundos
if (time() - $lastCheck >= 3) { if (time() - $lastCheck >= 3) {
// 1. Verificar mensajes nuevos individuales (últimos 10 segundos) // Mantener track de mensajes ya enviados para evitar duplicados
$recentMessages = $db->fetchAll( static $lastMessageId = 0;
"SELECT c.*, u.phone_number, u.name as user_name static $lastConversationCheck = null;
FROM conversations c
INNER JOIN users u ON c.user_id = u.id
WHERE c.created_at >= DATE_SUB(NOW(), INTERVAL 10 SECOND)
AND c.direction = 'incoming'
ORDER BY c.created_at DESC
LIMIT 10"
);
foreach ($recentMessages as $msg) { if ($lastConversationCheck === null) {
sendSSEEvent('new_message', [ // Primera vez: obtener el ID más alto y timestamp actual de la BD, sin enviar nada
'message_id' => $msg['id'], $maxMsg = $db->fetch("SELECT MAX(id) as max_id FROM conversations");
'user_id' => $msg['user_id'], $lastMessageId = $maxMsg['max_id'] ?? 0;
'phone_number' => $msg['phone_number'], // Usar el timestamp de la BD para evitar problemas de zona horaria
'user_name' => $msg['user_name'], $dbTime = $db->fetch("SELECT NOW() as now");
'content' => $msg['message_content'], $lastConversationCheck = $dbTime['now'] ?? date('Y-m-d H:i:s');
'message_type' => $msg['message_type'] ?? 'text', } else {
'direction' => $msg['direction'], // 1. Verificar mensajes NUEVOS (solo los que tienen ID mayor al último visto)
'created_at' => $msg['created_at'], $recentMessages = $db->fetchAll(
'timestamp' => time() "SELECT c.*, u.phone_number, u.name as user_name
]); FROM conversations c
} INNER JOIN users u ON c.user_id = u.id
WHERE c.id > ?
// 2. Verificar si hay nuevas conversaciones desde la última revisión AND c.direction = 'incoming'
$recentConversations = $db->fetchAll( ORDER BY c.id ASC
"SELECT DISTINCT c.user_id, u.phone_number, u.name, LIMIT 10",
MAX(c.created_at) as last_message_time, [$lastMessageId]
COUNT(*) as message_count
FROM conversations c
INNER JOIN users u ON c.user_id = u.id
WHERE c.created_at >= DATE_SUB(NOW(), INTERVAL 10 SECOND)
GROUP BY c.user_id
ORDER BY last_message_time DESC
LIMIT 5"
);
foreach ($recentConversations as $conv) {
sendSSEEvent('new_conversation', [
'user_id' => $conv['user_id'],
'phone_number' => $conv['phone_number'],
'name' => $conv['name'],
'message_count' => $conv['message_count'],
'timestamp' => $conv['last_message_time']
]);
}
// Verificar notificaciones no leídas
try {
$notifications = $db->fetchAll(
"SELECT id, user_id, type, message, data, is_read, created_at
FROM notifications
WHERE is_read = 0
ORDER BY created_at DESC
LIMIT 10"
); );
if ($notifications && count($notifications) > 0) { foreach ($recentMessages as $msg) {
foreach ($notifications as $notification) { sendSSEEvent('new_message', [
sendSSEEvent('notification', $notification); 'message_id' => $msg['id'],
'user_id' => $msg['user_id'],
'phone_number' => $msg['phone_number'],
'user_name' => $msg['user_name'],
'content' => $msg['message_content'],
'message_type' => $msg['message_type'] ?? 'text',
'direction' => $msg['direction'],
'created_at' => $msg['created_at'],
'timestamp' => time()
]);
// Actualizar último ID visto
if ($msg['id'] > $lastMessageId) {
$lastMessageId = $msg['id'];
}
}
// 2. Verificar si hay nuevas conversaciones desde la última revisión
$recentConversations = $db->fetchAll(
"SELECT DISTINCT c.user_id, u.phone_number, u.name,
MAX(c.created_at) as last_message_time,
COUNT(*) as message_count
FROM conversations c
INNER JOIN users u ON c.user_id = u.id
WHERE c.created_at > ?
GROUP BY c.user_id
ORDER BY last_message_time DESC
LIMIT 5",
[$lastConversationCheck]
);
foreach ($recentConversations as $conv) {
sendSSEEvent('new_conversation', [
'user_id' => $conv['user_id'],
'phone_number' => $conv['phone_number'],
'name' => $conv['name'],
'message_count' => $conv['message_count'],
'timestamp' => $conv['last_message_time']
]);
// Actualizar timestamp
if ($conv['last_message_time'] > $lastConversationCheck) {
$lastConversationCheck = $conv['last_message_time'];
}
}
} // Fin del else (primera inicialización vs verificaciones posteriores)
// Verificar notificaciones NUEVAS (solo las creadas desde la última verificación)
// NO enviar notificaciones guardadas - solo las que llegan en tiempo real
try {
static $lastNotificationCheck = null;
if ($lastNotificationCheck === null) {
// Primera vez: usar el timestamp de la BD, NO enviar nada
$dbTimeNotif = $db->fetch("SELECT NOW() as now");
$lastNotificationCheck = $dbTimeNotif['now'] ?? date('Y-m-d H:i:s');
} else {
// Verificaciones posteriores: solo notificaciones NUEVAS
$notifications = $db->fetchAll(
"SELECT id, user_id, type, message, data, is_read, created_at
FROM notifications
WHERE created_at > ?
ORDER BY created_at ASC
LIMIT 10",
[$lastNotificationCheck]
);
if ($notifications && count($notifications) > 0) {
foreach ($notifications as $notification) {
sendSSEEvent('notification', $notification);
// Actualizar timestamp para evitar duplicados
if ($notification['created_at'] > $lastNotificationCheck) {
$lastNotificationCheck = $notification['created_at'];
}
}
} }
} }
} catch (Exception $e) { } catch (Exception $e) {
+12 -8
View File
@@ -2,26 +2,27 @@
# Uso: docker-compose -f docker-compose.dev.yml up -d # Uso: docker-compose -f docker-compose.dev.yml up -d
services: services:
# MySQL Database # MariaDB Database (compatible con producción)
mysql: mysql:
image: mysql:8.0 image: mariadb:10.11
container_name: whatsapp-dev-mysql container_name: whatsapp-dev-mysql
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3306:3306" - "3306:3306"
environment: environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_password_2026} MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_password_2026}
MYSQL_DATABASE: ${DB_NAME:-usite_whatsapp_bot} MARIADB_DATABASE: ${DB_NAME:-usite_whatsapp_bot}
MYSQL_USER: ${DB_USER:-usite_whatsapp_user} MARIADB_USER: ${DB_USER:-usite_whatsapp_user}
MYSQL_PASSWORD: ${DB_PASS:-6q!Vio0fn@PFmsLyHjH5} MARIADB_PASSWORD: ${DB_PASS:-6q!Vio0fn@PFmsLyHjH5}
TZ: America/Bogota
volumes: volumes:
- mysql-dev-data:/var/lib/mysql - mysql-dev-data:/var/lib/mysql
- ./database:/docker-entrypoint-initdb.d:ro - ./database:/docker-entrypoint-initdb.d:ro
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-time-zone=America/Bogota
networks: networks:
- whatsapp-dev-network - whatsapp-dev-network
healthcheck: healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-root_password_2026}"] test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-root_password_2026}"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
@@ -50,6 +51,9 @@ services:
- ssl-certs:/etc/nginx/ssl - ssl-certs:/etc/nginx/ssl
- php-socket:/run/php - php-socket:/run/php
environment: environment:
# Zona horaria Colombia
- TZ=America/Bogota
# Entorno de desarrollo # Entorno de desarrollo
- APP_ENV=development - APP_ENV=development
- APP_DEBUG=true - APP_DEBUG=true
+4
View File
@@ -21,6 +21,9 @@ services:
- /etc/letsencrypt:/etc/letsencrypt:ro - /etc/letsencrypt:/etc/letsencrypt:ro
- ssl-certs:/etc/nginx/ssl - ssl-certs:/etc/nginx/ssl
environment: environment:
# Zona horaria Colombia
- TZ=America/Bogota
# Entorno # Entorno
- APP_ENV=production - APP_ENV=production
- APP_DEBUG=false - APP_DEBUG=false
@@ -95,6 +98,7 @@ services:
volumes: volumes:
- ./logs:/var/www/html/logs - ./logs:/var/www/html/logs
environment: environment:
- TZ=America/Bogota
- APP_ENV=production - APP_ENV=production
- APP_DEBUG=false - APP_DEBUG=false
- DB_HOST=${DB_HOST:-77.37.126.126} - DB_HOST=${DB_HOST:-77.37.126.126}
+4 -1
View File
@@ -4,7 +4,10 @@
* Verifica que todos los servicios estén funcionando * Verifica que todos los servicios estén funcionando
*/ */
require_once __DIR__ . '/vendor/autoload.php'; // Cargar autoload solo si existe (en desarrollo podría no estar)
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
require_once __DIR__ . '/vendor/autoload.php';
}
header('Content-Type: application/json'); header('Content-Type: application/json');