diff --git a/package.json b/package.json index c57c2e9e..ac58c624 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", + "cuid": "^3.0.0", "dayjs": "^1.11.7", "dotenv": "^16.4.5", "eventemitter2": "^6.4.9", diff --git a/prisma/mysql-schema.prisma b/prisma/mysql-schema.prisma index 8ce2f79b..15b3f3ae 100644 --- a/prisma/mysql-schema.prisma +++ b/prisma/mysql-schema.prisma @@ -127,7 +127,7 @@ model Chat { unreadMessages Int @default(0) @@index([instanceId]) @@index([remoteJid]) - @@unique([instanceId, remoteJid]) + @@unique([instanceId, remoteJid]) } model Contact { diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index b84ce254..376fd8cb 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -125,6 +125,7 @@ import { LabelAssociation } from 'baileys/lib/Types/LabelAssociation'; import { spawn } from 'child_process'; import { isArray, isBase64, isURL } from 'class-validator'; import { randomBytes } from 'crypto'; +import cuid from 'cuid'; import EventEmitter2 from 'eventemitter2'; import ffmpeg from 'fluent-ffmpeg'; import FormData from 'form-data'; @@ -1148,7 +1149,10 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CHATS_UPSERT, [chatToInsert]); if (this.configService.get('DATABASE').SAVE_DATA.CHATS) { try { - await this.prismaRepository.chat.create({ + await this.prismaRepository.chat.update({ + where: { + id: existingChat.id, + }, data: chatToInsert, }); } @@ -1489,7 +1493,10 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CHATS_UPSERT, [chatToInsert]); if (this.configService.get('DATABASE').SAVE_DATA.CHATS) { try { - await this.prismaRepository.chat.create({ + await this.prismaRepository.chat.update({ + where: { + id: existingChat.id, + }, data: chatToInsert, }); } @@ -1533,6 +1540,8 @@ export class BaileysStartupService extends ChannelStartupService { private readonly labelHandle = { [Events.LABELS_EDIT]: async (label: Label) => { + this.sendDataWebhook(Events.LABELS_EDIT, { ...label, instance: this.instance.name }); + const labelsRepository = await this.prismaRepository.label.findMany({ where: { instanceId: this.instanceId }, }); @@ -1567,7 +1576,6 @@ export class BaileysStartupService extends ChannelStartupService { create: labelData, }); } - this.sendDataWebhook(Events.LABELS_EDIT, { ...label, instance: this.instance.name }); } }, @@ -1575,26 +1583,18 @@ export class BaileysStartupService extends ChannelStartupService { data: { association: LabelAssociation; type: 'remove' | 'add' }, database: Database, ) => { + this.logger.info( + `labels association - ${data?.association?.chatId} (${data.type}-${data?.association?.type}): ${data?.association?.labelId}`, + ); if (database.SAVE_DATA.CHATS) { - const chats = await this.prismaRepository.chat.findMany({ - where: { instanceId: this.instanceId }, - }); - const chat = chats.find((c) => c.remoteJid === data.association.chatId); - if (chat) { - const labelsArray = Array.isArray(chat.labels) ? chat.labels.map((event) => String(event)) : []; - let labels = [...labelsArray]; - - if (data.type === 'remove') { - labels = labels.filter((label) => label !== data.association.labelId); - } else if (data.type === 'add') { - labels = [...labels, data.association.labelId]; - } - await this.prismaRepository.chat.update({ - where: { id: chat.id }, - data: { - labels, - }, - }); + const instanceId = this.instanceId; + const chatId = data.association.chatId; + const labelId = data.association.labelId; + + if (data.type === 'add') { + await this.addLabel(labelId, instanceId, chatId); + } else if (data.type === 'remove') { + await this.removeLabel(labelId, instanceId, chatId); } } @@ -3884,11 +3884,13 @@ export class BaileysStartupService extends ChannelStartupService { try { if (data.action === 'add') { await this.client.addChatLabel(contact.jid, data.labelId); + await this.addLabel(data.labelId, this.instanceId, contact.jid); return { numberJid: contact.jid, labelId: data.labelId, add: true }; } if (data.action === 'remove') { await this.client.removeChatLabel(contact.jid, data.labelId); + await this.removeLabel(data.labelId, this.instanceId, contact.jid); return { numberJid: contact.jid, labelId: data.labelId, remove: true }; } @@ -4233,6 +4235,7 @@ export class BaileysStartupService extends ChannelStartupService { throw new BadRequestException('Unable to leave the group', error.toString()); } } + public async templateMessage() { throw new Error('Method not available in the Baileys service'); } @@ -4349,4 +4352,52 @@ export class BaileysStartupService extends ChannelStartupService { return unreadMessages; } + + private async addLabel(labelId: string, instanceId: string, chatId: string) { + const id = cuid(); + + await this.prismaRepository.$executeRawUnsafe( + `INSERT INTO "Chat" ("id", "instanceId", "remoteJid", "labels", "createdAt", "updatedAt") + VALUES ($4, $2, $3, to_jsonb(ARRAY[$1]::text[]), NOW(), NOW()) ON CONFLICT ("instanceId", "remoteJid") + DO + UPDATE + SET "labels" = ( + SELECT to_jsonb(array_agg(DISTINCT elem)) + FROM ( + SELECT jsonb_array_elements_text("Chat"."labels") AS elem + UNION + SELECT $1::text AS elem + ) sub + ), + "updatedAt" = NOW();`, + labelId, + instanceId, + chatId, + id, + ); + } + + private async removeLabel(labelId: string, instanceId: string, chatId: string) { + const id = cuid(); + + await this.prismaRepository.$executeRawUnsafe( + `INSERT INTO "Chat" ("id", "instanceId", "remoteJid", "labels", "createdAt", "updatedAt") + VALUES ($4, $2, $3, '[]'::jsonb, NOW(), NOW()) ON CONFLICT ("instanceId", "remoteJid") + DO + UPDATE + SET "labels" = COALESCE ( + ( + SELECT jsonb_agg(elem) + FROM jsonb_array_elements_text("Chat"."labels") AS elem + WHERE elem <> $1 + ), + '[]'::jsonb + ), + "updatedAt" = NOW();`, + labelId, + instanceId, + chatId, + id, + ); + } } diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index 146375cb..84d40676 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -655,8 +655,8 @@ export class ChannelStartupService { (ARRAY_AGG("Message"."sessionId" ORDER BY "Message"."messageTimestamp" DESC))[1] AS last_message_session_id, (ARRAY_AGG("Message"."status" ORDER BY "Message"."messageTimestamp" DESC))[1] AS last_message_status FROM "Chat" - LEFT JOIN "Message" ON "Message"."messageType" != 'reactionMessage' and "Message"."key"->>'remoteJid' = "Chat"."remoteJid" - LEFT JOIN "Contact" ON "Chat"."remoteJid" = "Contact"."remoteJid" + LEFT JOIN "Message" ON "Message"."messageType" != 'reactionMessage' and "Message"."key"->>'remoteJid' = "Chat"."remoteJid" AND "Chat"."instanceId" = "Message"."instanceId" + LEFT JOIN "Contact" ON "Chat"."remoteJid" = "Contact"."remoteJid" AND "Chat"."instanceId" = "Contact"."instanceId" WHERE "Chat"."instanceId" = ${this.instanceId} GROUP BY @@ -690,8 +690,8 @@ export class ChannelStartupService { (ARRAY_AGG("Message"."sessionId" ORDER BY "Message"."messageTimestamp" DESC))[1] AS last_message_session_id, (ARRAY_AGG("Message"."status" ORDER BY "Message"."messageTimestamp" DESC))[1] AS last_message_status FROM "Chat" - LEFT JOIN "Message" ON "Message"."messageType" != 'reactionMessage' and "Message"."key"->>'remoteJid' = "Chat"."remoteJid" - LEFT JOIN "Contact" ON "Chat"."remoteJid" = "Contact"."remoteJid" + LEFT JOIN "Message" ON "Message"."messageType" != 'reactionMessage' and "Message"."key"->>'remoteJid' = "Chat"."remoteJid" AND "Chat"."instanceId" = "Message"."instanceId" + LEFT JOIN "Contact" ON "Chat"."remoteJid" = "Contact"."remoteJid" AND "Chat"."instanceId" = "Contact"."instanceId" WHERE "Chat"."instanceId" = ${this.instanceId} AND "Chat"."remoteJid" = ${remoteJid} and "Message"."messageType" != 'reactionMessage' GROUP BY