mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-14 01:41:24 -06:00
feat: generic chatbot
This commit is contained in:
parent
9a09a6662a
commit
35bde8498b
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `difyId` on the `IntegrationSession` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `openaiBotId` on the `IntegrationSession` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `typebotId` on the `IntegrationSession` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "IntegrationSession" DROP CONSTRAINT "IntegrationSession_difyId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "IntegrationSession" DROP CONSTRAINT "IntegrationSession_openaiBotId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "IntegrationSession" DROP CONSTRAINT "IntegrationSession_typebotId_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "IntegrationSession" DROP COLUMN "difyId",
|
||||||
|
DROP COLUMN "openaiBotId",
|
||||||
|
DROP COLUMN "typebotId",
|
||||||
|
ADD COLUMN "botId" TEXT;
|
@ -0,0 +1,57 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "GenericBot" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"description" VARCHAR(255),
|
||||||
|
"apiUrl" VARCHAR(255),
|
||||||
|
"apiKey" VARCHAR(255),
|
||||||
|
"expire" INTEGER DEFAULT 0,
|
||||||
|
"keywordFinish" VARCHAR(100),
|
||||||
|
"delayMessage" INTEGER,
|
||||||
|
"unknownMessage" VARCHAR(100),
|
||||||
|
"listeningFromMe" BOOLEAN DEFAULT false,
|
||||||
|
"stopBotFromMe" BOOLEAN DEFAULT false,
|
||||||
|
"keepOpen" BOOLEAN DEFAULT false,
|
||||||
|
"debounceTime" INTEGER,
|
||||||
|
"ignoreJids" JSONB,
|
||||||
|
"triggerType" "TriggerType",
|
||||||
|
"triggerOperator" "TriggerOperator",
|
||||||
|
"triggerValue" TEXT,
|
||||||
|
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP NOT NULL,
|
||||||
|
"instanceId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "GenericBot_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "GenericSetting" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"expire" INTEGER DEFAULT 0,
|
||||||
|
"keywordFinish" VARCHAR(100),
|
||||||
|
"delayMessage" INTEGER,
|
||||||
|
"unknownMessage" VARCHAR(100),
|
||||||
|
"listeningFromMe" BOOLEAN DEFAULT false,
|
||||||
|
"stopBotFromMe" BOOLEAN DEFAULT false,
|
||||||
|
"keepOpen" BOOLEAN DEFAULT false,
|
||||||
|
"debounceTime" INTEGER,
|
||||||
|
"ignoreJids" JSONB,
|
||||||
|
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP NOT NULL,
|
||||||
|
"botIdFallback" VARCHAR(100),
|
||||||
|
"instanceId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "GenericSetting_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "GenericSetting_instanceId_key" ON "GenericSetting"("instanceId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "GenericBot" ADD CONSTRAINT "GenericBot_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "Instance"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "GenericSetting" ADD CONSTRAINT "GenericSetting_botIdFallback_fkey" FOREIGN KEY ("botIdFallback") REFERENCES "GenericBot"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "GenericSetting" ADD CONSTRAINT "GenericSetting_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "Instance"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -100,6 +100,8 @@ model Instance {
|
|||||||
Dify Dify[]
|
Dify Dify[]
|
||||||
DifySetting DifySetting?
|
DifySetting DifySetting?
|
||||||
integrationSessions IntegrationSession[]
|
integrationSessions IntegrationSession[]
|
||||||
|
GenericBot GenericBot[]
|
||||||
|
GenericSetting GenericSetting?
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
@ -481,3 +483,47 @@ model DifySetting {
|
|||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model GenericBot {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
enabled Boolean @default(true) @db.Boolean
|
||||||
|
description String? @db.VarChar(255)
|
||||||
|
apiUrl String? @db.VarChar(255)
|
||||||
|
apiKey String? @db.VarChar(255)
|
||||||
|
expire Int? @default(0) @db.Integer
|
||||||
|
keywordFinish String? @db.VarChar(100)
|
||||||
|
delayMessage Int? @db.Integer
|
||||||
|
unknownMessage String? @db.VarChar(100)
|
||||||
|
listeningFromMe Boolean? @default(false) @db.Boolean
|
||||||
|
stopBotFromMe Boolean? @default(false) @db.Boolean
|
||||||
|
keepOpen Boolean? @default(false) @db.Boolean
|
||||||
|
debounceTime Int? @db.Integer
|
||||||
|
ignoreJids Json?
|
||||||
|
triggerType TriggerType?
|
||||||
|
triggerOperator TriggerOperator?
|
||||||
|
triggerValue String?
|
||||||
|
createdAt DateTime? @default(now()) @db.Timestamp
|
||||||
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
|
instanceId String
|
||||||
|
GenericSetting GenericSetting[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model GenericSetting {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
expire Int? @default(0) @db.Integer
|
||||||
|
keywordFinish String? @db.VarChar(100)
|
||||||
|
delayMessage Int? @db.Integer
|
||||||
|
unknownMessage String? @db.VarChar(100)
|
||||||
|
listeningFromMe Boolean? @default(false) @db.Boolean
|
||||||
|
stopBotFromMe Boolean? @default(false) @db.Boolean
|
||||||
|
keepOpen Boolean? @default(false) @db.Boolean
|
||||||
|
debounceTime Int? @db.Integer
|
||||||
|
ignoreJids Json?
|
||||||
|
createdAt DateTime? @default(now()) @db.Timestamp
|
||||||
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
|
Fallback GenericBot? @relation(fields: [botIdFallback], references: [id])
|
||||||
|
botIdFallback String? @db.VarChar(100)
|
||||||
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
|
instanceId String @unique
|
||||||
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { InstanceDto } from '@api/dto/instance.dto';
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
import { PrismaRepository } from '@api/repository/repository.service';
|
import { PrismaRepository } from '@api/repository/repository.service';
|
||||||
import { difyController, openaiController, typebotController, websocketController } from '@api/server.module';
|
import {
|
||||||
|
difyController,
|
||||||
|
genericController,
|
||||||
|
openaiController,
|
||||||
|
typebotController,
|
||||||
|
websocketController,
|
||||||
|
} from '@api/server.module';
|
||||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
import { IntegrationSession } from '@prisma/client';
|
import { IntegrationSession } from '@prisma/client';
|
||||||
@ -91,6 +97,9 @@ export class ChatbotController {
|
|||||||
|
|
||||||
// dify
|
// dify
|
||||||
await difyController.emit(emitData);
|
await difyController.emit(emitData);
|
||||||
|
|
||||||
|
// generic
|
||||||
|
await genericController.emit(emitData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setInstance(instanceName: string, data: any): Promise<any> {
|
public async setInstance(instanceName: string, data: any): Promise<any> {
|
||||||
|
@ -4,6 +4,8 @@ import { OpenaiRouter } from '@api/integrations/chatbot/openai/routes/openai.rou
|
|||||||
import { TypebotRouter } from '@api/integrations/chatbot/typebot/routes/typebot.router';
|
import { TypebotRouter } from '@api/integrations/chatbot/typebot/routes/typebot.router';
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
|
|
||||||
|
import { GenericRouter } from './generic/routes/generic.router';
|
||||||
|
|
||||||
export class ChatbotRouter {
|
export class ChatbotRouter {
|
||||||
public readonly router: Router;
|
public readonly router: Router;
|
||||||
|
|
||||||
@ -14,5 +16,6 @@ export class ChatbotRouter {
|
|||||||
this.router.use('/typebot', new TypebotRouter(...guards).router);
|
this.router.use('/typebot', new TypebotRouter(...guards).router);
|
||||||
this.router.use('/openai', new OpenaiRouter(...guards).router);
|
this.router.use('/openai', new OpenaiRouter(...guards).router);
|
||||||
this.router.use('/dify', new DifyRouter(...guards).router);
|
this.router.use('/dify', new DifyRouter(...guards).router);
|
||||||
|
this.router.use('/generic', new GenericRouter(...guards).router);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export * from '@api/integrations/chatbot/chatwoot/validate/chatwoot.schema';
|
export * from '@api/integrations/chatbot/chatwoot/validate/chatwoot.schema';
|
||||||
export * from '@api/integrations/chatbot/dify/validate/dify.schema';
|
export * from '@api/integrations/chatbot/dify/validate/dify.schema';
|
||||||
|
export * from '@api/integrations/chatbot/generic/validate/generic.schema';
|
||||||
export * from '@api/integrations/chatbot/openai/validate/openai.schema';
|
export * from '@api/integrations/chatbot/openai/validate/openai.schema';
|
||||||
export * from '@api/integrations/chatbot/typebot/validate/typebot.schema';
|
export * from '@api/integrations/chatbot/typebot/validate/typebot.schema';
|
||||||
|
@ -711,7 +711,7 @@ export class DifyController extends ChatbotController implements ChatbotControll
|
|||||||
if (!this.integrationEnabled) return;
|
if (!this.integrationEnabled) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const settings = await this.prismaRepository.difySetting.findFirst({
|
const settings = await this.settingsRepository.findFirst({
|
||||||
where: {
|
where: {
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
},
|
},
|
||||||
|
@ -25,7 +25,7 @@ export class DifyService {
|
|||||||
sessionId: data.remoteJid,
|
sessionId: data.remoteJid,
|
||||||
status: 'opened',
|
status: 'opened',
|
||||||
awaitUser: false,
|
awaitUser: false,
|
||||||
botId: data.difyId,
|
botId: data.botId,
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -52,7 +52,7 @@ export class DifyService {
|
|||||||
) {
|
) {
|
||||||
const data = await this.createNewSession(instance, {
|
const data = await this.createNewSession(instance, {
|
||||||
remoteJid,
|
remoteJid,
|
||||||
difyId: dify.id,
|
botId: dify.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data.session) {
|
if (data.session) {
|
||||||
|
@ -0,0 +1,806 @@
|
|||||||
|
import { IgnoreJidDto } from '@api/dto/chatbot.dto';
|
||||||
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
|
import { PrismaRepository } from '@api/repository/repository.service';
|
||||||
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
|
import { Logger } from '@config/logger.config';
|
||||||
|
import { BadRequestException } from '@exceptions';
|
||||||
|
import { getConversationMessage } from '@utils/getConversationMessage';
|
||||||
|
|
||||||
|
import { ChatbotController, ChatbotControllerInterface, EmitData } from '../../chatbot.controller';
|
||||||
|
import { GenericBotDto } from '../dto/generic.dto';
|
||||||
|
import { GenericService } from '../services/generic.service';
|
||||||
|
|
||||||
|
export class GenericController extends ChatbotController implements ChatbotControllerInterface {
|
||||||
|
constructor(
|
||||||
|
private readonly genericService: GenericService,
|
||||||
|
prismaRepository: PrismaRepository,
|
||||||
|
waMonitor: WAMonitoringService,
|
||||||
|
) {
|
||||||
|
super(prismaRepository, waMonitor);
|
||||||
|
|
||||||
|
this.botRepository = this.prismaRepository.genericBot;
|
||||||
|
this.settingsRepository = this.prismaRepository.genericSetting;
|
||||||
|
this.sessionRepository = this.prismaRepository.integrationSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly logger = new Logger(GenericController.name);
|
||||||
|
|
||||||
|
integrationEnabled: boolean;
|
||||||
|
botRepository: any;
|
||||||
|
settingsRepository: any;
|
||||||
|
sessionRepository: any;
|
||||||
|
userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {};
|
||||||
|
|
||||||
|
// Bots
|
||||||
|
public async createBot(instance: InstanceDto, data: GenericBotDto) {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!data.expire ||
|
||||||
|
!data.keywordFinish ||
|
||||||
|
!data.delayMessage ||
|
||||||
|
!data.unknownMessage ||
|
||||||
|
!data.listeningFromMe ||
|
||||||
|
!data.stopBotFromMe ||
|
||||||
|
!data.keepOpen ||
|
||||||
|
!data.debounceTime ||
|
||||||
|
!data.ignoreJids
|
||||||
|
) {
|
||||||
|
const defaultSettingCheck = await this.settingsRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data.expire) data.expire = defaultSettingCheck?.expire || 0;
|
||||||
|
if (!data.keywordFinish) data.keywordFinish = defaultSettingCheck?.keywordFinish || '';
|
||||||
|
if (!data.delayMessage) data.delayMessage = defaultSettingCheck?.delayMessage || 1000;
|
||||||
|
if (!data.unknownMessage) data.unknownMessage = defaultSettingCheck?.unknownMessage || '';
|
||||||
|
if (!data.listeningFromMe) data.listeningFromMe = defaultSettingCheck?.listeningFromMe || false;
|
||||||
|
if (!data.stopBotFromMe) data.stopBotFromMe = defaultSettingCheck?.stopBotFromMe || false;
|
||||||
|
if (!data.keepOpen) data.keepOpen = defaultSettingCheck?.keepOpen || false;
|
||||||
|
if (!data.debounceTime) data.debounceTime = defaultSettingCheck?.debounceTime || 0;
|
||||||
|
if (!data.ignoreJids) data.ignoreJids = defaultSettingCheck?.ignoreJids || [];
|
||||||
|
|
||||||
|
if (!defaultSettingCheck) {
|
||||||
|
await this.settings(instance, {
|
||||||
|
expire: data.expire,
|
||||||
|
keywordFinish: data.keywordFinish,
|
||||||
|
delayMessage: data.delayMessage,
|
||||||
|
unknownMessage: data.unknownMessage,
|
||||||
|
listeningFromMe: data.listeningFromMe,
|
||||||
|
stopBotFromMe: data.stopBotFromMe,
|
||||||
|
keepOpen: data.keepOpen,
|
||||||
|
debounceTime: data.debounceTime,
|
||||||
|
ignoreJids: data.ignoreJids,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkTriggerAll = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
enabled: true,
|
||||||
|
triggerType: 'all',
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkTriggerAll && data.triggerType === 'all') {
|
||||||
|
throw new Error('You already have a dify with an "All" trigger, you cannot have more bots while it is active');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDuplicate = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
apiUrl: data.apiUrl,
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkDuplicate) {
|
||||||
|
throw new Error('Dify already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.triggerType === 'keyword') {
|
||||||
|
if (!data.triggerOperator || !data.triggerValue) {
|
||||||
|
throw new Error('Trigger operator and value are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDuplicate = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
triggerOperator: data.triggerOperator,
|
||||||
|
triggerValue: data.triggerValue,
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkDuplicate) {
|
||||||
|
throw new Error('Trigger already exists');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.triggerType === 'advanced') {
|
||||||
|
if (!data.triggerValue) {
|
||||||
|
throw new Error('Trigger value is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDuplicate = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
triggerValue: data.triggerValue,
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkDuplicate) {
|
||||||
|
throw new Error('Trigger already exists');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bot = await this.botRepository.create({
|
||||||
|
data: {
|
||||||
|
enabled: data.enabled,
|
||||||
|
description: data.description,
|
||||||
|
apiUrl: data.apiUrl,
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
expire: data.expire,
|
||||||
|
keywordFinish: data.keywordFinish,
|
||||||
|
delayMessage: data.delayMessage,
|
||||||
|
unknownMessage: data.unknownMessage,
|
||||||
|
listeningFromMe: data.listeningFromMe,
|
||||||
|
stopBotFromMe: data.stopBotFromMe,
|
||||||
|
keepOpen: data.keepOpen,
|
||||||
|
debounceTime: data.debounceTime,
|
||||||
|
instanceId: instanceId,
|
||||||
|
triggerType: data.triggerType,
|
||||||
|
triggerOperator: data.triggerOperator,
|
||||||
|
triggerValue: data.triggerValue,
|
||||||
|
ignoreJids: data.ignoreJids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error creating bot');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findBot(instance: InstanceDto) {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const bots = await this.botRepository.findMany({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!bots.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchBot(instance: InstanceDto, botId: string) {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const bot = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
id: botId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!bot) {
|
||||||
|
throw new Error('Bot not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot.instanceId !== instanceId) {
|
||||||
|
throw new Error('Bot not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateBot(instance: InstanceDto, botId: string, data: GenericBotDto) {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const bot = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
id: botId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!bot) {
|
||||||
|
throw new Error('Bot not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot.instanceId !== instanceId) {
|
||||||
|
throw new Error('Bot not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.triggerType === 'all') {
|
||||||
|
const checkTriggerAll = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
enabled: true,
|
||||||
|
triggerType: 'all',
|
||||||
|
id: {
|
||||||
|
not: botId,
|
||||||
|
},
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkTriggerAll) {
|
||||||
|
throw new Error('You already have a bot with an "All" trigger, you cannot have more bots while it is active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDuplicate = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
not: botId,
|
||||||
|
},
|
||||||
|
instanceId: instanceId,
|
||||||
|
apiUrl: data.apiUrl,
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkDuplicate) {
|
||||||
|
throw new Error('Bot already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.triggerType === 'keyword') {
|
||||||
|
if (!data.triggerOperator || !data.triggerValue) {
|
||||||
|
throw new Error('Trigger operator and value are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDuplicate = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
triggerOperator: data.triggerOperator,
|
||||||
|
triggerValue: data.triggerValue,
|
||||||
|
id: { not: botId },
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkDuplicate) {
|
||||||
|
throw new Error('Trigger already exists');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.triggerType === 'advanced') {
|
||||||
|
if (!data.triggerValue) {
|
||||||
|
throw new Error('Trigger value is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDuplicate = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
triggerValue: data.triggerValue,
|
||||||
|
id: { not: botId },
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkDuplicate) {
|
||||||
|
throw new Error('Trigger already exists');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bot = await this.botRepository.update({
|
||||||
|
where: {
|
||||||
|
id: botId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
enabled: data.enabled,
|
||||||
|
apiUrl: data.apiUrl,
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
expire: data.expire,
|
||||||
|
keywordFinish: data.keywordFinish,
|
||||||
|
delayMessage: data.delayMessage,
|
||||||
|
unknownMessage: data.unknownMessage,
|
||||||
|
listeningFromMe: data.listeningFromMe,
|
||||||
|
stopBotFromMe: data.stopBotFromMe,
|
||||||
|
keepOpen: data.keepOpen,
|
||||||
|
debounceTime: data.debounceTime,
|
||||||
|
instanceId: instanceId,
|
||||||
|
triggerType: data.triggerType,
|
||||||
|
triggerOperator: data.triggerOperator,
|
||||||
|
triggerValue: data.triggerValue,
|
||||||
|
ignoreJids: data.ignoreJids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error updating bot');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteBot(instance: InstanceDto, botId: string) {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const bot = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
id: botId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!bot) {
|
||||||
|
throw new Error('Bot not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot.instanceId !== instanceId) {
|
||||||
|
throw new Error('Bot not found');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
|
where: {
|
||||||
|
botId: botId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.botRepository.delete({
|
||||||
|
where: {
|
||||||
|
id: botId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { bot: { id: botId } };
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error deleting bot');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
public async settings(instance: InstanceDto, data: any) {
|
||||||
|
try {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const settings = await this.settingsRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (settings) {
|
||||||
|
const updateSettings = await this.settingsRepository.update({
|
||||||
|
where: {
|
||||||
|
id: settings.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
expire: data.expire,
|
||||||
|
keywordFinish: data.keywordFinish,
|
||||||
|
delayMessage: data.delayMessage,
|
||||||
|
unknownMessage: data.unknownMessage,
|
||||||
|
listeningFromMe: data.listeningFromMe,
|
||||||
|
stopBotFromMe: data.stopBotFromMe,
|
||||||
|
keepOpen: data.keepOpen,
|
||||||
|
debounceTime: data.debounceTime,
|
||||||
|
botIdFallback: data.botIdFallback,
|
||||||
|
ignoreJids: data.ignoreJids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
expire: updateSettings.expire,
|
||||||
|
keywordFinish: updateSettings.keywordFinish,
|
||||||
|
delayMessage: updateSettings.delayMessage,
|
||||||
|
unknownMessage: updateSettings.unknownMessage,
|
||||||
|
listeningFromMe: updateSettings.listeningFromMe,
|
||||||
|
stopBotFromMe: updateSettings.stopBotFromMe,
|
||||||
|
keepOpen: updateSettings.keepOpen,
|
||||||
|
debounceTime: updateSettings.debounceTime,
|
||||||
|
botIdFallback: updateSettings.botIdFallback,
|
||||||
|
ignoreJids: updateSettings.ignoreJids,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSetttings = await this.settingsRepository.create({
|
||||||
|
data: {
|
||||||
|
expire: data.expire,
|
||||||
|
keywordFinish: data.keywordFinish,
|
||||||
|
delayMessage: data.delayMessage,
|
||||||
|
unknownMessage: data.unknownMessage,
|
||||||
|
listeningFromMe: data.listeningFromMe,
|
||||||
|
stopBotFromMe: data.stopBotFromMe,
|
||||||
|
keepOpen: data.keepOpen,
|
||||||
|
debounceTime: data.debounceTime,
|
||||||
|
botIdFallback: data.botIdFallback,
|
||||||
|
ignoreJids: data.ignoreJids,
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
expire: newSetttings.expire,
|
||||||
|
keywordFinish: newSetttings.keywordFinish,
|
||||||
|
delayMessage: newSetttings.delayMessage,
|
||||||
|
unknownMessage: newSetttings.unknownMessage,
|
||||||
|
listeningFromMe: newSetttings.listeningFromMe,
|
||||||
|
stopBotFromMe: newSetttings.stopBotFromMe,
|
||||||
|
keepOpen: newSetttings.keepOpen,
|
||||||
|
debounceTime: newSetttings.debounceTime,
|
||||||
|
botIdFallback: newSetttings.botIdFallback,
|
||||||
|
ignoreJids: newSetttings.ignoreJids,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error setting default settings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchSettings(instance: InstanceDto) {
|
||||||
|
try {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const settings = await this.settingsRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Fallback: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!settings) {
|
||||||
|
return {
|
||||||
|
expire: 0,
|
||||||
|
keywordFinish: '',
|
||||||
|
delayMessage: 0,
|
||||||
|
unknownMessage: '',
|
||||||
|
listeningFromMe: false,
|
||||||
|
stopBotFromMe: false,
|
||||||
|
keepOpen: false,
|
||||||
|
ignoreJids: [],
|
||||||
|
botIdFallback: '',
|
||||||
|
fallback: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
expire: settings.expire,
|
||||||
|
keywordFinish: settings.keywordFinish,
|
||||||
|
delayMessage: settings.delayMessage,
|
||||||
|
unknownMessage: settings.unknownMessage,
|
||||||
|
listeningFromMe: settings.listeningFromMe,
|
||||||
|
stopBotFromMe: settings.stopBotFromMe,
|
||||||
|
keepOpen: settings.keepOpen,
|
||||||
|
ignoreJids: settings.ignoreJids,
|
||||||
|
botIdFallback: settings.botIdFallback,
|
||||||
|
fallback: settings.Fallback,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error fetching default settings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sessions
|
||||||
|
public async changeStatus(instance: InstanceDto, data: any) {
|
||||||
|
if (!this.integrationEnabled) throw new BadRequestException('Dify is disabled');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const defaultSettingCheck = await this.settingsRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const remoteJid = data.remoteJid;
|
||||||
|
const status = data.status;
|
||||||
|
|
||||||
|
if (status === 'delete') {
|
||||||
|
await this.sessionRepository.deleteMany({
|
||||||
|
where: {
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
botId: { not: null },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { bot: { remoteJid: remoteJid, status: status } };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'closed') {
|
||||||
|
if (defaultSettingCheck?.keepOpen) {
|
||||||
|
await this.sessionRepository.updateMany({
|
||||||
|
where: {
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
botId: { not: null },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'closed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.sessionRepository.deleteMany({
|
||||||
|
where: {
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
botId: { not: null },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { bot: { ...instance, bot: { remoteJid: remoteJid, status: status } } };
|
||||||
|
} else {
|
||||||
|
const session = await this.sessionRepository.updateMany({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
botId: { not: null },
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: status,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const botData = {
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
status: status,
|
||||||
|
session,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { bot: { ...instance, bot: botData } };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error changing status');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchSessions(instance: InstanceDto, botId: string, remoteJid?: string) {
|
||||||
|
if (!this.integrationEnabled) throw new BadRequestException('Dify is disabled');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const bot = await this.botRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
id: botId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (bot && bot.instanceId !== instanceId) {
|
||||||
|
throw new Error('Dify not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.sessionRepository.findMany({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
remoteJid,
|
||||||
|
botId: bot ? botId : { not: null },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error fetching sessions');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ignoreJid(instance: InstanceDto, data: IgnoreJidDto) {
|
||||||
|
if (!this.integrationEnabled) throw new BadRequestException('Dify is disabled');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const instanceId = await this.prismaRepository.instance
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
name: instance.instanceName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((instance) => instance.id);
|
||||||
|
|
||||||
|
const settings = await this.settingsRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!settings) {
|
||||||
|
throw new Error('Settings not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
let ignoreJids: any = settings?.ignoreJids || [];
|
||||||
|
|
||||||
|
if (data.action === 'add') {
|
||||||
|
if (ignoreJids.includes(data.remoteJid)) return { ignoreJids: ignoreJids };
|
||||||
|
|
||||||
|
ignoreJids.push(data.remoteJid);
|
||||||
|
} else {
|
||||||
|
ignoreJids = ignoreJids.filter((jid) => jid !== data.remoteJid);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSettings = await this.settingsRepository.update({
|
||||||
|
where: {
|
||||||
|
id: settings.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
ignoreJids: ignoreJids,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
ignoreJids: updateSettings.ignoreJids,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error('Error setting default settings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit
|
||||||
|
public async emit({ instance, remoteJid, msg }: EmitData) {
|
||||||
|
if (!this.integrationEnabled) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const settings = await this.settingsRepository.findFirst({
|
||||||
|
where: {
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.checkIgnoreJids(settings?.ignoreJids, remoteJid)) return;
|
||||||
|
|
||||||
|
const session = await this.getSession(remoteJid, instance);
|
||||||
|
|
||||||
|
const content = getConversationMessage(msg);
|
||||||
|
|
||||||
|
const findBot = await this.findBotTrigger(
|
||||||
|
this.botRepository,
|
||||||
|
this.settingsRepository,
|
||||||
|
content,
|
||||||
|
instance,
|
||||||
|
session,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!findBot) return;
|
||||||
|
|
||||||
|
let expire = findBot.expire;
|
||||||
|
let keywordFinish = findBot.keywordFinish;
|
||||||
|
let delayMessage = findBot.delayMessage;
|
||||||
|
let unknownMessage = findBot.unknownMessage;
|
||||||
|
let listeningFromMe = findBot.listeningFromMe;
|
||||||
|
let stopBotFromMe = findBot.stopBotFromMe;
|
||||||
|
let keepOpen = findBot.keepOpen;
|
||||||
|
let debounceTime = findBot.debounceTime;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!expire ||
|
||||||
|
!keywordFinish ||
|
||||||
|
!delayMessage ||
|
||||||
|
!unknownMessage ||
|
||||||
|
!listeningFromMe ||
|
||||||
|
!stopBotFromMe ||
|
||||||
|
!keepOpen ||
|
||||||
|
!debounceTime
|
||||||
|
) {
|
||||||
|
if (!expire) expire = settings.expire;
|
||||||
|
|
||||||
|
if (!keywordFinish) keywordFinish = settings.keywordFinish;
|
||||||
|
|
||||||
|
if (!delayMessage) delayMessage = settings.delayMessage;
|
||||||
|
|
||||||
|
if (!unknownMessage) unknownMessage = settings.unknownMessage;
|
||||||
|
|
||||||
|
if (!listeningFromMe) listeningFromMe = settings.listeningFromMe;
|
||||||
|
|
||||||
|
if (!stopBotFromMe) stopBotFromMe = settings.stopBotFromMe;
|
||||||
|
|
||||||
|
if (!keepOpen) keepOpen = settings.keepOpen;
|
||||||
|
|
||||||
|
if (!debounceTime) debounceTime = settings.debounceTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = msg.key as {
|
||||||
|
id: string;
|
||||||
|
remoteJid: string;
|
||||||
|
fromMe: boolean;
|
||||||
|
participant: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stopBotFromMe && key.fromMe && session) {
|
||||||
|
await this.prismaRepository.integrationSession.update({
|
||||||
|
where: {
|
||||||
|
id: session.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'paused',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!listeningFromMe && key.fromMe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debounceTime && debounceTime > 0) {
|
||||||
|
this.processDebounce(this.userMessageDebounce, content, remoteJid, debounceTime, async (debouncedContent) => {
|
||||||
|
await this.genericService.processBot(
|
||||||
|
this.waMonitor.waInstances[instance.instanceName],
|
||||||
|
remoteJid,
|
||||||
|
findBot,
|
||||||
|
session,
|
||||||
|
settings,
|
||||||
|
debouncedContent,
|
||||||
|
msg?.pushName,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.genericService.processBot(
|
||||||
|
this.waMonitor.waInstances[instance.instanceName],
|
||||||
|
remoteJid,
|
||||||
|
findBot,
|
||||||
|
session,
|
||||||
|
settings,
|
||||||
|
content,
|
||||||
|
msg?.pushName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/api/integrations/chatbot/generic/dto/generic.dto.ts
Normal file
33
src/api/integrations/chatbot/generic/dto/generic.dto.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { TriggerOperator, TriggerType } from '@prisma/client';
|
||||||
|
|
||||||
|
export class GenericBotDto {
|
||||||
|
enabled?: boolean;
|
||||||
|
description?: string;
|
||||||
|
apiUrl?: string;
|
||||||
|
apiKey?: string;
|
||||||
|
expire?: number;
|
||||||
|
keywordFinish?: string;
|
||||||
|
delayMessage?: number;
|
||||||
|
unknownMessage?: string;
|
||||||
|
listeningFromMe?: boolean;
|
||||||
|
stopBotFromMe?: boolean;
|
||||||
|
keepOpen?: boolean;
|
||||||
|
debounceTime?: number;
|
||||||
|
triggerType?: TriggerType;
|
||||||
|
triggerOperator?: TriggerOperator;
|
||||||
|
triggerValue?: string;
|
||||||
|
ignoreJids?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GenericBotSettingDto {
|
||||||
|
expire?: number;
|
||||||
|
keywordFinish?: string;
|
||||||
|
delayMessage?: number;
|
||||||
|
unknownMessage?: string;
|
||||||
|
listeningFromMe?: boolean;
|
||||||
|
stopBotFromMe?: boolean;
|
||||||
|
keepOpen?: boolean;
|
||||||
|
debounceTime?: number;
|
||||||
|
botIdFallback?: string;
|
||||||
|
ignoreJids?: any;
|
||||||
|
}
|
124
src/api/integrations/chatbot/generic/routes/generic.router.ts
Normal file
124
src/api/integrations/chatbot/generic/routes/generic.router.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||||
|
import { IgnoreJidDto } from '@api/dto/chatbot.dto';
|
||||||
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
|
import { HttpStatus } from '@api/routes/index.router';
|
||||||
|
import { genericController } from '@api/server.module';
|
||||||
|
import { instanceSchema } from '@validate/instance.schema';
|
||||||
|
import { RequestHandler, Router } from 'express';
|
||||||
|
|
||||||
|
import { GenericBotDto, GenericBotSettingDto } from '../dto/generic.dto';
|
||||||
|
import {
|
||||||
|
genericIgnoreJidSchema,
|
||||||
|
genericSchema,
|
||||||
|
genericSettingSchema,
|
||||||
|
genericStatusSchema,
|
||||||
|
} from '../validate/generic.schema';
|
||||||
|
|
||||||
|
export class GenericRouter extends RouterBroker {
|
||||||
|
constructor(...guards: RequestHandler[]) {
|
||||||
|
super();
|
||||||
|
this.router
|
||||||
|
.post(this.routerPath('create'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<GenericBotDto>({
|
||||||
|
request: req,
|
||||||
|
schema: genericSchema,
|
||||||
|
ClassRef: GenericBotDto,
|
||||||
|
execute: (instance, data) => genericController.createBot(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
|
})
|
||||||
|
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => genericController.findBot(instance),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.get(this.routerPath('fetch/:genericId'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => genericController.fetchBot(instance, req.params.genericId),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.put(this.routerPath('update/:genericId'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<GenericBotDto>({
|
||||||
|
request: req,
|
||||||
|
schema: genericSchema,
|
||||||
|
ClassRef: GenericBotDto,
|
||||||
|
execute: (instance, data) => genericController.updateBot(instance, req.params.genericId, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.delete(this.routerPath('delete/:genericId'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => genericController.deleteBot(instance, req.params.genericId),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.post(this.routerPath('settings'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<GenericBotSettingDto>({
|
||||||
|
request: req,
|
||||||
|
schema: genericSettingSchema,
|
||||||
|
ClassRef: GenericBotSettingDto,
|
||||||
|
execute: (instance, data) => genericController.settings(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.get(this.routerPath('fetchSettings'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => genericController.fetchSettings(instance),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.post(this.routerPath('changeStatus'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: genericStatusSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance, data) => genericController.changeStatus(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.get(this.routerPath('fetchSessions/:genericId'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => genericController.fetchSessions(instance, req.params.genericId),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.post(this.routerPath('ignoreJid'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<IgnoreJidDto>({
|
||||||
|
request: req,
|
||||||
|
schema: genericIgnoreJidSchema,
|
||||||
|
ClassRef: IgnoreJidDto,
|
||||||
|
execute: (instance, data) => genericController.ignoreJid(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly router: Router = Router();
|
||||||
|
}
|
299
src/api/integrations/chatbot/generic/services/generic.service.ts
Normal file
299
src/api/integrations/chatbot/generic/services/generic.service.ts
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
|
import { PrismaRepository } from '@api/repository/repository.service';
|
||||||
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
|
import { Auth, ConfigService, HttpServer } from '@config/env.config';
|
||||||
|
import { Logger } from '@config/logger.config';
|
||||||
|
import { GenericBot, GenericSetting, IntegrationSession } from '@prisma/client';
|
||||||
|
import { sendTelemetry } from '@utils/sendTelemetry';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export class GenericService {
|
||||||
|
constructor(
|
||||||
|
private readonly waMonitor: WAMonitoringService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
private readonly prismaRepository: PrismaRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private readonly logger = new Logger('GenericService');
|
||||||
|
|
||||||
|
public async createNewSession(instance: InstanceDto, data: any) {
|
||||||
|
try {
|
||||||
|
const session = await this.prismaRepository.integrationSession.create({
|
||||||
|
data: {
|
||||||
|
remoteJid: data.remoteJid,
|
||||||
|
sessionId: data.remoteJid,
|
||||||
|
status: 'opened',
|
||||||
|
awaitUser: false,
|
||||||
|
botId: data.botId,
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { session };
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isImageMessage(content: string) {
|
||||||
|
return content.includes('imageMessage');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendMessageToBot(
|
||||||
|
instance: any,
|
||||||
|
session: IntegrationSession,
|
||||||
|
bot: GenericBot,
|
||||||
|
remoteJid: string,
|
||||||
|
pushName: string,
|
||||||
|
content: string,
|
||||||
|
) {
|
||||||
|
const payload: any = {
|
||||||
|
inputs: {
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
pushName: pushName,
|
||||||
|
instanceName: instance.instanceName,
|
||||||
|
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
|
||||||
|
apiKey: this.configService.get<Auth>('AUTHENTICATION').API_KEY.KEY,
|
||||||
|
},
|
||||||
|
query: content,
|
||||||
|
conversation_id: session.sessionId === remoteJid ? undefined : session.sessionId,
|
||||||
|
user: remoteJid,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.isImageMessage(content)) {
|
||||||
|
const contentSplit = content.split('|');
|
||||||
|
|
||||||
|
payload.files = [
|
||||||
|
{
|
||||||
|
type: 'image',
|
||||||
|
url: contentSplit[1].split('?')[0],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
payload.query = contentSplit[2] || content;
|
||||||
|
}
|
||||||
|
|
||||||
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
|
|
||||||
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
|
|
||||||
|
let headers: any = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bot.apiKey) {
|
||||||
|
headers = {
|
||||||
|
...headers,
|
||||||
|
Authorization: `Bearer ${bot.apiKey}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post(bot.apiUrl, payload, {
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
|
|
||||||
|
const message = response?.data?.answer;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendMessageWhatsApp(
|
||||||
|
instance: any,
|
||||||
|
remoteJid: string,
|
||||||
|
session: IntegrationSession,
|
||||||
|
settings: GenericSetting,
|
||||||
|
message: string,
|
||||||
|
) {
|
||||||
|
const regex = /!?\[(.*?)\]\((.*?)\)/g;
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
let lastIndex = 0;
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(message)) !== null) {
|
||||||
|
if (match.index > lastIndex) {
|
||||||
|
result.push({ text: message.slice(lastIndex, match.index).trim() });
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({ caption: match[1], url: match[2] });
|
||||||
|
|
||||||
|
lastIndex = regex.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastIndex < message.length) {
|
||||||
|
result.push({ text: message.slice(lastIndex).trim() });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of result) {
|
||||||
|
if (item.text) {
|
||||||
|
await instance.textMessage(
|
||||||
|
{
|
||||||
|
number: remoteJid.split('@')[0],
|
||||||
|
delay: settings?.delayMessage || 1000,
|
||||||
|
text: item.text,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.url) {
|
||||||
|
await instance.mediaMessage(
|
||||||
|
{
|
||||||
|
number: remoteJid.split('@')[0],
|
||||||
|
delay: settings?.delayMessage || 1000,
|
||||||
|
mediatype: 'image',
|
||||||
|
media: item.url,
|
||||||
|
caption: item.caption,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prismaRepository.integrationSession.update({
|
||||||
|
where: {
|
||||||
|
id: session.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'opened',
|
||||||
|
awaitUser: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sendTelemetry('/message/sendText');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initNewSession(
|
||||||
|
instance: any,
|
||||||
|
remoteJid: string,
|
||||||
|
bot: GenericBot,
|
||||||
|
settings: GenericSetting,
|
||||||
|
session: IntegrationSession,
|
||||||
|
content: string,
|
||||||
|
pushName?: string,
|
||||||
|
) {
|
||||||
|
const data = await this.createNewSession(instance, {
|
||||||
|
remoteJid,
|
||||||
|
botId: bot.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.session) {
|
||||||
|
session = data.session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await this.sendMessageToBot(instance, session, bot, remoteJid, pushName, content);
|
||||||
|
|
||||||
|
await this.sendMessageWhatsApp(instance, remoteJid, session, settings, message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async processBot(
|
||||||
|
instance: any,
|
||||||
|
remoteJid: string,
|
||||||
|
bot: GenericBot,
|
||||||
|
session: IntegrationSession,
|
||||||
|
settings: GenericSetting,
|
||||||
|
content: string,
|
||||||
|
pushName?: string,
|
||||||
|
) {
|
||||||
|
if (session && session.status !== 'opened') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session && settings.expire && settings.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 > settings.expire) {
|
||||||
|
if (settings.keepOpen) {
|
||||||
|
await this.prismaRepository.integrationSession.update({
|
||||||
|
where: {
|
||||||
|
id: session.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'closed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
|
where: {
|
||||||
|
botId: bot.id,
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.initNewSession(instance, remoteJid, bot, settings, session, content, pushName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
await this.initNewSession(instance, remoteJid, bot, settings, session, content, pushName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prismaRepository.integrationSession.update({
|
||||||
|
where: {
|
||||||
|
id: session.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'opened',
|
||||||
|
awaitUser: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
if (settings.unknownMessage) {
|
||||||
|
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
||||||
|
{
|
||||||
|
number: remoteJid.split('@')[0],
|
||||||
|
delay: settings.delayMessage || 1000,
|
||||||
|
text: settings.unknownMessage,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
sendTelemetry('/message/sendText');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
||||||
|
if (settings.keepOpen) {
|
||||||
|
await this.prismaRepository.integrationSession.update({
|
||||||
|
where: {
|
||||||
|
id: session.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'closed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
|
where: {
|
||||||
|
botId: bot.id,
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await this.sendMessageToBot(instance, session, bot, remoteJid, pushName, content);
|
||||||
|
|
||||||
|
await this.sendMessageWhatsApp(instance, remoteJid, session, settings, message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
107
src/api/integrations/chatbot/generic/validate/generic.schema.ts
Normal file
107
src/api/integrations/chatbot/generic/validate/generic.schema.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { JSONSchema7 } from 'json-schema';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||||
|
const properties = {};
|
||||||
|
propertyNames.forEach(
|
||||||
|
(property) =>
|
||||||
|
(properties[property] = {
|
||||||
|
minLength: 1,
|
||||||
|
description: `The "${property}" cannot be empty`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
if: {
|
||||||
|
propertyNames: {
|
||||||
|
enum: [...propertyNames],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
then: { properties },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genericSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
enabled: { type: 'boolean' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
apiUrl: { type: 'string' },
|
||||||
|
apiKey: { type: 'string' },
|
||||||
|
triggerType: { type: 'string', enum: ['all', 'keyword', 'none', 'advanced'] },
|
||||||
|
triggerOperator: { type: 'string', enum: ['equals', 'contains', 'startsWith', 'endsWith', 'regex'] },
|
||||||
|
triggerValue: { type: 'string' },
|
||||||
|
expire: { type: 'integer' },
|
||||||
|
keywordFinish: { type: 'string' },
|
||||||
|
delayMessage: { type: 'integer' },
|
||||||
|
unknownMessage: { type: 'string' },
|
||||||
|
listeningFromMe: { type: 'boolean' },
|
||||||
|
stopBotFromMe: { type: 'boolean' },
|
||||||
|
keepOpen: { type: 'boolean' },
|
||||||
|
debounceTime: { type: 'integer' },
|
||||||
|
ignoreJids: { type: 'array', items: { type: 'string' } },
|
||||||
|
},
|
||||||
|
required: ['enabled', 'apiUrl', 'triggerType'],
|
||||||
|
...isNotEmpty('enabled', 'apiUrl', 'triggerType'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genericStatusSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
remoteJid: { type: 'string' },
|
||||||
|
status: { type: 'string', enum: ['opened', 'closed', 'paused', 'delete'] },
|
||||||
|
},
|
||||||
|
required: ['remoteJid', 'status'],
|
||||||
|
...isNotEmpty('remoteJid', 'status'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genericSettingSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
expire: { type: 'integer' },
|
||||||
|
keywordFinish: { type: 'string' },
|
||||||
|
delayMessage: { type: 'integer' },
|
||||||
|
unknownMessage: { type: 'string' },
|
||||||
|
listeningFromMe: { type: 'boolean' },
|
||||||
|
stopBotFromMe: { type: 'boolean' },
|
||||||
|
keepOpen: { type: 'boolean' },
|
||||||
|
debounceTime: { type: 'integer' },
|
||||||
|
ignoreJids: { type: 'array', items: { type: 'string' } },
|
||||||
|
botIdFallback: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: [
|
||||||
|
'expire',
|
||||||
|
'keywordFinish',
|
||||||
|
'delayMessage',
|
||||||
|
'unknownMessage',
|
||||||
|
'listeningFromMe',
|
||||||
|
'stopBotFromMe',
|
||||||
|
'keepOpen',
|
||||||
|
'debounceTime',
|
||||||
|
'ignoreJids',
|
||||||
|
],
|
||||||
|
...isNotEmpty(
|
||||||
|
'expire',
|
||||||
|
'keywordFinish',
|
||||||
|
'delayMessage',
|
||||||
|
'unknownMessage',
|
||||||
|
'listeningFromMe',
|
||||||
|
'stopBotFromMe',
|
||||||
|
'keepOpen',
|
||||||
|
'debounceTime',
|
||||||
|
'ignoreJids',
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genericIgnoreJidSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
remoteJid: { type: 'string' },
|
||||||
|
action: { type: 'string', enum: ['add', 'remove'] },
|
||||||
|
},
|
||||||
|
required: ['remoteJid', 'action'],
|
||||||
|
...isNotEmpty('remoteJid', 'action'),
|
||||||
|
};
|
@ -17,6 +17,8 @@ import { ChatwootController } from './integrations/chatbot/chatwoot/controllers/
|
|||||||
import { ChatwootService } from './integrations/chatbot/chatwoot/services/chatwoot.service';
|
import { ChatwootService } from './integrations/chatbot/chatwoot/services/chatwoot.service';
|
||||||
import { DifyController } from './integrations/chatbot/dify/controllers/dify.controller';
|
import { DifyController } from './integrations/chatbot/dify/controllers/dify.controller';
|
||||||
import { DifyService } from './integrations/chatbot/dify/services/dify.service';
|
import { DifyService } from './integrations/chatbot/dify/services/dify.service';
|
||||||
|
import { GenericController } from './integrations/chatbot/generic/controllers/generic.controller';
|
||||||
|
import { GenericService } from './integrations/chatbot/generic/services/generic.service';
|
||||||
import { OpenaiController } from './integrations/chatbot/openai/controllers/openai.controller';
|
import { OpenaiController } from './integrations/chatbot/openai/controllers/openai.controller';
|
||||||
import { OpenaiService } from './integrations/chatbot/openai/services/openai.service';
|
import { OpenaiService } from './integrations/chatbot/openai/services/openai.service';
|
||||||
import { TypebotController } from './integrations/chatbot/typebot/controllers/typebot.controller';
|
import { TypebotController } from './integrations/chatbot/typebot/controllers/typebot.controller';
|
||||||
@ -116,4 +118,7 @@ export const openaiController = new OpenaiController(openaiService, prismaReposi
|
|||||||
const difyService = new DifyService(waMonitor, configService, prismaRepository);
|
const difyService = new DifyService(waMonitor, configService, prismaRepository);
|
||||||
export const difyController = new DifyController(difyService, prismaRepository, waMonitor);
|
export const difyController = new DifyController(difyService, prismaRepository, waMonitor);
|
||||||
|
|
||||||
|
const genericService = new GenericService(waMonitor, configService, prismaRepository);
|
||||||
|
export const genericController = new GenericController(genericService, prismaRepository, waMonitor);
|
||||||
|
|
||||||
logger.info('Module - ON');
|
logger.info('Module - ON');
|
||||||
|
Loading…
Reference in New Issue
Block a user