From a82b206fe62114009dd6ce31b56f2451de183105 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 10 Oct 2023 10:00:11 -0300 Subject: [PATCH 01/68] test: messages.upsert --- src/whatsapp/services/whatsapp.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c4d25c88..7493adc3 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1512,11 +1512,11 @@ export class WAStartupService { const received = messages[0]; if ( - type !== 'notify' || - !received?.message || - received.message?.protocolMessage || - received.message.senderKeyDistributionMessage || - received.message?.pollUpdateMessage + type !== 'notify' + // !received?.message || + // received.message?.protocolMessage || + // received.message.senderKeyDistributionMessage || + // received.message?.pollUpdateMessage ) { this.logger.verbose('message rejected'); return; From d8d7debfee2d8de3113797ec9de6375852a57cac Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 11 Oct 2023 17:27:49 -0300 Subject: [PATCH 02/68] fix: lid messages --- package.json | 2 +- src/whatsapp/services/whatsapp.service.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index e00f1e39..17a90ebc 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@sentry/node": "^7.59.2", - "@whiskeysockets/baileys": "^6.5.0", + "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys#fix-lids", "amqplib": "^0.10.3", "axios": "^1.3.5", "class-validator": "^0.13.2", diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 7493adc3..40ce46a4 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1512,11 +1512,11 @@ export class WAStartupService { const received = messages[0]; if ( - type !== 'notify' - // !received?.message || - // received.message?.protocolMessage || - // received.message.senderKeyDistributionMessage || - // received.message?.pollUpdateMessage + type !== 'notify' || + !received?.message || + received.message?.protocolMessage || + received.message.senderKeyDistributionMessage || + received.message?.pollUpdateMessage ) { this.logger.verbose('message rejected'); return; @@ -1934,8 +1934,8 @@ export class WAStartupService { private createJid(number: string): string { this.logger.verbose('Creating jid with number: ' + number); - if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) { - this.logger.verbose('Number already contains @g.us or @s.whatsapp.net'); + if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) { + this.logger.verbose('Number already contains @g.us or @s.whatsapp.net or @lid'); return number; } From e157a2a36b48aadde891e4879287b8bfeed4ea8c Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 18 Oct 2023 18:09:01 -0300 Subject: [PATCH 03/68] fix: adjusts in proxy --- src/whatsapp/services/monitor.service.ts | 4 ++-- src/whatsapp/services/whatsapp.service.ts | 27 +++++++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 1decd13c..40147bf8 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -117,7 +117,7 @@ export class WAMonitoringService { if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { instanceData.instance['serverUrl'] = this.configService.get('SERVER').URL; - instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey; + instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; instanceData.instance['chatwoot'] = chatwoot; } @@ -136,7 +136,7 @@ export class WAMonitoringService { if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { instanceData.instance['serverUrl'] = this.configService.get('SERVER').URL; - instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey; + instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; instanceData.instance['chatwoot'] = chatwoot; } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 40ce46a4..86ac3176 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -128,9 +128,7 @@ import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types' import { waMonitor } from '../whatsapp.module'; import { ChamaaiService } from './chamaai.service'; import { ChatwootService } from './chatwoot.service'; -//import { SocksProxyAgent } from './socks-proxy-agent'; import { TypebotService } from './typebot.service'; - export class WAStartupService { constructor( private readonly configService: ConfigService, @@ -584,7 +582,7 @@ export class WAStartupService { Object.assign(this.localProxy, data); this.logger.verbose('Proxy set'); - this.client?.ws?.close(); + this.reloadConnection(); } public async findProxy() { @@ -1183,10 +1181,22 @@ export class WAStartupService { if (this.localProxy.enabled) { this.logger.verbose('Proxy enabled'); - options = { - agent: new ProxyAgent(this.localProxy.proxy as any), - fetchAgent: new ProxyAgent(this.localProxy.proxy as any), - }; + + if (this.localProxy.proxy.includes('proxyscrape')) { + const response = await axios.get(this.localProxy.proxy); + const text = response.data; + const proxyUrls = text.split('\r\n'); + const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); + const proxyUrl = 'http://' + proxyUrls[rand]; + console.log(proxyUrl); + options = { + agent: new ProxyAgent(proxyUrl as any), + }; + } else { + options = { + agent: new ProxyAgent(this.localProxy.proxy as any), + }; + } } const socketConfig: UserFacingSocketConfig = { @@ -1546,7 +1556,6 @@ export class WAStartupService { reuploadRequest: this.client.updateMediaMessage, }, ); - console.log(buffer); messageRaw = { key: received.key, pushName: received.pushName, @@ -3086,7 +3095,7 @@ export class WAStartupService { await this.client.updateGroupsAddPrivacy(settings.privacySettings.groupadd); this.logger.verbose('Groups add privacy updated'); - this.client?.ws?.close(); + this.reloadConnection(); return { update: 'success', From daadc6cb684b7a087c726671968acda01c1d4ffa Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 18 Oct 2023 18:09:26 -0300 Subject: [PATCH 04/68] fix: adjusts in proxy --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98783e84..add0b1f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.5.5 (develop) + +### Fixed + +* Adjusts in proxy + # 1.5.4 (2023-10-09 20:43) ### Fixed From 23615cff4f21fbd2f2e941b029344a57ce2c4938 Mon Sep 17 00:00:00 2001 From: Jaison Date: Wed, 18 Oct 2023 18:57:11 -0300 Subject: [PATCH 05/68] -> Adjusting function cleaningStoreFiles to remove itens from missing mongo collections; -> AuthModel was using a wrong filter; --- src/whatsapp/services/monitor.service.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 1decd13c..a37d552b 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -12,12 +12,18 @@ import { dbserver } from '../../libs/db.connect'; import { RedisCache } from '../../libs/redis.client'; import { AuthModel, + ChamaaiModel, + ChatModel, ChatwootModel, ContactModel, MessageModel, MessageUpModel, + ProxyModel, + RabbitmqModel, SettingsModel, + TypebotModel, WebhookModel, + WebsocketModel, } from '../models'; import { RepositoryBroker } from '../repository/repository.manager'; import { WAStartupService } from './whatsapp.service'; @@ -233,13 +239,19 @@ export class WAMonitoringService { this.logger.verbose('cleaning store database instance: ' + instanceName); - await AuthModel.deleteMany({ owner: instanceName }); + await ChatModel.deleteMany({ owner: instanceName }); await ContactModel.deleteMany({ owner: instanceName }); - await MessageModel.deleteMany({ owner: instanceName }); await MessageUpModel.deleteMany({ owner: instanceName }); + await MessageModel.deleteMany({ owner: instanceName }); + await AuthModel.deleteMany({ _id: instanceName }); await WebhookModel.deleteMany({ _id: instanceName }); await ChatwootModel.deleteMany({ _id: instanceName }); + await ChamaaiModel.deleteMany({ _id: instanceName }); + await ProxyModel.deleteMany({ _id: instanceName }); + await RabbitmqModel.deleteMany({ _id: instanceName }); + await TypebotModel.deleteMany({ _id: instanceName }); + await WebsocketModel.deleteMany({ _id: instanceName }); await SettingsModel.deleteMany({ _id: instanceName }); return; From 3c3bbc84b341852aff7241cc8d9a37239ef17d94 Mon Sep 17 00:00:00 2001 From: jaison-x Date: Wed, 18 Oct 2023 19:52:33 -0300 Subject: [PATCH 06/68] -> When deleting a instance in connecting state, the event remove.instance was not triggered; --- src/whatsapp/controllers/instance.controller.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 3ead8690..d26c64cd 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -553,15 +553,13 @@ export class InstanceController { this.logger.verbose('logging out instance: ' + instanceName); await this.logout({ instanceName }); - delete this.waMonitor.waInstances[instanceName]; - return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } }; - } else { - this.logger.verbose('deleting instance: ' + instanceName); - - delete this.waMonitor.waInstances[instanceName]; - this.eventEmitter.emit('remove.instance', instanceName, 'inner'); - return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } }; } + + this.logger.verbose('deleting instance: ' + instanceName); + + delete this.waMonitor.waInstances[instanceName]; + this.eventEmitter.emit('remove.instance', instanceName, 'inner'); + return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } }; } catch (error) { throw new BadRequestException(error.toString()); } From 5b0e90e5b91ec23e5af32a580351cc92dfa83fc8 Mon Sep 17 00:00:00 2001 From: Wender Teixeira Date: Thu, 19 Oct 2023 22:41:16 -0300 Subject: [PATCH 07/68] Update whatsapp.service.ts --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 86ac3176..08310cc0 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3311,7 +3311,7 @@ export class WAStartupService { subject: group.subject, subjectOwner: group.subjectOwner, subjectTime: group.subjectTime, - size: group.size, + size: group.participants.length, creation: group.creation, owner: group.owner, desc: group.desc, From cd6cb8182ec1d40a294bf66a79a3d5c4c41ff1d7 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 24 Oct 2023 12:05:58 -0300 Subject: [PATCH 08/68] fix: start session --- src/whatsapp/services/typebot.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index c329a169..e7669ab9 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -121,6 +121,12 @@ export class TypebotService { } if (startSession) { + const session = sessions.find((session) => session.remoteJid === remoteJid); + + if (session) { + sessions.splice(sessions.indexOf(session), 1); + } + const response = await this.createNewSession(instance, { url: url, typebot: typebot, From cc9df1dabb8aef78af0e095124b6f489f03a1dc0 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 24 Oct 2023 12:06:20 -0300 Subject: [PATCH 09/68] fix: start session --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index add0b1f1..114178d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Fixed * Adjusts in proxy +* Adjusts in start session for Typebot # 1.5.4 (2023-10-09 20:43) From d8629e53f1a0eb039ba987a9c463c7bd835c4585 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 24 Oct 2023 13:16:35 -0300 Subject: [PATCH 10/68] fix: start session --- src/whatsapp/services/typebot.service.ts | 59 +++++++++++++++++------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index e7669ab9..30f1cb53 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -47,7 +47,7 @@ export class TypebotService { findData.sessions.splice(findData.sessions.indexOf(session), 1); const typebotData = { - enabled: true, + enabled: findData.enabled, url: findData.url, typebot: findData.typebot, expire: findData.expire, @@ -71,7 +71,7 @@ export class TypebotService { } const typebotData = { - enabled: true, + enabled: findData.enabled, url: findData.url, typebot: findData.typebot, expire: findData.expire, @@ -102,7 +102,6 @@ export class TypebotService { const startSession = data.startSession; const variables = data.variables; const findTypebot = await this.find(instance); - const sessions = (findTypebot.sessions as Session[]) ?? []; const expire = findTypebot.expire; const keyword_finish = findTypebot.keyword_finish; const delay_message = findTypebot.delay_message; @@ -121,13 +120,10 @@ export class TypebotService { } if (startSession) { - const session = sessions.find((session) => session.remoteJid === remoteJid); - - if (session) { - sessions.splice(sessions.indexOf(session), 1); - } + const newSessions = await this.clearSessions(instance, remoteJid); const response = await this.createNewSession(instance, { + enabled: findTypebot.enabled, url: url, typebot: typebot, remoteJid: remoteJid, @@ -136,7 +132,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions: sessions, + sessions: newSessions, prefilledVariables: prefilledVariables, }); @@ -263,7 +259,7 @@ export class TypebotService { }); const typebotData = { - enabled: true, + enabled: data.enabled, url: data.url, typebot: data.typebot, expire: data.expire, @@ -280,6 +276,37 @@ export class TypebotService { return request.data; } + public async clearSessions(instance: InstanceDto, remoteJid: string) { + const findTypebot = await this.find(instance); + const sessions = (findTypebot.sessions as Session[]) ?? []; + + const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid); + + if (sessionWithRemoteJid.length > 0) { + sessionWithRemoteJid.forEach((session) => { + sessions.splice(sessions.indexOf(session), 1); + }); + + const typebotData = { + enabled: findTypebot.enabled, + url: findTypebot.url, + typebot: findTypebot.typebot, + expire: findTypebot.expire, + keyword_finish: findTypebot.keyword_finish, + delay_message: findTypebot.delay_message, + unknown_message: findTypebot.unknown_message, + listening_from_me: findTypebot.listening_from_me, + sessions, + }; + + this.create(instance, typebotData); + + return sessions; + } + + return sessions; + } + public async sendWAMessage( instance: InstanceDto, remoteJid: string, @@ -450,9 +477,10 @@ export class TypebotService { const diffInMinutes = Math.floor(diff / 1000 / 60); if (diffInMinutes > expire) { - sessions.splice(sessions.indexOf(session), 1); + const newSessions = await this.clearSessions(instance, remoteJid); const data = await this.createNewSession(instance, { + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -460,7 +488,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions: sessions, + sessions: newSessions, remoteJid: remoteJid, pushName: msg.pushName, }); @@ -487,7 +515,7 @@ export class TypebotService { } if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { - sessions.splice(sessions.indexOf(session), 1); + const newSessions = await this.clearSessions(instance, remoteJid); const typebotData = { enabled: true, @@ -498,7 +526,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions, + sessions: newSessions, }; this.create(instance, typebotData); @@ -513,7 +541,6 @@ export class TypebotService { const request = await axios.post(url + '/api/v1/sendMessage', reqData); - console.log('request', request); await this.sendWAMessage( instance, remoteJid, @@ -533,6 +560,7 @@ export class TypebotService { if (!session) { const data = await this.createNewSession(instance, { + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -593,7 +621,6 @@ export class TypebotService { const request = await axios.post(url + '/api/v1/sendMessage', reqData); - console.log('request', request); await this.sendWAMessage( instance, remoteJid, From 50e1efe5d7ac6051dcea9074f8a002bb1b60f45d Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 25 Oct 2023 07:00:58 -0300 Subject: [PATCH 11/68] fix: start session --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 86ac3176..9aab7d29 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1601,7 +1601,7 @@ export class WAStartupService { ); } - if (this.localTypebot.enabled) { + if (this.localTypebot.enabled || this.localTypebot.sessions?.length > 0) { if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { await this.typebotService.sendTypebot( { instanceName: this.instance.name }, From bc70ec8b073854adafcda744c21337a7e1a18058 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 25 Oct 2023 08:19:44 -0300 Subject: [PATCH 12/68] fix: start session --- src/whatsapp/services/typebot.service.ts | 8 ++++---- src/whatsapp/services/whatsapp.service.ts | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 30f1cb53..f29de04f 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -518,7 +518,7 @@ export class TypebotService { const newSessions = await this.clearSessions(instance, remoteJid); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -598,7 +598,7 @@ export class TypebotService { sessions.splice(sessions.indexOf(session), 1); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -639,7 +639,7 @@ export class TypebotService { }); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -674,7 +674,7 @@ export class TypebotService { sessions.splice(sessions.indexOf(session), 1); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 9aab7d29..c922cd63 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1601,7 +1601,11 @@ export class WAStartupService { ); } - if (this.localTypebot.enabled || this.localTypebot.sessions?.length > 0) { + const typebotSessionRemoteJid = this.localTypebot.sessions?.find( + (session) => session.remoteJid === received.key.remoteJid, + ); + + if (this.localTypebot.enabled || typebotSessionRemoteJid) { if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { await this.typebotService.sendTypebot( { instanceName: this.instance.name }, From 783c00a1d9cff2b12dda87b6952dba33e887560b Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 25 Oct 2023 10:29:24 -0300 Subject: [PATCH 13/68] fix: Added mimetype field when sending media --- CHANGELOG.md | 1 + Dockerfile | 2 +- package.json | 2 +- src/docs/swagger.yaml | 2 +- src/whatsapp/dto/sendMessage.dto.ts | 1 + src/whatsapp/services/whatsapp.service.ts | 11 +++++++---- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 114178d0..47b359e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Adjusts in proxy * Adjusts in start session for Typebot +* Added mimetype field when sending media # 1.5.4 (2023-10-09 20:43) diff --git a/Dockerfile b/Dockerfile index dee414d6..f60ed48d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:20.7.0-alpine -LABEL version="1.5.4" description="Api to control whatsapp features through http requests." +LABEL version="1.5.5" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL contact="contato@agenciadgcode.com" diff --git a/package.json b/package.json index 17a90ebc..7997c3fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "1.5.4", + "version": "1.5.5", "description": "Rest api for communication with WhatsApp", "main": "./dist/src/main.js", "scripts": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 5f9235ac..589ba8dc 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -25,7 +25,7 @@ info: [![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442) - version: 1.5.4 + version: 1.5.5 contact: name: DavidsonGomes email: contato@agenciadgcode.com diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index c2ddb3a2..e0d6d8f9 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -61,6 +61,7 @@ export class SendPollDto extends Metadata { export type MediaType = 'image' | 'document' | 'video' | 'audio'; export class MediaMessage { mediatype: MediaType; + mimetype?: string; caption?: string; // for document fileName?: string; diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c922cd63..6f630b63 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1188,7 +1188,6 @@ export class WAStartupService { const proxyUrls = text.split('\r\n'); const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); const proxyUrl = 'http://' + proxyUrls[rand]; - console.log(proxyUrl); options = { agent: new ProxyAgent(proxyUrl as any), }; @@ -2447,10 +2446,14 @@ export class WAStartupService { let mimetype: string; - if (isURL(mediaMessage.media)) { - mimetype = getMIMEType(mediaMessage.media); + if (mediaMessage.mimetype) { + mimetype = mediaMessage.mimetype; } else { - mimetype = getMIMEType(mediaMessage.fileName); + if (isURL(mediaMessage.media)) { + mimetype = getMIMEType(mediaMessage.media); + } else { + mimetype = getMIMEType(mediaMessage.fileName); + } } this.logger.verbose('Mimetype: ' + mimetype); From d0a5ae1da4d6b05bc2864a377b64d656bb01df46 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 26 Oct 2023 18:01:17 -0300 Subject: [PATCH 14/68] fix: Removed senderKeyDistributionMessage --- src/whatsapp/services/whatsapp.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 6f630b63..3321605e 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1520,11 +1520,12 @@ export class WAStartupService { this.logger.verbose('Event received: messages.upsert'); const received = messages[0]; + console.log(received, type); if ( type !== 'notify' || !received?.message || received.message?.protocolMessage || - received.message.senderKeyDistributionMessage || + // received.message.senderKeyDistributionMessage || received.message?.pollUpdateMessage ) { this.logger.verbose('message rejected'); From 52d6a563d607f07e9c1e5736bff799c9e6896ecd Mon Sep 17 00:00:00 2001 From: Vitor Date: Fri, 27 Oct 2023 22:49:57 -0300 Subject: [PATCH 15/68] Handle optional chaining for 'settings.msg_call', this change prevents 'TypeError: Cannot read properties of undefined' errors when 'msg_call' is undefined. --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 3321605e..4237c133 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1801,7 +1801,7 @@ export class WAStartupService { this.client.rejectCall(call.id, call.from); } - if (settings?.msg_call.trim().length > 0 && call.status == 'offer') { + if (settings?.msg_call?.trim().length > 0 && call.status == 'offer') { this.logger.verbose('Sending message in call'); const msg = await this.client.sendMessage(call.from, { text: settings.msg_call, From 8a99386b33d970297a012fd9351c653bca6256d6 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Sat, 28 Oct 2023 21:21:12 -0300 Subject: [PATCH 16/68] handle erros --- src/whatsapp/services/typebot.service.ts | 44 +++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 659309e1..f9a07de3 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -96,6 +96,8 @@ export class TypebotService { } public async startTypebot(instance: InstanceDto, data: any) { + if (data.remoteJid === 'status@broadcast') return; + const remoteJid = data.remoteJid; const url = data.url; const typebot = data.typebot; @@ -155,7 +157,13 @@ export class TypebotService { }, }; - const request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + let request: any; + try { + request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + } catch (error) { + this.logger.error(error); + return; + } await this.sendWAMessage( instance, @@ -224,7 +232,9 @@ export class TypebotService { } public async createNewSession(instance: InstanceDto, data: any) { + if (data.remoteJid === 'status@broadcast') return; const id = Math.floor(Math.random() * 10000000000).toString(); + const reqData = { startParams: { typebot: data.typebot, @@ -237,7 +247,13 @@ export class TypebotService { }, }; - const request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + let request: any; + try { + request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + } catch (error) { + this.logger.error(error); + return; + } if (request.data.sessionId) { data.sessions.push({ @@ -503,7 +519,13 @@ export class TypebotService { sessionId: data.sessionId, }; - const request = await axios.post(url + '/api/v1/sendMessage', reqData); + let request: any; + try { + request = await axios.post(url + '/api/v1/sendMessage', reqData); + } catch (error) { + this.logger.error(error); + return; + } console.log('request', request); await this.sendWAMessage( @@ -583,7 +605,13 @@ export class TypebotService { sessionId: data.sessionId, }; - const request = await axios.post(url + '/api/v1/sendMessage', reqData); + let request: any; + try { + request = await axios.post(url + '/api/v1/sendMessage', reqData); + } catch (error) { + this.logger.error(error); + return; + } console.log('request', request); await this.sendWAMessage( @@ -660,7 +688,13 @@ export class TypebotService { sessionId: session.sessionId.split('-')[1], }; - const request = await axios.post(url + '/api/v1/sendMessage', reqData); + let request: any; + try { + request = await axios.post(url + '/api/v1/sendMessage', reqData); + } catch (error) { + this.logger.error(error); + return; + } await this.sendWAMessage( instance, From 0817c2589fc3b5763afcecdc962345ae776e7003 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:49:26 -0300 Subject: [PATCH 17/68] expand try catch for all function --- src/whatsapp/services/typebot.service.ts | 92 ++++++++++++------------ 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index f9a07de3..6a8052e4 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -157,29 +157,28 @@ export class TypebotService { }, }; - let request: any; try { - request = await axios.post(data.url + '/api/v1/sendMessage', 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, + ); + + this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, { + remoteJid: remoteJid, + url: url, + typebot: typebot, + variables: variables, + sessionId: id, + }); } catch (error) { this.logger.error(error); return; } - - await this.sendWAMessage( - instance, - remoteJid, - request.data.messages, - request.data.input, - request.data.clientSideActions, - ); - - this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, { - remoteJid: remoteJid, - url: url, - typebot: typebot, - variables: variables, - sessionId: id, - }); } return { @@ -255,7 +254,7 @@ export class TypebotService { return; } - if (request.data.sessionId) { + if (request?.data?.sessionId) { data.sessions.push({ remoteJid: data.remoteJid, sessionId: `${id}-${request.data.sessionId}`, @@ -519,22 +518,21 @@ export class TypebotService { sessionId: data.sessionId, }; - let request: any; try { - request = await axios.post(url + '/api/v1/sendMessage', reqData); + const request = await axios.post(url + '/api/v1/sendMessage', reqData); + + console.log('request', request); + await this.sendWAMessage( + instance, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); } catch (error) { this.logger.error(error); return; } - - console.log('request', request); - await this.sendWAMessage( - instance, - remoteJid, - request.data.messages, - request.data.input, - request.data.clientSideActions, - ); } return; @@ -608,19 +606,19 @@ export class TypebotService { let request: any; try { request = await axios.post(url + '/api/v1/sendMessage', reqData); + + console.log('request', request); + await this.sendWAMessage( + instance, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); } catch (error) { this.logger.error(error); return; } - - console.log('request', request); - await this.sendWAMessage( - instance, - remoteJid, - request.data.messages, - request.data.input, - request.data.clientSideActions, - ); } return; } @@ -691,19 +689,19 @@ export class TypebotService { let request: any; try { request = await axios.post(url + '/api/v1/sendMessage', reqData); + + await this.sendWAMessage( + instance, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); } catch (error) { this.logger.error(error); return; } - await this.sendWAMessage( - instance, - remoteJid, - request.data.messages, - request.data.input, - request.data.clientSideActions, - ); - return; } } From f71089884486fcddea0db41fa378df584efb9ef8 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:59:29 -0300 Subject: [PATCH 18/68] fix: in error catch in createNewSession --- .vscode/settings.json | 4 +- src/whatsapp/services/typebot.service.ts | 118 ++++++++++++++--------- 2 files changed, 78 insertions(+), 44 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5bc52817..20b82443 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,7 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": true, "source.fixAll": true - } + }, + "prisma-smart-formatter.typescript.defaultFormatter": "esbenp.prettier-vscode", + "prisma-smart-formatter.prisma.defaultFormatter": "Prisma.prisma" } \ No newline at end of file diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 6a8052e4..c9894f75 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -47,7 +47,7 @@ export class TypebotService { findData.sessions.splice(findData.sessions.indexOf(session), 1); const typebotData = { - enabled: true, + enabled: findData.enabled, url: findData.url, typebot: findData.typebot, expire: findData.expire, @@ -71,7 +71,7 @@ export class TypebotService { } const typebotData = { - enabled: true, + enabled: findData.enabled, url: findData.url, typebot: findData.typebot, expire: findData.expire, @@ -104,7 +104,6 @@ export class TypebotService { const startSession = data.startSession; const variables = data.variables; const findTypebot = await this.find(instance); - const sessions = (findTypebot.sessions as Session[]) ?? []; const expire = findTypebot.expire; const keyword_finish = findTypebot.keyword_finish; const delay_message = findTypebot.delay_message; @@ -116,12 +115,17 @@ export class TypebotService { instanceName: instance.instanceName, }; - variables.forEach((variable) => { - prefilledVariables[variable.name] = variable.value; - }); + if (variables?.length) { + variables.forEach((variable: { name: string | number; value: string }) => { + prefilledVariables[variable.name] = variable.value; + }); + } if (startSession) { + const newSessions = await this.clearSessions(instance, remoteJid); + const response = await this.createNewSession(instance, { + enabled: findTypebot.enabled, url: url, typebot: typebot, remoteJid: remoteJid, @@ -130,7 +134,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions: sessions, + sessions: newSessions, prefilledVariables: prefilledVariables, }); @@ -246,45 +250,74 @@ export class TypebotService { }, }; - let request: any; try { - request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + const request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + + if (request?.data?.sessionId) { + data.sessions.push({ + remoteJid: data.remoteJid, + sessionId: `${id}-${request.data.sessionId}`, + status: 'opened', + createdAt: Date.now(), + updateAt: Date.now(), + prefilledVariables: { + ...data.prefilledVariables, + remoteJid: data.remoteJid, + pushName: data.pushName || '', + instanceName: instance.instanceName, + }, + }); + + const typebotData = { + enabled: data.enabled, + url: data.url, + typebot: data.typebot, + expire: data.expire, + keyword_finish: data.keyword_finish, + delay_message: data.delay_message, + unknown_message: data.unknown_message, + listening_from_me: data.listening_from_me, + sessions: data.sessions, + }; + + this.create(instance, typebotData); + } + return request.data; } catch (error) { this.logger.error(error); return; } + } - if (request?.data?.sessionId) { - data.sessions.push({ - remoteJid: data.remoteJid, - sessionId: `${id}-${request.data.sessionId}`, - status: 'opened', - createdAt: Date.now(), - updateAt: Date.now(), - prefilledVariables: { - ...data.prefilledVariables, - remoteJid: data.remoteJid, - pushName: data.pushName || '', - instanceName: instance.instanceName, - }, + public async clearSessions(instance: InstanceDto, remoteJid: string) { + const findTypebot = await this.find(instance); + const sessions = (findTypebot.sessions as Session[]) ?? []; + + const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid); + + if (sessionWithRemoteJid.length > 0) { + sessionWithRemoteJid.forEach((session) => { + sessions.splice(sessions.indexOf(session), 1); }); const typebotData = { - enabled: true, - url: data.url, - typebot: data.typebot, - expire: data.expire, - keyword_finish: data.keyword_finish, - delay_message: data.delay_message, - unknown_message: data.unknown_message, - listening_from_me: data.listening_from_me, - sessions: data.sessions, + enabled: findTypebot.enabled, + url: findTypebot.url, + typebot: findTypebot.typebot, + expire: findTypebot.expire, + keyword_finish: findTypebot.keyword_finish, + delay_message: findTypebot.delay_message, + unknown_message: findTypebot.unknown_message, + listening_from_me: findTypebot.listening_from_me, + sessions, }; this.create(instance, typebotData); + + return sessions; } - return request.data; + return sessions; } public async sendWAMessage( @@ -457,9 +490,10 @@ export class TypebotService { const diffInMinutes = Math.floor(diff / 1000 / 60); if (diffInMinutes > expire) { - sessions.splice(sessions.indexOf(session), 1); + const newSessions = await this.clearSessions(instance, remoteJid); const data = await this.createNewSession(instance, { + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -467,7 +501,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions: sessions, + sessions: newSessions, remoteJid: remoteJid, pushName: msg.pushName, }); @@ -494,10 +528,10 @@ export class TypebotService { } if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { - sessions.splice(sessions.indexOf(session), 1); + const newSessions = await this.clearSessions(instance, remoteJid); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -505,7 +539,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions, + sessions: newSessions, }; this.create(instance, typebotData); @@ -521,7 +555,6 @@ export class TypebotService { try { const request = await axios.post(url + '/api/v1/sendMessage', reqData); - console.log('request', request); await this.sendWAMessage( instance, remoteJid, @@ -545,6 +578,7 @@ export class TypebotService { if (!session) { const data = await this.createNewSession(instance, { + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -582,7 +616,7 @@ export class TypebotService { sessions.splice(sessions.indexOf(session), 1); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -606,8 +640,6 @@ export class TypebotService { let request: any; try { request = await axios.post(url + '/api/v1/sendMessage', reqData); - - console.log('request', request); await this.sendWAMessage( instance, remoteJid, @@ -630,7 +662,7 @@ export class TypebotService { }); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -665,7 +697,7 @@ export class TypebotService { sessions.splice(sessions.indexOf(session), 1); const typebotData = { - enabled: true, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, From e30f196dad49e6d34095a49e7086263ac0871fca Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Sun, 29 Oct 2023 13:22:41 -0300 Subject: [PATCH 19/68] remove duplicate --- src/whatsapp/services/typebot.service.ts | 31 ------------------------ 1 file changed, 31 deletions(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index fe5cd2c1..c9894f75 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -320,37 +320,6 @@ export class TypebotService { return sessions; } - public async clearSessions(instance: InstanceDto, remoteJid: string) { - const findTypebot = await this.find(instance); - const sessions = (findTypebot.sessions as Session[]) ?? []; - - const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid); - - if (sessionWithRemoteJid.length > 0) { - sessionWithRemoteJid.forEach((session) => { - sessions.splice(sessions.indexOf(session), 1); - }); - - const typebotData = { - enabled: findTypebot.enabled, - url: findTypebot.url, - typebot: findTypebot.typebot, - expire: findTypebot.expire, - keyword_finish: findTypebot.keyword_finish, - delay_message: findTypebot.delay_message, - unknown_message: findTypebot.unknown_message, - listening_from_me: findTypebot.listening_from_me, - sessions, - }; - - this.create(instance, typebotData); - - return sessions; - } - - return sessions; - } - public async sendWAMessage( instance: InstanceDto, remoteJid: string, From ac5fc2204328d46d34b4a81ba64a2005b88ff64b Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:05:18 -0300 Subject: [PATCH 20/68] try catch entire sendTypebot --- src/whatsapp/services/typebot.service.ts | 291 +++++++++++------------ 1 file changed, 145 insertions(+), 146 deletions(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index c9894f75..9579cffd 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -482,16 +482,102 @@ export class TypebotService { const session = sessions.find((session) => session.remoteJid === remoteJid); - if (session && expire && expire > 0) { - const now = Date.now(); + try { + if (session && expire && expire > 0) { + const now = Date.now(); - const diff = now - session.updateAt; + const diff = now - session.updateAt; - const diffInMinutes = Math.floor(diff / 1000 / 60); + const diffInMinutes = Math.floor(diff / 1000 / 60); - if (diffInMinutes > expire) { - const newSessions = await this.clearSessions(instance, remoteJid); + if (diffInMinutes > expire) { + const newSessions = await this.clearSessions(instance, remoteJid); + const data = await this.createNewSession(instance, { + enabled: findTypebot.enabled, + url: url, + typebot: typebot, + expire: expire, + keyword_finish: keyword_finish, + delay_message: delay_message, + unknown_message: unknown_message, + listening_from_me: listening_from_me, + sessions: newSessions, + remoteJid: remoteJid, + pushName: msg.pushName, + }); + + await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions); + + if (data.messages.length === 0) { + const content = this.getConversationMessage(msg.message); + + if (!content) { + if (unknown_message) { + this.waMonitor.waInstances[instance.instanceName].textMessage({ + number: remoteJid.split('@')[0], + options: { + delay: delay_message || 1000, + presence: 'composing', + }, + textMessage: { + text: unknown_message, + }, + }); + } + return; + } + + if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { + const newSessions = await this.clearSessions(instance, remoteJid); + + const typebotData = { + enabled: findTypebot.enabled, + url: url, + typebot: typebot, + expire: expire, + keyword_finish: keyword_finish, + delay_message: delay_message, + unknown_message: unknown_message, + listening_from_me: listening_from_me, + sessions: newSessions, + }; + + this.create(instance, typebotData); + + return; + } + + const reqData = { + message: content, + sessionId: data.sessionId, + }; + + try { + const request = await axios.post(url + '/api/v1/sendMessage', reqData); + + await this.sendWAMessage( + instance, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); + } catch (error) { + this.logger.error(error); + return; + } + } + + return; + } + } + + if (session && session.status !== 'opened') { + return; + } + + if (!session) { const data = await this.createNewSession(instance, { enabled: findTypebot.enabled, url: url, @@ -501,7 +587,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions: newSessions, + sessions: sessions, remoteJid: remoteJid, pushName: msg.pushName, }); @@ -528,7 +614,7 @@ export class TypebotService { } if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { - const newSessions = await this.clearSessions(instance, remoteJid); + sessions.splice(sessions.indexOf(session), 1); const typebotData = { enabled: findTypebot.enabled, @@ -539,7 +625,7 @@ export class TypebotService { delay_message: delay_message, unknown_message: unknown_message, listening_from_me: listening_from_me, - sessions: newSessions, + sessions, }; this.create(instance, typebotData); @@ -552,9 +638,9 @@ export class TypebotService { sessionId: data.sessionId, }; + let request: any; try { - const request = await axios.post(url + '/api/v1/sendMessage', reqData); - + request = await axios.post(url + '/api/v1/sendMessage', reqData); await this.sendWAMessage( instance, remoteJid, @@ -567,135 +653,15 @@ export class TypebotService { return; } } - return; } - } - if (session && session.status !== 'opened') { - return; - } - - if (!session) { - const data = await this.createNewSession(instance, { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keyword_finish: keyword_finish, - delay_message: delay_message, - unknown_message: unknown_message, - listening_from_me: listening_from_me, - sessions: sessions, - remoteJid: remoteJid, - pushName: msg.pushName, + sessions.map((session) => { + if (session.remoteJid === remoteJid) { + session.updateAt = Date.now(); + } }); - await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions); - - if (data.messages.length === 0) { - const content = this.getConversationMessage(msg.message); - - if (!content) { - if (unknown_message) { - this.waMonitor.waInstances[instance.instanceName].textMessage({ - number: remoteJid.split('@')[0], - options: { - delay: delay_message || 1000, - presence: 'composing', - }, - textMessage: { - text: unknown_message, - }, - }); - } - return; - } - - if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { - sessions.splice(sessions.indexOf(session), 1); - - const typebotData = { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keyword_finish: keyword_finish, - delay_message: delay_message, - unknown_message: unknown_message, - listening_from_me: listening_from_me, - sessions, - }; - - this.create(instance, typebotData); - - return; - } - - const reqData = { - message: content, - sessionId: data.sessionId, - }; - - let request: any; - try { - request = await axios.post(url + '/api/v1/sendMessage', reqData); - await this.sendWAMessage( - instance, - remoteJid, - request.data.messages, - request.data.input, - request.data.clientSideActions, - ); - } catch (error) { - this.logger.error(error); - return; - } - } - return; - } - - sessions.map((session) => { - if (session.remoteJid === remoteJid) { - session.updateAt = Date.now(); - } - }); - - const typebotData = { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keyword_finish: keyword_finish, - delay_message: delay_message, - unknown_message: unknown_message, - listening_from_me: listening_from_me, - sessions, - }; - - this.create(instance, typebotData); - - const content = this.getConversationMessage(msg.message); - - if (!content) { - if (unknown_message) { - this.waMonitor.waInstances[instance.instanceName].textMessage({ - number: remoteJid.split('@')[0], - options: { - delay: delay_message || 1000, - presence: 'composing', - }, - textMessage: { - text: unknown_message, - }, - }); - } - return; - } - - if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { - sessions.splice(sessions.indexOf(session), 1); - const typebotData = { enabled: findTypebot.enabled, url: url, @@ -710,17 +676,50 @@ export class TypebotService { this.create(instance, typebotData); - return; - } + const content = this.getConversationMessage(msg.message); - const reqData = { - message: content, - sessionId: session.sessionId.split('-')[1], - }; + if (!content) { + if (unknown_message) { + this.waMonitor.waInstances[instance.instanceName].textMessage({ + number: remoteJid.split('@')[0], + options: { + delay: delay_message || 1000, + presence: 'composing', + }, + textMessage: { + text: unknown_message, + }, + }); + } + return; + } - let request: any; - try { - request = await axios.post(url + '/api/v1/sendMessage', reqData); + if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { + sessions.splice(sessions.indexOf(session), 1); + + const typebotData = { + enabled: findTypebot.enabled, + url: url, + typebot: typebot, + expire: expire, + keyword_finish: keyword_finish, + delay_message: delay_message, + unknown_message: unknown_message, + listening_from_me: listening_from_me, + sessions, + }; + + this.create(instance, typebotData); + + return; + } + + const reqData = { + message: content, + sessionId: session.sessionId.split('-')[1], + }; + + const request = await axios.post(url + '/api/v1/sendMessage', reqData); await this.sendWAMessage( instance, @@ -729,11 +728,11 @@ export class TypebotService { request.data.input, request.data.clientSideActions, ); + + return; } catch (error) { this.logger.error(error); return; } - - return; } } From 37f1620f7c4c66530a0ae23f2b9c3079527d812c Mon Sep 17 00:00:00 2001 From: Jean Carlo Nascimento Date: Sun, 12 Nov 2023 19:11:47 -0300 Subject: [PATCH 21/68] Update typebot.service.ts - element.underline change ~ for * There's no underline in WhatsApp ~ use strikethrough instead, I switched to * bold. --- src/whatsapp/services/typebot.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index c329a169..07a05926 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -323,7 +323,7 @@ export class TypebotService { } if (element.underline) { - text = `~${text}~`; + text = `*${text}*`; } if (element.url) { From 8b4cdf3b9b6d1480b4647f6dc66fa2c960583f9a Mon Sep 17 00:00:00 2001 From: craines Date: Tue, 14 Nov 2023 16:47:25 -0300 Subject: [PATCH 22/68] fix: Removed await from webhook when sending a message --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c4d25c88..c0c86ad8 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -2222,7 +2222,7 @@ export class WAStartupService { this.logger.log(messageRaw); this.logger.verbose('Sending data to webhook in event SEND_MESSAGE'); - await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw); + this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw); if (this.localChatwoot.enabled && !isChatwoot) { this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw); From 04e5443b82a57bfca3f9e8736d1834e0fa6416f1 Mon Sep 17 00:00:00 2001 From: craines Date: Wed, 15 Nov 2023 21:52:56 -0300 Subject: [PATCH 23/68] fix: send reaction --- src/whatsapp/services/whatsapp.service.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c0c86ad8..2ab3ab10 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -2159,6 +2159,21 @@ export class WAStartupService { !message['conversation'] && sender !== 'status@broadcast' ) { + + if (message['reactionMessage']) { + this.logger.verbose('Sending reaction'); + return await this.client.sendMessage( + sender, + { + react: { + text: message['reactionMessage']['text'], + key: message['reactionMessage']['key'] + } + } as unknown as AnyMessageContent, + option as unknown as MiscMessageGenerationOptions, + ); + } + if (!message['audio']) { this.logger.verbose('Sending message'); return await this.client.sendMessage( @@ -2174,7 +2189,6 @@ export class WAStartupService { ); } } - if (message['conversation']) { this.logger.verbose('Sending message'); return await this.client.sendMessage( From 6c9e86e17ae837e784e3a7bd55d1ca6a3dea77e4 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 20 Nov 2023 17:52:36 -0300 Subject: [PATCH 24/68] feat: sqs --- .DS_Store | Bin 6148 -> 8196 bytes package.json | 1 + src/config/env.config.ts | 16 +++ src/libs/redis.client.ts | 50 ++++---- src/libs/sqs.server.ts | 97 ++++++++++++++ src/main.ts | 5 +- src/validate/validate.schema.ts | 43 +++++++ .../controllers/instance.controller.ts | 59 +++++++++ src/whatsapp/controllers/sqs.controller.ts | 56 ++++++++ src/whatsapp/dto/instance.dto.ts | 2 + src/whatsapp/dto/sqs.dto.ts | 4 + src/whatsapp/models/index.ts | 1 + src/whatsapp/models/sqs.model.ts | 18 +++ src/whatsapp/repository/repository.manager.ts | 7 + src/whatsapp/repository/sqs.repository.ts | 62 +++++++++ src/whatsapp/routers/index.router.ts | 2 + src/whatsapp/routers/sqs.router.ts | 52 ++++++++ src/whatsapp/services/sqs.service.ts | 35 +++++ src/whatsapp/services/whatsapp.service.ts | 120 +++++++++++++++++- src/whatsapp/types/wa.types.ts | 5 + src/whatsapp/whatsapp.module.ts | 11 ++ 21 files changed, 621 insertions(+), 25 deletions(-) create mode 100644 src/libs/sqs.server.ts create mode 100644 src/whatsapp/controllers/sqs.controller.ts create mode 100644 src/whatsapp/dto/sqs.dto.ts create mode 100644 src/whatsapp/models/sqs.model.ts create mode 100644 src/whatsapp/repository/sqs.repository.ts create mode 100644 src/whatsapp/routers/sqs.router.ts create mode 100644 src/whatsapp/services/sqs.service.ts diff --git a/.DS_Store b/.DS_Store index 9880dac3989431d51dbcb1b03b08b34ce3c75340..34664d41769177fab1fa2c048600c0fac7e10380 100644 GIT binary patch literal 8196 zcmeHMOKVd>6h6}?G0~J3T(paVcj3+lwjgw)+?HDqi=ZY5E=0|PG&ac%X_HF9l0~T#1kwm^qg@XXg9no9W!18zN$B-O3Wt z0ug0Vxt%+MCZ=#duaPpPdM?2-jHgCzsZjQt?MS5UunJfOtO8a6tAJJDe^CJMY+kYn z?|tdDtyRD(Fp&!I`@uow)>XDIt++bSh!g-eg>F&sJjWkk9c5i*`_jUL!sJj`Ih3gs zgUR9OcT}9KY+qVAoR~^Jm_lYMLtz3P^E)D(m@BPqtpZkor~*89Ptj$nQJr>-{QYRr z*L_Xj^GXfBgud+Nk2|+c4!*9M{>oT?do%bpzz$wwD4haskG82yK2@k{pc8u@jnFyV zef4qhZeMT+&C~P)CdpAcA9ZJ?z@iR5rVg{`e~Wc0pU&a<+s}GEPETovI`o*f4Ia7K4Z%b6 z>pZUgDh$sEZboItZ8r&sv9<0P^riauNPhDz+Su19~9uptv{5SDB z@5TIpGtW?)x>N$!I+cyRPv>8R{W`yMAC`-IH^kq7F^8^VQjE@n(qiwYvA3NNgY4mF zFi;*H`}k^cc7OQgW$g+8^?VvtF<%P%fr4>Wah;$rQq~oxo9}MyAAe4`BmF-K5 W9+ZFnAz=9Xq<#Mj{cSUu3j6_mfvRo* delta 200 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{MGjdE!6q~50D9Q@rfW=Z7igMBogOl@f3nnHm zXJnn+BPhqj^>OkVK^e9)yvKXQ#3!Eyzq zVDfAsg^ej8Y>U}BI0TuYg4{sb6=eCw#P7_L`DH8>KoUUfnV>Wahz4>%&IGxfVRJms G9A*H@L?lxH diff --git a/package.json b/package.json index 7997c3fa..6d97cf0b 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@sentry/node": "^7.59.2", "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys#fix-lids", "amqplib": "^0.10.3", + "aws-sdk": "^2.1499.0", "axios": "^1.3.5", "class-validator": "^0.13.2", "compression": "^1.7.4", diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 873c54ce..118836b7 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -66,6 +66,14 @@ export type Rabbitmq = { URI: string; }; +export type Sqs = { + ENABLED: boolean; + ACCESS_KEY_ID: string; + SECRET_ACCESS_KEY: string; + ACCOUNT_ID: string; + REGION: string; +}; + export type Websocket = { ENABLED: boolean; }; @@ -135,6 +143,7 @@ export interface Env { DATABASE: Database; REDIS: Redis; RABBITMQ: Rabbitmq; + SQS: Sqs; WEBSOCKET: Websocket; LOG: Log; DEL_INSTANCE: DelInstance; @@ -226,6 +235,13 @@ export class ConfigService { ENABLED: process.env?.RABBITMQ_ENABLED === 'true', URI: process.env.RABBITMQ_URI || '', }, + SQS: { + ENABLED: process.env?.SQS_ENABLED === 'true', + ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '', + SECRET_ACCESS_KEY: process.env.SQS_SECRET_ACCESS_KEY || '', + ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '', + REGION: process.env.SQS_REGION || '', + }, WEBSOCKET: { ENABLED: process.env?.WEBSOCKET_ENABLED === 'true', }, diff --git a/src/libs/redis.client.ts b/src/libs/redis.client.ts index f03513ba..1d74ff15 100644 --- a/src/libs/redis.client.ts +++ b/src/libs/redis.client.ts @@ -5,49 +5,55 @@ import { Redis } from '../config/env.config'; import { Logger } from '../config/logger.config'; export class RedisCache { - async disconnect() { - await this.client.disconnect(); - this.statusConnection = false; - } - constructor() { - this.logger.verbose('instance created'); - process.on('beforeExit', async () => { - this.logger.verbose('instance destroyed'); - if (this.statusConnection) { - this.logger.verbose('instance disconnect'); - await this.client.disconnect(); - } - }); - } - + private readonly logger = new Logger(RedisCache.name); + private client: RedisClientType; private statusConnection = false; private instanceName: string; private redisEnv: Redis; + constructor() { + this.logger.verbose('RedisCache instance created'); + process.on('beforeExit', () => { + this.logger.verbose('RedisCache instance destroyed'); + this.disconnect(); + }); + } + public set reference(reference: string) { this.logger.verbose('set reference: ' + reference); this.instanceName = reference; } public async connect(redisEnv: Redis) { - this.logger.verbose('connecting'); + this.logger.verbose('Connecting to Redis...'); this.client = createClient({ url: redisEnv.URI }); - this.logger.verbose('connected in ' + redisEnv.URI); + this.client.on('error', (err) => this.logger.error('Redis Client Error ' + err)); + await this.client.connect(); this.statusConnection = true; this.redisEnv = redisEnv; + this.logger.verbose(`Connected to ${redisEnv.URI}`); } - private readonly logger = new Logger(RedisCache.name); - private client: RedisClientType; + public async disconnect() { + if (this.statusConnection) { + await this.client.disconnect(); + this.statusConnection = false; + this.logger.verbose('Redis client disconnected'); + } + } public async instanceKeys(): Promise { + const keys: string[] = []; try { - this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*'); - return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']); + this.logger.verbose('Fetching instance keys'); + for await (const key of this.client.scanIterator({ MATCH: `${this.redisEnv.PREFIX_KEY}:*` })) { + keys.push(key); + } } catch (error) { - this.logger.error(error); + this.logger.error('Error fetching instance keys ' + error); } + return keys; } public async keyExists(key?: string) { diff --git a/src/libs/sqs.server.ts b/src/libs/sqs.server.ts new file mode 100644 index 00000000..04184542 --- /dev/null +++ b/src/libs/sqs.server.ts @@ -0,0 +1,97 @@ +import { SQS } from 'aws-sdk'; + +import { configService, Sqs } from '../config/env.config'; +import { Logger } from '../config/logger.config'; + +const logger = new Logger('SQS'); + +let sqs: SQS; + +export const initSQS = () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return new Promise((resolve, reject) => { + const awsConfig = configService.get('SQS'); + sqs = new SQS({ + accessKeyId: awsConfig.ACCESS_KEY_ID, + secretAccessKey: awsConfig.SECRET_ACCESS_KEY, + region: awsConfig.REGION, + }); + + logger.info('SQS initialized'); + resolve(); + }); +}; + +export const getSQS = (): SQS => { + return sqs; +}; + +export const initQueues = (instanceName: string, events: string[]) => { + if (!events || !events.length) return; + + const queues = events.map((event) => { + return `${event.replace(/_/g, '_').toLowerCase()}`; + }); + + const sqs = getSQS(); + + queues.forEach((event) => { + const queueName = `${instanceName}_${event}.fifo`; + + sqs.createQueue( + { + QueueName: queueName, + Attributes: { + FifoQueue: 'true', + }, + }, + (err, data) => { + if (err) { + logger.error(`Error creating queue ${queueName}: ${err.message}`); + } else { + logger.info(`Queue ${queueName} created: ${data.QueueUrl}`); + } + }, + ); + }); +}; + +export const removeQueues = (instanceName: string, events: string[]) => { + if (!events || !events.length) return; + + const sqs = getSQS(); + + const queues = events.map((event) => { + return `${event.replace(/_/g, '_').toLowerCase()}`; + }); + + queues.forEach((event) => { + const queueName = `${instanceName}_${event}.fifo`; + + sqs.getQueueUrl( + { + QueueName: queueName, + }, + (err, data) => { + if (err) { + logger.error(`Error getting queue URL for ${queueName}: ${err.message}`); + } else { + const queueUrl = data.QueueUrl; + + sqs.deleteQueue( + { + QueueUrl: queueUrl, + }, + (deleteErr) => { + if (deleteErr) { + logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`); + } else { + logger.info(`Queue ${queueName} deleted`); + } + }, + ); + } + }, + ); + }); +}; diff --git a/src/main.ts b/src/main.ts index 3dcd0f2d..52bdd798 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,13 +6,14 @@ import cors from 'cors'; import express, { json, NextFunction, Request, Response, urlencoded } from 'express'; import { join } from 'path'; -import { Auth, configService, Cors, HttpServer, Rabbitmq, Webhook } from './config/env.config'; +import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config'; import { onUnexpectedError } from './config/error.config'; import { Logger } from './config/logger.config'; import { ROOT_DIR } from './config/path.config'; import { swaggerRouter } from './docs/swagger.conf'; import { initAMQP } from './libs/amqp.server'; import { initIO } from './libs/socket.server'; +import { initSQS } from './libs/sqs.server'; import { ServerUP } from './utils/server-up'; import { HttpStatus, router } from './whatsapp/routers/index.router'; import { waMonitor } from './whatsapp/whatsapp.module'; @@ -128,6 +129,8 @@ function bootstrap() { if (configService.get('RABBITMQ')?.ENABLED) initAMQP(); + if (configService.get('SQS')?.ENABLED) initSQS(); + onUnexpectedError(); } diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index b8a4c0ad..9781e18c 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -987,6 +987,49 @@ export const rabbitmqSchema: JSONSchema7 = { ...isNotEmpty('enabled'), }; +export const sqsSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + enabled: { type: 'boolean', enum: [true, false] }, + events: { + type: 'array', + minItems: 0, + items: { + type: 'string', + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'CALL', + 'NEW_JWT_TOKEN', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + 'CHAMA_AI_ACTION', + ], + }, + }, + }, + required: ['enabled'], + ...isNotEmpty('enabled'), +}; + export const typebotSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index d26c64cd..5409a7dd 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -13,6 +13,7 @@ import { ChatwootService } from '../services/chatwoot.service'; import { WAMonitoringService } from '../services/monitor.service'; import { RabbitmqService } from '../services/rabbitmq.service'; import { SettingsService } from '../services/settings.service'; +import { SqsService } from '../services/sqs.service'; import { TypebotService } from '../services/typebot.service'; import { WebhookService } from '../services/webhook.service'; import { WebsocketService } from '../services/websocket.service'; @@ -31,6 +32,7 @@ export class InstanceController { private readonly settingsService: SettingsService, private readonly websocketService: WebsocketService, private readonly rabbitmqService: RabbitmqService, + private readonly sqsService: SqsService, private readonly typebotService: TypebotService, private readonly cache: RedisCache, ) {} @@ -62,6 +64,8 @@ export class InstanceController { websocket_events, rabbitmq_enabled, rabbitmq_events, + sqs_enabled, + sqs_events, typebot_url, typebot, typebot_expire, @@ -243,6 +247,53 @@ export class InstanceController { } } + let sqsEvents: string[]; + + if (sqs_enabled) { + this.logger.verbose('creating sqs'); + try { + let newEvents: string[] = []; + if (sqs_events.length === 0) { + newEvents = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'CALL', + 'NEW_JWT_TOKEN', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + 'CHAMA_AI_ACTION', + ]; + } else { + newEvents = sqs_events; + } + this.sqsService.create(instance, { + enabled: true, + events: newEvents, + }); + + sqsEvents = (await this.sqsService.find(instance)).events; + } catch (error) { + this.logger.log(error); + } + } + if (typebot_url) { try { if (!isURL(typebot_url, { require_tld: false })) { @@ -310,6 +361,10 @@ export class InstanceController { enabled: rabbitmq_enabled, events: rabbitmqEvents, }, + sqs: { + enabled: sqs_enabled, + events: sqsEvents, + }, typebot: { enabled: typebot_url ? true : false, url: typebot_url, @@ -404,6 +459,10 @@ export class InstanceController { enabled: rabbitmq_enabled, events: rabbitmqEvents, }, + sqs: { + enabled: sqs_enabled, + events: sqsEvents, + }, typebot: { enabled: typebot_url ? true : false, url: typebot_url, diff --git a/src/whatsapp/controllers/sqs.controller.ts b/src/whatsapp/controllers/sqs.controller.ts new file mode 100644 index 00000000..063e29ed --- /dev/null +++ b/src/whatsapp/controllers/sqs.controller.ts @@ -0,0 +1,56 @@ +import { Logger } from '../../config/logger.config'; +import { InstanceDto } from '../dto/instance.dto'; +import { SqsDto } from '../dto/sqs.dto'; +import { SqsService } from '../services/sqs.service'; + +const logger = new Logger('SqsController'); + +export class SqsController { + constructor(private readonly sqsService: SqsService) {} + + public async createSqs(instance: InstanceDto, data: SqsDto) { + logger.verbose('requested createSqs from ' + instance.instanceName + ' instance'); + + if (!data.enabled) { + logger.verbose('sqs disabled'); + data.events = []; + } + + if (data.events.length === 0) { + logger.verbose('sqs events empty'); + data.events = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'CALL', + 'NEW_JWT_TOKEN', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + 'CHAMA_AI_ACTION', + ]; + } + + return this.sqsService.create(instance, data); + } + + public async findSqs(instance: InstanceDto) { + logger.verbose('requested findSqs from ' + instance.instanceName + ' instance'); + return this.sqsService.find(instance); + } +} diff --git a/src/whatsapp/dto/instance.dto.ts b/src/whatsapp/dto/instance.dto.ts index 700fa099..84e2b1de 100644 --- a/src/whatsapp/dto/instance.dto.ts +++ b/src/whatsapp/dto/instance.dto.ts @@ -23,6 +23,8 @@ export class InstanceDto { websocket_events?: string[]; rabbitmq_enabled?: boolean; rabbitmq_events?: string[]; + sqs_enabled?: boolean; + sqs_events?: string[]; typebot_url?: string; typebot?: string; typebot_expire?: number; diff --git a/src/whatsapp/dto/sqs.dto.ts b/src/whatsapp/dto/sqs.dto.ts new file mode 100644 index 00000000..9b8aeedd --- /dev/null +++ b/src/whatsapp/dto/sqs.dto.ts @@ -0,0 +1,4 @@ +export class SqsDto { + enabled: boolean; + events?: string[]; +} diff --git a/src/whatsapp/models/index.ts b/src/whatsapp/models/index.ts index e79093f9..7903e5b5 100644 --- a/src/whatsapp/models/index.ts +++ b/src/whatsapp/models/index.ts @@ -7,6 +7,7 @@ export * from './message.model'; export * from './proxy.model'; export * from './rabbitmq.model'; export * from './settings.model'; +export * from './sqs.model'; export * from './typebot.model'; export * from './webhook.model'; export * from './websocket.model'; diff --git a/src/whatsapp/models/sqs.model.ts b/src/whatsapp/models/sqs.model.ts new file mode 100644 index 00000000..2d5f432f --- /dev/null +++ b/src/whatsapp/models/sqs.model.ts @@ -0,0 +1,18 @@ +import { Schema } from 'mongoose'; + +import { dbserver } from '../../libs/db.connect'; + +export class SqsRaw { + _id?: string; + enabled?: boolean; + events?: string[]; +} + +const sqsSchema = new Schema({ + _id: { type: String, _id: true }, + enabled: { type: Boolean, required: true }, + events: { type: [String], required: true }, +}); + +export const SqsModel = dbserver?.model(SqsRaw.name, sqsSchema, 'sqs'); +export type ISqsModel = typeof SqsModel; diff --git a/src/whatsapp/repository/repository.manager.ts b/src/whatsapp/repository/repository.manager.ts index 1c16fdef..ab4da1e3 100644 --- a/src/whatsapp/repository/repository.manager.ts +++ b/src/whatsapp/repository/repository.manager.ts @@ -14,6 +14,7 @@ import { MessageUpRepository } from './messageUp.repository'; import { ProxyRepository } from './proxy.repository'; import { RabbitmqRepository } from './rabbitmq.repository'; import { SettingsRepository } from './settings.repository'; +import { SqsRepository } from './sqs.repository'; import { TypebotRepository } from './typebot.repository'; import { WebhookRepository } from './webhook.repository'; import { WebsocketRepository } from './websocket.repository'; @@ -28,6 +29,7 @@ export class RepositoryBroker { public readonly settings: SettingsRepository, public readonly websocket: WebsocketRepository, public readonly rabbitmq: RabbitmqRepository, + public readonly sqs: SqsRepository, public readonly typebot: TypebotRepository, public readonly proxy: ProxyRepository, public readonly chamaai: ChamaaiRepository, @@ -63,6 +65,7 @@ export class RepositoryBroker { const settingsDir = join(storePath, 'settings'); const websocketDir = join(storePath, 'websocket'); const rabbitmqDir = join(storePath, 'rabbitmq'); + const sqsDir = join(storePath, 'sqs'); const typebotDir = join(storePath, 'typebot'); const proxyDir = join(storePath, 'proxy'); const chamaaiDir = join(storePath, 'chamaai'); @@ -108,6 +111,10 @@ export class RepositoryBroker { this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir); fs.mkdirSync(rabbitmqDir, { recursive: true }); } + if (!fs.existsSync(sqsDir)) { + this.logger.verbose('creating sqs dir: ' + sqsDir); + fs.mkdirSync(sqsDir, { recursive: true }); + } if (!fs.existsSync(typebotDir)) { this.logger.verbose('creating typebot dir: ' + typebotDir); fs.mkdirSync(typebotDir, { recursive: true }); diff --git a/src/whatsapp/repository/sqs.repository.ts b/src/whatsapp/repository/sqs.repository.ts new file mode 100644 index 00000000..50ea1cd3 --- /dev/null +++ b/src/whatsapp/repository/sqs.repository.ts @@ -0,0 +1,62 @@ +import { readFileSync } from 'fs'; +import { join } from 'path'; + +import { ConfigService } from '../../config/env.config'; +import { Logger } from '../../config/logger.config'; +import { IInsert, Repository } from '../abstract/abstract.repository'; +import { ISqsModel, SqsRaw } from '../models'; + +export class SqsRepository extends Repository { + constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) { + super(configService); + } + + private readonly logger = new Logger('SqsRepository'); + + public async create(data: SqsRaw, instance: string): Promise { + try { + this.logger.verbose('creating sqs'); + if (this.dbSettings.ENABLED) { + this.logger.verbose('saving sqs to db'); + const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true }); + + this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs'); + return { insertCount: insert.modifiedCount }; + } + + this.logger.verbose('saving sqs to store'); + + this.writeStore({ + path: join(this.storePath, 'sqs'), + fileName: instance, + data, + }); + + this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance); + + this.logger.verbose('sqs created'); + return { insertCount: 1 }; + } catch (error) { + return error; + } + } + + public async find(instance: string): Promise { + try { + this.logger.verbose('finding sqs'); + if (this.dbSettings.ENABLED) { + this.logger.verbose('finding sqs in db'); + return await this.sqsModel.findOne({ _id: instance }); + } + + this.logger.verbose('finding sqs in store'); + return JSON.parse( + readFileSync(join(this.storePath, 'sqs', instance + '.json'), { + encoding: 'utf-8', + }), + ) as SqsRaw; + } catch (error) { + return {}; + } + } +} diff --git a/src/whatsapp/routers/index.router.ts b/src/whatsapp/routers/index.router.ts index e35d21e4..fbe28ddd 100644 --- a/src/whatsapp/routers/index.router.ts +++ b/src/whatsapp/routers/index.router.ts @@ -13,6 +13,7 @@ import { ProxyRouter } from './proxy.router'; import { RabbitmqRouter } from './rabbitmq.router'; import { MessageRouter } from './sendMessage.router'; import { SettingsRouter } from './settings.router'; +import { SqsRouter } from './sqs.router'; import { TypebotRouter } from './typebot.router'; import { ViewsRouter } from './view.router'; import { WebhookRouter } from './webhook.router'; @@ -53,6 +54,7 @@ router .use('/settings', new SettingsRouter(...guards).router) .use('/websocket', new WebsocketRouter(...guards).router) .use('/rabbitmq', new RabbitmqRouter(...guards).router) + .use('/sqs', new SqsRouter(...guards).router) .use('/typebot', new TypebotRouter(...guards).router) .use('/proxy', new ProxyRouter(...guards).router) .use('/chamaai', new ChamaaiRouter(...guards).router); diff --git a/src/whatsapp/routers/sqs.router.ts b/src/whatsapp/routers/sqs.router.ts new file mode 100644 index 00000000..e1bf8e93 --- /dev/null +++ b/src/whatsapp/routers/sqs.router.ts @@ -0,0 +1,52 @@ +import { RequestHandler, Router } from 'express'; + +import { Logger } from '../../config/logger.config'; +import { instanceNameSchema, sqsSchema } from '../../validate/validate.schema'; +import { RouterBroker } from '../abstract/abstract.router'; +import { InstanceDto } from '../dto/instance.dto'; +import { SqsDto } from '../dto/sqs.dto'; +import { sqsController } from '../whatsapp.module'; +import { HttpStatus } from './index.router'; + +const logger = new Logger('SqsRouter'); + +export class SqsRouter extends RouterBroker { + constructor(...guards: RequestHandler[]) { + super(); + this.router + .post(this.routerPath('set'), ...guards, async (req, res) => { + logger.verbose('request received in setSqs'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: sqsSchema, + ClassRef: SqsDto, + execute: (instance, data) => sqsController.createSqs(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .get(this.routerPath('find'), ...guards, async (req, res) => { + logger.verbose('request received in findSqs'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: InstanceDto, + execute: (instance) => sqsController.findSqs(instance), + }); + + res.status(HttpStatus.OK).json(response); + }); + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/services/sqs.service.ts b/src/whatsapp/services/sqs.service.ts new file mode 100644 index 00000000..236d4ceb --- /dev/null +++ b/src/whatsapp/services/sqs.service.ts @@ -0,0 +1,35 @@ +import { Logger } from '../../config/logger.config'; +import { initQueues } from '../../libs/sqs.server'; +import { InstanceDto } from '../dto/instance.dto'; +import { SqsDto } from '../dto/sqs.dto'; +import { SqsRaw } from '../models'; +import { WAMonitoringService } from './monitor.service'; + +export class SqsService { + constructor(private readonly waMonitor: WAMonitoringService) {} + + private readonly logger = new Logger(SqsService.name); + + public create(instance: InstanceDto, data: SqsDto) { + this.logger.verbose('create sqs: ' + instance.instanceName); + this.waMonitor.waInstances[instance.instanceName].setSqs(data); + + initQueues(instance.instanceName, data.events); + return { sqs: { ...instance, sqs: data } }; + } + + public async find(instance: InstanceDto): Promise { + try { + this.logger.verbose('find sqs: ' + instance.instanceName); + const result = await this.waMonitor.waInstances[instance.instanceName].findSqs(); + + if (Object.keys(result).length === 0) { + throw new Error('Sqs not found'); + } + + return result; + } catch (error) { + return { enabled: false, events: [] }; + } + } +} diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index d1b5a62f..47a3bc50 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -60,6 +60,7 @@ import { Log, QrCode, Redis, + Sqs, Webhook, Websocket, } from '../../config/env.config'; @@ -70,6 +71,7 @@ import { getAMQP, removeQueues } from '../../libs/amqp.server'; import { dbserver } from '../../libs/db.connect'; import { RedisCache } from '../../libs/redis.client'; import { getIO } from '../../libs/socket.server'; +import { getSQS, removeQueues as removeQueuesSQS } from '../../libs/sqs.server'; import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; import { @@ -113,7 +115,7 @@ import { SendTextDto, StatusMessage, } from '../dto/sendMessage.dto'; -import { ChamaaiRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, TypebotRaw } from '../models'; +import { ChamaaiRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, SqsRaw, TypebotRaw } from '../models'; import { ChatRaw } from '../models/chat.model'; import { ChatwootRaw } from '../models/chatwoot.model'; import { ContactRaw } from '../models/contact.model'; @@ -149,6 +151,7 @@ export class WAStartupService { private readonly localSettings: wa.LocalSettings = {}; private readonly localWebsocket: wa.LocalWebsocket = {}; private readonly localRabbitmq: wa.LocalRabbitmq = {}; + private readonly localSqs: wa.LocalSqs = {}; public readonly localTypebot: wa.LocalTypebot = {}; private readonly localProxy: wa.LocalProxy = {}; private readonly localChamaai: wa.LocalChamaai = {}; @@ -504,6 +507,48 @@ export class WAStartupService { } } + private async loadSqs() { + this.logger.verbose('Loading sqs'); + const data = await this.repository.sqs.find(this.instanceName); + + this.localSqs.enabled = data?.enabled; + this.logger.verbose(`Sqs enabled: ${this.localSqs.enabled}`); + + this.localSqs.events = data?.events; + this.logger.verbose(`Sqs events: ${this.localSqs.events}`); + + this.logger.verbose('Sqs loaded'); + } + + public async setSqs(data: SqsRaw) { + this.logger.verbose('Setting sqs'); + await this.repository.sqs.create(data, this.instanceName); + this.logger.verbose(`Sqs events: ${data.events}`); + Object.assign(this.localSqs, data); + this.logger.verbose('Sqs set'); + } + + public async findSqs() { + this.logger.verbose('Finding sqs'); + const data = await this.repository.sqs.find(this.instanceName); + + if (!data) { + this.logger.verbose('Sqs not found'); + throw new NotFoundException('Sqs not found'); + } + + this.logger.verbose(`Sqs events: ${data.events}`); + return data; + } + + public async removeSqsQueues() { + this.logger.verbose('Removing sqs'); + + if (this.localSqs.enabled) { + removeQueuesSQS(this.instanceName, this.localSqs.events); + } + } + private async loadTypebot() { this.logger.verbose('Loading typebot'); const data = await this.repository.typebot.find(this.instanceName); @@ -648,6 +693,7 @@ export class WAStartupService { const webhookLocal = this.localWebhook.events; const websocketLocal = this.localWebsocket.events; const rabbitmqLocal = this.localRabbitmq.events; + const sqsLocal = this.localSqs.events; const serverUrl = this.configService.get('SERVER').URL; const we = event.replace(/[.-]/gm, '_').toUpperCase(); const transformedWe = we.replace(/_/gm, '-').toLowerCase(); @@ -720,6 +766,76 @@ export class WAStartupService { } } + if (this.localSqs.enabled) { + const sqs = getSQS(); + + if (sqs) { + if (Array.isArray(sqsLocal) && sqsLocal.includes(we)) { + const eventFormatted = `${event.replace('.', '_').toLowerCase()}`; + + const queueName = `${this.instanceName}_${eventFormatted}.fifo`; + + const sqsConfig = this.configService.get('SQS'); + + const sqsUrl = `https://sqs.${sqsConfig.REGION}.amazonaws.com/${sqsConfig.ACCOUNT_ID}/${queueName}`; + + const message = { + event, + instance: this.instance.name, + data, + server_url: serverUrl, + date_time: now, + sender: this.wuid, + }; + + if (expose && instanceApikey) { + message['apikey'] = instanceApikey; + } + + const params = { + MessageBody: JSON.stringify(message), + MessageGroupId: 'evolution', + MessageDeduplicationId: `${this.instanceName}_${eventFormatted}_${Date.now()}`, + QueueUrl: sqsUrl, + }; + + sqs.sendMessage(params, (err, data) => { + if (err) { + this.logger.error({ + local: WAStartupService.name + '.sendData-SQS', + message: err?.message, + hostName: err?.hostname, + code: err?.code, + stack: err?.stack, + name: err?.name, + url: queueName, + server_url: serverUrl, + }); + } else { + if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { + const logData = { + local: WAStartupService.name + '.sendData-SQS', + event, + instance: this.instance.name, + data, + server_url: serverUrl, + apikey: (expose && instanceApikey) || null, + date_time: now, + sender: this.wuid, + }; + + if (expose && instanceApikey) { + logData['apikey'] = instanceApikey; + } + + this.logger.log(logData); + } + } + }); + } + } + } + if (this.configService.get('WEBSOCKET')?.ENABLED && this.localWebsocket.enabled) { this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name); if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) { @@ -1165,6 +1281,7 @@ export class WAStartupService { this.loadSettings(); this.loadWebsocket(); this.loadRabbitmq(); + this.loadSqs(); this.loadTypebot(); this.loadProxy(); this.loadChamaai(); @@ -1520,7 +1637,6 @@ export class WAStartupService { this.logger.verbose('Event received: messages.upsert'); const received = messages[0]; - console.log(received, type); if ( type !== 'notify' || !received?.message || diff --git a/src/whatsapp/types/wa.types.ts b/src/whatsapp/types/wa.types.ts index 9f326c8a..27582001 100644 --- a/src/whatsapp/types/wa.types.ts +++ b/src/whatsapp/types/wa.types.ts @@ -84,6 +84,11 @@ export declare namespace wa { events?: string[]; }; + export type LocalSqs = { + enabled?: boolean; + events?: string[]; + }; + type Session = { remoteJid?: string; sessionId?: string; diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index a37e98ef..7211f8e6 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -12,6 +12,7 @@ import { ProxyController } from './controllers/proxy.controller'; import { RabbitmqController } from './controllers/rabbitmq.controller'; import { SendMessageController } from './controllers/sendMessage.controller'; import { SettingsController } from './controllers/settings.controller'; +import { SqsController } from './controllers/sqs.controller'; import { TypebotController } from './controllers/typebot.controller'; import { ViewsController } from './controllers/views.controller'; import { WebhookController } from './controllers/webhook.controller'; @@ -27,6 +28,7 @@ import { ProxyModel, RabbitmqModel, SettingsModel, + SqsModel, TypebotModel, WebhookModel, WebsocketModel, @@ -42,6 +44,7 @@ import { ProxyRepository } from './repository/proxy.repository'; import { RabbitmqRepository } from './repository/rabbitmq.repository'; import { RepositoryBroker } from './repository/repository.manager'; import { SettingsRepository } from './repository/settings.repository'; +import { SqsRepository } from './repository/sqs.repository'; import { TypebotRepository } from './repository/typebot.repository'; import { WebhookRepository } from './repository/webhook.repository'; import { WebsocketRepository } from './repository/websocket.repository'; @@ -52,6 +55,7 @@ import { WAMonitoringService } from './services/monitor.service'; import { ProxyService } from './services/proxy.service'; import { RabbitmqService } from './services/rabbitmq.service'; import { SettingsService } from './services/settings.service'; +import { SqsService } from './services/sqs.service'; import { TypebotService } from './services/typebot.service'; import { WebhookService } from './services/webhook.service'; import { WebsocketService } from './services/websocket.service'; @@ -68,6 +72,7 @@ const websocketRepository = new WebsocketRepository(WebsocketModel, configServic const proxyRepository = new ProxyRepository(ProxyModel, configService); const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService); const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService); +const sqsRepository = new SqsRepository(SqsModel, configService); const chatwootRepository = new ChatwootRepository(ChatwootModel, configService); const settingsRepository = new SettingsRepository(SettingsModel, configService); const authRepository = new AuthRepository(AuthModel, configService); @@ -82,6 +87,7 @@ export const repository = new RepositoryBroker( settingsRepository, websocketRepository, rabbitmqRepository, + sqsRepository, typebotRepository, proxyRepository, chamaaiRepository, @@ -120,6 +126,10 @@ const rabbitmqService = new RabbitmqService(waMonitor); export const rabbitmqController = new RabbitmqController(rabbitmqService); +const sqsService = new SqsService(waMonitor); + +export const sqsController = new SqsController(sqsService); + const chatwootService = new ChatwootService(waMonitor, configService); export const chatwootController = new ChatwootController(chatwootService, configService); @@ -139,6 +149,7 @@ export const instanceController = new InstanceController( settingsService, websocketService, rabbitmqService, + sqsService, typebotService, cache, ); From a277d366969c7d0eba52ca1fdabc1ed82af42451 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 20 Nov 2023 17:53:29 -0300 Subject: [PATCH 25/68] feat: sqs --- CHANGELOG.md | 5 ++++- Dockerfile | 2 +- package.json | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b359e6..ef25397c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -# 1.5.5 (develop) +# 1.6.0 (develop) + +### Feature +* Added AWS SQS Integration ### Fixed diff --git a/Dockerfile b/Dockerfile index f60ed48d..8e610d12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:20.7.0-alpine -LABEL version="1.5.5" description="Api to control whatsapp features through http requests." +LABEL version="1.6.0" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL contact="contato@agenciadgcode.com" diff --git a/package.json b/package.json index 6d97cf0b..8625030a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "1.5.5", + "version": "1.6.0", "description": "Rest api for communication with WhatsApp", "main": "./dist/src/main.js", "scripts": { From e17baddf016835bda085f3f7c661ade1732bfc62 Mon Sep 17 00:00:00 2001 From: jaison-x Date: Fri, 24 Nov 2023 18:59:19 -0300 Subject: [PATCH 26/68] fix: workaround to manage param data as an array in mongodb When saving data from a group session (sender-key-xxxxx@g.us::xxxxx::xx) we receive the data param as an array. In mongodb we can't save an array as a root document. Bacause this, in this case we save the array in a property called content_array. --- src/utils/use-multi-file-auth-state-db.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/utils/use-multi-file-auth-state-db.ts b/src/utils/use-multi-file-auth-state-db.ts index a021372a..237b15d0 100644 --- a/src/utils/use-multi-file-auth-state-db.ts +++ b/src/utils/use-multi-file-auth-state-db.ts @@ -25,7 +25,14 @@ export async function useMultiFileAuthStateDb( const writeData = async (data: any, key: string): Promise => { try { await client.connect(); - return await collection.replaceOne({ _id: key }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), { + let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer)); + if (Array.isArray(msgParsed)) { + msgParsed = { + _id: key, + content_array: msgParsed, + }; + } + return await collection.replaceOne({ _id: key }, msgParsed, { upsert: true, }); } catch (error) { @@ -36,7 +43,10 @@ export async function useMultiFileAuthStateDb( const readData = async (key: string): Promise => { try { await client.connect(); - const data = await collection.findOne({ _id: key }); + let data = (await collection.findOne({ _id: key })) as any; + if (data?.content_array) { + data = data.content_array; + } const creds = JSON.stringify(data); return JSON.parse(creds, BufferJSON.reviver); } catch (error) { From edeb970a82288f8d83b7e9d8f60e718a871a549c Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 27 Nov 2023 19:57:59 -0300 Subject: [PATCH 27/68] fix: fixed lids messages --- .DS_Store | Bin 8196 -> 8196 bytes src/whatsapp/services/whatsapp.service.ts | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.DS_Store b/.DS_Store index 34664d41769177fab1fa2c048600c0fac7e10380..0ae7ca6cd2d3b1da20e291685617f13b31a9cf8a 100644 GIT binary patch delta 40 pcmZp1XmOa}XEU^hRb=4KuNM%K-}qE$>23mP`FOR&K>wEzYI4B7wy delta 290 zcmZp1XmOa}dGU^hRb?q(hVM%Mabh7yK+h9ZX4oOHwB3~<@p zd>5Cboctu9C`YVanD4HZqmJk*QV6Ih$Uw3ozyPS7fps&FXf@Mjb_uq@;F1a^0FFaL A&Hw-a diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 47a3bc50..34aa6d06 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1326,6 +1326,7 @@ export class WAStartupService { browser, version, markOnlineOnConnect: this.localSettings.always_online, + retryRequestDelayMs: 10, connectTimeoutMs: 60_000, qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, @@ -1335,7 +1336,7 @@ export class WAStartupService { generateHighQualityLinkPreview: true, syncFullHistory: true, userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, + transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { const requiresPatch = !!(message.buttonsMessage || message.listMessage || message.templateMessage); if (requiresPatch) { From 57fb3c978597fe5d5938ecdb6afbd13ba4d2d89b Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 27 Nov 2023 19:58:23 -0300 Subject: [PATCH 28/68] fix: fixed lids messages --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef25397c..1bf7e86a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Adjusts in proxy * Adjusts in start session for Typebot * Added mimetype field when sending media +* Fixed lids messages # 1.5.4 (2023-10-09 20:43) From 26d3ff97ce9e024da8e40b7ae51c5c2a7ba6af74 Mon Sep 17 00:00:00 2001 From: raimartinsb Date: Tue, 28 Nov 2023 11:10:29 -0300 Subject: [PATCH 29/68] fix messages not received: error handling when updating contact --- src/whatsapp/services/chatwoot.service.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index b19fa31f..463f81d7 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -338,14 +338,18 @@ export class ChatwootService { } this.logger.verbose('update contact in chatwoot'); - const contact = await client.contacts.update({ - accountId: this.provider.account_id, - id, - data, - }); + try { + const contact = await client.contacts.update({ + accountId: this.provider.account_id, + id, + data, + }); - this.logger.verbose('contact updated'); - return contact; + this.logger.verbose('contact updated'); + return contact; + } catch (error) { + this.logger.error(error); + } } public async findContact(instance: InstanceDto, phoneNumber: string) { @@ -488,6 +492,9 @@ export class ChatwootService { avatar_url: picture_url.profilePictureUrl || null, }); } + if (!contact) { + contact = await this.findContact(instance, chatId); + } } else { const jid = isGroup ? null : body.key.remoteJid; contact = await this.createContact( From a5c5879e4fbab345147a87efb177e22740f371c2 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 30 Nov 2023 07:01:08 -0300 Subject: [PATCH 30/68] fix: adjusts in validation to messages.upsert --- src/whatsapp/services/whatsapp.service.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 34aa6d06..6fb92f7f 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1638,13 +1638,7 @@ export class WAStartupService { this.logger.verbose('Event received: messages.upsert'); const received = messages[0]; - if ( - type !== 'notify' || - !received?.message || - received.message?.protocolMessage || - // received.message.senderKeyDistributionMessage || - received.message?.pollUpdateMessage - ) { + if (type !== 'notify' || received.message?.protocolMessage || received.message?.pollUpdateMessage) { this.logger.verbose('message rejected'); return; } From 4362de219893cdeff5111fac51006a8ff7817d8c Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 30 Nov 2023 07:01:26 -0300 Subject: [PATCH 31/68] fix: adjusts in validation to messages.upsert --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf7e86a..ed5ee8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Adjusts in start session for Typebot * Added mimetype field when sending media * Fixed lids messages +* Ajusts in validations to messages.upsert # 1.5.4 (2023-10-09 20:43) From 4fa895086e8564659e369c610ceb07d0bc48a0f4 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 30 Nov 2023 07:02:25 -0300 Subject: [PATCH 32/68] fix: adjusts in validation to messages.upsert --- src/whatsapp/services/whatsapp.service.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index e094d4ac..75a04a65 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -2283,18 +2283,17 @@ export class WAStartupService { !message['conversation'] && sender !== 'status@broadcast' ) { - if (message['reactionMessage']) { this.logger.verbose('Sending reaction'); return await this.client.sendMessage( - sender, - { - react: { - text: message['reactionMessage']['text'], - key: message['reactionMessage']['key'] - } - } as unknown as AnyMessageContent, - option as unknown as MiscMessageGenerationOptions, + sender, + { + react: { + text: message['reactionMessage']['text'], + key: message['reactionMessage']['key'], + }, + } as unknown as AnyMessageContent, + option as unknown as MiscMessageGenerationOptions, ); } From 3c19bdfaa9e3c3d977b9680e641f5c433236ee04 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 30 Nov 2023 07:09:11 -0300 Subject: [PATCH 33/68] fix: Removed await from webhook when sending a message --- CHANGELOG.md | 3 ++ src/whatsapp/services/whatsapp.service.ts | 34 +++++++++++------------ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed5ee8d7..c92a192d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ * Added mimetype field when sending media * Fixed lids messages * Ajusts in validations to messages.upsert +* Fixed messages not received: error handling when updating contact in chatwoot +* Fix workaround to manage param data as an array in mongodb +* Removed await from webhook when sending a message # 1.5.4 (2023-10-09 20:43) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 75a04a65..03d092b1 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1466,10 +1466,10 @@ export class WAStartupService { } this.logger.verbose('Sending data to webhook in event CHATS_UPSERT'); - await this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); + this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); this.logger.verbose('Inserting chats in database'); - await this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); + this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); }, 'chats.update': async ( @@ -1487,7 +1487,7 @@ export class WAStartupService { }); this.logger.verbose('Sending data to webhook in event CHATS_UPDATE'); - await this.sendDataWebhook(Events.CHATS_UPDATE, chatsRaw); + this.sendDataWebhook(Events.CHATS_UPDATE, chatsRaw); }, 'chats.delete': async (chats: string[]) => { @@ -1502,7 +1502,7 @@ export class WAStartupService { ); this.logger.verbose('Sending data to webhook in event CHATS_DELETE'); - await this.sendDataWebhook(Events.CHATS_DELETE, [...chats]); + this.sendDataWebhook(Events.CHATS_DELETE, [...chats]); }, }; @@ -1531,10 +1531,10 @@ export class WAStartupService { } this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); + this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); this.logger.verbose('Inserting contacts in database'); - await this.repository.contact.insert(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); + this.repository.contact.insert(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); }, 'contacts.update': async (contacts: Partial[], database: Database) => { @@ -1552,10 +1552,10 @@ export class WAStartupService { } this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); + this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); this.logger.verbose('Updating contacts in database'); - await this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); + this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); }, }; @@ -1585,10 +1585,10 @@ export class WAStartupService { }); this.logger.verbose('Sending data to webhook in event CHATS_SET'); - await this.sendDataWebhook(Events.CHATS_SET, chatsRaw); + this.sendDataWebhook(Events.CHATS_SET, chatsRaw); this.logger.verbose('Inserting chats in database'); - await this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); + this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); } const messagesRaw: MessageRaw[] = []; @@ -1702,7 +1702,7 @@ export class WAStartupService { this.logger.log(messageRaw); this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); - await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); if (this.localChatwoot.enabled) { await this.chatwootService.eventWhatsapp( @@ -1764,7 +1764,7 @@ export class WAStartupService { }; this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); + this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); if (this.localChatwoot.enabled) { await this.chatwootService.eventWhatsapp( @@ -1782,10 +1782,10 @@ export class WAStartupService { this.logger.verbose('Contact not found in database'); this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); + this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); this.logger.verbose('Inserting contact in database'); - await this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); + this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); }, 'messages.update': async (args: WAMessageUpdate[], database: Database, settings: SettingsRaw) => { @@ -1829,7 +1829,7 @@ export class WAStartupService { this.logger.verbose('Message deleted'); this.logger.verbose('Sending data to webhook in event MESSAGE_DELETE'); - await this.sendDataWebhook(Events.MESSAGES_DELETE, key); + this.sendDataWebhook(Events.MESSAGES_DELETE, key); const message: MessageUpdateRaw = { ...key, @@ -1860,10 +1860,10 @@ export class WAStartupService { this.logger.verbose(message); this.logger.verbose('Sending data to webhook in event MESSAGES_UPDATE'); - await this.sendDataWebhook(Events.MESSAGES_UPDATE, message); + this.sendDataWebhook(Events.MESSAGES_UPDATE, message); this.logger.verbose('Inserting message in database'); - await this.repository.messageUpdate.insert([message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE); + this.repository.messageUpdate.insert([message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE); } } }, From a1d13f8ff378ade05f8a77c67f10f01d73569dff Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 30 Nov 2023 07:12:14 -0300 Subject: [PATCH 34/68] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c92a192d..1c164e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Fixed messages not received: error handling when updating contact in chatwoot * Fix workaround to manage param data as an array in mongodb * Removed await from webhook when sending a message +* Update typebot.service.ts - element.underline change ~ for * # 1.5.4 (2023-10-09 20:43) From 876320b8496ba50aeef7c2a12ed848f5e3a73862 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 30 Nov 2023 17:13:05 -0300 Subject: [PATCH 35/68] feat: added compatibility with typebot v2 --- CHANGELOG.md | 1 + Docker/.env.example | 8 ++++++++ Dockerfile | 8 ++++++++ src/config/env.config.ts | 5 +++++ src/dev-env.yml | 10 ++++++++++ src/whatsapp/services/typebot.service.ts | 19 +++++++++++++------ src/whatsapp/services/whatsapp.service.ts | 2 +- src/whatsapp/whatsapp.module.ts | 2 +- 8 files changed, 47 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c164e29..021449e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Feature * Added AWS SQS Integration +* Added compatibility with typebot v2 ### Fixed diff --git a/Docker/.env.example b/Docker/.env.example index 88be79fe..b170ac28 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -51,6 +51,12 @@ RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672 WEBSOCKET_ENABLED=false +SQS_ENABLED=false +SQS_ACCESS_KEY_ID= +SQS_SECRET_ACCESS_KEY= +SQS_ACCOUNT_ID= +SQS_REGION= + # Global Webhook Settings # Each instance's Webhook URL and events will be requested at the time it is created ## Define a global webhook that will listen for enabled events from all instances @@ -99,6 +105,8 @@ CONFIG_SESSION_PHONE_NAME=Chrome QRCODE_LIMIT=30 QRCODE_COLOR=#198754 +TYPEBOT_API_VERSION=v1 + # Defines an authentication type for the api # We recommend using the apikey because it will allow you to use a custom token, # if you use jwt, a random token will be generated and may be expired and you will have to generate a new token diff --git a/Dockerfile b/Dockerfile index 8e610d12..0195a249 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,6 +56,12 @@ ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672 ENV WEBSOCKET_ENABLED=false +ENV SQS_ENABLED=false +ENV SQS_ACCESS_KEY_ID= +ENV SQS_SECRET_ACCESS_KEY= +ENV SQS_ACCOUNT_ID= +ENV SQS_REGION= + ENV WEBHOOK_GLOBAL_URL= ENV WEBHOOK_GLOBAL_ENABLED=false @@ -98,6 +104,8 @@ ENV CONFIG_SESSION_PHONE_NAME=Chrome ENV QRCODE_LIMIT=30 ENV QRCODE_COLOR=#198754 +ENV TYPEBOT_API_VERSION=v1 + ENV AUTHENTICATION_TYPE=apikey ENV AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976 diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 118836b7..dcb90fbc 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -132,6 +132,7 @@ export type SslConf = { PRIVKEY: string; FULLCHAIN: string }; export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook }; export type ConfigSessionPhone = { CLIENT: string; NAME: string }; export type QrCode = { LIMIT: number; COLOR: string }; +export type Typebot = { API_VERSION: string }; export type Production = boolean; export interface Env { @@ -150,6 +151,7 @@ export interface Env { WEBHOOK: Webhook; CONFIG_SESSION_PHONE: ConfigSessionPhone; QRCODE: QrCode; + TYPEBOT: Typebot; AUTHENTICATION: Auth; PRODUCTION?: Production; CHATWOOT?: Chatwoot; @@ -305,6 +307,9 @@ export class ConfigService { LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30, COLOR: process.env.QRCODE_COLOR || '#198754', }, + TYPEBOT: { + API_VERSION: process.env?.TYPEBOT_API_VERSION || 'v1', + }, AUTHENTICATION: { TYPE: process.env.AUTHENTICATION_TYPE as 'apikey', API_KEY: { diff --git a/src/dev-env.yml b/src/dev-env.yml index 7af78d40..b2a2e521 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -83,6 +83,13 @@ RABBITMQ: ENABLED: false URI: "amqp://guest:guest@localhost:5672" +SQS: + ENABLED: true + ACCESS_KEY_ID: "" + SECRET_ACCESS_KEY: "" + ACCOUNT_ID: "" + REGION: "us-east-1" + WEBSOCKET: ENABLED: false @@ -139,6 +146,9 @@ QRCODE: LIMIT: 30 COLOR: "#198754" +TYPEBOT: + API_VERSION: 'v1' # v1 | v2 + # Defines an authentication type for the api # We recommend using the apikey because it will allow you to use a custom token, # if you use jwt, a random token will be generated and may be expired and you will have to generate a new token diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index fd7b16fb..4ed9b04b 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -1,5 +1,6 @@ import axios from 'axios'; +import { ConfigService, Typebot } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { InstanceDto } from '../dto/instance.dto'; import { Session, TypebotDto } from '../dto/typebot.dto'; @@ -8,7 +9,7 @@ import { Events } from '../types/wa.types'; import { WAMonitoringService } from './monitor.service'; export class TypebotService { - constructor(private readonly waMonitor: WAMonitoringService) {} + constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {} private readonly logger = new Logger(TypebotService.name); @@ -162,7 +163,8 @@ export class TypebotService { }; try { - const request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + const version = this.configService.get('TYPEBOT').API_VERSION; + const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); await this.sendWAMessage( instance, @@ -251,7 +253,8 @@ export class TypebotService { }; try { - const request = await axios.post(data.url + '/api/v1/sendMessage', reqData); + const version = this.configService.get('TYPEBOT').API_VERSION; + const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); if (request?.data?.sessionId) { data.sessions.push({ @@ -554,7 +557,8 @@ export class TypebotService { }; try { - const request = await axios.post(url + '/api/v1/sendMessage', reqData); + const version = this.configService.get('TYPEBOT').API_VERSION; + const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); await this.sendWAMessage( instance, @@ -640,7 +644,9 @@ export class TypebotService { let request: any; try { - request = await axios.post(url + '/api/v1/sendMessage', reqData); + const version = this.configService.get('TYPEBOT').API_VERSION; + request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); + await this.sendWAMessage( instance, remoteJid, @@ -719,7 +725,8 @@ export class TypebotService { sessionId: session.sessionId.split('-')[1], }; - const request = await axios.post(url + '/api/v1/sendMessage', reqData); + const version = this.configService.get('TYPEBOT').API_VERSION; + const request = await axios.post(`${url}/api/${version}/sendMessage`, reqData); await this.sendWAMessage( instance, diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 03d092b1..6002a3e4 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -166,7 +166,7 @@ export class WAStartupService { private chatwootService = new ChatwootService(waMonitor, this.configService); - private typebotService = new TypebotService(waMonitor); + private typebotService = new TypebotService(waMonitor, this.configService); private chamaaiService = new ChamaaiService(waMonitor, this.configService); diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index 7211f8e6..e116cc9c 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -102,7 +102,7 @@ export const waMonitor = new WAMonitoringService(eventEmitter, configService, re const authService = new AuthService(configService, waMonitor, repository); -const typebotService = new TypebotService(waMonitor); +const typebotService = new TypebotService(waMonitor, configService); export const typebotController = new TypebotController(typebotService); From 78c03d8f2f557a4e56987fab7fcd2bc4a09c0049 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 08:21:36 -0300 Subject: [PATCH 36/68] test: adjusts wasocket --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 6002a3e4..b4abe170 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1336,7 +1336,7 @@ export class WAStartupService { generateHighQualityLinkPreview: true, syncFullHistory: true, userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, + transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { const requiresPatch = !!(message.buttonsMessage || message.listMessage || message.templateMessage); if (requiresPatch) { From cf7de369b2e05d963c0562e4252d2015a7efb608 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 08:22:40 -0300 Subject: [PATCH 37/68] test: adjusts wasocket --- src/whatsapp/services/whatsapp.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index b4abe170..e8d9ec2a 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1326,7 +1326,7 @@ export class WAStartupService { browser, version, markOnlineOnConnect: this.localSettings.always_online, - retryRequestDelayMs: 10, + // retryRequestDelayMs: 10, connectTimeoutMs: 60_000, qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, @@ -1336,7 +1336,7 @@ export class WAStartupService { generateHighQualityLinkPreview: true, syncFullHistory: true, userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, + transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { const requiresPatch = !!(message.buttonsMessage || message.listMessage || message.templateMessage); if (requiresPatch) { From 1631c2c342018c7b40217c7dbef055e077739aa7 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 09:42:56 -0300 Subject: [PATCH 38/68] test: adjusts wasocket --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index e8d9ec2a..6002a3e4 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1326,7 +1326,7 @@ export class WAStartupService { browser, version, markOnlineOnConnect: this.localSettings.always_online, - // retryRequestDelayMs: 10, + retryRequestDelayMs: 10, connectTimeoutMs: 60_000, qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, From e49f30641e1468104e8076e98eba7478612d5009 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 14:30:20 -0300 Subject: [PATCH 39/68] test: adjusts wasocket --- src/whatsapp/services/monitor.service.ts | 3 ++- src/whatsapp/services/whatsapp.service.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 49cb6f91..011758b0 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -277,7 +277,7 @@ export class WAMonitoringService { const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache); instance.instanceName = name; this.logger.verbose('Instance loaded: ' + name); - + console.log('Instance loaded: ' + name); await instance.connectToWhatsapp(); this.logger.verbose('connectToWhatsapp: ' + name); @@ -304,6 +304,7 @@ export class WAMonitoringService { if (collections.length > 0) { this.logger.verbose('Reading collections and setting instances'); + console.log(collections); await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, '')))); } else { this.logger.verbose('No collections found'); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 6002a3e4..ac755ad4 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1656,7 +1656,7 @@ export class WAStartupService { if ( (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) || - received?.message.imageMessage + received?.message?.imageMessage ) { const buffer = await downloadMediaMessage( { key: received.key, message: received?.message }, From d75163aa57f9e8bcf93c66f2324e1f3a99d42c47 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 15:04:57 -0300 Subject: [PATCH 40/68] test: adjusts wasocket --- src/whatsapp/services/monitor.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 011758b0..45cd4a1f 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -277,7 +277,6 @@ export class WAMonitoringService { const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache); instance.instanceName = name; this.logger.verbose('Instance loaded: ' + name); - console.log('Instance loaded: ' + name); await instance.connectToWhatsapp(); this.logger.verbose('connectToWhatsapp: ' + name); @@ -304,7 +303,6 @@ export class WAMonitoringService { if (collections.length > 0) { this.logger.verbose('Reading collections and setting instances'); - console.log(collections); await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, '')))); } else { this.logger.verbose('No collections found'); From 2de0b61726c393ebf1e756ecd329d2a648b29b78 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 20:09:19 -0300 Subject: [PATCH 41/68] fix: adjusts in proxy --- src/whatsapp/controllers/instance.controller.ts | 17 +++++++++++++++++ src/whatsapp/dto/instance.dto.ts | 3 +-- src/whatsapp/services/whatsapp.service.ts | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 5409a7dd..66b94396 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -11,6 +11,7 @@ import { RepositoryBroker } from '../repository/repository.manager'; import { AuthService, OldToken } from '../services/auth.service'; import { ChatwootService } from '../services/chatwoot.service'; import { WAMonitoringService } from '../services/monitor.service'; +import { ProxyService } from '../services/proxy.service'; import { RabbitmqService } from '../services/rabbitmq.service'; import { SettingsService } from '../services/settings.service'; import { SqsService } from '../services/sqs.service'; @@ -32,6 +33,7 @@ export class InstanceController { private readonly settingsService: SettingsService, private readonly websocketService: WebsocketService, private readonly rabbitmqService: RabbitmqService, + private readonly proxyService: ProxyService, private readonly sqsService: SqsService, private readonly typebotService: TypebotService, private readonly cache: RedisCache, @@ -73,6 +75,7 @@ export class InstanceController { typebot_delay_message, typebot_unknown_message, typebot_listening_from_me, + proxy, }: InstanceDto) { try { this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); @@ -247,6 +250,18 @@ export class InstanceController { } } + if (proxy) { + this.logger.verbose('creating proxy'); + try { + this.proxyService.create(instance, { + enabled: true, + proxy, + }); + } catch (error) { + this.logger.log(error); + } + } + let sqsEvents: string[]; if (sqs_enabled) { @@ -377,6 +392,7 @@ export class InstanceController { }, settings, qrcode: getQrcode, + proxy, }; this.logger.verbose('instance created'); @@ -486,6 +502,7 @@ export class InstanceController { name_inbox: instance.instanceName, webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`, }, + proxy, }; } catch (error) { this.logger.error(error.message[0]); diff --git a/src/whatsapp/dto/instance.dto.ts b/src/whatsapp/dto/instance.dto.ts index 84e2b1de..c63620c5 100644 --- a/src/whatsapp/dto/instance.dto.ts +++ b/src/whatsapp/dto/instance.dto.ts @@ -32,6 +32,5 @@ export class InstanceDto { typebot_delay_message?: number; typebot_unknown_message?: string; typebot_listening_from_me?: boolean; - proxy_enabled?: boolean; - proxy_proxy?: string; + proxy?: string; } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index ac755ad4..d1a7fb5b 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1297,7 +1297,7 @@ export class WAStartupService { let options; if (this.localProxy.enabled) { - this.logger.verbose('Proxy enabled'); + this.logger.info('Proxy enabled: ' + this.localProxy.proxy); if (this.localProxy.proxy.includes('proxyscrape')) { const response = await axios.get(this.localProxy.proxy); From 359bd9f762230d74279bbc8c9368effc698189be Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 20:09:22 -0300 Subject: [PATCH 42/68] fix: adjusts in proxy --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 021449e3..80613150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Fix workaround to manage param data as an array in mongodb * Removed await from webhook when sending a message * Update typebot.service.ts - element.underline change ~ for * +* Adjusts in proxy # 1.5.4 (2023-10-09 20:43) From 4c69b059d4e9ae32c75a1ffb22a57acdae900239 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 1 Dec 2023 21:24:38 -0300 Subject: [PATCH 43/68] proxy --- src/whatsapp/controllers/instance.controller.ts | 12 ++++++++---- src/whatsapp/services/proxy.service.ts | 4 ++-- src/whatsapp/services/whatsapp.service.ts | 6 ++++-- src/whatsapp/whatsapp.module.ts | 1 + 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 66b94396..4c4e5cfb 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -253,10 +253,14 @@ export class InstanceController { if (proxy) { this.logger.verbose('creating proxy'); try { - this.proxyService.create(instance, { - enabled: true, - proxy, - }); + this.proxyService.create( + instance, + { + enabled: true, + proxy, + }, + false, + ); } catch (error) { this.logger.log(error); } diff --git a/src/whatsapp/services/proxy.service.ts b/src/whatsapp/services/proxy.service.ts index c6631671..1039fd5c 100644 --- a/src/whatsapp/services/proxy.service.ts +++ b/src/whatsapp/services/proxy.service.ts @@ -9,9 +9,9 @@ export class ProxyService { private readonly logger = new Logger(ProxyService.name); - public create(instance: InstanceDto, data: ProxyDto) { + public create(instance: InstanceDto, data: ProxyDto, reload = true) { this.logger.verbose('create proxy: ' + instance.instanceName); - this.waMonitor.waInstances[instance.instanceName].setProxy(data); + this.waMonitor.waInstances[instance.instanceName].setProxy(data, reload); return { proxy: { ...instance, proxy: data } }; } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index d1a7fb5b..5c3edc44 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -620,14 +620,16 @@ export class WAStartupService { this.logger.verbose('Proxy loaded'); } - public async setProxy(data: ProxyRaw) { + public async setProxy(data: ProxyRaw, reload = true) { this.logger.verbose('Setting proxy'); await this.repository.proxy.create(data, this.instanceName); this.logger.verbose(`Proxy proxy: ${data.proxy}`); Object.assign(this.localProxy, data); this.logger.verbose('Proxy set'); - this.reloadConnection(); + if (reload) { + this.reloadConnection(); + } } public async findProxy() { diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index e116cc9c..0bb9409f 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -149,6 +149,7 @@ export const instanceController = new InstanceController( settingsService, websocketService, rabbitmqService, + proxyService, sqsService, typebotService, cache, From aa891489f0ad5b2e09d2b65d177d52af7370b2b3 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Sun, 3 Dec 2023 21:00:26 -0300 Subject: [PATCH 44/68] Add session creation for typebot service --- src/whatsapp/services/typebot.service.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 4ed9b04b..14b04383 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -69,6 +69,20 @@ export class TypebotService { session.status = status; } }); + } else { + const session: Session = { + remoteJid: remoteJid, + sessionId: Math.floor(Math.random() * 10000000000).toString(), + status: status, + createdAt: Date.now(), + updateAt: Date.now(), + prefilledVariables: { + remoteJid: remoteJid, + pushName: '', + additionalData: {}, + }, + }; + findData.sessions.push(session); } const typebotData = { From ee0f0f0be0457119e33d98aa055cb93ddbe3731b Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:31:35 -0300 Subject: [PATCH 45/68] add send presence router --- src/validate/validate.schema.ts | 10 ++++++ .../controllers/sendMessage.controller.ts | 6 ++++ src/whatsapp/dto/sendMessage.dto.ts | 9 +++++ src/whatsapp/routers/sendMessage.router.ts | 18 ++++++++++ src/whatsapp/services/whatsapp.service.ts | 33 +++++++++++++++++++ 5 files changed, 76 insertions(+) diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 9781e18c..4fad3269 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -149,6 +149,16 @@ export const textMessageSchema: JSONSchema7 = { required: ['textMessage', 'number'], }; +export const presenceSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema, required: ['presence', 'delay'] }, + }, + required: ['options', 'number'], +}; + export const pollMessageSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/whatsapp/controllers/sendMessage.controller.ts index 20e38ae5..a6b844dc 100644 --- a/src/whatsapp/controllers/sendMessage.controller.ts +++ b/src/whatsapp/controllers/sendMessage.controller.ts @@ -11,6 +11,7 @@ import { SendLocationDto, SendMediaDto, SendPollDto, + SendPresenceDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -23,6 +24,11 @@ const logger = new Logger('MessageRouter'); export class SendMessageController { constructor(private readonly waMonitor: WAMonitoringService) {} + public async sendPresence({ instanceName }: InstanceDto, data: SendPresenceDto) { + logger.verbose('requested sendPresence from ' + instanceName + ' instance'); + return await this.waMonitor.waInstances[instanceName].sendPresence(data); + } + public async sendText({ instanceName }: InstanceDto, data: SendTextDto) { logger.verbose('requested sendText from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].textMessage(data); diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index e0d6d8f9..8cc4dd1e 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -46,9 +46,18 @@ class PollMessage { values: string[]; messageSecret?: Uint8Array; } +export class SendPresenceDto extends Metadata { + options: { + presence: WAPresence; + delay: number; + }; +} export class SendTextDto extends Metadata { textMessage: TextMessage; } +export class SendPresence extends Metadata { + textMessage: TextMessage; +} export class SendStatusDto extends Metadata { statusMessage: StatusMessage; diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts index d87db44d..02047249 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -9,6 +9,7 @@ import { locationMessageSchema, mediaMessageSchema, pollMessageSchema, + presenceSchema, reactionMessageSchema, statusMessageSchema, stickerMessageSchema, @@ -23,6 +24,7 @@ import { SendLocationDto, SendMediaDto, SendPollDto, + SendPresenceDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -37,6 +39,22 @@ export class MessageRouter extends RouterBroker { constructor(...guards: RequestHandler[]) { super(); this.router + .post(this.routerPath('sendPresence'), ...guards, async (req, res) => { + logger.verbose('request received in sendText'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: presenceSchema, + ClassRef: SendPresenceDto, + execute: (instance, data) => sendMessageController.sendPresence(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) .post(this.routerPath('sendText'), ...guards, async (req, res) => { logger.verbose('request received in sendText'); logger.verbose('request body: '); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 5c3edc44..83df4b74 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -109,6 +109,7 @@ import { SendLocationDto, SendMediaDto, SendPollDto, + SendPresenceDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -2387,6 +2388,38 @@ export class WAStartupService { return this.stateConnection; } + public async sendPresence(data: SendPresenceDto) { + try { + const { number } = data; + + this.logger.verbose(`Check if number "${number}" is WhatsApp`); + const isWA = (await this.whatsappNumber({ numbers: [number] }))?.shift(); + + this.logger.verbose(`Exists: "${isWA.exists}" | jid: ${isWA.jid}`); + if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) { + throw new BadRequestException(isWA); + } + + const sender = isWA.jid; + + this.logger.verbose('Sending presence'); + await this.client.presenceSubscribe(sender); + this.logger.verbose('Subscribing to presence'); + + await this.client.sendPresenceUpdate(data.options?.presence ?? 'composing', sender); + this.logger.verbose('Sending presence update: ' + data.options?.presence ?? 'composing'); + + await delay(data.options.delay); + this.logger.verbose('Set delay: ' + data.options.delay); + + await this.client.sendPresenceUpdate('paused', sender); + this.logger.verbose('Sending presence update: paused'); + } catch (error) { + this.logger.error(error); + throw new BadRequestException(error.toString()); + } + } + // Send Message Controller public async textMessage(data: SendTextDto, isChatwoot = false) { this.logger.verbose('Sending text message'); From 4222c0e53bdb2ef0a9022dc3f222eb8369d52ed5 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:32:27 -0300 Subject: [PATCH 46/68] Fix logger message in sendPresence route --- src/whatsapp/routers/sendMessage.router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts index 02047249..79fedbe2 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -40,7 +40,7 @@ export class MessageRouter extends RouterBroker { super(); this.router .post(this.routerPath('sendPresence'), ...guards, async (req, res) => { - logger.verbose('request received in sendText'); + logger.verbose('request received in sendPresence'); logger.verbose('request body: '); logger.verbose(req.body); From a90f0f2c597267f1f4c539c1d02010ed1087dfec Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Wed, 6 Dec 2023 15:21:50 -0300 Subject: [PATCH 47/68] Fixes in mongodb and chatwoot --- CHANGELOG.md | 3 + src/config/error.config.ts | 4 +- src/utils/use-multi-file-auth-state-db.ts | 2 +- src/whatsapp/services/chatwoot.service.ts | 38 +-- src/whatsapp/services/monitor.service.ts | 36 +-- src/whatsapp/services/typebot.service.ts | 2 +- src/whatsapp/services/whatsapp.service.ts | 291 +++++++++++----------- 7 files changed, 197 insertions(+), 179 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80613150..9a6f1781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Feature * Added AWS SQS Integration * Added compatibility with typebot v2 +* Added endpoint sendPresence ### Fixed @@ -16,6 +17,8 @@ * Removed await from webhook when sending a message * Update typebot.service.ts - element.underline change ~ for * * Adjusts in proxy +* Removed api restart on receiving an error +* Fixes in mongodb and chatwoot # 1.5.4 (2023-10-09 20:43) diff --git a/src/config/error.config.ts b/src/config/error.config.ts index 7a6717da..dbba0d6a 100644 --- a/src/config/error.config.ts +++ b/src/config/error.config.ts @@ -8,7 +8,7 @@ export function onUnexpectedError() { stderr: process.stderr.fd, error, }); - process.exit(1); + // process.exit(1); }); process.on('unhandledRejection', (error, origin) => { @@ -18,6 +18,6 @@ export function onUnexpectedError() { stderr: process.stderr.fd, error, }); - process.exit(1); + // process.exit(1); }); } diff --git a/src/utils/use-multi-file-auth-state-db.ts b/src/utils/use-multi-file-auth-state-db.ts index 237b15d0..995ac92a 100644 --- a/src/utils/use-multi-file-auth-state-db.ts +++ b/src/utils/use-multi-file-auth-state-db.ts @@ -101,7 +101,7 @@ export async function useMultiFileAuthStateDb( }, }, saveCreds: async () => { - return writeData(creds, 'creds'); + return await writeData(creds, 'creds'); }, }; } diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 463f81d7..5523ccf2 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -987,6 +987,10 @@ export class ChatwootService { const state = waInstance?.connectionStatus?.state; if (state !== 'open') { + if (state === 'close') { + this.logger.verbose('request cleaning up instance: ' + instance.instanceName); + await this.waMonitor.cleaningUp(instance.instanceName); + } this.logger.verbose('connect to whatsapp'); const number = command.split(':')[1]; await waInstance.connectToWhatsapp(number); @@ -1272,13 +1276,6 @@ export class ChatwootService { public async eventWhatsapp(event: string, instance: InstanceDto, body: any) { this.logger.verbose('event whatsapp to instance: ' + instance.instanceName); try { - const client = await this.clientCw(instance); - - if (!client) { - this.logger.warn('client not found'); - return null; - } - const waInstance = this.waMonitor.waInstances[instance.instanceName]; if (!waInstance) { @@ -1286,6 +1283,13 @@ export class ChatwootService { return null; } + const client = await this.clientCw(instance); + + if (!client) { + this.logger.warn('client not found'); + return null; + } + if (event === 'messages.upsert' || event === 'send.message') { this.logger.verbose('event messages.upsert'); @@ -1535,16 +1539,18 @@ export class ChatwootService { await this.createBotMessage(instance, msgStatus, 'incoming'); } - // if (event === 'connection.update') { - // this.logger.verbose('event connection.update'); + if (event === 'connection.update') { + this.logger.verbose('event connection.update'); - // if (body.status === 'open') { - // const msgConnection = `🚀 Connection successfully established!`; - - // this.logger.verbose('send message to chatwoot'); - // await this.createBotMessage(instance, msgConnection, 'incoming'); - // } - // } + if (body.status === 'open') { + // if we have qrcode count then we understand that a new connection was established + if (this.waMonitor.waInstances[instance.instanceName].qrCode.count > 0) { + const msgConnection = `🚀 Connection successfully established!`; + this.logger.verbose('send message to chatwoot'); + await this.createBotMessage(instance, msgConnection, 'incoming'); + } + } + } if (event === 'qrcode.updated') { this.logger.verbose('event qrcode.updated'); diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 45cd4a1f..766569de 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -13,11 +13,11 @@ import { RedisCache } from '../../libs/redis.client'; import { AuthModel, ChamaaiModel, - ChatModel, + // ChatModel, ChatwootModel, - ContactModel, - MessageModel, - MessageUpModel, + // ContactModel, + // MessageModel, + // MessageUpModel, ProxyModel, RabbitmqModel, SettingsModel, @@ -39,7 +39,7 @@ export class WAMonitoringService { this.removeInstance(); this.noConnection(); - this.delInstanceFiles(); + // this.delInstanceFiles(); Object.assign(this.db, configService.get('DATABASE')); Object.assign(this.redis, configService.get('REDIS')); @@ -193,6 +193,13 @@ export class WAMonitoringService { public async cleaningUp(instanceName: string) { this.logger.verbose('cleaning up instance: ' + instanceName); + if (this.redis.ENABLED) { + this.logger.verbose('cleaning up instance in redis: ' + instanceName); + this.cache.reference = instanceName; + await this.cache.delAll(); + return; + } + if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { this.logger.verbose('cleaning up instance in database: ' + instanceName); await this.repository.dbServer.connect(); @@ -203,13 +210,6 @@ export class WAMonitoringService { return; } - if (this.redis.ENABLED) { - this.logger.verbose('cleaning up instance in redis: ' + instanceName); - this.cache.reference = instanceName; - await this.cache.delAll(); - return; - } - this.logger.verbose('cleaning up instance in files: ' + instanceName); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); } @@ -239,10 +239,10 @@ export class WAMonitoringService { this.logger.verbose('cleaning store database instance: ' + instanceName); - await ChatModel.deleteMany({ owner: instanceName }); - await ContactModel.deleteMany({ owner: instanceName }); - await MessageUpModel.deleteMany({ owner: instanceName }); - await MessageModel.deleteMany({ owner: instanceName }); + // await ChatModel.deleteMany({ owner: instanceName }); + // await ContactModel.deleteMany({ owner: instanceName }); + // await MessageUpModel.deleteMany({ owner: instanceName }); + // await MessageModel.deleteMany({ owner: instanceName }); await AuthModel.deleteMany({ _id: instanceName }); await WebhookModel.deleteMany({ _id: instanceName }); @@ -357,8 +357,8 @@ export class WAMonitoringService { this.eventEmitter.on('logout.instance', async (instanceName: string) => { this.logger.verbose('logout instance: ' + instanceName); try { - this.logger.verbose('request cleaning up instance: ' + instanceName); - this.cleaningUp(instanceName); + // this.logger.verbose('request cleaning up instance: ' + instanceName); + // this.cleaningUp(instanceName); } finally { this.logger.warn(`Instance "${instanceName}" - LOGOUT`); } diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 4ed9b04b..63fcebed 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -246,7 +246,7 @@ export class TypebotService { prefilledVariables: { ...data.prefilledVariables, remoteJid: data.remoteJid, - pushName: data.pushName || '', + pushName: data.pushName || data.prefilledVariables?.pushName || '', instanceName: instance.instanceName, }, }, diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 5c3edc44..630c6a27 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1336,7 +1336,7 @@ export class WAStartupService { msgRetryCounterCache: this.msgRetryCounterCache, getMessage: async (key) => (await this.getMessage(key)) as Promise, generateHighQualityLinkPreview: true, - syncFullHistory: true, + syncFullHistory: false, userDevicesCache: this.userDevicesCache, transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { @@ -1409,6 +1409,7 @@ export class WAStartupService { browser, version, markOnlineOnConnect: this.localSettings.always_online, + retryRequestDelayMs: 10, connectTimeoutMs: 60_000, qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, @@ -1416,9 +1417,9 @@ export class WAStartupService { msgRetryCounterCache: this.msgRetryCounterCache, getMessage: async (key) => (await this.getMessage(key)) as Promise, generateHighQualityLinkPreview: true, - syncFullHistory: true, + syncFullHistory: false, userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, + transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, patchMessageBeforeSending: (message) => { const requiresPatch = !!(message.buttonsMessage || message.listMessage || message.templateMessage); if (requiresPatch) { @@ -1637,157 +1638,165 @@ export class WAStartupService { database: Database, settings: SettingsRaw, ) => { - this.logger.verbose('Event received: messages.upsert'); - const received = messages[0]; + try { + this.logger.verbose('Event received: messages.upsert'); + for (const received of messages) { + if ( + (type !== 'notify' && type !== 'append') || + received.message?.protocolMessage || + received.message?.pollUpdateMessage + ) { + this.logger.verbose('message rejected'); + return; + } - if (type !== 'notify' || received.message?.protocolMessage || received.message?.pollUpdateMessage) { - this.logger.verbose('message rejected'); - return; - } + if (Long.isLong(received.messageTimestamp)) { + received.messageTimestamp = received.messageTimestamp?.toNumber(); + } - if (Long.isLong(received.messageTimestamp)) { - received.messageTimestamp = received.messageTimestamp?.toNumber(); - } + if (settings?.groups_ignore && received.key.remoteJid.includes('@g.us')) { + this.logger.verbose('group ignored'); + return; + } - if (settings?.groups_ignore && received.key.remoteJid.includes('@g.us')) { - this.logger.verbose('group ignored'); - return; - } + let messageRaw: MessageRaw; - let messageRaw: MessageRaw; + if ( + (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) || + received?.message?.imageMessage + ) { + const buffer = await downloadMediaMessage( + { key: received.key, message: received?.message }, + 'buffer', + {}, + { + logger: P({ level: 'error' }) as any, + reuploadRequest: this.client.updateMediaMessage, + }, + ); + messageRaw = { + key: received.key, + pushName: received.pushName, + message: { + ...received.message, + base64: buffer ? buffer.toString('base64') : undefined, + }, + messageType: getContentType(received.message), + messageTimestamp: received.messageTimestamp as number, + owner: this.instance.name, + source: getDevice(received.key.id), + }; + } else { + messageRaw = { + key: received.key, + pushName: received.pushName, + message: { ...received.message }, + messageType: getContentType(received.message), + messageTimestamp: received.messageTimestamp as number, + owner: this.instance.name, + source: getDevice(received.key.id), + }; + } - if ( - (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) || - received?.message?.imageMessage - ) { - const buffer = await downloadMediaMessage( - { key: received.key, message: received?.message }, - 'buffer', - {}, - { - logger: P({ level: 'error' }) as any, - reuploadRequest: this.client.updateMediaMessage, - }, - ); - messageRaw = { - key: received.key, - pushName: received.pushName, - message: { - ...received.message, - base64: buffer ? buffer.toString('base64') : undefined, - }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - } else { - messageRaw = { - key: received.key, - pushName: received.pushName, - message: { ...received.message }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - } + if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { + await this.client.readMessages([received.key]); + } - if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { - await this.client.readMessages([received.key]); - } + if (this.localSettings.read_status && received.key.id === 'status@broadcast') { + await this.client.readMessages([received.key]); + } - if (this.localSettings.read_status && received.key.id === 'status@broadcast') { - await this.client.readMessages([received.key]); - } + this.logger.log(messageRaw); - this.logger.log(messageRaw); + this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); + this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); - this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + if (this.localChatwoot.enabled) { + await this.chatwootService.eventWhatsapp( + Events.MESSAGES_UPSERT, + { instanceName: this.instance.name }, + messageRaw, + ); + } - if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( - Events.MESSAGES_UPSERT, - { instanceName: this.instance.name }, - messageRaw, - ); - } - - const typebotSessionRemoteJid = this.localTypebot.sessions?.find( - (session) => session.remoteJid === received.key.remoteJid, - ); - - if (this.localTypebot.enabled || typebotSessionRemoteJid) { - if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { - await this.typebotService.sendTypebot( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, + const typebotSessionRemoteJid = this.localTypebot.sessions?.find( + (session) => session.remoteJid === received.key.remoteJid, ); + + if ((this.localTypebot.enabled && type === 'notify') || typebotSessionRemoteJid) { + if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { + await this.typebotService.sendTypebot( + { instanceName: this.instance.name }, + messageRaw.key.remoteJid, + messageRaw, + ); + } + } + + if (this.localChamaai.enabled && messageRaw.key.fromMe === false && type === 'notify') { + await this.chamaaiService.sendChamaai( + { instanceName: this.instance.name }, + messageRaw.key.remoteJid, + messageRaw, + ); + } + + this.logger.verbose('Inserting message in database'); + await this.repository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); + + this.logger.verbose('Verifying contact from message'); + const contact = await this.repository.contact.find({ + where: { owner: this.instance.name, id: received.key.remoteJid }, + }); + + const contactRaw: ContactRaw = { + id: received.key.remoteJid, + pushName: received.pushName, + profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, + owner: this.instance.name, + }; + + if (contactRaw.id === 'status@broadcast') { + this.logger.verbose('Contact is status@broadcast'); + return; + } + + if (contact?.length) { + this.logger.verbose('Contact found in database'); + const contactRaw: ContactRaw = { + id: received.key.remoteJid, + pushName: contact[0].pushName, + profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, + owner: this.instance.name, + }; + + this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); + this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); + + if (this.localChatwoot.enabled) { + await this.chatwootService.eventWhatsapp( + Events.CONTACTS_UPDATE, + { instanceName: this.instance.name }, + contactRaw, + ); + } + + this.logger.verbose('Updating contact in database'); + await this.repository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); + return; + } + + this.logger.verbose('Contact not found in database'); + + this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); + this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); + + this.logger.verbose('Inserting contact in database'); + this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); } + } catch (error) { + this.logger.error(error); } - - if (this.localChamaai.enabled && messageRaw.key.fromMe === false) { - await this.chamaaiService.sendChamaai( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); - } - - this.logger.verbose('Inserting message in database'); - await this.repository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); - - this.logger.verbose('Verifying contact from message'); - const contact = await this.repository.contact.find({ - where: { owner: this.instance.name, id: received.key.remoteJid }, - }); - - const contactRaw: ContactRaw = { - id: received.key.remoteJid, - pushName: received.pushName, - profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, - owner: this.instance.name, - }; - - if (contactRaw.id === 'status@broadcast') { - this.logger.verbose('Contact is status@broadcast'); - return; - } - - if (contact?.length) { - this.logger.verbose('Contact found in database'); - const contactRaw: ContactRaw = { - id: received.key.remoteJid, - pushName: contact[0].pushName, - profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, - owner: this.instance.name, - }; - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); - - if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( - Events.CONTACTS_UPDATE, - { instanceName: this.instance.name }, - contactRaw, - ); - } - - this.logger.verbose('Updating contact in database'); - await this.repository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); - return; - } - - this.logger.verbose('Contact not found in database'); - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); - - this.logger.verbose('Inserting contact in database'); - this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); }, 'messages.update': async (args: WAMessageUpdate[], database: Database, settings: SettingsRaw) => { From 42dd280aca536721955da120c8afaae8453c3e55 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:40:48 -0300 Subject: [PATCH 48/68] change sendPresence from sendMessage to chat --- .vscode/settings.json | 4 ++-- src/whatsapp/controllers/chat.controller.ts | 6 ++++++ .../controllers/sendMessage.controller.ts | 6 ------ src/whatsapp/dto/chat.dto.ts | 19 ++++++++++++++++++- src/whatsapp/dto/sendMessage.dto.ts | 7 +------ src/whatsapp/routers/chat.router.ts | 18 ++++++++++++++++++ src/whatsapp/routers/sendMessage.router.ts | 18 ------------------ src/whatsapp/services/whatsapp.service.ts | 2 +- 8 files changed, 46 insertions(+), 34 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 20b82443..71db0b08 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,8 +5,8 @@ "editor.smoothScrolling": true, "editor.tabSize": 2, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true, - "source.fixAll": true + "source.fixAll.eslint": "explicit", + "source.fixAll": "explicit" }, "prisma-smart-formatter.typescript.defaultFormatter": "esbenp.prettier-vscode", "prisma-smart-formatter.prisma.defaultFormatter": "Prisma.prisma" diff --git a/src/whatsapp/controllers/chat.controller.ts b/src/whatsapp/controllers/chat.controller.ts index 0299841c..60a9c618 100644 --- a/src/whatsapp/controllers/chat.controller.ts +++ b/src/whatsapp/controllers/chat.controller.ts @@ -9,6 +9,7 @@ import { ProfilePictureDto, ProfileStatusDto, ReadMessageDto, + SendPresenceDto, WhatsAppNumberDto, } from '../dto/chat.dto'; import { InstanceDto } from '../dto/instance.dto'; @@ -77,6 +78,11 @@ export class ChatController { return await this.waMonitor.waInstances[instanceName].fetchChats(); } + public async sendPresence({ instanceName }: InstanceDto, data: SendPresenceDto) { + logger.verbose('requested sendPresence from ' + instanceName + ' instance'); + return await this.waMonitor.waInstances[instanceName].sendPresence(data); + } + public async fetchPrivacySettings({ instanceName }: InstanceDto) { logger.verbose('requested fetchPrivacySettings from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings(); diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/whatsapp/controllers/sendMessage.controller.ts index a6b844dc..20e38ae5 100644 --- a/src/whatsapp/controllers/sendMessage.controller.ts +++ b/src/whatsapp/controllers/sendMessage.controller.ts @@ -11,7 +11,6 @@ import { SendLocationDto, SendMediaDto, SendPollDto, - SendPresenceDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -24,11 +23,6 @@ const logger = new Logger('MessageRouter'); export class SendMessageController { constructor(private readonly waMonitor: WAMonitoringService) {} - public async sendPresence({ instanceName }: InstanceDto, data: SendPresenceDto) { - logger.verbose('requested sendPresence from ' + instanceName + ' instance'); - return await this.waMonitor.waInstances[instanceName].sendPresence(data); - } - public async sendText({ instanceName }: InstanceDto, data: SendTextDto) { logger.verbose('requested sendText from ' + instanceName + ' instance'); return await this.waMonitor.waInstances[instanceName].textMessage(data); diff --git a/src/whatsapp/dto/chat.dto.ts b/src/whatsapp/dto/chat.dto.ts index f8a5da5f..07553c90 100644 --- a/src/whatsapp/dto/chat.dto.ts +++ b/src/whatsapp/dto/chat.dto.ts @@ -1,4 +1,4 @@ -import { proto, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '@whiskeysockets/baileys'; +import { proto, WAPresence, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '@whiskeysockets/baileys'; export class OnWhatsAppDto { constructor(public readonly jid: string, public readonly exists: boolean, public readonly name?: string) {} @@ -83,3 +83,20 @@ export class DeleteMessage { remoteJid: string; participant?: string; } +export class Options { + delay?: number; + presence?: WAPresence; +} +class OptionsMessage { + options: Options; +} +export class Metadata extends OptionsMessage { + number: string; +} + +export class SendPresenceDto extends Metadata { + options: { + presence: WAPresence; + delay: number; + }; +} diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index 8cc4dd1e..bfa5763f 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -46,12 +46,7 @@ class PollMessage { values: string[]; messageSecret?: Uint8Array; } -export class SendPresenceDto extends Metadata { - options: { - presence: WAPresence; - delay: number; - }; -} + export class SendTextDto extends Metadata { textMessage: TextMessage; } diff --git a/src/whatsapp/routers/chat.router.ts b/src/whatsapp/routers/chat.router.ts index 285c29a0..29d1cdc3 100644 --- a/src/whatsapp/routers/chat.router.ts +++ b/src/whatsapp/routers/chat.router.ts @@ -7,6 +7,7 @@ import { deleteMessageSchema, messageUpSchema, messageValidateSchema, + presenceSchema, privacySettingsSchema, profileNameSchema, profilePictureSchema, @@ -26,6 +27,7 @@ import { ProfilePictureDto, ProfileStatusDto, ReadMessageDto, + SendPresenceDto, WhatsAppNumberDto, } from '../dto/chat.dto'; import { InstanceDto } from '../dto/instance.dto'; @@ -228,6 +230,22 @@ export class ChatRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) + .post(this.routerPath('sendPresence'), ...guards, async (req, res) => { + logger.verbose('request received in sendPresence'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: presenceSchema, + ClassRef: SendPresenceDto, + execute: (instance, data) => chatController.sendPresence(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) // Profile routes .get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => { logger.verbose('request received in fetchPrivacySettings'); diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts index 79fedbe2..d87db44d 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -9,7 +9,6 @@ import { locationMessageSchema, mediaMessageSchema, pollMessageSchema, - presenceSchema, reactionMessageSchema, statusMessageSchema, stickerMessageSchema, @@ -24,7 +23,6 @@ import { SendLocationDto, SendMediaDto, SendPollDto, - SendPresenceDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -39,22 +37,6 @@ export class MessageRouter extends RouterBroker { constructor(...guards: RequestHandler[]) { super(); this.router - .post(this.routerPath('sendPresence'), ...guards, async (req, res) => { - logger.verbose('request received in sendPresence'); - logger.verbose('request body: '); - logger.verbose(req.body); - - logger.verbose('request query: '); - logger.verbose(req.query); - const response = await this.dataValidate({ - request: req, - schema: presenceSchema, - ClassRef: SendPresenceDto, - execute: (instance, data) => sendMessageController.sendPresence(instance, data), - }); - - return res.status(HttpStatus.CREATED).json(response); - }) .post(this.routerPath('sendText'), ...guards, async (req, res) => { logger.verbose('request received in sendText'); logger.verbose('request body: '); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 83df4b74..a7b5152f 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -83,6 +83,7 @@ import { OnWhatsAppDto, PrivacySettingDto, ReadMessageDto, + SendPresenceDto, WhatsAppNumberDto, } from '../dto/chat.dto'; import { @@ -109,7 +110,6 @@ import { SendLocationDto, SendMediaDto, SendPollDto, - SendPresenceDto, SendReactionDto, SendStatusDto, SendStickerDto, From 9a5dbe055e68de80c1d8babe90746cd262aa05a7 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 8 Dec 2023 18:44:52 -0300 Subject: [PATCH 49/68] fix: adjusts in connection --- src/config/env.config.ts | 8 -------- src/dev-env.yml | 4 ---- src/whatsapp/services/whatsapp.service.ts | 13 +++++++++++++ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/config/env.config.ts b/src/config/env.config.ts index dcb90fbc..42c8826a 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -78,10 +78,6 @@ export type Websocket = { ENABLED: boolean; }; -export type Chatwoot = { - USE_REPLY_ID: boolean; -}; - export type EventsWebhook = { APPLICATION_STARTUP: boolean; QRCODE_UPDATED: boolean; @@ -154,7 +150,6 @@ export interface Env { TYPEBOT: Typebot; AUTHENTICATION: Auth; PRODUCTION?: Production; - CHATWOOT?: Chatwoot; } export type Key = keyof Env; @@ -323,9 +318,6 @@ export class ConfigService { SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`', }, }, - CHATWOOT: { - USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true', - }, }; } } diff --git a/src/dev-env.yml b/src/dev-env.yml index b2a2e521..cefdcbeb 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -164,7 +164,3 @@ AUTHENTICATION: JWT: EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires SECRET: L=0YWt]b2w[WF>#>:&E` - -# Configure to chatwoot -CHATWOOT: - USE_REPLY_ID: false diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 630c6a27..99b4ab32 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -17,6 +17,7 @@ import makeWASocket, { getContentType, getDevice, GroupMetadata, + isJidBroadcast, isJidGroup, isJidUser, makeCacheableSignalKeyStore, @@ -1333,6 +1334,12 @@ export class WAStartupService { qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, emitOwnEvents: false, + shouldIgnoreJid: (jid) => { + const isGroupJid = this.localSettings.groups_ignore && isJidGroup(jid); + const isBroadcast = !this.localSettings.read_status && isJidBroadcast(jid); + + return isGroupJid || isBroadcast; + }, msgRetryCounterCache: this.msgRetryCounterCache, getMessage: async (key) => (await this.getMessage(key)) as Promise, generateHighQualityLinkPreview: true, @@ -1414,6 +1421,12 @@ export class WAStartupService { qrTimeout: 40_000, defaultQueryTimeoutMs: undefined, emitOwnEvents: false, + shouldIgnoreJid: (jid) => { + const isGroupJid = this.localSettings.groups_ignore && isJidGroup(jid); + const isBroadcast = !this.localSettings.read_status && isJidBroadcast(jid); + + return isGroupJid || isBroadcast; + }, msgRetryCounterCache: this.msgRetryCounterCache, getMessage: async (key) => (await this.getMessage(key)) as Promise, generateHighQualityLinkPreview: true, From fd15ae5e8c1cb934104051b79dbe486ce27627b9 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:00:33 -0300 Subject: [PATCH 50/68] Fix condition to check for empty result object --- src/whatsapp/controllers/chatwoot.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/controllers/chatwoot.controller.ts b/src/whatsapp/controllers/chatwoot.controller.ts index 46b93aee..40ab1bca 100644 --- a/src/whatsapp/controllers/chatwoot.controller.ts +++ b/src/whatsapp/controllers/chatwoot.controller.ts @@ -64,7 +64,7 @@ export class ChatwootController { const urlServer = this.configService.get('SERVER').URL; - if (Object.keys(result).length === 0) { + if (Object.keys(result || {}).length === 0) { return { enabled: false, url: '', From b8d9a8c07216d583bc7d25430343646df2aee4a4 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 11 Dec 2023 12:08:58 -0300 Subject: [PATCH 51/68] fix: adjusted return from queries in mongodb --- CHANGELOG.md | 1 + .../controllers/instance.controller.ts | 2 +- .../controllers/settings.controller.ts | 3 +- src/whatsapp/services/settings.service.ts | 2 +- src/whatsapp/services/whatsapp.service.ts | 69 ++++++++++++++++--- 5 files changed, 65 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a6f1781..b627c2eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Adjusts in proxy * Removed api restart on receiving an error * Fixes in mongodb and chatwoot +* Adjusted return from queries in mongodb # 1.5.4 (2023-10-09 20:43) diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 4c4e5cfb..1087bed4 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -340,7 +340,7 @@ export class InstanceController { const settings: wa.LocalSettings = { reject_call: reject_call || false, msg_call: msg_call || '', - groups_ignore: groups_ignore || false, + groups_ignore: groups_ignore || true, always_online: always_online || false, read_messages: read_messages || false, read_status: read_status || false, diff --git a/src/whatsapp/controllers/settings.controller.ts b/src/whatsapp/controllers/settings.controller.ts index 1a8baafc..0f559d1b 100644 --- a/src/whatsapp/controllers/settings.controller.ts +++ b/src/whatsapp/controllers/settings.controller.ts @@ -19,6 +19,7 @@ export class SettingsController { public async findSettings(instance: InstanceDto) { logger.verbose('requested findSettings from ' + instance.instanceName + ' instance'); - return this.settingsService.find(instance); + const settings = this.settingsService.find(instance); + return settings; } } diff --git a/src/whatsapp/services/settings.service.ts b/src/whatsapp/services/settings.service.ts index 6815ca40..741a2cbc 100644 --- a/src/whatsapp/services/settings.service.ts +++ b/src/whatsapp/services/settings.service.ts @@ -26,7 +26,7 @@ export class SettingsService { return result; } catch (error) { - return { reject_call: false, msg_call: '', groups_ignore: false }; + return { reject_call: false, msg_call: '', groups_ignore: true }; } } } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 99b4ab32..9c420e3a 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -304,7 +304,14 @@ export class WAStartupService { this.logger.verbose(`Webhook url: ${data.url}`); this.logger.verbose(`Webhook events: ${data.events}`); - return data; + + return { + enabled: data.enabled, + url: data.url, + events: data.events, + webhook_by_events: data.webhook_by_events, + webhook_base64: data.webhook_base64, + }; } private async loadChatwoot() { @@ -372,7 +379,16 @@ export class WAStartupService { this.logger.verbose(`Chatwoot reopen conversation: ${data.reopen_conversation}`); this.logger.verbose(`Chatwoot conversation pending: ${data.conversation_pending}`); - return data; + return { + enabled: data.enabled, + account_id: data.account_id, + token: data.token, + url: data.url, + name_inbox: data.name_inbox, + sign_msg: data.sign_msg, + reopen_conversation: data.reopen_conversation, + conversation_pending: data.conversation_pending, + }; } private async loadSettings() { @@ -429,7 +445,14 @@ export class WAStartupService { this.logger.verbose(`Settings always_online: ${data.always_online}`); this.logger.verbose(`Settings read_messages: ${data.read_messages}`); this.logger.verbose(`Settings read_status: ${data.read_status}`); - return data; + return { + reject_call: data.reject_call, + msg_call: data.msg_call, + groups_ignore: data.groups_ignore, + always_online: data.always_online, + read_messages: data.read_messages, + read_status: data.read_status, + }; } private async loadWebsocket() { @@ -463,7 +486,10 @@ export class WAStartupService { } this.logger.verbose(`Websocket events: ${data.events}`); - return data; + return { + enabled: data.enabled, + events: data.events, + }; } private async loadRabbitmq() { @@ -497,7 +523,10 @@ export class WAStartupService { } this.logger.verbose(`Rabbitmq events: ${data.events}`); - return data; + return { + enabled: data.enabled, + events: data.events, + }; } public async removeRabbitmqQueues() { @@ -539,7 +568,10 @@ export class WAStartupService { } this.logger.verbose(`Sqs events: ${data.events}`); - return data; + return { + enabled: data.enabled, + events: data.events, + }; } public async removeSqsQueues() { @@ -605,7 +637,17 @@ export class WAStartupService { throw new NotFoundException('Typebot not found'); } - return data; + return { + enabled: data.enabled, + url: data.url, + typebot: data.typebot, + expire: data.expire, + keyword_finish: data.keyword_finish, + delay_message: data.delay_message, + unknown_message: data.unknown_message, + listening_from_me: data.listening_from_me, + sessions: data.sessions, + }; } private async loadProxy() { @@ -642,7 +684,10 @@ export class WAStartupService { throw new NotFoundException('Proxy not found'); } - return data; + return { + enabled: data.enabled, + proxy: data.proxy, + }; } private async loadChamaai() { @@ -688,7 +733,13 @@ export class WAStartupService { throw new NotFoundException('Chamaai not found'); } - return data; + return { + enabled: data.enabled, + url: data.url, + token: data.token, + waNumber: data.waNumber, + answerByAudio: data.answerByAudio, + }; } public async sendDataWebhook(event: Events, data: T, local = true) { From 8e88f00fb2943f8fe5248677f37195beb3b5bfb7 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:42:09 -0300 Subject: [PATCH 52/68] fix: only create if is paused --- src/whatsapp/services/typebot.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 14b04383..9da18ed2 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -69,7 +69,7 @@ export class TypebotService { session.status = status; } }); - } else { + } else if (status === 'paused') { const session: Session = { remoteJid: remoteJid, sessionId: Math.floor(Math.random() * 10000000000).toString(), From b2e144f35ca1176eca5baa3cbb53770c3173f2c6 Mon Sep 17 00:00:00 2001 From: Gabriel Pastori <58153955+gabrielpastori1@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:06:42 -0300 Subject: [PATCH 53/68] add manager --- package.json | 1 + src/whatsapp/controllers/views.controller.ts | 23 ------------------- src/whatsapp/routers/view.router.ts | 24 +++++++++++++++++--- src/whatsapp/whatsapp.module.ts | 2 -- 4 files changed, 22 insertions(+), 28 deletions(-) delete mode 100644 src/whatsapp/controllers/views.controller.ts diff --git a/package.json b/package.json index 8625030a..b5d3ee92 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "cross-env": "^7.0.3", "dayjs": "^1.11.7", "eventemitter2": "^6.4.9", + "evolution-manager": "latest", "exiftool-vendored": "^22.0.0", "express": "^4.18.2", "express-async-errors": "^3.1.1", diff --git a/src/whatsapp/controllers/views.controller.ts b/src/whatsapp/controllers/views.controller.ts deleted file mode 100644 index 7e15dfe7..00000000 --- a/src/whatsapp/controllers/views.controller.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Request, Response } from 'express'; - -import { Auth, ConfigService, HttpServer } from '../../config/env.config'; -import { HttpStatus } from '../routers/index.router'; -import { WAMonitoringService } from '../services/monitor.service'; - -export class ViewsController { - constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {} - - public async manager(request: Request, response: Response) { - try { - const token = this.configService.get('AUTHENTICATION').API_KEY.KEY; - const port = this.configService.get('SERVER').PORT; - - const instances = await this.waMonitor.instanceInfo(); - - console.log('INSTANCES: ', instances); - return response.status(HttpStatus.OK).render('manager', { token, port, instances }); - } catch (error) { - console.log('ERROR: ', error); - } - } -} diff --git a/src/whatsapp/routers/view.router.ts b/src/whatsapp/routers/view.router.ts index 11002777..ecfe64c1 100644 --- a/src/whatsapp/routers/view.router.ts +++ b/src/whatsapp/routers/view.router.ts @@ -1,14 +1,32 @@ import { Router } from 'express'; +import fs from 'fs'; +import mime from 'mime-types'; import { RouterBroker } from '../abstract/abstract.router'; -import { viewsController } from '../whatsapp.module'; export class ViewsRouter extends RouterBroker { constructor() { super(); - this.router.get('/', (req, res) => { - return viewsController.manager(req, res); + const basePath = 'evolution-manager/dist'; + + const indexPath = require.resolve(`${basePath}/index.html`); + + this.router.get('/*', (req, res) => { + try { + const pathname = req.url.split('?')[0]; + + // verify if url is a file in dist folder + if (pathname === '/') throw {}; + const filePath = require.resolve(`${basePath}${pathname}`); + + const contentType = mime.lookup(filePath) || 'text/plain'; + res.set('Content-Type', contentType); + res.end(fs.readFileSync(filePath)); + } catch { + res.set('Content-Type', 'text/html'); + res.send(fs.readFileSync(indexPath)); + } }); } diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index 0bb9409f..d64d2532 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -14,7 +14,6 @@ import { SendMessageController } from './controllers/sendMessage.controller'; import { SettingsController } from './controllers/settings.controller'; import { SqsController } from './controllers/sqs.controller'; import { TypebotController } from './controllers/typebot.controller'; -import { ViewsController } from './controllers/views.controller'; import { WebhookController } from './controllers/webhook.controller'; import { WebsocketController } from './controllers/websocket.controller'; import { @@ -154,7 +153,6 @@ export const instanceController = new InstanceController( typebotService, cache, ); -export const viewsController = new ViewsController(waMonitor, configService); export const sendMessageController = new SendMessageController(waMonitor); export const chatController = new ChatController(waMonitor); export const groupController = new GroupController(waMonitor); From 324d46120bdff195d4814d8a22d4cf4be8da9856 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 11 Dec 2023 15:31:43 -0300 Subject: [PATCH 54/68] feat: new manager --- CHANGELOG.md | 1 + README.md | 5 +++-- public/images/qrcode-pix.png | Bin 0 -> 3652 bytes src/whatsapp/routers/index.router.ts | 1 + src/whatsapp/services/whatsapp.service.ts | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 public/images/qrcode-pix.png diff --git a/CHANGELOG.md b/CHANGELOG.md index b627c2eb..b7026190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Added AWS SQS Integration * Added compatibility with typebot v2 * Added endpoint sendPresence +* New Instance Manager ### Fixed diff --git a/README.md b/README.md index c5c4bc37..13e68151 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,13 @@ This code was produced based on the baileys library and it is still under develo -#### Buy me coffe +#### Buy me coffe - PIX
- + +

CHAVE PIX (Telefone): (74)99987-9409


\ No newline at end of file diff --git a/public/images/qrcode-pix.png b/public/images/qrcode-pix.png new file mode 100644 index 0000000000000000000000000000000000000000..36ef00c69f8a439e0ce7072022f20319220f3c6a GIT binary patch literal 3652 zcmcgvQEXFX7`?ac8cI`$Ho-t+Y_y?9L-0Y{AcR#jktS{>U{E0jeGoS0-I5WAGj$Xa z6G&XVt|l5wXk-h70OP@~b-Ik;izC{EZOFpzWFgLl1gECcgsXM)obMLG)4jYX$=0sD z_rK>m=X~e;IJ%|j#lX^4OBF>4BsVu~RTNd{&k`>_$y|tij~7?6;klRJQGZ$0HHNEx z;$L$Y4{4F_dOR18c>a9l+Hc7%@74ReS9bh(wi@k;{PW@Jp2(V)BFP<}Ztf{O^--kd z%}0}C-!$~}y?->4^aqnYEB=QIx+-;(v*+C%W3g0b=(M)*Y`8Qz>($#QV$np?czm_K22Fl$Qs#NBfVn)JWv9>?{ zZnR^*>%^{JZR2UJDKoV<7YygG#^ShnT}y5!3-RjN;$U4v{uzfVlo`|-O|%utpuwjH z@1bEV7Qcs?K0N4Hp?pdmy=JHXjp&7haq~4pkjQCgtQ&p$g?{b0VonTn|7zU#B^G>S1$_UR##9aW(X-WAHh zu+-RH?BtW0GU+k9;>7)_u0znO!mFng2w%#iAJkK=xe%Y>1MT>7j>IKN0xuk12>`>k zw0&NU?&{}>dvkG>8uBo&KHaaise_|2xU{=CsLt?6{MjysOKfbhbJv!I?DAI27^1I~ zI!+CJdVBztPoKr%W*>bjpwH**n!GwAoTgYAs|1?uKw};tjqni4&!l${UmZ2O63csM zvbO0^Z`Kh_O@dQu=QiH`18h9ioI~ZuY35L(YrW;k?MC_w+tf)7D-ac34VCGf5?NY2 z%*GONCuhfbaxmOZvO$zalewGk(7c9BM*lLi=Y?Z)hye( zk_$>+rXblM*fFPN(!cRPkqf3${%oqDP@%ZAUPg-*&qKT^jIppKCYNShZ8B4NJ zR4$`Pl1Mty9;w*UU@U+fDg9j0Bly(K~XHr0M-k;Dd@TO=3m*i9Lyp0Wph`@4zB^Vp;_Ci@43s9W!5Eq^AgI4R* z6YK3;C4H#YuiSkA0Il3Csf|gS;s${zlc9w;cXNV`XQ~d}msYLNl4&53n3W7;b4d3H zX4umUg6CH=c29!EHyv?eT842N(8ljbc84hJ+_89v5bpx{#-cJ&_hl?p|G*sn^SA7+SChLN8 Date: Mon, 11 Dec 2023 17:32:07 -0300 Subject: [PATCH 55/68] feat: added auto_create to the chatwoot set to create the inbox automatically or not --- src/config/error.config.ts | 2 -- src/validate/validate.schema.ts | 1 + src/whatsapp/controllers/chatwoot.controller.ts | 1 + src/whatsapp/controllers/instance.controller.ts | 9 +-------- src/whatsapp/dto/chatwoot.dto.ts | 1 + src/whatsapp/services/chatwoot.service.ts | 15 ++++++++++++++- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/config/error.config.ts b/src/config/error.config.ts index dbba0d6a..6449d52e 100644 --- a/src/config/error.config.ts +++ b/src/config/error.config.ts @@ -8,7 +8,6 @@ export function onUnexpectedError() { stderr: process.stderr.fd, error, }); - // process.exit(1); }); process.on('unhandledRejection', (error, origin) => { @@ -18,6 +17,5 @@ export function onUnexpectedError() { stderr: process.stderr.fd, error, }); - // process.exit(1); }); } diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 4fad3269..a468b151 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -891,6 +891,7 @@ export const chatwootSchema: JSONSchema7 = { sign_msg: { type: 'boolean', enum: [true, false] }, reopen_conversation: { type: 'boolean', enum: [true, false] }, conversation_pending: { type: 'boolean', enum: [true, false] }, + auto_create: { type: 'boolean', enum: [true, false] }, }, required: ['enabled', 'account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'], ...isNotEmpty('account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'), diff --git a/src/whatsapp/controllers/chatwoot.controller.ts b/src/whatsapp/controllers/chatwoot.controller.ts index 40ab1bca..2aef63c8 100644 --- a/src/whatsapp/controllers/chatwoot.controller.ts +++ b/src/whatsapp/controllers/chatwoot.controller.ts @@ -42,6 +42,7 @@ export class ChatwootController { data.sign_msg = false; data.reopen_conversation = false; data.conversation_pending = false; + data.auto_create = false; } data.name_inbox = instance.instanceName; diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 1087bed4..8480c675 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -446,15 +446,8 @@ export class InstanceController { number, reopen_conversation: chatwoot_reopen_conversation || false, conversation_pending: chatwoot_conversation_pending || false, + auto_create: true, }); - - this.chatwootService.initInstanceChatwoot( - instance, - instance.instanceName.split('-cwId-')[0], - `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`, - qrcode, - number, - ); } catch (error) { this.logger.log(error); } diff --git a/src/whatsapp/dto/chatwoot.dto.ts b/src/whatsapp/dto/chatwoot.dto.ts index b270c869..22085faf 100644 --- a/src/whatsapp/dto/chatwoot.dto.ts +++ b/src/whatsapp/dto/chatwoot.dto.ts @@ -8,4 +8,5 @@ export class ChatwootDto { number?: string; reopen_conversation?: boolean; conversation_pending?: boolean; + auto_create?: boolean; } diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 5523ccf2..6e292191 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -6,7 +6,7 @@ import Jimp from 'jimp'; import mimeTypes from 'mime-types'; import path from 'path'; -import { ConfigService } from '../../config/env.config'; +import { ConfigService, HttpServer } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { ROOT_DIR } from '../../config/path.config'; import { ChatwootDto } from '../dto/chatwoot.dto'; @@ -99,9 +99,22 @@ export class ChatwootService { public create(instance: InstanceDto, data: ChatwootDto) { this.logger.verbose('create chatwoot: ' + instance.instanceName); + this.waMonitor.waInstances[instance.instanceName].setChatwoot(data); this.logger.verbose('chatwoot created'); + + if (data.auto_create) { + const urlServer = this.configService.get('SERVER').URL; + + this.initInstanceChatwoot( + instance, + instance.instanceName.split('-cwId-')[0], + `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`, + true, + data.number, + ); + } return data; } From 48f6ee8846b33bf3584a4e1c669979913795b7ad Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 11 Dec 2023 17:32:19 -0300 Subject: [PATCH 56/68] feat: added auto_create to the chatwoot set to create the inbox automatically or not --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7026190..2a067a26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Added compatibility with typebot v2 * Added endpoint sendPresence * New Instance Manager +* Added auto_create to the chatwoot set to create the inbox automatically or not ### Fixed From 7ee5bcecff8fe67a6db43d8ecdc582e16636e266 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 11 Dec 2023 18:15:24 -0300 Subject: [PATCH 57/68] fix: added restart instance when update profile pricture --- src/whatsapp/services/whatsapp.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c673d245..cc05a824 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3427,6 +3427,8 @@ export class WAStartupService { await this.client.updateProfilePicture(this.instance.wuid, pic); this.logger.verbose('Profile picture updated'); + this.reloadConnection(); + return { update: 'success' }; } catch (error) { throw new InternalServerErrorException('Error updating profile picture', error.toString()); @@ -3438,6 +3440,8 @@ export class WAStartupService { try { await this.client.removeProfilePicture(this.instance.wuid); + this.reloadConnection(); + return { update: 'success' }; } catch (error) { throw new InternalServerErrorException('Error removing profile picture', error.toString()); From d4a33e22902814e18e3325037690535cc5da8dd3 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 11 Dec 2023 18:16:11 -0300 Subject: [PATCH 58/68] fix: added restart instance when update profile pricture --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a067a26..a3732db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * Removed api restart on receiving an error * Fixes in mongodb and chatwoot * Adjusted return from queries in mongodb +* Added restart instance when update profile picture # 1.5.4 (2023-10-09 20:43) From f069a4139015a55b9e05f4bde58d86bc91867bfc Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 11 Dec 2023 18:17:18 -0300 Subject: [PATCH 59/68] fix: added restart instance when update profile pricture --- src/whatsapp/services/whatsapp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index cc05a824..5ed1f60b 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3427,7 +3427,7 @@ export class WAStartupService { await this.client.updateProfilePicture(this.instance.wuid, pic); this.logger.verbose('Profile picture updated'); - this.reloadConnection(); + await this.reloadConnection(); return { update: 'success' }; } catch (error) { From 64c1440c460830a655d068a48086dacbea295949 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 13:56:41 -0300 Subject: [PATCH 60/68] fix: correction of chatwoot functioning with admin flows --- package.json | 2 +- .../controllers/chatwoot.controller.ts | 2 +- src/whatsapp/services/chatwoot.service.ts | 33 ++++++++++--------- src/whatsapp/services/whatsapp.service.ts | 14 ++++++-- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index b5d3ee92..f2730fe1 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "cross-env": "^7.0.3", "dayjs": "^1.11.7", "eventemitter2": "^6.4.9", - "evolution-manager": "latest", + "evolution-manager": "^0.4.4", "exiftool-vendored": "^22.0.0", "express": "^4.18.2", "express-async-errors": "^3.1.1", diff --git a/src/whatsapp/controllers/chatwoot.controller.ts b/src/whatsapp/controllers/chatwoot.controller.ts index 2aef63c8..b70cda9c 100644 --- a/src/whatsapp/controllers/chatwoot.controller.ts +++ b/src/whatsapp/controllers/chatwoot.controller.ts @@ -47,7 +47,7 @@ export class ChatwootController { data.name_inbox = instance.instanceName; - const result = this.chatwootService.create(instance, data); + const result = await this.chatwootService.create(instance, data); const urlServer = this.configService.get('SERVER').URL; diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 6e292191..b2fbb879 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -52,25 +52,26 @@ export class ChatwootService { private async getProvider(instance: InstanceDto) { this.logger.verbose('get provider to instance: ' + instance.instanceName); - try { - const provider = await this.waMonitor.waInstances[instance.instanceName].findChatwoot(); + const provider = await this.waMonitor.waInstances[instance.instanceName]?.findChatwoot(); - if (!provider) { - this.logger.warn('provider not found'); - return null; - } - - this.logger.verbose('provider found'); - - return provider; - } catch (error) { - this.logger.error('provider not found'); + if (!provider) { + this.logger.warn('provider not found'); return null; } + + this.logger.verbose('provider found'); + + return provider; + // try { + // } catch (error) { + // this.logger.error('provider not found'); + // return null; + // } } private async clientCw(instance: InstanceDto) { this.logger.verbose('get client to instance: ' + instance.instanceName); + const provider = await this.getProvider(instance); if (!provider) { @@ -97,17 +98,17 @@ export class ChatwootService { return client; } - public create(instance: InstanceDto, data: ChatwootDto) { + public async create(instance: InstanceDto, data: ChatwootDto) { this.logger.verbose('create chatwoot: ' + instance.instanceName); - this.waMonitor.waInstances[instance.instanceName].setChatwoot(data); + await this.waMonitor.waInstances[instance.instanceName].setChatwoot(data); this.logger.verbose('chatwoot created'); if (data.auto_create) { const urlServer = this.configService.get('SERVER').URL; - this.initInstanceChatwoot( + await this.initInstanceChatwoot( instance, instance.instanceName.split('-cwId-')[0], `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`, @@ -1002,7 +1003,7 @@ export class ChatwootService { if (state !== 'open') { if (state === 'close') { this.logger.verbose('request cleaning up instance: ' + instance.instanceName); - await this.waMonitor.cleaningUp(instance.instanceName); + // await this.waMonitor.cleaningUp(instance.instanceName); } this.logger.verbose('connect to whatsapp'); const number = command.split(':')[1]; diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 5ed1f60b..f2e6d811 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -211,6 +211,7 @@ export class WAStartupService { public async getProfileName() { this.logger.verbose('Getting profile name'); + let profileName = this.client.user?.name ?? this.client.user?.verifiedName; if (!profileName) { this.logger.verbose('Profile name not found, trying to get from database'); @@ -3311,7 +3312,16 @@ export class WAStartupService { public async fetchPrivacySettings() { this.logger.verbose('Fetching privacy settings'); - return await this.client.fetchPrivacySettings(); + const privacy = await this.client.fetchPrivacySettings(); + + return { + readreceipts: privacy.readreceipts, + profile: privacy.profile, + status: privacy.status, + online: privacy.online, + last: privacy.last, + groupadd: privacy.groupadd, + }; } public async updatePrivacySettings(settings: PrivacySettingDto) { @@ -3427,7 +3437,7 @@ export class WAStartupService { await this.client.updateProfilePicture(this.instance.wuid, pic); this.logger.verbose('Profile picture updated'); - await this.reloadConnection(); + this.reloadConnection(); return { update: 'success' }; } catch (error) { From 3e904aa1602b48f155467f725a43878281666948 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 13:56:47 -0300 Subject: [PATCH 61/68] fix: correction of chatwoot functioning with admin flows --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3732db2..33ca12c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ * Fixes in mongodb and chatwoot * Adjusted return from queries in mongodb * Added restart instance when update profile picture +* Correction of chatwoot functioning with admin flows # 1.5.4 (2023-10-09 20:43) From ae66be197e20bd000bd757717b3ad394b383b755 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 15:16:09 -0300 Subject: [PATCH 62/68] feat: added reply, delete and message reaction in chatwoot v3.3.1 --- CHANGELOG.md | 1 + .../controllers/chatwoot.controller.ts | 9 +- src/whatsapp/models/message.model.ts | 2 + src/whatsapp/repository/message.repository.ts | 51 +++++ src/whatsapp/services/chatwoot.service.ts | 210 ++++++++++++++++-- src/whatsapp/services/whatsapp.service.ts | 8 +- src/whatsapp/whatsapp.module.ts | 4 +- 7 files changed, 264 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33ca12c1..7a7f22ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Added endpoint sendPresence * New Instance Manager * Added auto_create to the chatwoot set to create the inbox automatically or not +* Added reply, delete and message reaction in chatwoot v3.3.1 ### Fixed diff --git a/src/whatsapp/controllers/chatwoot.controller.ts b/src/whatsapp/controllers/chatwoot.controller.ts index b70cda9c..b83b2ddc 100644 --- a/src/whatsapp/controllers/chatwoot.controller.ts +++ b/src/whatsapp/controllers/chatwoot.controller.ts @@ -5,13 +5,18 @@ import { Logger } from '../../config/logger.config'; import { BadRequestException } from '../../exceptions'; import { ChatwootDto } from '../dto/chatwoot.dto'; import { InstanceDto } from '../dto/instance.dto'; +import { RepositoryBroker } from '../repository/repository.manager'; import { ChatwootService } from '../services/chatwoot.service'; import { waMonitor } from '../whatsapp.module'; const logger = new Logger('ChatwootController'); export class ChatwootController { - constructor(private readonly chatwootService: ChatwootService, private readonly configService: ConfigService) {} + constructor( + private readonly chatwootService: ChatwootService, + private readonly configService: ConfigService, + private readonly repository: RepositoryBroker, + ) {} public async createChatwoot(instance: InstanceDto, data: ChatwootDto) { logger.verbose('requested createChatwoot from ' + instance.instanceName + ' instance'); @@ -87,7 +92,7 @@ export class ChatwootController { public async receiveWebhook(instance: InstanceDto, data: any) { logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance'); - const chatwootService = new ChatwootService(waMonitor, this.configService); + const chatwootService = new ChatwootService(waMonitor, this.configService, this.repository); return chatwootService.receiveWebhook(instance, data); } diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts index 252cd6e4..af5b7d0b 100644 --- a/src/whatsapp/models/message.model.ts +++ b/src/whatsapp/models/message.model.ts @@ -22,6 +22,7 @@ export class MessageRaw { source?: 'android' | 'web' | 'ios'; source_id?: string; source_reply_id?: string; + chatwootMessageId?: string; } const messageSchema = new Schema({ @@ -39,6 +40,7 @@ const messageSchema = new Schema({ source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] }, messageTimestamp: { type: Number, required: true }, owner: { type: String, required: true, minlength: 1 }, + chatwootMessageId: { type: String, required: false }, }); export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages'); diff --git a/src/whatsapp/repository/message.repository.ts b/src/whatsapp/repository/message.repository.ts index ed362815..e212ca3d 100644 --- a/src/whatsapp/repository/message.repository.ts +++ b/src/whatsapp/repository/message.repository.ts @@ -144,4 +144,55 @@ export class MessageRepository extends Repository { return []; } } + + public async update(data: MessageRaw[], instanceName: string, saveDb?: boolean): Promise { + try { + if (this.dbSettings.ENABLED && saveDb) { + this.logger.verbose('updating messages in db'); + + const messages = data.map((message) => { + return { + updateOne: { + filter: { 'key.id': message.key.id }, + update: { ...message }, + }, + }; + }); + + const { nModified } = await this.messageModel.bulkWrite(messages); + + this.logger.verbose('messages updated in db: ' + nModified + ' messages'); + return { insertCount: nModified }; + } + + this.logger.verbose('updating messages in store'); + + const store = this.configService.get('STORE'); + + if (store.MESSAGES) { + this.logger.verbose('updating messages in store'); + data.forEach((message) => { + this.writeStore({ + path: join(this.storePath, 'messages', instanceName), + fileName: message.key.id, + data: message, + }); + this.logger.verbose( + 'messages updated in store in path: ' + + join(this.storePath, 'messages', instanceName) + + '/' + + message.key.id, + ); + }); + + this.logger.verbose('messages updated in store: ' + data.length + ' messages'); + return { insertCount: data.length }; + } + + this.logger.verbose('messages not updated'); + return { insertCount: 0 }; + } catch (error) { + this.logger.error(error); + } + } } diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index b2fbb879..352b7e0e 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -11,7 +11,9 @@ import { Logger } from '../../config/logger.config'; import { ROOT_DIR } from '../../config/path.config'; import { ChatwootDto } from '../dto/chatwoot.dto'; import { InstanceDto } from '../dto/instance.dto'; -import { SendAudioDto, SendMediaDto, SendTextDto } from '../dto/sendMessage.dto'; +import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '../dto/sendMessage.dto'; +import { MessageRaw } from '../models'; +import { RepositoryBroker } from '../repository/repository.manager'; import { WAMonitoringService } from './monitor.service'; export class ChatwootService { @@ -22,7 +24,11 @@ export class ChatwootService { private provider: any; - constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) { + constructor( + private readonly waMonitor: WAMonitoringService, + private readonly configService: ConfigService, + private readonly repository: RepositoryBroker, + ) { this.messageCache = new Set(); } @@ -640,6 +646,7 @@ export class ChatwootService { encoding: string; filename: string; }[], + messageBody?: any, ) { this.logger.verbose('create message to instance: ' + instance.instanceName); @@ -650,6 +657,8 @@ export class ChatwootService { return null; } + const replyToIds = await this.getReplyToIds(messageBody, instance); + this.logger.verbose('create message in chatwoot'); const message = await client.messages.create({ accountId: this.provider.account_id, @@ -659,6 +668,9 @@ export class ChatwootService { message_type: messageType, attachments: attachments, private: privateMessage || false, + content_attributes: { + ...replyToIds, + }, }, }); @@ -754,6 +766,8 @@ export class ChatwootService { file: string, messageType: 'incoming' | 'outgoing' | undefined, content?: string, + instance?: InstanceDto, + messageBody?: any, ) { this.logger.verbose('send data to chatwoot'); @@ -770,6 +784,16 @@ export class ChatwootService { this.logger.verbose('temp file found'); data.append('attachments[]', createReadStream(file)); + if (messageBody && instance) { + const replyToIds = await this.getReplyToIds(messageBody, instance); + + if (replyToIds.in_reply_to || replyToIds.in_reply_to_external_id) { + data.append('content_attributes', { + ...replyToIds, + }); + } + } + this.logger.verbose('get client to instance: ' + this.provider.instanceName); const config = { method: 'post', @@ -890,7 +914,7 @@ export class ChatwootService { } } - public async sendAttachment(waInstance: any, number: string, media: any, caption?: string) { + public async sendAttachment(waInstance: any, number: string, media: any, caption?: string, options?: Options) { this.logger.verbose('send attachment to instance: ' + waInstance.instanceName); try { @@ -932,13 +956,14 @@ export class ChatwootService { options: { delay: 1200, presence: 'recording', + ...options, }, }; - await waInstance?.audioWhatsapp(data, true); + const messageSent = await waInstance?.audioWhatsapp(data, true); this.logger.verbose('audio sent'); - return; + return messageSent; } this.logger.verbose('send media to instance: ' + waInstance.instanceName); @@ -952,6 +977,7 @@ export class ChatwootService { options: { delay: 1200, presence: 'composing', + ...options, }, }; @@ -960,10 +986,10 @@ export class ChatwootService { data.mediaMessage.caption = caption; } - await waInstance?.mediaMessage(data, true); + const messageSent = await waInstance?.mediaMessage(data, true); this.logger.verbose('media sent'); - return; + return messageSent; } catch (error) { this.logger.error(error); } @@ -982,7 +1008,13 @@ export class ChatwootService { } this.logger.verbose('check if is bot'); - if (!body?.conversation || body.private || body.event === 'message_updated') return { message: 'bot' }; + if ( + !body?.conversation || + body.private || + (body.event === 'message_updated' && !body.content_attributes?.deleted) + ) { + return { message: 'bot' }; + } this.logger.verbose('check if is group'); const chatId = @@ -991,6 +1023,21 @@ export class ChatwootService { const senderName = body?.sender?.name; const waInstance = this.waMonitor.waInstances[instance.instanceName]; + this.logger.verbose('check if is a message deletion'); + if (body.event === 'message_updated' && body.content_attributes?.deleted) { + const message = await this.repository.message.find({ + where: { + owner: instance.instanceName, + chatwootMessageId: body.id, + }, + limit: 1, + }); + if (message.length && message[0].key?.id) { + await waInstance?.client.sendMessage(message[0].key.remoteJid, { delete: message[0].key }); + } + return { message: 'bot' }; + } + if (chatId === '123456' && body.message_type === 'outgoing') { this.logger.verbose('check if is command'); @@ -1082,7 +1129,26 @@ export class ChatwootService { formatText = null; } - await this.sendAttachment(waInstance, chatId, attachment.data_url, formatText); + const options: Options = { + quoted: await this.getQuotedMessage(body, instance), + }; + + const messageSent = await this.sendAttachment( + waInstance, + chatId, + attachment.data_url, + formatText, + options, + ); + + this.updateChatwootMessageId( + { + ...messageSent, + owner: instance.instanceName, + }, + body.id, + instance, + ); } } else { this.logger.verbose('message is text'); @@ -1096,10 +1162,20 @@ export class ChatwootService { options: { delay: 1200, presence: 'composing', + quoted: await this.getQuotedMessage(body, instance), }, }; - await waInstance?.textMessage(data, true); + const messageSent = await waInstance?.textMessage(data, true); + + this.updateChatwootMessageId( + { + ...messageSent, + owner: instance.instanceName, + }, + body.id, + instance, + ); } } } @@ -1131,6 +1207,65 @@ export class ChatwootService { } } + private updateChatwootMessageId(message: MessageRaw, chatwootMessageId: string, instance: InstanceDto) { + if (!chatwootMessageId) { + return; + } + message.chatwootMessageId = chatwootMessageId; + this.repository.message.update([message], instance.instanceName, true); + } + + private async getReplyToIds( + msg: any, + instance: InstanceDto, + ): Promise<{ in_reply_to: string; in_reply_to_external_id: string }> { + let inReplyTo = null; + let inReplyToExternalId = null; + + if (msg) { + inReplyToExternalId = msg.message?.extendedTextMessage?.contextInfo?.stanzaId; + if (inReplyToExternalId) { + const message = await this.repository.message.find({ + where: { + key: { + id: inReplyToExternalId, + }, + owner: instance.instanceName, + }, + limit: 1, + }); + if (message.length && message[0]?.chatwootMessageId) { + inReplyTo = message[0].chatwootMessageId; + } + } + } + + return { + in_reply_to: inReplyTo, + in_reply_to_external_id: inReplyToExternalId, + }; + } + + private async getQuotedMessage(msg: any, instance: InstanceDto): Promise { + if (msg?.content_attributes?.in_reply_to) { + const message = await this.repository.message.find({ + where: { + chatwootMessageId: msg?.content_attributes?.in_reply_to, + owner: instance.instanceName, + }, + limit: 1, + }); + if (message.length && message[0]?.key?.id) { + return { + key: message[0].key, + message: message[0].message, + }; + } + } + + return null; + } + private isMediaMessage(message: any) { this.logger.verbose('check if is media message'); const media = [ @@ -1164,6 +1299,18 @@ export class ChatwootService { return adsMessage; } + private getReactionMessage(msg: any) { + interface ReactionMessage { + key: MessageRaw['key']; + text: string; + } + const reactionMessage: ReactionMessage | undefined = msg?.reactionMessage; + + this.logger.verbose('Get reaction message if it exists'); + reactionMessage && this.logger.verbose('Reaction message: ' + reactionMessage); + return reactionMessage; + } + private getTypeMessage(msg: any) { this.logger.verbose('get type message'); @@ -1319,7 +1466,9 @@ export class ChatwootService { const adsMessage = this.getAdsMessage(body.message); - if (!bodyMessage && !isMedia) { + const reactionMessage = this.getReactionMessage(body.message); + + if (!bodyMessage && !isMedia && !reactionMessage) { this.logger.warn('no body message found'); return; } @@ -1378,7 +1527,7 @@ export class ChatwootService { } this.logger.verbose('send data to chatwoot'); - const send = await this.sendData(getConversation, fileName, messageType, content); + const send = await this.sendData(getConversation, fileName, messageType, content, instance, body); if (!send) { this.logger.warn('message not sent'); @@ -1399,7 +1548,7 @@ export class ChatwootService { this.logger.verbose('message is not group'); this.logger.verbose('send data to chatwoot'); - const send = await this.sendData(getConversation, fileName, messageType, bodyMessage); + const send = await this.sendData(getConversation, fileName, messageType, bodyMessage, instance, body); if (!send) { this.logger.warn('message not sent'); @@ -1419,6 +1568,35 @@ export class ChatwootService { } } + this.logger.verbose('check if has ReactionMessage'); + if (reactionMessage) { + this.logger.verbose('send data to chatwoot'); + if (reactionMessage.text) { + const send = await this.createMessage( + instance, + getConversation, + reactionMessage.text, + messageType, + false, + [], + { + message: { extendedTextMessage: { contextInfo: { stanzaId: reactionMessage.key.id } } }, + }, + ); + if (!send) { + this.logger.warn('message not sent'); + return; + } + this.messageCacheFile = path.join(ROOT_DIR, 'store', 'chatwoot', `${instance.instanceName}_cache.txt`); + this.messageCache = this.loadMessageCache(); + this.messageCache.add(send.id.toString()); + this.logger.verbose('save message cache'); + this.saveMessageCache(); + } + + return; + } + this.logger.verbose('check if has Ads Message'); if (adsMessage) { this.logger.verbose('message is from Ads'); @@ -1461,6 +1639,8 @@ export class ChatwootService { fileName, messageType, `${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`, + instance, + body, ); if (!send) { @@ -1496,7 +1676,7 @@ export class ChatwootService { } this.logger.verbose('send data to chatwoot'); - const send = await this.createMessage(instance, getConversation, content, messageType); + const send = await this.createMessage(instance, getConversation, content, messageType, false, [], body); if (!send) { this.logger.warn('message not sent'); @@ -1517,7 +1697,7 @@ export class ChatwootService { this.logger.verbose('message is not group'); this.logger.verbose('send data to chatwoot'); - const send = await this.createMessage(instance, getConversation, bodyMessage, messageType); + const send = await this.createMessage(instance, getConversation, bodyMessage, messageType, false, [], body); if (!send) { this.logger.warn('message not sent'); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index f2e6d811..73cbfdf6 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -166,7 +166,7 @@ export class WAStartupService { private phoneNumber: string; - private chatwootService = new ChatwootService(waMonitor, this.configService); + private chatwootService = new ChatwootService(waMonitor, this.configService, this.repository); private typebotService = new TypebotService(waMonitor, this.configService); @@ -1778,11 +1778,15 @@ export class WAStartupService { this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( + const chatwootSentMessage = await this.chatwootService.eventWhatsapp( Events.MESSAGES_UPSERT, { instanceName: this.instance.name }, messageRaw, ); + + if (chatwootSentMessage?.id) { + messageRaw.chatwootMessageId = chatwootSentMessage.id; + } } const typebotSessionRemoteJid = this.localTypebot.sessions?.find( diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index d64d2532..1fb84de6 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -129,9 +129,9 @@ const sqsService = new SqsService(waMonitor); export const sqsController = new SqsController(sqsService); -const chatwootService = new ChatwootService(waMonitor, configService); +const chatwootService = new ChatwootService(waMonitor, configService, repository); -export const chatwootController = new ChatwootController(chatwootService, configService); +export const chatwootController = new ChatwootController(chatwootService, configService, repository); const settingsService = new SettingsService(waMonitor); From 1568554a1c3d6449c4d59c6a81cb8bee12ce478b Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 15:24:54 -0300 Subject: [PATCH 63/68] feat: added reply, delete and message reaction in chatwoot v3.3.1 --- CHANGELOG.md | 5 +++++ src/whatsapp/models/message.model.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a7f22ab..ac6d2b06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,11 @@ * Added restart instance when update profile picture * Correction of chatwoot functioning with admin flows +### Integrations + +- Chatwoot: v3.3.1 +- Typebot: v2.16.0 + # 1.5.4 (2023-10-09 20:43) ### Fixed diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts index af5b7d0b..395b100b 100644 --- a/src/whatsapp/models/message.model.ts +++ b/src/whatsapp/models/message.model.ts @@ -43,6 +43,11 @@ const messageSchema = new Schema({ chatwootMessageId: { type: String, required: false }, }); +messageSchema.index({ chatwootMessageId: 1, owner: 1 }); +messageSchema.index({ 'key.id': 1 }); +messageSchema.index({ 'key.id': 1, owner: 1 }); +messageSchema.index({ owner: 1 }); + export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages'); export type IMessageModel = typeof MessageModel; From c296bf4178b8f9d56efb1e2787040265fec8571b Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 15:46:10 -0300 Subject: [PATCH 64/68] fix: Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled --- CHANGELOG.md | 1 + src/whatsapp/services/chatwoot.service.ts | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac6d2b06..856581b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * Adjusted return from queries in mongodb * Added restart instance when update profile picture * Correction of chatwoot functioning with admin flows +* Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled ### Integrations diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 352b7e0e..26f4d573 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -249,10 +249,6 @@ export class ChatwootService { inbox_id: inboxId.toString(), }; - if (this.provider.conversation_pending) { - data['status'] = 'pending'; - } - const conversation = await client.conversations.create({ accountId: this.provider.account_id, data, From f246516a6e63ed8eeb67c10dfcd8b15753848819 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 16:08:09 -0300 Subject: [PATCH 65/68] fix: fixed issue where CSAT opened a new ticket when reopen_conversation was disabled --- CHANGELOG.md | 1 + src/whatsapp/services/chatwoot.service.ts | 5 +++++ src/whatsapp/services/whatsapp.service.ts | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 856581b3..f23924f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * Added restart instance when update profile picture * Correction of chatwoot functioning with admin flows * Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled +* Fixed issue where CSAT opened a new ticket when reopen_conversation was disabled ### Integrations diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 26f4d573..c33095b4 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -1458,6 +1458,11 @@ export class ChatwootService { this.logger.verbose('get conversation message'); const bodyMessage = await this.getConversationMessage(body.message); + if (bodyMessage.includes('Por favor, classifique esta conversa, http')) { + this.logger.verbose('conversation is closed'); + return; + } + const isMedia = this.isMediaMessage(body.message); const adsMessage = this.getAdsMessage(body.message); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 73cbfdf6..9d21ed0e 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1777,7 +1777,7 @@ export class WAStartupService { this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - if (this.localChatwoot.enabled) { + if (this.localChatwoot.enabled && !received.key.id.includes('@broadcast')) { const chatwootSentMessage = await this.chatwootService.eventWhatsapp( Events.MESSAGES_UPSERT, { instanceName: this.instance.name }, From ade39520166c54cb9eadf5ea7429373e281eb1ad Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 16:28:01 -0300 Subject: [PATCH 66/68] fix: Fixed issue sending contact to Chatwoot via iOS --- CHANGELOG.md | 1 + src/whatsapp/services/chatwoot.service.ts | 12 +++++++++++- src/whatsapp/services/whatsapp.service.ts | 11 ++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f23924f8..a2d728da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ * Correction of chatwoot functioning with admin flows * Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled * Fixed issue where CSAT opened a new ticket when reopen_conversation was disabled +* Fixed issue sending contact to Chatwoot via iOS ### Integrations diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index c33095b4..a995f367 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -1373,6 +1373,11 @@ export class ChatwootService { formattedContact += `\n**number ${numberCount}:** ${phoneNumber}`; numberCount++; } + if (key.includes('TEL')) { + const phoneNumber = contactInfo[key]; + formattedContact += `\n**number:** ${phoneNumber}`; + numberCount++; + } }); this.logger.verbose('message content: ' + formattedContact); @@ -1401,6 +1406,11 @@ export class ChatwootService { formattedContact += `\n**number ${numberCount}:** ${phoneNumber}`; numberCount++; } + if (key.includes('TEL')) { + const phoneNumber = contactInfo[key]; + formattedContact += `\n**number:** ${phoneNumber}`; + numberCount++; + } }); return formattedContact; @@ -1458,7 +1468,7 @@ export class ChatwootService { this.logger.verbose('get conversation message'); const bodyMessage = await this.getConversationMessage(body.message); - if (bodyMessage.includes('Por favor, classifique esta conversa, http')) { + if (bodyMessage && bodyMessage.includes('Por favor, classifique esta conversa, http')) { this.logger.verbose('conversation is closed'); return; } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 9d21ed0e..618aa65b 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1795,11 +1795,12 @@ export class WAStartupService { if ((this.localTypebot.enabled && type === 'notify') || typebotSessionRemoteJid) { if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { - await this.typebotService.sendTypebot( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); + if (messageRaw.messageType !== 'reactionMessage') + await this.typebotService.sendTypebot( + { instanceName: this.instance.name }, + messageRaw.key.remoteJid, + messageRaw, + ); } } From ff06cd7643b3e83ef2aa6f0f21ee7ef64e8fd069 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 16:55:46 -0300 Subject: [PATCH 67/68] feat: Added support for new typebot API --- CHANGELOG.md | 2 +- Docker/.env.example | 3 +- Dockerfile | 2 +- src/dev-env.yml | 2 +- src/whatsapp/services/typebot.service.ts | 84 +++++++++++++++++------- 5 files changed, 67 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2d728da..4393e6d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Feature * Added AWS SQS Integration -* Added compatibility with typebot v2 +* Added support for new typebot API * Added endpoint sendPresence * New Instance Manager * Added auto_create to the chatwoot set to create the inbox automatically or not diff --git a/Docker/.env.example b/Docker/.env.example index b170ac28..fefd9456 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -105,7 +105,8 @@ CONFIG_SESSION_PHONE_NAME=Chrome QRCODE_LIMIT=30 QRCODE_COLOR=#198754 -TYPEBOT_API_VERSION=v1 +# old | latest +TYPEBOT_API_VERSION=latest # Defines an authentication type for the api # We recommend using the apikey because it will allow you to use a custom token, diff --git a/Dockerfile b/Dockerfile index 0195a249..10be07c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -104,7 +104,7 @@ ENV CONFIG_SESSION_PHONE_NAME=Chrome ENV QRCODE_LIMIT=30 ENV QRCODE_COLOR=#198754 -ENV TYPEBOT_API_VERSION=v1 +ENV TYPEBOT_API_VERSION=latest ENV AUTHENTICATION_TYPE=apikey diff --git a/src/dev-env.yml b/src/dev-env.yml index cefdcbeb..7c4e2f53 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -147,7 +147,7 @@ QRCODE: COLOR: "#198754" TYPEBOT: - API_VERSION: 'v1' # v1 | v2 + API_VERSION: 'latest' # old | latest # Defines an authentication type for the api # We recommend using the apikey because it will allow you to use a custom token, diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index e0f6c458..0da51193 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -171,14 +171,20 @@ export class TypebotService { const reqData = { startParams: { - typebot: data.typebot, + publicId: data.typebot, prefilledVariables: prefilledVariables, }, }; try { const version = this.configService.get('TYPEBOT').API_VERSION; - const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); + let url: string; + if (version === 'latest') { + url = `${data.url}/api/v1/typebots/${data.typebot}/startChat`; + } else { + url = `${data.url}/api/v1/sendMessage`; + } + const request = await axios.post(url, reqData); await this.sendWAMessage( instance, @@ -256,7 +262,7 @@ export class TypebotService { const reqData = { startParams: { - typebot: data.typebot, + publicId: data.typebot, prefilledVariables: { ...data.prefilledVariables, remoteJid: data.remoteJid, @@ -268,7 +274,13 @@ export class TypebotService { try { const version = this.configService.get('TYPEBOT').API_VERSION; - const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); + let url: string; + if (version === 'latest') { + url = `${data.url}/api/v1/typebots/${data.typebot}/startChat`; + } else { + url = `${data.url}/api/v1/sendMessage`; + } + const request = await axios.post(url, reqData); if (request?.data?.sessionId) { data.sessions.push({ @@ -565,14 +577,24 @@ export class TypebotService { return; } - const reqData = { - message: content, - sessionId: data.sessionId, - }; - try { const version = this.configService.get('TYPEBOT').API_VERSION; - const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); + let urlTypebot: string; + let reqData: {}; + if (version === 'latest') { + urlTypebot = `${data.url}/api/v1/sessions/${data.sessionId}/continueChat`; + reqData = { + message: content, + }; + } else { + urlTypebot = `${data.url}/api/v1/sendMessage`; + reqData = { + message: content, + sessionId: data.sessionId, + }; + } + + const request = await axios.post(urlTypebot, reqData); await this.sendWAMessage( instance, @@ -651,15 +673,24 @@ export class TypebotService { return; } - const reqData = { - message: content, - sessionId: data.sessionId, - }; - let request: any; try { const version = this.configService.get('TYPEBOT').API_VERSION; - request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData); + let urlTypebot: string; + let reqData: {}; + if (version === 'latest') { + urlTypebot = `${data.url}/api/v1/sessions/${data.sessionId}/continueChat`; + reqData = { + message: content, + }; + } else { + urlTypebot = `${data.url}/api/v1/sendMessage`; + reqData = { + message: content, + sessionId: data.sessionId, + }; + } + request = await axios.post(urlTypebot, reqData); await this.sendWAMessage( instance, @@ -734,13 +765,22 @@ export class TypebotService { return; } - const reqData = { - message: content, - sessionId: session.sessionId.split('-')[1], - }; - const version = this.configService.get('TYPEBOT').API_VERSION; - const request = await axios.post(`${url}/api/${version}/sendMessage`, reqData); + let urlTypebot: string; + let reqData: {}; + if (version === 'latest') { + urlTypebot = `${url}/api/v1/sessions/${session.sessionId.split('-')[1]}/continueChat`; + reqData = { + message: content, + }; + } else { + urlTypebot = `${url}/api/v1/sendMessage`; + reqData = { + message: content, + sessionId: session.sessionId.split('-')[1], + }; + } + const request = await axios.post(urlTypebot, reqData); await this.sendWAMessage( instance, From 379855714e7729089c70be7aebfd1ad340243426 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 12 Dec 2023 17:44:00 -0300 Subject: [PATCH 68/68] version: 1.6.0 --- CHANGELOG.md | 11 ++++++++--- package.json | 2 +- src/config/env.config.ts | 2 +- src/dev-env.yml | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4393e6d8..1b67354e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -# 1.6.0 (develop) +# 1.6.1 (develop) + +### Fixed + +* Fixed Lid Messages + +# 1.6.0 (2023-12-12 17:24) ### Feature * Added AWS SQS Integration @@ -13,7 +19,6 @@ * Adjusts in proxy * Adjusts in start session for Typebot * Added mimetype field when sending media -* Fixed lids messages * Ajusts in validations to messages.upsert * Fixed messages not received: error handling when updating contact in chatwoot * Fix workaround to manage param data as an array in mongodb @@ -32,7 +37,7 @@ ### Integrations - Chatwoot: v3.3.1 -- Typebot: v2.16.0 +- Typebot: v2.20.0 # 1.5.4 (2023-10-09 20:43) diff --git a/package.json b/package.json index f2730fe1..19891113 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@sentry/node": "^7.59.2", - "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys#fix-lids", + "@whiskeysockets/baileys": "^6.5.0", "amqplib": "^0.10.3", "aws-sdk": "^2.1499.0", "axios": "^1.3.5", diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 42c8826a..da491505 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -303,7 +303,7 @@ export class ConfigService { COLOR: process.env.QRCODE_COLOR || '#198754', }, TYPEBOT: { - API_VERSION: process.env?.TYPEBOT_API_VERSION || 'v1', + API_VERSION: process.env?.TYPEBOT_API_VERSION || 'old', }, AUTHENTICATION: { TYPE: process.env.AUTHENTICATION_TYPE as 'apikey', diff --git a/src/dev-env.yml b/src/dev-env.yml index 7c4e2f53..117226a2 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -147,7 +147,7 @@ QRCODE: COLOR: "#198754" TYPEBOT: - API_VERSION: 'latest' # old | latest + API_VERSION: 'old' # old | latest # Defines an authentication type for the api # We recommend using the apikey because it will allow you to use a custom token,