"use strict"; 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var WhatsAppService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.WhatsAppService = void 0; const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); const axios_1 = __importDefault(require("axios")); const credentials_provider_service_1 = require("../common/credentials-provider.service"); let WhatsAppService = WhatsAppService_1 = class WhatsAppService { constructor(configService, credentialsProvider) { this.configService = configService; this.credentialsProvider = credentialsProvider; this.logger = new common_1.Logger(WhatsAppService_1.name); this.clientCache = new Map(); } getClient(credentials) { const cacheKey = credentials.isFromPlatform ? 'platform' : `tenant:${credentials.tenantId}`; if (!this.clientCache.has(cacheKey)) { const client = axios_1.default.create({ baseURL: 'https://graph.facebook.com/v18.0', headers: { Authorization: `Bearer ${credentials.accessToken}`, 'Content-Type': 'application/json', }, }); this.clientCache.set(cacheKey, client); } return this.clientCache.get(cacheKey); } async getPhoneNumberId(tenantId) { const credentials = await this.credentialsProvider.getWhatsAppCredentials(tenantId); return credentials.phoneNumberId; } invalidateClientCache(tenantId) { const cacheKey = tenantId ? `tenant:${tenantId}` : 'platform'; this.clientCache.delete(cacheKey); if (tenantId) { this.credentialsProvider.invalidateCache(tenantId); } } async sendTextMessage(to, text, tenantId) { const message = { messaging_product: 'whatsapp', recipient_type: 'individual', to: this.formatPhoneNumber(to), type: 'text', text: { body: text }, }; return this.sendMessage(message, tenantId); } async sendInteractiveButtons(to, bodyText, buttons, headerText, footerText, tenantId) { const message = { messaging_product: 'whatsapp', recipient_type: 'individual', to: this.formatPhoneNumber(to), type: 'interactive', interactive: { type: 'button', header: headerText ? { type: 'text', text: headerText } : undefined, body: { text: bodyText }, footer: footerText ? { text: footerText } : undefined, action: { buttons: buttons.slice(0, 3).map((btn) => ({ type: 'reply', reply: { id: btn.id, title: btn.title.substring(0, 20) }, })), }, }, }; return this.sendMessage(message, tenantId); } async sendInteractiveList(to, bodyText, buttonText, sections, headerText, footerText, tenantId) { const message = { messaging_product: 'whatsapp', recipient_type: 'individual', to: this.formatPhoneNumber(to), type: 'interactive', interactive: { type: 'list', header: headerText ? { type: 'text', text: headerText } : undefined, body: { text: bodyText }, footer: footerText ? { text: footerText } : undefined, action: { button: buttonText.substring(0, 20), sections: sections.map((section) => ({ title: section.title?.substring(0, 24), rows: section.rows.slice(0, 10).map((row) => ({ id: row.id.substring(0, 200), title: row.title.substring(0, 24), description: row.description?.substring(0, 72), })), })), }, }, }; return this.sendMessage(message, tenantId); } async sendTemplate(to, templateName, languageCode = 'es_MX', components, tenantId) { const message = { messaging_product: 'whatsapp', recipient_type: 'individual', to: this.formatPhoneNumber(to), type: 'template', template: { name: templateName, language: { code: languageCode }, components, }, }; return this.sendMessage(message, tenantId); } async sendImage(to, imageUrl, caption, tenantId) { const message = { messaging_product: 'whatsapp', recipient_type: 'individual', to: this.formatPhoneNumber(to), type: 'image', image: { link: imageUrl, caption, }, }; return this.sendMessage(message, tenantId); } async sendDocument(to, documentUrl, filename, caption, tenantId) { const message = { messaging_product: 'whatsapp', recipient_type: 'individual', to: this.formatPhoneNumber(to), type: 'document', document: { link: documentUrl, filename, caption, }, }; return this.sendMessage(message, tenantId); } async downloadMedia(mediaId, tenantId) { try { const credentials = await this.credentialsProvider.getWhatsAppCredentials(tenantId); const client = this.getClient(credentials); const { data: mediaInfo } = await client.get(`/${mediaId}`); const { data: mediaBuffer } = await axios_1.default.get(mediaInfo.url, { headers: { Authorization: `Bearer ${credentials.accessToken}`, }, responseType: 'arraybuffer', }); return Buffer.from(mediaBuffer); } catch (error) { this.logger.error(`Error downloading media: ${error.message}`); throw error; } } async sendMessage(message, tenantId) { try { const credentials = await this.credentialsProvider.getWhatsAppCredentials(tenantId); const client = this.getClient(credentials); const { data } = await client.post(`/${credentials.phoneNumberId}/messages`, message); const source = credentials.isFromPlatform ? 'platform' : `tenant:${tenantId}`; this.logger.log(`Message sent to ${message.to} via ${source}: ${data.messages[0].id}`); return data.messages[0].id; } catch (error) { this.logger.error(`Error sending message: ${error.response?.data || error.message}`); throw error; } } formatPhoneNumber(phone) { let cleaned = phone.replace(/\D/g, ''); if (cleaned.length === 10) { cleaned = '52' + cleaned; } return cleaned; } async sendOrderConfirmation(to, orderNumber, items, total, tenantId) { const itemsList = items .map((item) => `• ${item.quantity}x ${item.name} - $${item.price.toFixed(2)}`) .join('\n'); const message = `*Pedido Confirmado* 🎉 Número: *${orderNumber}* ${itemsList} *Total: $${total.toFixed(2)}* Te avisaremos cuando esté listo.`; return this.sendTextMessage(to, message, tenantId); } async sendOrderStatusUpdate(to, orderNumber, status, tenantId) { const statusMessages = { preparing: `*Preparando tu pedido* 👨‍🍳\n\nPedido: ${orderNumber}\n\nEstamos trabajando en tu orden.`, ready: `*¡Pedido listo!* ✅\n\nPedido: ${orderNumber}\n\nTu pedido está listo para recoger.`, delivered: `*Pedido entregado* 📦\n\nPedido: ${orderNumber}\n\n¡Gracias por tu compra!`, }; return this.sendTextMessage(to, statusMessages[status], tenantId); } async sendFiadoReminder(to, customerName, amount, dueDate, tenantId) { let message = `Hola ${customerName} 👋 Te recordamos que tienes un saldo pendiente de *$${amount.toFixed(2)}*`; if (dueDate) { message += ` con vencimiento el ${dueDate}`; } message += `.\n\n¿Deseas abonar o consultar tu estado de cuenta?`; return this.sendInteractiveButtons(to, message, [ { id: 'pay_fiado', title: 'Quiero pagar' }, { id: 'check_balance', title: 'Ver mi cuenta' }, ], undefined, undefined, tenantId); } async sendLowStockAlert(to, products, tenantId) { const productList = products .map((p) => `• ${p.name}: ${p.stock} unidades`) .join('\n'); const message = `*⚠️ Alerta de Stock Bajo* Los siguientes productos necesitan reabastecimiento: ${productList} ¿Deseas hacer un pedido al proveedor?`; return this.sendTextMessage(to, message, tenantId); } }; exports.WhatsAppService = WhatsAppService; exports.WhatsAppService = WhatsAppService = WhatsAppService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [config_1.ConfigService, credentials_provider_service_1.CredentialsProviderService]) ], WhatsAppService); //# sourceMappingURL=whatsapp.service.js.map