216 lines
No EOL
9.9 KiB
JavaScript
216 lines
No EOL
9.9 KiB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
};
|
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
};
|
|
var WorkerService_1;
|
|
import { Injectable, Logger } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { HttpService } from '@nestjs/axios';
|
|
import { firstValueFrom } from 'rxjs';
|
|
let WorkerService = WorkerService_1 = class WorkerService {
|
|
config;
|
|
http;
|
|
logger = new Logger(WorkerService_1.name);
|
|
isRunning = true;
|
|
apiUrl;
|
|
workerKey;
|
|
openwaUrl;
|
|
openwaApiKey;
|
|
intervalMs;
|
|
constructor(config, http) {
|
|
this.config = config;
|
|
this.http = http;
|
|
this.apiUrl = this.config.get('API_URL') || 'http://localhost:3000/v1';
|
|
this.workerKey = this.config.get('WORKER_KEY') || '';
|
|
this.openwaUrl = this.config.get('OPENWA_URL') || 'http://localhost:2785';
|
|
this.openwaApiKey = this.config.get('OPENWA_API_KEY') || '';
|
|
this.intervalMs = Number(this.config.get('WORKER_INTERVAL')) || 30000;
|
|
}
|
|
onModuleInit() {
|
|
this.logger.log(`Worker iniciado. Intervalo entre mensajes: ${this.intervalMs}ms`);
|
|
this.procesarCiclo();
|
|
}
|
|
onModuleDestroy() {
|
|
this.isRunning = false;
|
|
this.logger.log('Worker detenido');
|
|
}
|
|
timestamp() {
|
|
return new Date().toLocaleTimeString('es-BO', { hour12: false });
|
|
}
|
|
async procesarCiclo() {
|
|
if (!this.isRunning)
|
|
return;
|
|
await this.procesar();
|
|
const delayMs = this.calcularDelayEntreMensajes();
|
|
const delaySeg = (delayMs / 1000).toFixed(1);
|
|
this.logger.log(`[${this.timestamp()}] ⏳ Esperando ${delaySeg}s antes del siguiente mensaje...`);
|
|
await this.delay(delayMs);
|
|
setTimeout(() => this.procesarCiclo(), 0);
|
|
}
|
|
async procesar() {
|
|
const mensaje = await this.obtenerPendiente();
|
|
if (!mensaje) {
|
|
this.logger.log(`[${this.timestamp()}] No hay mensajes pendientes`);
|
|
return;
|
|
}
|
|
await this.procesarMensaje(mensaje);
|
|
}
|
|
async obtenerPendiente() {
|
|
try {
|
|
const response = await firstValueFrom(this.http.get(`${this.apiUrl}/mensaje/pendientes`, {
|
|
params: { limit: 1 },
|
|
headers: { 'x-worker-key': this.workerKey },
|
|
}));
|
|
const data = response.data?.data;
|
|
return data && data.length > 0 ? data[0] : null;
|
|
}
|
|
catch (error) {
|
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
this.logger.error(`[${this.timestamp()}] Error obteniendo mensaje pendiente: ${message}`);
|
|
return null;
|
|
}
|
|
}
|
|
async procesarMensaje(mensaje) {
|
|
const { idMensajeWhatsApp, telefonoDestino, sessionId, tipo, mensaje1, mensaje2, mensaje3, urlQR } = mensaje;
|
|
const inicio = Date.now();
|
|
this.logger.log(`[${this.timestamp()}] ▶ Procesando mensaje ${idMensajeWhatsApp} → ${telefonoDestino} (${tipo})`);
|
|
try {
|
|
if (!sessionId) {
|
|
await this.actualizarEstado(idMensajeWhatsApp, 'ERROR', 'No sessionId configurado');
|
|
return;
|
|
}
|
|
const chatId = `${telefonoDestino}@c.us`;
|
|
const mensajes = [mensaje1, mensaje2, mensaje3].filter((m) => Boolean(m));
|
|
if (mensajes.length > 0) {
|
|
const texto1 = mensajes[0];
|
|
if (tipo === 'RECEPCION_PAQUETE' && urlQR) {
|
|
await this.enviarImagen(sessionId, chatId, urlQR, texto1);
|
|
this.logger.log(`[${this.timestamp()}] 📷✉️ Mensaje 1 (QR + imagen) enviado INMEDIATO`);
|
|
}
|
|
else {
|
|
await this.enviarTexto(sessionId, chatId, texto1);
|
|
this.logger.log(`[${this.timestamp()}] ✉️ Mensaje 1 enviado INMEDIATO`);
|
|
}
|
|
}
|
|
const texto1ParaDelay = mensajes[0] || '';
|
|
const delay1 = this.calcularDelayEscritura(texto1ParaDelay.length);
|
|
const delay1Seg = (delay1 / 1000).toFixed(1);
|
|
this.logger.log(`[${this.timestamp()}] ⏳ Delay1 calculado: ${delay1Seg}s (para usar después)`);
|
|
for (let i = 1; i < mensajes.length; i++) {
|
|
const texto = mensajes[i];
|
|
const delayMsg = this.calcularDelayEscritura(texto.length);
|
|
const delayMsgSeg = (delayMsg / 1000).toFixed(1);
|
|
await this.setTyping(sessionId, chatId);
|
|
this.logger.log(`[${this.timestamp()}] ⌨️ Typing mensaje ${i + 1}...`);
|
|
this.logger.log(`[${this.timestamp()}] ⏳ Esperando ${delayMsgSeg}s (${texto.length} chars)`);
|
|
await this.delay(delayMsg);
|
|
await this.enviarTexto(sessionId, chatId, texto);
|
|
this.logger.log(`[${this.timestamp()}] ✉️ Mensaje ${i + 1} enviado`);
|
|
if (i < mensajes.length - 1) {
|
|
const delayEntre = this.calcularDelayEntreMensajes();
|
|
const delayEntreSeg = (delayEntre / 1000).toFixed(1);
|
|
this.logger.log(`[${this.timestamp()}] ⏳ Esperando ${delayEntreSeg}s antes del siguiente mensaje...`);
|
|
await this.delay(delayEntre);
|
|
}
|
|
}
|
|
this.logger.log(`[${this.timestamp()}] ⏳ Esperando delay1: ${delay1Seg}s antes de continuar...`);
|
|
await this.delay(delay1);
|
|
await this.actualizarEstado(idMensajeWhatsApp, 'ENVIADO');
|
|
const totalSeg = ((Date.now() - inicio) / 1000).toFixed(1);
|
|
this.logger.log(`[${this.timestamp()}] ✅ Mensaje ${idMensajeWhatsApp} ENVIADO (${mensajes.length} mensajes) → tiempo total: ${totalSeg}s`);
|
|
}
|
|
catch (error) {
|
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
const responseMessage = error?.response?.data?.message || error?.response?.data?.error || '';
|
|
const fullError = responseMessage ? `${message} → ${responseMessage}` : message;
|
|
await this.actualizarEstado(idMensajeWhatsApp, 'ERROR', fullError);
|
|
const totalSeg = ((Date.now() - inicio) / 1000).toFixed(1);
|
|
this.logger.error(`[${this.timestamp()}] ❌ Error en ${idMensajeWhatsApp}: ${fullError} (${totalSeg}s)`);
|
|
}
|
|
}
|
|
calcularDelayEscritura(caracteres) {
|
|
const caracteresPorMinuto = 200;
|
|
const base = (caracteres / caracteresPorMinuto) * 60 * 1000;
|
|
const random = 1 + (Math.random() * 0.6 - 0.3);
|
|
const reaccion = 500 + Math.random() * 1000;
|
|
const delay = base * random + reaccion;
|
|
return Math.min(Math.max(delay, 3000), 120000);
|
|
}
|
|
calcularDelayEntreMensajes() {
|
|
const base = 3000;
|
|
const random = 1 + (Math.random() * 0.6 - 0.3);
|
|
return Math.min(Math.max(base * random, 2000), 8000);
|
|
}
|
|
delay(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
async setTyping(sessionId, chatId) {
|
|
try {
|
|
await firstValueFrom(this.http.post(`${this.openwaUrl}/api/sessions/${sessionId}/chat-state`, { chatId, state: 'composing' }, {
|
|
headers: {
|
|
'X-API-Key': this.openwaApiKey,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}));
|
|
}
|
|
catch {
|
|
}
|
|
}
|
|
async enviarTexto(sessionId, chatId, text) {
|
|
const response = await firstValueFrom(this.http.post(`${this.openwaUrl}/api/sessions/${sessionId}/messages/send-text`, { chatId, text }, {
|
|
headers: {
|
|
'X-API-Key': this.openwaApiKey,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}));
|
|
return response.data;
|
|
}
|
|
async enviarImagen(sessionId, chatId, imageUrl, caption) {
|
|
const fullUrl = imageUrl.startsWith('http')
|
|
? imageUrl
|
|
: `http://localhost:3000${imageUrl}`;
|
|
const response = await firstValueFrom(this.http.get(fullUrl, { responseType: 'arraybuffer' }));
|
|
const base64Data = Buffer.from(response.data).toString('base64');
|
|
const dataUrl = `data:image/png;base64,${base64Data}`;
|
|
const result = await firstValueFrom(this.http.post(`${this.openwaUrl}/api/sessions/${sessionId}/messages/send-image`, {
|
|
chatId,
|
|
base64: dataUrl,
|
|
caption,
|
|
mimetype: 'image/png',
|
|
filename: 'qr.png',
|
|
}, {
|
|
headers: {
|
|
'X-API-Key': this.openwaApiKey,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}));
|
|
return result.data;
|
|
}
|
|
async actualizarEstado(id, estado, error) {
|
|
try {
|
|
await firstValueFrom(this.http.put(`${this.apiUrl}/mensaje/${id}/estado`, {
|
|
estado,
|
|
error: error || null,
|
|
fechaEnvio: new Date().toISOString(),
|
|
}, {
|
|
headers: { 'x-worker-key': this.workerKey },
|
|
}));
|
|
}
|
|
catch (err) {
|
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
this.logger.error(`[${this.timestamp()}] Error actualizando estado del mensaje ${id}: ${message}`);
|
|
}
|
|
}
|
|
};
|
|
WorkerService = WorkerService_1 = __decorate([
|
|
Injectable(),
|
|
__metadata("design:paramtypes", [ConfigService,
|
|
HttpService])
|
|
], WorkerService);
|
|
export { WorkerService };
|
|
//# sourceMappingURL=worker.service.js.map
|