mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-13 15:14:49 -06:00
Merge pull request #1126 from jesus-chacon/update_chats
Fix: cuid security deprecation, update libs, lint and improve chat DB update
This commit is contained in:
commit
ca451bfacc
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,7 +21,6 @@ lerna-debug.log*
|
||||
# Package
|
||||
/yarn.lock
|
||||
/pnpm-lock.yaml
|
||||
/package-lock.json
|
||||
|
||||
# IDEs
|
||||
.vscode/*
|
||||
|
12517
package-lock.json
generated
Normal file
12517
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
75
package.json
75
package.json
@ -49,73 +49,72 @@
|
||||
"homepage": "https://github.com/EvolutionAPI/evolution-api#readme",
|
||||
"dependencies": {
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@aws-sdk/client-sqs": "^3.569.0",
|
||||
"@aws-sdk/client-sqs": "^3.723.0",
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@figuro/chatwoot-sdk": "^1.1.16",
|
||||
"@hapi/boom": "^10.0.1",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@prisma/client": "^6.1.0",
|
||||
"@sentry/node": "^8.28.0",
|
||||
"amqplib": "^0.10.3",
|
||||
"axios": "^1.6.5",
|
||||
"@sentry/node": "^8.47.0",
|
||||
"amqplib": "^0.10.5",
|
||||
"axios": "^1.7.9",
|
||||
"baileys": "github:EvolutionAPI/Baileys",
|
||||
"class-validator": "^0.14.1",
|
||||
"compression": "^1.7.4",
|
||||
"compression": "^1.7.5",
|
||||
"cors": "^2.8.5",
|
||||
"cuid": "^3.0.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"dotenv": "^16.4.5",
|
||||
"dayjs": "^1.11.13",
|
||||
"dotenv": "^16.4.7",
|
||||
"eventemitter2": "^6.4.9",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.2",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"form-data": "^4.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"form-data": "^4.0.1",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"i18next": "^23.7.19",
|
||||
"jimp": "^0.16.13",
|
||||
"json-schema": "^0.4.0",
|
||||
"jsonschema": "^1.4.1",
|
||||
"link-preview-js": "^3.0.4",
|
||||
"link-preview-js": "^3.0.13",
|
||||
"long": "^5.2.3",
|
||||
"mediainfo.js": "^0.3.2",
|
||||
"mime": "^3.0.0",
|
||||
"minio": "^8.0.1",
|
||||
"mediainfo.js": "^0.3.4",
|
||||
"mime": "^4.0.6",
|
||||
"minio": "^8.0.3",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"openai": "^4.52.7",
|
||||
"pg": "^8.11.3",
|
||||
"openai": "^4.77.3",
|
||||
"pg": "^8.13.1",
|
||||
"pino": "^8.11.0",
|
||||
"prisma": "^6.1.0",
|
||||
"pusher": "^5.2.0",
|
||||
"qrcode": "^1.5.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"redis": "^4.6.5",
|
||||
"sharp": "^0.32.2",
|
||||
"socket.io": "^4.7.1",
|
||||
"tsup": "^8.2.4",
|
||||
"uuid": "^9.0.0"
|
||||
"redis": "^4.7.0",
|
||||
"sharp": "^0.32.6",
|
||||
"socket.io": "^4.8.1",
|
||||
"tsup": "^8.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/compression": "^1.7.5",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "^4.17.18",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@types/mime": "3.0.0",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/mime": "4.0.0",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/qrcode": "^1.5.0",
|
||||
"@types/qrcode-terminal": "^0.12.0",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qrcode-terminal": "^0.12.2",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier": "^3.4.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.5.4"
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,10 @@ import axios from 'axios';
|
||||
const logger = new Logger('ProxyController');
|
||||
|
||||
export class ProxyController {
|
||||
constructor(private readonly proxyService: ProxyService, private readonly waMonitor: WAMonitoringService) {}
|
||||
constructor(
|
||||
private readonly proxyService: ProxyService,
|
||||
private readonly waMonitor: WAMonitoringService,
|
||||
) {}
|
||||
|
||||
public async createProxy(instance: InstanceDto, data: ProxyDto) {
|
||||
if (!this.waMonitor.waInstances[instance.instanceName]) {
|
||||
|
@ -71,10 +71,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
}
|
||||
|
||||
private isMediaMessage(message: any) {
|
||||
return message.document ||
|
||||
message.image ||
|
||||
message.audio ||
|
||||
message.video
|
||||
return message.document || message.image || message.audio || message.video;
|
||||
}
|
||||
|
||||
private async post(message: any, params: string) {
|
||||
@ -333,13 +330,17 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
|
||||
const buffer = await axios.get(result.data.url, { headers, responseType: 'arraybuffer' });
|
||||
|
||||
const mediaType = message.messages[0].document
|
||||
? 'document'
|
||||
: message.messages[0].image
|
||||
? 'image'
|
||||
: message.messages[0].audio
|
||||
? 'audio'
|
||||
: 'video';
|
||||
let mediaType;
|
||||
|
||||
if (message.messages[0].document) {
|
||||
mediaType = 'document';
|
||||
} else if (message.messages[0].image) {
|
||||
mediaType = 'image';
|
||||
} else if (message.messages[0].audio) {
|
||||
mediaType = 'audio';
|
||||
} else {
|
||||
mediaType = 'video';
|
||||
}
|
||||
|
||||
const mimetype = result.data?.mime_type || result.headers['content-type'];
|
||||
|
||||
@ -479,7 +480,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
message: {
|
||||
mediaUrl: messageRaw.message.mediaUrl,
|
||||
...messageRaw,
|
||||
}
|
||||
},
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
@ -800,7 +801,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
}
|
||||
if (message['media']) {
|
||||
const isImage = message['mimetype']?.startsWith('image/');
|
||||
|
||||
|
||||
content = {
|
||||
messaging_product: 'whatsapp',
|
||||
recipient_type: 'individual',
|
||||
@ -815,7 +816,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
};
|
||||
quoted ? (content.context = { message_id: quoted.id }) : content;
|
||||
return await this.post(content, 'messages');
|
||||
}
|
||||
}
|
||||
if (message['audio']) {
|
||||
content = {
|
||||
messaging_product: 'whatsapp',
|
||||
@ -1103,11 +1104,10 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
|
||||
if (file?.buffer) {
|
||||
mediaData.audio = file.buffer.toString('base64');
|
||||
}
|
||||
else if(isURL(mediaData.audio)){
|
||||
mediaData.audio = mediaData.audio
|
||||
}
|
||||
else {
|
||||
} else if (isURL(mediaData.audio)) {
|
||||
// DO NOTHING
|
||||
// mediaData.audio = mediaData.audio;
|
||||
} else {
|
||||
console.error('El archivo no tiene buffer o file es undefined');
|
||||
throw new Error('File or buffer is undefined');
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ import {
|
||||
import { BadRequestException, InternalServerErrorException, NotFoundException } from '@exceptions';
|
||||
import ffmpegPath from '@ffmpeg-installer/ffmpeg';
|
||||
import { Boom } from '@hapi/boom';
|
||||
import { createId as cuid } from '@paralleldrive/cuid2';
|
||||
import { Instance } from '@prisma/client';
|
||||
import { makeProxyAgent } from '@utils/makeProxyAgent';
|
||||
import { getOnWhatsappCache, saveOnWhatsappCache } from '@utils/onWhatsappCache';
|
||||
@ -125,7 +126,6 @@ import { LabelAssociation } from 'baileys/lib/Types/LabelAssociation';
|
||||
import { spawn } from 'child_process';
|
||||
import { isArray, isBase64, isURL } from 'class-validator';
|
||||
import { randomBytes } from 'crypto';
|
||||
import cuid from 'cuid';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
import ffmpeg from 'fluent-ffmpeg';
|
||||
import FormData from 'form-data';
|
||||
@ -1136,29 +1136,25 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
}
|
||||
const existingChat = await this.prismaRepository.chat.findFirst({
|
||||
where: { instanceId: this.instanceId, remoteJid: received.key.remoteJid },
|
||||
select: { id: true, name: true },
|
||||
});
|
||||
|
||||
if (existingChat) {
|
||||
const chatToInsert = {
|
||||
remoteJid: received.key.remoteJid,
|
||||
instanceId: this.instanceId,
|
||||
name: received.pushName || '',
|
||||
unreadMessages: 0,
|
||||
};
|
||||
|
||||
this.sendDataWebhook(Events.CHATS_UPSERT, [chatToInsert]);
|
||||
if (
|
||||
existingChat &&
|
||||
received.pushName &&
|
||||
existingChat.name !== received.pushName &&
|
||||
received.pushName.trim().length > 0
|
||||
) {
|
||||
this.sendDataWebhook(Events.CHATS_UPSERT, [{ ...existingChat, name: received.pushName }]);
|
||||
if (this.configService.get<Database>('DATABASE').SAVE_DATA.CHATS) {
|
||||
try {
|
||||
await this.prismaRepository.chat.update({
|
||||
where: {
|
||||
id: existingChat.id,
|
||||
},
|
||||
data: chatToInsert,
|
||||
});
|
||||
}
|
||||
catch(error){
|
||||
console.log(`Chat insert record ignored: ${chatToInsert.remoteJid} - ${chatToInsert.instanceId}`);
|
||||
}
|
||||
try {
|
||||
await this.prismaRepository.chat.update({
|
||||
where: { id: existingChat.id },
|
||||
data: { name: received.pushName },
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Chat insert record ignored: ${received.key.remoteJid} - ${this.instanceId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1494,13 +1490,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
if (this.configService.get<Database>('DATABASE').SAVE_DATA.CHATS) {
|
||||
try {
|
||||
await this.prismaRepository.chat.update({
|
||||
where: {
|
||||
id: existingChat.id,
|
||||
},
|
||||
where: {
|
||||
id: existingChat.id,
|
||||
},
|
||||
data: chatToInsert,
|
||||
});
|
||||
}
|
||||
catch(error){
|
||||
} catch (error) {
|
||||
console.log(`Chat insert record ignored: ${chatToInsert.remoteJid} - ${chatToInsert.instanceId}`);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,6 @@ export class WebhookController extends EventController implements EventControlle
|
||||
local,
|
||||
}: EmitData): Promise<void> {
|
||||
const instance = (await this.get(instanceName)) as wa.LocalWebHook;
|
||||
|
||||
|
||||
const webhookConfig = configService.get<Webhook>('WEBHOOK');
|
||||
const webhookLocal = instance?.events;
|
||||
@ -86,7 +85,7 @@ export class WebhookController extends EventController implements EventControlle
|
||||
apikey: apiKey,
|
||||
};
|
||||
|
||||
if (local && !instance || !instance?.enabled) {
|
||||
if ((local && !instance) || !instance?.enabled) {
|
||||
if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) {
|
||||
let baseURL: string;
|
||||
|
||||
|
@ -8,7 +8,10 @@ import { instanceSchema, webhookSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
export class WebhookRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||
constructor(
|
||||
readonly configService: ConfigService,
|
||||
...guards: RequestHandler[]
|
||||
) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
|
@ -8,7 +8,10 @@ import { RequestHandler, Router } from 'express';
|
||||
import { HttpStatus } from './index.router';
|
||||
|
||||
export class InstanceRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||
constructor(
|
||||
readonly configService: ConfigService,
|
||||
...guards: RequestHandler[]
|
||||
) {
|
||||
super();
|
||||
this.router
|
||||
.post('/create', ...guards, async (req, res) => {
|
||||
|
@ -9,7 +9,10 @@ import { RequestHandler, Router } from 'express';
|
||||
import { HttpStatus } from './index.router';
|
||||
|
||||
export class TemplateRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||
constructor(
|
||||
readonly configService: ConfigService,
|
||||
...guards: RequestHandler[]
|
||||
) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('create'), ...guards, async (req, res) => {
|
||||
|
@ -42,20 +42,23 @@ export class WAMonitoringService {
|
||||
public delInstanceTime(instance: string) {
|
||||
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
|
||||
if (typeof time === 'number' && time > 0) {
|
||||
setTimeout(async () => {
|
||||
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
|
||||
if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') {
|
||||
if ((await this.waInstances[instance].integration) === Integration.WHATSAPP_BAILEYS) {
|
||||
await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance);
|
||||
this.waInstances[instance]?.client?.ws?.close();
|
||||
this.waInstances[instance]?.client?.end(undefined);
|
||||
setTimeout(
|
||||
async () => {
|
||||
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
|
||||
if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') {
|
||||
if ((await this.waInstances[instance].integration) === Integration.WHATSAPP_BAILEYS) {
|
||||
await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance);
|
||||
this.waInstances[instance]?.client?.ws?.close();
|
||||
this.waInstances[instance]?.client?.end(undefined);
|
||||
}
|
||||
this.eventEmitter.emit('remove.instance', instance, 'inner');
|
||||
} else {
|
||||
this.eventEmitter.emit('remove.instance', instance, 'inner');
|
||||
}
|
||||
this.eventEmitter.emit('remove.instance', instance, 'inner');
|
||||
} else {
|
||||
this.eventEmitter.emit('remove.instance', instance, 'inner');
|
||||
}
|
||||
}
|
||||
}, 1000 * 60 * time);
|
||||
},
|
||||
1000 * 60 * time,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,14 +75,15 @@ export class WAMonitoringService {
|
||||
|
||||
const clientName = this.configService.get<Database>('DATABASE').CONNECTION.CLIENT_NAME;
|
||||
|
||||
const where = instanceNames && instanceNames.length > 0
|
||||
? {
|
||||
name: {
|
||||
in: instanceNames,
|
||||
},
|
||||
clientName,
|
||||
}
|
||||
: { clientName };
|
||||
const where =
|
||||
instanceNames && instanceNames.length > 0
|
||||
? {
|
||||
name: {
|
||||
in: instanceNames,
|
||||
},
|
||||
clientName,
|
||||
}
|
||||
: { clientName };
|
||||
|
||||
const instances = await this.prismaRepository.instance.findMany({
|
||||
where,
|
||||
@ -218,7 +222,7 @@ export class WAMonitoringService {
|
||||
id: data.instanceId,
|
||||
name: data.instanceName,
|
||||
connectionStatus:
|
||||
data.integration && data.integration === Integration.WHATSAPP_BAILEYS ? 'close' : data.status ?? 'open',
|
||||
data.integration && data.integration === Integration.WHATSAPP_BAILEYS ? 'close' : (data.status ?? 'open'),
|
||||
number: data.number,
|
||||
integration: data.integration || Integration.WHATSAPP_BAILEYS,
|
||||
token: data.hash,
|
||||
|
@ -131,7 +131,14 @@ export declare namespace wa {
|
||||
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
|
||||
}
|
||||
|
||||
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage', 'ptvMessage'];
|
||||
export const TypeMediaMessage = [
|
||||
'imageMessage',
|
||||
'documentMessage',
|
||||
'audioMessage',
|
||||
'videoMessage',
|
||||
'stickerMessage',
|
||||
'ptvMessage',
|
||||
];
|
||||
|
||||
export const MessageSubtype = [
|
||||
'ephemeralMessage',
|
||||
|
5
src/cache/cacheengine.ts
vendored
5
src/cache/cacheengine.ts
vendored
@ -10,7 +10,10 @@ const logger = new Logger('CacheEngine');
|
||||
export class CacheEngine {
|
||||
private engine: ICache;
|
||||
|
||||
constructor(private readonly configService: ConfigService, module: string) {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
module: string,
|
||||
) {
|
||||
const cacheConf = configService.get<CacheConf>('CACHE');
|
||||
|
||||
if (cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') {
|
||||
|
5
src/cache/localcache.ts
vendored
5
src/cache/localcache.ts
vendored
@ -9,7 +9,10 @@ export class LocalCache implements ICache {
|
||||
private conf: CacheConfLocal;
|
||||
static localCache = new NodeCache();
|
||||
|
||||
constructor(private readonly configService: ConfigService, private readonly module: string) {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly module: string,
|
||||
) {
|
||||
this.conf = this.configService.get<CacheConf>('CACHE')?.LOCAL;
|
||||
}
|
||||
|
||||
|
5
src/cache/rediscache.ts
vendored
5
src/cache/rediscache.ts
vendored
@ -11,7 +11,10 @@ export class RedisCache implements ICache {
|
||||
private client: RedisClientType;
|
||||
private conf: CacheConfRedis;
|
||||
|
||||
constructor(private readonly configService: ConfigService, private readonly module: string) {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly module: string,
|
||||
) {
|
||||
this.conf = this.configService.get<CacheConf>('CACHE')?.REDIS;
|
||||
this.client = redisClient.getConnection();
|
||||
}
|
||||
|
@ -17,13 +17,14 @@ const getTypeMessage = (msg: any) => {
|
||||
msg?.message?.viewOnceMessageV2?.message?.audioMessage?.url,
|
||||
listResponseMessage: msg?.message?.listResponseMessage?.title,
|
||||
responseRowId: msg?.message?.listResponseMessage?.singleSelectReply?.selectedRowId,
|
||||
templateButtonReplyMessage: msg?.message?.templateButtonReplyMessage?.selectedId || msg?.message?.buttonsResponseMessage?.selectedButtonId,
|
||||
templateButtonReplyMessage:
|
||||
msg?.message?.templateButtonReplyMessage?.selectedId || msg?.message?.buttonsResponseMessage?.selectedButtonId,
|
||||
// Medias
|
||||
audioMessage: msg?.message?.speechToText
|
||||
? msg?.message?.speechToText
|
||||
: msg?.message?.audioMessage
|
||||
? `audioMessage|${mediaId}`
|
||||
: undefined,
|
||||
? `audioMessage|${mediaId}`
|
||||
: undefined,
|
||||
imageMessage: msg?.message?.imageMessage
|
||||
? `imageMessage|${mediaId}${msg?.message?.imageMessage?.caption ? `|${msg?.message?.imageMessage?.caption}` : ''}`
|
||||
: undefined,
|
||||
|
@ -1,12 +1,12 @@
|
||||
import * as Sentry from "@sentry/node";
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
||||
const dsn = process.env.SENTRY_DSN;
|
||||
|
||||
if (dsn) {
|
||||
Sentry.init({
|
||||
dsn: dsn,
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
tracesSampleRate: 1.0,
|
||||
profilesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
Sentry.init({
|
||||
dsn: dsn,
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
tracesSampleRate: 1.0,
|
||||
profilesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user