feat: Added debounce time for typebot messages

This commit is contained in:
Davidson Gomes 2024-06-11 17:56:20 -03:00
parent 0bf8e55144
commit 2776f113cb
21 changed files with 756 additions and 255 deletions

View File

@ -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

View File

@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Typebot" ADD COLUMN "debounceTime" INTEGER;
-- AlterTable
ALTER TABLE "TypebotSetting" ADD COLUMN "debounceTime" INTEGER;

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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<RabbitmqDto>({
request: req,
schema: rabbitmqSchema,

View File

@ -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',
],
},
},
},

View File

@ -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);

View File

@ -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',
],
},
},
},

View File

@ -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;
}

View File

@ -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,
);
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;
}
if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) {
await this.prismaRepository.typebotSession.deleteMany({
where: {
typebotId: findTypebot.id,
remoteJid: remoteJid,
},
});
return;
}
try {
const version = this.configService.get<Typebot>('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;
}
}
return;
}
});
} else {
await this.processTypebot(
instance,
remoteJid,
msg,
session,
findTypebot,
url,
expire,
typebot,
keywordFinish,
delayMessage,
unknownMessage,
listeningFromMe,
stopBotFromMe,
keepOpen,
content,
);
}
if (session && session.status !== 'opened') {
return;
}
// await this.processTypebot(
// instance,
// remoteJid,
// msg,
// session,
// findTypebot,
// url,
// expire,
// typebot,
// keywordFinish,
// delayMessage,
// unknownMessage,
// listeningFromMe,
// stopBotFromMe,
// keepOpen,
// content,
// );
if (session && !session.awaitUser) return;
} 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();
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,
},
});
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>('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,60 +1445,34 @@ export class TypebotService {
return;
}
}
return;
}
}
await this.prismaRepository.typebotSession.update({
where: {
id: session.id,
},
data: {
status: 'opened',
awaitUser: false,
},
if (session && session.status !== 'opened') {
return;
}
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 (!content) {
if (unknownMessage) {
this.waMonitor.waInstances[instance.instanceName].textMessage(
{
number: remoteJid.split('@')[0],
delay: delayMessage || 1000,
text: unknownMessage,
},
true,
);
}
return;
if (data.session) {
session = data.session;
}
if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) {
await this.prismaRepository.typebotSession.deleteMany({
where: {
typebotId: findTypebot.id,
remoteJid: remoteJid,
},
});
return;
}
const version = this.configService.get<Typebot>('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,
@ -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,
);
return;
} catch (error) {
this.logger.error(error);
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>('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;
}
if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) {
await this.prismaRepository.typebotSession.deleteMany({
where: {
typebotId: findTypebot.id,
remoteJid: remoteJid,
},
});
return;
}
const version = this.configService.get<Typebot>('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;
}
}

View File

@ -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'),

View File

@ -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);

View File

@ -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',
],
},
},
},

View File

@ -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<ProviderSession>('PROVIDER').ENABLED) {
providerFiles = new ProviderFiles(configService);
await providerFiles.onModuleInit();
logger.info('Provider:Files - ON');
}
const prismaRepository = new PrismaRepository(configService);
await prismaRepository.onModuleInit();

View File

@ -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

View File

@ -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',
];

View File

@ -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',
],
},
},
},

View File

@ -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',
],
},
},
},