From 4019aa6acf2df8f3d682d83deea2d059f3aaad08 Mon Sep 17 00:00:00 2001 From: Amilton Morais Date: Sat, 30 Sep 2023 20:04:06 -0300 Subject: [PATCH] Delete whatsapp.service.ts --- whatsapp.service.ts | 3382 ------------------------------------------- 1 file changed, 3382 deletions(-) delete mode 100644 whatsapp.service.ts diff --git a/whatsapp.service.ts b/whatsapp.service.ts deleted file mode 100644 index bf7039bb..00000000 --- a/whatsapp.service.ts +++ /dev/null @@ -1,3382 +0,0 @@ -import ffmpegPath from '@ffmpeg-installer/ffmpeg'; -import { Boom } from '@hapi/boom'; -import makeWASocket, { - AnyMessageContent, - BufferedEventData, - BufferJSON, - CacheStore, - Chat, - ConnectionState, - Contact, - delay, - DisconnectReason, - downloadMediaMessage, - fetchLatestBaileysVersion, - generateWAMessageFromContent, - getAggregateVotesInPollMessage, - getContentType, - getDevice, - GroupMetadata, - isJidGroup, - isJidUser, - makeCacheableSignalKeyStore, - MessageUpsertType, - MiscMessageGenerationOptions, - ParticipantAction, - prepareWAMessageMedia, - proto, - useMultiFileAuthState, - UserFacingSocketConfig, - WABrowserDescription, - WAMediaUpload, - WAMessage, - WAMessageUpdate, - WASocket, -} from '@whiskeysockets/baileys'; -import axios from 'axios'; -import { exec, execSync } from 'child_process'; -import { arrayUnique, isBase64, isURL } from 'class-validator'; -import EventEmitter2 from 'eventemitter2'; -import fs, { existsSync, readFileSync } from 'fs'; -import Long from 'long'; -import NodeCache from 'node-cache'; -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'; -import { v4 } from 'uuid'; - -import { - Auth, - CleanStoreConf, - ConfigService, - ConfigSessionPhone, - Database, - HttpServer, - Log, - QrCode, - Redis, - Webhook, - Websocket, -} from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { INSTANCE_DIR, ROOT_DIR } from '../../config/path.config'; -import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../exceptions'; -import { getAMQP } from '../../libs/amqp.server'; -import { dbserver } from '../../libs/db.connect'; -import { RedisCache } from '../../libs/redis.client'; -import { getIO } from '../../libs/socket.server'; -import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; -import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; -import { - ArchiveChatDto, - DeleteMessage, - getBase64FromMediaMessageDto, - LastMessage, - NumberBusiness, - OnWhatsAppDto, - PrivacySettingDto, - ReadMessageDto, - WhatsAppNumberDto, -} from '../dto/chat.dto'; -import { - CreateGroupDto, - GetParticipant, - GroupDescriptionDto, - GroupInvite, - GroupJid, - GroupPictureDto, - GroupSendInvite, - GroupSubjectDto, - GroupToggleEphemeralDto, - GroupUpdateParticipantDto, - GroupUpdateSettingDto, -} from '../dto/group.dto'; -import { - ContactMessage, - MediaMessage, - Options, - SendAudioDto, - SendButtonDto, - SendContactDto, - SendListDto, - SendLocationDto, - SendMediaDto, - SendPollDto, - SendReactionDto, - SendStatusDto, - SendStickerDto, - SendTextDto, - StatusMessage, -} from '../dto/sendMessage.dto'; -import { ChamaaiRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, TypebotRaw } from '../models'; -import { ChatRaw } from '../models/chat.model'; -import { ChatwootRaw } from '../models/chatwoot.model'; -import { ContactRaw } from '../models/contact.model'; -import { MessageRaw, MessageUpdateRaw } from '../models/message.model'; -import { WebhookRaw } from '../models/webhook.model'; -import { WebsocketRaw } from '../models/websocket.model'; -import { ContactQuery } from '../repository/contact.repository'; -import { MessageQuery } from '../repository/message.repository'; -import { MessageUpQuery } from '../repository/messageUp.repository'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types'; -import { waMonitor } from '../whatsapp.module'; -import { ChamaaiService } from './chamaai.service'; -import { ChatwootService } from './chatwoot.service'; -//import { SocksProxyAgent } from './socks-proxy-agent'; -import { TypebotService } from './typebot.service'; - -export class WAStartupService { - constructor( - private readonly configService: ConfigService, - private readonly eventEmitter: EventEmitter2, - private readonly repository: RepositoryBroker, - private readonly cache: RedisCache, - ) { - this.logger.verbose('WAStartupService initialized'); - this.cleanStore(); - this.instance.qrcode = { count: 0 }; - } - - private readonly logger = new Logger(WAStartupService.name); - public readonly instance: wa.Instance = {}; - public client: WASocket; - private readonly localWebhook: wa.LocalWebHook = {}; - private readonly localChatwoot: wa.LocalChatwoot = {}; - private readonly localSettings: wa.LocalSettings = {}; - private readonly localWebsocket: wa.LocalWebsocket = {}; - private readonly localRabbitmq: wa.LocalRabbitmq = {}; - public readonly localTypebot: wa.LocalTypebot = {}; - private readonly localProxy: wa.LocalProxy = {}; - private readonly localChamaai: wa.LocalChamaai = {}; - public stateConnection: wa.StateConnection = { state: 'close' }; - public readonly storePath = join(ROOT_DIR, 'store'); - private readonly msgRetryCounterCache: CacheStore = new NodeCache(); - private readonly userDevicesCache: CacheStore = new NodeCache(); - private endSession = false; - private logBaileys = this.configService.get('LOG').BAILEYS; - - private phoneNumber: string; - - private chatwootService = new ChatwootService(waMonitor, this.configService); - - private typebotService = new TypebotService(waMonitor); - - private chamaaiService = new ChamaaiService(waMonitor, this.configService); - - public set instanceName(name: string) { - this.logger.verbose(`Initializing instance '${name}'`); - if (!name) { - this.logger.verbose('Instance name not found, generating random name with uuid'); - this.instance.name = v4(); - return; - } - this.instance.name = name; - this.logger.verbose(`Instance '${this.instance.name}' initialized`); - this.logger.verbose('Sending instance status to webhook'); - this.sendDataWebhook(Events.STATUS_INSTANCE, { - instance: this.instance.name, - status: 'created', - }); - - if (this.localChatwoot.enabled) { - this.chatwootService.eventWhatsapp( - Events.STATUS_INSTANCE, - { instanceName: this.instance.name }, - { - instance: this.instance.name, - status: 'created', - }, - ); - } - } - - public get instanceName() { - this.logger.verbose('Getting instance name'); - return this.instance.name; - } - - public get wuid() { - this.logger.verbose('Getting remoteJid of instance'); - return this.instance.wuid; - } - - public async getProfileName() { - this.logger.verbose('Getting profile name'); - let profileName = this.client.user?.name ?? this.client.user?.verifiedName; - if (!profileName) { - this.logger.verbose('Profile name not found, trying to get from database'); - if (this.configService.get('DATABASE').ENABLED) { - this.logger.verbose('Database enabled, trying to get from database'); - const collection = dbserver - .getClient() - .db(this.configService.get('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances') - .collection(this.instanceName); - const data = await collection.findOne({ _id: 'creds' }); - if (data) { - this.logger.verbose('Profile name found in database'); - const creds = JSON.parse(JSON.stringify(data), BufferJSON.reviver); - profileName = creds.me?.name || creds.me?.verifiedName; - } - } else if (existsSync(join(INSTANCE_DIR, this.instanceName, 'creds.json'))) { - this.logger.verbose('Profile name found in file'); - const creds = JSON.parse( - readFileSync(join(INSTANCE_DIR, this.instanceName, 'creds.json'), { - encoding: 'utf-8', - }), - ); - profileName = creds.me?.name || creds.me?.verifiedName; - } - } - - this.logger.verbose(`Profile name: ${profileName}`); - return profileName; - } - - public async getProfileStatus() { - this.logger.verbose('Getting profile status'); - const status = await this.client.fetchStatus(this.instance.wuid); - - this.logger.verbose(`Profile status: ${status.status}`); - return status.status; - } - - public get profilePictureUrl() { - this.logger.verbose('Getting profile picture url'); - return this.instance.profilePictureUrl; - } - - public get qrCode(): wa.QrCode { - this.logger.verbose('Getting qrcode'); - - return { - pairingCode: this.instance.qrcode?.pairingCode, - code: this.instance.qrcode?.code, - base64: this.instance.qrcode?.base64, - }; - } - - private async loadWebhook() { - this.logger.verbose('Loading webhook'); - const data = await this.repository.webhook.find(this.instanceName); - this.localWebhook.url = data?.url; - this.logger.verbose(`Webhook url: ${this.localWebhook.url}`); - - this.localWebhook.enabled = data?.enabled; - this.logger.verbose(`Webhook enabled: ${this.localWebhook.enabled}`); - - this.localWebhook.events = data?.events; - this.logger.verbose(`Webhook events: ${this.localWebhook.events}`); - - this.localWebhook.webhook_by_events = data?.webhook_by_events; - this.logger.verbose(`Webhook by events: ${this.localWebhook.webhook_by_events}`); - - this.logger.verbose('Webhook loaded'); - } - - public async setWebhook(data: WebhookRaw) { - this.logger.verbose('Setting webhook'); - await this.repository.webhook.create(data, this.instanceName); - this.logger.verbose(`Webhook url: ${data.url}`); - this.logger.verbose(`Webhook events: ${data.events}`); - Object.assign(this.localWebhook, data); - this.logger.verbose('Webhook set'); - } - - public async findWebhook() { - this.logger.verbose('Finding webhook'); - const data = await this.repository.webhook.find(this.instanceName); - - if (!data) { - this.logger.verbose('Webhook not found'); - throw new NotFoundException('Webhook not found'); - } - - this.logger.verbose(`Webhook url: ${data.url}`); - this.logger.verbose(`Webhook events: ${data.events}`); - return data; - } - - private async loadChatwoot() { - this.logger.verbose('Loading chatwoot'); - const data = await this.repository.chatwoot.find(this.instanceName); - this.localChatwoot.enabled = data?.enabled; - this.logger.verbose(`Chatwoot enabled: ${this.localChatwoot.enabled}`); - - this.localChatwoot.account_id = data?.account_id; - this.logger.verbose(`Chatwoot account id: ${this.localChatwoot.account_id}`); - - this.localChatwoot.token = data?.token; - this.logger.verbose(`Chatwoot token: ${this.localChatwoot.token}`); - - this.localChatwoot.url = data?.url; - this.logger.verbose(`Chatwoot url: ${this.localChatwoot.url}`); - - this.localChatwoot.name_inbox = data?.name_inbox; - this.logger.verbose(`Chatwoot inbox name: ${this.localChatwoot.name_inbox}`); - - this.localChatwoot.sign_msg = data?.sign_msg; - this.logger.verbose(`Chatwoot sign msg: ${this.localChatwoot.sign_msg}`); - - this.localChatwoot.number = data?.number; - this.logger.verbose(`Chatwoot number: ${this.localChatwoot.number}`); - - this.localChatwoot.reopen_conversation = data?.reopen_conversation; - this.logger.verbose(`Chatwoot reopen conversation: ${this.localChatwoot.reopen_conversation}`); - - this.localChatwoot.conversation_pending = data?.conversation_pending; - this.logger.verbose(`Chatwoot conversation pending: ${this.localChatwoot.conversation_pending}`); - - this.logger.verbose('Chatwoot loaded'); - } - - public async setChatwoot(data: ChatwootRaw) { - this.logger.verbose('Setting chatwoot'); - await this.repository.chatwoot.create(data, this.instanceName); - this.logger.verbose(`Chatwoot account id: ${data.account_id}`); - this.logger.verbose(`Chatwoot token: ${data.token}`); - this.logger.verbose(`Chatwoot url: ${data.url}`); - this.logger.verbose(`Chatwoot inbox name: ${data.name_inbox}`); - this.logger.verbose(`Chatwoot sign msg: ${data.sign_msg}`); - this.logger.verbose(`Chatwoot reopen conversation: ${data.reopen_conversation}`); - this.logger.verbose(`Chatwoot conversation pending: ${data.conversation_pending}`); - - Object.assign(this.localChatwoot, data); - this.logger.verbose('Chatwoot set'); - } - - public async findChatwoot() { - this.logger.verbose('Finding chatwoot'); - const data = await this.repository.chatwoot.find(this.instanceName); - - if (!data) { - this.logger.verbose('Chatwoot not found'); - return null; - } - - this.logger.verbose(`Chatwoot account id: ${data.account_id}`); - this.logger.verbose(`Chatwoot token: ${data.token}`); - this.logger.verbose(`Chatwoot url: ${data.url}`); - this.logger.verbose(`Chatwoot inbox name: ${data.name_inbox}`); - this.logger.verbose(`Chatwoot sign msg: ${data.sign_msg}`); - this.logger.verbose(`Chatwoot reopen conversation: ${data.reopen_conversation}`); - this.logger.verbose(`Chatwoot conversation pending: ${data.conversation_pending}`); - - return data; - } - - private async loadSettings() { - this.logger.verbose('Loading settings'); - const data = await this.repository.settings.find(this.instanceName); - this.localSettings.reject_call = data?.reject_call; - this.logger.verbose(`Settings reject_call: ${this.localSettings.reject_call}`); - - this.localSettings.msg_call = data?.msg_call; - this.logger.verbose(`Settings msg_call: ${this.localSettings.msg_call}`); - - this.localSettings.groups_ignore = data?.groups_ignore; - this.logger.verbose(`Settings groups_ignore: ${this.localSettings.groups_ignore}`); - - this.localSettings.always_online = data?.always_online; - this.logger.verbose(`Settings always_online: ${this.localSettings.always_online}`); - - this.localSettings.read_messages = data?.read_messages; - this.logger.verbose(`Settings read_messages: ${this.localSettings.read_messages}`); - - this.localSettings.read_status = data?.read_status; - this.logger.verbose(`Settings read_status: ${this.localSettings.read_status}`); - - this.logger.verbose('Settings loaded'); - } - - public async setSettings(data: SettingsRaw) { - this.logger.verbose('Setting settings'); - await this.repository.settings.create(data, this.instanceName); - this.logger.verbose(`Settings reject_call: ${data.reject_call}`); - this.logger.verbose(`Settings msg_call: ${data.msg_call}`); - this.logger.verbose(`Settings groups_ignore: ${data.groups_ignore}`); - this.logger.verbose(`Settings always_online: ${data.always_online}`); - this.logger.verbose(`Settings read_messages: ${data.read_messages}`); - this.logger.verbose(`Settings read_status: ${data.read_status}`); - Object.assign(this.localSettings, data); - this.logger.verbose('Settings set'); - - this.client?.ws?.close(); - } - - public async findSettings() { - this.logger.verbose('Finding settings'); - const data = await this.repository.settings.find(this.instanceName); - - if (!data) { - this.logger.verbose('Settings not found'); - return null; - } - - this.logger.verbose(`Settings url: ${data.reject_call}`); - this.logger.verbose(`Settings msg_call: ${data.msg_call}`); - this.logger.verbose(`Settings groups_ignore: ${data.groups_ignore}`); - this.logger.verbose(`Settings always_online: ${data.always_online}`); - this.logger.verbose(`Settings read_messages: ${data.read_messages}`); - this.logger.verbose(`Settings read_status: ${data.read_status}`); - return data; - } - - private async loadWebsocket() { - this.logger.verbose('Loading websocket'); - const data = await this.repository.websocket.find(this.instanceName); - - this.localWebsocket.enabled = data?.enabled; - this.logger.verbose(`Websocket enabled: ${this.localWebsocket.enabled}`); - - this.localWebsocket.events = data?.events; - this.logger.verbose(`Websocket events: ${this.localWebsocket.events}`); - - this.logger.verbose('Websocket loaded'); - } - - public async setWebsocket(data: WebsocketRaw) { - this.logger.verbose('Setting websocket'); - await this.repository.websocket.create(data, this.instanceName); - this.logger.verbose(`Websocket events: ${data.events}`); - Object.assign(this.localWebsocket, data); - this.logger.verbose('Websocket set'); - } - - public async findWebsocket() { - this.logger.verbose('Finding websocket'); - const data = await this.repository.websocket.find(this.instanceName); - - if (!data) { - this.logger.verbose('Websocket not found'); - throw new NotFoundException('Websocket not found'); - } - - this.logger.verbose(`Websocket events: ${data.events}`); - return data; - } - - private async loadRabbitmq() { - this.logger.verbose('Loading rabbitmq'); - const data = await this.repository.rabbitmq.find(this.instanceName); - - this.localRabbitmq.enabled = data?.enabled; - this.logger.verbose(`Rabbitmq enabled: ${this.localRabbitmq.enabled}`); - - this.localRabbitmq.events = data?.events; - this.logger.verbose(`Rabbitmq events: ${this.localRabbitmq.events}`); - - this.logger.verbose('Rabbitmq loaded'); - } - - public async setRabbitmq(data: RabbitmqRaw) { - this.logger.verbose('Setting rabbitmq'); - await this.repository.rabbitmq.create(data, this.instanceName); - this.logger.verbose(`Rabbitmq events: ${data.events}`); - Object.assign(this.localRabbitmq, data); - this.logger.verbose('Rabbitmq set'); - } - - public async findRabbitmq() { - this.logger.verbose('Finding rabbitmq'); - const data = await this.repository.rabbitmq.find(this.instanceName); - - if (!data) { - this.logger.verbose('Rabbitmq not found'); - throw new NotFoundException('Rabbitmq not found'); - } - - this.logger.verbose(`Rabbitmq events: ${data.events}`); - return data; - } - - private async loadTypebot() { - this.logger.verbose('Loading typebot'); - const data = await this.repository.typebot.find(this.instanceName); - - this.localTypebot.enabled = data?.enabled; - this.logger.verbose(`Typebot enabled: ${this.localTypebot.enabled}`); - - this.localTypebot.url = data?.url; - this.logger.verbose(`Typebot url: ${this.localTypebot.url}`); - - this.localTypebot.typebot = data?.typebot; - this.logger.verbose(`Typebot typebot: ${this.localTypebot.typebot}`); - - this.localTypebot.expire = data?.expire; - this.logger.verbose(`Typebot expire: ${this.localTypebot.expire}`); - - this.localTypebot.keyword_finish = data?.keyword_finish; - this.logger.verbose(`Typebot keyword_finish: ${this.localTypebot.keyword_finish}`); - - this.localTypebot.delay_message = data?.delay_message; - this.logger.verbose(`Typebot delay_message: ${this.localTypebot.delay_message}`); - - this.localTypebot.unknown_message = data?.unknown_message; - this.logger.verbose(`Typebot unknown_message: ${this.localTypebot.unknown_message}`); - - this.localTypebot.listening_from_me = data?.listening_from_me; - this.logger.verbose(`Typebot listening_from_me: ${this.localTypebot.listening_from_me}`); - - this.localTypebot.sessions = data?.sessions; - - this.logger.verbose('Typebot loaded'); - } - - public async setTypebot(data: TypebotRaw) { - this.logger.verbose('Setting typebot'); - await this.repository.typebot.create(data, this.instanceName); - this.logger.verbose(`Typebot typebot: ${data.typebot}`); - this.logger.verbose(`Typebot expire: ${data.expire}`); - this.logger.verbose(`Typebot keyword_finish: ${data.keyword_finish}`); - this.logger.verbose(`Typebot delay_message: ${data.delay_message}`); - this.logger.verbose(`Typebot unknown_message: ${data.unknown_message}`); - this.logger.verbose(`Typebot listening_from_me: ${data.listening_from_me}`); - Object.assign(this.localTypebot, data); - this.logger.verbose('Typebot set'); - } - - public async findTypebot() { - this.logger.verbose('Finding typebot'); - const data = await this.repository.typebot.find(this.instanceName); - - if (!data) { - this.logger.verbose('Typebot not found'); - throw new NotFoundException('Typebot not found'); - } - - return data; - } - - private async loadProxy() { - this.logger.verbose('Loading proxy'); - const data = await this.repository.proxy.find(this.instanceName); - - this.localProxy.enabled = data?.enabled; - 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 loaded'); - } - - public async setProxy(data: ProxyRaw) { - this.logger.verbose('Setting proxy'); - await this.repository.proxy.create(data, this.instanceName); - this.logger.verbose(`Proxy proxy: ${data.proxy}`); - Object.assign(this.localProxy, data); - this.logger.verbose('Proxy set'); - - this.client?.ws?.close(); - } - - public async findProxy() { - this.logger.verbose('Finding proxy'); - const data = await this.repository.proxy.find(this.instanceName); - - if (!data) { - this.logger.verbose('Proxy not found'); - throw new NotFoundException('Proxy not found'); - } - - return data; - } - - private async loadChamaai() { - this.logger.verbose('Loading chamaai'); - const data = await this.repository.chamaai.find(this.instanceName); - - this.localChamaai.enabled = data?.enabled; - this.logger.verbose(`Chamaai enabled: ${this.localChamaai.enabled}`); - - this.localChamaai.url = data?.url; - this.logger.verbose(`Chamaai url: ${this.localChamaai.url}`); - - this.localChamaai.token = data?.token; - this.logger.verbose(`Chamaai token: ${this.localChamaai.token}`); - - this.localChamaai.waNumber = data?.waNumber; - this.logger.verbose(`Chamaai waNumber: ${this.localChamaai.waNumber}`); - - this.localChamaai.answerByAudio = data?.answerByAudio; - this.logger.verbose(`Chamaai answerByAudio: ${this.localChamaai.answerByAudio}`); - - this.logger.verbose('Chamaai loaded'); - } - - public async setChamaai(data: ChamaaiRaw) { - this.logger.verbose('Setting chamaai'); - await this.repository.chamaai.create(data, this.instanceName); - this.logger.verbose(`Chamaai url: ${data.url}`); - this.logger.verbose(`Chamaai token: ${data.token}`); - this.logger.verbose(`Chamaai waNumber: ${data.waNumber}`); - this.logger.verbose(`Chamaai answerByAudio: ${data.answerByAudio}`); - - Object.assign(this.localChamaai, data); - this.logger.verbose('Chamaai set'); - } - - public async findChamaai() { - this.logger.verbose('Finding chamaai'); - const data = await this.repository.chamaai.find(this.instanceName); - - if (!data) { - this.logger.verbose('Chamaai not found'); - throw new NotFoundException('Chamaai not found'); - } - - return data; - } - - public async sendDataWebhook(event: Events, data: T, local = true) { - const webhookGlobal = this.configService.get('WEBHOOK'); - const webhookLocal = this.localWebhook.events; - const websocketLocal = this.localWebsocket.events; - const rabbitmqLocal = this.localRabbitmq.events; - const serverUrl = this.configService.get('SERVER').URL; - const we = event.replace(/[.-]/gm, '_').toUpperCase(); - const transformedWe = we.replace(/_/gm, '-').toLowerCase(); - const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds - const localISOTime = new Date(Date.now() - tzoffset).toISOString(); - const now = localISOTime; - - const expose = this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES; - const tokenStore = await this.repository.auth.find(this.instanceName); - const instanceApikey = tokenStore?.apikey || 'Apikey not found'; - - if (this.localRabbitmq.enabled) { - const amqp = getAMQP(); - - if (amqp) { - if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) { - const exchangeName = this.instanceName ?? 'evolution_exchange'; - - amqp.assertExchange(exchangeName, 'topic', { - durable: true, - autoDelete: false, - }); - - const queueName = `${this.instanceName}.${event}`; - - amqp.assertQueue(queueName, { - durable: true, - autoDelete: false, - arguments: { - 'x-queue-type': 'quorum', - }, - }); - - amqp.bindQueue(queueName, exchangeName, event); - - const message = { - event, - instance: this.instance.name, - data, - server_url: serverUrl, - date_time: now, - sender: this.wuid, - }; - - if (expose && instanceApikey) { - message['apikey'] = instanceApikey; - } - - amqp.publish(exchangeName, event, Buffer.from(JSON.stringify(message))); - - if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { - const logData = { - local: WAStartupService.name + '.sendData-RabbitMQ', - event, - instance: this.instance.name, - data, - server_url: serverUrl, - apikey: (expose && instanceApikey) || null, - date_time: now, - sender: this.wuid, - }; - - if (expose && instanceApikey) { - logData['apikey'] = instanceApikey; - } - - this.logger.log(logData); - } - } - } - } - - if (this.configService.get('WEBSOCKET')?.ENABLED && this.localWebsocket.enabled) { - this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name); - if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) { - this.logger.verbose('Sending data to websocket on event: ' + event); - const io = getIO(); - - const message = { - event, - instance: this.instance.name, - data, - server_url: serverUrl, - date_time: now, - sender: this.wuid, - }; - - if (expose && instanceApikey) { - message['apikey'] = instanceApikey; - } - - this.logger.verbose('Sending data to socket.io in channel: ' + this.instance.name); - io.of(`/${this.instance.name}`).emit(event, message); - - if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { - const logData = { - local: WAStartupService.name + '.sendData-Websocket', - event, - instance: this.instance.name, - data, - server_url: serverUrl, - apikey: (expose && instanceApikey) || null, - date_time: now, - sender: this.wuid, - }; - - if (expose && instanceApikey) { - logData['apikey'] = instanceApikey; - } - - this.logger.log(logData); - } - } - } - - const globalApiKey = this.configService.get('AUTHENTICATION').API_KEY.KEY; - - if (local) { - if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) { - this.logger.verbose('Sending data to webhook local'); - let baseURL: string; - - if (this.localWebhook.webhook_by_events) { - baseURL = `${this.localWebhook.url}/${transformedWe}`; - } else { - baseURL = this.localWebhook.url; - } - - if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { - const logData = { - local: WAStartupService.name + '.sendDataWebhook-local', - url: baseURL, - event, - instance: this.instance.name, - data, - destination: this.localWebhook.url, - date_time: now, - sender: this.wuid, - server_url: serverUrl, - apikey: (expose && instanceApikey) || null, - }; - - if (expose && instanceApikey) { - logData['apikey'] = instanceApikey; - } - - this.logger.log(logData); - } - - try { - if (this.localWebhook.enabled && isURL(this.localWebhook.url, { require_tld: false })) { - const httpService = axios.create({ baseURL }); - const postData = { - event, - instance: this.instance.name, - data, - destination: this.localWebhook.url, - date_time: now, - sender: this.wuid, - server_url: serverUrl, - }; - - if (expose && instanceApikey) { - postData['apikey'] = instanceApikey; - } - - await httpService.post('', postData); - } - } catch (error) { - this.logger.error({ - local: WAStartupService.name + '.sendDataWebhook-local', - message: error?.message, - hostName: error?.hostname, - syscall: error?.syscall, - code: error?.code, - error: error?.errno, - stack: error?.stack, - name: error?.name, - url: baseURL, - server_url: serverUrl, - }); - } - } - } - - if (webhookGlobal.GLOBAL?.ENABLED) { - if (webhookGlobal.EVENTS[we]) { - this.logger.verbose('Sending data to webhook global'); - const globalWebhook = this.configService.get('WEBHOOK').GLOBAL; - - let globalURL; - - if (webhookGlobal.GLOBAL.WEBHOOK_BY_EVENTS) { - globalURL = `${globalWebhook.URL}/${transformedWe}`; - } else { - globalURL = globalWebhook.URL; - } - - const localUrl = this.localWebhook.url; - - if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { - const logData = { - local: WAStartupService.name + '.sendDataWebhook-global', - url: globalURL, - event, - instance: this.instance.name, - data, - destination: localUrl, - date_time: now, - sender: this.wuid, - server_url: serverUrl, - }; - - if (expose && globalApiKey) { - logData['apikey'] = globalApiKey; - } - - this.logger.log(logData); - } - - try { - if (globalWebhook && globalWebhook?.ENABLED && isURL(globalURL)) { - const httpService = axios.create({ baseURL: globalURL }); - const postData = { - event, - instance: this.instance.name, - data, - destination: localUrl, - date_time: now, - sender: this.wuid, - server_url: serverUrl, - }; - - if (expose && globalApiKey) { - postData['apikey'] = globalApiKey; - } - - await httpService.post('', postData); - } - } catch (error) { - this.logger.error({ - local: WAStartupService.name + '.sendDataWebhook-global', - message: error?.message, - hostName: error?.hostname, - syscall: error?.syscall, - code: error?.code, - error: error?.errno, - stack: error?.stack, - name: error?.name, - url: globalURL, - server_url: serverUrl, - }); - } - } - } - } - - private async connectionUpdate({ qr, connection, lastDisconnect }: Partial) { - this.logger.verbose('Connection update'); - if (qr) { - this.logger.verbose('QR code found'); - if (this.instance.qrcode.count === this.configService.get('QRCODE').LIMIT) { - this.logger.verbose('QR code limit reached'); - - this.logger.verbose('Sending data to webhook in event QRCODE_UPDATED'); - this.sendDataWebhook(Events.QRCODE_UPDATED, { - message: 'QR code limit reached, please login again', - statusCode: DisconnectReason.badSession, - }); - - if (this.localChatwoot.enabled) { - this.chatwootService.eventWhatsapp( - Events.QRCODE_UPDATED, - { instanceName: this.instance.name }, - { - message: 'QR code limit reached, please login again', - statusCode: DisconnectReason.badSession, - }, - ); - } - - this.logger.verbose('Sending data to webhook in event CONNECTION_UPDATE'); - this.sendDataWebhook(Events.CONNECTION_UPDATE, { - instance: this.instance.name, - state: 'refused', - statusReason: DisconnectReason.connectionClosed, - }); - - this.logger.verbose('endSession defined as true'); - this.endSession = true; - - this.logger.verbose('Emmiting event logout.instance'); - return this.eventEmitter.emit('no.connection', this.instance.name); - } - - this.logger.verbose('Incrementing QR code count'); - this.instance.qrcode.count++; - - const color = this.configService.get('QRCODE').COLOR; - - const optsQrcode: QRCodeToDataURLOptions = { - margin: 3, - scale: 4, - errorCorrectionLevel: 'H', - color: { light: '#ffffff', dark: color }, - }; - - if (this.phoneNumber) { - await delay(2000); - this.instance.qrcode.pairingCode = await this.client.requestPairingCode(this.phoneNumber); - } else { - this.instance.qrcode.pairingCode = null; - } - - this.logger.verbose('Generating QR code'); - qrcode.toDataURL(qr, optsQrcode, (error, base64) => { - if (error) { - this.logger.error('Qrcode generate failed:' + error.toString()); - return; - } - - this.instance.qrcode.base64 = base64; - this.instance.qrcode.code = qr; - - this.sendDataWebhook(Events.QRCODE_UPDATED, { - qrcode: { - instance: this.instance.name, - pairingCode: this.instance.qrcode.pairingCode, - code: qr, - base64, - }, - }); - - if (this.localChatwoot.enabled) { - this.chatwootService.eventWhatsapp( - Events.QRCODE_UPDATED, - { instanceName: this.instance.name }, - { - qrcode: { - instance: this.instance.name, - pairingCode: this.instance.qrcode.pairingCode, - code: qr, - base64, - }, - }, - ); - } - }); - - this.logger.verbose('Generating QR code in terminal'); - qrcodeTerminal.generate(qr, { small: true }, (qrcode) => - this.logger.log( - `\n{ instance: ${this.instance.name} pairingCode: ${this.instance.qrcode.pairingCode}, qrcodeCount: ${this.instance.qrcode.count} }\n` + - qrcode, - ), - ); - } - - if (connection) { - this.logger.verbose('Connection found'); - this.stateConnection = { - state: connection, - statusReason: (lastDisconnect?.error as Boom)?.output?.statusCode ?? 200, - }; - - this.logger.verbose('Sending data to webhook in event CONNECTION_UPDATE'); - this.sendDataWebhook(Events.CONNECTION_UPDATE, { - instance: this.instance.name, - ...this.stateConnection, - }); - } - - if (connection === 'close') { - this.logger.verbose('Connection closed'); - const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut; - if (shouldReconnect) { - this.logger.verbose('Reconnecting to whatsapp'); - await this.connectToWhatsapp(); - } else { - this.logger.verbose('Do not reconnect to whatsapp'); - this.logger.verbose('Sending data to webhook in event STATUS_INSTANCE'); - this.sendDataWebhook(Events.STATUS_INSTANCE, { - instance: this.instance.name, - status: 'closed', - }); - - if (this.localChatwoot.enabled) { - this.chatwootService.eventWhatsapp( - Events.STATUS_INSTANCE, - { instanceName: this.instance.name }, - { - instance: this.instance.name, - status: 'closed', - }, - ); - } - - this.logger.verbose('Emittin event logout.instance'); - this.eventEmitter.emit('logout.instance', this.instance.name, 'inner'); - this.client?.ws?.close(); - this.client.end(new Error('Close connection')); - this.logger.verbose('Connection closed'); - } - } - - if (connection === 'open') { - this.logger.verbose('Connection opened'); - this.instance.wuid = this.client.user.id.replace(/:\d+/, ''); - this.instance.profilePictureUrl = (await this.profilePicture(this.instance.wuid)).profilePictureUrl; - this.logger.info( - ` - ┌──────────────────────────────┐ - │ CONNECTED TO WHATSAPP │ - └──────────────────────────────┘`.replace(/^ +/gm, ' '), - ); - - if (this.localChatwoot.enabled) { - this.chatwootService.eventWhatsapp( - Events.CONNECTION_UPDATE, - { instanceName: this.instance.name }, - { - instance: this.instance.name, - status: 'open', - }, - ); - } - } - } - - private async getMessage(key: proto.IMessageKey, full = false) { - this.logger.verbose('Getting message with key: ' + JSON.stringify(key)); - try { - const webMessageInfo = (await this.repository.message.find({ - where: { owner: this.instance.name, key: { id: key.id } }, - })) as unknown as proto.IWebMessageInfo[]; - if (full) { - this.logger.verbose('Returning full message'); - return webMessageInfo[0]; - } - if (webMessageInfo[0].message?.pollCreationMessage) { - this.logger.verbose('Returning poll message'); - const messageSecretBase64 = webMessageInfo[0].message?.messageContextInfo?.messageSecret; - - if (typeof messageSecretBase64 === 'string') { - const messageSecret = Buffer.from(messageSecretBase64, 'base64'); - - const msg = { - messageContextInfo: { - messageSecret, - }, - pollCreationMessage: webMessageInfo[0].message?.pollCreationMessage, - }; - - return msg; - } - } - - this.logger.verbose('Returning message'); - return webMessageInfo[0].message; - } catch (error) { - return { conversation: '' }; - } - } - - private cleanStore() { - this.logger.verbose('Cronjob to clean store initialized'); - const cleanStore = this.configService.get('CLEAN_STORE'); - const database = this.configService.get('DATABASE'); - if (cleanStore?.CLEANING_INTERVAL && !database.ENABLED) { - this.logger.verbose('Cronjob to clean store enabled'); - setInterval(() => { - try { - for (const [key, value] of Object.entries(cleanStore)) { - if (value === true) { - execSync( - `rm -rf ${join(this.storePath, key.toLowerCase().replace('_', '-'), this.instance.name)}/*.json`, - ); - this.logger.verbose( - `Cleaned ${join(this.storePath, key.toLowerCase().replace('_', '-'), this.instance.name)}/*.json`, - ); - } - } - } catch (error) { - this.logger.error(error); - } - }, (cleanStore?.CLEANING_INTERVAL ?? 3600) * 1000); - } - } - - private async defineAuthState() { - this.logger.verbose('Defining auth state'); - const db = this.configService.get('DATABASE'); - const redis = this.configService.get('REDIS'); - - if (redis?.ENABLED) { - this.logger.verbose('Redis enabled'); - this.cache.reference = this.instance.name; - return await useMultiFileAuthStateRedisDb(this.cache); - } - - if (db.SAVE_DATA.INSTANCE && db.ENABLED) { - this.logger.verbose('Database enabled'); - return await useMultiFileAuthStateDb(this.instance.name); - } - - this.logger.verbose('Store file enabled'); - return await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name)); - } - - public async connectToWhatsapp(number?: string): Promise { - this.logger.verbose('Connecting to whatsapp'); - try { - this.loadWebhook(); - this.loadChatwoot(); - this.loadSettings(); - this.loadWebsocket(); - this.loadRabbitmq(); - this.loadTypebot(); - this.loadProxy(); - this.loadChamaai(); - - this.instance.authState = await this.defineAuthState(); - - 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()]; - this.logger.verbose('Browser: ' + JSON.stringify(browser)); - - let options; - - 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), - }; - } - - const socketConfig: UserFacingSocketConfig = { - ...options, - auth: { - creds: this.instance.authState.state.creds, - keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' })), - }, - logger: P({ level: this.logBaileys }), - printQRInTerminal: false, - browser, - version, - markOnlineOnConnect: this.localSettings.always_online, - connectTimeoutMs: 60_000, - qrTimeout: 40_000, - defaultQueryTimeoutMs: undefined, - emitOwnEvents: false, - msgRetryCounterCache: this.msgRetryCounterCache, - getMessage: async (key) => (await this.getMessage(key)) as Promise, - generateHighQualityLinkPreview: true, - syncFullHistory: true, - userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, - patchMessageBeforeSending: (message) => { - const requiresPatch = !!(message.buttonsMessage || message.listMessage || message.templateMessage); - if (requiresPatch) { - message = { - viewOnceMessageV2: { - message: { - messageContextInfo: { - deviceListMetadataVersion: 2, - deviceListMetadata: {}, - }, - ...message, - }, - }, - }; - } - - return message; - }, - }; - - this.endSession = false; - - this.logger.verbose('Creating socket'); - - this.client = makeWASocket(socketConfig); - - this.logger.verbose('Socket created'); - - this.eventHandler(); - - this.logger.verbose('Socket event handler initialized'); - - this.phoneNumber = number; - - return this.client; - } catch (error) { - this.logger.error(error); - throw new InternalServerErrorException(error?.toString()); - } - } - - private readonly chatHandle = { - 'chats.upsert': async (chats: Chat[], database: Database) => { - this.logger.verbose('Event received: chats.upsert'); - - this.logger.verbose('Finding chats in database'); - const chatsRepository = await this.repository.chat.find({ - where: { owner: this.instance.name }, - }); - - this.logger.verbose('Verifying if chats exists in database to insert'); - const chatsRaw: ChatRaw[] = []; - for await (const chat of chats) { - if (chatsRepository.find((cr) => cr.id === chat.id)) { - continue; - } - - chatsRaw.push({ id: chat.id, owner: this.instance.wuid }); - } - - this.logger.verbose('Sending data to webhook in event CHATS_UPSERT'); - await this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); - - this.logger.verbose('Inserting chats in database'); - await this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); - }, - - 'chats.update': async ( - chats: Partial< - proto.IConversation & { - lastMessageRecvTimestamp?: number; - } & { - conditional: (bufferedData: BufferedEventData) => boolean; - } - >[], - ) => { - this.logger.verbose('Event received: chats.update'); - const chatsRaw: ChatRaw[] = chats.map((chat) => { - return { id: chat.id, owner: this.instance.wuid }; - }); - - this.logger.verbose('Sending data to webhook in event CHATS_UPDATE'); - await this.sendDataWebhook(Events.CHATS_UPDATE, chatsRaw); - }, - - 'chats.delete': async (chats: string[]) => { - this.logger.verbose('Event received: chats.delete'); - - this.logger.verbose('Deleting chats in database'); - chats.forEach( - async (chat) => - await this.repository.chat.delete({ - where: { owner: this.instance.name, id: chat }, - }), - ); - - this.logger.verbose('Sending data to webhook in event CHATS_DELETE'); - await this.sendDataWebhook(Events.CHATS_DELETE, [...chats]); - }, - }; - - private readonly contactHandle = { - 'contacts.upsert': async (contacts: Contact[], database: Database) => { - this.logger.verbose('Event received: contacts.upsert'); - - this.logger.verbose('Finding contacts in database'); - const contactsRepository = await this.repository.contact.find({ - where: { owner: this.instance.name }, - }); - - this.logger.verbose('Verifying if contacts exists in database to insert'); - const contactsRaw: ContactRaw[] = []; - for await (const contact of contacts) { - if (contactsRepository.find((cr) => cr.id === contact.id)) { - continue; - } - - contactsRaw.push({ - id: contact.id, - pushName: contact?.name || contact?.verifiedName, - profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, - owner: this.instance.name, - }); - } - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); - - this.logger.verbose('Inserting contacts in database'); - await this.repository.contact.insert(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); - }, - - 'contacts.update': async (contacts: Partial[], database: Database) => { - this.logger.verbose('Event received: contacts.update'); - - this.logger.verbose('Verifying if contacts exists in database to update'); - const contactsRaw: ContactRaw[] = []; - for await (const contact of contacts) { - contactsRaw.push({ - id: contact.id, - pushName: contact?.name ?? contact?.verifiedName, - profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, - owner: this.instance.name, - }); - } - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); - - this.logger.verbose('Updating contacts in database'); - await this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); - }, - }; - - private readonly messageHandle = { - 'messaging-history.set': async ( - { - messages, - chats, - isLatest, - }: { - chats: Chat[]; - contacts: Contact[]; - messages: proto.IWebMessageInfo[]; - isLatest: boolean; - }, - database: Database, - ) => { - this.logger.verbose('Event received: messaging-history.set'); - if (isLatest) { - this.logger.verbose('isLatest defined as true'); - const chatsRaw: ChatRaw[] = chats.map((chat) => { - return { - id: chat.id, - owner: this.instance.name, - lastMsgTimestamp: chat.lastMessageRecvTimestamp, - }; - }); - - this.logger.verbose('Sending data to webhook in event CHATS_SET'); - await this.sendDataWebhook(Events.CHATS_SET, chatsRaw); - - this.logger.verbose('Inserting chats in database'); - await this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); - } - - const messagesRaw: MessageRaw[] = []; - const messagesRepository = await this.repository.message.find({ - where: { owner: this.instance.name }, - }); - for await (const [, m] of Object.entries(messages)) { - if (!m.message) { - continue; - } - if (messagesRepository.find((mr) => mr.owner === this.instance.name && mr.key.id === m.key.id)) { - continue; - } - - if (Long.isLong(m?.messageTimestamp)) { - m.messageTimestamp = m.messageTimestamp?.toNumber(); - } - - messagesRaw.push({ - key: m.key, - pushName: m.pushName, - participant: m.participant, - message: { ...m.message }, - messageType: getContentType(m.message), - messageTimestamp: m.messageTimestamp as number, - owner: this.instance.name, - }); - } - - this.logger.verbose('Sending data to webhook in event MESSAGES_SET'); - this.sendDataWebhook(Events.MESSAGES_SET, [...messagesRaw]); - - messages = undefined; - }, - - 'messages.upsert': async ( - { - messages, - type, - }: { - messages: proto.IWebMessageInfo[]; - type: MessageUpsertType; - }, - database: Database, - settings: SettingsRaw, - ) => { - this.logger.verbose('Event received: messages.upsert'); - const received = messages[0]; - - if ( - type !== 'notify' || - !received?.message || - received.message?.protocolMessage || - received.message.senderKeyDistributionMessage || - received.message?.pollUpdateMessage - ) { - this.logger.verbose('message rejected'); - return; - } - - if (Long.isLong(received.messageTimestamp)) { - received.messageTimestamp = received.messageTimestamp?.toNumber(); - } - - if (settings?.groups_ignore && received.key.remoteJid.includes('@g.us')) { - this.logger.verbose('group ignored'); - return; - } - - //----------------------Inserido por Amilton------------------------ - let messageRaw: MessageRaw; - - const globalWebhook = this.configService.get('WEBHOOK').GLOBAL; - - if (globalWebhook.WEBHOOK_BASE64 === true && received?.message.documentMessage || received?.message.imageMessage ) { - const buffer = await downloadMediaMessage( - { key: received.key, message: received?.message }, - 'buffer', - {}, - { - logger: P({ level: 'error' }), - reuploadRequest: this.client.updateMediaMessage, - }, - ); - - messageRaw = { - key: received.key, - pushName: received.pushName, - message: { - ...received.message, - base64: buffer ? buffer.toString('base64') : undefined, - }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - } else { - messageRaw = { - key: received.key, - pushName: received.pushName, - message: { ...received.message }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - } - - // Fim----------------------Inserido por Amilton------------------------ - - // --------------------------Ocultado por Amilton----------------------- - /* - const messageRaw: MessageRaw = { - key: received.key, - pushName: received.pushName, - message: { ...received.message }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; - */ - - if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { - await this.client.readMessages([received.key]); - } - - - if (this.localSettings.read_status && received.key.id === 'status@broadcast') { - await this.client.readMessages([received.key]); - } - - this.logger.log(messageRaw); - - this.logger.verbose('Sending data to webhook in event MESSAGES_UPSERT'); - await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - - if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( - Events.MESSAGES_UPSERT, - { instanceName: this.instance.name }, - messageRaw, - ); - } - - if (this.localTypebot.enabled) { - if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { - await this.typebotService.sendTypebot( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); - } - } - - if (this.localChamaai.enabled && messageRaw.key.fromMe === false) { - await this.chamaaiService.sendChamaai( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); - } - - this.logger.verbose('Inserting message in database'); - await this.repository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); - - this.logger.verbose('Verifying contact from message'); - const contact = await this.repository.contact.find({ - where: { owner: this.instance.name, id: received.key.remoteJid }, - }); - - const contactRaw: ContactRaw = { - id: received.key.remoteJid, - pushName: received.pushName, - profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, - owner: this.instance.name, - }; - - if (contactRaw.id === 'status@broadcast') { - this.logger.verbose('Contact is status@broadcast'); - return; - } - - if (contact?.length) { - this.logger.verbose('Contact found in database'); - const contactRaw: ContactRaw = { - id: received.key.remoteJid, - pushName: contact[0].pushName, - profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl, - owner: this.instance.name, - }; - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw); - - if (this.localChatwoot.enabled) { - await this.chatwootService.eventWhatsapp( - Events.CONTACTS_UPDATE, - { instanceName: this.instance.name }, - contactRaw, - ); - } - - this.logger.verbose('Updating contact in database'); - await this.repository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); - return; - } - - this.logger.verbose('Contact not found in database'); - - this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); - - this.logger.verbose('Inserting contact in database'); - await this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); - }, - - 'messages.update': async (args: WAMessageUpdate[], database: Database, settings: SettingsRaw) => { - this.logger.verbose('Event received: messages.update'); - const status: Record = { - 0: 'ERROR', - 1: 'PENDING', - 2: 'SERVER_ACK', - 3: 'DELIVERY_ACK', - 4: 'READ', - 5: 'PLAYED', - }; - for await (const { key, update } of args) { - if (settings?.groups_ignore && key.remoteJid.includes('@g.us')) { - this.logger.verbose('group ignored'); - return; - } - if (key.remoteJid !== 'status@broadcast' && !key?.remoteJid?.match(/(:\d+)/)) { - this.logger.verbose('Message update is valid'); - - let pollUpdates: any; - if (update.pollUpdates) { - this.logger.verbose('Poll update found'); - - this.logger.verbose('Getting poll message'); - const pollCreation = await this.getMessage(key); - this.logger.verbose(pollCreation); - - if (pollCreation) { - this.logger.verbose('Getting aggregate votes in poll message'); - pollUpdates = getAggregateVotesInPollMessage({ - message: pollCreation as proto.IMessage, - pollUpdates: update.pollUpdates, - }); - } - } - - if (status[update.status] === 'READ' && !key.fromMe) return; - - if (update.message === null && update.status === undefined) { - this.logger.verbose('Message deleted'); - - this.logger.verbose('Sending data to webhook in event MESSAGE_DELETE'); - await this.sendDataWebhook(Events.MESSAGES_DELETE, key); - - const message: MessageUpdateRaw = { - ...key, - status: 'DELETED', - datetime: Date.now(), - owner: this.instance.name, - }; - - this.logger.verbose(message); - - this.logger.verbose('Inserting message in database'); - await this.repository.messageUpdate.insert( - [message], - this.instance.name, - database.SAVE_DATA.MESSAGE_UPDATE, - ); - return; - } - - const message: MessageUpdateRaw = { - ...key, - status: status[update.status], - datetime: Date.now(), - owner: this.instance.name, - pollUpdates, - }; - - this.logger.verbose(message); - - this.logger.verbose('Sending data to webhook in event MESSAGES_UPDATE'); - await this.sendDataWebhook(Events.MESSAGES_UPDATE, message); - - this.logger.verbose('Inserting message in database'); - await this.repository.messageUpdate.insert([message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE); - } - } - }, - }; - - private readonly groupHandler = { - 'groups.upsert': (groupMetadata: GroupMetadata[]) => { - this.logger.verbose('Event received: groups.upsert'); - - this.logger.verbose('Sending data to webhook in event GROUPS_UPSERT'); - this.sendDataWebhook(Events.GROUPS_UPSERT, groupMetadata); - }, - - 'groups.update': (groupMetadataUpdate: Partial[]) => { - this.logger.verbose('Event received: groups.update'); - - this.logger.verbose('Sending data to webhook in event GROUPS_UPDATE'); - this.sendDataWebhook(Events.GROUPS_UPDATE, groupMetadataUpdate); - }, - - 'group-participants.update': (participantsUpdate: { - id: string; - participants: string[]; - action: ParticipantAction; - }) => { - this.logger.verbose('Event received: group-participants.update'); - - this.logger.verbose('Sending data to webhook in event GROUP_PARTICIPANTS_UPDATE'); - this.sendDataWebhook(Events.GROUP_PARTICIPANTS_UPDATE, participantsUpdate); - }, - }; - - private eventHandler() { - this.logger.verbose('Initializing event handler'); - this.client.ev.process(async (events) => { - if (!this.endSession) { - const database = this.configService.get('DATABASE'); - const settings = await this.findSettings(); - - if (events.call) { - this.logger.verbose('Listening event: call'); - const call = events.call[0]; - - if (settings?.reject_call && call.status == 'offer') { - this.logger.verbose('Rejecting call'); - this.client.rejectCall(call.id, call.from); - } - - if (settings?.msg_call.trim().length > 0 && call.status == 'offer') { - this.logger.verbose('Sending message in call'); - const msg = await this.client.sendMessage(call.from, { - text: settings.msg_call, - }); - - this.logger.verbose('Sending data to event messages.upsert'); - this.client.ev.emit('messages.upsert', { - messages: [msg], - type: 'notify', - }); - } - - this.logger.verbose('Sending data to webhook in event CALL'); - this.sendDataWebhook(Events.CALL, call); - } - - if (events['connection.update']) { - this.logger.verbose('Listening event: connection.update'); - this.connectionUpdate(events['connection.update']); - } - - if (events['creds.update']) { - this.logger.verbose('Listening event: creds.update'); - this.instance.authState.saveCreds(); - } - - if (events['messaging-history.set']) { - this.logger.verbose('Listening event: messaging-history.set'); - const payload = events['messaging-history.set']; - this.messageHandle['messaging-history.set'](payload, database); - } - - if (events['messages.upsert']) { - this.logger.verbose('Listening event: messages.upsert'); - const payload = events['messages.upsert']; - this.messageHandle['messages.upsert'](payload, database, settings); - } - - if (events['messages.update']) { - this.logger.verbose('Listening event: messages.update'); - const payload = events['messages.update']; - this.messageHandle['messages.update'](payload, database, settings); - } - - if (events['presence.update']) { - this.logger.verbose('Listening event: presence.update'); - const payload = events['presence.update']; - - if (settings.groups_ignore && payload.id.includes('@g.us')) { - this.logger.verbose('group ignored'); - return; - } - this.sendDataWebhook(Events.PRESENCE_UPDATE, payload); - } - - if (!settings?.groups_ignore) { - if (events['groups.upsert']) { - this.logger.verbose('Listening event: groups.upsert'); - const payload = events['groups.upsert']; - this.groupHandler['groups.upsert'](payload); - } - - if (events['groups.update']) { - this.logger.verbose('Listening event: groups.update'); - const payload = events['groups.update']; - this.groupHandler['groups.update'](payload); - } - - if (events['group-participants.update']) { - this.logger.verbose('Listening event: group-participants.update'); - const payload = events['group-participants.update']; - this.groupHandler['group-participants.update'](payload); - } - } - - if (events['chats.upsert']) { - this.logger.verbose('Listening event: chats.upsert'); - const payload = events['chats.upsert']; - this.chatHandle['chats.upsert'](payload, database); - } - - if (events['chats.update']) { - this.logger.verbose('Listening event: chats.update'); - const payload = events['chats.update']; - this.chatHandle['chats.update'](payload); - } - - if (events['chats.delete']) { - this.logger.verbose('Listening event: chats.delete'); - const payload = events['chats.delete']; - this.chatHandle['chats.delete'](payload); - } - - if (events['contacts.upsert']) { - this.logger.verbose('Listening event: contacts.upsert'); - const payload = events['contacts.upsert']; - this.contactHandle['contacts.upsert'](payload, database); - } - - if (events['contacts.update']) { - this.logger.verbose('Listening event: contacts.update'); - const payload = events['contacts.update']; - this.contactHandle['contacts.update'](payload, database); - } - } - }); - } - - // Check if the number is MX or AR - private formatMXOrARNumber(jid: string): string { - const countryCode = jid.substring(0, 2); - - if (Number(countryCode) === 52 || Number(countryCode) === 54) { - if (jid.length === 13) { - const number = countryCode + jid.substring(3); - return number; - } - - return jid; - } - return jid; - } - - // Check if the number is br - private formatBRNumber(jid: string) { - const regexp = new RegExp(/^(\d{2})(\d{2})\d{1}(\d{8})$/); - if (regexp.test(jid)) { - const match = regexp.exec(jid); - if (match && match[1] === '55') { - const joker = Number.parseInt(match[3][0]); - const ddd = Number.parseInt(match[2]); - if (joker < 7 || ddd < 31) { - return match[0]; - } - return match[1] + match[2] + match[3]; - } - return jid; - } else { - return jid; - } - } - - private createJid(number: string): string { - this.logger.verbose('Creating jid with number: ' + number); - - if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) { - this.logger.verbose('Number already contains @g.us or @s.whatsapp.net'); - return number; - } - - if (number.includes('@broadcast')) { - this.logger.verbose('Number already contains @broadcast'); - return number; - } - - number = number - ?.replace(/\s/g, '') - .replace(/\+/g, '') - .replace(/\(/g, '') - .replace(/\)/g, '') - .split(':')[0] - .split('@')[0]; - - if (number.includes('-') && number.length >= 24) { - this.logger.verbose('Jid created is group: ' + `${number}@g.us`); - number = number.replace(/[^\d-]/g, ''); - return `${number}@g.us`; - } - - number = number.replace(/\D/g, ''); - - if (number.length >= 18) { - this.logger.verbose('Jid created is group: ' + `${number}@g.us`); - number = number.replace(/[^\d-]/g, ''); - return `${number}@g.us`; - } - - this.logger.verbose('Jid created is whatsapp: ' + `${number}@s.whatsapp.net`); - return `${number}@s.whatsapp.net`; - } - - public async profilePicture(number: string) { - const jid = this.createJid(number); - - this.logger.verbose('Getting profile picture with jid: ' + jid); - try { - this.logger.verbose('Getting profile picture url'); - return { - wuid: jid, - profilePictureUrl: await this.client.profilePictureUrl(jid, 'image'), - }; - } catch (error) { - this.logger.verbose('Profile picture not found'); - return { - wuid: jid, - profilePictureUrl: null, - }; - } - } - - public async getStatus(number: string) { - const jid = this.createJid(number); - - this.logger.verbose('Getting profile status with jid:' + jid); - try { - this.logger.verbose('Getting status'); - return { - wuid: jid, - status: (await this.client.fetchStatus(jid))?.status, - }; - } catch (error) { - this.logger.verbose('Status not found'); - return { - wuid: jid, - status: null, - }; - } - } - - public async fetchProfile(instanceName: string, number?: string) { - const jid = number ? this.createJid(number) : this.client?.user?.id; - - this.logger.verbose('Getting profile with jid: ' + jid); - try { - this.logger.verbose('Getting profile info'); - - if (number) { - const info = (await this.whatsappNumber({ numbers: [jid] }))?.shift(); - const picture = await this.profilePicture(info?.jid); - const status = await this.getStatus(info?.jid); - const business = await this.fetchBusinessProfile(info?.jid); - - return { - wuid: info?.jid || jid, - name: info?.name, - numberExists: info?.exists, - picture: picture?.profilePictureUrl, - status: status?.status, - isBusiness: business.isBusiness, - email: business?.email, - description: business?.description, - website: business?.website?.shift(), - }; - } else { - const info = await waMonitor.instanceInfo(instanceName); - const business = await this.fetchBusinessProfile(jid); - - return { - wuid: jid, - name: info?.instance?.profileName, - numberExists: true, - picture: info?.instance?.profilePictureUrl, - status: info?.instance?.profileStatus, - isBusiness: business.isBusiness, - email: business?.email, - description: business?.description, - website: business?.website?.shift(), - }; - } - } catch (error) { - this.logger.verbose('Profile not found'); - return { - wuid: jid, - name: null, - picture: null, - status: null, - os: null, - isBusiness: false, - }; - } - } - - private async sendMessageWithTyping(number: string, message: T, options?: Options) { - this.logger.verbose('Sending message with typing'); - - this.logger.verbose(`Check if number "${number}" is WhatsApp`); - const isWA = (await this.whatsappNumber({ numbers: [number] }))?.shift(); - - this.logger.verbose(`Exists: "${isWA.exists}" | jid: ${isWA.jid}`); - if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) { - throw new BadRequestException(isWA); - } - - const sender = isWA.jid; - - try { - if (options?.delay) { - this.logger.verbose('Delaying message'); - - await this.client.presenceSubscribe(sender); - this.logger.verbose('Subscribing to presence'); - - await this.client.sendPresenceUpdate(options?.presence ?? 'composing', sender); - this.logger.verbose('Sending presence update: ' + options?.presence ?? 'composing'); - - await delay(options.delay); - this.logger.verbose('Set delay: ' + options.delay); - - await this.client.sendPresenceUpdate('paused', sender); - this.logger.verbose('Sending presence update: paused'); - } - - const linkPreview = options?.linkPreview != false ? undefined : false; - - let quoted: WAMessage; - - if (options?.quoted) { - const m = options?.quoted; - - const msg = m?.message ? m : ((await this.getMessage(m.key, true)) as proto.IWebMessageInfo); - - if (!msg) { - throw 'Message not found'; - } - - quoted = msg; - this.logger.verbose('Quoted message'); - } - - let mentions: string[]; - if (isJidGroup(sender)) { - try { - const group = await this.findGroup({ groupJid: sender }, 'inner'); - - if (!group) { - throw new NotFoundException('Group not found'); - } - - if (options?.mentions) { - this.logger.verbose('Mentions defined'); - - if (options.mentions?.everyOne) { - this.logger.verbose('Mentions everyone'); - - this.logger.verbose('Getting group metadata'); - mentions = group.participants.map((participant) => participant.id); - this.logger.verbose('Getting group metadata for mentions'); - } else if (options.mentions?.mentioned?.length) { - this.logger.verbose('Mentions manually defined'); - mentions = options.mentions.mentioned.map((mention) => { - const jid = this.createJid(mention); - if (isJidGroup(jid)) { - return null; - } - return jid; - }); - } - } - } catch (error) { - throw new NotFoundException('Group not found'); - } - } - - const messageSent = await (async () => { - const option = { - quoted, - }; - - if ( - !message['audio'] && - !message['poll'] && - !message['sticker'] && - !message['conversation'] && - sender !== 'status@broadcast' - ) { - if (!message['audio']) { - this.logger.verbose('Sending message'); - return await this.client.sendMessage( - sender, - { - forward: { - key: { remoteJid: this.instance.wuid, fromMe: true }, - message, - }, - mentions, - }, - option as unknown as MiscMessageGenerationOptions, - ); - } - } - - if (message['conversation']) { - this.logger.verbose('Sending message'); - return await this.client.sendMessage( - sender, - { - text: message['conversation'], - mentions, - linkPreview: linkPreview, - } as unknown as AnyMessageContent, - option as unknown as MiscMessageGenerationOptions, - ); - } - - if (sender === 'status@broadcast') { - this.logger.verbose('Sending message'); - return await this.client.sendMessage( - sender, - message['status'].content as unknown as AnyMessageContent, - { - backgroundColor: message['status'].option.backgroundColor, - font: message['status'].option.font, - statusJidList: message['status'].option.statusJidList, - } as unknown as MiscMessageGenerationOptions, - ); - } - - this.logger.verbose('Sending message'); - return await this.client.sendMessage( - sender, - message as unknown as AnyMessageContent, - option as unknown as MiscMessageGenerationOptions, - ); - })(); - - const messageRaw: MessageRaw = { - key: messageSent.key, - pushName: messageSent.pushName, - message: { ...messageSent.message }, - messageType: getContentType(messageSent.message), - messageTimestamp: messageSent.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(messageSent.key.id), - }; - - this.logger.log(messageRaw); - - this.logger.verbose('Sending data to webhook in event SEND_MESSAGE'); - await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw); - - if (this.localChatwoot.enabled) { - this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw); - } - - this.logger.verbose('Inserting message in database'); - await this.repository.message.insert( - [messageRaw], - this.instance.name, - this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE, - ); - - return messageSent; - } catch (error) { - this.logger.error(error); - throw new BadRequestException(error.toString()); - } - } - - // Instance Controller - public get connectionStatus() { - this.logger.verbose('Getting connection status'); - return this.stateConnection; - } - - // Send Message Controller - public async textMessage(data: SendTextDto) { - this.logger.verbose('Sending text message'); - return await this.sendMessageWithTyping( - data.number, - { - conversation: data.textMessage.text, - }, - data?.options, - ); - } - - public async pollMessage(data: SendPollDto) { - this.logger.verbose('Sending poll message'); - return await this.sendMessageWithTyping( - data.number, - { - poll: { - name: data.pollMessage.name, - selectableCount: data.pollMessage.selectableCount, - values: data.pollMessage.values, - }, - }, - data?.options, - ); - } - - private async formatStatusMessage(status: StatusMessage) { - this.logger.verbose('Formatting status message'); - - if (!status.type) { - throw new BadRequestException('Type is required'); - } - - if (!status.content) { - throw new BadRequestException('Content is required'); - } - - if (status.allContacts) { - this.logger.verbose('All contacts defined as true'); - - this.logger.verbose('Getting contacts from database'); - const contacts = await this.repository.contact.find({ - where: { owner: this.instance.name }, - }); - - if (!contacts.length) { - throw new BadRequestException('Contacts not found'); - } - - this.logger.verbose('Getting contacts with push name'); - status.statusJidList = contacts.filter((contact) => contact.pushName).map((contact) => contact.id); - - this.logger.verbose(status.statusJidList); - } - - if (!status.statusJidList?.length && !status.allContacts) { - throw new BadRequestException('StatusJidList is required'); - } - - if (status.type === 'text') { - this.logger.verbose('Type defined as text'); - - if (!status.backgroundColor) { - throw new BadRequestException('Background color is required'); - } - - if (!status.font) { - throw new BadRequestException('Font is required'); - } - - return { - content: { - text: status.content, - }, - option: { - backgroundColor: status.backgroundColor, - font: status.font, - statusJidList: status.statusJidList, - }, - }; - } - if (status.type === 'image') { - this.logger.verbose('Type defined as image'); - - return { - content: { - image: { - url: status.content, - }, - caption: status.caption, - }, - option: { - statusJidList: status.statusJidList, - }, - }; - } - if (status.type === 'video') { - this.logger.verbose('Type defined as video'); - - return { - content: { - video: { - url: status.content, - }, - caption: status.caption, - }, - option: { - statusJidList: status.statusJidList, - }, - }; - } - if (status.type === 'audio') { - this.logger.verbose('Type defined as audio'); - - this.logger.verbose('Processing audio'); - const convert = await this.processAudio(status.content, 'status@broadcast'); - if (typeof convert === 'string') { - this.logger.verbose('Audio processed'); - const audio = fs.readFileSync(convert).toString('base64'); - - const result = { - content: { - audio: Buffer.from(audio, 'base64'), - ptt: true, - mimetype: 'audio/mp4', - }, - option: { - statusJidList: status.statusJidList, - }, - }; - - fs.unlinkSync(convert); - - return result; - } else { - throw new InternalServerErrorException(convert); - } - } - - throw new BadRequestException('Type not found'); - } - - public async statusMessage(data: SendStatusDto) { - this.logger.verbose('Sending status message'); - const status = await this.formatStatusMessage(data.statusMessage); - - return await this.sendMessageWithTyping('status@broadcast', { - status, - }); - } - - private async prepareMediaMessage(mediaMessage: MediaMessage) { - try { - this.logger.verbose('Preparing media message'); - const prepareMedia = await prepareWAMessageMedia( - { - [mediaMessage.mediatype]: isURL(mediaMessage.media) - ? { url: mediaMessage.media } - : Buffer.from(mediaMessage.media, 'base64'), - } as any, - { upload: this.client.waUploadToServer }, - ); - - const mediaType = mediaMessage.mediatype + 'Message'; - this.logger.verbose('Media type: ' + mediaType); - - if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) { - this.logger.verbose('If media type is document and file name is not defined then'); - const regex = new RegExp(/.*\/(.+?)\./); - const arrayMatch = regex.exec(mediaMessage.media); - mediaMessage.fileName = arrayMatch[1]; - this.logger.verbose('File name: ' + mediaMessage.fileName); - } - // *inserido francis inicio - let mimetype: string; - // *inserido francis final - - - if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) { - mediaMessage.fileName = 'image.png'; - // inserido francis inicio - mimetype = 'image/png'; - // inserido francis inicio - - } - - if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) { - mediaMessage.fileName = 'video.mp4'; - // inserido francis inicio - mimetype = 'video/mp4'; - // inserido francis final - } - - // ocultado francis inicio - // let mimetype: string; - - - // if (isURL(mediaMessage.media)) { - // mimetype = getMIMEType(mediaMessage.media); - // } else { - // mimetype = getMIMEType(mediaMessage.fileName); - // } - // ocultado francis final - - this.logger.verbose('Mimetype: ' + mimetype); - - prepareMedia[mediaType].caption = mediaMessage?.caption; - prepareMedia[mediaType].mimetype = mimetype; - prepareMedia[mediaType].fileName = mediaMessage.fileName; - - if (mediaMessage.mediatype === 'video') { - this.logger.verbose('Is media type video then set gif playback as false'); - prepareMedia[mediaType].jpegThumbnail = Uint8Array.from( - readFileSync(join(process.cwd(), 'public', 'images', 'video-cover.png')), - ); - prepareMedia[mediaType].gifPlayback = false; - } - - this.logger.verbose('Generating wa message from content'); - return generateWAMessageFromContent( - '', - { [mediaType]: { ...prepareMedia[mediaType] } }, - { userJid: this.instance.wuid }, - ); - } catch (error) { - this.logger.error(error); - throw new InternalServerErrorException(error?.toString() || error); - } - } - - private async convertToWebP(image: string, number: string) { - try { - this.logger.verbose('Converting image to WebP to sticker'); - - let imagePath: string; - const hash = `${number}-${new Date().getTime()}`; - this.logger.verbose('Hash to image name: ' + hash); - - const outputPath = `${join(this.storePath, 'temp', `${hash}.webp`)}`; - this.logger.verbose('Output path: ' + outputPath); - - if (isBase64(image)) { - this.logger.verbose('Image is base64'); - - const base64Data = image.replace(/^data:image\/(jpeg|png|gif);base64,/, ''); - const imageBuffer = Buffer.from(base64Data, 'base64'); - imagePath = `${join(this.storePath, 'temp', `temp-${hash}.png`)}`; - this.logger.verbose('Image path: ' + imagePath); - - await sharp(imageBuffer).toFile(imagePath); - this.logger.verbose('Image created'); - } else { - this.logger.verbose('Image is url'); - - const timestamp = new Date().getTime(); - const url = `${image}?timestamp=${timestamp}`; - this.logger.verbose('including timestamp in url: ' + url); - - const response = await axios.get(url, { responseType: 'arraybuffer' }); - this.logger.verbose('Getting image from url'); - - const imageBuffer = Buffer.from(response.data, 'binary'); - imagePath = `${join(this.storePath, 'temp', `temp-${hash}.png`)}`; - this.logger.verbose('Image path: ' + imagePath); - - await sharp(imageBuffer).toFile(imagePath); - this.logger.verbose('Image created'); - } - - await sharp(imagePath).webp().toFile(outputPath); - this.logger.verbose('Image converted to WebP'); - - fs.unlinkSync(imagePath); - this.logger.verbose('Temp image deleted'); - - return outputPath; - } catch (error) { - console.error('Erro ao converter a imagem para WebP:', error); - } - } - - public async mediaSticker(data: SendStickerDto) { - this.logger.verbose('Sending media sticker'); - const convert = await this.convertToWebP(data.stickerMessage.image, data.number); - const result = await this.sendMessageWithTyping( - data.number, - { - sticker: { url: convert }, - }, - data?.options, - ); - - fs.unlinkSync(convert); - this.logger.verbose('Converted image deleted'); - - return result; - } - - public async mediaMessage(data: SendMediaDto) { - this.logger.verbose('Sending media message'); - const generate = await this.prepareMediaMessage(data.mediaMessage); - - return await this.sendMessageWithTyping(data.number, { ...generate.message }, data?.options); - } - - public async processAudio(audio: string, number: string) { - this.logger.verbose('Processing audio'); - let tempAudioPath: string; - let outputAudio: string; - - const hash = `${number}-${new Date().getTime()}`; - this.logger.verbose('Hash to audio name: ' + hash); - - if (isURL(audio)) { - this.logger.verbose('Audio is url'); - - outputAudio = `${join(this.storePath, 'temp', `${hash}.mp4`)}`; - tempAudioPath = `${join(this.storePath, 'temp', `temp-${hash}.mp3`)}`; - - this.logger.verbose('Output audio path: ' + outputAudio); - this.logger.verbose('Temp audio path: ' + tempAudioPath); - - const timestamp = new Date().getTime(); - const url = `${audio}?timestamp=${timestamp}`; - - this.logger.verbose('Including timestamp in url: ' + url); - - const response = await axios.get(url, { responseType: 'arraybuffer' }); - this.logger.verbose('Getting audio from url'); - - fs.writeFileSync(tempAudioPath, response.data); - } else { - this.logger.verbose('Audio is base64'); - - outputAudio = `${join(this.storePath, 'temp', `${hash}.mp4`)}`; - tempAudioPath = `${join(this.storePath, 'temp', `temp-${hash}.mp3`)}`; - - this.logger.verbose('Output audio path: ' + outputAudio); - this.logger.verbose('Temp audio path: ' + tempAudioPath); - - const audioBuffer = Buffer.from(audio, 'base64'); - fs.writeFileSync(tempAudioPath, audioBuffer); - this.logger.verbose('Temp audio created'); - } - - this.logger.verbose('Converting audio to mp4'); - return new Promise((resolve, reject) => { - exec(`${ffmpegPath.path} -i ${tempAudioPath} -vn -ab 128k -ar 44100 -f ipod ${outputAudio} -y`, (error) => { - fs.unlinkSync(tempAudioPath); - this.logger.verbose('Temp audio deleted'); - - if (error) reject(error); - - this.logger.verbose('Audio converted to mp4'); - resolve(outputAudio); - }); - }); - } - - public async audioWhatsapp(data: SendAudioDto) { - this.logger.verbose('Sending audio whatsapp'); - - if (!data.options?.encoding && data.options?.encoding !== false) { - data.options.encoding = true; - } - - if (data.options?.encoding) { - const convert = await this.processAudio(data.audioMessage.audio, data.number); - if (typeof convert === 'string') { - const audio = fs.readFileSync(convert).toString('base64'); - const result = this.sendMessageWithTyping( - data.number, - { - audio: Buffer.from(audio, 'base64'), - ptt: true, - mimetype: 'audio/mp4', - }, - { presence: 'recording', delay: data?.options?.delay }, - ); - - fs.unlinkSync(convert); - this.logger.verbose('Converted audio deleted'); - - return result; - } else { - throw new InternalServerErrorException(convert); - } - } - - return await this.sendMessageWithTyping( - data.number, - { - audio: isURL(data.audioMessage.audio) - ? { url: data.audioMessage.audio } - : Buffer.from(data.audioMessage.audio, 'base64'), - ptt: true, - mimetype: 'audio/ogg; codecs=opus', - }, - { presence: 'recording', delay: data?.options?.delay }, - ); - } - - public async buttonMessage(data: SendButtonDto) { - this.logger.verbose('Sending button message'); - const embeddedMedia: any = {}; - let mediatype = 'TEXT'; - - if (data.buttonMessage?.mediaMessage) { - mediatype = data.buttonMessage.mediaMessage?.mediatype.toUpperCase() ?? 'TEXT'; - embeddedMedia.mediaKey = mediatype.toLowerCase() + 'Message'; - const generate = await this.prepareMediaMessage(data.buttonMessage.mediaMessage); - embeddedMedia.message = generate.message[embeddedMedia.mediaKey]; - embeddedMedia.contentText = `*${data.buttonMessage.title}*\n\n${data.buttonMessage.description}`; - } - - const btnItems = { - text: data.buttonMessage.buttons.map((btn) => btn.buttonText), - ids: data.buttonMessage.buttons.map((btn) => btn.buttonId), - }; - - if (!arrayUnique(btnItems.text) || !arrayUnique(btnItems.ids)) { - throw new BadRequestException('Button texts cannot be repeated', 'Button IDs cannot be repeated.'); - } - - return await this.sendMessageWithTyping( - data.number, - { - buttonsMessage: { - text: !embeddedMedia?.mediaKey ? data.buttonMessage.title : undefined, - contentText: embeddedMedia?.contentText ?? data.buttonMessage.description, - footerText: data.buttonMessage?.footerText, - buttons: data.buttonMessage.buttons.map((button) => { - return { - buttonText: { - displayText: button.buttonText, - }, - buttonId: button.buttonId, - type: 1, - }; - }), - headerType: proto.Message.ButtonsMessage.HeaderType[mediatype], - [embeddedMedia?.mediaKey]: embeddedMedia?.message, - }, - }, - data?.options, - ); - } - - public async locationMessage(data: SendLocationDto) { - this.logger.verbose('Sending location message'); - return await this.sendMessageWithTyping( - data.number, - { - locationMessage: { - degreesLatitude: data.locationMessage.latitude, - degreesLongitude: data.locationMessage.longitude, - name: data.locationMessage?.name, - address: data.locationMessage?.address, - }, - }, - data?.options, - ); - } - - public async listMessage(data: SendListDto) { - this.logger.verbose('Sending list message'); - return await this.sendMessageWithTyping( - data.number, - { - listMessage: { - title: data.listMessage.title, - description: data.listMessage.description, - buttonText: data.listMessage?.buttonText, - footerText: data.listMessage?.footerText, - sections: data.listMessage.sections, - listType: 1, - }, - }, - data?.options, - ); - } - - public async contactMessage(data: SendContactDto) { - this.logger.verbose('Sending contact message'); - const message: proto.IMessage = {}; - - const vcard = (contact: ContactMessage) => { - this.logger.verbose('Creating vcard'); - let result = 'BEGIN:VCARD\n' + 'VERSION:3.0\n' + `N:${contact.fullName}\n` + `FN:${contact.fullName}\n`; - - if (contact.organization) { - this.logger.verbose('Organization defined'); - result += `ORG:${contact.organization};\n`; - } - - if (contact.email) { - this.logger.verbose('Email defined'); - result += `EMAIL:${contact.email}\n`; - } - - if (contact.url) { - this.logger.verbose('Url defined'); - result += `URL:${contact.url}\n`; - } - - if (!contact.wuid) { - this.logger.verbose('Wuid defined'); - contact.wuid = this.createJid(contact.phoneNumber); - } - - result += `item1.TEL;waid=${contact.wuid}:${contact.phoneNumber}\n` + 'item1.X-ABLabel:Celular\n' + 'END:VCARD'; - - this.logger.verbose('Vcard created'); - return result; - }; - - if (data.contactMessage.length === 1) { - message.contactMessage = { - displayName: data.contactMessage[0].fullName, - vcard: vcard(data.contactMessage[0]), - }; - } else { - message.contactsArrayMessage = { - displayName: `${data.contactMessage.length} contacts`, - contacts: data.contactMessage.map((contact) => { - return { - displayName: contact.fullName, - vcard: vcard(contact), - }; - }), - }; - } - - return await this.sendMessageWithTyping(data.number, { ...message }, data?.options); - } - - public async reactionMessage(data: SendReactionDto) { - this.logger.verbose('Sending reaction message'); - return await this.sendMessageWithTyping(data.reactionMessage.key.remoteJid, { - reactionMessage: { - key: data.reactionMessage.key, - text: data.reactionMessage.reaction, - }, - }); - } - - // Chat Controller - 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); - - if (isJidGroup(jid)) { - const group = await this.findGroup({ groupJid: jid }, 'inner'); - - if (!group) throw new BadRequestException('Group not found'); - - onWhatsapp.push(new OnWhatsAppDto(group.id, !!group?.id, group?.subject)); - } 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)); - } else { - onWhatsapp.push(new OnWhatsAppDto(result.jid, result.exists)); - } - } - } - - return onWhatsapp; - } - - public async markMessageAsRead(data: ReadMessageDto) { - this.logger.verbose('Marking message as read'); - try { - const keys: proto.IMessageKey[] = []; - data.read_messages.forEach((read) => { - if (isJidGroup(read.remoteJid) || isJidUser(read.remoteJid)) { - keys.push({ - remoteJid: read.remoteJid, - fromMe: read.fromMe, - id: read.id, - }); - } - }); - await this.client.readMessages(keys); - return { message: 'Read messages', read: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Read messages fail', error.toString()); - } - } - - public async getLastMessage(number: string) { - const messages = await this.fetchMessages({ - where: { - key: { - remoteJid: number, - }, - owner: this.instance.name, - }, - }); - - let lastMessage = messages.pop(); - - for (const message of messages) { - if (message.messageTimestamp >= lastMessage.messageTimestamp) { - lastMessage = message; - } - } - - return lastMessage as unknown as LastMessage; - } - - public async archiveChat(data: ArchiveChatDto) { - this.logger.verbose('Archiving chat'); - try { - let last_message = data.lastMessage; - let number = data.chat; - - if (!last_message && number) { - last_message = await this.getLastMessage(number); - } else { - last_message = data.lastMessage; - last_message.messageTimestamp = last_message?.messageTimestamp ?? Date.now(); - number = last_message?.key?.remoteJid; - } - - if (!last_message || Object.keys(last_message).length === 0) { - throw new NotFoundException('Last message not found'); - } - - await this.client.chatModify( - { - archive: data.archive, - lastMessages: [last_message], - }, - this.createJid(number), - ); - - return { - chatId: number, - archived: true, - }; - } catch (error) { - throw new InternalServerErrorException({ - archived: false, - message: ['An error occurred while archiving the chat. Open a calling.', error.toString()], - }); - } - } - - public async deleteMessage(del: DeleteMessage) { - this.logger.verbose('Deleting message'); - try { - return await this.client.sendMessage(del.remoteJid, { delete: del }); - } catch (error) { - throw new InternalServerErrorException('Error while deleting message for everyone', error?.toString()); - } - } - - public async getBase64FromMediaMessage(data: getBase64FromMediaMessageDto) { - this.logger.verbose('Getting base64 from media message'); - try { - const m = data?.message; - const convertToMp4 = data?.convertToMp4 ?? false; - - const msg = m?.message ? m : ((await this.getMessage(m.key, true)) as proto.IWebMessageInfo); - - if (!msg) { - throw 'Message not found'; - } - - for (const subtype of MessageSubtype) { - if (msg.message[subtype]) { - msg.message = msg.message[subtype].message; - } - } - - let mediaMessage: any; - let mediaType: string; - - for (const type of TypeMediaMessage) { - mediaMessage = msg.message[type]; - if (mediaMessage) { - mediaType = type; - break; - } - } - - if (!mediaMessage) { - throw 'The message is not of the media type'; - } - - if (typeof mediaMessage['mediaKey'] === 'object') { - msg.message = JSON.parse(JSON.stringify(msg.message)); - } - - this.logger.verbose('Downloading media message'); - const buffer = await downloadMediaMessage( - { key: msg?.key, message: msg?.message }, - 'buffer', - {}, - { - logger: P({ level: 'error' }), - reuploadRequest: this.client.updateMediaMessage, - }, - ); - const typeMessage = getContentType(msg.message); - - if (convertToMp4 && typeMessage === 'audioMessage') { - this.logger.verbose('Converting audio to mp4'); - const number = msg.key.remoteJid.split('@')[0]; - const convert = await this.processAudio(buffer.toString('base64'), number); - - if (typeof convert === 'string') { - const audio = fs.readFileSync(convert).toString('base64'); - this.logger.verbose('Audio converted to mp4'); - - const result = { - mediaType, - fileName: mediaMessage['fileName'], - caption: mediaMessage['caption'], - size: { - fileLength: mediaMessage['fileLength'], - height: mediaMessage['height'], - width: mediaMessage['width'], - }, - mimetype: 'audio/mp4', - base64: Buffer.from(audio, 'base64').toString('base64'), - }; - - fs.unlinkSync(convert); - this.logger.verbose('Converted audio deleted'); - - this.logger.verbose('Media message downloaded'); - return result; - } - } - - this.logger.verbose('Media message downloaded'); - return { - mediaType, - fileName: mediaMessage['fileName'], - caption: mediaMessage['caption'], - size: { - fileLength: mediaMessage['fileLength'], - height: mediaMessage['height'], - width: mediaMessage['width'], - }, - mimetype: mediaMessage['mimetype'], - base64: buffer.toString('base64'), - }; - } catch (error) { - this.logger.error(error); - throw new BadRequestException(error.toString()); - } - } - - public async fetchContacts(query: ContactQuery) { - this.logger.verbose('Fetching contacts'); - if (query?.where) { - query.where.owner = this.instance.name; - if (query.where?.id) { - query.where.id = this.createJid(query.where.id); - } - } else { - query = { - where: { - owner: this.instance.name, - }, - }; - } - return await this.repository.contact.find(query); - } - - public async fetchMessages(query: MessageQuery) { - this.logger.verbose('Fetching messages'); - if (query?.where) { - if (query.where?.key?.remoteJid) { - query.where.key.remoteJid = this.createJid(query.where.key.remoteJid); - } - query.where.owner = this.instance.name; - } else { - query = { - where: { - owner: this.instance.name, - }, - limit: query?.limit, - }; - } - return await this.repository.message.find(query); - } - - public async fetchStatusMessage(query: MessageUpQuery) { - this.logger.verbose('Fetching status messages'); - if (query?.where) { - if (query.where?.remoteJid) { - query.where.remoteJid = this.createJid(query.where.remoteJid); - } - query.where.owner = this.instance.name; - } else { - query = { - where: { - owner: this.instance.name, - }, - limit: query?.limit, - }; - } - return await this.repository.messageUpdate.find(query); - } - - public async fetchChats() { - this.logger.verbose('Fetching chats'); - return await this.repository.chat.find({ where: { owner: this.instance.name } }); - } - - public async fetchPrivacySettings() { - this.logger.verbose('Fetching privacy settings'); - return await this.client.fetchPrivacySettings(); - } - - public async updatePrivacySettings(settings: PrivacySettingDto) { - this.logger.verbose('Updating privacy settings'); - try { - await this.client.updateReadReceiptsPrivacy(settings.privacySettings.readreceipts); - this.logger.verbose('Read receipts privacy updated'); - - await this.client.updateProfilePicturePrivacy(settings.privacySettings.profile); - this.logger.verbose('Profile picture privacy updated'); - - await this.client.updateStatusPrivacy(settings.privacySettings.status); - this.logger.verbose('Status privacy updated'); - - await this.client.updateOnlinePrivacy(settings.privacySettings.online); - this.logger.verbose('Online privacy updated'); - - await this.client.updateLastSeenPrivacy(settings.privacySettings.last); - this.logger.verbose('Last seen privacy updated'); - - await this.client.updateGroupsAddPrivacy(settings.privacySettings.groupadd); - this.logger.verbose('Groups add privacy updated'); - - this.client?.ws?.close(); - - return { - update: 'success', - data: { - readreceipts: settings.privacySettings.readreceipts, - profile: settings.privacySettings.profile, - status: settings.privacySettings.status, - online: settings.privacySettings.online, - last: settings.privacySettings.last, - groupadd: settings.privacySettings.groupadd, - }, - }; - } catch (error) { - throw new InternalServerErrorException('Error updating privacy settings', error.toString()); - } - } - - public async fetchBusinessProfile(number: string): Promise { - this.logger.verbose('Fetching business profile'); - try { - const jid = number ? this.createJid(number) : this.instance.wuid; - - const profile = await this.client.getBusinessProfile(jid); - this.logger.verbose('Trying to get business profile'); - - if (!profile) { - const info = await this.whatsappNumber({ numbers: [jid] }); - - return { - isBusiness: false, - message: 'Not is business profile', - ...info?.shift(), - }; - } - - this.logger.verbose('Business profile fetched'); - return { - isBusiness: true, - ...profile, - }; - } catch (error) { - throw new InternalServerErrorException('Error updating profile name', error.toString()); - } - } - - public async updateProfileName(name: string) { - this.logger.verbose('Updating profile name to ' + name); - try { - await this.client.updateProfileName(name); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error updating profile name', error.toString()); - } - } - - public async updateProfileStatus(status: string) { - this.logger.verbose('Updating profile status to: ' + status); - try { - await this.client.updateProfileStatus(status); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error updating profile status', error.toString()); - } - } - - public async updateProfilePicture(picture: string) { - this.logger.verbose('Updating profile picture'); - try { - let pic: WAMediaUpload; - if (isURL(picture)) { - this.logger.verbose('Picture is url'); - - const timestamp = new Date().getTime(); - const url = `${picture}?timestamp=${timestamp}`; - this.logger.verbose('Including timestamp in url: ' + url); - - pic = (await axios.get(url, { responseType: 'arraybuffer' })).data; - this.logger.verbose('Getting picture from url'); - } else if (isBase64(picture)) { - this.logger.verbose('Picture is base64'); - pic = Buffer.from(picture, 'base64'); - this.logger.verbose('Getting picture from base64'); - } else { - throw new BadRequestException('"profilePicture" must be a url or a base64'); - } - await this.client.updateProfilePicture(this.instance.wuid, pic); - this.logger.verbose('Profile picture updated'); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error updating profile picture', error.toString()); - } - } - - public async removeProfilePicture() { - this.logger.verbose('Removing profile picture'); - try { - await this.client.removeProfilePicture(this.instance.wuid); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error removing profile picture', error.toString()); - } - } - - // Group - public async createGroup(create: CreateGroupDto) { - this.logger.verbose('Creating group: ' + create.subject); - try { - const participants = (await this.whatsappNumber({ numbers: create.participants })) - .filter((participant) => participant.exists) - .map((participant) => participant.jid); - const { id } = await this.client.groupCreate(create.subject, participants); - this.logger.verbose('Group created: ' + id); - - if (create?.description) { - this.logger.verbose('Updating group description: ' + create.description); - await this.client.groupUpdateDescription(id, create.description); - } - - if (create?.promoteParticipants) { - this.logger.verbose('Prometing group participants: ' + participants); - await this.updateGParticipant({ - groupJid: id, - action: 'promote', - participants: participants, - }); - } - - this.logger.verbose('Getting group metadata'); - const group = await this.client.groupMetadata(id); - - return group; - } catch (error) { - this.logger.error(error); - throw new InternalServerErrorException('Error creating group', error.toString()); - } - } - - public async updateGroupPicture(picture: GroupPictureDto) { - this.logger.verbose('Updating group picture'); - try { - let pic: WAMediaUpload; - if (isURL(picture.image)) { - this.logger.verbose('Picture is url'); - - const timestamp = new Date().getTime(); - const url = `${picture.image}?timestamp=${timestamp}`; - this.logger.verbose('Including timestamp in url: ' + url); - - pic = (await axios.get(url, { responseType: 'arraybuffer' })).data; - this.logger.verbose('Getting picture from url'); - } else if (isBase64(picture.image)) { - this.logger.verbose('Picture is base64'); - pic = Buffer.from(picture.image, 'base64'); - this.logger.verbose('Getting picture from base64'); - } else { - throw new BadRequestException('"profilePicture" must be a url or a base64'); - } - await this.client.updateProfilePicture(picture.groupJid, pic); - this.logger.verbose('Group picture updated'); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error update group picture', error.toString()); - } - } - - public async updateGroupSubject(data: GroupSubjectDto) { - this.logger.verbose('Updating group subject to: ' + data.subject); - try { - await this.client.groupUpdateSubject(data.groupJid, data.subject); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error updating group subject', error.toString()); - } - } - - public async updateGroupDescription(data: GroupDescriptionDto) { - this.logger.verbose('Updating group description to: ' + data.description); - try { - await this.client.groupUpdateDescription(data.groupJid, data.description); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error updating group description', error.toString()); - } - } - - public async findGroup(id: GroupJid, reply: 'inner' | 'out' = 'out') { - this.logger.verbose('Fetching group'); - try { - return await this.client.groupMetadata(id.groupJid); - } catch (error) { - if (reply === 'inner') { - return; - } - throw new NotFoundException('Error fetching group', error.toString()); - } - } - - public async fetchAllGroups(getParticipants: GetParticipant) { - this.logger.verbose('Fetching all groups'); - try { - const fetch = Object.values(await this.client.groupFetchAllParticipating()); - - const groups = fetch.map((group) => { - const result = { - id: group.id, - subject: group.subject, - subjectOwner: group.subjectOwner, - subjectTime: group.subjectTime, - size: group.size, - creation: group.creation, - owner: group.owner, - desc: group.desc, - descId: group.descId, - restrict: group.restrict, - announce: group.announce, - }; - - if (getParticipants.getParticipants == 'true') { - result['participants'] = group.participants; - } - - return result; - }); - - return groups; - } catch (error) { - throw new NotFoundException('Error fetching group', error.toString()); - } - } - - public async inviteCode(id: GroupJid) { - this.logger.verbose('Fetching invite code for group: ' + id.groupJid); - try { - const code = await this.client.groupInviteCode(id.groupJid); - return { inviteUrl: `https://chat.whatsapp.com/${code}`, inviteCode: code }; - } catch (error) { - throw new NotFoundException('No invite code', error.toString()); - } - } - - public async inviteInfo(id: GroupInvite) { - this.logger.verbose('Fetching invite info for code: ' + id.inviteCode); - try { - return await this.client.groupGetInviteInfo(id.inviteCode); - } catch (error) { - throw new NotFoundException('No invite info', id.inviteCode); - } - } - - public async sendInvite(id: GroupSendInvite) { - this.logger.verbose('Sending invite for group: ' + id.groupJid); - try { - const inviteCode = await this.inviteCode({ groupJid: id.groupJid }); - this.logger.verbose('Getting invite code: ' + inviteCode.inviteCode); - - const inviteUrl = inviteCode.inviteUrl; - this.logger.verbose('Invite url: ' + inviteUrl); - - const numbers = id.numbers.map((number) => this.createJid(number)); - const description = id.description ?? ''; - - const msg = `${description}\n\n${inviteUrl}`; - - const message = { - conversation: msg, - }; - - for await (const number of numbers) { - await this.sendMessageWithTyping(number, message); - } - - this.logger.verbose('Invite sent for numbers: ' + numbers.join(', ')); - - return { send: true, inviteUrl }; - } catch (error) { - throw new NotFoundException('No send invite'); - } - } - - public async revokeInviteCode(id: GroupJid) { - this.logger.verbose('Revoking invite code for group: ' + id.groupJid); - try { - const inviteCode = await this.client.groupRevokeInvite(id.groupJid); - return { revoked: true, inviteCode }; - } catch (error) { - throw new NotFoundException('Revoke error', error.toString()); - } - } - - public async findParticipants(id: GroupJid) { - this.logger.verbose('Fetching participants for group: ' + id.groupJid); - try { - const participants = (await this.client.groupMetadata(id.groupJid)).participants; - return { participants }; - } catch (error) { - throw new NotFoundException('No participants', error.toString()); - } - } - - public async updateGParticipant(update: GroupUpdateParticipantDto) { - this.logger.verbose('Updating participants'); - try { - const participants = update.participants.map((p) => this.createJid(p)); - const updateParticipants = await this.client.groupParticipantsUpdate( - update.groupJid, - participants, - update.action, - ); - return { updateParticipants: updateParticipants }; - } catch (error) { - throw new BadRequestException('Error updating participants', error.toString()); - } - } - - public async updateGSetting(update: GroupUpdateSettingDto) { - this.logger.verbose('Updating setting for group: ' + update.groupJid); - try { - const updateSetting = await this.client.groupSettingUpdate(update.groupJid, update.action); - return { updateSetting: updateSetting }; - } catch (error) { - throw new BadRequestException('Error updating setting', error.toString()); - } - } - - public async toggleEphemeral(update: GroupToggleEphemeralDto) { - this.logger.verbose('Toggling ephemeral for group: ' + update.groupJid); - try { - await this.client.groupToggleEphemeral(update.groupJid, update.expiration); - return { success: true }; - } catch (error) { - throw new BadRequestException('Error updating setting', error.toString()); - } - } - - public async leaveGroup(id: GroupJid) { - this.logger.verbose('Leaving group: ' + id.groupJid); - try { - await this.client.groupLeave(id.groupJid); - return { groupJid: id.groupJid, leave: true }; - } catch (error) { - throw new BadRequestException('Unable to leave the group', error.toString()); - } - } -} \ No newline at end of file