From 96be63f50bfd8aa5072b407d9eec9a4bd7578af8 Mon Sep 17 00:00:00 2001 From: edisoncm-ti Date: Sat, 20 Jan 2024 11:09:05 -0300 Subject: [PATCH 01/32] fix: remove quebra de linha no index final do array quando usado variavel no Typebot --- 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 b5640240..96483b98 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -412,7 +412,7 @@ export class TypebotService { text += element.text; } - if (element.type === 'p' || element.type === 'inline-variable' || element.type === 'a') { + if (element.children && (element.type === 'p' || element.type === 'a' || element.type === 'inline-variable' || element.type === 'variable')) { for (const child of element.children) { text += applyFormatting(child); } From 440ff2f3eac85e5c8dbd2fc50f7d9ca0882eca84 Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Sat, 20 Jan 2024 11:44:10 -0300 Subject: [PATCH 02/32] Add number parameter to OnWhatsAppDto constructor --- src/whatsapp/dto/chat.dto.ts | 7 ++++++- src/whatsapp/services/whatsapp.service.ts | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/whatsapp/dto/chat.dto.ts b/src/whatsapp/dto/chat.dto.ts index efa74db8..dc0584bb 100644 --- a/src/whatsapp/dto/chat.dto.ts +++ b/src/whatsapp/dto/chat.dto.ts @@ -1,7 +1,12 @@ 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) {} + constructor( + public readonly jid: string, + public readonly exists: boolean, + public readonly number: string, + public readonly name?: string, + ) {} } export class getBase64FromMediaMessageDto { diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c49df6bf..80ac7397 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3137,9 +3137,9 @@ export class WAStartupService { if (!group) throw new BadRequestException('Group not found'); - onWhatsapp.push(new OnWhatsAppDto(group.id, !!group?.id, group?.subject)); + onWhatsapp.push(new OnWhatsAppDto(group.id, !!group?.id, number, group?.subject)); } else if (jid === 'status@broadcast') { - onWhatsapp.push(new OnWhatsAppDto(jid, false)); + onWhatsapp.push(new OnWhatsAppDto(jid, false, number)); } else { jid = !jid.startsWith('+') ? `+${jid}` : jid; const verify = await this.client.onWhatsApp(jid); @@ -3147,9 +3147,9 @@ export class WAStartupService { const result = verify[0]; if (!result) { - onWhatsapp.push(new OnWhatsAppDto(jid, false)); + onWhatsapp.push(new OnWhatsAppDto(jid, false, number)); } else { - onWhatsapp.push(new OnWhatsAppDto(result.jid, result.exists)); + onWhatsapp.push(new OnWhatsAppDto(result.jid, result.exists, number)); } } } From c9b0e6664158b706e669cb03b30636eabbd809ea Mon Sep 17 00:00:00 2001 From: edisoncm-ti Date: Sat, 20 Jan 2024 18:28:00 -0300 Subject: [PATCH 03/32] feat: Obter a resposta de um sendList (Lista) e prosseguir o fluxo Typebot --- src/whatsapp/services/typebot.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 96483b98..36645f63 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -274,6 +274,7 @@ export class TypebotService { const types = { conversation: msg.conversation, extendedTextMessage: msg.extendedTextMessage?.text, + responseRowId: msg.listResponseMessage.singleSelectReply?.selectedRowId, }; this.logger.verbose('type message: ' + types); From 1a633dcf105656db7ca6e25a73cb29cb37414ab0 Mon Sep 17 00:00:00 2001 From: edisoncm-ti Date: Sat, 20 Jan 2024 18:31:26 -0300 Subject: [PATCH 04/32] feat: obter a resposta de um sendList (Lista) e prosseguir o fluxo Typebot --- src/whatsapp/services/typebot.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 96483b98..36645f63 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -274,6 +274,7 @@ export class TypebotService { const types = { conversation: msg.conversation, extendedTextMessage: msg.extendedTextMessage?.text, + responseRowId: msg.listResponseMessage.singleSelectReply?.selectedRowId, }; this.logger.verbose('type message: ' + types); From e19e37eef48aa78bb14f1369f9a1b3007771dacb Mon Sep 17 00:00:00 2001 From: Leandro Rocha Date: Sun, 21 Jan 2024 02:13:24 -0300 Subject: [PATCH 05/32] Join in Group by Invite Code --- package.json | 2 +- src/validate/validate.schema.ts | 10 ++++++++++ src/whatsapp/controllers/group.controller.ts | 6 ++++++ src/whatsapp/dto/group.dto.ts | 4 ++++ src/whatsapp/routers/group.router.ts | 18 ++++++++++++++++++ src/whatsapp/services/whatsapp.service.ts | 11 +++++++++++ 6 files changed, 50 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f0c56235..828e0d1b 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": "6.5.0", "amqplib": "^0.10.3", "aws-sdk": "^2.1499.0", "axios": "^1.3.5", diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index efc6f685..498d9982 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -771,6 +771,16 @@ export const groupInviteSchema: JSONSchema7 = { ...isNotEmpty('inviteCode'), }; +export const AcceptGroupInviteSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + inviteCode: { type: 'string', pattern: '^[a-zA-Z0-9]{22}$' }, + }, + required: ['inviteCode'], + ...isNotEmpty('inviteCode'), +}; + export const updateParticipantsSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/controllers/group.controller.ts b/src/whatsapp/controllers/group.controller.ts index 0cf093ca..e452bc0c 100644 --- a/src/whatsapp/controllers/group.controller.ts +++ b/src/whatsapp/controllers/group.controller.ts @@ -1,5 +1,6 @@ import { Logger } from '../../config/logger.config'; import { + AcceptGroupInvite, CreateGroupDto, GetParticipant, GroupDescriptionDto, @@ -65,6 +66,11 @@ export class GroupController { return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data); } + public async acceptInviteCode(instance: InstanceDto, inviteCode: AcceptGroupInvite) { + logger.verbose('requested acceptInviteCode from ' + instance.instanceName + ' instance'); + return await this.waMonitor.waInstances[instance.instanceName].acceptInviteCode(inviteCode); + } + public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) { logger.verbose('requested revokeInviteCode from ' + instance.instanceName + ' instance'); return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(groupJid); diff --git a/src/whatsapp/dto/group.dto.ts b/src/whatsapp/dto/group.dto.ts index 6dfdc45c..293329d2 100644 --- a/src/whatsapp/dto/group.dto.ts +++ b/src/whatsapp/dto/group.dto.ts @@ -32,6 +32,10 @@ export class GroupInvite { inviteCode: string; } +export class AcceptGroupInvite { + inviteCode: string; +} + export class GroupSendInvite { groupJid: string; description: string; diff --git a/src/whatsapp/routers/group.router.ts b/src/whatsapp/routers/group.router.ts index f59e82a4..bf088129 100644 --- a/src/whatsapp/routers/group.router.ts +++ b/src/whatsapp/routers/group.router.ts @@ -2,6 +2,7 @@ import { RequestHandler, Router } from 'express'; import { Logger } from '../../config/logger.config'; import { + AcceptGroupInviteSchema, createGroupSchema, getParticipantsSchema, groupInviteSchema, @@ -16,6 +17,7 @@ import { } from '../../validate/validate.schema'; import { RouterBroker } from '../abstract/abstract.router'; import { + AcceptGroupInvite, CreateGroupDto, GetParticipant, GroupDescriptionDto, @@ -182,6 +184,22 @@ export class GroupRouter extends RouterBroker { res.status(HttpStatus.OK).json(response); }) + .get(this.routerPath('acceptInviteCode'), ...guards, async (req, res) => { + logger.verbose('request received in acceptInviteCode'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.inviteCodeValidate({ + request: req, + schema: AcceptGroupInviteSchema, + ClassRef: AcceptGroupInvite, + execute: (instance, data) => groupController.acceptInviteCode(instance, data), + }); + + res.status(HttpStatus.OK).json(response); + }) .post(this.routerPath('sendInvite'), ...guards, async (req, res) => { logger.verbose('request received in sendInvite'); logger.verbose('request body: '); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index c49df6bf..024b88bd 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -89,6 +89,7 @@ import { WhatsAppNumberDto, } from '../dto/chat.dto'; import { + AcceptGroupInvite, CreateGroupDto, GetParticipant, GroupDescriptionDto, @@ -3744,6 +3745,16 @@ export class WAStartupService { } } + public async acceptInviteCode(id: AcceptGroupInvite) { + this.logger.verbose('Joining the group by invitation code: ' + id.inviteCode); + try { + const groupJid = await this.client.groupAcceptInvite(id.inviteCode); + return { accepted: true, groupJid: groupJid }; + } catch (error) { + throw new NotFoundException('Accept invite error', error.toString()); + } + } + public async revokeInviteCode(id: GroupJid) { this.logger.verbose('Revoking invite code for group: ' + id.groupJid); try { From 35520d85a26e74800550175981151634b49ebfbe Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Sun, 21 Jan 2024 13:00:23 -0300 Subject: [PATCH 06/32] Add https-proxy-agent dependency and handle NotFoundException in ProxyController --- package.json | 1 + src/whatsapp/controllers/proxy.controller.ts | 37 +++++++++++--------- src/whatsapp/whatsapp.module.ts | 2 +- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index f0c56235..d5a5076f 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "express": "^4.18.2", "express-async-errors": "^3.1.1", "hbs": "^4.2.0", + "https-proxy-agent": "^7.0.2", "jimp": "^0.16.13", "join": "^3.0.0", "js-yaml": "^4.1.0", diff --git a/src/whatsapp/controllers/proxy.controller.ts b/src/whatsapp/controllers/proxy.controller.ts index 555c5975..3613433b 100644 --- a/src/whatsapp/controllers/proxy.controller.ts +++ b/src/whatsapp/controllers/proxy.controller.ts @@ -1,19 +1,25 @@ import axios from 'axios'; +import { HttpsProxyAgent } from 'https-proxy-agent'; import { Logger } from '../../config/logger.config'; -import { BadRequestException } from '../../exceptions'; +import { BadRequestException, NotFoundException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; import { ProxyDto } from '../dto/proxy.dto'; +import { WAMonitoringService } from '../services/monitor.service'; import { ProxyService } from '../services/proxy.service'; const logger = new Logger('ProxyController'); export class ProxyController { - constructor(private readonly proxyService: ProxyService) {} + constructor(private readonly proxyService: ProxyService, private readonly waMonitor: WAMonitoringService) {} public async createProxy(instance: InstanceDto, data: ProxyDto) { logger.verbose('requested createProxy from ' + instance.instanceName + ' instance'); + if (!this.waMonitor.waInstances[instance.instanceName]) { + throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`); + } + if (!data.enabled) { logger.verbose('proxy disabled'); data.proxy = null; @@ -33,37 +39,36 @@ export class ProxyController { public async findProxy(instance: InstanceDto) { logger.verbose('requested findProxy from ' + instance.instanceName + ' instance'); + + if (!this.waMonitor.waInstances[instance.instanceName]) { + throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`); + } + return this.proxyService.find(instance); } private async testProxy(host: string, port: string, protocol: string, username?: string, password?: string) { logger.verbose('requested testProxy'); try { - let proxyConfig: any = { - host: host, - port: parseInt(port), - protocol: protocol, - }; + let proxyUrl = `${protocol}://${host}:${port}`; if (username && password) { - proxyConfig = { - ...proxyConfig, - auth: { - username: username, - password: password, - }, - }; + proxyUrl = `${protocol}://${username}:${password}@${host}:${port}`; } const serverIp = await axios.get('http://meuip.com/api/meuip.php'); const response = await axios.get('http://meuip.com/api/meuip.php', { - proxy: proxyConfig, + httpsAgent: new HttpsProxyAgent(proxyUrl), }); logger.verbose('testProxy response: ' + response.data); return response.data !== serverIp.data; } catch (error) { - logger.error('testProxy error: ' + error); + let errorMessage = error; + if (axios.isAxiosError(error) && error.response.data) { + errorMessage = error.response.data; + } + logger.error('testProxy error: ' + errorMessage); return false; } } diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index 0b5da554..3e52504f 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -119,7 +119,7 @@ export const websocketController = new WebsocketController(websocketService); const proxyService = new ProxyService(waMonitor); -export const proxyController = new ProxyController(proxyService); +export const proxyController = new ProxyController(proxyService, waMonitor); const chamaaiService = new ChamaaiService(waMonitor, configService); From 1e9b3a1e426ffc1784e83224fb5ce3d6af1405fb Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Sun, 21 Jan 2024 13:01:06 -0300 Subject: [PATCH 07/32] Update proxy controller to use a different IP lookup service --- src/whatsapp/controllers/proxy.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/whatsapp/controllers/proxy.controller.ts b/src/whatsapp/controllers/proxy.controller.ts index 3613433b..359b31a9 100644 --- a/src/whatsapp/controllers/proxy.controller.ts +++ b/src/whatsapp/controllers/proxy.controller.ts @@ -55,9 +55,9 @@ export class ProxyController { if (username && password) { proxyUrl = `${protocol}://${username}:${password}@${host}:${port}`; } - const serverIp = await axios.get('http://meuip.com/api/meuip.php'); - const response = await axios.get('http://meuip.com/api/meuip.php', { + const serverIp = await axios.get('https://icanhazip.com/'); + const response = await axios.get('https://icanhazip.com/', { httpsAgent: new HttpsProxyAgent(proxyUrl), }); From 82e111f1be5646dabe468aee1cf1cf84d004db74 Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Sun, 21 Jan 2024 13:01:35 -0300 Subject: [PATCH 08/32] Fix proxy handling in WAStartupService preventing instance to be created --- src/whatsapp/services/whatsapp.service.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 80ac7397..d52b632a 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1384,14 +1384,18 @@ export class WAStartupService { this.logger.info('Proxy enabled: ' + this.localProxy.proxy); if (this.localProxy.proxy.host.includes('proxyscrape')) { - const response = await axios.get(this.localProxy.proxy.host); - 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]; - options = { - agent: new ProxyAgent(proxyUrl as any), - }; + try { + const response = await axios.get(this.localProxy.proxy.host); + 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]; + options = { + agent: new ProxyAgent(proxyUrl as any), + }; + } catch (error) { + this.localProxy.enabled = false; + } } else { let proxyUri = this.localProxy.proxy.protocol + '://' + this.localProxy.proxy.host + ':' + this.localProxy.proxy.port; From 5292e569d98166e7c8059189e649ab4cc17788e6 Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Sun, 21 Jan 2024 13:54:42 -0300 Subject: [PATCH 09/32] Remove unused dependencies and refactor proxy handling In some tests made on the testProxy function, the `new ProxyAgent()` was not working, even adding it the IP result was the same. I've change it to the `new HttpsProxyAgent()` and it worked. Refactored the proxy agent to center the same function/creation the same where it is used --- package.json | 1 - src/utils/makeProxyAgent.ts | 17 +++++++++++++++++ src/whatsapp/controllers/proxy.controller.ts | 15 ++++----------- src/whatsapp/services/whatsapp.service.ts | 17 +++++------------ 4 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 src/utils/makeProxyAgent.ts diff --git a/package.json b/package.json index d5a5076f..5b36dc3d 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "node-mime-types": "^1.1.0", "node-windows": "^1.0.0-beta.8", "pino": "^8.11.0", - "proxy-agent": "^6.3.0", "qrcode": "^1.5.1", "qrcode-terminal": "^0.12.0", "redis": "^4.6.5", diff --git a/src/utils/makeProxyAgent.ts b/src/utils/makeProxyAgent.ts new file mode 100644 index 00000000..45486523 --- /dev/null +++ b/src/utils/makeProxyAgent.ts @@ -0,0 +1,17 @@ +import { HttpsProxyAgent } from 'https-proxy-agent'; + +import { wa } from '../whatsapp/types/wa.types'; + +export function makeProxyAgent(proxy: wa.Proxy | string) { + if (typeof proxy === 'string') { + return new HttpsProxyAgent(proxy); + } + + const { host, password, port, protocol, username } = proxy; + let proxyUrl = `${protocol}://${host}:${port}`; + + if (username && password) { + proxyUrl = `${protocol}://${username}:${password}@${host}:${port}`; + } + return new HttpsProxyAgent(proxyUrl); +} diff --git a/src/whatsapp/controllers/proxy.controller.ts b/src/whatsapp/controllers/proxy.controller.ts index 359b31a9..0dc79a3a 100644 --- a/src/whatsapp/controllers/proxy.controller.ts +++ b/src/whatsapp/controllers/proxy.controller.ts @@ -1,8 +1,8 @@ import axios from 'axios'; -import { HttpsProxyAgent } from 'https-proxy-agent'; import { Logger } from '../../config/logger.config'; import { BadRequestException, NotFoundException } from '../../exceptions'; +import { makeProxyAgent } from '../../utils/makeProxyAgent'; import { InstanceDto } from '../dto/instance.dto'; import { ProxyDto } from '../dto/proxy.dto'; import { WAMonitoringService } from '../services/monitor.service'; @@ -27,8 +27,7 @@ export class ProxyController { if (data.proxy) { logger.verbose('proxy enabled'); - const { host, port, protocol, username, password } = data.proxy; - const testProxy = await this.testProxy(host, port, protocol, username, password); + const testProxy = await this.testProxy(data.proxy); if (!testProxy) { throw new BadRequestException('Invalid proxy'); } @@ -47,18 +46,12 @@ export class ProxyController { return this.proxyService.find(instance); } - private async testProxy(host: string, port: string, protocol: string, username?: string, password?: string) { + private async testProxy(proxy: ProxyDto['proxy']) { logger.verbose('requested testProxy'); try { - let proxyUrl = `${protocol}://${host}:${port}`; - - if (username && password) { - proxyUrl = `${protocol}://${username}:${password}@${host}:${port}`; - } - const serverIp = await axios.get('https://icanhazip.com/'); const response = await axios.get('https://icanhazip.com/', { - httpsAgent: new HttpsProxyAgent(proxyUrl), + httpsAgent: makeProxyAgent(proxy), }); logger.verbose('testProxy response: ' + response.data); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index d52b632a..9b9539ce 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -45,7 +45,6 @@ import { getMIMEType } from 'node-mime-types'; import { release } from 'os'; import { join } from 'path'; import P from 'pino'; -import { ProxyAgent } from 'proxy-agent'; import qrcode, { QRCodeToDataURLOptions } from 'qrcode'; import qrcodeTerminal from 'qrcode-terminal'; import sharp from 'sharp'; @@ -73,6 +72,7 @@ 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 { makeProxyAgent } from '../../utils/makeProxyAgent'; import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; import { @@ -1391,21 +1391,14 @@ export class WAStartupService { const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); const proxyUrl = 'http://' + proxyUrls[rand]; options = { - agent: new ProxyAgent(proxyUrl as any), + agent: makeProxyAgent(proxyUrl), }; } catch (error) { this.localProxy.enabled = false; } } else { - let proxyUri = - this.localProxy.proxy.protocol + '://' + this.localProxy.proxy.host + ':' + this.localProxy.proxy.port; - - if (this.localProxy.proxy.username && this.localProxy.proxy.password) { - proxyUri = `${this.localProxy.proxy.username}:${this.localProxy.proxy.password}@${proxyUri}`; - } - options = { - agent: new ProxyAgent(proxyUri as any), + agent: makeProxyAgent(this.localProxy.proxy), }; } } @@ -1492,8 +1485,8 @@ 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), + agent: makeProxyAgent(this.localProxy.proxy), + fetchAgent: makeProxyAgent(this.localProxy.proxy), }; } From 9e5bf935809f53ab9654687864b244457625f5df Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Mon, 22 Jan 2024 13:39:11 -0300 Subject: [PATCH 10/32] Fix polls in message sending --- 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 0d8fb5b2..df455a06 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -2469,7 +2469,7 @@ export class WAStartupService { ); } - if (!message['audio'] && sender != 'status@broadcast') { + if (!message['audio'] && !message['poll'] && sender != 'status@broadcast') { this.logger.verbose('Sending message'); return await this.client.sendMessage( sender, From ed6c50621c97c1e587aa95bdd3ed805aeed5c48d Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Tue, 23 Jan 2024 18:16:15 -0300 Subject: [PATCH 11/32] Improved the method of numbers validation --- package.json | 2 + src/whatsapp/services/whatsapp.service.ts | 73 ++++++++++++++++------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 20ff59d9..a1477452 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "exiftool-vendored": "^22.0.0", "express": "^4.18.2", "express-async-errors": "^3.1.1", + "fast-levenshtein": "^3.0.0", "hbs": "^4.2.0", "https-proxy-agent": "^7.0.2", "jimp": "^0.16.13", @@ -88,6 +89,7 @@ "@types/compression": "^1.7.2", "@types/cors": "^2.8.13", "@types/express": "^4.17.17", + "@types/fast-levenshtein": "^0.0.4", "@types/js-yaml": "^4.0.5", "@types/jsonwebtoken": "^8.5.9", "@types/mime-types": "^2.1.1", diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 0d8fb5b2..76511260 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -38,6 +38,7 @@ import axios from 'axios'; import { exec, execSync } from 'child_process'; import { arrayUnique, isBase64, isURL } from 'class-validator'; import EventEmitter2 from 'eventemitter2'; +import levenshtein from 'fast-levenshtein'; import fs, { existsSync, readFileSync } from 'fs'; import Long from 'long'; import NodeCache from 'node-cache'; @@ -3126,31 +3127,63 @@ export class WAStartupService { public async whatsappNumber(data: WhatsAppNumberDto) { this.logger.verbose('Getting whatsapp number'); - const onWhatsapp: OnWhatsAppDto[] = []; - for await (const number of data.numbers) { - let jid = this.createJid(number); + const jids: { + groups: { number: string; jid: string }[]; + broadcast: { number: string; jid: string }[]; + users: { number: string; jid: string }[]; + } = { + groups: [], + broadcast: [], + users: [], + }; + + data.numbers.forEach((number) => { + const jid = this.createJid(number); if (isJidGroup(jid)) { + jids.groups.push({ number, jid }); + } else if (jid === 'status@broadcast') { + jids.broadcast.push({ number, jid }); + } else { + jids.users.push({ number, jid }); + } + }); + + const onWhatsapp: OnWhatsAppDto[] = []; + + // BROADCAST + onWhatsapp.push(...jids.broadcast.map(({ jid, number }) => new OnWhatsAppDto(jid, false, number))); + + // GROUPS + const groups = await Promise.all( + jids.groups.map(async ({ jid, number }) => { const group = await this.findGroup({ groupJid: jid }, 'inner'); - if (!group) throw new BadRequestException('Group not found'); - - onWhatsapp.push(new OnWhatsAppDto(group.id, !!group?.id, number, group?.subject)); - } else if (jid === 'status@broadcast') { - onWhatsapp.push(new OnWhatsAppDto(jid, false, number)); - } else { - jid = !jid.startsWith('+') ? `+${jid}` : jid; - const verify = await this.client.onWhatsApp(jid); - - const result = verify[0]; - - if (!result) { - onWhatsapp.push(new OnWhatsAppDto(jid, false, number)); - } else { - onWhatsapp.push(new OnWhatsAppDto(result.jid, result.exists, number)); + if (!group) { + new OnWhatsAppDto(jid, false, number); } - } - } + + return new OnWhatsAppDto(group.id, !!group?.id, number, group?.subject); + }), + ); + onWhatsapp.push(...groups); + + // USERS + const verify = await this.client.onWhatsApp( + ...jids.users.map(({ jid }) => (!jid.startsWith('+') ? `+${jid}` : jid)), + ); + const users: OnWhatsAppDto[] = jids.users.map((user) => { + const MAX_SIMILARITY_THRESHOLD = 0.0358; + const numberVerified = verify.find( + (v) => levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length) <= MAX_SIMILARITY_THRESHOLD, + ); + return { + exists: !!numberVerified?.exists, + jid: numberVerified?.jid || user.jid, + number: user.number, + }; + }); + onWhatsapp.push(...users); return onWhatsapp; } From eb96c9feceeba6a24a358244494aa0ea8e255d45 Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Tue, 23 Jan 2024 18:37:31 -0300 Subject: [PATCH 12/32] Added new validation on the 9 digit This is to prevent returning another jid when only one digit is different and not the 9 digit --- src/whatsapp/services/whatsapp.service.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 76511260..4919c1bb 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3173,10 +3173,15 @@ export class WAStartupService { ...jids.users.map(({ jid }) => (!jid.startsWith('+') ? `+${jid}` : jid)), ); const users: OnWhatsAppDto[] = jids.users.map((user) => { - const MAX_SIMILARITY_THRESHOLD = 0.0358; - const numberVerified = verify.find( - (v) => levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length) <= MAX_SIMILARITY_THRESHOLD, - ); + const MAX_SIMILARITY_THRESHOLD = 0.01; + const isBrWithDigit = user.jid.startsWith('55') && user.jid.slice(4, 5) === '9' && user.jid.length === 28; + const jid = isBrWithDigit ? user.jid.slice(0, 4) + user.jid.slice(5) : user.jid; + + const numberVerified = verify.find((v) => { + const mainJidSimilarity = levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length); + const jidSimilarity = levenshtein.get(jid, v.jid) / Math.max(jid.length, v.jid.length); + return mainJidSimilarity <= MAX_SIMILARITY_THRESHOLD || jidSimilarity <= MAX_SIMILARITY_THRESHOLD; + }); return { exists: !!numberVerified?.exists, jid: numberVerified?.jid || user.jid, From 804d1777824e530ce1a15d5a7fbb7acaeff73ad0 Mon Sep 17 00:00:00 2001 From: Amilton Morais Date: Tue, 23 Jan 2024 19:34:36 -0300 Subject: [PATCH 13/32] Udate "@whiskeysockets/baileys": "6.6.0" and models The message.model.ts class was updated to version 6.6.0 of baileys. 'unknown','desktop' was included to avoid errors using version 6.6.0 of baileys --- package.json | 12 +++++++----- src/whatsapp/models/message.model.ts | 4 ++-- start.sh | 0 3 files changed, 9 insertions(+), 7 deletions(-) mode change 100755 => 100644 start.sh diff --git a/package.json b/package.json index 20ff59d9..4fbf620b 100644 --- a/package.json +++ b/package.json @@ -46,11 +46,11 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@sentry/node": "^7.59.2", - "@whiskeysockets/baileys": "6.5.0", + "@whiskeysockets/baileys": "6.6.0", "amqplib": "^0.10.3", "aws-sdk": "^2.1499.0", - "axios": "^1.3.5", - "class-validator": "^0.13.2", + "axios": "^1.6.5", + "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", "cross-env": "^7.0.3", @@ -66,22 +66,24 @@ "join": "^3.0.0", "js-yaml": "^4.1.0", "jsonschema": "^1.4.1", - "jsonwebtoken": "^8.5.1", + "jsonwebtoken": "^9.0.2", "libphonenumber-js": "^1.10.39", "link-preview-js": "^3.0.4", "mongoose": "^6.10.5", "node-cache": "^5.1.2", "node-mime-types": "^1.1.0", "node-windows": "^1.0.0-beta.8", + "parse-bmfont-xml": "^1.1.4", "pino": "^8.11.0", "qrcode": "^1.5.1", "qrcode-terminal": "^0.12.0", "redis": "^4.6.5", - "sharp": "^0.30.7", + "sharp": "^0.32.2", "socket.io": "^4.7.1", "socks-proxy-agent": "^8.0.1", "swagger-ui-express": "^5.0.0", "uuid": "^9.0.0", + "xml2js": "^0.6.2", "yamljs": "^0.3.0" }, "devDependencies": { diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts index 9c7ac9dc..8388799c 100644 --- a/src/whatsapp/models/message.model.ts +++ b/src/whatsapp/models/message.model.ts @@ -26,7 +26,7 @@ export class MessageRaw { messageType?: string; messageTimestamp?: number | Long.Long; owner: string; - source?: 'android' | 'web' | 'ios'; + source?: 'android' | 'web' | 'ios' | 'unknown' | 'desktop'; source_id?: string; source_reply_id?: string; chatwoot?: ChatwootMessage; @@ -45,7 +45,7 @@ const messageSchema = new Schema({ participant: { type: String }, messageType: { type: String }, message: { type: Object }, - source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] }, + source: { type: String, minlength: 3, enum: ['android', 'web', 'ios','unknown','desktop' ] }, messageTimestamp: { type: Number, required: true }, owner: { type: String, required: true, minlength: 1 }, chatwoot: { diff --git a/start.sh b/start.sh old mode 100755 new mode 100644 From 679f8118f6fc16e806dbeb13ec12f6743644ffa7 Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Tue, 23 Jan 2024 23:59:12 -0300 Subject: [PATCH 14/32] Added sendList endpoint to swagger documentation --- src/docs/swagger.yaml | 67 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 924434e9..d3301cae 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -940,7 +940,72 @@ paths: description: Successful response content: application/json: {} - + /message/sendList/{instanceName}: + post: + tags: + - Send Message Controller + summary: Send a list to a specified instance. + description: This endpoint allows users to send a list to a chat. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + number: + type: string + options: + type: object + properties: + delay: + type: integer + presence: + type: string + listMessage: + type: object + properties: + title: + type: string + description: + type: string + footerText: + type: string + nullable: true + buttonText: + type: string + sections: + type: array + items: + type: object + properties: + title: + type: string + rows: + type: array + items: + type: object + properties: + title: + type: string + description: + type: string + rowId: + type: string + parameters: + - name: instanceName + in: path + required: true + schema: + type: string + description: The name of the instance to which the poll should be sent. + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/whatsappNumbers/{instanceName}: post: tags: From 0e50da324b9b0c8f3effa6206a2007fff116599b Mon Sep 17 00:00:00 2001 From: Amilton Morais Date: Wed, 24 Jan 2024 13:38:29 -0300 Subject: [PATCH 15/32] Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot A new variable has been created, and a function has been implemented to manage the deletion of messages on WhatsApp. Now, when deleting a message for everyone on WhatsApp, the same action will be automatically performed on Chatwoot, ensuring consistency across platforms --- src/config/env.config.ts | 5 +++ src/dev-env.yml | 4 ++ src/whatsapp/models/message.model.ts | 4 +- src/whatsapp/services/chatwoot.service.ts | 54 ++++++++++++----------- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/config/env.config.ts b/src/config/env.config.ts index fde4a073..25dd72a3 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -147,6 +147,7 @@ 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; KEEP_OPEN: boolean }; +export type ChatWoot = { MESSAGE_DELETE: boolean }; export type CacheConf = { REDIS: CacheConfRedis; LOCAL: CacheConfLocal }; export type Production = boolean; @@ -167,6 +168,7 @@ export interface Env { CONFIG_SESSION_PHONE: ConfigSessionPhone; QRCODE: QrCode; TYPEBOT: Typebot; + CHATWOOT: ChatWoot; CACHE: CacheConf; AUTHENTICATION: Auth; PRODUCTION?: Production; @@ -330,6 +332,9 @@ export class ConfigService { API_VERSION: process.env?.TYPEBOT_API_VERSION || 'old', KEEP_OPEN: process.env.TYPEBOT_KEEP_OPEN === 'true', }, + CHATWOOT: { + MESSAGE_DELETE: process.env.CHATWOOT_MESSAGE_DELETE === 'false', + }, CACHE: { REDIS: { ENABLED: process.env?.CACHE_REDIS_ENABLED === 'true', diff --git a/src/dev-env.yml b/src/dev-env.yml index 42438aff..c4a9d909 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -153,6 +153,10 @@ TYPEBOT: API_VERSION: 'old' # old | latest KEEP_OPEN: false +# If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot. +CHATWOOT: + MESSAGE_DELETE: true # false | true + # Cache to optimize application performance CACHE: REDIS: diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts index 9c7ac9dc..4deb0f0f 100644 --- a/src/whatsapp/models/message.model.ts +++ b/src/whatsapp/models/message.model.ts @@ -26,7 +26,7 @@ export class MessageRaw { messageType?: string; messageTimestamp?: number | Long.Long; owner: string; - source?: 'android' | 'web' | 'ios'; + source?: 'android' | 'web' | 'ios' | 'ios' | 'unknown' | 'desktop'; source_id?: string; source_reply_id?: string; chatwoot?: ChatwootMessage; @@ -45,7 +45,7 @@ const messageSchema = new Schema({ participant: { type: String }, messageType: { type: String }, message: { type: Object }, - source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] }, + source: { type: String, minlength: 3, enum: ['android', 'web', 'ios','unknown','desktop' ] }, messageTimestamp: { type: Number, required: true }, owner: { type: String, required: true, minlength: 1 }, chatwoot: { diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index ef70cf63..3c4c011c 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -7,7 +7,7 @@ import Jimp from 'jimp'; import mimeTypes from 'mime-types'; import path from 'path'; -import { ConfigService, HttpServer } from '../../config/env.config'; +import { ConfigService, HttpServer, ChatWoot} from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { ICache } from '../abstract/abstract.cache'; import { ChatwootDto } from '../dto/chatwoot.dto'; @@ -1894,33 +1894,37 @@ export class ChatwootService { } if (event === Events.MESSAGES_DELETE) { - this.logger.verbose('deleting message from instance: ' + instance.instanceName); + + const chatwootDelete = this.configService.get('CHATWOOT').MESSAGE_DELETE + if (chatwootDelete === true) { + this.logger.verbose('deleting message from instance: ' + instance.instanceName); - if (!body?.key?.id) { - this.logger.warn('message id not found'); - return; - } + if (!body?.key?.id) { + this.logger.warn('message id not found'); + return; + } - const message = await this.getMessageByKeyId(instance, body.key.id); - if (message?.chatwoot?.messageId && message?.chatwoot?.conversationId) { - this.logger.verbose('deleting message in repository. Message id: ' + body.key.id); - this.repository.message.delete({ - where: { - key: { - id: body.key.id, - }, - owner: instance.instanceName, - }, - }); + const message = await this.getMessageByKeyId(instance, body.key.id); + if (message?.chatwoot?.messageId && message?.chatwoot?.conversationId) { + this.logger.verbose('deleting message in repository. Message id: ' + body.key.id); + this.repository.message.delete({ + where: { + key: { + id: body.key.id, + }, + owner: instance.instanceName, + }, + }); - this.logger.verbose('deleting message in chatwoot. Message id: ' + body.key.id); - return await client.messages.delete({ - accountId: this.provider.account_id, - conversationId: message.chatwoot.conversationId, - messageId: message.chatwoot.messageId, - }); - } - } + this.logger.verbose('deleting message in chatwoot. Message id: ' + body.key.id); + return await client.messages.delete({ + accountId: this.provider.account_id, + conversationId: message.chatwoot.conversationId, + messageId: message.chatwoot.messageId, + }); + } + } + } if (event === 'messages.read') { this.logger.verbose('read message from instance: ' + instance.instanceName); From dfa72fd6af03160af609378b29df78e86e24b5a4 Mon Sep 17 00:00:00 2001 From: Amilton Morais Date: Wed, 24 Jan 2024 13:43:19 -0300 Subject: [PATCH 16/32] Update start.sh --- start.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 start.sh diff --git a/start.sh b/start.sh old mode 100755 new mode 100644 From 0edc8a92842441e012a1e58baf08d4742ecbdd66 Mon Sep 17 00:00:00 2001 From: Douglas Rauber at Nitro Date: Thu, 25 Jan 2024 16:19:08 -0300 Subject: [PATCH 17/32] Add translate capabilities to QRMessages in CW --- .gitignore | 3 +- package.json | 1 + src/config/env.config.ts | 3 ++ src/dev-env.yml | 3 ++ src/utils/i18n.ts | 36 +++++++++++++++++++++++ src/utils/translations/en.json | 5 ++++ src/utils/translations/pt-BR.json | 5 ++++ src/whatsapp/services/chatwoot.service.ts | 8 +++-- src/whatsapp/services/typebot.service.ts | 10 +++++-- 9 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/utils/i18n.ts create mode 100644 src/utils/translations/en.json create mode 100644 src/utils/translations/pt-BR.json diff --git a/.gitignore b/.gitignore index c55eb628..55cd9d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ docker-compose.yaml /temp/* .DS_Store -*.DS_Store \ No newline at end of file +*.DS_Store +.tool-versions diff --git a/package.json b/package.json index 20ff59d9..3f2cf39f 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "express-async-errors": "^3.1.1", "hbs": "^4.2.0", "https-proxy-agent": "^7.0.2", + "i18next": "^23.7.19", "jimp": "^0.16.13", "join": "^3.0.0", "js-yaml": "^4.1.0", diff --git a/src/config/env.config.ts b/src/config/env.config.ts index fde4a073..cfe525d7 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -127,6 +127,8 @@ export type Auth = { export type DelInstance = number | boolean; +export type Language = string | 'en'; + export type GlobalWebhook = { URL: string; ENABLED: boolean; @@ -163,6 +165,7 @@ export interface Env { WEBSOCKET: Websocket; LOG: Log; DEL_INSTANCE: DelInstance; + LANGUAGE: Language; WEBHOOK: Webhook; CONFIG_SESSION_PHONE: ConfigSessionPhone; QRCODE: QrCode; diff --git a/src/dev-env.yml b/src/dev-env.yml index 42438aff..6c9aa372 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -179,3 +179,6 @@ AUTHENTICATION: JWT: EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires SECRET: L=0YWt]b2w[WF>#>:&E` + + +LANGUAGE: "pt-BR" # pt-BR, en \ No newline at end of file diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 00000000..65feab48 --- /dev/null +++ b/src/utils/i18n.ts @@ -0,0 +1,36 @@ +import fs from 'fs'; +import i18next from 'i18next'; +import path from 'path'; + +import { ConfigService, Language } from '../config/env.config'; + +// export class i18n { +// constructor(private readonly configService: ConfigService) { +const languages = ['en', 'pt-BR']; +const translationsPath = path.join(__dirname, 'translations'); +const configService: ConfigService = new ConfigService(); + +const resources: any = {}; + +languages.forEach((language) => { + const languagePath = path.join(translationsPath, `${language}.json`); + if (fs.existsSync(languagePath)) { + resources[language] = { + translation: require(languagePath), + }; + } +}); + +i18next.init({ + resources, + fallbackLng: 'en', + lng: configService.get('LANGUAGE'), + debug: false, + + interpolation: { + escapeValue: false, + }, +}); +// } +// } +export default i18next; diff --git a/src/utils/translations/en.json b/src/utils/translations/en.json new file mode 100644 index 00000000..f92c4e08 --- /dev/null +++ b/src/utils/translations/en.json @@ -0,0 +1,5 @@ +{ + "qrgeneratedsuccesfully": "QRCode successfully generated!", + "scanqr": "Scan this QR code within the next 40 seconds.", + "qrlimitreached": "QRCode generation limit reached, to generate a new QRCode, send the 'init' message again." +} \ No newline at end of file diff --git a/src/utils/translations/pt-BR.json b/src/utils/translations/pt-BR.json new file mode 100644 index 00000000..d48ff148 --- /dev/null +++ b/src/utils/translations/pt-BR.json @@ -0,0 +1,5 @@ +{ + "qrgeneratedsuccesfully": "QRCode gerado com sucesso!", + "scanqr": "Escanei o QRCode com o Whatsapp nos próximos 40 segundos.", + "qrlimitreached": "Limite de geração de QRCode atingido! Para gerar um novo QRCode, envie o texto 'init' nesta conversa." +} \ No newline at end of file diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index ef70cf63..b9ab9c3f 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -9,6 +9,7 @@ import path from 'path'; import { ConfigService, HttpServer } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; +import i18next from '../../utils/i18n'; import { ICache } from '../abstract/abstract.cache'; import { ChatwootDto } from '../dto/chatwoot.dto'; import { InstanceDto } from '../dto/instance.dto'; @@ -1994,7 +1995,8 @@ export class ChatwootService { this.logger.verbose('event qrcode.updated'); if (body.statusCode === 500) { this.logger.verbose('qrcode error'); - const erroQRcode = `🚨 QRCode generation limit reached, to generate a new QRCode, send the 'init' message again.`; + + const erroQRcode = `🚨 ${i18next.t('qrlimitreached')}`; this.logger.verbose('send message to chatwoot'); return await this.createBotMessage(instance, erroQRcode, 'incoming'); @@ -2010,9 +2012,9 @@ export class ChatwootService { writeFileSync(fileName, fileData, 'utf8'); this.logger.verbose('send qrcode to chatwoot'); - await this.createBotQr(instance, 'QRCode successfully generated!', 'incoming', fileName); + await this.createBotQr(instance, i18next.t('qrgeneratedsuccesfully'), 'incoming', fileName); - let msgQrCode = `⚡️ QRCode successfully generated!\n\nScan this QR code within the next 40 seconds.`; + let msgQrCode = `⚡️${i18next.t('qrgeneratedsuccesfully')}\n\n${i18next.t('scanqr')}`; if (body?.qrcode?.pairingCode) { msgQrCode = diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 36645f63..85eb3558 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -274,7 +274,7 @@ export class TypebotService { const types = { conversation: msg.conversation, extendedTextMessage: msg.extendedTextMessage?.text, - responseRowId: msg.listResponseMessage.singleSelectReply?.selectedRowId, + responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId, }; this.logger.verbose('type message: ' + types); @@ -413,7 +413,13 @@ export class TypebotService { text += element.text; } - if (element.children && (element.type === 'p' || element.type === 'a' || element.type === 'inline-variable' || element.type === 'variable')) { + if ( + element.children && + (element.type === 'p' || + element.type === 'a' || + element.type === 'inline-variable' || + element.type === 'variable') + ) { for (const child of element.children) { text += applyFormatting(child); } From 3755d3870e7aea209137f74cf76f48e8dce2b4f9 Mon Sep 17 00:00:00 2001 From: edisoncm-ti Date: Fri, 26 Jan 2024 19:10:20 -0300 Subject: [PATCH 18/32] fix: collecting responses with text or numbers in Typebot --- 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 36645f63..6d6237f9 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -274,7 +274,7 @@ export class TypebotService { const types = { conversation: msg.conversation, extendedTextMessage: msg.extendedTextMessage?.text, - responseRowId: msg.listResponseMessage.singleSelectReply?.selectedRowId, + responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId, }; this.logger.verbose('type message: ' + types); From 59f5208c5c05526e08a210e7776b58653e6d8a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20Cl=C3=AAuder=20Lima=20de=20Jesus?= Date: Sat, 27 Jan 2024 22:29:04 -0300 Subject: [PATCH 19/32] fix: search number without 9 in number from brazil --- src/whatsapp/services/chatwoot.service.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 26b0cce9..c78d2bb1 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -380,8 +380,9 @@ export class ChatwootService { } let query: any; + const isGroup = phoneNumber.includes('@g.us'); - if (!phoneNumber.includes('@g.us')) { + if (!isGroup) { this.logger.verbose('format phone number'); query = `+${phoneNumber}`; } else { @@ -390,12 +391,21 @@ export class ChatwootService { } this.logger.verbose('find contact in chatwoot'); - const contact: any = await client.contacts.search({ + let contact: any = await client.contacts.search({ accountId: this.provider.account_id, q: query, }); - if (!contact) { + if (!contact && !isGroup && query.startsWith('+55') && query.length > 13) { + this.logger.verbose('trying without the 9th digit'); + query = query.slice(0, 3) + query.slice(4); + contact = await client.contacts.search({ + accountId: this.provider.account_id, + q: query, + }); + } + + if(!contact) { this.logger.warn('contact not found'); return null; } From 838905f3dde77b8449cfe0d27660f60ba9d9562b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20Cl=C3=AAuder=20Lima=20de=20Jesus?= Date: Sat, 27 Jan 2024 22:37:58 -0300 Subject: [PATCH 20/32] fix: position splice --- src/whatsapp/services/chatwoot.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index c78d2bb1..e4754cd8 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -398,7 +398,7 @@ export class ChatwootService { if (!contact && !isGroup && query.startsWith('+55') && query.length > 13) { this.logger.verbose('trying without the 9th digit'); - query = query.slice(0, 3) + query.slice(4); + query = query.slice(0, 5) + query.slice(6); contact = await client.contacts.search({ accountId: this.provider.account_id, q: query, From 535d5ee47f50d1ea743ef5d409fbf58dc05860f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yves=20Cl=C3=AAuder=20Lima=20de=20Jesus?= Date: Sun, 28 Jan 2024 10:36:42 -0300 Subject: [PATCH 21/32] fix: change method search for filter --- src/whatsapp/services/chatwoot.service.ts | 70 ++++++++++++++++++++--- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index e4754cd8..90e2ad6c 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -391,18 +391,18 @@ export class ChatwootService { } this.logger.verbose('find contact in chatwoot'); - let contact: any = await client.contacts.search({ - accountId: this.provider.account_id, - q: query, - }); + let contact: any; - if (!contact && !isGroup && query.startsWith('+55') && query.length > 13) { - this.logger.verbose('trying without the 9th digit'); - query = query.slice(0, 5) + query.slice(6); + if(isGroup) { contact = await client.contacts.search({ accountId: this.provider.account_id, q: query, }); + } else { + contact = await client.contacts.filter({ + accountId: this.provider.account_id, + payload: this.getFilterPayload(query), + }); } if(!contact) { @@ -410,15 +410,67 @@ export class ChatwootService { return null; } - if (!phoneNumber.includes('@g.us')) { + if (!isGroup) { this.logger.verbose('return contact'); - return contact.payload.find((contact) => contact.phone_number === query); + return this.findContactInContactList(contact.payload, query); } else { this.logger.verbose('return group'); return contact.payload.find((contact) => contact.identifier === query); } } + private findContactInContactList(contacts: any[], query: string) { + const phoneNumbers = this.getNumbers(query); + const searchableFields = this.getSearchableFields(); + + for (const contact of contacts) { + for (const field of searchableFields) { + if (contact[field] && phoneNumbers.includes(contact[field])) { + return contact; + } + } + } + + return null; + } + + private getNumbers(query: string) { + const numbers = []; + numbers.push(query); + + if (query.startsWith('+55') && query.length === 14) { + const withoutNine = query.slice(0, 5) + query.slice(6); + numbers.push(withoutNine); + } else if (query.startsWith('+55') && query.length === 13) { + const withNine = query.slice(0, 5) + '9' + query.slice(5); + numbers.push(withNine); + } + + return numbers; + } + + private getSearchableFields() { + return ['identifier', 'phone_number', 'name', 'email']; + } + + private getFilterPayload(query: string) { + const payload = []; + const values = this.getNumbers(query) + + const fields = this.getSearchableFields(); + fields.forEach((key, index) => { + const queryOperator = fields.length - 1 === index ? null : 'OR'; + payload.push({ + "attribute_key": key, + "filter_operator": "contains", + "values": values, + "query_operator": queryOperator + }); + }); + + return payload; + } + public async createConversation(instance: InstanceDto, body: any) { this.logger.verbose('create conversation to instance: ' + instance.instanceName); try { From cdf822291f6c50d4165f0e53ac53ad5e6ff6bf33 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 29 Jan 2024 11:35:30 -0300 Subject: [PATCH 22/32] lint --- src/config/env.config.ts | 2 +- src/whatsapp/models/message.model.ts | 2 +- src/whatsapp/services/chatwoot.service.ts | 57 +++++++++++------------ 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/config/env.config.ts b/src/config/env.config.ts index fcc00c7c..fec66eff 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -149,7 +149,7 @@ 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; KEEP_OPEN: boolean }; -export type ChatWoot = { MESSAGE_DELETE: boolean }; +export type ChatWoot = { MESSAGE_DELETE: boolean }; export type CacheConf = { REDIS: CacheConfRedis; LOCAL: CacheConfLocal }; export type Production = boolean; diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts index 8388799c..326f982b 100644 --- a/src/whatsapp/models/message.model.ts +++ b/src/whatsapp/models/message.model.ts @@ -45,7 +45,7 @@ const messageSchema = new Schema({ participant: { type: String }, messageType: { type: String }, message: { type: Object }, - source: { type: String, minlength: 3, enum: ['android', 'web', 'ios','unknown','desktop' ] }, + source: { type: String, minlength: 3, enum: ['android', 'web', 'ios', 'unknown', 'desktop'] }, messageTimestamp: { type: Number, required: true }, owner: { type: String, required: true, minlength: 1 }, chatwoot: { diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index d0c0ac85..afa6e874 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -7,7 +7,7 @@ import Jimp from 'jimp'; import mimeTypes from 'mime-types'; import path from 'path'; -import { ConfigService, HttpServer, ChatWoot} from '../../config/env.config'; +import { ChatWoot, ConfigService, HttpServer } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import i18next from '../../utils/i18n'; import { ICache } from '../abstract/abstract.cache'; @@ -1895,37 +1895,36 @@ export class ChatwootService { } if (event === Events.MESSAGES_DELETE) { - - const chatwootDelete = this.configService.get('CHATWOOT').MESSAGE_DELETE - if (chatwootDelete === true) { - this.logger.verbose('deleting message from instance: ' + instance.instanceName); + const chatwootDelete = this.configService.get('CHATWOOT').MESSAGE_DELETE; + if (chatwootDelete === true) { + this.logger.verbose('deleting message from instance: ' + instance.instanceName); - if (!body?.key?.id) { - this.logger.warn('message id not found'); - return; - } + if (!body?.key?.id) { + this.logger.warn('message id not found'); + return; + } - const message = await this.getMessageByKeyId(instance, body.key.id); - if (message?.chatwoot?.messageId && message?.chatwoot?.conversationId) { - this.logger.verbose('deleting message in repository. Message id: ' + body.key.id); - this.repository.message.delete({ - where: { - key: { - id: body.key.id, - }, - owner: instance.instanceName, - }, - }); + const message = await this.getMessageByKeyId(instance, body.key.id); + if (message?.chatwoot?.messageId && message?.chatwoot?.conversationId) { + this.logger.verbose('deleting message in repository. Message id: ' + body.key.id); + this.repository.message.delete({ + where: { + key: { + id: body.key.id, + }, + owner: instance.instanceName, + }, + }); - this.logger.verbose('deleting message in chatwoot. Message id: ' + body.key.id); - return await client.messages.delete({ - accountId: this.provider.account_id, - conversationId: message.chatwoot.conversationId, - messageId: message.chatwoot.messageId, - }); - } - } - } + this.logger.verbose('deleting message in chatwoot. Message id: ' + body.key.id); + return await client.messages.delete({ + accountId: this.provider.account_id, + conversationId: message.chatwoot.conversationId, + messageId: message.chatwoot.messageId, + }); + } + } + } if (event === 'messages.read') { this.logger.verbose('read message from instance: ' + instance.instanceName); From 058acc5042be96e42c2329b397c385c1833c6a25 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Mon, 29 Jan 2024 11:43:42 -0300 Subject: [PATCH 23/32] changelog --- CHANGELOG.md | 15 +++++++++++++++ src/config/env.config.ts | 1 + 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9033f10f..a9284df3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ### Feature * Added update message endpoint +* Add translate capabilities to QRMessages in CW +* Join in Group by Invite Code +* Read messages from whatsapp in chatwoot +* Add support to use use redis in cacheservice ### Fixed @@ -16,6 +20,17 @@ * When receiving a file from whatsapp, use the original filename in chatwoot if possible * Remove message ids cache in chatwoot to use chatwoot's api itself * Adjusts the quoted message, now has contextInfo in the message Raw +* Collecting responses with text or numbers in Typebot +* Added sendList endpoint to swagger documentation +* Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot. +* Improvement on numbers validation +* Fix polls in message sending +* Sending status message +* Message 'connection successfully' spamming +* Invalidate the conversation cache if reopen_conversation is false and the conversation was resolved +* Fix looping when deleting a message in chatwoot +* When receiving a file from whatsapp, use the original filename in chatwoot if possible +* Correction in the sendList Function # 1.6.1 (2023-12-22 11:43) diff --git a/src/config/env.config.ts b/src/config/env.config.ts index fec66eff..fd6187ab 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -286,6 +286,7 @@ export class ConfigService { DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE) ? process.env.DEL_INSTANCE === 'true' : Number.parseInt(process.env.DEL_INSTANCE) || false, + LANGUAGE: process.env?.LANGUAGE || 'en', WEBHOOK: { GLOBAL: { URL: process.env?.WEBHOOK_GLOBAL_URL || '', From 66b82ac10a89c1a740a05332e2017d99c329b9ff Mon Sep 17 00:00:00 2001 From: Leandro Rocha Date: Wed, 31 Jan 2024 10:50:04 -0300 Subject: [PATCH 24/32] If contact stored with name, name added in return --- src/whatsapp/services/whatsapp.service.ts | 51 +++++++++++++++-------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 15cf7176..baaba15d 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3130,7 +3130,7 @@ export class WAStartupService { const jids: { groups: { number: string; jid: string }[]; broadcast: { number: string; jid: string }[]; - users: { number: string; jid: string }[]; + users: { number: string; jid: string; contact?: string; name?: string }[]; } = { groups: [], broadcast: [], @@ -3145,7 +3145,7 @@ export class WAStartupService { } else if (jid === 'status@broadcast') { jids.broadcast.push({ number, jid }); } else { - jids.users.push({ number, jid }); + jids.users.push({ number, jid, contact: jid }); } }); @@ -3172,22 +3172,39 @@ export class WAStartupService { const verify = await this.client.onWhatsApp( ...jids.users.map(({ jid }) => (!jid.startsWith('+') ? `+${jid}` : jid)), ); - const users: OnWhatsAppDto[] = jids.users.map((user) => { - const MAX_SIMILARITY_THRESHOLD = 0.01; - const isBrWithDigit = user.jid.startsWith('55') && user.jid.slice(4, 5) === '9' && user.jid.length === 28; - const jid = isBrWithDigit ? user.jid.slice(0, 4) + user.jid.slice(5) : user.jid; + const users: OnWhatsAppDto[] = await Promise.all( + jids.users.map(async (user) => { + const MAX_SIMILARITY_THRESHOLD = 0.01; + const isBrWithDigit = user.jid.startsWith('55') && user.jid.slice(4, 5) === '9' && user.jid.length === 28; + const jid = isBrWithDigit ? user.jid.slice(0, 4) + user.jid.slice(5) : user.jid; + + const query: ContactQuery = { + where: { + owner: this.instance.name, + id: user.contact, + }, + }; + const contacts: ContactRaw[] = await this.repository.contact.find(query); + let firstContactFound; + if (contacts.length > 0) { + firstContactFound = contacts[0].pushName; + console.log(contacts[0]); + } + + const numberVerified = verify.find((v) => { + const mainJidSimilarity = levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length); + const jidSimilarity = levenshtein.get(jid, v.jid) / Math.max(jid.length, v.jid.length); + return mainJidSimilarity <= MAX_SIMILARITY_THRESHOLD || jidSimilarity <= MAX_SIMILARITY_THRESHOLD; + }); + return { + exists: !!numberVerified?.exists, + jid: numberVerified?.jid || user.jid, + name: firstContactFound, + number: user.number, + }; + }), + ); - const numberVerified = verify.find((v) => { - const mainJidSimilarity = levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length); - const jidSimilarity = levenshtein.get(jid, v.jid) / Math.max(jid.length, v.jid.length); - return mainJidSimilarity <= MAX_SIMILARITY_THRESHOLD || jidSimilarity <= MAX_SIMILARITY_THRESHOLD; - }); - return { - exists: !!numberVerified?.exists, - jid: numberVerified?.jid || user.jid, - number: user.number, - }; - }); onWhatsapp.push(...users); return onWhatsapp; From 3da73b821d2f71bdfad932000943d19fbed94f06 Mon Sep 17 00:00:00 2001 From: Leandro Santos Rocha Date: Wed, 31 Jan 2024 10:55:37 -0300 Subject: [PATCH 25/32] Removed console.log --- src/whatsapp/services/whatsapp.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index baaba15d..77d5570d 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3188,7 +3188,6 @@ export class WAStartupService { let firstContactFound; if (contacts.length > 0) { firstContactFound = contacts[0].pushName; - console.log(contacts[0]); } const numberVerified = verify.find((v) => { From dfb1ee0c568aa12511c8ec818fdb7f82ed4007f3 Mon Sep 17 00:00:00 2001 From: Leandro Santos Rocha Date: Wed, 31 Jan 2024 11:37:19 -0300 Subject: [PATCH 26/32] Adjust to use the same jid --- src/whatsapp/services/whatsapp.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 77d5570d..4858e433 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3130,7 +3130,7 @@ export class WAStartupService { const jids: { groups: { number: string; jid: string }[]; broadcast: { number: string; jid: string }[]; - users: { number: string; jid: string; contact?: string; name?: string }[]; + users: { number: string; jid: string; name?: string }[]; } = { groups: [], broadcast: [], @@ -3145,7 +3145,7 @@ export class WAStartupService { } else if (jid === 'status@broadcast') { jids.broadcast.push({ number, jid }); } else { - jids.users.push({ number, jid, contact: jid }); + jids.users.push({ number, jid }); } }); @@ -3181,7 +3181,7 @@ export class WAStartupService { const query: ContactQuery = { where: { owner: this.instance.name, - id: user.contact, + id: user.jid.startsWith('+') ? user.jid.substring(1) : user.jid;, }, }; const contacts: ContactRaw[] = await this.repository.contact.find(query); From 7439d2401de1a75cfbf5ea94376974bbe9e853cd Mon Sep 17 00:00:00 2001 From: Leandro Santos Rocha Date: Wed, 31 Jan 2024 11:39:04 -0300 Subject: [PATCH 27/32] Fix error ; --- 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 4858e433..71ca431e 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -3181,7 +3181,7 @@ export class WAStartupService { const query: ContactQuery = { where: { owner: this.instance.name, - id: user.jid.startsWith('+') ? user.jid.substring(1) : user.jid;, + id: user.jid.startsWith('+') ? user.jid.substring(1) : user.jid, }, }; const contacts: ContactRaw[] = await this.repository.contact.find(query); From f41f3aaba8d85281c0a3c3cef0e17e894a5f55fc Mon Sep 17 00:00:00 2001 From: jaison-x Date: Thu, 1 Feb 2024 17:31:50 -0300 Subject: [PATCH 28/32] Update chatwoot.service.ts hotfix: bug on chatwoot sdk --- src/whatsapp/services/chatwoot.service.ts | 28 +++++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 84028cf6..0bc74ecd 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -382,19 +382,27 @@ export class ChatwootService { this.logger.verbose('find contact in chatwoot'); let contact: any; - if(isGroup) { + if (isGroup) { contact = await client.contacts.search({ accountId: this.provider.account_id, q: query, }); } else { - contact = await client.contacts.filter({ - accountId: this.provider.account_id, - payload: this.getFilterPayload(query), + // contact = await client.contacts.filter({ + // accountId: this.provider.account_id, + // payload: this.getFilterPayload(query), + // }); + // hotfix for: https://github.com/EvolutionAPI/evolution-api/pull/382. waiting fix: https://github.com/figurolatam/chatwoot-sdk/pull/7 + contact = await chatwootRequest(this.getClientCwConfig(), { + method: 'POST', + url: `/api/v1/accounts/${this.provider.account_id}/contacts/filter`, + body: { + payload: this.getFilterPayload(query), + }, }); } - if(!contact) { + if (!contact) { this.logger.warn('contact not found'); return null; } @@ -444,16 +452,16 @@ export class ChatwootService { private getFilterPayload(query: string) { const payload = []; - const values = this.getNumbers(query) + const values = this.getNumbers(query); const fields = this.getSearchableFields(); fields.forEach((key, index) => { const queryOperator = fields.length - 1 === index ? null : 'OR'; payload.push({ - "attribute_key": key, - "filter_operator": "contains", - "values": values, - "query_operator": queryOperator + attribute_key: key, + filter_operator: 'contains', + values: values, + query_operator: queryOperator, }); }); From b995cdfc32c601623adff8555e386856291322bd Mon Sep 17 00:00:00 2001 From: Deivison Lincoln Date: Thu, 1 Feb 2024 18:53:36 -0300 Subject: [PATCH 29/32] Fix for contats find payload --- src/whatsapp/services/chatwoot.service.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 0bc74ecd..729dcb31 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -455,19 +455,22 @@ export class ChatwootService { const values = this.getNumbers(query); const fields = this.getSearchableFields(); - fields.forEach((key, index) => { - const queryOperator = fields.length - 1 === index ? null : 'OR'; - payload.push({ - attribute_key: key, - filter_operator: 'contains', - values: values, - query_operator: queryOperator, + + fields.forEach((field, index1) => { + values.forEach((number, index2) => { + const queryOperator = fields.length - 1 === index1 && values.length - 1 === index2 ? null : 'OR'; + payload.push({ + attribute_key: field, + filter_operator: 'contains', + values: [number], + query_operator: queryOperator, + }); }); }); + this.logger.verbose('Payload: ' + JSON.stringify(payload)); return payload; } - public async createConversation(instance: InstanceDto, body: any) { this.logger.verbose('create conversation to instance: ' + instance.instanceName); try { From 54603002a68976dfa126c48ff1adc80bef7df090 Mon Sep 17 00:00:00 2001 From: Deivison Lincoln Date: Thu, 1 Feb 2024 22:13:19 -0300 Subject: [PATCH 30/32] Fix for brazil 9 digit --- src/whatsapp/services/chatwoot.service.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 729dcb31..c725e246 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -447,29 +447,28 @@ export class ChatwootService { } private getSearchableFields() { - return ['identifier', 'phone_number', 'name', 'email']; + return ['phone_number']; } private getFilterPayload(query: string) { - const payload = []; - const values = this.getNumbers(query); + const filterPayload = []; - const fields = this.getSearchableFields(); + const numbers = this.getNumbers(query); + const fieldsToSearch = this.getSearchableFields(); - fields.forEach((field, index1) => { - values.forEach((number, index2) => { - const queryOperator = fields.length - 1 === index1 && values.length - 1 === index2 ? null : 'OR'; - payload.push({ + fieldsToSearch.forEach((field, index1) => { + numbers.forEach((number, index2) => { + const queryOperator = fieldsToSearch.length - 1 === index1 && numbers.length - 1 === index2 ? null : 'OR'; + filterPayload.push({ attribute_key: field, - filter_operator: 'contains', - values: [number], + filter_operator: 'equal_to', + values: [number.replace('+', '')], query_operator: queryOperator, }); }); }); - this.logger.verbose('Payload: ' + JSON.stringify(payload)); - return payload; + return filterPayload; } public async createConversation(instance: InstanceDto, body: any) { this.logger.verbose('create conversation to instance: ' + instance.instanceName); From b3adde3a7a1494672a2591aca494878409abb7ae Mon Sep 17 00:00:00 2001 From: Deivison Lincoln Date: Thu, 1 Feb 2024 23:42:52 -0300 Subject: [PATCH 31/32] Feat Reject Message If is not a valid wpp number --- src/utils/translations/en.json | 3 ++- src/utils/translations/pt-BR.json | 3 ++- src/whatsapp/services/chatwoot.service.ts | 21 +++++++++++++++++++++ src/whatsapp/services/whatsapp.service.ts | 8 ++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/utils/translations/en.json b/src/utils/translations/en.json index f92c4e08..d8566c72 100644 --- a/src/utils/translations/en.json +++ b/src/utils/translations/en.json @@ -1,5 +1,6 @@ { "qrgeneratedsuccesfully": "QRCode successfully generated!", "scanqr": "Scan this QR code within the next 40 seconds.", - "qrlimitreached": "QRCode generation limit reached, to generate a new QRCode, send the 'init' message again." + "qrlimitreached": "QRCode generation limit reached, to generate a new QRCode, send the 'init' message again.", + "numbernotinwhatsapp": "The message was not sent as the contact is not a valid Whatsapp number." } \ No newline at end of file diff --git a/src/utils/translations/pt-BR.json b/src/utils/translations/pt-BR.json index d48ff148..a9668848 100644 --- a/src/utils/translations/pt-BR.json +++ b/src/utils/translations/pt-BR.json @@ -1,5 +1,6 @@ { "qrgeneratedsuccesfully": "QRCode gerado com sucesso!", "scanqr": "Escanei o QRCode com o Whatsapp nos próximos 40 segundos.", - "qrlimitreached": "Limite de geração de QRCode atingido! Para gerar um novo QRCode, envie o texto 'init' nesta conversa." + "qrlimitreached": "Limite de geração de QRCode atingido! Para gerar um novo QRCode, envie o texto 'init' nesta conversa.", + "numbernotinwhatsapp": "A mensagem não foi enviada, pois o contato não é um número válido do Whatsapp." } \ No newline at end of file diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 0bc74ecd..1ceeb2dd 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -1684,6 +1684,27 @@ export class ChatwootService { return null; } + if (event === 'contact.is_not_in_wpp') { + const getConversation = await this.createConversation(instance, body); + + if (!getConversation) { + this.logger.warn('conversation not found'); + return; + } + + client.messages.create({ + accountId: this.provider.account_id, + conversationId: getConversation, + data: { + content: `🚨 ${i18next.t('numbernotinwhatsapp')}`, + message_type: 'incoming', + private: true, + }, + }); + + return; + } + if (event === 'messages.upsert' || event === 'send.message') { this.logger.verbose('event messages.upsert'); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 71ca431e..5c562b3c 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -2357,7 +2357,15 @@ export class WAStartupService { 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')) { + if (this.localChatwoot.enabled) { + const body = { + key: { remoteJid: isWA.jid }, + }; + + this.chatwootService.eventWhatsapp('contact.is_not_in_wpp', { instanceName: this.instance.name }, body); + } throw new BadRequestException(isWA); } From c130846fe83ad2e55bc4d6230927e717e026565a Mon Sep 17 00:00:00 2001 From: Deivison Lincoln Date: Fri, 2 Feb 2024 08:57:44 -0300 Subject: [PATCH 32/32] Change message_type Change message_type to 'outgoing' --- src/whatsapp/services/chatwoot.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 1ceeb2dd..bc7d4471 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -1697,7 +1697,7 @@ export class ChatwootService { conversationId: getConversation, data: { content: `🚨 ${i18next.t('numbernotinwhatsapp')}`, - message_type: 'incoming', + message_type: 'outgoing', private: true, }, });