diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 8fab7f2f..cf731064 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -23,6 +23,7 @@ import { WebsocketService } from '../services/websocket.service'; import { BaileysStartupService } from '../services/whatsapp.baileys.service'; import { BusinessStartupService } from '../services/whatsapp.business.service'; import { Events, Integration, wa } from '../types/wa.types'; +import { ProxyController } from './proxy.controller'; export class InstanceController { constructor( @@ -39,6 +40,7 @@ export class InstanceController { private readonly sqsService: SqsService, private readonly typebotService: TypebotService, private readonly integrationService: IntegrationService, + private readonly proxyService: ProxyController, private readonly cache: RedisCache, private readonly chatwootCache: CacheService, ) {} @@ -85,6 +87,7 @@ export class InstanceController { typebot_delay_message, typebot_unknown_message, typebot_listening_from_me, + proxy, }: InstanceDto) { try { this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); @@ -346,6 +349,18 @@ export class InstanceController { } } + if (proxy) { + const testProxy = await this.proxyService.testProxy(proxy); + if (!testProxy) { + throw new BadRequestException('Invalid proxy'); + } + + await this.proxyService.createProxy(instance, { + enabled: true, + proxy, + }); + } + if (typebot_url) { try { if (!isURL(typebot_url, { require_tld: false })) { diff --git a/src/whatsapp/controllers/proxy.controller.ts b/src/whatsapp/controllers/proxy.controller.ts index d826d8c4..7b34a9d9 100644 --- a/src/whatsapp/controllers/proxy.controller.ts +++ b/src/whatsapp/controllers/proxy.controller.ts @@ -46,7 +46,7 @@ export class ProxyController { return this.proxyService.find(instance); } - private async testProxy(proxy: ProxyDto['proxy']) { + public async testProxy(proxy: ProxyDto['proxy']) { logger.verbose('requested testProxy'); try { const serverIp = await axios.get('https://icanhazip.com/'); diff --git a/src/whatsapp/dto/instance.dto.ts b/src/whatsapp/dto/instance.dto.ts index 4b533de1..b703b9da 100644 --- a/src/whatsapp/dto/instance.dto.ts +++ b/src/whatsapp/dto/instance.dto.ts @@ -1,5 +1,7 @@ import { WAPresence } from '@whiskeysockets/baileys'; +import { ProxyDto } from './proxy.dto'; + export class InstanceDto { instanceName: string; instanceId?: string; @@ -41,7 +43,7 @@ export class InstanceDto { typebot_delay_message?: number; typebot_unknown_message?: string; typebot_listening_from_me?: boolean; - proxy?: string; + proxy?: ProxyDto['proxy']; } export class SetPresenceDto { diff --git a/src/whatsapp/routers/chat.router.ts b/src/whatsapp/routers/chat.router.ts index d8096c79..a0ce3216 100644 --- a/src/whatsapp/routers/chat.router.ts +++ b/src/whatsapp/routers/chat.router.ts @@ -9,7 +9,6 @@ import { messageUpSchema, messageValidateSchema, presenceSchema, - presenceOnlySchema, privacySettingsSchema, profileNameSchema, profilePictureSchema, diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index 7336ff24..8c267759 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -57,7 +57,7 @@ export class TypebotService { if (session) { if (status === 'closed') { - const found_session : Session[] = findData.sessions.splice(findData.sessions.indexOf(session), 1); + const found_session: Session[] = findData.sessions.splice(findData.sessions.indexOf(session), 1); const typebotData = { enabled: findData.enabled, @@ -269,27 +269,30 @@ export class TypebotService { } private getTypeMessage(msg: any) { - this.logger.verbose('get type message'); - const types = { - conversation: msg.conversation, - extendedTextMessage: msg.extendedTextMessage?.text, - audioMessage: msg.audioMessage?.url, - imageMessage: msg.imageMessage?.url, - videoMessage: msg.videoMessage?.url, - documentMessage: msg.documentMessage?.fileName, - contactMessage: msg.contactMessage?.displayName, - locationMessage: msg.locationMessage?.degreesLatitude, - viewOnceMessageV2: msg.viewOnceMessageV2?.message?.imageMessage?.url || msg.viewOnceMessageV2?.message?.videoMessage?.url || msg.viewOnceMessageV2?.message?.audioMessage?.url, - listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId, - responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId, - }; + this.logger.verbose('get type message'); + const types = { + conversation: msg.conversation, + extendedTextMessage: msg.extendedTextMessage?.text, + audioMessage: msg.audioMessage?.url, + imageMessage: msg.imageMessage?.url, + videoMessage: msg.videoMessage?.url, + documentMessage: msg.documentMessage?.fileName, + contactMessage: msg.contactMessage?.displayName, + locationMessage: msg.locationMessage?.degreesLatitude, + viewOnceMessageV2: + msg.viewOnceMessageV2?.message?.imageMessage?.url || + msg.viewOnceMessageV2?.message?.videoMessage?.url || + msg.viewOnceMessageV2?.message?.audioMessage?.url, + listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId, + responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId, + }; - const messageType = Object.keys(types).find(key => types[key] !== undefined) || 'unknown'; - - this.logger.verbose('Type message: ' + JSON.stringify(types)); - return { ...types, messageType }; + const messageType = Object.keys(types).find((key) => types[key] !== undefined) || 'unknown'; + + this.logger.verbose('Type message: ' + JSON.stringify(types)); + return { ...types, messageType }; } - + private getMessageContent(types: any) { this.logger.verbose('get message content'); const typeKey = Object.keys(types).find((key) => types[key] !== undefined); @@ -317,7 +320,7 @@ export class TypebotService { this.logger.verbose('get audio message content'); const types = this.getTypeMessage(msg); - + const audioContent = types.audioMessage; this.logger.verbose('audio message URL: ' + audioContent); @@ -327,85 +330,85 @@ export class TypebotService { private getImageMessageContent(msg: any) { this.logger.verbose('get image message content'); - + const types = this.getTypeMessage(msg); - + const imageContent = types.imageMessage; - + this.logger.verbose('image message URL: ' + imageContent); - + return imageContent; } - + private getVideoMessageContent(msg: any) { this.logger.verbose('get video message content'); - + const types = this.getTypeMessage(msg); - + const videoContent = types.videoMessage; - + this.logger.verbose('video message URL: ' + videoContent); - + return videoContent; } - + private getDocumentMessageContent(msg: any) { this.logger.verbose('get document message content'); - + const types = this.getTypeMessage(msg); - + const documentContent = types.documentMessage; - + this.logger.verbose('document message fileName: ' + documentContent); - + return documentContent; } private getContactMessageContent(msg: any) { this.logger.verbose('get contact message content'); - + const types = this.getTypeMessage(msg); - + const contactContent = types.contactMessage; - + this.logger.verbose('contact message displayName: ' + contactContent); - + return contactContent; } private getLocationMessageContent(msg: any) { this.logger.verbose('get location message content'); - + const types = this.getTypeMessage(msg); - + const locationContent = types.locationMessage; - + this.logger.verbose('location message degreesLatitude: ' + locationContent); - + return locationContent; } private getViewOnceMessageV2Content(msg: any) { this.logger.verbose('get viewOnceMessageV2 content'); - + const types = this.getTypeMessage(msg); - + const viewOnceContent = types.viewOnceMessageV2; - + this.logger.verbose('viewOnceMessageV2 URL: ' + viewOnceContent); - + return viewOnceContent; } private getListResponseMessageContent(msg: any) { this.logger.verbose('get listResponseMessage content'); - + const types = this.getTypeMessage(msg); - + const listResponseContent = types.listResponseMessage || types.responseRowId; - + this.logger.verbose('listResponseMessage selectedRowId: ' + listResponseContent); - + return listResponseContent; } public async createNewSession(instance: InstanceDto, data: any) { @@ -791,7 +794,7 @@ export class TypebotService { sessions: sessions, remoteJid: remoteJid, pushName: msg.pushName, - prefilledVariables: { + prefilledVariables: { messageType: messageType, }, }); diff --git a/src/whatsapp/services/whatsapp.baileys.service.ts b/src/whatsapp/services/whatsapp.baileys.service.ts index 9f0475cd..088548e2 100644 --- a/src/whatsapp/services/whatsapp.baileys.service.ts +++ b/src/whatsapp/services/whatsapp.baileys.service.ts @@ -12,7 +12,6 @@ import makeWASocket, { DisconnectReason, downloadMediaMessage, fetchLatestBaileysVersion, - generateMobileNode, generateWAMessageFromContent, getAggregateVotesInPollMessage, getContentType, @@ -486,7 +485,7 @@ export class BaileysStartupService extends WAStartupService { let options; if (this.localProxy.enabled) { - this.logger.info('Proxy enabled: ' + this.localProxy.proxy); + this.logger.info('Proxy enabled: ' + this.localProxy.proxy.host); if (this.localProxy?.proxy?.host?.includes('proxyscrape')) { try { @@ -656,11 +655,26 @@ export class BaileysStartupService extends WAStartupService { let options; if (this.localProxy.enabled) { - this.logger.verbose('Proxy enabled'); - options = { - agent: makeProxyAgent(this.localProxy.proxy), - fetchAgent: makeProxyAgent(this.localProxy.proxy), - }; + this.logger.info('Proxy enabled: ' + this.localProxy.proxy.host); + + if (this.localProxy?.proxy?.host?.includes('proxyscrape')) { + 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: makeProxyAgent(proxyUrl), + }; + } catch (error) { + this.localProxy.enabled = false; + } + } else { + options = { + agent: makeProxyAgent(this.localProxy.proxy), + }; + } } const socketConfig: UserFacingSocketConfig = { @@ -2147,7 +2161,18 @@ export class BaileysStartupService extends WAStartupService { mimetype = getMIMEType(mediaMessage.fileName); if (!mimetype && isURL(mediaMessage.media)) { - const response = await axios.get(mediaMessage.media, { responseType: 'arraybuffer' }); + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + const response = await axios.get(mediaMessage.media, config); mimetype = response.headers['content-type']; } @@ -2216,7 +2241,18 @@ export class BaileysStartupService extends WAStartupService { const url = `${image}?timestamp=${timestamp}`; this.logger.verbose('including timestamp in url: ' + url); - const response = await axios.get(url, { responseType: 'arraybuffer' }); + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + const response = await axios.get(url, config); this.logger.verbose('Getting image from url'); const imageBuffer = Buffer.from(response.data, 'binary'); @@ -2286,7 +2322,18 @@ export class BaileysStartupService extends WAStartupService { this.logger.verbose('Including timestamp in url: ' + url); - const response = await axios.get(url, { responseType: 'arraybuffer' }); + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + const response = await axios.get(url, config); this.logger.verbose('Getting audio from url'); fs.writeFileSync(tempAudioPath, response.data); @@ -2920,7 +2967,18 @@ export class BaileysStartupService extends WAStartupService { const url = `${picture}?timestamp=${timestamp}`; this.logger.verbose('Including timestamp in url: ' + url); - pic = (await axios.get(url, { responseType: 'arraybuffer' })).data; + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + pic = (await axios.get(url, config)).data; this.logger.verbose('Getting picture from url'); } else if (isBase64(picture)) { this.logger.verbose('Picture is base64'); @@ -3080,7 +3138,18 @@ export class BaileysStartupService extends WAStartupService { const url = `${picture.image}?timestamp=${timestamp}`; this.logger.verbose('Including timestamp in url: ' + url); - pic = (await axios.get(url, { responseType: 'arraybuffer' })).data; + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + pic = (await axios.get(url, config)).data; this.logger.verbose('Getting picture from url'); } else if (isBase64(picture.image)) { this.logger.verbose('Picture is base64'); diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 7c50b043..eaa4dc65 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -583,7 +583,7 @@ export class WAStartupService { this.logger.verbose(`Proxy enabled: ${this.localProxy.enabled}`); this.localProxy.proxy = data?.proxy; - this.logger.verbose(`Proxy proxy: ${this.localProxy.proxy}`); + this.logger.verbose(`Proxy proxy: ${this.localProxy.proxy.host}`); this.logger.verbose('Proxy loaded'); } diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index c3fe2665..366988e2 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -167,6 +167,7 @@ export const instanceController = new InstanceController( sqsService, typebotService, integrationService, + proxyController, cache, chatwootCache, );