mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-12 19:39:36 -06:00
refactor: remove EvolutionBot chatbot integration
Remove the EvolutionBot chatbot integration as it is no longer needed. The project now supports only external chatbot integrations: Chatwoot, Typebot, OpenAI, Dify, Flowise, N8N, and EvoAI. Changes: - Deleted EvolutionBot directory with all files (controller, service, router, dto, schema) - Removed EvolutionBot models from Prisma schemas (postgresql and mysql) - Updated ChatbotController to remove evolutionBotController.emit() call - Updated ChatbotRouter to remove EvolutionBot route - Cleaned up imports and exports in server.module.ts - Updated documentation (CLAUDE.md, AGENTS.md) Note: Prisma migrations will be created separately to drop EvolutionBot and EvolutionBotSetting tables from the database.
This commit is contained in:
parent
f7f8bf1182
commit
8884ef42d0
@ -236,7 +236,7 @@ const result = await this.prismaRepository.instance.findUnique({
|
|||||||
- **Base classes**: Extend `BaseChatbotService` and `BaseChatbotController`
|
- **Base classes**: Extend `BaseChatbotService` and `BaseChatbotController`
|
||||||
- **Trigger system**: Support keyword, regex, and advanced triggers
|
- **Trigger system**: Support keyword, regex, and advanced triggers
|
||||||
- **Session management**: Handle conversation state per user
|
- **Session management**: Handle conversation state per user
|
||||||
- **Available integrations**: EvolutionBot, OpenAI, Dify, Typebot, Chatwoot, Flowise, N8N, EvoAI
|
- **Available integrations**: OpenAI, Dify, Typebot, Chatwoot, Flowise, N8N, EvoAI
|
||||||
|
|
||||||
### Event Integration
|
### Event Integration
|
||||||
- **Internal events**: EventEmitter2 for application events
|
- **Internal events**: EventEmitter2 for application events
|
||||||
|
|||||||
@ -101,7 +101,6 @@ src/
|
|||||||
- Connection lifecycle management per instance with automatic reconnection
|
- Connection lifecycle management per instance with automatic reconnection
|
||||||
|
|
||||||
**Chatbot Integrations** (`src/api/integrations/chatbot/`):
|
**Chatbot Integrations** (`src/api/integrations/chatbot/`):
|
||||||
- **EvolutionBot**: Native chatbot with trigger system
|
|
||||||
- **Chatwoot**: Customer service platform integration
|
- **Chatwoot**: Customer service platform integration
|
||||||
- **Typebot**: Visual chatbot flow builder
|
- **Typebot**: Visual chatbot flow builder
|
||||||
- **OpenAI**: AI capabilities including GPT and Whisper (audio transcription)
|
- **OpenAI**: AI capabilities including GPT and Whisper (audio transcription)
|
||||||
|
|||||||
@ -102,8 +102,6 @@ model Instance {
|
|||||||
Dify Dify[]
|
Dify Dify[]
|
||||||
DifySetting DifySetting?
|
DifySetting DifySetting?
|
||||||
IntegrationSession IntegrationSession[]
|
IntegrationSession IntegrationSession[]
|
||||||
EvolutionBot EvolutionBot[]
|
|
||||||
EvolutionBotSetting EvolutionBotSetting?
|
|
||||||
Flowise Flowise[]
|
Flowise Flowise[]
|
||||||
FlowiseSetting FlowiseSetting?
|
FlowiseSetting FlowiseSetting?
|
||||||
N8n N8n[]
|
N8n N8n[]
|
||||||
@ -555,54 +553,6 @@ model DifySetting {
|
|||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
model EvolutionBot {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
enabled Boolean @default(true)
|
|
||||||
description String? @db.VarChar(255)
|
|
||||||
apiUrl String? @db.VarChar(255)
|
|
||||||
apiKey String? @db.VarChar(255)
|
|
||||||
expire Int? @default(0) @db.Int
|
|
||||||
keywordFinish String? @db.VarChar(100)
|
|
||||||
delayMessage Int? @db.Int
|
|
||||||
unknownMessage String? @db.VarChar(100)
|
|
||||||
listeningFromMe Boolean? @default(false)
|
|
||||||
stopBotFromMe Boolean? @default(false)
|
|
||||||
keepOpen Boolean? @default(false)
|
|
||||||
debounceTime Int? @db.Int
|
|
||||||
ignoreJids Json?
|
|
||||||
splitMessages Boolean? @default(false)
|
|
||||||
timePerChar Int? @default(50) @db.Int
|
|
||||||
triggerType TriggerType?
|
|
||||||
triggerOperator TriggerOperator?
|
|
||||||
triggerValue String?
|
|
||||||
createdAt DateTime? @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp
|
|
||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
|
||||||
instanceId String
|
|
||||||
EvolutionBotSetting EvolutionBotSetting[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model EvolutionBotSetting {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
expire Int? @default(0) @db.Int
|
|
||||||
keywordFinish String? @db.VarChar(100)
|
|
||||||
delayMessage Int? @db.Int
|
|
||||||
unknownMessage String? @db.VarChar(100)
|
|
||||||
listeningFromMe Boolean? @default(false)
|
|
||||||
stopBotFromMe Boolean? @default(false)
|
|
||||||
keepOpen Boolean? @default(false)
|
|
||||||
debounceTime Int? @db.Int
|
|
||||||
ignoreJids Json?
|
|
||||||
splitMessages Boolean? @default(false)
|
|
||||||
timePerChar Int? @default(50) @db.Int
|
|
||||||
createdAt DateTime? @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp
|
|
||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
|
||||||
Fallback EvolutionBot? @relation(fields: [botIdFallback], references: [id])
|
|
||||||
botIdFallback String? @db.VarChar(100)
|
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
|
||||||
instanceId String @unique
|
|
||||||
}
|
|
||||||
|
|
||||||
model Flowise {
|
model Flowise {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
enabled Boolean @default(true)
|
enabled Boolean @default(true)
|
||||||
|
|||||||
@ -102,8 +102,6 @@ model Instance {
|
|||||||
Dify Dify[]
|
Dify Dify[]
|
||||||
DifySetting DifySetting?
|
DifySetting DifySetting?
|
||||||
IntegrationSession IntegrationSession[]
|
IntegrationSession IntegrationSession[]
|
||||||
EvolutionBot EvolutionBot[]
|
|
||||||
EvolutionBotSetting EvolutionBotSetting?
|
|
||||||
Flowise Flowise[]
|
Flowise Flowise[]
|
||||||
FlowiseSetting FlowiseSetting?
|
FlowiseSetting FlowiseSetting?
|
||||||
Pusher Pusher?
|
Pusher Pusher?
|
||||||
@ -559,54 +557,6 @@ model DifySetting {
|
|||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
model EvolutionBot {
|
|
||||||
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?
|
|
||||||
splitMessages Boolean? @default(false) @db.Boolean
|
|
||||||
timePerChar Int? @default(50) @db.Integer
|
|
||||||
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
|
|
||||||
EvolutionBotSetting EvolutionBotSetting[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model EvolutionBotSetting {
|
|
||||||
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?
|
|
||||||
splitMessages Boolean? @default(false) @db.Boolean
|
|
||||||
timePerChar Int? @default(50) @db.Integer
|
|
||||||
createdAt DateTime? @default(now()) @db.Timestamp
|
|
||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
|
||||||
Fallback EvolutionBot? @relation(fields: [botIdFallback], references: [id])
|
|
||||||
botIdFallback String? @db.VarChar(100)
|
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
|
||||||
instanceId String @unique
|
|
||||||
}
|
|
||||||
|
|
||||||
model Flowise {
|
model Flowise {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
enabled Boolean @default(true) @db.Boolean
|
enabled Boolean @default(true) @db.Boolean
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { PrismaRepository } from '@api/repository/repository.service';
|
|||||||
import {
|
import {
|
||||||
difyController,
|
difyController,
|
||||||
evoaiController,
|
evoaiController,
|
||||||
evolutionBotController,
|
|
||||||
flowiseController,
|
flowiseController,
|
||||||
n8nController,
|
n8nController,
|
||||||
openaiController,
|
openaiController,
|
||||||
@ -91,7 +90,6 @@ export class ChatbotController {
|
|||||||
pushName,
|
pushName,
|
||||||
isIntegration,
|
isIntegration,
|
||||||
};
|
};
|
||||||
evolutionBotController.emit(emitData);
|
|
||||||
|
|
||||||
typebotController.emit(emitData);
|
typebotController.emit(emitData);
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { TypebotRouter } from '@api/integrations/chatbot/typebot/routes/typebot.
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
|
|
||||||
import { EvoaiRouter } from './evoai/routes/evoai.router';
|
import { EvoaiRouter } from './evoai/routes/evoai.router';
|
||||||
import { EvolutionBotRouter } from './evolutionBot/routes/evolutionBot.router';
|
|
||||||
import { FlowiseRouter } from './flowise/routes/flowise.router';
|
import { FlowiseRouter } from './flowise/routes/flowise.router';
|
||||||
import { N8nRouter } from './n8n/routes/n8n.router';
|
import { N8nRouter } from './n8n/routes/n8n.router';
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ export class ChatbotRouter {
|
|||||||
constructor(...guards: any[]) {
|
constructor(...guards: any[]) {
|
||||||
this.router = Router();
|
this.router = Router();
|
||||||
|
|
||||||
this.router.use('/evolutionBot', new EvolutionBotRouter(...guards).router);
|
|
||||||
this.router.use('/chatwoot', new ChatwootRouter(...guards).router);
|
this.router.use('/chatwoot', new ChatwootRouter(...guards).router);
|
||||||
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);
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
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/evoai/validate/evoai.schema';
|
export * from '@api/integrations/chatbot/evoai/validate/evoai.schema';
|
||||||
export * from '@api/integrations/chatbot/evolutionBot/validate/evolutionBot.schema';
|
|
||||||
export * from '@api/integrations/chatbot/flowise/validate/flowise.schema';
|
export * from '@api/integrations/chatbot/flowise/validate/flowise.schema';
|
||||||
export * from '@api/integrations/chatbot/n8n/validate/n8n.schema';
|
export * from '@api/integrations/chatbot/n8n/validate/n8n.schema';
|
||||||
export * from '@api/integrations/chatbot/openai/validate/openai.schema';
|
export * from '@api/integrations/chatbot/openai/validate/openai.schema';
|
||||||
|
|||||||
@ -1,96 +0,0 @@
|
|||||||
import { PrismaRepository } from '@api/repository/repository.service';
|
|
||||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
|
||||||
import { Logger } from '@config/logger.config';
|
|
||||||
import { EvolutionBot, IntegrationSession } from '@prisma/client';
|
|
||||||
|
|
||||||
import { BaseChatbotController } from '../../base-chatbot.controller';
|
|
||||||
import { EvolutionBotDto } from '../dto/evolutionBot.dto';
|
|
||||||
import { EvolutionBotService } from '../services/evolutionBot.service';
|
|
||||||
|
|
||||||
export class EvolutionBotController extends BaseChatbotController<EvolutionBot, EvolutionBotDto> {
|
|
||||||
constructor(
|
|
||||||
private readonly evolutionBotService: EvolutionBotService,
|
|
||||||
prismaRepository: PrismaRepository,
|
|
||||||
waMonitor: WAMonitoringService,
|
|
||||||
) {
|
|
||||||
super(prismaRepository, waMonitor);
|
|
||||||
|
|
||||||
this.botRepository = this.prismaRepository.evolutionBot;
|
|
||||||
this.settingsRepository = this.prismaRepository.evolutionBotSetting;
|
|
||||||
this.sessionRepository = this.prismaRepository.integrationSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly logger = new Logger('EvolutionBotController');
|
|
||||||
protected readonly integrationName = 'EvolutionBot';
|
|
||||||
|
|
||||||
integrationEnabled = true; // Set to true by default or use config value if available
|
|
||||||
botRepository: any;
|
|
||||||
settingsRepository: any;
|
|
||||||
sessionRepository: any;
|
|
||||||
userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {};
|
|
||||||
|
|
||||||
// Implementation of abstract methods required by BaseChatbotController
|
|
||||||
|
|
||||||
protected getFallbackBotId(settings: any): string | undefined {
|
|
||||||
return settings?.botIdFallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getFallbackFieldName(): string {
|
|
||||||
return 'botIdFallback';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getIntegrationType(): string {
|
|
||||||
return 'evolution';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getAdditionalBotData(data: EvolutionBotDto): Record<string, any> {
|
|
||||||
return {
|
|
||||||
apiUrl: data.apiUrl,
|
|
||||||
apiKey: data.apiKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for bot-specific updates
|
|
||||||
protected getAdditionalUpdateFields(data: EvolutionBotDto): Record<string, any> {
|
|
||||||
return {
|
|
||||||
apiUrl: data.apiUrl,
|
|
||||||
apiKey: data.apiKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for bot-specific duplicate validation on update
|
|
||||||
protected async validateNoDuplicatesOnUpdate(
|
|
||||||
botId: string,
|
|
||||||
instanceId: string,
|
|
||||||
data: EvolutionBotDto,
|
|
||||||
): Promise<void> {
|
|
||||||
const checkDuplicate = await this.botRepository.findFirst({
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
not: botId,
|
|
||||||
},
|
|
||||||
instanceId: instanceId,
|
|
||||||
apiUrl: data.apiUrl,
|
|
||||||
apiKey: data.apiKey,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (checkDuplicate) {
|
|
||||||
throw new Error('Evolution Bot already exists');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process bot-specific logic
|
|
||||||
protected async processBot(
|
|
||||||
instance: any,
|
|
||||||
remoteJid: string,
|
|
||||||
bot: EvolutionBot,
|
|
||||||
session: IntegrationSession,
|
|
||||||
settings: any,
|
|
||||||
content: string,
|
|
||||||
pushName?: string,
|
|
||||||
msg?: any,
|
|
||||||
) {
|
|
||||||
await this.evolutionBotService.process(instance, remoteJid, bot, session, settings, content, pushName, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { BaseChatbotDto, BaseChatbotSettingDto } from '../../base-chatbot.dto';
|
|
||||||
|
|
||||||
export class EvolutionBotDto extends BaseChatbotDto {
|
|
||||||
apiUrl: string;
|
|
||||||
apiKey: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EvolutionBotSettingDto extends BaseChatbotSettingDto {
|
|
||||||
botIdFallback?: string;
|
|
||||||
}
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
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 { evolutionBotController } from '@api/server.module';
|
|
||||||
import { instanceSchema } from '@validate/instance.schema';
|
|
||||||
import { RequestHandler, Router } from 'express';
|
|
||||||
|
|
||||||
import { EvolutionBotDto, EvolutionBotSettingDto } from '../dto/evolutionBot.dto';
|
|
||||||
import {
|
|
||||||
evolutionBotIgnoreJidSchema,
|
|
||||||
evolutionBotSchema,
|
|
||||||
evolutionBotSettingSchema,
|
|
||||||
evolutionBotStatusSchema,
|
|
||||||
} from '../validate/evolutionBot.schema';
|
|
||||||
|
|
||||||
export class EvolutionBotRouter extends RouterBroker {
|
|
||||||
constructor(...guards: RequestHandler[]) {
|
|
||||||
super();
|
|
||||||
this.router
|
|
||||||
.post(this.routerPath('create'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<EvolutionBotDto>({
|
|
||||||
request: req,
|
|
||||||
schema: evolutionBotSchema,
|
|
||||||
ClassRef: EvolutionBotDto,
|
|
||||||
execute: (instance, data) => evolutionBotController.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) => evolutionBotController.findBot(instance),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
})
|
|
||||||
.get(this.routerPath('fetch/:evolutionBotId'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
|
||||||
request: req,
|
|
||||||
schema: instanceSchema,
|
|
||||||
ClassRef: InstanceDto,
|
|
||||||
execute: (instance) => evolutionBotController.fetchBot(instance, req.params.evolutionBotId),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
})
|
|
||||||
.put(this.routerPath('update/:evolutionBotId'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<EvolutionBotDto>({
|
|
||||||
request: req,
|
|
||||||
schema: evolutionBotSchema,
|
|
||||||
ClassRef: EvolutionBotDto,
|
|
||||||
execute: (instance, data) => evolutionBotController.updateBot(instance, req.params.evolutionBotId, data),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
})
|
|
||||||
.delete(this.routerPath('delete/:evolutionBotId'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
|
||||||
request: req,
|
|
||||||
schema: instanceSchema,
|
|
||||||
ClassRef: InstanceDto,
|
|
||||||
execute: (instance) => evolutionBotController.deleteBot(instance, req.params.evolutionBotId),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
})
|
|
||||||
.post(this.routerPath('settings'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<EvolutionBotSettingDto>({
|
|
||||||
request: req,
|
|
||||||
schema: evolutionBotSettingSchema,
|
|
||||||
ClassRef: EvolutionBotSettingDto,
|
|
||||||
execute: (instance, data) => evolutionBotController.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) => evolutionBotController.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: evolutionBotStatusSchema,
|
|
||||||
ClassRef: InstanceDto,
|
|
||||||
execute: (instance, data) => evolutionBotController.changeStatus(instance, data),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
})
|
|
||||||
.get(this.routerPath('fetchSessions/:evolutionBotId'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
|
||||||
request: req,
|
|
||||||
schema: instanceSchema,
|
|
||||||
ClassRef: InstanceDto,
|
|
||||||
execute: (instance) => evolutionBotController.fetchSessions(instance, req.params.evolutionBotId),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
})
|
|
||||||
.post(this.routerPath('ignoreJid'), ...guards, async (req, res) => {
|
|
||||||
const response = await this.dataValidate<IgnoreJidDto>({
|
|
||||||
request: req,
|
|
||||||
schema: evolutionBotIgnoreJidSchema,
|
|
||||||
ClassRef: IgnoreJidDto,
|
|
||||||
execute: (instance, data) => evolutionBotController.ignoreJid(instance, data),
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly router: Router = Router();
|
|
||||||
}
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
import { PrismaRepository } from '@api/repository/repository.service';
|
|
||||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
|
||||||
import { Integration } from '@api/types/wa.types';
|
|
||||||
import { ConfigService, HttpServer } from '@config/env.config';
|
|
||||||
import { EvolutionBot, EvolutionBotSetting, IntegrationSession } from '@prisma/client';
|
|
||||||
import { sendTelemetry } from '@utils/sendTelemetry';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { isURL } from 'class-validator';
|
|
||||||
|
|
||||||
import { BaseChatbotService } from '../../base-chatbot.service';
|
|
||||||
import { OpenaiService } from '../../openai/services/openai.service';
|
|
||||||
|
|
||||||
export class EvolutionBotService extends BaseChatbotService<EvolutionBot, EvolutionBotSetting> {
|
|
||||||
private openaiService: OpenaiService;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
waMonitor: WAMonitoringService,
|
|
||||||
prismaRepository: PrismaRepository,
|
|
||||||
configService: ConfigService,
|
|
||||||
openaiService: OpenaiService,
|
|
||||||
) {
|
|
||||||
super(waMonitor, prismaRepository, 'EvolutionBotService', configService);
|
|
||||||
this.openaiService = openaiService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the bot type identifier
|
|
||||||
*/
|
|
||||||
protected getBotType(): string {
|
|
||||||
return 'evolution';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to the Evolution Bot API
|
|
||||||
*/
|
|
||||||
protected async sendMessageToBot(
|
|
||||||
instance: any,
|
|
||||||
session: IntegrationSession,
|
|
||||||
settings: EvolutionBotSetting,
|
|
||||||
bot: EvolutionBot,
|
|
||||||
remoteJid: string,
|
|
||||||
pushName: string,
|
|
||||||
content: string,
|
|
||||||
msg?: any,
|
|
||||||
): Promise<void> {
|
|
||||||
try {
|
|
||||||
const payload: any = {
|
|
||||||
inputs: {
|
|
||||||
sessionId: session.id,
|
|
||||||
remoteJid: remoteJid,
|
|
||||||
pushName: pushName,
|
|
||||||
fromMe: msg?.key?.fromMe,
|
|
||||||
instanceName: instance.instanceName,
|
|
||||||
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
|
|
||||||
apiKey: instance.token,
|
|
||||||
},
|
|
||||||
query: content,
|
|
||||||
conversation_id: session.sessionId === remoteJid ? undefined : session.sessionId,
|
|
||||||
user: remoteJid,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.isAudioMessage(content) && msg) {
|
|
||||||
try {
|
|
||||||
this.logger.debug(`[EvolutionBot] Downloading audio for Whisper transcription`);
|
|
||||||
const transcription = await this.openaiService.speechToText(msg, instance);
|
|
||||||
if (transcription) {
|
|
||||||
payload.query = `[audio] ${transcription}`;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.logger.error(`[EvolutionBot] Failed to transcribe audio: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isImageMessage(content) && msg) {
|
|
||||||
const media = content.split('|');
|
|
||||||
|
|
||||||
if (msg.message.mediaUrl || msg.message.base64) {
|
|
||||||
payload.files = [
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: msg.message.base64 || msg.message.mediaUrl,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
payload.files = [
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: media[1].split('?')[0],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.query = media[2] || content;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
|
||||||
}
|
|
||||||
|
|
||||||
const endpoint = bot.apiUrl;
|
|
||||||
|
|
||||||
if (!endpoint) {
|
|
||||||
this.logger.error('No Evolution Bot endpoint defined');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let headers: any = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bot.apiKey) {
|
|
||||||
headers = {
|
|
||||||
...headers,
|
|
||||||
Authorization: `Bearer ${bot.apiKey}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize payload for logging (remove sensitive data)
|
|
||||||
const sanitizedPayload = {
|
|
||||||
...payload,
|
|
||||||
inputs: {
|
|
||||||
...payload.inputs,
|
|
||||||
apiKey: payload.inputs.apiKey ? '[REDACTED]' : undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await axios.post(endpoint, payload, {
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
|
||||||
}
|
|
||||||
|
|
||||||
let message = response?.data?.message;
|
|
||||||
const rawLinkPreview = response?.data?.linkPreview;
|
|
||||||
|
|
||||||
// Validate linkPreview is boolean and default to true for backward compatibility
|
|
||||||
const linkPreview = typeof rawLinkPreview === 'boolean' ? rawLinkPreview : true;
|
|
||||||
|
|
||||||
if (message && typeof message === 'string' && message.startsWith("'") && message.endsWith("'")) {
|
|
||||||
const innerContent = message.slice(1, -1);
|
|
||||||
if (!innerContent.includes("'")) {
|
|
||||||
message = innerContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message) {
|
|
||||||
// Use the base class method that handles splitMessages functionality
|
|
||||||
await this.sendMessageWhatsApp(instance, remoteJid, message, settings, linkPreview);
|
|
||||||
} else {
|
|
||||||
this.logger.warn(`[EvolutionBot] No message content received from bot response`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send telemetry
|
|
||||||
sendTelemetry('/message/sendText');
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error in sendMessageToBot: ${error.message || JSON.stringify(error)}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
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 evolutionBotSchema: 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' } },
|
|
||||||
splitMessages: { type: 'boolean' },
|
|
||||||
timePerChar: { type: 'integer' },
|
|
||||||
},
|
|
||||||
required: ['enabled', 'apiUrl', 'triggerType'],
|
|
||||||
...isNotEmpty('enabled', 'apiUrl', 'triggerType'),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const evolutionBotStatusSchema: 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 evolutionBotSettingSchema: 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' },
|
|
||||||
splitMessages: { type: 'boolean' },
|
|
||||||
timePerChar: { type: 'integer' },
|
|
||||||
},
|
|
||||||
required: [
|
|
||||||
'expire',
|
|
||||||
'keywordFinish',
|
|
||||||
'delayMessage',
|
|
||||||
'unknownMessage',
|
|
||||||
'listeningFromMe',
|
|
||||||
'stopBotFromMe',
|
|
||||||
'keepOpen',
|
|
||||||
'debounceTime',
|
|
||||||
'ignoreJids',
|
|
||||||
'splitMessages',
|
|
||||||
'timePerChar',
|
|
||||||
],
|
|
||||||
...isNotEmpty(
|
|
||||||
'expire',
|
|
||||||
'keywordFinish',
|
|
||||||
'delayMessage',
|
|
||||||
'unknownMessage',
|
|
||||||
'listeningFromMe',
|
|
||||||
'stopBotFromMe',
|
|
||||||
'keepOpen',
|
|
||||||
'debounceTime',
|
|
||||||
'ignoreJids',
|
|
||||||
'splitMessages',
|
|
||||||
'timePerChar',
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const evolutionBotIgnoreJidSchema: JSONSchema7 = {
|
|
||||||
$id: v4(),
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
remoteJid: { type: 'string' },
|
|
||||||
action: { type: 'string', enum: ['add', 'remove'] },
|
|
||||||
},
|
|
||||||
required: ['remoteJid', 'action'],
|
|
||||||
...isNotEmpty('remoteJid', 'action'),
|
|
||||||
};
|
|
||||||
@ -23,8 +23,6 @@ import { DifyController } from './integrations/chatbot/dify/controllers/dify.con
|
|||||||
import { DifyService } from './integrations/chatbot/dify/services/dify.service';
|
import { DifyService } from './integrations/chatbot/dify/services/dify.service';
|
||||||
import { EvoaiController } from './integrations/chatbot/evoai/controllers/evoai.controller';
|
import { EvoaiController } from './integrations/chatbot/evoai/controllers/evoai.controller';
|
||||||
import { EvoaiService } from './integrations/chatbot/evoai/services/evoai.service';
|
import { EvoaiService } from './integrations/chatbot/evoai/services/evoai.service';
|
||||||
import { EvolutionBotController } from './integrations/chatbot/evolutionBot/controllers/evolutionBot.controller';
|
|
||||||
import { EvolutionBotService } from './integrations/chatbot/evolutionBot/services/evolutionBot.service';
|
|
||||||
import { FlowiseController } from './integrations/chatbot/flowise/controllers/flowise.controller';
|
import { FlowiseController } from './integrations/chatbot/flowise/controllers/flowise.controller';
|
||||||
import { FlowiseService } from './integrations/chatbot/flowise/services/flowise.service';
|
import { FlowiseService } from './integrations/chatbot/flowise/services/flowise.service';
|
||||||
import { N8nController } from './integrations/chatbot/n8n/controllers/n8n.controller';
|
import { N8nController } from './integrations/chatbot/n8n/controllers/n8n.controller';
|
||||||
@ -124,9 +122,6 @@ export const typebotController = new TypebotController(typebotService, prismaRep
|
|||||||
const difyService = new DifyService(waMonitor, prismaRepository, configService, openaiService);
|
const difyService = new DifyService(waMonitor, prismaRepository, configService, openaiService);
|
||||||
export const difyController = new DifyController(difyService, prismaRepository, waMonitor);
|
export const difyController = new DifyController(difyService, prismaRepository, waMonitor);
|
||||||
|
|
||||||
const evolutionBotService = new EvolutionBotService(waMonitor, prismaRepository, configService, openaiService);
|
|
||||||
export const evolutionBotController = new EvolutionBotController(evolutionBotService, prismaRepository, waMonitor);
|
|
||||||
|
|
||||||
const flowiseService = new FlowiseService(waMonitor, prismaRepository, configService, openaiService);
|
const flowiseService = new FlowiseService(waMonitor, prismaRepository, configService, openaiService);
|
||||||
export const flowiseController = new FlowiseController(flowiseService, prismaRepository, waMonitor);
|
export const flowiseController = new FlowiseController(flowiseService, prismaRepository, waMonitor);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user