From 2776f113cb140ff4e423615506d1daf35a71a3c9 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 11 Jun 2024 17:56:20 -0300 Subject: [PATCH] feat: Added debounce time for typebot messages --- .env.example | 2 +- .../migration.sql | 5 + prisma/mysql-schema.prisma | 2 + prisma/postgresql-schema.prisma | 2 + src/api/controllers/instance.controller.ts | 113 +++- src/api/controllers/webhook.controller.ts | 29 +- .../controllers/rabbitmq.controller.ts | 29 +- .../rabbitmq/routes/rabbitmq.router.ts | 1 + .../rabbitmq/validate/rabbitmq.schema.ts | 30 +- .../sqs/controllers/sqs.controller.ts | 29 +- .../integrations/sqs/validate/sqs.schema.ts | 30 +- .../integrations/typebot/dto/typebot.dto.ts | 2 + .../typebot/services/typebot.service.ts | 484 +++++++++++------- .../typebot/validate/typebot.schema.ts | 1 + .../controllers/websocket.controller.ts | 29 +- .../websocket/validate/websocket.schema.ts | 30 +- src/main.ts | 12 +- src/validate/instance.schema.ts | 85 ++- src/validate/validate.schema.ts | 28 - src/validate/webhook.schema.ts | 30 +- src/validate/websocket.schema.ts | 30 +- 21 files changed, 752 insertions(+), 251 deletions(-) create mode 100644 prisma/migrations/20240611202817_create_columns_debounce_time_typebot/migration.sql diff --git a/.env.example b/.env.example index ed9693df2..36e4b0124 100644 --- a/.env.example +++ b/.env.example @@ -123,7 +123,7 @@ CACHE_REDIS_ENABLED=true CACHE_REDIS_URI=redis://localhost:6379/6 CACHE_REDIS_PREFIX_KEY=evolution CACHE_REDIS_SAVE_INSTANCES=false -CACHE_LOCAL_ENABLED=true +CACHE_LOCAL_ENABLED=false AUTHENTICATION_API_KEY=429683C4C977415CAAFCCE10F7D57E11 AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true diff --git a/prisma/migrations/20240611202817_create_columns_debounce_time_typebot/migration.sql b/prisma/migrations/20240611202817_create_columns_debounce_time_typebot/migration.sql new file mode 100644 index 000000000..85d2e768b --- /dev/null +++ b/prisma/migrations/20240611202817_create_columns_debounce_time_typebot/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Typebot" ADD COLUMN "debounceTime" INTEGER; + +-- AlterTable +ALTER TABLE "TypebotSetting" ADD COLUMN "debounceTime" INTEGER; diff --git a/prisma/mysql-schema.prisma b/prisma/mysql-schema.prisma index 7db54d505..55fdaba31 100644 --- a/prisma/mysql-schema.prisma +++ b/prisma/mysql-schema.prisma @@ -262,6 +262,7 @@ model Typebot { listeningFromMe Boolean? @default(false) stopBotFromMe Boolean? @default(false) keepOpen Boolean? @default(false) + debounceTime Int? @db.Int createdAt DateTime? @default(now()) @db.Date updatedAt DateTime? @updatedAt @db.Date triggerType TriggerType? @@ -279,6 +280,7 @@ model TypebotSession { sessionId String @db.VarChar(100) status String @db.VarChar(100) prefilledVariables Json? @db.Json + debounceTime Int? @db.Int createdAt DateTime? @default(now()) @db.Date updatedAt DateTime @updatedAt @db.Date Typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: Cascade) diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index ab8b77d9b..6dfc6e0f0 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -261,6 +261,7 @@ model Typebot { listeningFromMe Boolean? @default(false) @db.Boolean stopBotFromMe Boolean? @default(false) @db.Boolean keepOpen Boolean? @default(false) @db.Boolean + debounceTime Int? @db.Integer createdAt DateTime? @default(now()) @db.Timestamp updatedAt DateTime? @updatedAt @db.Timestamp triggerType TriggerType? @@ -297,6 +298,7 @@ model TypebotSetting { listeningFromMe Boolean? @default(false) @db.Boolean stopBotFromMe Boolean? @default(false) @db.Boolean keepOpen Boolean? @default(false) @db.Boolean + debounceTime Int? @db.Integer createdAt DateTime? @default(now()) @db.Timestamp updatedAt DateTime @updatedAt @db.Timestamp Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index efde8afc8..a055579bf 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -7,7 +7,6 @@ import { v4 } from 'uuid'; import { Auth, Chatwoot, ConfigService, HttpServer, WaBusiness } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { BadRequestException, InternalServerErrorException, UnauthorizedException } from '../../exceptions'; -import { Events as EventsArray } from '../../validate/validate.schema'; import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service'; import { RabbitmqService } from '../integrations/rabbitmq/services/rabbitmq.service'; @@ -154,7 +153,33 @@ export class InstanceController { try { let newEvents: string[] = []; if (webhookEvents.length === 0) { - newEvents = EventsArray; + newEvents = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } else { newEvents = webhookEvents; } @@ -180,7 +205,33 @@ export class InstanceController { try { let newEvents: string[] = []; if (websocketEvents.length === 0) { - newEvents = EventsArray; + newEvents = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } else { newEvents = websocketEvents; } @@ -205,7 +256,33 @@ export class InstanceController { try { let newEvents: string[] = []; if (rabbitmqEvents.length === 0) { - newEvents = EventsArray; + newEvents = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } else { newEvents = rabbitmqEvents; } @@ -228,7 +305,33 @@ export class InstanceController { try { let newEvents: string[] = []; if (sqsEvents.length === 0) { - newEvents = EventsArray; + newEvents = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } else { newEvents = sqsEvents; } diff --git a/src/api/controllers/webhook.controller.ts b/src/api/controllers/webhook.controller.ts index 889b216fa..8eb0174e7 100644 --- a/src/api/controllers/webhook.controller.ts +++ b/src/api/controllers/webhook.controller.ts @@ -1,7 +1,6 @@ import { isURL } from 'class-validator'; import { BadRequestException } from '../../exceptions'; -import { Events } from '../../validate/validate.schema'; import { InstanceDto } from '../dto/instance.dto'; import { WebhookDto } from '../dto/webhook.dto'; import { WAMonitoringService } from '../services/monitor.service'; @@ -21,7 +20,33 @@ export class WebhookController { data.url = ''; data.events = []; } else if (data.events.length === 0) { - data.events = Events; + data.events = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } return this.webhookService.create(instance, data); diff --git a/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts b/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts index d5a76c3a4..773b02cbb 100644 --- a/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts +++ b/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts @@ -1,6 +1,5 @@ import { configService, Rabbitmq } from '../../../../config/env.config'; import { BadRequestException } from '../../../../exceptions'; -import { Events } from '../../../../validate/validate.schema'; import { InstanceDto } from '../../../dto/instance.dto'; import { RabbitmqDto } from '../dto/rabbitmq.dto'; import { RabbitmqService } from '../services/rabbitmq.service'; @@ -16,7 +15,33 @@ export class RabbitmqController { } if (data.events.length === 0) { - data.events = Events; + data.events = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } return this.rabbitmqService.create(instance, data); diff --git a/src/api/integrations/rabbitmq/routes/rabbitmq.router.ts b/src/api/integrations/rabbitmq/routes/rabbitmq.router.ts index d1a0a891c..52e5f7bae 100644 --- a/src/api/integrations/rabbitmq/routes/rabbitmq.router.ts +++ b/src/api/integrations/rabbitmq/routes/rabbitmq.router.ts @@ -12,6 +12,7 @@ export class RabbitmqRouter extends RouterBroker { super(); this.router .post(this.routerPath('set'), ...guards, async (req, res) => { + console.log('RabbitmqRouter -> constructor -> req', req.body); const response = await this.dataValidate({ request: req, schema: rabbitmqSchema, diff --git a/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts b/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts index c3bed060c..1fedf607f 100644 --- a/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts +++ b/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts @@ -1,8 +1,6 @@ import { JSONSchema7 } from 'json-schema'; import { v4 } from 'uuid'; -import { Events } from '../../../../validate/validate.schema'; - const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; propertyNames.forEach( @@ -32,7 +30,33 @@ export const rabbitmqSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, }, diff --git a/src/api/integrations/sqs/controllers/sqs.controller.ts b/src/api/integrations/sqs/controllers/sqs.controller.ts index 8bce4e6b8..7990a9bcf 100644 --- a/src/api/integrations/sqs/controllers/sqs.controller.ts +++ b/src/api/integrations/sqs/controllers/sqs.controller.ts @@ -1,6 +1,5 @@ import { configService, Sqs } from '../../../../config/env.config'; import { BadRequestException } from '../../../../exceptions'; -import { Events } from '../../../../validate/validate.schema'; import { InstanceDto } from '../../../dto/instance.dto'; import { SqsDto } from '../dto/sqs.dto'; import { SqsService } from '../services/sqs.service'; @@ -16,7 +15,33 @@ export class SqsController { } if (data.events.length === 0) { - data.events = Events; + data.events = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } return this.sqsService.create(instance, data); diff --git a/src/api/integrations/sqs/validate/sqs.schema.ts b/src/api/integrations/sqs/validate/sqs.schema.ts index ccd90c6b8..4bea583c5 100644 --- a/src/api/integrations/sqs/validate/sqs.schema.ts +++ b/src/api/integrations/sqs/validate/sqs.schema.ts @@ -1,8 +1,6 @@ import { JSONSchema7 } from 'json-schema'; import { v4 } from 'uuid'; -import { Events } from '../../../../validate/validate.schema'; - const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; propertyNames.forEach( @@ -32,7 +30,33 @@ export const sqsSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, }, diff --git a/src/api/integrations/typebot/dto/typebot.dto.ts b/src/api/integrations/typebot/dto/typebot.dto.ts index 6957a00cb..bc968df3a 100644 --- a/src/api/integrations/typebot/dto/typebot.dto.ts +++ b/src/api/integrations/typebot/dto/typebot.dto.ts @@ -27,6 +27,7 @@ export class TypebotDto { listeningFromMe?: boolean; stopBotFromMe?: boolean; keepOpen?: boolean; + debounceTime?: number; triggerType?: TriggerType; triggerOperator?: TriggerOperator; triggerValue?: string; @@ -40,4 +41,5 @@ export class TypebotSettingDto { listeningFromMe?: boolean; stopBotFromMe?: boolean; keepOpen?: boolean; + debounceTime?: number; } diff --git a/src/api/integrations/typebot/services/typebot.service.ts b/src/api/integrations/typebot/services/typebot.service.ts index de047955b..3cb23c0f0 100644 --- a/src/api/integrations/typebot/services/typebot.service.ts +++ b/src/api/integrations/typebot/services/typebot.service.ts @@ -1,4 +1,4 @@ -import { Message, TypebotSession } from '@prisma/client'; +import { Message, Typebot as TypebotModel, TypebotSession } from '@prisma/client'; import axios from 'axios'; import { ConfigService, Typebot } from '../../../../config/env.config'; @@ -16,6 +16,8 @@ export class TypebotService { private readonly prismaRepository: PrismaRepository, ) {} + private userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {}; + private readonly logger = new Logger(TypebotService.name); public async create(instance: InstanceDto, data: TypebotDto) { @@ -34,7 +36,8 @@ export class TypebotService { !data.unknownMessage || !data.listeningFromMe || !data.stopBotFromMe || - !data.keepOpen + !data.keepOpen || + !data.debounceTime ) { const defaultSettingCheck = await this.prismaRepository.typebotSetting.findFirst({ where: { @@ -53,6 +56,7 @@ export class TypebotService { if (!data.listeningFromMe) data.listeningFromMe = defaultSettingCheck.listeningFromMe; if (!data.stopBotFromMe) data.stopBotFromMe = defaultSettingCheck.stopBotFromMe; if (!data.keepOpen) data.keepOpen = defaultSettingCheck.keepOpen; + if (!data.debounceTime) data.debounceTime = defaultSettingCheck.debounceTime; } const checkTriggerAll = await this.prismaRepository.typebot.findFirst({ @@ -106,6 +110,8 @@ export class TypebotService { unknownMessage: data.unknownMessage, listeningFromMe: data.listeningFromMe, stopBotFromMe: data.stopBotFromMe, + keepOpen: data.keepOpen, + debounceTime: data.debounceTime, instanceId: instanceId, triggerType: data.triggerType, triggerOperator: data.triggerOperator, @@ -238,6 +244,8 @@ export class TypebotService { unknownMessage: data.unknownMessage, listeningFromMe: data.listeningFromMe, stopBotFromMe: data.stopBotFromMe, + keepOpen: data.keepOpen, + debounceTime: data.debounceTime, triggerType: data.triggerType, triggerOperator: data.triggerOperator, triggerValue: data.triggerValue, @@ -349,6 +357,7 @@ export class TypebotService { listeningFromMe: data.listeningFromMe, stopBotFromMe: data.stopBotFromMe, keepOpen: data.keepOpen, + debounceTime: data.debounceTime, }, }); @@ -360,6 +369,7 @@ export class TypebotService { listeningFromMe: updateSettings.listeningFromMe, stopBotFromMe: updateSettings.stopBotFromMe, keepOpen: updateSettings.keepOpen, + debounceTime: updateSettings.debounceTime, }; } @@ -372,6 +382,7 @@ export class TypebotService { listeningFromMe: data.listeningFromMe, stopBotFromMe: data.stopBotFromMe, keepOpen: data.keepOpen, + debounceTime: data.debounceTime, instanceId: instanceId, }, }); @@ -384,6 +395,7 @@ export class TypebotService { listeningFromMe: newSetttings.listeningFromMe, stopBotFromMe: newSetttings.stopBotFromMe, keepOpen: newSetttings.keepOpen, + debounceTime: newSetttings.debounceTime, }; } catch (error) { this.logger.error(error); @@ -1119,9 +1131,30 @@ export class TypebotService { return typebot; } + private processDebounce(content: string, remoteJid: string, debounceTime: number, callback: any) { + if (this.userMessageDebounce[remoteJid]) { + this.userMessageDebounce[remoteJid].message += ` ${content}`; + this.logger.log('message debounced: ' + this.userMessageDebounce[remoteJid].message); + clearTimeout(this.userMessageDebounce[remoteJid].timeoutId); + } else { + this.userMessageDebounce[remoteJid] = { + message: content, + timeoutId: null, + }; + } + + this.userMessageDebounce[remoteJid].timeoutId = setTimeout(() => { + const myQuestion = this.userMessageDebounce[remoteJid].message; + this.logger.log('Debounce complete. Processing message: ' + myQuestion); + + delete this.userMessageDebounce[remoteJid]; + callback(myQuestion); + }, debounceTime * 1000); + } + public async sendTypebot(instance: InstanceDto, remoteJid: string, msg: Message) { try { - let session = await this.prismaRepository.typebotSession.findFirst({ + const session = await this.prismaRepository.typebotSession.findFirst({ where: { remoteJid: remoteJid, }, @@ -1154,6 +1187,7 @@ export class TypebotService { let listeningFromMe = findTypebot?.listeningFromMe; let stopBotFromMe = findTypebot?.stopBotFromMe; let keepOpen = findTypebot?.keepOpen; + let debounceTime = findTypebot?.debounceTime; if ( !expire || @@ -1183,6 +1217,8 @@ export class TypebotService { if (!stopBotFromMe) stopBotFromMe = settings.stopBotFromMe; if (!keepOpen) keepOpen = settings.keepOpen; + + if (!debounceTime) debounceTime = settings.debounceTime; } const key = msg.key as { @@ -1206,141 +1242,107 @@ export class TypebotService { return; } - if (session && !session.awaitUser) return; - - if (session && expire && expire > 0) { - const now = Date.now(); - - const sessionUpdatedAt = new Date(session.updatedAt).getTime(); - - const diff = now - sessionUpdatedAt; - - const diffInMinutes = Math.floor(diff / 1000 / 60); - - if (diffInMinutes > expire) { - await this.prismaRepository.typebotSession.deleteMany({ - where: { - typebotId: findTypebot.id, - remoteJid: remoteJid, - }, - }); - - const data = await this.createNewSession(instance, { - enabled: findTypebot.enabled, - url: url, - typebot: typebot, - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - remoteJid: remoteJid, - pushName: msg.pushName, - typebotId: findTypebot.id, - }); - - if (data.session) { - session = data.session; - } - - await this.sendWAMessage( + if (debounceTime && debounceTime > 0) { + this.processDebounce(content, remoteJid, debounceTime, async (debouncedContent) => { + await this.processTypebot( instance, - session, - { - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - stopBotFromMe: stopBotFromMe, - keepOpen: keepOpen, - }, remoteJid, - data.messages, - data.input, - data.clientSideActions, + msg, + session, + findTypebot, + url, + expire, + typebot, + keywordFinish, + delayMessage, + unknownMessage, + listeningFromMe, + stopBotFromMe, + keepOpen, + debouncedContent, ); + }); + } else { + await this.processTypebot( + instance, + remoteJid, + msg, + session, + findTypebot, + url, + expire, + typebot, + keywordFinish, + delayMessage, + unknownMessage, + listeningFromMe, + stopBotFromMe, + keepOpen, + content, + ); + } - if (data.messages.length === 0) { - const content = this.getConversationMessage(msg.message); - - if (!content) { - if (unknownMessage) { - this.waMonitor.waInstances[instance.instanceName].textMessage( - { - number: remoteJid.split('@')[0], - delay: delayMessage || 1000, - text: unknownMessage, - }, - true, - ); - } - return; - } + // await this.processTypebot( + // instance, + // remoteJid, + // msg, + // session, + // findTypebot, + // url, + // expire, + // typebot, + // keywordFinish, + // delayMessage, + // unknownMessage, + // listeningFromMe, + // stopBotFromMe, + // keepOpen, + // content, + // ); - if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { - await this.prismaRepository.typebotSession.deleteMany({ - where: { - typebotId: findTypebot.id, - remoteJid: remoteJid, - }, - }); - return; - } + if (session && !session.awaitUser) return; + } catch (error) { + this.logger.error(error); + return; + } + } - try { - const version = this.configService.get('TYPEBOT').API_VERSION; - let urlTypebot: string; - let reqData: {}; - if (version === 'latest') { - urlTypebot = `${url}/api/v1/sessions/${data.sessionId}/continueChat`; - reqData = { - message: content, - }; - } else { - urlTypebot = `${url}/api/v1/sendMessage`; - reqData = { - message: content, - sessionId: data.sessionId, - }; - } - - const request = await axios.post(urlTypebot, reqData); - - await this.sendWAMessage( - instance, - session, - { - expire: expire, - keywordFinish: keywordFinish, - delayMessage: delayMessage, - unknownMessage: unknownMessage, - listeningFromMe: listeningFromMe, - stopBotFromMe: stopBotFromMe, - keepOpen: keepOpen, - }, - remoteJid, - request.data.messages, - request.data.input, - request.data.clientSideActions, - ); - } catch (error) { - this.logger.error(error); - return; - } - } + private async processTypebot( + instance: InstanceDto, + remoteJid: string, + msg: Message, + session: TypebotSession, + findTypebot: TypebotModel, + url: string, + expire: number, + typebot: string, + keywordFinish: string, + delayMessage: number, + unknownMessage: string, + listeningFromMe: boolean, + stopBotFromMe: boolean, + keepOpen: boolean, + content: string, + ) { + if (session && expire && expire > 0) { + const now = Date.now(); - return; - } - } + const sessionUpdatedAt = new Date(session.updatedAt).getTime(); - if (session && session.status !== 'opened') { - return; - } + const diff = now - sessionUpdatedAt; + + const diffInMinutes = Math.floor(diff / 1000 / 60); + + if (diffInMinutes > expire) { + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.id, + remoteJid: remoteJid, + }, + }); - if (!session) { const data = await this.createNewSession(instance, { - enabled: findTypebot?.enabled, + enabled: findTypebot.enabled, url: url, typebot: typebot, expire: expire, @@ -1370,12 +1372,14 @@ export class TypebotService { keepOpen: keepOpen, }, remoteJid, - data?.messages, - data?.input, - data?.clientSideActions, + data.messages, + data.input, + data.clientSideActions, ); if (data.messages.length === 0) { + const content = this.getConversationMessage(msg.message); + if (!content) { if (unknownMessage) { this.waMonitor.waInstances[instance.instanceName].textMessage( @@ -1397,11 +1401,9 @@ export class TypebotService { remoteJid: remoteJid, }, }); - return; } - let request: any; try { const version = this.configService.get('TYPEBOT').API_VERSION; let urlTypebot: string; @@ -1418,7 +1420,8 @@ export class TypebotService { sessionId: data.sessionId, }; } - request = await axios.post(urlTypebot, reqData); + + const request = await axios.post(urlTypebot, reqData); await this.sendWAMessage( instance, @@ -1442,59 +1445,33 @@ export class TypebotService { return; } } - return; - } - await this.prismaRepository.typebotSession.update({ - where: { - id: session.id, - }, - data: { - status: 'opened', - awaitUser: false, - }, - }); - - if (!content) { - if (unknownMessage) { - this.waMonitor.waInstances[instance.instanceName].textMessage( - { - number: remoteJid.split('@')[0], - delay: delayMessage || 1000, - text: unknownMessage, - }, - true, - ); - } return; } + } - if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { - await this.prismaRepository.typebotSession.deleteMany({ - where: { - typebotId: findTypebot.id, - remoteJid: remoteJid, - }, - }); - return; - } + if (session && session.status !== 'opened') { + return; + } - const version = this.configService.get('TYPEBOT').API_VERSION; - let urlTypebot: string; - let reqData: {}; - if (version === 'latest') { - urlTypebot = `${url}/api/v1/sessions/${session.sessionId.split('-')[1]}/continueChat`; - reqData = { - message: content, - }; - } else { - urlTypebot = `${url}/api/v1/sendMessage`; - reqData = { - message: content, - sessionId: session.sessionId.split('-')[1], - }; + if (!session) { + const data = await this.createNewSession(instance, { + enabled: findTypebot?.enabled, + url: url, + typebot: typebot, + expire: expire, + keywordFinish: keywordFinish, + delayMessage: delayMessage, + unknownMessage: unknownMessage, + listeningFromMe: listeningFromMe, + remoteJid: remoteJid, + pushName: msg.pushName, + typebotId: findTypebot.id, + }); + + if (data.session) { + session = data.session; } - const request = await axios.post(urlTypebot, reqData); await this.sendWAMessage( instance, @@ -1509,15 +1486,150 @@ export class TypebotService { keepOpen: keepOpen, }, remoteJid, - request?.data?.messages, - request?.data?.input, - request?.data?.clientSideActions, + data?.messages, + data?.input, + data?.clientSideActions, ); + if (data.messages.length === 0) { + if (!content) { + if (unknownMessage) { + this.waMonitor.waInstances[instance.instanceName].textMessage( + { + number: remoteJid.split('@')[0], + delay: delayMessage || 1000, + text: unknownMessage, + }, + true, + ); + } + return; + } + + if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.id, + remoteJid: remoteJid, + }, + }); + + return; + } + + let request: any; + try { + const version = this.configService.get('TYPEBOT').API_VERSION; + let urlTypebot: string; + let reqData: {}; + if (version === 'latest') { + urlTypebot = `${url}/api/v1/sessions/${data.sessionId}/continueChat`; + reqData = { + message: content, + }; + } else { + urlTypebot = `${url}/api/v1/sendMessage`; + reqData = { + message: content, + sessionId: data.sessionId, + }; + } + request = await axios.post(urlTypebot, reqData); + + await this.sendWAMessage( + instance, + session, + { + expire: expire, + keywordFinish: keywordFinish, + delayMessage: delayMessage, + unknownMessage: unknownMessage, + listeningFromMe: listeningFromMe, + stopBotFromMe: stopBotFromMe, + keepOpen: keepOpen, + }, + remoteJid, + request.data.messages, + request.data.input, + request.data.clientSideActions, + ); + } catch (error) { + this.logger.error(error); + return; + } + } + return; + } + + await this.prismaRepository.typebotSession.update({ + where: { + id: session.id, + }, + data: { + status: 'opened', + awaitUser: false, + }, + }); + + if (!content) { + if (unknownMessage) { + this.waMonitor.waInstances[instance.instanceName].textMessage( + { + number: remoteJid.split('@')[0], + delay: delayMessage || 1000, + text: unknownMessage, + }, + true, + ); + } return; - } catch (error) { - this.logger.error(error); + } + + if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) { + await this.prismaRepository.typebotSession.deleteMany({ + where: { + typebotId: findTypebot.id, + remoteJid: remoteJid, + }, + }); return; } + + const version = this.configService.get('TYPEBOT').API_VERSION; + let urlTypebot: string; + let reqData: {}; + if (version === 'latest') { + urlTypebot = `${url}/api/v1/sessions/${session.sessionId.split('-')[1]}/continueChat`; + reqData = { + message: content, + }; + } else { + urlTypebot = `${url}/api/v1/sendMessage`; + reqData = { + message: content, + sessionId: session.sessionId.split('-')[1], + }; + } + const request = await axios.post(urlTypebot, reqData); + + await this.sendWAMessage( + instance, + session, + { + expire: expire, + keywordFinish: keywordFinish, + delayMessage: delayMessage, + unknownMessage: unknownMessage, + listeningFromMe: listeningFromMe, + stopBotFromMe: stopBotFromMe, + keepOpen: keepOpen, + }, + remoteJid, + request?.data?.messages, + request?.data?.input, + request?.data?.clientSideActions, + ); + + return; } } diff --git a/src/api/integrations/typebot/validate/typebot.schema.ts b/src/api/integrations/typebot/validate/typebot.schema.ts index b0f94f8d4..e17eab8b3 100644 --- a/src/api/integrations/typebot/validate/typebot.schema.ts +++ b/src/api/integrations/typebot/validate/typebot.schema.ts @@ -75,6 +75,7 @@ export const typebotSettingSchema: JSONSchema7 = { listeningFromMe: { type: 'boolean' }, stopBotFromMe: { type: 'boolean' }, keepOpen: { type: 'boolean' }, + debounceTime: { type: 'integer' }, }, required: ['expire', 'keywordFinish', 'delayMessage', 'unknownMessage', 'listeningFromMe', 'stopBotFromMe'], ...isNotEmpty('expire', 'keywordFinish', 'delayMessage', 'unknownMessage', 'listeningFromMe', 'stopBotFromMe'), diff --git a/src/api/integrations/websocket/controllers/websocket.controller.ts b/src/api/integrations/websocket/controllers/websocket.controller.ts index 9dd144526..a487f48ab 100644 --- a/src/api/integrations/websocket/controllers/websocket.controller.ts +++ b/src/api/integrations/websocket/controllers/websocket.controller.ts @@ -1,4 +1,3 @@ -import { Events } from '../../../../validate/validate.schema'; import { InstanceDto } from '../../../dto/instance.dto'; import { WebsocketDto } from '../dto/websocket.dto'; import { WebsocketService } from '../services/websocket.service'; @@ -12,7 +11,33 @@ export class WebsocketController { } if (data.events.length === 0) { - data.events = Events; + data.events = [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ]; } return this.websocketService.create(instance, data); diff --git a/src/api/integrations/websocket/validate/websocket.schema.ts b/src/api/integrations/websocket/validate/websocket.schema.ts index dbf51a4c9..8a7678c1f 100644 --- a/src/api/integrations/websocket/validate/websocket.schema.ts +++ b/src/api/integrations/websocket/validate/websocket.schema.ts @@ -1,8 +1,6 @@ import { JSONSchema7 } from 'json-schema'; import { v4 } from 'uuid'; -import { Events } from '../../../../validate/validate.schema'; - const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; propertyNames.forEach( @@ -32,7 +30,33 @@ export const websocketSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, }, diff --git a/src/main.ts b/src/main.ts index 4bd259bab..156762499 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,7 +13,7 @@ import { ProviderFiles } from './api/provider/sessions'; import { PrismaRepository } from './api/repository/repository.service'; import { HttpStatus, router } from './api/routes/index.router'; import { waMonitor } from './api/server.module'; -import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config'; +import { Auth, configService, Cors, HttpServer, ProviderSession, Rabbitmq, Sqs, Webhook } from './config/env.config'; import { onUnexpectedError } from './config/error.config'; import { Logger } from './config/logger.config'; import { ROOT_DIR } from './config/path.config'; @@ -27,9 +27,13 @@ async function bootstrap() { const logger = new Logger('SERVER'); const app = express(); - const providerFiles = new ProviderFiles(configService); - await providerFiles.onModuleInit(); - logger.info('Provider:Files - ON'); + let providerFiles: ProviderFiles = null; + if (configService.get('PROVIDER').ENABLED) { + providerFiles = new ProviderFiles(configService); + await providerFiles.onModuleInit(); + logger.info('Provider:Files - ON'); + } + const prismaRepository = new PrismaRepository(configService); await prismaRepository.onModuleInit(); diff --git a/src/validate/instance.schema.ts b/src/validate/instance.schema.ts index 41b135708..2fdcc4c4f 100644 --- a/src/validate/instance.schema.ts +++ b/src/validate/instance.schema.ts @@ -2,7 +2,6 @@ import { JSONSchema7 } from 'json-schema'; import { v4 } from 'uuid'; import { Integration } from '../api/types/wa.types'; -import { Events } from './validate.schema'; const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; @@ -59,7 +58,33 @@ export const instanceSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, // RabbitMQ @@ -69,7 +94,33 @@ export const instanceSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, // SQS @@ -79,7 +130,33 @@ export const instanceSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, // Chatwoot diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 8b35656aa..e6ea15778 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -15,31 +15,3 @@ export * from './proxy.schema'; export * from './settings.schema'; export * from './webhook.schema'; export * from './websocket.schema'; - -export const Events = [ - 'APPLICATION_STARTUP', - 'QRCODE_UPDATED', - 'MESSAGES_SET', - 'MESSAGES_UPSERT', - 'MESSAGES_EDITED', - 'MESSAGES_UPDATE', - 'MESSAGES_DELETE', - 'SEND_MESSAGE', - 'CONTACTS_SET', - 'CONTACTS_UPSERT', - 'CONTACTS_UPDATE', - 'PRESENCE_UPDATE', - 'CHATS_SET', - 'CHATS_UPSERT', - 'CHATS_UPDATE', - 'CHATS_DELETE', - 'GROUPS_UPSERT', - 'GROUP_UPDATE', - 'GROUP_PARTICIPANTS_UPDATE', - 'CONNECTION_UPDATE', - 'LABELS_EDIT', - 'LABELS_ASSOCIATION', - 'CALL', - 'TYPEBOT_START', - 'TYPEBOT_CHANGE_STATUS', -]; diff --git a/src/validate/webhook.schema.ts b/src/validate/webhook.schema.ts index 05e5d7421..82a3a5c2a 100644 --- a/src/validate/webhook.schema.ts +++ b/src/validate/webhook.schema.ts @@ -1,8 +1,6 @@ import { JSONSchema7 } from 'json-schema'; import { v4 } from 'uuid'; -import { Events } from './validate.schema'; - const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; propertyNames.forEach( @@ -35,7 +33,33 @@ export const webhookSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, }, diff --git a/src/validate/websocket.schema.ts b/src/validate/websocket.schema.ts index 871d18abd..8a7678c1f 100644 --- a/src/validate/websocket.schema.ts +++ b/src/validate/websocket.schema.ts @@ -1,8 +1,6 @@ import { JSONSchema7 } from 'json-schema'; import { v4 } from 'uuid'; -import { Events } from './validate.schema'; - const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; propertyNames.forEach( @@ -32,7 +30,33 @@ export const websocketSchema: JSONSchema7 = { minItems: 0, items: { type: 'string', - enum: Events, + enum: [ + 'APPLICATION_STARTUP', + 'QRCODE_UPDATED', + 'MESSAGES_SET', + 'MESSAGES_UPSERT', + 'MESSAGES_EDITED', + 'MESSAGES_UPDATE', + 'MESSAGES_DELETE', + 'SEND_MESSAGE', + 'CONTACTS_SET', + 'CONTACTS_UPSERT', + 'CONTACTS_UPDATE', + 'PRESENCE_UPDATE', + 'CHATS_SET', + 'CHATS_UPSERT', + 'CHATS_UPDATE', + 'CHATS_DELETE', + 'GROUPS_UPSERT', + 'GROUP_UPDATE', + 'GROUP_PARTICIPANTS_UPDATE', + 'CONNECTION_UPDATE', + 'LABELS_EDIT', + 'LABELS_ASSOCIATION', + 'CALL', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + ], }, }, },