From 14df0b368b44f20f335d7f5d6e7f9e9a92edaae4 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 16:34:32 -0300 Subject: [PATCH 1/9] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7c4c841..79d7eaa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Validate message before sending chatwoot * Adds the message status to the return of the "prepareMessage" function * Fixed openai setting when send a message with chatwoot +* Fix buildkey function in hSet and hDelete # 2.1.1 (2024-09-22 10:31) From 4fa3c5013839e8c70d9d92a46e3c437b1784ef2f Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 16:36:28 -0300 Subject: [PATCH 2/9] fix: mexico number --- .../integrations/channel/whatsapp/whatsapp.baileys.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 99c7eb96..5d30ff6a 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -2734,7 +2734,7 @@ export class BaileysStartupService extends ChannelStartupService { if (!numberVerified && (user.number.startsWith('52') || user.number.startsWith('54'))) { let prefix = ''; if (user.number.startsWith('52')) { - prefix = '1'; + prefix = ''; } if (user.number.startsWith('54')) { prefix = '9'; From 7ca9bff6e0ee5439b64c353dd1afe0c18a361e77 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 16:36:42 -0300 Subject: [PATCH 3/9] fix: mexico number --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79d7eaa3..2a625a22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Adds the message status to the return of the "prepareMessage" function * Fixed openai setting when send a message with chatwoot * Fix buildkey function in hSet and hDelete +* Fix mexico number # 2.1.1 (2024-09-22 10:31) From 1ded6c81add5c9c8788ab57b8ae8540368b1648c Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 16:46:50 -0300 Subject: [PATCH 4/9] chore: Set the maximum number of listeners that can be registered for events --- .env.example | 3 +++ CHANGELOG.md | 1 + src/config/event.config.ts | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index b5fe50a3..893a714c 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,9 @@ LOG_COLOR=true # Log Baileys - "fatal" | "error" | "warn" | "info" | "debug" | "trace" LOG_BAILEYS=error +# Set the maximum number of listeners that can be registered for an event +EVENT_EMITTER_MAX_LISTENERS=50 + # Determine how long the instance should be deleted from memory in case of no connection. # Default time: 5 minutes # If you don't even want an expiration, enter the value false diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a625a22..c0f9d0cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features * Sync lost messages on chatwoot +* Set the maximum number of listeners that can be registered for events ### Fixed diff --git a/src/config/event.config.ts b/src/config/event.config.ts index ff9b92f3..20cd1e40 100644 --- a/src/config/event.config.ts +++ b/src/config/event.config.ts @@ -1,8 +1,10 @@ import EventEmitter2 from 'eventemitter2'; +const maxListeners = parseInt(process.env.EVENT_EMITTER_MAX_LISTENERS, 10) || 50; + export const eventEmitter = new EventEmitter2({ delimiter: '.', newListener: false, ignoreErrors: false, - maxListeners: 50, + maxListeners: maxListeners, }); From 23549e3c1f54bf9bd012c5ad34f901cab40bf3cb Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 16:56:13 -0300 Subject: [PATCH 5/9] chore: update baileys version --- CHANGELOG.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0f9d0cf..e1bd69a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Fixed openai setting when send a message with chatwoot * Fix buildkey function in hSet and hDelete * Fix mexico number +* Update baileys version # 2.1.1 (2024-09-22 10:31) diff --git a/package.json b/package.json index e5fea578..9ab52f81 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@sentry/node": "^8.28.0", "amqplib": "^0.10.3", "axios": "^1.6.5", - "baileys": "6.7.7", + "baileys": "6.7.8", "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", From 88c1830bf57e36299ef8a62ddb00930034911500 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 17:22:52 -0300 Subject: [PATCH 6/9] fix: timeout error on send status message --- CHANGELOG.md | 2 ++ .../whatsapp/whatsapp.baileys.service.ts | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1bd69a9..d661a3ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ * Fix buildkey function in hSet and hDelete * Fix mexico number * Update baileys version +* Update in Baileys version that fixes timeout when updating profile picture +* Adjusts for fix timeout error on send status message # 2.1.1 (2024-09-22 10:31) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 5d30ff6a..7ba56ad9 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1741,9 +1741,25 @@ export class BaileysStartupService extends ChannelStartupService { } if (sender === 'status@broadcast') { - const jidList = message['status'].option.statusJidList; + let jidList; + if (message['status'].option.allContacts) { + const contacts = await this.prismaRepository.contact.findMany({ + where: { + instanceId: this.instanceId, + remoteJid: { + not: { + endsWith: '@g.us', + }, + }, + }, + }); - const batchSize = 500; + jidList = contacts.map((contact) => contact.remoteJid); + } else { + jidList = message['status'].option.statusJidList; + } + + const batchSize = 10; const batches = Array.from({ length: Math.ceil(jidList.length / batchSize) }, (_, i) => jidList.slice(i * batchSize, i * batchSize + batchSize), From 14d10c00ec175c227768db394ab89a62d05e5897 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 17:40:21 -0300 Subject: [PATCH 7/9] chore: chatwoot verbose logs --- CHANGELOG.md | 1 + .../whatsapp/whatsapp.baileys.service.ts | 9 +-- .../chatwoot/services/chatwoot.service.ts | 65 ++++++++++++++----- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d661a3ff..ade63d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * Update baileys version * Update in Baileys version that fixes timeout when updating profile picture * Adjusts for fix timeout error on send status message +* Chatwoot verbose logs # 2.1.1 (2024-09-22 10:31) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 7ba56ad9..3c0ab6d8 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1195,10 +1195,11 @@ export class BaileysStartupService extends ChannelStartupService { ); } - this.prismaRepository.contact.updateMany({ - where: { remoteJid: received.key.remoteJid, instanceId: this.instanceId }, - data: contactRaw, - }); + if (this.configService.get('DATABASE').SAVE_DATA.CONTACTS) + await this.prismaRepository.contact.create({ + data: contactRaw, + }); + return; } diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 0d0cc9a0..9eb5a76a 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -538,26 +538,37 @@ export class ChatwootService { public async createConversation(instance: InstanceDto, body: any) { try { + this.logger.verbose('--- Start createConversation ---'); + this.logger.verbose(`Instance: ${JSON.stringify(instance)}`); + this.logger.verbose(`Body: ${JSON.stringify(body)}`); + const client = await this.clientCw(instance); if (!client) { - this.logger.warn('client not found'); + this.logger.warn(`Client not found for instance: ${JSON.stringify(instance)}`); return null; } const cacheKey = `${instance.instanceName}:createConversation-${body.key.remoteJid}`; + this.logger.verbose(`Cache key: ${cacheKey}`); + if (await this.cache.has(cacheKey)) { + this.logger.verbose(`Cache hit for key: ${cacheKey}`); const conversationId = (await this.cache.get(cacheKey)) as number; + this.logger.verbose(`Cached conversation ID: ${conversationId}`); let conversationExists: conversation | boolean; try { conversationExists = await client.conversations.get({ accountId: this.provider.accountId, conversationId: conversationId, }); + this.logger.verbose(`Conversation exists: ${JSON.stringify(conversationExists)}`); } catch (error) { + this.logger.error(`Error getting conversation: ${error}`); conversationExists = false; } if (!conversationExists) { + this.logger.verbose('Conversation does not exist, re-calling createConversation'); this.cache.delete(cacheKey); return await this.createConversation(instance, body); } @@ -566,30 +577,37 @@ export class ChatwootService { } const isGroup = body.key.remoteJid.includes('@g.us'); + this.logger.verbose(`Is group: ${isGroup}`); const chatId = isGroup ? body.key.remoteJid : body.key.remoteJid.split('@')[0]; + this.logger.verbose(`Chat ID: ${chatId}`); let nameContact: string; nameContact = !body.key.fromMe ? body.pushName : chatId; + this.logger.verbose(`Name contact: ${nameContact}`); const filterInbox = await this.getInbox(instance); if (!filterInbox) { - this.logger.warn('inbox not found'); + this.logger.warn(`Inbox not found for instance: ${JSON.stringify(instance)}`); return null; } if (isGroup) { + this.logger.verbose('Processing group conversation'); const group = await this.waMonitor.waInstances[instance.instanceName].client.groupMetadata(chatId); + this.logger.verbose(`Group metadata: ${JSON.stringify(group)}`); nameContact = `${group.subject} (GROUP)`; const picture_url = await this.waMonitor.waInstances[instance.instanceName].profilePicture( body.key.participant.split('@')[0], ); + this.logger.verbose(`Participant profile picture URL: ${JSON.stringify(picture_url)}`); const findParticipant = await this.findContact(instance, body.key.participant.split('@')[0]); + this.logger.verbose(`Found participant: ${JSON.stringify(findParticipant)}`); if (findParticipant) { if (!findParticipant.name || findParticipant.name === chatId) { @@ -612,8 +630,10 @@ export class ChatwootService { } const picture_url = await this.waMonitor.waInstances[instance.instanceName].profilePicture(chatId); + this.logger.verbose(`Contact profile picture URL: ${JSON.stringify(picture_url)}`); let contact = await this.findContact(instance, chatId); + this.logger.verbose(`Found contact: ${JSON.stringify(contact)}`); if (contact) { if (!body.key.fromMe) { @@ -630,8 +650,10 @@ export class ChatwootService { ) : false); - const contactNeedsUpdate = pictureNeedsUpdate || nameNeedsUpdate; - if (contactNeedsUpdate) { + this.logger.verbose(`Picture needs update: ${pictureNeedsUpdate}`); + this.logger.verbose(`Name needs update: ${nameNeedsUpdate}`); + + if (pictureNeedsUpdate || nameNeedsUpdate) { contact = await this.updateContact(instance, contact.id, { ...(nameNeedsUpdate && { name: nameContact }), ...(waProfilePictureFile === '' && { avatar: null }), @@ -653,38 +675,50 @@ export class ChatwootService { } if (!contact) { - this.logger.warn('contact not found'); + this.logger.warn('Contact not created or found'); return null; } const contactId = contact?.payload?.id || contact?.payload?.contact?.id || contact?.id; + this.logger.verbose(`Contact ID: ${contactId}`); const contactConversations = (await client.contacts.listConversations({ accountId: this.provider.accountId, id: contactId, })) as any; + this.logger.verbose(`Contact conversations: ${JSON.stringify(contactConversations)}`); - if (contactConversations?.payload?.length) { + if (!contactConversations || !contactConversations.payload) { + this.logger.error('No conversations found or payload is undefined'); + return null; + } + + if (contactConversations.payload.length) { let conversation: any; if (this.provider.reopenConversation) { conversation = contactConversations.payload.find((conversation) => conversation.inbox_id == filterInbox.id); + this.logger.verbose(`Found conversation in reopenConversation mode: ${JSON.stringify(conversation)}`); if (this.provider.conversationPending) { - await client.conversations.toggleStatus({ - accountId: this.provider.accountId, - conversationId: conversation.id, - data: { - status: 'pending', - }, - }); + if (conversation) { + await client.conversations.toggleStatus({ + accountId: this.provider.accountId, + conversationId: conversation.id, + data: { + status: 'pending', + }, + }); + } } } else { conversation = contactConversations.payload.find( (conversation) => conversation.status !== 'resolved' && conversation.inbox_id == filterInbox.id, ); + this.logger.verbose(`Found conversation: ${JSON.stringify(conversation)}`); } if (conversation) { + this.logger.verbose(`Returning existing conversation ID: ${conversation.id}`); this.cache.set(cacheKey, conversation.id); return conversation.id; } @@ -705,14 +739,15 @@ export class ChatwootService { }); if (!conversation) { - this.logger.warn('conversation not found'); + this.logger.warn('Conversation not created or found'); return null; } + this.logger.verbose(`New conversation created with ID: ${conversation.id}`); this.cache.set(cacheKey, conversation.id); return conversation.id; } catch (error) { - this.logger.error(error); + this.logger.error(`Error in createConversation: ${error}`); } } From d1bc0e61501b6a29d9d7a1af4f34f3a38a87348f Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 17:59:40 -0300 Subject: [PATCH 8/9] fix: adjusts on prisma connections --- CHANGELOG.md | 1 + src/api/guards/instance.guard.ts | 7 ++----- src/libs/prisma.connect.ts | 16 ---------------- src/utils/use-multi-file-auth-state-prisma.ts | 14 ++++++-------- 4 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 src/libs/prisma.connect.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ade63d5e..fda681e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Update in Baileys version that fixes timeout when updating profile picture * Adjusts for fix timeout error on send status message * Chatwoot verbose logs +* Adjusts on prisma connections # 2.1.1 (2024-09-22 10:31) diff --git a/src/api/guards/instance.guard.ts b/src/api/guards/instance.guard.ts index 874fa07f..e692f362 100644 --- a/src/api/guards/instance.guard.ts +++ b/src/api/guards/instance.guard.ts @@ -1,8 +1,7 @@ import { InstanceDto } from '@api/dto/instance.dto'; -import { cache, waMonitor } from '@api/server.module'; +import { cache, prismaRepository, waMonitor } from '@api/server.module'; import { CacheConf, configService } from '@config/env.config'; import { BadRequestException, ForbiddenException, InternalServerErrorException, NotFoundException } from '@exceptions'; -import { prismaServer } from '@libs/prisma.connect'; import { NextFunction, Request, Response } from 'express'; async function getInstance(instanceName: string) { @@ -17,9 +16,7 @@ async function getInstance(instanceName: string) { return exists || keyExists; } - const prisma = prismaServer; - - return exists || (await prisma.instance.findMany({ where: { name: instanceName } })).length > 0; + return exists || (await prismaRepository.instance.findMany({ where: { name: instanceName } })).length > 0; } catch (error) { throw new InternalServerErrorException(error?.toString()); } diff --git a/src/libs/prisma.connect.ts b/src/libs/prisma.connect.ts deleted file mode 100644 index fa8d6600..00000000 --- a/src/libs/prisma.connect.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Logger } from '@config/logger.config'; -import { PrismaClient } from '@prisma/client'; - -const logger = new Logger('Prisma'); - -export const prismaServer = (() => { - logger.verbose('connecting'); - const db = new PrismaClient(); - - process.on('beforeExit', () => { - logger.verbose('instance destroyed'); - db.$disconnect(); - }); - - return db; -})(); diff --git a/src/utils/use-multi-file-auth-state-prisma.ts b/src/utils/use-multi-file-auth-state-prisma.ts index b7b6e1b2..02f96f15 100644 --- a/src/utils/use-multi-file-auth-state-prisma.ts +++ b/src/utils/use-multi-file-auth-state-prisma.ts @@ -1,12 +1,10 @@ +import { prismaRepository } from '@api/server.module'; import { CacheService } from '@api/services/cache.service'; import { INSTANCE_DIR } from '@config/path.config'; -import { prismaServer } from '@libs/prisma.connect'; import { AuthenticationState, BufferJSON, initAuthCreds, WAProto as proto } from 'baileys'; import fs from 'fs/promises'; import path from 'path'; -const prisma = prismaServer; - // const fixFileName = (file: string): string | undefined => { // if (!file) { // return undefined; @@ -18,7 +16,7 @@ const prisma = prismaServer; export async function keyExists(sessionId: string): Promise { try { - const key = await prisma.session.findUnique({ where: { sessionId: sessionId } }); + const key = await prismaRepository.session.findUnique({ where: { sessionId: sessionId } }); return !!key; } catch (error) { return false; @@ -29,13 +27,13 @@ export async function saveKey(sessionId: string, keyJson: any): Promise { const exists = await keyExists(sessionId); try { if (!exists) - return await prisma.session.create({ + return await prismaRepository.session.create({ data: { sessionId: sessionId, creds: JSON.stringify(keyJson), }, }); - await prisma.session.update({ + await prismaRepository.session.update({ where: { sessionId: sessionId }, data: { creds: JSON.stringify(keyJson) }, }); @@ -48,7 +46,7 @@ export async function getAuthKey(sessionId: string): Promise { try { const register = await keyExists(sessionId); if (!register) return null; - const auth = await prisma.session.findUnique({ where: { sessionId: sessionId } }); + const auth = await prismaRepository.session.findUnique({ where: { sessionId: sessionId } }); return JSON.parse(auth?.creds); } catch (error) { return null; @@ -59,7 +57,7 @@ async function deleteAuthKey(sessionId: string): Promise { try { const register = await keyExists(sessionId); if (!register) return; - await prisma.session.delete({ where: { sessionId: sessionId } }); + await prismaRepository.session.delete({ where: { sessionId: sessionId } }); } catch (error) { return; } From d84713f6ca9f9a33edc13a9105971f467eeafa24 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 3 Oct 2024 18:14:52 -0300 Subject: [PATCH 9/9] license: license terms updated --- CHANGELOG.md | 1 + LICENSE | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda681e5..eb524c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Adjusts for fix timeout error on send status message * Chatwoot verbose logs * Adjusts on prisma connections +* License terms updated # 2.1.1 (2024-09-22 10:31) diff --git a/LICENSE b/LICENSE index 84d8e909..c0e8e9be 100644 --- a/LICENSE +++ b/LICENSE @@ -9,6 +9,8 @@ a. Multi-tenant SaaS service: Unless explicitly authorized in writing, you may n b. LOGO and copyright information: In the process of using Evolution API's frontend components, you may not remove or modify the LOGO or copyright information in the Evolution API console or applications. This restriction is inapplicable to uses of Evolution API that do not involve its frontend components. +c. Usage Notification Requirement: If Evolution API is used as part of any project, including closed-source systems (e.g., proprietary software), the user is required to display a clear notification within the system that Evolution API is being utilized. This notification should be visible to system administrators and accessible from the system's documentation or settings page. Failure to comply with this requirement may result in the necessity for a commercial license, as determined by the producer. + Please contact contato@atendai.com to inquire about licensing matters. 2. As a contributor, you should agree that: