import { Contact, Message } from '@prisma/client'; import axios from 'axios'; import { WASocket } from 'baileys'; import { isURL } from 'class-validator'; import EventEmitter2 from 'eventemitter2'; import { join } from 'path'; import { v4 } from 'uuid'; import { Auth, Chatwoot, ConfigService, HttpServer, Log, Rabbitmq, Sqs, Webhook, Websocket, } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { ROOT_DIR } from '../../config/path.config'; import { NotFoundException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; import { ProxyDto } from '../dto/proxy.dto'; import { SettingsDto } from '../dto/settings.dto'; import { WebhookDto } from '../dto/webhook.dto'; import { ChatwootDto } from '../integrations/chatwoot/dto/chatwoot.dto'; import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service'; import { DifyService } from '../integrations/dify/services/dify.service'; import { OpenaiService } from '../integrations/openai/services/openai.service'; import { RabbitmqDto } from '../integrations/rabbitmq/dto/rabbitmq.dto'; import { getAMQP, removeQueues } from '../integrations/rabbitmq/libs/amqp.server'; import { SqsDto } from '../integrations/sqs/dto/sqs.dto'; import { getSQS, removeQueues as removeQueuesSQS } from '../integrations/sqs/libs/sqs.server'; import { TypebotService } from '../integrations/typebot/services/typebot.service'; import { WebsocketDto } from '../integrations/websocket/dto/websocket.dto'; import { getIO } from '../integrations/websocket/libs/socket.server'; import { PrismaRepository, Query } from '../repository/repository.service'; import { waMonitor } from '../server.module'; import { Events, wa } from '../types/wa.types'; import { CacheService } from './cache.service'; export class ChannelStartupService { constructor( public readonly configService: ConfigService, public readonly eventEmitter: EventEmitter2, public readonly prismaRepository: PrismaRepository, public readonly chatwootCache: CacheService, ) {} public readonly logger = new Logger(ChannelStartupService.name); public client: WASocket; public readonly instance: wa.Instance = {}; public readonly localWebhook: wa.LocalWebHook = {}; public readonly localChatwoot: wa.LocalChatwoot = {}; public readonly localWebsocket: wa.LocalWebsocket = {}; public readonly localRabbitmq: wa.LocalRabbitmq = {}; public readonly localSqs: wa.LocalSqs = {}; public readonly localProxy: wa.LocalProxy = {}; public readonly localSettings: wa.LocalSettings = {}; public readonly storePath = join(ROOT_DIR, 'store'); public chatwootService = new ChatwootService( waMonitor, this.configService, this.prismaRepository, this.chatwootCache, ); public typebotService = new TypebotService(waMonitor, this.configService, this.prismaRepository); public openaiService = new OpenaiService(waMonitor, this.configService, this.prismaRepository); public difyService = new DifyService(waMonitor, this.configService, this.prismaRepository); public setInstance(instance: InstanceDto) { this.logger.setInstance(instance.instanceName); this.instance.name = instance.instanceName; this.instance.id = instance.instanceId; this.instance.integration = instance.integration; this.instance.number = instance.number; this.instance.token = instance.token; this.instance.businessId = instance.businessId; this.sendDataWebhook(Events.STATUS_INSTANCE, { instance: this.instance.name, status: 'created', }); if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot.enabled) { this.chatwootService.eventWhatsapp( Events.STATUS_INSTANCE, { instanceName: this.instance.name }, { instance: this.instance.name, status: 'created', }, ); } } public set instanceName(name: string) { this.logger.setInstance(name); if (!name) { this.instance.name = v4(); return; } this.instance.name = name; } public get instanceName() { return this.instance.name; } public set instanceId(id: string) { if (!id) { this.instance.id = v4(); return; } this.instance.id = id; } public get instanceId() { return this.instance.id; } public set integration(integration: string) { this.instance.integration = integration; } public get integration() { return this.instance.integration; } public set number(number: string) { this.instance.number = number; } public get number() { return this.instance.number; } public set token(token: string) { this.instance.token = token; } public get token() { return this.instance.token; } public get wuid() { return this.instance.wuid; } public async loadSettings() { const data = await this.prismaRepository.setting.findUnique({ where: { instanceId: this.instanceId, }, }); this.localSettings.rejectCall = data?.rejectCall; this.localSettings.msgCall = data?.msgCall; this.localSettings.groupsIgnore = data?.groupsIgnore; this.localSettings.alwaysOnline = data?.alwaysOnline; this.localSettings.readMessages = data?.readMessages; this.localSettings.readStatus = data?.readStatus; this.localSettings.syncFullHistory = data?.syncFullHistory; } public async setSettings(data: SettingsDto) { await this.prismaRepository.setting.upsert({ where: { instanceId: this.instanceId, }, update: { rejectCall: data.rejectCall, msgCall: data.msgCall, groupsIgnore: data.groupsIgnore, alwaysOnline: data.alwaysOnline, readMessages: data.readMessages, readStatus: data.readStatus, syncFullHistory: data.syncFullHistory, }, create: { rejectCall: data.rejectCall, msgCall: data.msgCall, groupsIgnore: data.groupsIgnore, alwaysOnline: data.alwaysOnline, readMessages: data.readMessages, readStatus: data.readStatus, syncFullHistory: data.syncFullHistory, instanceId: this.instanceId, }, }); this.localSettings.rejectCall = data?.rejectCall; this.localSettings.msgCall = data?.msgCall; this.localSettings.groupsIgnore = data?.groupsIgnore; this.localSettings.alwaysOnline = data?.alwaysOnline; this.localSettings.readMessages = data?.readMessages; this.localSettings.readStatus = data?.readStatus; this.localSettings.syncFullHistory = data?.syncFullHistory; } public async findSettings() { const data = await this.prismaRepository.setting.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { return null; } return { rejectCall: data.rejectCall, msgCall: data.msgCall, groupsIgnore: data.groupsIgnore, alwaysOnline: data.alwaysOnline, readMessages: data.readMessages, readStatus: data.readStatus, syncFullHistory: data.syncFullHistory, }; } public async loadWebhook() { const data = await this.prismaRepository.webhook.findUnique({ where: { instanceId: this.instanceId, }, }); this.localWebhook.url = data?.url; this.localWebhook.enabled = data?.enabled; this.localWebhook.events = data?.events; this.localWebhook.webhookByEvents = data?.webhookByEvents; this.localWebhook.webhookBase64 = data?.webhookBase64; } public async setWebhook(data: WebhookDto) { const findWebhook = await this.prismaRepository.webhook.findUnique({ where: { instanceId: this.instanceId, }, }); if (findWebhook) { await this.prismaRepository.webhook.update({ where: { instanceId: this.instanceId, }, data: { url: data.url, enabled: data.enabled, events: data.events, webhookByEvents: data.webhookByEvents, webhookBase64: data.webhookBase64, }, }); Object.assign(this.localWebhook, data); return; } await this.prismaRepository.webhook.create({ data: { url: data.url, enabled: data.enabled, events: data.events, webhookByEvents: data.webhookByEvents, webhookBase64: data.webhookBase64, instanceId: this.instanceId, }, }); Object.assign(this.localWebhook, data); return; } public async findWebhook() { const data = await this.prismaRepository.webhook.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { throw new NotFoundException('Webhook not found'); } return data; } public async loadChatwoot() { if (!this.configService.get('CHATWOOT').ENABLED) { return; } const data = await this.prismaRepository.chatwoot.findUnique({ where: { instanceId: this.instanceId, }, }); this.localChatwoot.enabled = data?.enabled; this.localChatwoot.accountId = data?.accountId; this.localChatwoot.token = data?.token; this.localChatwoot.url = data?.url; this.localChatwoot.nameInbox = data?.nameInbox; this.localChatwoot.signMsg = data?.signMsg; this.localChatwoot.signDelimiter = data?.signDelimiter; this.localChatwoot.number = data?.number; this.localChatwoot.reopenConversation = data?.reopenConversation; this.localChatwoot.conversationPending = data?.conversationPending; this.localChatwoot.mergeBrazilContacts = data?.mergeBrazilContacts; this.localChatwoot.importContacts = data?.importContacts; this.localChatwoot.importMessages = data?.importMessages; this.localChatwoot.daysLimitImportMessages = data?.daysLimitImportMessages; } public async setChatwoot(data: ChatwootDto) { if (!this.configService.get('CHATWOOT').ENABLED) { return; } const chatwoot = await this.prismaRepository.chatwoot.findUnique({ where: { instanceId: this.instanceId, }, }); if (chatwoot) { await this.prismaRepository.chatwoot.update({ where: { instanceId: this.instanceId, }, data: { enabled: data.enabled, accountId: data.accountId, token: data.token, url: data.url, nameInbox: data.nameInbox, signMsg: data.signMsg, signDelimiter: data.signMsg ? data.signDelimiter : null, number: data.number, reopenConversation: data.reopenConversation, conversationPending: data.conversationPending, mergeBrazilContacts: data.mergeBrazilContacts, importContacts: data.importContacts, importMessages: data.importMessages, daysLimitImportMessages: data.daysLimitImportMessages, organization: data.organization, logo: data.logo, }, }); Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null }); this.clearCacheChatwoot(); return; } await this.prismaRepository.chatwoot.create({ data: { enabled: data.enabled, accountId: data.accountId, token: data.token, url: data.url, nameInbox: data.nameInbox, signMsg: data.signMsg, number: data.number, reopenConversation: data.reopenConversation, conversationPending: data.conversationPending, mergeBrazilContacts: data.mergeBrazilContacts, importContacts: data.importContacts, importMessages: data.importMessages, daysLimitImportMessages: data.daysLimitImportMessages, instanceId: this.instanceId, }, }); Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null }); this.clearCacheChatwoot(); } public async findChatwoot() { if (!this.configService.get('CHATWOOT').ENABLED) { return null; } const data = await this.prismaRepository.chatwoot.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { return null; } return { enabled: data.enabled, accountId: data.accountId, token: data.token, url: data.url, nameInbox: data.nameInbox, signMsg: data.signMsg, signDelimiter: data.signDelimiter || null, reopenConversation: data.reopenConversation, conversationPending: data.conversationPending, mergeBrazilContacts: data.mergeBrazilContacts, importContacts: data.importContacts, importMessages: data.importMessages, daysLimitImportMessages: data.daysLimitImportMessages, }; } public clearCacheChatwoot() { if (this.localChatwoot.enabled) { this.chatwootService.getCache()?.deleteAll(this.instanceName); } } public async loadWebsocket() { const data = await this.prismaRepository.websocket.findUnique({ where: { instanceId: this.instanceId, }, }); this.localWebsocket.enabled = data?.enabled; this.localWebsocket.events = data?.events; } public async setWebsocket(data: WebsocketDto) { await this.prismaRepository.websocket.create({ data: { enabled: data.enabled, events: data.events, instanceId: this.instanceId, }, }); Object.assign(this.localWebsocket, data); } public async findWebsocket() { const data = await this.prismaRepository.websocket.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { throw new NotFoundException('Websocket not found'); } return data; } public async loadRabbitmq() { const data = await this.prismaRepository.rabbitmq.findUnique({ where: { instanceId: this.instanceId, }, }); this.localRabbitmq.enabled = data?.enabled; this.localRabbitmq.events = data?.events; } public async setRabbitmq(data: RabbitmqDto) { const findRabbitmq = await this.prismaRepository.rabbitmq.findUnique({ where: { instanceId: this.instanceId, }, }); if (findRabbitmq) { await this.prismaRepository.rabbitmq.update({ where: { instanceId: this.instanceId, }, data: { enabled: data.enabled, events: data.events, }, }); Object.assign(this.localRabbitmq, data); return; } await this.prismaRepository.rabbitmq.create({ data: { enabled: data.enabled, events: data.events, instanceId: this.instanceId, }, }); Object.assign(this.localRabbitmq, data); return; } public async findRabbitmq() { const data = await this.prismaRepository.rabbitmq.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { throw new NotFoundException('Rabbitmq not found'); } return data; } public async removeRabbitmqQueues() { if (this.localRabbitmq.enabled) { removeQueues(this.instanceName, this.localRabbitmq.events); } } public async loadSqs() { const data = await this.prismaRepository.sqs.findUnique({ where: { instanceId: this.instanceId, }, }); this.localSqs.enabled = data?.enabled; this.localSqs.events = data?.events; } public async setSqs(data: SqsDto) { const findSqs = await this.prismaRepository.sqs.findUnique({ where: { instanceId: this.instanceId, }, }); if (findSqs) { await this.prismaRepository.sqs.update({ where: { instanceId: this.instanceId, }, data: { enabled: data.enabled, events: data.events, }, }); Object.assign(this.localSqs, data); return; } await this.prismaRepository.sqs.create({ data: { enabled: data.enabled, events: data.events, instanceId: this.instanceId, }, }); Object.assign(this.localSqs, data); return; } public async findSqs() { const data = await this.prismaRepository.sqs.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { throw new NotFoundException('Sqs not found'); } return data; } public async removeSqsQueues() { if (this.localSqs.enabled) { removeQueuesSQS(this.instanceName, this.localSqs.events); } } public async loadProxy() { const data = await this.prismaRepository.proxy.findUnique({ where: { instanceId: this.instanceId, }, }); this.localProxy.enabled = data?.enabled; this.localProxy.host = data?.host; this.localProxy.port = data?.port; this.localProxy.protocol = data?.protocol; this.localProxy.username = data?.username; this.localProxy.password = data?.password; } public async setProxy(data: ProxyDto) { await this.prismaRepository.proxy.create({ data: { enabled: data.enabled, host: data.host, port: data.port, protocol: data.protocol, username: data.username, password: data.password, instanceId: this.instanceId, }, }); Object.assign(this.localProxy, data); } public async findProxy() { const data = await this.prismaRepository.proxy.findUnique({ where: { instanceId: this.instanceId, }, }); if (!data) { throw new NotFoundException('Proxy 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 sqsLocal = this.localSqs.events; const serverUrl = this.configService.get('SERVER').URL; const rabbitmqEnabled = this.configService.get('RABBITMQ').ENABLED; const rabbitmqGlobal = this.configService.get('RABBITMQ').GLOBAL_ENABLED; const rabbitmqEvents = this.configService.get('RABBITMQ').EVENTS; 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 instanceApikey = this.token || 'Apikey not found'; if (rabbitmqEnabled) { const amqp = getAMQP(); if (this.localRabbitmq.enabled && amqp) { if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) { const exchangeName = this.instanceName ?? 'evolution_exchange'; let retry = 0; while (retry < 3) { try { await amqp.assertExchange(exchangeName, 'topic', { durable: true, autoDelete: false, }); const eventName = event.replace(/_/g, '.').toLowerCase(); const queueName = `${this.instanceName}.${eventName}`; await amqp.assertQueue(queueName, { durable: true, autoDelete: false, arguments: { 'x-queue-type': 'quorum', }, }); await amqp.bindQueue(queueName, exchangeName, eventName); const message = { event, instance: this.instance.name, data, server_url: serverUrl, date_time: now, sender: this.wuid, }; if (expose && instanceApikey) { message['apikey'] = instanceApikey; } await amqp.publish(exchangeName, event, Buffer.from(JSON.stringify(message))); if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: ChannelStartupService.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); } break; } catch (error) { retry++; } } } } if (rabbitmqGlobal && rabbitmqEvents[we] && amqp) { const exchangeName = 'evolution_exchange'; let retry = 0; while (retry < 3) { try { await amqp.assertExchange(exchangeName, 'topic', { durable: true, autoDelete: false, }); const queueName = event; await amqp.assertQueue(queueName, { durable: true, autoDelete: false, arguments: { 'x-queue-type': 'quorum', }, }); await 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; } await amqp.publish(exchangeName, event, Buffer.from(JSON.stringify(message))); if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: ChannelStartupService.name + '.sendData-RabbitMQ-Global', 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); } break; } catch (error) { retry++; } } } } if (this.localSqs.enabled) { const sqs = getSQS(); if (sqs) { if (Array.isArray(sqsLocal) && sqsLocal.includes(we)) { const eventFormatted = `${event.replace('.', '_').toLowerCase()}`; const queueName = `${this.instanceName}_${eventFormatted}.fifo`; const sqsConfig = this.configService.get('SQS'); const sqsUrl = `https://sqs.${sqsConfig.REGION}.amazonaws.com/${sqsConfig.ACCOUNT_ID}/${queueName}`; const message = { event, instance: this.instance.name, data, server_url: serverUrl, date_time: now, sender: this.wuid, }; if (expose && instanceApikey) { message['apikey'] = instanceApikey; } const params = { MessageBody: JSON.stringify(message), MessageGroupId: 'evolution', MessageDeduplicationId: `${this.instanceName}_${eventFormatted}_${Date.now()}`, QueueUrl: sqsUrl, }; sqs.sendMessage(params, (err, data) => { if (err) { this.logger.error({ local: ChannelStartupService.name + '.sendData-SQS', message: err?.message, hostName: err?.hostname, code: err?.code, stack: err?.stack, name: err?.name, url: queueName, server_url: serverUrl, }); } else { if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: ChannelStartupService.name + '.sendData-SQS', 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) { 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; } if (this.configService.get('WEBSOCKET')?.GLOBAL_EVENTS) { io.emit(event, message); if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: ChannelStartupService.name + '.sendData-WebsocketGlobal', 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.localWebsocket.enabled && Array.isArray(websocketLocal) && websocketLocal.includes(we)) { io.of(`/${this.instance.name}`).emit(event, message); if (this.configService.get('WEBSOCKET')?.GLOBAL_EVENTS) { io.emit(event, message); } if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: ChannelStartupService.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)) { let baseURL: string; if (this.localWebhook.webhookByEvents) { baseURL = `${this.localWebhook.url}/${transformedWe}`; } else { baseURL = this.localWebhook.url; } if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: ChannelStartupService.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: ChannelStartupService.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]) { 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: ChannelStartupService.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: ChannelStartupService.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, }); } } } } // Check if the number is MX or AR public 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 public 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; } } public createJid(number: string): string { if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) { return number; } if (number.includes('@broadcast')) { return number; } number = number ?.replace(/\s/g, '') .replace(/\+/g, '') .replace(/\(/g, '') .replace(/\)/g, '') .split(':')[0] .split('@')[0]; if (number.includes('-') && number.length >= 24) { number = number.replace(/[^\d-]/g, ''); return `${number}@g.us`; } number = number.replace(/\D/g, ''); if (number.length >= 18) { number = number.replace(/[^\d-]/g, ''); return `${number}@g.us`; } number = this.formatMXOrARNumber(number); number = this.formatBRNumber(number); return `${number}@s.whatsapp.net`; } public async fetchContacts(query: Query) { const remoteJid = query?.where?.remoteJid ? query?.where?.remoteJid.includes('@') ? query.where?.remoteJid : this.createJid(query.where?.remoteJid) : null; const where = { instanceId: this.instanceId, }; if (remoteJid) { where['remoteJid'] = remoteJid; } return await this.prismaRepository.contact.findMany({ where, }); } public async fetchMessages(query: Query) { const keyFilters = query?.where?.key as { id?: string; fromMe?: boolean; remoteJid?: string; participants?: string; }; const remoteJid = keyFilters?.remoteJid ? keyFilters?.remoteJid.includes('@') ? keyFilters?.remoteJid : this.createJid(keyFilters?.remoteJid) : null; const count = await this.prismaRepository.message.count({ where: { instanceId: this.instanceId, id: query?.where?.id, source: query?.where?.source, messageType: query?.where?.messageType, AND: [ keyFilters?.id ? { key: { path: ['id'], equals: keyFilters?.id } } : {}, keyFilters?.fromMe ? { key: { path: ['fromMe'], equals: keyFilters?.fromMe } } : {}, remoteJid ? { key: { path: ['remoteJid'], equals: remoteJid } } : {}, keyFilters?.participants ? { key: { path: ['participants'], equals: keyFilters?.participants } } : {}, ], }, }); if (!query?.offset) { query.offset = 50; } if (!query?.page) { query.page = 1; } const messages = await this.prismaRepository.message.findMany({ where: { instanceId: this.instanceId, id: query?.where?.id, source: query?.where?.source, messageType: query?.where?.messageType, AND: [ keyFilters?.id ? { key: { path: ['id'], equals: keyFilters?.id } } : {}, keyFilters?.fromMe ? { key: { path: ['fromMe'], equals: keyFilters?.fromMe } } : {}, remoteJid ? { key: { path: ['remoteJid'], equals: remoteJid } } : {}, keyFilters?.participants ? { key: { path: ['participants'], equals: keyFilters?.participants } } : {}, ], }, orderBy: { messageTimestamp: 'desc', }, skip: query.offset * (query?.page === 1 ? 0 : (query?.page as number) - 1), take: query.offset, select: { id: true, key: true, pushName: true, messageType: true, message: true, messageTimestamp: true, instanceId: true, source: true, MessageUpdate: { select: { status: true, }, }, }, }); return { messages: { total: count, pages: Math.ceil(count / query.offset), currentPage: query.page, records: messages, }, }; } public async fetchStatusMessage(query: any) { return await this.prismaRepository.messageUpdate.findMany({ where: { instanceId: this.instanceId, remoteJid: query.where?.remoteJid, messageId: query.where?.id, }, skip: query.offset * (query?.page === 1 ? 0 : (query?.page as number) - 1), take: query.offset, }); } public async fetchChats(query: any) { const remoteJid = query?.where?.remoteJid ? query?.where?.remoteJid.includes('@') ? query.where?.remoteJid : this.createJid(query.where?.remoteJid) : null; let result; if (remoteJid) { result = await this.prismaRepository.$queryRaw` SELECT "Chat"."id", "Chat"."remoteJid", "Chat"."labels", "Chat"."createdAt", "Chat"."updatedAt", "Contact"."pushName", "Contact"."profilePicUrl" FROM "Chat" LEFT JOIN "Contact" ON "Chat"."remoteJid" = "Contact"."remoteJid" WHERE "Chat"."instanceId" = ${this.instanceId} AND "Chat"."remoteJid" = ${remoteJid} ORDER BY "Chat"."updatedAt" DESC `; } else { result = await this.prismaRepository.$queryRaw` SELECT "Chat"."id", "Chat"."remoteJid", "Chat"."labels", "Chat"."createdAt", "Chat"."updatedAt", "Contact"."pushName", "Contact"."profilePicUrl" FROM "Chat" LEFT JOIN "Contact" ON "Chat"."remoteJid" = "Contact"."remoteJid" WHERE "Chat"."instanceId" = ${this.instanceId} ORDER BY "Chat"."updatedAt" DESC `; } return result; } }