diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index fa80872f..21302be3 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -72,7 +72,6 @@ model Session { model Chat { id Int @id @default(autoincrement()) remoteJid String @db.VarChar(100) - lastMsgTimestamp String? @db.VarChar(100) labels Json? @db.JsonB createdAt DateTime? @default(now()) @db.Date updatedAt DateTime? @updatedAt @db.Date @@ -100,7 +99,7 @@ model Message { message Json @db.JsonB contextInfo Json? @db.JsonB source DeviceMessage - messageTimestamp String @db.VarChar(100) + messageTimestamp Int @db.Integer chatwootMessageId Int? @db.Integer chatwootInboxId Int? @db.Integer chatwootConversationId Int? @db.Integer @@ -119,7 +118,7 @@ model MessageUpdate { remoteJid String @db.VarChar(100) fromMe Boolean @db.Boolean participant String? @db.VarChar(100) - dateTime DateTime @db.Date + dateTime Int @db.Integer pollUpdates Json? @db.JsonB status String @db.VarChar(30) Message Message @relation(fields: [messageId], references: [id], onDelete: Cascade) diff --git a/src/api/integrations/typebot/controllers/typebot.controller.ts b/src/api/integrations/typebot/controllers/typebot.controller.ts index e45f88f7..78c48d08 100644 --- a/src/api/integrations/typebot/controllers/typebot.controller.ts +++ b/src/api/integrations/typebot/controllers/typebot.controller.ts @@ -18,8 +18,8 @@ export class TypebotController { } else { const saveData = await this.typebotService.find(instance); - if (saveData.enabled) { - data.sessions = saveData.sessions; + if (saveData?.typebot?.enabled) { + data.sessions = saveData?.sessions; } } diff --git a/src/api/integrations/typebot/services/typebot.service.ts b/src/api/integrations/typebot/services/typebot.service.ts index 37b9ac66..92a1fc9f 100644 --- a/src/api/integrations/typebot/services/typebot.service.ts +++ b/src/api/integrations/typebot/services/typebot.service.ts @@ -2,9 +2,10 @@ import { Message } from '@prisma/client'; import axios from 'axios'; import EventEmitter2 from 'eventemitter2'; -import { ConfigService, Typebot } from '../../../../config/env.config'; +import { Auth, ConfigService, Typebot } from '../../../../config/env.config'; import { Logger } from '../../../../config/logger.config'; import { InstanceDto } from '../../../dto/instance.dto'; +import { PrismaRepository } from '../../../repository/repository.service'; import { WAMonitoringService } from '../../../services/monitor.service'; import { Events } from '../../../types/wa.types'; import { TypebotDto } from '../dto/typebot.dto'; @@ -13,6 +14,7 @@ export class TypebotService { constructor( private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService, + private readonly prismaRepository: PrismaRepository, private readonly eventEmitter: EventEmitter2, ) { this.eventEmitter.on('typebot:end', async (data) => { @@ -31,17 +33,26 @@ export class TypebotService { return { typebot: { ...instance, typebot: data } }; } - public async find(instance: InstanceDto): Promise { + public async find(instance: InstanceDto): Promise { try { - const result = await this.waMonitor.waInstances[instance.instanceName].findTypebot(); + const typebot = await this.waMonitor.waInstances[instance.instanceName].findTypebot(); - if (Object.keys(result).length === 0) { + if (Object.keys(typebot).length === 0) { throw new Error('Typebot not found'); } - return result; + const sessions = await this.prismaRepository.typebotSession.findMany({ + where: { + typebotId: typebot.id, + }, + }); + + return { + typebot, + sessions, + }; } catch (error) { - return { enabled: false, url: '', typebot: '', expire: 0, sessions: [] }; + return null; } } @@ -51,96 +62,55 @@ export class TypebotService { const findData = await this.find(instance); - const session = findData.sessions.find((session) => session.remoteJid === remoteJid); - - if (session) { - if (status === 'closed') { - findData.sessions.splice(findData.sessions.indexOf(session), 1); - - const typebotData = { - enabled: findData.enabled, - url: findData.url, - typebot: findData.typebot, - expire: findData.expire, - keywordFinish: findData.keywordFinish, - delayMessage: findData.delayMessage, - unknownMessage: findData.unknownMessage, - listeningFromMe: findData.listeningFromMe, - sessions: findData.sessions, - }; - - this.create(instance, typebotData); - - return { typebot: { ...instance, typebot: typebotData } }; - } - - findData.sessions.map((session) => { - if (session.remoteJid === remoteJid) { - session.status = status; - } - }); - } else if (status === 'paused') { - // const session: Session = { - // remoteJid: remoteJid, - // sessionId: Math.floor(Math.random() * 10000000000).toString(), - // status: status, - // createdAt: Date.now(), - // updateAt: Date.now(), - // prefilledVariables: { - // remoteJid: remoteJid, - // pushName: '', - // additionalData: {}, - // }, - // }; - // findData.sessions.push(session); - } + const session = await this.prismaRepository.typebotSession.updateMany({ + where: { + typebotId: findData?.typebot?.id, + remoteJid: remoteJid, + }, + data: { + status: status, + }, + }); const typebotData = { - enabled: findData.enabled, - url: findData.url, - typebot: findData.typebot, - expire: findData.expire, - keywordFinish: findData.keywordFinish, - delayMessage: findData.delayMessage, - unknownMessage: findData.unknownMessage, - listeningFromMe: findData.listeningFromMe, - sessions: findData.sessions, - }; - - this.create(instance, typebotData); - - this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_CHANGE_STATUS, { remoteJid: remoteJid, status: status, - url: findData.url, - typebot: findData.typebot, + url: findData?.typebot?.url, + typebot: findData?.typebot?.typebot, session, - }); + }; + + this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_CHANGE_STATUS, typebotData); return { typebot: { ...instance, typebot: typebotData } }; } public async clearSessions(instance: InstanceDto, remoteJid: string) { const findTypebot = await this.find(instance); - const sessions = []; - // const sessions = (findTypebot.sessions as Session[]) ?? []; + const sessions = await this.prismaRepository.typebotSession.findMany({ + where: { + typebotId: findTypebot?.typebot?.id, + remoteJid: remoteJid, + }, + }); - const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid); - - if (sessionWithRemoteJid.length > 0) { - sessionWithRemoteJid.forEach((session) => { - sessions.splice(sessions.indexOf(session), 1); + if (sessions.length > 0) { + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot?.typebot?.id, + remoteJid: remoteJid, + }, }); const typebotData = { - enabled: findTypebot.enabled, - url: findTypebot.url, - typebot: findTypebot.typebot, - expire: findTypebot.expire, - keywordFinish: findTypebot.keywordFinish, - delayMessage: findTypebot.delayMessage, - unknownMessage: findTypebot.unknownMessage, - listeningFromMe: findTypebot.listeningFromMe, + enabled: findTypebot?.typebot?.enabled, + url: findTypebot?.typebot?.url, + typebot: findTypebot?.typebot?.typebot, + expire: findTypebot?.typebot?.expire, + keywordFinish: findTypebot?.typebot?.keywordFinish, + delayMessage: findTypebot?.typebot?.delayMessage, + unknownMessage: findTypebot?.typebot?.unknownMessage, + listeningFromMe: findTypebot?.typebot?.listeningFromMe, sessions, }; @@ -161,17 +131,20 @@ export class TypebotService { const startSession = data.startSession; const variables = data.variables; const findTypebot = await this.find(instance); - const expire = findTypebot.expire; - const keywordFinish = findTypebot.keywordFinish; - const delayMessage = findTypebot.delayMessage; - const unknownMessage = findTypebot.unknownMessage; - const listeningFromMe = findTypebot.listeningFromMe; + const expire = findTypebot?.typebot?.expire; + const keywordFinish = findTypebot?.typebot?.keywordFinish; + const delayMessage = findTypebot?.typebot?.delayMessage; + const unknownMessage = findTypebot?.typebot?.unknownMessage; + const listeningFromMe = findTypebot?.typebot?.listeningFromMe; const prefilledVariables = { remoteJid: remoteJid, instanceName: instance.instanceName, }; + if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) + prefilledVariables['token'] = instance.token; + if (variables?.length) { variables.forEach((variable: { name: string | number; value: string }) => { prefilledVariables[variable.name] = variable.value; @@ -179,10 +152,15 @@ export class TypebotService { } if (startSession) { - const newSessions = await this.clearSessions(instance, remoteJid); + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.typebot.id, + remoteJid: remoteJid, + }, + }); const response = await this.createNewSession(instance, { - enabled: findTypebot.enabled, + enabled: findTypebot?.typebot?.enabled, url: url, typebot: typebot, remoteJid: remoteJid, @@ -191,8 +169,8 @@ export class TypebotService { delayMessage: delayMessage, unknownMessage: unknownMessage, listeningFromMe: listeningFromMe, - sessions: newSessions, prefilledVariables: prefilledVariables, + typebotId: findTypebot.typebot.id, }); if (response.sessionId) { @@ -306,70 +284,6 @@ export class TypebotService { return messageContent; } - private getAudioMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const audioContent = types.audioMessage; - - return audioContent; - } - - private getImageMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const imageContent = types.imageMessage; - - return imageContent; - } - - private getVideoMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const videoContent = types.videoMessage; - - return videoContent; - } - - private getDocumentMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const documentContent = types.documentMessage; - - return documentContent; - } - - private getContactMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const contactContent = types.contactMessage; - - return contactContent; - } - - private getLocationMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const locationContent = types.locationMessage; - - return locationContent; - } - - private getViewOnceMessageV2Content(msg: any) { - const types = this.getTypeMessage(msg); - - const viewOnceContent = types.viewOnceMessageV2; - - return viewOnceContent; - } - - private getListResponseMessageContent(msg: any) { - const types = this.getTypeMessage(msg); - - const listResponseContent = types.listResponseMessage || types.responseRowId; - - return listResponseContent; - } - public async createNewSession(instance: InstanceDto, data: any) { if (data.remoteJid === 'status@broadcast') return; const id = Math.floor(Math.random() * 10000000000).toString(); @@ -407,33 +321,22 @@ export class TypebotService { const request = await axios.post(url, reqData); if (request?.data?.sessionId) { - data.sessions.push({ - remoteJid: data.remoteJid, - sessionId: `${id}-${request.data.sessionId}`, - status: 'opened', - createdAt: Date.now(), - updateAt: Date.now(), - prefilledVariables: { - ...data.prefilledVariables, + await this.prismaRepository.typebotSession.create({ + data: { remoteJid: data.remoteJid, pushName: data.pushName || '', - instanceName: instance.instanceName, + sessionId: `${id}-${request.data.sessionId}`, + status: 'opened', + prefilledVariables: { + ...data.prefilledVariables, + remoteJid: data.remoteJid, + pushName: data.pushName || '', + instanceName: instance.instanceName, + }, + typebotId: data.typebotId, + instanceId: instance.instanceId, }, }); - - const typebotData = { - enabled: data.enabled, - url: data.url, - typebot: data.typebot, - expire: data.expire, - keywordFinish: data.keywordFinish, - delayMessage: data.delayMessage, - unknownMessage: data.unknownMessage, - listeningFromMe: data.listeningFromMe, - sessions: data.sessions, - }; - - this.create(instance, typebotData); } return request.data; } catch (error) { @@ -636,16 +539,14 @@ export class TypebotService { public async sendTypebot(instance: InstanceDto, remoteJid: string, msg: Message) { const findTypebot = await this.find(instance); - const url = findTypebot.url; - const typebot = findTypebot.typebot; - // const sessions = (findTypebot.sessions as Session[]) ?? []; - const sessions = []; - const expire = findTypebot.expire; - const keywordFinish = findTypebot.keywordFinish; - const delayMessage = findTypebot.delayMessage; - const unknownMessage = findTypebot.unknownMessage; - const listeningFromMe = findTypebot.listeningFromMe; - const messageType = this.getTypeMessage(msg.message).messageType; + const url = findTypebot.typebot?.url; + const typebot = findTypebot.typebot?.typebot; + const sessions = findTypebot.sessions; + const expire = findTypebot.typebot?.expire; + const keywordFinish = findTypebot.typebot?.keywordFinish; + const delayMessage = findTypebot.typebot?.delayMessage; + const unknownMessage = findTypebot.typebot?.unknownMessage; + const listeningFromMe = findTypebot.typebot?.listeningFromMe; const session = sessions.find((session) => session.remoteJid === remoteJid); @@ -653,15 +554,22 @@ export class TypebotService { if (session && expire && expire > 0) { const now = Date.now(); - const diff = now - session.updateAt; + const sessionUpdatedAt = new Date(session.updatedAt).getTime(); + + const diff = now - sessionUpdatedAt; const diffInMinutes = Math.floor(diff / 1000 / 60); if (diffInMinutes > expire) { - const newSessions = await this.clearSessions(instance, remoteJid); + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.typebot.id, + remoteJid: remoteJid, + }, + }); const data = await this.createNewSession(instance, { - enabled: findTypebot.enabled, + enabled: findTypebot.typebot.enabled, url: url, typebot: typebot, expire: expire, @@ -669,9 +577,9 @@ export class TypebotService { delayMessage: delayMessage, unknownMessage: unknownMessage, listeningFromMe: listeningFromMe, - sessions: newSessions, remoteJid: remoteJid, pushName: msg.pushName, + typebotId: findTypebot.typebot.id, }); await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions); @@ -691,22 +599,12 @@ export class TypebotService { } if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { - const newSessions = await this.clearSessions(instance, remoteJid); - - const typebotData = { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - sessions: newSessions, - }; - - this.create(instance, typebotData); - + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.typebot.id, + remoteJid: remoteJid, + }, + }); return; } @@ -752,7 +650,7 @@ export class TypebotService { if (!session) { const data = await this.createNewSession(instance, { - enabled: findTypebot.enabled, + enabled: findTypebot.typebot?.enabled, url: url, typebot: typebot, expire: expire, @@ -760,15 +658,12 @@ export class TypebotService { delayMessage: delayMessage, unknownMessage: unknownMessage, listeningFromMe: listeningFromMe, - sessions: sessions, remoteJid: remoteJid, pushName: msg.pushName, - prefilledVariables: { - messageType: messageType, - }, + typebotId: findTypebot.typebot.id, }); - await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions); + await this.sendWAMessage(instance, remoteJid, data?.messages, data?.input, data?.clientSideActions); if (data.messages.length === 0) { const content = this.getConversationMessage(msg.message); @@ -785,21 +680,12 @@ export class TypebotService { } if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { - const newSessions = await this.clearSessions(instance, remoteJid); - - const typebotData = { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - sessions: newSessions, - }; - - this.create(instance, typebotData); + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.typebot.id, + remoteJid: remoteJid, + }, + }); return; } @@ -838,26 +724,15 @@ export class TypebotService { return; } - sessions.map((session) => { - if (session.remoteJid === remoteJid) { - session.updateAt = Date.now(); - } + await this.prismaRepository.typebotSession.update({ + where: { + id: session.id, + }, + data: { + status: 'opened', + }, }); - const typebotData = { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - sessions, - }; - - this.create(instance, typebotData); - const content = this.getConversationMessage(msg.message); if (!content) { @@ -872,22 +747,12 @@ export class TypebotService { } if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { - const newSessions = await this.clearSessions(instance, remoteJid); - - const typebotData = { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - sessions: newSessions, - }; - - this.create(instance, typebotData); - + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.typebot.id, + remoteJid: remoteJid, + }, + }); return; } diff --git a/src/api/integrations/typebot/validate/typebot.schema.ts b/src/api/integrations/typebot/validate/typebot.schema.ts index 6bd26106..cc990713 100644 --- a/src/api/integrations/typebot/validate/typebot.schema.ts +++ b/src/api/integrations/typebot/validate/typebot.schema.ts @@ -24,13 +24,13 @@ export const typebotSchema: JSONSchema7 = { $id: v4(), type: 'object', properties: { - enabled: { type: 'boolean', enum: [true, false] }, + enabled: { type: 'boolean' }, url: { type: 'string' }, typebot: { type: 'string' }, expire: { type: 'integer' }, delayMessage: { type: 'integer' }, unknownMessage: { type: 'string' }, - listeningFromMe: { type: 'boolean', enum: [true, false] }, + listeningFromMe: { type: 'boolean' }, }, required: ['enabled', 'url', 'typebot', 'expire', 'delayMessage', 'unknownMessage', 'listeningFromMe'], ...isNotEmpty('enabled', 'url', 'typebot', 'expire', 'delayMessage', 'unknownMessage', 'listeningFromMe'), diff --git a/src/api/server.module.ts b/src/api/server.module.ts index 153e89d9..1c881a78 100644 --- a/src/api/server.module.ts +++ b/src/api/server.module.ts @@ -54,7 +54,7 @@ export const waMonitor = new WAMonitoringService( const authService = new AuthService(prismaRepository); -const typebotService = new TypebotService(waMonitor, configService, eventEmitter); +const typebotService = new TypebotService(waMonitor, configService, prismaRepository, eventEmitter); export const typebotController = new TypebotController(typebotService); const webhookService = new WebhookService(waMonitor); diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index b69bf01a..15524d92 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -71,7 +71,7 @@ export class ChannelStartupService { this.chatwootCache, ); - public typebotService = new TypebotService(waMonitor, this.configService, this.eventEmitter); + public typebotService = new TypebotService(waMonitor, this.configService, this.prismaRepository, this.eventEmitter); public setInstance(instance: InstanceDto) { this.instance.name = instance.instanceName; @@ -547,17 +547,7 @@ export class ChannelStartupService { throw new NotFoundException('Typebot not found'); } - return { - enabled: data.enabled, - url: data.url, - typebot: data.typebot, - expire: data.expire, - keywordFinish: data.keywordFinish, - delayMessage: data.delayMessage, - unknownMessage: data.unknownMessage, - listeningFromMe: data.listeningFromMe, - sessions: data.sessions, - }; + return data; } public async loadProxy() { diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index 531e9186..2e414b18 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -794,7 +794,7 @@ export class BaileysStartupService extends ChannelStartupService { remoteJid: chat.id, }, data: { - lastMsgTimestamp: Long.fromValue(chat.lastMessageRecvTimestamp).toString(), + remoteJid: chat.id, }, }); } @@ -936,7 +936,7 @@ export class BaileysStartupService extends ChannelStartupService { chatsRaw.push({ remoteJid: chat.id, instanceId: this.instanceId, - lastMsgTimestamp: chat.lastMessageRecvTimestamp, + lastMsgTimestamp: parseInt(chat.lastMessageRecvTimestamp?.toString()).toString(), }); } @@ -1190,7 +1190,7 @@ export class BaileysStartupService extends ChannelStartupService { if (!(this.localTypebot.listeningFromMe === false && messageRaw.key.fromMe === true)) { if (messageRaw.messageType !== 'reactionMessage') await this.typebotService.sendTypebot( - { instanceName: this.instance.name }, + { instanceName: this.instance.name, instanceId: this.instanceId }, messageRaw.key.remoteJid, messageRaw, ); @@ -1312,7 +1312,7 @@ export class BaileysStartupService extends ChannelStartupService { fromMe: key.fromMe, participant: key?.remoteJid, status: 'DELETED', - dateTime: Date.now(), + dateTime: parseInt(new Date().getTime().toString()).toString(), instanceId: this.instanceId, }; @@ -1338,7 +1338,7 @@ export class BaileysStartupService extends ChannelStartupService { fromMe: key.fromMe, participant: key?.remoteJid, status: status[update.status], - dateTime: Date.now(), + dateTime: parseInt(new Date().getTime().toString()).toString(), pollUpdates, instanceId: this.instanceId, }; @@ -1878,13 +1878,17 @@ export class BaileysStartupService extends ChannelStartupService { const contentMsg = messageSent.message[getContentType(messageSent.message)] as any; + if (Long.isLong(messageSent?.messageTimestamp)) { + messageSent.messageTimestamp = messageSent.messageTimestamp?.toNumber(); + } + const messageRaw: any = { key: messageSent.key, pushName: messageSent.pushName, message: { ...messageSent.message }, contextInfo: contentMsg?.contextInfo, messageType: getContentType(messageSent.message), - messageTimestamp: Long.fromValue(messageSent.messageTimestamp).toString(), + messageTimestamp: messageSent.messageTimestamp as number, instanceId: this.instanceId, source: getDevice(messageSent.key.id), };