diff --git a/CHANGELOG.md b/CHANGELOG.md index 8edec3a0..059dd781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 1.5.0 (homolog) +# 1.5.0 (2023-08-18 12:47) ### Feature @@ -10,6 +10,7 @@ * Added rabbitmq to send events * Added Typebot integration * Added proxy endpoint +* Added send and date_time in webhook data ### Fixed @@ -19,6 +20,7 @@ * Update Dockerfile * If you pass empty events in create instance and set webhook it is understood as all * Fixed issue that did not output base64 averages +* Messages sent by the api now arrive in chatwoot ### Integrations diff --git a/src/libs/amqp.server.ts b/src/libs/amqp.server.ts index 86819c80..cc0f13b5 100644 --- a/src/libs/amqp.server.ts +++ b/src/libs/amqp.server.ts @@ -24,7 +24,11 @@ export const initAMQP = () => { const exchangeName = 'evolution_exchange'; - channel.assertExchange(exchangeName, 'topic', { durable: false }); + channel.assertExchange(exchangeName, 'topic', { + durable: true, + autoDelete: false, + }); + amqpChannel = channel; logger.info('AMQP initialized'); diff --git a/src/main.ts b/src/main.ts index adc868af..75dd95b3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,7 +27,9 @@ function bootstrap() { cors({ origin(requestOrigin, callback) { const { ORIGIN } = configService.get('CORS'); - !requestOrigin ? (requestOrigin = '*') : undefined; + if (ORIGIN.includes('*')) { + return callback(null, true); + } if (ORIGIN.indexOf(requestOrigin) !== -1) { return callback(null, true); } diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 81a61127..bce05660 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -1001,6 +1001,18 @@ export const typebotStatusSchema: JSONSchema7 = { ...isNotEmpty('remoteJid', 'status'), }; +export const typebotStartSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + remoteJid: { type: 'string' }, + url: { type: 'string' }, + typebot: { type: 'string' }, + }, + required: ['remoteJid', 'url', 'typebot'], + ...isNotEmpty('remoteJid', 'url', 'typebot'), +}; + export const proxySchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/controllers/typebot.controller.ts b/src/whatsapp/controllers/typebot.controller.ts index 0cda7d57..53dc967f 100644 --- a/src/whatsapp/controllers/typebot.controller.ts +++ b/src/whatsapp/controllers/typebot.controller.ts @@ -38,4 +38,9 @@ export class TypebotController { logger.verbose('requested changeStatus from ' + instance.instanceName + ' instance'); return this.typebotService.changeStatus(instance, data); } + + public async startTypebot(instance: InstanceDto, data: any) { + logger.verbose('requested startTypebot from ' + instance.instanceName + ' instance'); + return this.typebotService.startTypebot(instance, data); + } } diff --git a/src/whatsapp/routers/typebot.router.ts b/src/whatsapp/routers/typebot.router.ts index 68bb6395..0f785de3 100644 --- a/src/whatsapp/routers/typebot.router.ts +++ b/src/whatsapp/routers/typebot.router.ts @@ -1,7 +1,12 @@ import { RequestHandler, Router } from 'express'; import { Logger } from '../../config/logger.config'; -import { instanceNameSchema, typebotSchema, typebotStatusSchema } from '../../validate/validate.schema'; +import { + instanceNameSchema, + typebotSchema, + typebotStartSchema, + typebotStatusSchema, +} from '../../validate/validate.schema'; import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto } from '../dto/instance.dto'; import { TypebotDto } from '../dto/typebot.dto'; @@ -47,7 +52,7 @@ export class TypebotRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) .post(this.routerPath('changeStatus'), ...guards, async (req, res) => { - logger.verbose('request received in findTypebot'); + logger.verbose('request received in changeStatusTypebot'); logger.verbose('request body: '); logger.verbose(req.body); @@ -60,6 +65,22 @@ export class TypebotRouter extends RouterBroker { execute: (instance, data) => typebotController.changeStatus(instance, data), }); + res.status(HttpStatus.OK).json(response); + }) + .post(this.routerPath('start'), ...guards, async (req, res) => { + logger.verbose('request received in startTypebot'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: typebotStartSchema, + ClassRef: InstanceDto, + execute: (instance, data) => typebotController.startTypebot(instance, data), + }); + res.status(HttpStatus.OK).json(response); }); } diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index ce307509..1128bb0d 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -84,6 +84,48 @@ export class TypebotService { return { typebot: { ...instance, typebot: typebotData } }; } + public async startTypebot(instance: InstanceDto, data: any) { + const remoteJid = data.remoteJid; + const url = data.url; + const typebot = data.typebot; + + const id = Math.floor(Math.random() * 10000000000).toString(); + + const reqData = { + sessionId: id, + startParams: { + typebot: data.typebot, + prefilledVariables: { + remoteJid: data.remoteJid, + pushName: data.pushName, + instanceName: instance.instanceName, + }, + }, + }; + console.log(reqData); + + const request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + + await this.sendWAMessage( + instance, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); + + return { + typebot: { + ...instance, + typebot: { + url: url, + remoteJid: remoteJid, + typebot: typebot, + }, + }, + }; + } + private getTypeMessage(msg: any) { this.logger.verbose('get type message'); @@ -162,13 +204,34 @@ export class TypebotService { return request.data; } - public async sendWAMessage(instance: InstanceDto, remoteJid: string, messages: any[], input: any[]) { - processMessages(this.waMonitor.waInstances[instance.instanceName], messages, input).catch((err) => { - console.error('Erro ao processar mensagens:', err); - }); + public async sendWAMessage( + instance: InstanceDto, + remoteJid: string, + messages: any[], + input: any[], + clientSideActions: any[], + ) { + processMessages(this.waMonitor.waInstances[instance.instanceName], messages, input, clientSideActions).catch( + (err) => { + console.error('Erro ao processar mensagens:', err); + }, + ); - async function processMessages(instance, messages, input) { + function findItemAndGetSecondsToWait(array, targetId) { + if (!array) return null; + + for (const item of array) { + if (item.lastBubbleBlockId === targetId) { + return item.wait?.secondsToWaitFor; + } + } + return null; + } + + async function processMessages(instance, messages, input, clientSideActions) { for (const message of messages) { + const wait = findItemAndGetSecondsToWait(clientSideActions, message.id); + if (message.type === 'text') { let formattedText = ''; @@ -209,7 +272,7 @@ export class TypebotService { await instance.textMessage({ number: remoteJid.split('@')[0], options: { - delay: instance.localTypebot.delay_message || 1000, + delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000, presence: 'composing', linkPreview: linkPreview, }, @@ -223,7 +286,7 @@ export class TypebotService { await instance.mediaMessage({ number: remoteJid.split('@')[0], options: { - delay: instance.localTypebot.delay_message || 1000, + delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000, presence: 'composing', }, mediaMessage: { @@ -237,7 +300,7 @@ export class TypebotService { await instance.mediaMessage({ number: remoteJid.split('@')[0], options: { - delay: instance.localTypebot.delay_message || 1000, + delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000, presence: 'composing', }, mediaMessage: { @@ -251,7 +314,7 @@ export class TypebotService { await instance.audioWhatsapp({ number: remoteJid.split('@')[0], options: { - delay: instance.localTypebot.delay_message || 1000, + delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000, presence: 'recording', encoding: true, }, @@ -323,7 +386,7 @@ export class TypebotService { pushName: msg.pushName, }); - await this.sendWAMessage(instance, remoteJid, data.messages, data.input); + await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions); return; } @@ -346,7 +409,7 @@ export class TypebotService { pushName: msg.pushName, }); - await this.sendWAMessage(instance, remoteJid, data.messages, data.input); + await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions); return; } @@ -414,7 +477,13 @@ export class TypebotService { const request = await axios.post(url + '/api/v1/sendMessage', reqData); - await this.sendWAMessage(instance, remoteJid, request.data.messages, request.data.input); + await this.sendWAMessage( + instance, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); return; } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index b20da03b..ebc5e377 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -588,6 +588,9 @@ export class WAStartupService { const serverUrl = this.configService.get('SERVER').URL; const we = event.replace(/[.-]/gm, '_').toUpperCase(); const transformedWe = we.replace(/_/gm, '-').toLowerCase(); + const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds + const localISOTime = new Date(Date.now() - tzoffset).toISOString(); + const now = localISOTime; const expose = this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES; const tokenStore = await this.repository.auth.find(this.instanceName); @@ -598,13 +601,22 @@ export class WAStartupService { if (amqp) { if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) { - const exchangeName = 'evolution_exchange'; + const exchangeName = this.instanceName ?? 'evolution_exchange'; - amqp.assertExchange(exchangeName, 'topic', { durable: false }); + amqp.assertExchange(exchangeName, 'topic', { + durable: true, + autoDelete: false, + }); const queueName = `${this.instanceName}.${event}`; - amqp.assertQueue(queueName, { durable: false }); + amqp.assertQueue(queueName, { + durable: true, + autoDelete: false, + arguments: { + 'x-queue-type': 'quorum', + }, + }); amqp.bindQueue(queueName, exchangeName, event); @@ -613,6 +625,8 @@ export class WAStartupService { instance: this.instance.name, data, server_url: serverUrl, + date_time: now, + sender: this.wuid, }; if (expose && instanceApikey) { @@ -635,6 +649,8 @@ export class WAStartupService { instance: this.instance.name, data, server_url: serverUrl, + date_time: now, + sender: this.wuid, }; if (expose && instanceApikey) { @@ -667,6 +683,8 @@ export class WAStartupService { instance: this.instance.name, data, destination: this.localWebhook.url, + date_time: now, + sender: this.wuid, server_url: serverUrl, apikey: (expose && instanceApikey) || null, }; @@ -686,6 +704,8 @@ export class WAStartupService { instance: this.instance.name, data, destination: this.localWebhook.url, + date_time: now, + sender: this.wuid, server_url: serverUrl, }; @@ -735,6 +755,8 @@ export class WAStartupService { instance: this.instance.name, data, destination: localUrl, + date_time: now, + sender: this.wuid, server_url: serverUrl, }; @@ -753,6 +775,8 @@ export class WAStartupService { instance: this.instance.name, data, destination: localUrl, + date_time: now, + sender: this.wuid, server_url: serverUrl, }; @@ -1976,13 +2000,9 @@ export class WAStartupService { this.logger.verbose('Sending data to webhook in event SEND_MESSAGE'); await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw); - // if (this.localChatwoot.enabled) { - // this.chatwootService.eventWhatsapp( - // Events.SEND_MESSAGE, - // { instanceName: this.instance.name }, - // messageRaw, - // ); - // } + if (this.localChatwoot.enabled) { + this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw); + } this.logger.verbose('Inserting message in database'); await this.repository.message.insert(