"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 WebhookService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebhookService = void 0; const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); const whatsapp_service_1 = require("../whatsapp/whatsapp.service"); const llm_service_1 = require("../llm/llm.service"); const credentials_provider_service_1 = require("../common/credentials-provider.service"); let WebhookService = WebhookService_1 = class WebhookService { constructor(configService, whatsAppService, llmService, credentialsProvider) { this.configService = configService; this.whatsAppService = whatsAppService; this.llmService = llmService; this.credentialsProvider = credentialsProvider; this.logger = new common_1.Logger(WebhookService_1.name); this.conversations = new Map(); } verifyWebhook(mode, token, challenge) { const verifyToken = this.configService.get('WHATSAPP_VERIFY_TOKEN'); if (mode === 'subscribe' && token === verifyToken) { this.logger.log('Webhook verified successfully'); return challenge; } this.logger.warn('Webhook verification failed'); return null; } async processIncomingMessage(message, contact, phoneNumberId) { const phoneNumber = message.from; const customerName = contact.profile.name; const tenantId = await this.credentialsProvider.resolveTenantFromPhoneNumberId(phoneNumberId); const source = tenantId ? `tenant:${tenantId}` : 'platform'; this.logger.log(`Processing message from ${customerName} (${phoneNumber}) via ${source}: ${message.type}`); let context = this.conversations.get(phoneNumber); if (!context) { context = { customerName, phoneNumber, lastActivity: new Date(), cart: [], tenantId, businessName: tenantId ? undefined : 'MiChangarrito', }; this.conversations.set(phoneNumber, context); } context.lastActivity = new Date(); context.tenantId = tenantId; try { switch (message.type) { case 'text': await this.handleTextMessage(phoneNumber, message.text?.body || '', context); break; case 'interactive': await this.handleInteractiveMessage(phoneNumber, message, context); break; case 'image': await this.handleImageMessage(phoneNumber, message, context); break; case 'audio': await this.handleAudioMessage(phoneNumber, message, context); break; case 'location': await this.handleLocationMessage(phoneNumber, message, context); break; default: await this.whatsAppService.sendTextMessage(phoneNumber, 'Lo siento, no puedo procesar ese tipo de mensaje. Puedes escribirme texto o usar los botones.', context.tenantId); } } catch (error) { this.logger.error(`Error processing message: ${error.message}`); await this.whatsAppService.sendTextMessage(phoneNumber, 'Disculpa, hubo un problema procesando tu mensaje. Por favor intenta de nuevo.', context.tenantId); } } async handleTextMessage(phoneNumber, text, context) { const lowerText = text.toLowerCase().trim(); if (this.isGreeting(lowerText)) { await this.sendWelcomeMessage(phoneNumber, context.customerName, context); return; } if (lowerText.includes('menu') || lowerText.includes('productos')) { await this.sendMainMenu(phoneNumber, context); return; } if (lowerText.includes('ayuda') || lowerText.includes('help')) { await this.sendHelpMessage(phoneNumber, context); return; } if (lowerText.includes('pedido') || lowerText.includes('orden')) { await this.sendOrderStatus(phoneNumber, context); return; } if (lowerText.includes('fiado') || lowerText.includes('cuenta') || lowerText.includes('deuda')) { await this.sendFiadoInfo(phoneNumber, context); return; } const response = await this.llmService.processMessage(text, context); if (response.action) { await this.executeAction(phoneNumber, response.action, response.data, context); } else { await this.whatsAppService.sendTextMessage(phoneNumber, response.message, context.tenantId); } } async handleInteractiveMessage(phoneNumber, message, context) { const interactive = message.interactive; if (interactive?.button_reply) { const buttonId = interactive.button_reply.id; await this.handleButtonResponse(phoneNumber, buttonId, context); } else if (interactive?.list_reply) { const listId = interactive.list_reply.id; await this.handleListResponse(phoneNumber, listId, context); } } async handleImageMessage(phoneNumber, message, context) { await this.whatsAppService.sendTextMessage(phoneNumber, 'Gracias por la imagen. Esta funcionalidad estara disponible pronto.', context.tenantId); } async handleAudioMessage(phoneNumber, message, context) { await this.whatsAppService.sendTextMessage(phoneNumber, 'Gracias por el audio. La transcripcion estara disponible pronto.', context.tenantId); } async handleLocationMessage(phoneNumber, message, context) { const location = message.location; if (location) { await this.whatsAppService.sendTextMessage(phoneNumber, `Ubicacion recibida. Coordenadas: ${location.latitude}, ${location.longitude}`, context.tenantId); } } async handleButtonResponse(phoneNumber, buttonId, context) { this.logger.log(`Button response: ${buttonId}`); switch (buttonId) { case 'menu_products': await this.sendProductCategories(phoneNumber, context); break; case 'menu_orders': await this.sendOrderStatus(phoneNumber, context); break; case 'menu_fiado': await this.sendFiadoInfo(phoneNumber, context); break; case 'pay_fiado': await this.sendPaymentOptions(phoneNumber, context); break; case 'check_balance': await this.sendFiadoBalance(phoneNumber, context); break; case 'confirm_order': await this.confirmOrder(phoneNumber, context); break; case 'cancel_order': await this.cancelOrder(phoneNumber, context); break; default: await this.whatsAppService.sendTextMessage(phoneNumber, 'Opcion no reconocida. Escribe "menu" para ver las opciones disponibles.', context.tenantId); } } async handleListResponse(phoneNumber, listId, context) { this.logger.log(`List response: ${listId}`); if (listId.startsWith('cat_')) { const categoryId = listId.replace('cat_', ''); await this.sendCategoryProducts(phoneNumber, categoryId, context); } else if (listId.startsWith('prod_')) { const productId = listId.replace('prod_', ''); await this.addToCart(phoneNumber, productId, context); } } async sendWelcomeMessage(phoneNumber, customerName, context) { const businessName = context.businessName || 'MiChangarrito'; const message = `Hola ${customerName}! Bienvenido a ${businessName}. Soy tu asistente virtual. Puedo ayudarte con: - Ver productos disponibles - Hacer pedidos - Consultar tu cuenta de fiado - Revisar el estado de tus pedidos Como puedo ayudarte hoy?`; await this.whatsAppService.sendInteractiveButtons(phoneNumber, message, [ { id: 'menu_products', title: 'Ver productos' }, { id: 'menu_orders', title: 'Mis pedidos' }, { id: 'menu_fiado', title: 'Mi fiado' }, ], undefined, undefined, context.tenantId); } async sendMainMenu(phoneNumber, context) { await this.whatsAppService.sendInteractiveButtons(phoneNumber, 'Que te gustaria hacer?', [ { id: 'menu_products', title: 'Ver productos' }, { id: 'menu_orders', title: 'Mis pedidos' }, { id: 'menu_fiado', title: 'Mi fiado' }, ], 'Menu Principal', undefined, context.tenantId); } async sendHelpMessage(phoneNumber, context) { const helpText = `*Comandos disponibles:* - *menu* - Ver opciones principales - *productos* - Ver catalogo - *pedido* - Estado de tu pedido - *fiado* - Tu cuenta de fiado - *ayuda* - Este mensaje Tambien puedes escribirme de forma natural y tratare de entenderte!`; await this.whatsAppService.sendTextMessage(phoneNumber, helpText, context.tenantId); } async sendProductCategories(phoneNumber, context) { const categories = [ { id: 'cat_bebidas', title: 'Bebidas', description: 'Refrescos, aguas, jugos' }, { id: 'cat_botanas', title: 'Botanas', description: 'Papas, cacahuates, dulces' }, { id: 'cat_abarrotes', title: 'Abarrotes', description: 'Productos basicos' }, { id: 'cat_lacteos', title: 'Lacteos', description: 'Leche, queso, yogurt' }, ]; await this.whatsAppService.sendInteractiveList(phoneNumber, 'Selecciona una categoria para ver los productos disponibles:', 'Ver categorias', [{ title: 'Categorias', rows: categories }], 'Catalogo', undefined, context.tenantId); } async sendCategoryProducts(phoneNumber, categoryId, context) { const products = [ { id: 'prod_1', title: 'Coca-Cola 600ml', description: '$18.00' }, { id: 'prod_2', title: 'Pepsi 600ml', description: '$17.00' }, { id: 'prod_3', title: 'Agua natural 1L', description: '$12.00' }, ]; await this.whatsAppService.sendInteractiveList(phoneNumber, 'Selecciona un producto para agregarlo a tu carrito:', 'Ver productos', [{ title: 'Productos', rows: products }], 'Productos disponibles', undefined, context.tenantId); } async addToCart(phoneNumber, productId, context) { if (!context.cart) { context.cart = []; } context.cart.push({ productId, name: 'Producto de prueba', quantity: 1, price: 18.00, }); const cartTotal = context.cart.reduce((sum, item) => sum + item.price * item.quantity, 0); await this.whatsAppService.sendInteractiveButtons(phoneNumber, `Producto agregado al carrito.\n\nTotal actual: $${cartTotal.toFixed(2)}\nArticulos: ${context.cart.length}`, [ { id: 'menu_products', title: 'Seguir comprando' }, { id: 'confirm_order', title: 'Finalizar pedido' }, ], undefined, undefined, context.tenantId); } async sendOrderStatus(phoneNumber, context) { await this.whatsAppService.sendTextMessage(phoneNumber, 'No tienes pedidos activos en este momento.\n\nEscribe "menu" para hacer un nuevo pedido.', context.tenantId); } async sendFiadoInfo(phoneNumber, context) { await this.whatsAppService.sendInteractiveButtons(phoneNumber, 'Tu cuenta de fiado:\n\nSaldo pendiente: *$0.00*\nLimite de credito: *$500.00*\nCredito disponible: *$500.00*', [ { id: 'pay_fiado', title: 'Hacer pago' }, { id: 'menu_products', title: 'Comprar a fiado' }, ], 'Mi Fiado', undefined, context.tenantId); } async sendFiadoBalance(phoneNumber, context) { await this.whatsAppService.sendTextMessage(phoneNumber, '*Detalle de tu cuenta:*\n\nNo hay movimientos pendientes.', context.tenantId); } async sendPaymentOptions(phoneNumber, context) { await this.whatsAppService.sendTextMessage(phoneNumber, '*Opciones de pago:*\n\n1. Efectivo en tienda\n2. Transferencia bancaria\n\nPara transferencias:\nCLABE: XXXX XXXX XXXX XXXX\nBanco: BBVA\n\nEnvia tu comprobante por este medio.', context.tenantId); } async confirmOrder(phoneNumber, context) { if (!context.cart || context.cart.length === 0) { await this.whatsAppService.sendTextMessage(phoneNumber, 'Tu carrito esta vacio. Escribe "productos" para ver el catalogo.', context.tenantId); return; } const total = context.cart.reduce((sum, item) => sum + item.price * item.quantity, 0); const orderNumber = `MCH-${Date.now().toString(36).toUpperCase()}`; await this.whatsAppService.sendOrderConfirmation(phoneNumber, orderNumber, context.cart, total, context.tenantId); context.cart = []; } async cancelOrder(phoneNumber, context) { context.cart = []; await this.whatsAppService.sendTextMessage(phoneNumber, 'Pedido cancelado. Tu carrito ha sido vaciado.\n\nEscribe "menu" cuando quieras hacer un nuevo pedido.', context.tenantId); } async executeAction(phoneNumber, action, data, context) { switch (action) { case 'show_menu': await this.sendMainMenu(phoneNumber, context); break; case 'show_products': await this.sendProductCategories(phoneNumber, context); break; case 'show_fiado': await this.sendFiadoInfo(phoneNumber, context); break; default: this.logger.warn(`Unknown action: ${action}`); } } processStatusUpdate(status) { this.logger.log(`Message ${status.id} status: ${status.status}`); if (status.status === 'failed' && status.errors) { this.logger.error(`Message failed: ${JSON.stringify(status.errors)}`); } } isGreeting(text) { const greetings = ['hola', 'hi', 'hello', 'buenos dias', 'buenas tardes', 'buenas noches', 'que tal', 'hey']; return greetings.some((g) => text.includes(g)); } }; exports.WebhookService = WebhookService; exports.WebhookService = WebhookService = WebhookService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [config_1.ConfigService, whatsapp_service_1.WhatsAppService, llm_service_1.LlmService, credentials_provider_service_1.CredentialsProviderService]) ], WebhookService); //# sourceMappingURL=webhook.service.js.map