From 440ff2f3eac85e5c8dbd2fc50f7d9ca0882eca84 Mon Sep 17 00:00:00 2001 From: Judson Junior Date: Sat, 20 Jan 2024 11:44:10 -0300 Subject: [PATCH 1/7] 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 e19e37eef48aa78bb14f1369f9a1b3007771dacb Mon Sep 17 00:00:00 2001 From: Leandro Rocha Date: Sun, 21 Jan 2024 02:13:24 -0300 Subject: [PATCH 2/7] 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 3/7] 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 4/7] 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 5/7] 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 6/7] 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 7/7] 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,