mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-22 20:12:02 -06:00
Merge branch 'EvolutionAPI:v2.0.0' into v2.0.0
This commit is contained in:
commit
7b41d333d7
@ -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
|
||||
|
@ -3,6 +3,7 @@
|
||||
### Features
|
||||
|
||||
* Sync lost messages on chatwoot
|
||||
* Set the maximum number of listeners that can be registered for events
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -12,6 +13,14 @@
|
||||
* 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
|
||||
* 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
|
||||
* Chatwoot verbose logs
|
||||
* Adjusts on prisma connections
|
||||
* License terms updated
|
||||
|
||||
# 2.1.1 (2024-09-22 10:31)
|
||||
|
||||
|
2
LICENSE
2
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:
|
||||
|
@ -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",
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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>('DATABASE').SAVE_DATA.CONTACTS)
|
||||
await this.prismaRepository.contact.create({
|
||||
data: contactRaw,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1743,9 +1744,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),
|
||||
@ -2736,7 +2753,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';
|
||||
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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;
|
||||
})();
|
@ -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<any> {
|
||||
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<any> {
|
||||
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<any> {
|
||||
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<any> {
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user