diff --git a/package.json b/package.json index 69ea3b64..c1d8ddd2 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.6.0", + "@whiskeysockets/baileys": "github:AtendAI/Baileys", "amqplib": "^0.10.3", "aws-sdk": "^2.1499.0", "axios": "^1.6.5", diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index 91103e33..c389b819 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -53,6 +53,7 @@ export class InstanceController { events, qrcode, number, + mobile, integration, token, chatwoot_account_id, @@ -115,7 +116,7 @@ export class InstanceController { ); } - await this.waMonitor.saveInstance({ integration, instanceName, token, number }); + await this.waMonitor.saveInstance({ integration, instanceName, token, number, mobile }); instance.instanceName = instanceName; @@ -405,7 +406,7 @@ export class InstanceController { if (qrcode) { this.logger.verbose('creating qrcode'); - await instance.connectToWhatsapp(number); + await instance.connectToWhatsapp(number, mobile); await delay(5000); getQrcode = instance.qrCode; } @@ -569,7 +570,7 @@ export class InstanceController { } } - public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) { + public async connectToWhatsapp({ instanceName, number = null, mobile = null }: InstanceDto) { try { this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance'); @@ -592,7 +593,7 @@ export class InstanceController { if (state == 'close') { this.logger.verbose('connecting'); - await instance.connectToWhatsapp(number); + await instance.connectToWhatsapp(number, mobile); await delay(5000); return instance.qrCode; @@ -633,6 +634,20 @@ export class InstanceController { } } + public async registerMobileCode({ instanceName }: InstanceDto, { mobileCode }: any) { + try { + this.logger.verbose('requested registerMobileCode from ' + instanceName + ' instance'); + + const instance = this.waMonitor.waInstances[instanceName]; + + console.log('mobileCode', mobileCode); + return await instance.receiveMobileCode(mobileCode); + // return { status: 'SUCCESS', error: false, response: { message: 'Mobile code registered' } }; + } catch (error) { + this.logger.error(error); + } + } + public async connectionState({ instanceName }: InstanceDto) { this.logger.verbose('requested connectionState from ' + instanceName + ' instance'); return { diff --git a/src/whatsapp/dto/instance.dto.ts b/src/whatsapp/dto/instance.dto.ts index eaf21aab..4b533de1 100644 --- a/src/whatsapp/dto/instance.dto.ts +++ b/src/whatsapp/dto/instance.dto.ts @@ -1,10 +1,11 @@ -import { WAPresence } from "@whiskeysockets/baileys"; +import { WAPresence } from '@whiskeysockets/baileys'; export class InstanceDto { instanceName: string; instanceId?: string; qrcode?: boolean; number?: string; + mobile?: boolean; integration?: string; token?: string; webhook?: string; diff --git a/src/whatsapp/routers/instance.router.ts b/src/whatsapp/routers/instance.router.ts index 6d4727e2..fa78c197 100644 --- a/src/whatsapp/routers/instance.router.ts +++ b/src/whatsapp/routers/instance.router.ts @@ -3,7 +3,7 @@ import { RequestHandler, Router } from 'express'; import { Auth, ConfigService, Database } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { dbserver } from '../../libs/db.connect'; -import {instanceNameSchema, oldTokenSchema, presenceOnlySchema} from '../../validate/validate.schema'; +import { instanceNameSchema, oldTokenSchema, presenceOnlySchema } from '../../validate/validate.schema'; import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; import { OldToken } from '../services/auth.service'; @@ -50,6 +50,22 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) + .post(this.routerPath('registerMobileCode'), ...guards, async (req, res) => { + logger.verbose('request received in registerMobileCode'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: SetPresenceDto, + execute: (instance, data) => instanceController.registerMobileCode(instance, data), + }); + + return res.status(HttpStatus.OK).json(response); + }) .get(this.routerPath('connect'), ...guards, async (req, res) => { logger.verbose('request received in connectInstance'); logger.verbose('request body: '); diff --git a/src/whatsapp/services/whatsapp.baileys.service.ts b/src/whatsapp/services/whatsapp.baileys.service.ts index f99e074d..6ac1e5e6 100644 --- a/src/whatsapp/services/whatsapp.baileys.service.ts +++ b/src/whatsapp/services/whatsapp.baileys.service.ts @@ -12,6 +12,7 @@ import makeWASocket, { DisconnectReason, downloadMediaMessage, fetchLatestBaileysVersion, + generateMobileNode, generateWAMessageFromContent, getAggregateVotesInPollMessage, getContentType, @@ -24,6 +25,7 @@ import makeWASocket, { MessageUpsertType, MiscMessageGenerationOptions, ParticipantAction, + PHONENUMBER_MCC, prepareWAMessageMedia, proto, useMultiFileAuthState, @@ -42,6 +44,7 @@ import { exec } from 'child_process'; import { arrayUnique, isBase64, isURL } from 'class-validator'; import EventEmitter2 from 'eventemitter2'; import fs, { existsSync, readFileSync } from 'fs'; +import { parsePhoneNumber } from 'libphonenumber-js'; import Long from 'long'; import NodeCache from 'node-cache'; import { getMIMEType } from 'node-mime-types'; @@ -132,6 +135,7 @@ export class BaileysStartupService extends WAStartupService { this.logger.verbose('BaileysStartupService initialized'); this.cleanStore(); this.instance.qrcode = { count: 0 }; + this.mobile = false; } private readonly msgRetryCounterCache: CacheStore = new NodeCache(); @@ -141,7 +145,8 @@ export class BaileysStartupService extends WAStartupService { public stateConnection: wa.StateConnection = { state: 'close' }; - private phoneNumber: string; + public phoneNumber: string; + public mobile: boolean; public get connectionStatus() { this.logger.verbose('Getting connection status'); @@ -389,6 +394,10 @@ export class BaileysStartupService extends WAStartupService { ); } } + + if (connection === 'connecting') { + if (this.mobile) this.sendMobileCode(); + } } private async getMessage(key: proto.IMessageKey, full = false) { @@ -446,7 +455,7 @@ export class BaileysStartupService extends WAStartupService { return await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name)); } - public async connectToWhatsapp(number?: string): Promise { + public async connectToWhatsapp(number?: string, mobile?: boolean): Promise { this.logger.verbose('Connecting to whatsapp'); try { this.loadWebhook(); @@ -461,7 +470,14 @@ export class BaileysStartupService extends WAStartupService { this.instance.authState = await this.defineAuthState(); + if (!mobile) { + this.mobile = false; + } else { + this.mobile = mobile; + } + const { version } = await fetchLatestBaileysVersion(); + this.logger.verbose('Baileys version: ' + version); const session = this.configService.get('CONFIG_SESSION_PHONE'); const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; @@ -500,6 +516,7 @@ export class BaileysStartupService extends WAStartupService { }, logger: P({ level: this.logBaileys }), printQRInTerminal: false, + mobile, browser: number ? ['Chrome (Linux)', session.NAME, release()] : browser, version, markOnlineOnConnect: this.localSettings.always_online, @@ -564,6 +581,70 @@ export class BaileysStartupService extends WAStartupService { } } + private async sendMobileCode() { + const { registration } = this.client.authState.creds || null; + + let phoneNumber = registration.phoneNumber || this.phoneNumber; + + if (!phoneNumber.startsWith('+')) { + phoneNumber = '+' + phoneNumber; + } + + if (!phoneNumber) { + this.logger.error('Phone number not found'); + return; + } + + console.log('phoneNumber', phoneNumber); + + const parsedPhoneNumber = parsePhoneNumber(phoneNumber); + + console.log('parsedPhoneNumber', parsedPhoneNumber); + + if (!parsedPhoneNumber?.isValid()) { + this.logger.error('Phone number invalid'); + return; + } + + registration.phoneNumber = parsedPhoneNumber.format('E.164'); + registration.phoneNumberCountryCode = parsedPhoneNumber.countryCallingCode; + registration.phoneNumberNationalNumber = parsedPhoneNumber.nationalNumber; + + const mcc = await PHONENUMBER_MCC[parsedPhoneNumber.countryCallingCode]; + if (!mcc) { + this.logger.error('MCC not found'); + return; + } + + registration.phoneNumberMobileCountryCode = mcc; + registration.method = 'voice'; + + try { + const response = await this.client.requestRegistrationCode(registration); + + console.log('response', response); + if (['ok', 'sent'].includes(response?.status)) { + this.logger.verbose('Registration code sent successfully'); + + return response; + } + } catch (error) { + this.logger.error(error); + } + } + + public async receiveMobileCode(code: string) { + await this.client + .register(code.replace(/["']/g, '').trim().toLowerCase()) + .then(async (response) => { + this.logger.verbose('Registration code received successfully'); + console.log(response); + }) + .catch((error) => { + this.logger.error(error); + }); + } + public async reloadConnection(): Promise { try { this.instance.authState = await this.defineAuthState(); diff --git a/src/whatsapp/services/whatsapp.business.service.ts b/src/whatsapp/services/whatsapp.business.service.ts index 2b4a32cf..35b1e028 100644 --- a/src/whatsapp/services/whatsapp.business.service.ts +++ b/src/whatsapp/services/whatsapp.business.service.ts @@ -44,7 +44,8 @@ export class BusinessStartupService extends WAStartupService { public stateConnection: wa.StateConnection = { state: 'open' }; - private phoneNumber: string; + public phoneNumber: string; + public mobile: boolean; public get connectionStatus() { this.logger.verbose('Getting connection status'); @@ -1347,4 +1348,7 @@ export class BusinessStartupService extends WAStartupService { public async handleLabel() { throw new BadRequestException('Method not available on WhatsApp Business API'); } + public async receiveMobileCode() { + throw new BadRequestException('Method not available on WhatsApp Business API'); + } }