From b475d1d5ea9eac48928a1af77378de26fa579d8c Mon Sep 17 00:00:00 2001 From: Fabio Date: Thu, 15 Aug 2024 11:26:30 -0400 Subject: [PATCH 01/14] set region from env and fix size in uploadFile --- src/api/integrations/s3/libs/minio.server.ts | 1 + src/api/services/channels/whatsapp.baileys.service.ts | 2 +- src/config/env.config.ts | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/integrations/s3/libs/minio.server.ts b/src/api/integrations/s3/libs/minio.server.ts index 0aed54de..20571379 100644 --- a/src/api/integrations/s3/libs/minio.server.ts +++ b/src/api/integrations/s3/libs/minio.server.ts @@ -21,6 +21,7 @@ const minioClient = (() => { useSSL: BUCKET.USE_SSL, accessKey: BUCKET.ACCESS_KEY, secretKey: BUCKET.SECRET_KEY, + region: BUCKET.REGION }); } })(); diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index fa117234..9d5a4334 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -1157,7 +1157,7 @@ export class BaileysStartupService extends ChannelStartupService { const fullName = join(`${this.instance.id}`, received.key.remoteJid, mediaType, fileName); - await s3Service.uploadFile(fullName, buffer, size.fileLength, { + await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { 'Content-Type': mimetype, }); diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 26ff40a0..7df8dd3a 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -202,6 +202,7 @@ export type S3 = { ENABLE: boolean; PORT?: number; USE_SSL?: boolean; + REGION?: string; }; export type CacheConf = { REDIS: CacheConfRedis; LOCAL: CacheConfLocal }; @@ -463,6 +464,7 @@ export class ConfigService { ENABLE: process.env?.S3_ENABLED === 'true', PORT: Number.parseInt(process.env?.S3_PORT || '9000'), USE_SSL: process.env?.S3_USE_SSL === 'true', + REGION: process.env?.S3_REGION }, AUTHENTICATION: { API_KEY: { From 785a72cbc8b312721c46e3de5f71c2419c0c93e2 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 12:29:08 -0300 Subject: [PATCH 02/14] feat: openai now identifies images --- CHANGELOG.md | 2 + .../dify/services/dify.service.ts | 16 +- .../openai/services/openai.service.ts | 140 ++++++++++++++---- 3 files changed, 121 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b34fe32a..f047851e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Added ignoreJids in chatwoot settings * Dify now identifies images +* Openai now identifies images ### Fixed @@ -16,6 +17,7 @@ * Deprecate buttons and list in new Baileys version * Changed labels to be unique on the same instance * Remove instance from redis even if using database +* Unified integration session system so they don't overlap # 2.0.9-rc (2024-08-09 18:00) diff --git a/src/api/integrations/dify/services/dify.service.ts b/src/api/integrations/dify/services/dify.service.ts index 3fb1dca6..597232b7 100644 --- a/src/api/integrations/dify/services/dify.service.ts +++ b/src/api/integrations/dify/services/dify.service.ts @@ -1101,7 +1101,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.query = contentSplit[2]; + payload.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1169,7 +1169,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.inputs.query = contentSplit[2]; + payload.inputs.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1237,7 +1237,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.query = contentSplit[2]; + payload.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1329,7 +1329,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.inputs.query = contentSplit[2]; + payload.inputs.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1506,7 +1506,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.query = contentSplit[2]; + payload.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1574,7 +1574,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.inputs.query = contentSplit[2]; + payload.inputs.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1642,7 +1642,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.query = contentSplit[2]; + payload.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); @@ -1741,7 +1741,7 @@ export class DifyService { url: contentSplit[1].split('?')[0], }, ]; - payload.inputs.query = contentSplit[2]; + payload.inputs.query = contentSplit[2] || content; } await instance.client.presenceSubscribe(remoteJid); diff --git a/src/api/integrations/openai/services/openai.service.ts b/src/api/integrations/openai/services/openai.service.ts index 501f08f8..880b2b7e 100644 --- a/src/api/integrations/openai/services/openai.service.ts +++ b/src/api/integrations/openai/services/openai.service.ts @@ -874,11 +874,27 @@ export class OpenaiService { : msg?.message?.audioMessage ? `audioMessage|${mediaId}` : undefined, - imageMessage: msg?.message?.imageMessage ? `imageMessage|${mediaId}` : undefined, - videoMessage: msg?.message?.videoMessage ? `videoMessage|${mediaId}` : undefined, - documentMessage: msg?.message?.documentMessage ? `documentMessage|${mediaId}` : undefined, - documentWithCaptionMessage: msg?.message?.auddocumentWithCaptionMessageioMessage - ? `documentWithCaptionMessage|${mediaId}` + imageMessage: msg?.message?.imageMessage + ? `imageMessage|${mediaId}${ + msg?.message?.imageMessage?.caption ? `|${msg?.message?.imageMessage?.caption}` : '' + }` + : undefined, + videoMessage: msg?.message?.videoMessage + ? `videoMessage|${mediaId}${ + msg?.message?.videoMessage?.caption ? `|${msg?.message?.videoMessage?.caption}` : '' + }` + : undefined, + documentMessage: msg?.message?.documentMessage + ? `documentMessage|${mediaId}${ + msg?.message?.documentMessage?.caption ? `|${msg?.message?.documentMessage?.caption}` : '' + }` + : undefined, + documentWithCaptionMessage: msg?.message?.documentWithCaptionMessage?.message?.documentMessage + ? `documentWithCaptionMessage|${mediaId}${ + msg?.message?.documentWithCaptionMessage?.message?.documentMessage?.caption + ? `|${msg?.message?.documentWithCaptionMessage?.message?.documentMessage?.caption}` + : '' + }` : undefined, }; @@ -1303,10 +1319,28 @@ export class OpenaiService { session = data.session; } - await this.client.beta.threads.messages.create(data.session.sessionId, { + const messageData: any = { role: 'user', - content, - }); + content: [{ type: 'text', text: content }], + }; + + if (this.isImageMessage(content)) { + const contentSplit = content.split('|'); + + const url = contentSplit[1].split('?')[0]; + + messageData.content = [ + { type: 'text', text: contentSplit[2] || content }, + { + type: 'image_url', + image_url: { + url: url, + }, + }, + ]; + } + + await this.client.beta.threads.messages.create(data.session.sessionId, messageData); const runAssistant = await this.client.beta.threads.runs.create(data.session.sessionId, { assistant_id: openaiBot.assistantId, @@ -1420,6 +1454,10 @@ export class OpenaiService { } } + private isImageMessage(content: string) { + return content.includes('imageMessage'); + } + private async processOpenaiAssistant( instance: any, remoteJid: string, @@ -1531,10 +1569,28 @@ export class OpenaiService { const threadId = session.sessionId; - await this.client.beta.threads.messages.create(threadId, { + const messageData: any = { role: 'user', - content, - }); + content: [{ type: 'text', text: content }], + }; + + if (this.isImageMessage(content)) { + const contentSplit = content.split('|'); + + const url = contentSplit[1].split('?')[0]; + + messageData.content = [ + { type: 'text', text: contentSplit[2] || content }, + { + type: 'image_url', + image_url: { + url: url, + }, + }, + ]; + } + + await this.client.beta.threads.messages.create(threadId, messageData); const runAssistant = await this.client.beta.threads.runs.create(threadId, { assistant_id: openaiBot.assistantId, @@ -1654,15 +1710,28 @@ export class OpenaiService { }; }); - const messages: any[] = [ - ...messagesSystem, - ...messagesAssistant, - ...messagesUser, - { - role: 'user', - content: content, - }, - ]; + const messageData: any = { + role: 'user', + content: [{ type: 'text', text: content }], + }; + + if (this.isImageMessage(content)) { + const contentSplit = content.split('|'); + + const url = contentSplit[1].split('?')[0]; + + messageData.content = [ + { type: 'text', text: contentSplit[2] || content }, + { + type: 'image_url', + image_url: { + url: url, + }, + }, + ]; + } + + const messages: any[] = [...messagesSystem, ...messagesAssistant, ...messagesUser, messageData]; await instance.client.presenceSubscribe(remoteJid); @@ -1838,15 +1907,28 @@ export class OpenaiService { }; }); - const messages: any[] = [ - ...messagesSystem, - ...messagesAssistant, - ...messagesUser, - { - role: 'user', - content: content, - }, - ]; + const messageData: any = { + role: 'user', + content: [{ type: 'text', text: content }], + }; + + if (this.isImageMessage(content)) { + const contentSplit = content.split('|'); + + const url = contentSplit[1].split('?')[0]; + + messageData.content = [ + { type: 'text', text: contentSplit[2] || content }, + { + type: 'image_url', + image_url: { + url: url, + }, + }, + ]; + } + + const messages: any[] = [...messagesSystem, ...messagesAssistant, ...messagesUser, messageData]; await instance.client.presenceSubscribe(remoteJid); From c4f91ead54023f13b0fc2c4fa741cf7358b193f6 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 12:31:12 -0300 Subject: [PATCH 03/14] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f047851e..74b6e818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * Changed labels to be unique on the same instance * Remove instance from redis even if using database * Unified integration session system so they don't overlap +* Temporary fix for pictureUrl bug in groups # 2.0.9-rc (2024-08-09 18:00) From 285a950c67872008939f930b9369b5d6726515c7 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 12:31:47 -0300 Subject: [PATCH 04/14] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74b6e818..1d8b5736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 2.0.9 (pre release) +# 2.0.9 (2024-08-15 12:31) ### Features From 975f41d58ed40fca48f21fbed4ad57853174eef0 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 13:13:08 -0300 Subject: [PATCH 05/14] fix: migrations --- CHANGELOG.md | 1 + Docker/scripts/deploy_database.sh | 8 +- Docker/scripts/generate_database.sh | 2 +- Dockerfile | 4 +- package.json | 2 +- .../migration.sql | 234 ++++++++++-------- .../migration.sql | 9 + 7 files changed, 153 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d8b5736..bbfb2230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Remove instance from redis even if using database * Unified integration session system so they don't overlap * Temporary fix for pictureUrl bug in groups +* Fix on migrations # 2.0.9-rc (2024-08-09 18:00) diff --git a/Docker/scripts/deploy_database.sh b/Docker/scripts/deploy_database.sh index a3cf379d..fde46d12 100755 --- a/Docker/scripts/deploy_database.sh +++ b/Docker/scripts/deploy_database.sh @@ -10,16 +10,16 @@ if [[ "$DATABASE_PROVIDER" == "postgresql" || "$DATABASE_PROVIDER" == "mysql" ]] export DATABASE_URL echo "Deploying migrations for $DATABASE_PROVIDER" echo "Database URL: $DATABASE_URL" - rm -rf ./prisma/migrations - cp -r ./prisma/$DATABASE_PROVIDER-migrations ./prisma/migrations - npx prisma migrate deploy --schema ./prisma/$DATABASE_PROVIDER-schema.prisma + # rm -rf ./prisma/migrations + # cp -r ./prisma/$DATABASE_PROVIDER-migrations ./prisma/migrations + npm run db:deploy if [ $? -ne 0 ]; then echo "Migration failed" exit 1 else echo "Migration succeeded" fi - npx prisma generate --schema ./prisma/$DATABASE_PROVIDER-schema.prisma + npm run db:generate if [ $? -ne 0 ]; then echo "Prisma generate failed" exit 1 diff --git a/Docker/scripts/generate_database.sh b/Docker/scripts/generate_database.sh index 570a60d8..892682ef 100644 --- a/Docker/scripts/generate_database.sh +++ b/Docker/scripts/generate_database.sh @@ -10,7 +10,7 @@ if [[ "$DATABASE_PROVIDER" == "postgresql" || "$DATABASE_PROVIDER" == "mysql" ]] export DATABASE_URL echo "Generating database for $DATABASE_PROVIDER" echo "Database URL: $DATABASE_URL" - npx prisma generate --schema=prisma/$DATABASE_PROVIDER-schema.prisma + npm run db:generate if [ $? -ne 0 ]; then echo "Prisma generate failed" exit 1 diff --git a/Dockerfile b/Dockerfile index c2373396..33ec4a28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM node:20-alpine AS builder RUN apk update && \ apk add git ffmpeg wget curl bash -LABEL version="2.0.9-rc" description="Api to control whatsapp features through http requests." +LABEL version="2.0.9" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL contact="contato@agenciadgcode.com" @@ -18,6 +18,7 @@ COPY ./public ./public COPY ./prisma ./prisma COPY ./manager ./manager COPY ./.env.example ./.env +COPY ./runWithProvider.js ./ COPY ./Docker ./Docker @@ -47,6 +48,7 @@ COPY --from=builder /evolution/manager ./manager COPY --from=builder /evolution/public ./public COPY --from=builder /evolution/.env ./.env COPY --from=builder /evolution/Docker ./Docker +COPY --from=builder /evolution/runWithProvider.js ./runWithProvider.js ENV DOCKER_ENV=true diff --git a/package.json b/package.json index 273caa01..b5f7a252 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "./dist/main.js", "type": "commonjs", "scripts": { - "build": "tsup", + "build": "tsup src/main.ts --out-dir dist", "start": "tsnd -r tsconfig-paths/register --files --transpile-only ./src/main.ts", "start:prod": "node dist/main", "dev:server": "clear && tsnd -r tsconfig-paths/register --files --transpile-only --respawn --ignore-watch node_modules ./src/main.ts", diff --git a/prisma/mysql-migrations/20240813153900_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql b/prisma/mysql-migrations/20240813153900_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql index 5d6d0c04..65dbdb69 100644 --- a/prisma/mysql-migrations/20240813153900_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql +++ b/prisma/mysql-migrations/20240813153900_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql @@ -1,151 +1,185 @@ /* - Warnings: - - - You are about to alter the column `createdAt` on the `Chat` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Chat` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Chatwoot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Chatwoot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Contact` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Contact` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Dify` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Dify` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `DifySession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `DifySession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `DifySetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `DifySetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `disconnectionAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Label` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Label` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Media` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `OpenaiBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `OpenaiBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `OpenaiCreds` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `OpenaiCreds` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `OpenaiSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `OpenaiSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `OpenaiSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `OpenaiSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Proxy` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Proxy` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Rabbitmq` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Rabbitmq` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Session` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Setting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Setting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Sqs` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Sqs` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Template` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Template` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Typebot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Typebot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `TypebotSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `TypebotSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `TypebotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `TypebotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Webhook` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Webhook` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `createdAt` on the `Websocket` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - You are about to alter the column `updatedAt` on the `Websocket` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. - - A unique constraint covering the columns `[remoteJid,instanceId]` on the table `Contact` will be added. If there are existing duplicate values, this will fail. - +Warnings: +- You are about to alter the column `createdAt` on the `Chat` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Chat` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Chatwoot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Chatwoot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Contact` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Contact` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Dify` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Dify` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `DifySession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `DifySession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `DifySetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `DifySetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `disconnectionAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Label` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Label` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Media` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `OpenaiBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `OpenaiBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `OpenaiCreds` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `OpenaiCreds` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `OpenaiSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `OpenaiSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `OpenaiSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `OpenaiSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Proxy` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Proxy` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Rabbitmq` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Rabbitmq` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Session` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Setting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Setting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Sqs` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Sqs` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Template` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Template` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Typebot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Typebot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `TypebotSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `TypebotSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `TypebotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `TypebotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Webhook` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Webhook` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `createdAt` on the `Websocket` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- You are about to alter the column `updatedAt` on the `Websocket` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`. +- A unique constraint covering the columns `[remoteJid,instanceId]` on the table `Contact` will be added. If there are existing duplicate values, this will fail. */ -- AlterTable -ALTER TABLE `Chat` ADD COLUMN `name` VARCHAR(100) NULL, - MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NULL; +ALTER TABLE `Chat` +ADD COLUMN `name` VARCHAR(100) NULL, +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NULL; -- AlterTable -ALTER TABLE `Chatwoot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Chatwoot` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Contact` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NULL; +ALTER TABLE `Contact` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NULL; -- AlterTable -ALTER TABLE `Dify` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Dify` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `DifySession` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `DifySession` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `DifySetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `DifySetting` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Instance` MODIFY `disconnectionAt` TIMESTAMP NULL, - MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NULL; +ALTER TABLE `Instance` +MODIFY `disconnectionAt` TIMESTAMP NULL, +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NULL; -- AlterTable -ALTER TABLE `Label` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Label` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Media` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP; +ALTER TABLE `Media` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP; -- AlterTable -ALTER TABLE `OpenaiBot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `OpenaiBot` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `OpenaiCreds` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `OpenaiCreds` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `OpenaiSession` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `OpenaiSession` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `OpenaiSetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `OpenaiSetting` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Proxy` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Proxy` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Rabbitmq` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Rabbitmq` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Session` MODIFY `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; +ALTER TABLE `Session` +MODIFY `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; -- AlterTable -ALTER TABLE `Setting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Setting` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Sqs` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Sqs` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Template` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Template` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Typebot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NULL; +ALTER TABLE `Typebot` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NULL; -- AlterTable -ALTER TABLE `TypebotSession` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `TypebotSession` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `TypebotSetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `TypebotSetting` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Webhook` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Webhook` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; -- AlterTable -ALTER TABLE `Websocket` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - MODIFY `updatedAt` TIMESTAMP NOT NULL; +ALTER TABLE `Websocket` +MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, +MODIFY `updatedAt` TIMESTAMP NOT NULL; + +-- Remove the duplicates +DELETE c1 +FROM `Contact` c1 + INNER JOIN ( + SELECT MIN(id) as id + FROM `Contact` + GROUP BY + `remoteJid`, `instanceId` + ) c2 ON c1.`remoteJid` = c2.`remoteJid` + AND c1.`instanceId` = c2.`instanceId` + AND c1.id != c2.id; -- CreateIndex -CREATE UNIQUE INDEX `Contact_remoteJid_instanceId_key` ON `Contact`(`remoteJid`, `instanceId`); +CREATE UNIQUE INDEX `Contact_remoteJid_instanceId_key` ON `Contact` (`remoteJid`, `instanceId`); \ No newline at end of file diff --git a/prisma/postgresql-migrations/20240811183328_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql b/prisma/postgresql-migrations/20240811183328_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql index b0289bb2..1adcb7f4 100644 --- a/prisma/postgresql-migrations/20240811183328_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql +++ b/prisma/postgresql-migrations/20240811183328_add_unique_index_for_remoted_jid_and_instance_in_contacts/migration.sql @@ -4,5 +4,14 @@ - A unique constraint covering the columns `[remoteJid,instanceId]` on the table `Contact` will be added. If there are existing duplicate values, this will fail. */ +-- Remove the duplicates +DELETE FROM "Contact" +WHERE ctid NOT IN ( + SELECT min(ctid) + FROM "Contact" + GROUP BY "remoteJid", "instanceId" +); + + -- CreateIndex CREATE UNIQUE INDEX "Contact_remoteJid_instanceId_key" ON "Contact"("remoteJid", "instanceId"); From 60eb923f640ac35a9c8119a957f9059186d8bd0b Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 14:00:24 -0300 Subject: [PATCH 06/14] chore: Update tsup configuration Modified: tsup.config.ts This commit updates the tsup configuration file. The changes made aim to improve the project's build process and optimize the bundle size. No new functionality or bug fixes are introduced in this commit. --- tsup.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tsup.config.ts b/tsup.config.ts index 8ea3c3a7..f1399a13 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -8,4 +8,7 @@ export default defineConfig({ clean: true, minify: true, format: ['cjs', 'esm'], + loader: { + '.json': 'file', + }, }); From 00cb80f1730e3312ed2f7ebda5a417170e3c2ab2 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 14:05:52 -0300 Subject: [PATCH 07/14] fix: docker --- .env.example | 3 --- Dockerfile | 2 ++ package.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 231dc4f1..30a55879 100644 --- a/.env.example +++ b/.env.example @@ -3,9 +3,6 @@ SERVER_PORT=8080 # Server URL - Set your application url SERVER_URL=http://localhost:8080 -TELEMETRY=true -TELEMETRY_URL= - # Cors - * for all or set separate by commas - ex.: 'yourdomain1.com, yourdomain2.com' CORS_ORIGIN=* CORS_METHODS=GET,POST,PUT,DELETE diff --git a/Dockerfile b/Dockerfile index 33ec4a28..5a22601a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ COPY ./prisma ./prisma COPY ./manager ./manager COPY ./.env.example ./.env COPY ./runWithProvider.js ./ +COPY ./tsup.config.ts ./ COPY ./Docker ./Docker @@ -49,6 +50,7 @@ COPY --from=builder /evolution/public ./public COPY --from=builder /evolution/.env ./.env COPY --from=builder /evolution/Docker ./Docker COPY --from=builder /evolution/runWithProvider.js ./runWithProvider.js +COPY --from=builder /evolution/tsup.config.ts ./tsup.config.ts ENV DOCKER_ENV=true diff --git a/package.json b/package.json index b5f7a252..273caa01 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "./dist/main.js", "type": "commonjs", "scripts": { - "build": "tsup src/main.ts --out-dir dist", + "build": "tsup", "start": "tsnd -r tsconfig-paths/register --files --transpile-only ./src/main.ts", "start:prod": "node dist/main", "dev:server": "clear && tsnd -r tsconfig-paths/register --files --transpile-only --respawn --ignore-watch node_modules ./src/main.ts", From a679776f89ea71cd6614274fe43bf28af8fa4b62 Mon Sep 17 00:00:00 2001 From: Judson Cairo Date: Thu, 15 Aug 2024 15:32:17 -0300 Subject: [PATCH 08/14] Fixed chatwoot translation files on build --- tsup.config.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tsup.config.ts b/tsup.config.ts index f1399a13..2450b52f 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,3 +1,5 @@ +import { cpSync } from 'node:fs'; + import { defineConfig } from 'tsup'; export default defineConfig({ @@ -8,6 +10,9 @@ export default defineConfig({ clean: true, minify: true, format: ['cjs', 'esm'], + onSuccess: async () => { + cpSync('src/utils/translations', 'dist/translations', { recursive: true }); + }, loader: { '.json': 'file', }, From 19f029671827b8c68bcb9a608828f8ebcbedfafc Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 15:41:30 -0300 Subject: [PATCH 09/14] fix: chatwoot translations and s3 region --- .env.example | 2 ++ src/api/integrations/s3/libs/minio.server.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 30a55879..2ad41b90 100644 --- a/.env.example +++ b/.env.example @@ -175,6 +175,7 @@ S3_SECRET_KEY= S3_BUCKET=evolution S3_PORT=443 S3_ENDPOINT=s3.domain.com +S3_REGION=eu-west-3 S3_USE_SSL=true # AMAZON S3 - Environment variables @@ -183,6 +184,7 @@ S3_USE_SSL=true # S3_ACCESS_KEY=access_key_id # S3_SECRET_KEY=secret_access_key # S3_ENDPOINT=s3.amazonaws.com # region: s3.eu-west-3.amazonaws.com +# S3_REGION=eu-west-3 # MINIO Use SSL - Environment variables # S3_ENABLED=true diff --git a/src/api/integrations/s3/libs/minio.server.ts b/src/api/integrations/s3/libs/minio.server.ts index 2f8b99cd..70869cd8 100644 --- a/src/api/integrations/s3/libs/minio.server.ts +++ b/src/api/integrations/s3/libs/minio.server.ts @@ -21,7 +21,7 @@ const minioClient = (() => { useSSL: BUCKET.USE_SSL, accessKey: BUCKET.ACCESS_KEY, secretKey: BUCKET.SECRET_KEY, - region: BUCKET.REGION + region: BUCKET.REGION, }); } })(); From fb93c890caccddeef69e848d1c562c2bd25f5fdb Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 15 Aug 2024 20:24:57 -0300 Subject: [PATCH 10/14] feat: dify send images --- .../dify/services/dify.service.ts | 371 +++++++++++++++--- 1 file changed, 315 insertions(+), 56 deletions(-) diff --git a/src/api/integrations/dify/services/dify.service.ts b/src/api/integrations/dify/services/dify.service.ts index 597232b7..7a2cc6ba 100644 --- a/src/api/integrations/dify/services/dify.service.ts +++ b/src/api/integrations/dify/services/dify.service.ts @@ -1118,14 +1118,51 @@ export class DifyService { const message = response?.data?.answer; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1186,14 +1223,51 @@ export class DifyService { const message = response?.data?.answer; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1274,14 +1348,51 @@ export class DifyService { const message = response?.data?.answer; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1346,14 +1457,51 @@ export class DifyService { const message = response?.data?.data.outputs.text; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } if (settings.keepOpen) { await this.prismaRepository.integrationSession.update({ @@ -1523,14 +1671,51 @@ export class DifyService { const message = response?.data?.answer; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1591,14 +1776,51 @@ export class DifyService { const message = response?.data?.answer; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1758,14 +1980,51 @@ export class DifyService { const message = response?.data?.data.outputs.text; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } if (settings.keepOpen) { await this.prismaRepository.integrationSession.update({ From 3ef80bd6c50c3cb4123e788cff87d098e391aa43 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 16 Aug 2024 07:23:34 -0300 Subject: [PATCH 11/14] feat: openai send images --- .../openai/services/openai.service.ts | 212 +++++++++++++++--- 1 file changed, 180 insertions(+), 32 deletions(-) diff --git a/src/api/integrations/openai/services/openai.service.ts b/src/api/integrations/openai/services/openai.service.ts index 880b2b7e..45310162 100644 --- a/src/api/integrations/openai/services/openai.service.ts +++ b/src/api/integrations/openai/services/openai.service.ts @@ -1356,14 +1356,51 @@ export class OpenaiService { const message = response?.data[0].content[0].text.value; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1606,14 +1643,51 @@ export class OpenaiService { const message = response?.data[0].content[0].text.value; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1747,14 +1821,51 @@ export class OpenaiService { const message = completions.choices[0].message.content; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { @@ -1944,14 +2055,51 @@ export class OpenaiService { const message = completions.choices[0].message.content; - await instance.textMessage( - { - number: remoteJid.split('@')[0], - delay: settings?.delayMessage || 1000, - text: message, - }, - false, - ); + const regex = /!?\[(.*?)\]\((.*?)\)/g; + + const result = []; + let lastIndex = 0; + + let match; + while ((match = regex.exec(message)) !== null) { + if (match.index > lastIndex) { + result.push({ text: message.slice(lastIndex, match.index).trim() }); + } + + result.push({ caption: match[1], url: match[2] }); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < message.length) { + result.push({ text: message.slice(lastIndex).trim() }); + } + + for (const item of result) { + if (item.text) { + await instance.textMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + text: item.text, + }, + false, + ); + } + + if (item.url) { + await instance.mediaMessage( + { + number: remoteJid.split('@')[0], + delay: settings?.delayMessage || 1000, + mediatype: 'image', + media: item.url, + caption: item.caption, + }, + false, + ); + } + } await this.prismaRepository.integrationSession.update({ where: { From b921a4d324bfc22efd07500d960dbab224e225c7 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 16 Aug 2024 11:31:46 -0300 Subject: [PATCH 12/14] feat: IA send images and Sentry implemented --- .env.example | 4 +- .eslintrc.js | 66 ++-- CHANGELOG.md | 12 + Docker/swarm/evolution_api_v2.yaml | 3 +- package.json | 9 +- src/api/controllers/instance.controller.ts | 2 +- src/api/guards/auth.guard.ts | 2 +- src/api/guards/instance.guard.ts | 11 +- .../chatwoot/services/chatwoot.service.ts | 2 +- .../chatwoot/utils/chatwoot-import-helper.ts | 7 +- .../channels/whatsapp.baileys.service.ts | 321 ++++++++++-------- src/api/services/monitor.service.ts | 32 +- src/config/env.config.ts | 4 +- src/libs/prisma.connect.ts | 19 +- src/main.ts | 16 +- 15 files changed, 283 insertions(+), 227 deletions(-) diff --git a/.env.example b/.env.example index 2ad41b90..90649eeb 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,8 @@ SERVER_PORT=8080 # Server URL - Set your application url SERVER_URL=http://localhost:8080 +SENTRY_DSN= + # Cors - * for all or set separate by commas - ex.: 'yourdomain1.com, yourdomain2.com' CORS_ORIGIN=* CORS_METHODS=GET,POST,PUT,DELETE @@ -19,8 +21,6 @@ LOG_BAILEYS=error # If you don't even want an expiration, enter the value false DEL_INSTANCE=false -# Permanent data storage -DATABASE_ENABLED=true # Provider: postgresql | mysql DATABASE_PROVIDER=postgresql DATABASE_CONNECTION_URI='postgresql://user:pass@localhost:5432/evolution?schema=public' diff --git a/.eslintrc.js b/.eslintrc.js index f805da92..74a4c2ea 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,50 +1,42 @@ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { - sourceType: 'CommonJS', + sourceType: 'CommonJS', }, - plugins: [ - '@typescript-eslint', - 'simple-import-sort', - 'import' - ], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended' - ], + plugins: ['@typescript-eslint', 'simple-import-sort', 'import'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], globals: { - Atomics: 'readonly', - SharedArrayBuffer: 'readonly', + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', }, root: true, env: { - node: true, - jest: true, + node: true, + jest: true, }, ignorePatterns: ['.eslintrc.js'], rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/no-unused-vars': 'error', - 'import/first': 'error', - 'import/no-duplicates': 'error', - 'simple-import-sort/imports': 'error', - 'simple-import-sort/exports': 'error', - '@typescript-eslint/ban-types': [ - 'error', - { - extendDefaults: true, - types: { - '{}': false, - Object: false, - }, + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-unused-vars': 'error', + 'import/first': 'error', + 'import/no-duplicates': 'error', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + '@typescript-eslint/ban-types': [ + 'error', + { + extendDefaults: true, + types: { + '{}': false, + Object: false, }, - ], - 'prettier/prettier': ['error', { endOfLine: 'auto' }], + }, + ], + 'prettier/prettier': ['error', { endOfLine: 'auto' }], }, -}; \ No newline at end of file +}; diff --git a/CHANGELOG.md b/CHANGELOG.md index bbfb2230..611f4244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 2.1.0 (develop) + +### Features + +* OpenAI send images when markdown +* Dify send images when markdown +* Sentry implemented + +### Fixed + +* Fix on get profilePicture + # 2.0.9 (2024-08-15 12:31) ### Features diff --git a/Docker/swarm/evolution_api_v2.yaml b/Docker/swarm/evolution_api_v2.yaml index 8d63ef55..e46e40d9 100644 --- a/Docker/swarm/evolution_api_v2.yaml +++ b/Docker/swarm/evolution_api_v2.yaml @@ -2,7 +2,7 @@ version: "3.7" services: evolution_v2: - image: atendai/evolution-api:v2.0.9-rc + image: atendai/evolution-api:v2.0.9 volumes: - evolution_instances:/evolution/instances networks: @@ -10,7 +10,6 @@ services: environment: - SERVER_URL=https://evo2.site.com - DEL_INSTANCE=false - - DATABASE_ENABLED=true - DATABASE_PROVIDER=postgresql - DATABASE_CONNECTION_URI=postgresql://postgres:SENHA@postgres:5432/evolution - DATABASE_SAVE_DATA_INSTANCE=true diff --git a/package.json b/package.json index 273caa01..c8e42e0b 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "evolution-api", - "version": "2.0.9", + "version": "2.1.0", "description": "Rest api for communication with WhatsApp", "main": "./dist/main.js", "type": "commonjs", "scripts": { - "build": "tsup", + "build": "tsc --noEmit && tsup", "start": "tsnd -r tsconfig-paths/register --files --transpile-only ./src/main.ts", "start:prod": "node dist/main", "dev:server": "clear && tsnd -r tsconfig-paths/register --files --transpile-only --respawn --ignore-watch node_modules ./src/main.ts", @@ -52,10 +52,11 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@prisma/client": "^5.15.0", - "@sentry/node": "^7.59.2", + "@sentry/node": "^7.119.0", + "@sentry/profiling-node": "^8.26.0", "amqplib": "^0.10.3", "axios": "^1.6.5", - "baileys": "6.7.6", + "baileys": "6.7.5", "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index e5e9a53b..45a5c998 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -570,7 +570,7 @@ export class InstanceController { if (state == 'close') { await instance.connectToWhatsapp(number); - await delay(5000); + await delay(2000); return instance.qrCode; } diff --git a/src/api/guards/auth.guard.ts b/src/api/guards/auth.guard.ts index 89b0874e..9ad20b61 100644 --- a/src/api/guards/auth.guard.ts +++ b/src/api/guards/auth.guard.ts @@ -34,7 +34,7 @@ async function apikey(req: Request, _: Response, next: NextFunction) { return next(); } } else { - if (req.originalUrl.includes('/instance/fetchInstances') && db.ENABLED) { + if (req.originalUrl.includes('/instance/fetchInstances') && db.SAVE_DATA.INSTANCE) { const instanceByKey = await prismaRepository.instance.findFirst({ where: { token: key }, }); diff --git a/src/api/guards/instance.guard.ts b/src/api/guards/instance.guard.ts index 9f8eb090..29c320ec 100644 --- a/src/api/guards/instance.guard.ts +++ b/src/api/guards/instance.guard.ts @@ -1,13 +1,12 @@ import { InstanceDto } from '@api/dto/instance.dto'; import { cache, waMonitor } from '@api/server.module'; -import { CacheConf, configService, Database } from '@config/env.config'; +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) { try { - const db = configService.get('DATABASE'); const cacheConf = configService.get('CACHE'); const exists = !!waMonitor.waInstances[instanceName]; @@ -18,13 +17,9 @@ async function getInstance(instanceName: string) { return exists || keyExists; } - if (db.ENABLED) { - const prisma = prismaServer; + const prisma = prismaServer; - return exists || (await prisma.instance.findMany({ where: { name: instanceName } })).length > 0; - } - - return false; + return exists || (await prisma.instance.findMany({ where: { name: instanceName } })).length > 0; } catch (error) { throw new InternalServerErrorException(error?.toString()); } diff --git a/src/api/integrations/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatwoot/services/chatwoot.service.ts index 3df8795a..0904ac61 100644 --- a/src/api/integrations/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatwoot/services/chatwoot.service.ts @@ -354,7 +354,7 @@ export class ChatwootService { return contact; } catch (error) { - this.logger.error(error); + return null; } } diff --git a/src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts b/src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts index e5f0dbc9..765e9cf3 100644 --- a/src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts +++ b/src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts @@ -289,7 +289,12 @@ class ChatwootImport { this.deleteHistoryMessages(instance); this.deleteRepositoryMessagesCache(instance); - this.importHistoryContacts(instance, provider); + const providerData: ChatwootDto = { + ...provider, + ignoreJids: Array.isArray(provider.ignoreJids) ? provider.ignoreJids.map((event) => String(event)) : [], + }; + + this.importHistoryContacts(instance, providerData); return totalMessagesImported; } catch (error) { diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index 9d5a4334..06c21481 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -68,7 +68,6 @@ import { S3, Typebot, } from '@config/env.config'; -import { INSTANCE_DIR } from '@config/path.config'; import { BadRequestException, InternalServerErrorException, NotFoundException } from '@exceptions'; import ffmpegPath from '@ffmpeg-installer/ffmpeg'; import { Boom } from '@hapi/boom'; @@ -98,6 +97,7 @@ import makeWASocket, { GroupParticipant, isJidBroadcast, isJidGroup, + // isJidNewsletter, isJidUser, makeCacheableSignalKeyStore, MessageUpsertType, @@ -118,10 +118,8 @@ import { LabelAssociation } from 'baileys/lib/Types/LabelAssociation'; import { isBase64, isURL } from 'class-validator'; import { randomBytes } from 'crypto'; import EventEmitter2 from 'eventemitter2'; -// import { exec } from 'child_process'; import ffmpeg from 'fluent-ffmpeg'; -// import ffmpeg from 'fluent-ffmpeg'; -import { existsSync, readFileSync } from 'fs'; +import { readFileSync } from 'fs'; import Long from 'long'; import mime from 'mime'; import NodeCache from 'node-cache'; @@ -148,7 +146,7 @@ export class BaileysStartupService extends ChannelStartupService { ) { super(configService, eventEmitter, prismaRepository, chatwootCache); this.instance.qrcode = { count: 0 }; - this.recoveringMessages(); + // this.recoveringMessages(); this.authStateProvider = new AuthStateProvider(this.providerFiles); } @@ -163,57 +161,57 @@ export class BaileysStartupService extends ChannelStartupService { public phoneNumber: string; - private async recoveringMessages() { - const cacheConf = this.configService.get('CACHE'); + // private async recoveringMessages() { + // const cacheConf = this.configService.get('CACHE'); - if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) { - this.logger.info('Recovering messages lost from cache'); - setInterval(async () => { - this.baileysCache.keys().then((keys) => { - keys.forEach(async (key) => { - const data = await this.baileysCache.get(key.split(':')[2]); + // if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) { + // this.logger.info('Recovering messages lost from cache'); + // setInterval(async () => { + // this.baileysCache.keys().then((keys) => { + // keys.forEach(async (key) => { + // const data = await this.baileysCache.get(key.split(':')[2]); - let message: any; - let retry: number; + // let message: any; + // let retry: number; - if (!data?.message) { - message = data; - retry = 0; - } else { - message = data.message; - retry = data.retry; - } + // if (!data?.message) { + // message = data; + // retry = 0; + // } else { + // message = data.message; + // retry = data.retry; + // } - if (message.messageStubParameters && message.messageStubParameters[0] === 'Message absent from node') { - retry = retry + 1; - this.logger.info(`Message absent from node, retrying to send, key: ${key.split(':')[2]} retry: ${retry}`); - if (message.messageStubParameters[1]) { - await this.client.sendMessageAck(JSON.parse(message.messageStubParameters[1], BufferJSON.reviver)); - } + // if (message.messageStubParameters && message.messageStubParameters[0] === 'Message absent from node') { + // retry = retry + 1; + // this.logger.info(`Message absent from node, retrying to send, key: ${key.split(':')[2]} retry: ${retry}`); + // if (message.messageStubParameters[1]) { + // await this.client.sendMessageAck(JSON.parse(message.messageStubParameters[1], BufferJSON.reviver)); + // } - this.baileysCache.set(key.split(':')[2], { message, retry }); + // this.baileysCache.set(key.split(':')[2], { message, retry }); - if (retry >= 100) { - this.logger.warn(`Message absent from node, retry limit reached, key: ${key.split(':')[2]}`); - this.baileysCache.delete(key.split(':')[2]); - return; - } - } - }); - }); - // 15 minutes - }, 15 * 60 * 1000); - } - } + // if (retry >= 100) { + // this.logger.warn(`Message absent from node, retry limit reached, key: ${key.split(':')[2]}`); + // this.baileysCache.delete(key.split(':')[2]); + // return; + // } + // } + // }); + // }); + // // 15 minutes + // }, 15 * 60 * 1000); + // } + // } - private async forceUpdateGroupMetadataCache() { - this.logger.verbose('Force update group metadata cache'); - const groups = await this.fetchAllGroups({ getParticipants: 'false' }); + // private async forceUpdateGroupMetadataCache() { + // this.logger.verbose('Force update group metadata cache'); + // const groups = await this.fetchAllGroups({ getParticipants: 'false' }); - for (const group of groups) { - await this.updateGroupMetadataCache(group.id); - } - } + // for (const group of groups) { + // await this.updateGroupMetadataCache(group.id); + // } + // } public get connectionStatus() { return this.stateConnection; @@ -239,21 +237,12 @@ export class BaileysStartupService extends ChannelStartupService { public async getProfileName() { let profileName = this.client.user?.name ?? this.client.user?.verifiedName; if (!profileName) { - if (this.configService.get('DATABASE').ENABLED) { - const data = await this.prismaRepository.session.findUnique({ - where: { sessionId: this.instanceId }, - }); + const data = await this.prismaRepository.session.findUnique({ + where: { sessionId: this.instanceId }, + }); - if (data) { - const creds = JSON.parse(JSON.stringify(data.creds), BufferJSON.reviver); - profileName = creds.me?.name || creds.me?.verifiedName; - } - } else if (existsSync(join(INSTANCE_DIR, this.instanceName, 'creds.json'))) { - const creds = JSON.parse( - readFileSync(join(INSTANCE_DIR, this.instanceName, 'creds.json'), { - encoding: 'utf-8', - }), - ); + if (data) { + const creds = JSON.parse(JSON.stringify(data.creds), BufferJSON.reviver); profileName = creds.me?.name || creds.me?.verifiedName; } } @@ -404,17 +393,15 @@ export class BaileysStartupService extends ChannelStartupService { disconnectionObject: JSON.stringify(lastDisconnect), }); - if (this.configService.get('DATABASE').ENABLED) { - await this.prismaRepository.instance.update({ - where: { id: this.instanceId }, - data: { - connectionStatus: 'close', - disconnectionAt: new Date(), - disconnectionReasonCode: statusCode, - disconnectionObject: JSON.stringify(lastDisconnect), - }, - }); - } + await this.prismaRepository.instance.update({ + where: { id: this.instanceId }, + data: { + connectionStatus: 'close', + disconnectionAt: new Date(), + disconnectionReasonCode: statusCode, + disconnectionObject: JSON.stringify(lastDisconnect), + }, + }); if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot.enabled) { this.chatwootService.eventWhatsapp( @@ -462,17 +449,15 @@ export class BaileysStartupService extends ChannelStartupService { `, ); - if (this.configService.get('DATABASE').ENABLED) { - await this.prismaRepository.instance.update({ - where: { id: this.instanceId }, - data: { - ownerJid: this.instance.wuid, - profileName: (await this.getProfileName()) as string, - profilePicUrl: this.instance.profilePictureUrl, - connectionStatus: 'open', - }, - }); - } + await this.prismaRepository.instance.update({ + where: { id: this.instanceId }, + data: { + ownerJid: this.instance.wuid, + profileName: (await this.getProfileName()) as string, + profilePicUrl: this.instance.profilePictureUrl, + connectionStatus: 'open', + }, + }); if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot.enabled) { this.chatwootService.eventWhatsapp( @@ -520,6 +505,7 @@ export class BaileysStartupService extends ChannelStartupService { return webMessageInfo[0].message; } catch (error) { + this.logger.error('line 508'); return { conversation: '' }; } } @@ -539,7 +525,7 @@ export class BaileysStartupService extends ChannelStartupService { return await useMultiFileAuthStateRedisDb(this.instance.id, this.cache); } - if (db.SAVE_DATA.INSTANCE && db.ENABLED) { + if (db.SAVE_DATA.INSTANCE) { return await useMultiFileAuthStatePrisma(this.instance.id, this.cache); } } @@ -619,56 +605,40 @@ export class BaileysStartupService extends ChannelStartupService { const socketConfig: UserFacingSocketConfig = { ...options, + version, + logger: P({ level: this.logBaileys }), + printQRInTerminal: false, auth: { creds: this.instance.authState.state.creds, keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' }) as any), }, - logger: P({ level: this.logBaileys }), - printQRInTerminal: false, + msgRetryCounterCache: this.msgRetryCounterCache, + generateHighQualityLinkPreview: true, + getMessage: async (key) => (await this.getMessage(key)) as Promise, ...browserOptions, - version, markOnlineOnConnect: this.localSettings.alwaysOnline, retryRequestDelayMs: 350, maxMsgRetryCount: 4, fireInitQueries: true, - connectTimeoutMs: 20_000, + connectTimeoutMs: 30_000, keepAliveIntervalMs: 30_000, qrTimeout: 45_000, emitOwnEvents: false, shouldIgnoreJid: (jid) => { const isGroupJid = this.localSettings.groupsIgnore && isJidGroup(jid); const isBroadcast = !this.localSettings.readStatus && isJidBroadcast(jid); - const isNewsletter = jid ? jid.includes('newsletter') : false; + // const isNewsletter = !isJidNewsletter(jid); + const isNewsletter = jid && jid.includes('newsletter'); return isGroupJid || isBroadcast || isNewsletter; }, - msgRetryCounterCache: this.msgRetryCounterCache, - getMessage: async (key) => (await this.getMessage(key)) as Promise, - generateHighQualityLinkPreview: true, syncFullHistory: this.localSettings.syncFullHistory, shouldSyncHistoryMessage: (msg: proto.Message.IHistorySyncNotification) => { return this.historySyncNotification(msg); }, + // cachedGroupMetadata: this.getGroupMetadataCache, userDevicesCache: this.userDevicesCache, transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 3000 }, - patchMessageBeforeSending(message) { - if ( - message.deviceSentMessage?.message?.listMessage?.listType === proto.Message.ListMessage.ListType.PRODUCT_LIST - ) { - message = JSON.parse(JSON.stringify(message)); - - message.deviceSentMessage.message.listMessage.listType = proto.Message.ListMessage.ListType.SINGLE_SELECT; - } - - if (message.listMessage?.listType == proto.Message.ListMessage.ListType.PRODUCT_LIST) { - message = JSON.parse(JSON.stringify(message)); - - message.listMessage.listType = proto.Message.ListMessage.ListType.SINGLE_SELECT; - } - - return message; - }, - forceGroupsPrekeys: false, }; this.endSession = false; @@ -694,6 +664,7 @@ export class BaileysStartupService extends ChannelStartupService { return await this.createClient(number); } catch (error) { + this.logger.error('line 667'); this.logger.error(error); throw new InternalServerErrorException(error?.toString()); } @@ -703,6 +674,7 @@ export class BaileysStartupService extends ChannelStartupService { try { return await this.createClient(this.phoneNumber); } catch (error) { + this.logger.error('line 677'); this.logger.error(error); throw new InternalServerErrorException(error?.toString()); } @@ -820,7 +792,7 @@ export class BaileysStartupService extends ChannelStartupService { if (updatedContacts.length > 0) { await Promise.all( - updatedContacts.map(async function (contact) { + updatedContacts.map(async (contact) => { const update = this.prismaRepository.contact.updateMany({ where: { remoteJid: contact.remoteJid, instanceId: this.instanceId }, data: { @@ -830,7 +802,7 @@ export class BaileysStartupService extends ChannelStartupService { const instance = { instanceName: this.instance.name, instanceId: this.instance.id }; - const findParticipant = await this.findContact(instance, contact.remoteJid.split('@')[0]); + const findParticipant = await this.chatwootService.findContact(instance, contact.remoteJid.split('@')[0]); this.chatwootService.updateContact(instance, findParticipant.id, { name: contact.pushName, @@ -842,6 +814,7 @@ export class BaileysStartupService extends ChannelStartupService { ); } } catch (error) { + this.logger.error('line 817'); this.logger.error(`Error: ${error.message}`); } }, @@ -880,13 +853,25 @@ export class BaileysStartupService extends ChannelStartupService { messages, chats, contacts, + isLatest, + progress, + syncType, }: { chats: Chat[]; contacts: Contact[]; messages: proto.IWebMessageInfo[]; - isLatest: boolean; + isLatest?: boolean; + progress?: number; + syncType?: proto.HistorySync.HistorySyncType; }) => { try { + if (syncType === proto.HistorySync.HistorySyncType.ON_DEMAND) { + console.log('received on-demand history sync, messages=', messages); + } + console.log( + `recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest}, progress: ${progress}%), type: ${syncType}`, + ); + const instance: InstanceDto = { instanceName: this.instance.name }; let timestampLimitToImport = null; @@ -1023,6 +1008,7 @@ export class BaileysStartupService extends ChannelStartupService { messages = undefined; chats = undefined; } catch (error) { + this.logger.error('line 1011'); this.logger.error(error); } }, @@ -1031,14 +1017,31 @@ export class BaileysStartupService extends ChannelStartupService { { messages, type, + requestId, }: { messages: proto.IWebMessageInfo[]; type: MessageUpsertType; + requestId?: string; }, settings: any, ) => { try { for (const received of messages) { + if (received.message?.conversation || received.message?.extendedTextMessage?.text) { + const text = received.message?.conversation || received.message?.extendedTextMessage?.text; + if (text == 'requestPlaceholder' && !requestId) { + // const messageId = await this.client.requestPlaceholderResend(received.key); + // console.log('requested placeholder resync, id=', messageId); + } else if (requestId) { + console.log('Message received from phone, id=', requestId, received); + } + + if (text == 'onDemandHistSync') { + // const messageId = await this.client.fetchMessageHistory(50, received.key, received.messageTimestamp!); + // console.log('requested on-demand sync, id=', messageId); + } + } + if (received.message?.protocolMessage?.editedMessage || received.message?.editedMessage?.message) { const editedMessage = received.message?.protocolMessage || received.message?.editedMessage?.message?.protocolMessage; @@ -1175,6 +1178,7 @@ export class BaileysStartupService extends ChannelStartupService { messageRaw.message.mediaUrl = mediaUrl; } catch (error) { + this.logger.error('line 1181'); this.logger.error(['Error on upload file to minio', error?.message, error?.stack]); } } @@ -1311,6 +1315,7 @@ export class BaileysStartupService extends ChannelStartupService { }); } } catch (error) { + this.logger.error('line 1318'); this.logger.error(error); } }, @@ -1489,7 +1494,7 @@ export class BaileysStartupService extends ChannelStartupService { data: { association: LabelAssociation; type: 'remove' | 'add' }, database: Database, ) => { - if (database.ENABLED && database.SAVE_DATA.CHATS) { + if (database.SAVE_DATA.CHATS) { const chats = await this.prismaRepository.chat.findMany({ where: { instanceId: this.instanceId }, }); @@ -1512,7 +1517,6 @@ export class BaileysStartupService extends ChannelStartupService { } } - // Envia dados para o webhook this.sendDataWebhook(Events.LABELS_ASSOCIATION, { instance: this.instance.name, type: data.type, @@ -1559,7 +1563,7 @@ export class BaileysStartupService extends ChannelStartupService { if (events['messaging-history.set']) { const payload = events['messaging-history.set']; - this.messageHandle['messaging-history.set'](payload as any); + this.messageHandle['messaging-history.set'](payload); } if (events['messages.upsert']) { @@ -1671,9 +1675,9 @@ export class BaileysStartupService extends ChannelStartupService { public async profilePicture(number: string) { const jid = this.createJid(number); - const profilePictureUrl = await this.client.profilePictureUrl(jid, 'image'); - try { + const profilePictureUrl = await this.client.profilePictureUrl(jid, 'image'); + return { wuid: jid, profilePictureUrl, @@ -1772,11 +1776,11 @@ export class BaileysStartupService extends ChannelStartupService { }; if (isJidGroup(sender)) { + option.useCachedGroupMetadata = true; if (participants) option.cachedGroupMetadata = async () => { return { participants: participants as GroupParticipant[] }; }; - else option.cachedGroupMetadata = this.getGroupMetadataCache; } if (ephemeralExpiration) option.ephemeralExpiration = ephemeralExpiration; @@ -2003,7 +2007,7 @@ export class BaileysStartupService extends ChannelStartupService { quoted, null, group?.ephemeralDuration, - group.participants, + // group?.participants, ); } else { messageSent = await this.sendMessage(sender, message, mentions, linkPreview, quoted); @@ -2074,6 +2078,7 @@ export class BaileysStartupService extends ChannelStartupService { return messageSent; } catch (error) { + this.logger.error('line 2081'); this.logger.error(error); throw new BadRequestException(error.toString()); } @@ -2126,6 +2131,7 @@ export class BaileysStartupService extends ChannelStartupService { return { presence: data.presence }; } catch (error) { + this.logger.error('line 2134'); this.logger.error(error); throw new BadRequestException(error.toString()); } @@ -2138,6 +2144,7 @@ export class BaileysStartupService extends ChannelStartupService { return { presence: data.presence }; } catch (error) { + this.logger.error('line 2147'); this.logger.error(error); throw new BadRequestException(error.toString()); } @@ -2368,6 +2375,7 @@ export class BaileysStartupService extends ChannelStartupService { { userJid: this.instance.wuid }, ); } catch (error) { + this.logger.error('line 2378'); this.logger.error(error); throw new InternalServerErrorException(error?.toString() || error); } @@ -2409,6 +2417,7 @@ export class BaileysStartupService extends ChannelStartupService { return webpBuffer; } catch (error) { + this.logger.error('line 2420'); console.error('Erro ao converter a imagem para WebP:', error); throw error; } @@ -2806,6 +2815,7 @@ export class BaileysStartupService extends ChannelStartupService { await this.client.readMessages(keys); return { message: 'Read messages', read: 'success' }; } catch (error) { + this.logger.error('line 2818'); throw new InternalServerErrorException('Read messages fail', error.toString()); } } @@ -2871,6 +2881,7 @@ export class BaileysStartupService extends ChannelStartupService { archived: true, }; } catch (error) { + this.logger.error('line 2884'); throw new InternalServerErrorException({ archived: false, message: ['An error occurred while archiving the chat. Open a calling.', error.toString()], @@ -2908,6 +2919,7 @@ export class BaileysStartupService extends ChannelStartupService { markedChatUnread: true, }; } catch (error) { + this.logger.error('line 2922'); throw new InternalServerErrorException({ markedChatUnread: false, message: ['An error occurred while marked unread the chat. Open a calling.', error.toString()], @@ -2919,6 +2931,7 @@ export class BaileysStartupService extends ChannelStartupService { try { return await this.client.sendMessage(del.remoteJid, { delete: del }); } catch (error) { + this.logger.error('line 2934'); throw new InternalServerErrorException('Error while deleting message for everyone', error?.toString()); } } @@ -3010,6 +3023,7 @@ export class BaileysStartupService extends ChannelStartupService { buffer: getBuffer ? buffer : null, }; } catch (error) { + this.logger.error('line 3026'); this.logger.error(error); throw new BadRequestException(error.toString()); } @@ -3051,6 +3065,7 @@ export class BaileysStartupService extends ChannelStartupService { }, }; } catch (error) { + this.logger.error('line 3068'); throw new InternalServerErrorException('Error updating privacy settings', error.toString()); } } @@ -3076,6 +3091,7 @@ export class BaileysStartupService extends ChannelStartupService { ...profile, }; } catch (error) { + this.logger.error('line 3094'); throw new InternalServerErrorException('Error updating profile name', error.toString()); } } @@ -3086,6 +3102,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3105'); throw new InternalServerErrorException('Error updating profile name', error.toString()); } } @@ -3096,6 +3113,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3116'); throw new InternalServerErrorException('Error updating profile status', error.toString()); } } @@ -3137,6 +3155,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3158'); throw new InternalServerErrorException('Error updating profile picture', error.toString()); } } @@ -3149,6 +3168,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3171'); throw new InternalServerErrorException('Error removing profile picture', error.toString()); } } @@ -3169,6 +3189,7 @@ export class BaileysStartupService extends ChannelStartupService { return { block: 'success' }; } catch (error) { + this.logger.error('line 3192'); throw new InternalServerErrorException('Error blocking user', error.toString()); } } @@ -3199,6 +3220,7 @@ export class BaileysStartupService extends ChannelStartupService { return null; } catch (error) { + this.logger.error('line 3223'); this.logger.error(error); throw new BadRequestException(error.toString()); } @@ -3220,6 +3242,7 @@ export class BaileysStartupService extends ChannelStartupService { edit: data.key, }); } catch (error) { + this.logger.error('line 3245'); this.logger.error(error); throw new BadRequestException(error.toString()); } @@ -3262,6 +3285,7 @@ export class BaileysStartupService extends ChannelStartupService { return { numberJid: contact.jid, labelId: data.labelId, remove: true }; } } catch (error) { + this.logger.error('line 3288'); throw new BadRequestException(`Unable to ${data.action} label to chat`, error.toString()); } } @@ -3271,14 +3295,19 @@ export class BaileysStartupService extends ChannelStartupService { try { const meta = await this.client.groupMetadata(groupJid); - this.logger.verbose(`Updating cache for group: ${groupJid}`); - await groupMetadataCache.set(groupJid, { - timestamp: Date.now(), - data: meta, - }); + const cacheConf = this.configService.get('CACHE'); + + if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) { + this.logger.verbose(`Updating cache for group: ${groupJid}`); + await groupMetadataCache.set(groupJid, { + timestamp: Date.now(), + data: meta, + }); + } return meta; } catch (error) { + this.logger.error('line 3310'); this.logger.error(error); return null; } @@ -3287,19 +3316,25 @@ export class BaileysStartupService extends ChannelStartupService { private async getGroupMetadataCache(groupJid: string) { if (!isJidGroup(groupJid)) return null; - if (await groupMetadataCache.has(groupJid)) { - console.log(`Cache request for group: ${groupJid}`); - const meta = await groupMetadataCache.get(groupJid); + const cacheConf = this.configService.get('CACHE'); - if (Date.now() - meta.timestamp > 3600000) { - await this.updateGroupMetadataCache(groupJid); + if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) { + if (await groupMetadataCache.has(groupJid)) { + console.log(`Cache request for group: ${groupJid}`); + const meta = await groupMetadataCache.get(groupJid); + + if (Date.now() - meta.timestamp > 3600000) { + await this.updateGroupMetadataCache(groupJid); + } + + return meta.data; } - return meta.data; + console.log(`Cache request for group: ${groupJid} - not found`); + return await this.updateGroupMetadataCache(groupJid); } - console.log(`Cache request for group: ${groupJid} - not found`); - return await this.updateGroupMetadataCache(groupJid); + return await this.findGroup({ groupJid }, 'inner'); } public async createGroup(create: CreateGroupDto) { @@ -3325,6 +3360,7 @@ export class BaileysStartupService extends ChannelStartupService { return group; } catch (error) { + this.logger.error('line 3363'); this.logger.error(error); throw new InternalServerErrorException('Error creating group', error.toString()); } @@ -3364,6 +3400,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3403'); throw new InternalServerErrorException('Error update group picture', error.toString()); } } @@ -3374,6 +3411,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3414'); throw new InternalServerErrorException('Error updating group subject', error.toString()); } } @@ -3384,6 +3422,7 @@ export class BaileysStartupService extends ChannelStartupService { return { update: 'success' }; } catch (error) { + this.logger.error('line 3425'); throw new InternalServerErrorException('Error updating group description', error.toString()); } } @@ -3418,6 +3457,7 @@ export class BaileysStartupService extends ChannelStartupService { if (reply === 'inner') { return; } + this.logger.error('line 3460'); throw new NotFoundException('Error fetching group', error.toString()); } } @@ -3427,8 +3467,7 @@ export class BaileysStartupService extends ChannelStartupService { let groups = []; for (const group of fetch) { - const picture = null; - // const picture = await this.profilePicture(group.id); + const picture = await this.profilePicture(group.id); const result = { id: group.id, @@ -3460,6 +3499,7 @@ export class BaileysStartupService extends ChannelStartupService { const code = await this.client.groupInviteCode(id.groupJid); return { inviteUrl: `https://chat.whatsapp.com/${code}`, inviteCode: code }; } catch (error) { + this.logger.error('line 3502'); throw new NotFoundException('No invite code', error.toString()); } } @@ -3468,6 +3508,7 @@ export class BaileysStartupService extends ChannelStartupService { try { return await this.client.groupGetInviteInfo(id.inviteCode); } catch (error) { + this.logger.error('line 3511'); throw new NotFoundException('No invite info', id.inviteCode); } } @@ -3493,6 +3534,7 @@ export class BaileysStartupService extends ChannelStartupService { return { send: true, inviteUrl }; } catch (error) { + this.logger.error('line 3537'); throw new NotFoundException('No send invite'); } } @@ -3502,6 +3544,7 @@ export class BaileysStartupService extends ChannelStartupService { const groupJid = await this.client.groupAcceptInvite(id.inviteCode); return { accepted: true, groupJid: groupJid }; } catch (error) { + this.logger.error('line 3547'); throw new NotFoundException('Accept invite error', error.toString()); } } @@ -3511,6 +3554,7 @@ export class BaileysStartupService extends ChannelStartupService { const inviteCode = await this.client.groupRevokeInvite(id.groupJid); return { revoked: true, inviteCode }; } catch (error) { + this.logger.error('line 3557'); throw new NotFoundException('Revoke error', error.toString()); } } @@ -3536,6 +3580,7 @@ export class BaileysStartupService extends ChannelStartupService { }); return { participants: parsedParticipants }; } catch (error) { + this.logger.error('line 3583'); throw new NotFoundException('No participants', error.toString()); } } @@ -3550,6 +3595,7 @@ export class BaileysStartupService extends ChannelStartupService { ); return { updateParticipants: updateParticipants }; } catch (error) { + this.logger.error('line 3598'); throw new BadRequestException('Error updating participants', error.toString()); } } @@ -3559,6 +3605,7 @@ export class BaileysStartupService extends ChannelStartupService { const updateSetting = await this.client.groupSettingUpdate(update.groupJid, update.action); return { updateSetting: updateSetting }; } catch (error) { + this.logger.error('line 3608'); throw new BadRequestException('Error updating setting', error.toString()); } } @@ -3568,6 +3615,7 @@ export class BaileysStartupService extends ChannelStartupService { await this.client.groupToggleEphemeral(update.groupJid, update.expiration); return { success: true }; } catch (error) { + this.logger.error('line 3618'); throw new BadRequestException('Error updating setting', error.toString()); } } @@ -3577,6 +3625,7 @@ export class BaileysStartupService extends ChannelStartupService { await this.client.groupLeave(id.groupJid); return { groupJid: id.groupJid, leave: true }; } catch (error) { + this.logger.error('line 3628'); throw new BadRequestException('Unable to leave the group', error.toString()); } } diff --git a/src/api/services/monitor.service.ts b/src/api/services/monitor.service.ts index a5f20332..2e18229a 100644 --- a/src/api/services/monitor.service.ts +++ b/src/api/services/monitor.service.ts @@ -121,7 +121,7 @@ export class WAMonitoringService { public async cleaningUp(instanceName: string) { let instanceDbId: string; - if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { + if (this.db.SAVE_DATA.INSTANCE) { const instance = await this.prismaRepository.instance.update({ where: { name: instanceName }, data: { connectionStatus: 'close' }, @@ -181,7 +181,7 @@ export class WAMonitoringService { try { if (this.providerSession?.ENABLED) { await this.loadInstancesFromProvider(); - } else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { + } else if (this.db.SAVE_DATA.INSTANCE) { await this.loadInstancesFromDatabasePostgres(); } else if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) { await this.loadInstancesFromRedis(); @@ -193,21 +193,19 @@ export class WAMonitoringService { public async saveInstance(data: any) { try { - if (this.db.ENABLED) { - const clientName = await this.configService.get('DATABASE').CONNECTION.CLIENT_NAME; - await this.prismaRepository.instance.create({ - data: { - id: data.instanceId, - name: data.instanceName, - connectionStatus: data.integration && data.integration === Integration.WHATSAPP_BUSINESS ? 'open' : 'close', - number: data.number, - integration: data.integration || Integration.WHATSAPP_BAILEYS, - token: data.hash, - clientName: clientName, - businessId: data.businessId, - }, - }); - } + const clientName = await this.configService.get('DATABASE').CONNECTION.CLIENT_NAME; + await this.prismaRepository.instance.create({ + data: { + id: data.instanceId, + name: data.instanceName, + connectionStatus: data.integration && data.integration === Integration.WHATSAPP_BUSINESS ? 'open' : 'close', + number: data.number, + integration: data.integration || Integration.WHATSAPP_BAILEYS, + token: data.hash, + clientName: clientName, + businessId: data.businessId, + }, + }); } catch (error) { this.logger.error(error); } diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 7df8dd3a..bc232e89 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -51,7 +51,6 @@ export type DBConnection = { }; export type Database = { CONNECTION: DBConnection; - ENABLED: boolean; PROVIDER: string; SAVE_DATA: SaveData; }; @@ -286,7 +285,6 @@ export class ConfigService { URI: process.env.DATABASE_CONNECTION_URI || '', CLIENT_NAME: process.env.DATABASE_CONNECTION_CLIENT_NAME || 'evolution', }, - ENABLED: process.env?.DATABASE_ENABLED === 'true', PROVIDER: process.env.DATABASE_PROVIDER || 'postgresql', SAVE_DATA: { INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true', @@ -464,7 +462,7 @@ export class ConfigService { ENABLE: process.env?.S3_ENABLED === 'true', PORT: Number.parseInt(process.env?.S3_PORT || '9000'), USE_SSL: process.env?.S3_USE_SSL === 'true', - REGION: process.env?.S3_REGION + REGION: process.env?.S3_REGION, }, AUTHENTICATION: { API_KEY: { diff --git a/src/libs/prisma.connect.ts b/src/libs/prisma.connect.ts index 849d26e2..fa8d6600 100644 --- a/src/libs/prisma.connect.ts +++ b/src/libs/prisma.connect.ts @@ -1,21 +1,16 @@ -import { configService, Database } from '@config/env.config'; import { Logger } from '@config/logger.config'; import { PrismaClient } from '@prisma/client'; const logger = new Logger('Prisma'); -const db = configService.get('DATABASE'); - export const prismaServer = (() => { - if (db.ENABLED) { - logger.verbose('connecting'); - const db = new PrismaClient(); + logger.verbose('connecting'); + const db = new PrismaClient(); - process.on('beforeExit', () => { - logger.verbose('instance destroyed'); - db.$disconnect(); - }); + process.on('beforeExit', () => { + logger.verbose('instance destroyed'); + db.$disconnect(); + }); - return db; - } + return db; })(); diff --git a/src/main.ts b/src/main.ts index d96291e4..0598ae75 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,3 @@ -import 'express-async-errors'; - import { initAMQP, initGlobalQueues } from '@api/integrations/rabbitmq/libs/amqp.server'; import { initSQS } from '@api/integrations/sqs/libs/sqs.server'; import { initIO } from '@api/integrations/websocket/libs/socket.server'; @@ -11,6 +9,7 @@ import { Auth, configService, Cors, HttpServer, ProviderSession, Rabbitmq, Sqs, import { onUnexpectedError } from '@config/error.config'; import { Logger } from '@config/logger.config'; import { ROOT_DIR } from '@config/path.config'; +import * as Sentry from '@sentry/node'; import { ServerUP } from '@utils/server-up'; import axios from 'axios'; import compression from 'compression'; @@ -25,6 +24,19 @@ function initWA() { async function bootstrap() { const logger = new Logger('SERVER'); const app = express(); + const dsn = process.env.SENTRY_DSN; + + if (dsn) { + logger.info('Sentry - ON'); + Sentry.init({ + dsn: dsn, + environment: process.env.NODE_ENV || 'development', + tracesSampleRate: 1.0, + }); + app.use(Sentry.Handlers.requestHandler()); + app.use(Sentry.Handlers.tracingHandler()); + app.use(Sentry.Handlers.errorHandler()); + } let providerFiles: ProviderFiles = null; if (configService.get('PROVIDER').ENABLED) { From 0dbb5f53f16517b37b196e84dcf429977758b32f Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 16 Aug 2024 16:24:34 -0300 Subject: [PATCH 13/14] changelog --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 611f4244..84d7cb59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 2.1.0 (develop) +# 2.0.10 (2024-08-16 16:23) ### Features diff --git a/package.json b/package.json index c8e42e0b..6810b12d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "2.1.0", + "version": "2.0.10", "description": "Rest api for communication with WhatsApp", "main": "./dist/main.js", "type": "commonjs", From a77fa414e5f0b9bb53c8a97efe816fa36903e015 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 16 Aug 2024 17:07:34 -0300 Subject: [PATCH 14/14] changelog --- CHANGELOG.md | 1 + Dockerfile | 2 +- src/api/dto/chat.dto.ts | 11 ++--------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84d7cb59..e6496521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixed * Fix on get profilePicture +* Added S3_REGION on minio settings # 2.0.9 (2024-08-15 12:31) diff --git a/Dockerfile b/Dockerfile index 5a22601a..27b6333c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM node:20-alpine AS builder RUN apk update && \ apk add git ffmpeg wget curl bash -LABEL version="2.0.9" description="Api to control whatsapp features through http requests." +LABEL version="2.0.10" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL contact="contato@agenciadgcode.com" diff --git a/src/api/dto/chat.dto.ts b/src/api/dto/chat.dto.ts index 00da7fdd..fc2ff5d3 100644 --- a/src/api/dto/chat.dto.ts +++ b/src/api/dto/chat.dto.ts @@ -1,11 +1,4 @@ -import { - proto, - WAPresence, - WAPrivacyGroupAddValue, - WAPrivacyOnlineValue, - WAPrivacyValue, - WAReadReceiptsValue, -} from 'baileys'; +import { proto, WAPresence, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from 'baileys'; export class OnWhatsAppDto { constructor( @@ -91,7 +84,7 @@ export class PrivacySettingDto { status: WAPrivacyValue; online: WAPrivacyOnlineValue; last: WAPrivacyValue; - groupadd: WAPrivacyGroupAddValue; + groupadd: WAPrivacyValue; } export class DeleteMessage {