Skip to content

Commit 95bd85b

Browse files
committed
refactor: update Flowise integration for improved configuration and validation
This commit refines the Flowise integration by enhancing configuration management and validation logic. Key changes include: - Reordered parameters in the FlowiseService constructor for consistency. - Updated FlowiseController to utilize the configService for integration enablement checks. - Simplified FlowiseDto and FlowiseSettingDto by removing unused properties. - Enhanced validation logic in flowise.schema.ts to include new fields. - Improved error handling in the createBot method to prevent duplicate entries. These updates contribute to a more robust and maintainable Flowise integration.
1 parent 64fc7a0 commit 95bd85b

File tree

6 files changed

+141
-131
lines changed

6 files changed

+141
-131
lines changed

src/api/integrations/chatbot/flowise/controllers/flowise.controller.ts

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
import { InstanceDto } from '@api/dto/instance.dto';
12
import { PrismaRepository } from '@api/repository/repository.service';
23
import { WAMonitoringService } from '@api/services/monitor.service';
4+
import { configService, Flowise } from '@config/env.config';
35
import { Logger } from '@config/logger.config';
4-
import { Flowise, IntegrationSession } from '@prisma/client';
6+
import { BadRequestException } from '@exceptions';
7+
import { Flowise as FlowiseModel, IntegrationSession } from '@prisma/client';
58

69
import { BaseChatbotController } from '../../base-chatbot.controller';
710
import { FlowiseDto } from '../dto/flowise.dto';
811
import { FlowiseService } from '../services/flowise.service';
912

10-
export class FlowiseController extends BaseChatbotController<Flowise, FlowiseDto> {
13+
export class FlowiseController extends BaseChatbotController<FlowiseModel, FlowiseDto> {
1114
constructor(
1215
private readonly flowiseService: FlowiseService,
1316
prismaRepository: PrismaRepository,
@@ -23,14 +26,12 @@ export class FlowiseController extends BaseChatbotController<Flowise, FlowiseDto
2326
public readonly logger = new Logger('FlowiseController');
2427
protected readonly integrationName = 'Flowise';
2528

26-
integrationEnabled = true; // Set to true by default or use config value if available
29+
integrationEnabled = configService.get<Flowise>('FLOWISE').ENABLED;
2730
botRepository: any;
2831
settingsRepository: any;
2932
sessionRepository: any;
3033
userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {};
3134

32-
// Implementation of abstract methods required by BaseChatbotController
33-
3435
protected getFallbackBotId(settings: any): string | undefined {
3536
return settings?.flowiseIdFallback;
3637
}
@@ -50,21 +51,17 @@ export class FlowiseController extends BaseChatbotController<Flowise, FlowiseDto
5051
};
5152
}
5253

53-
// Implementation for bot-specific updates
5454
protected getAdditionalUpdateFields(data: FlowiseDto): Record<string, any> {
5555
return {
5656
apiUrl: data.apiUrl,
5757
apiKey: data.apiKey,
5858
};
5959
}
6060

61-
// Implementation for bot-specific duplicate validation on update
6261
protected async validateNoDuplicatesOnUpdate(botId: string, instanceId: string, data: FlowiseDto): Promise<void> {
6362
const checkDuplicate = await this.botRepository.findFirst({
6463
where: {
65-
id: {
66-
not: botId,
67-
},
64+
id: { not: botId },
6865
instanceId: instanceId,
6966
apiUrl: data.apiUrl,
7067
apiKey: data.apiKey,
@@ -76,16 +73,47 @@ export class FlowiseController extends BaseChatbotController<Flowise, FlowiseDto
7673
}
7774
}
7875

79-
// Process bot-specific logic
76+
// Process Flowise-specific bot logic
8077
protected async processBot(
8178
instance: any,
8279
remoteJid: string,
83-
bot: Flowise,
80+
bot: FlowiseModel,
8481
session: IntegrationSession,
8582
settings: any,
8683
content: string,
8784
pushName?: string,
85+
msg?: any,
8886
) {
89-
await this.flowiseService.process(instance, remoteJid, bot, session, settings, content, pushName);
87+
await this.flowiseService.processBot(instance, remoteJid, bot, session, settings, content, pushName, msg);
88+
}
89+
90+
// Override createBot to add module availability check and Flowise-specific validation
91+
public async createBot(instance: InstanceDto, data: FlowiseDto) {
92+
if (!this.integrationEnabled)
93+
throw new BadRequestException('Flowise is disabled');
94+
95+
const instanceId = await this.prismaRepository.instance
96+
.findFirst({
97+
where: {
98+
name: instance.instanceName,
99+
},
100+
})
101+
.then((instance) => instance.id);
102+
103+
// Flowise-specific duplicate check
104+
const checkDuplicate = await this.botRepository.findFirst({
105+
where: {
106+
instanceId: instanceId,
107+
apiUrl: data.apiUrl,
108+
apiKey: data.apiKey,
109+
},
110+
});
111+
112+
if (checkDuplicate) {
113+
throw new Error('Flowise already exists');
114+
}
115+
116+
// Let the base class handle the rest
117+
return super.createBot(instance, data);
90118
}
91119
}
Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,10 @@
1-
import { TriggerOperator, TriggerType } from '@prisma/client';
2-
31
import { BaseChatbotDto, BaseChatbotSettingDto } from '../../base-chatbot.dto';
42

53
export class FlowiseDto extends BaseChatbotDto {
64
apiUrl: string;
7-
apiKey: string;
8-
description: string;
9-
keywordFinish?: string | null;
10-
triggerType: TriggerType;
11-
enabled?: boolean;
12-
expire?: number;
13-
delayMessage?: number;
14-
unknownMessage?: string;
15-
listeningFromMe?: boolean;
16-
stopBotFromMe?: boolean;
17-
keepOpen?: boolean;
18-
debounceTime?: number;
19-
triggerOperator?: TriggerOperator;
20-
triggerValue?: string;
21-
ignoreJids?: any;
22-
splitMessages?: boolean;
23-
timePerChar?: number;
5+
apiKey?: string;
246
}
257

268
export class FlowiseSettingDto extends BaseChatbotSettingDto {
27-
expire?: number;
28-
keywordFinish?: string | null;
29-
delayMessage?: number;
30-
unknownMessage?: string;
31-
listeningFromMe?: boolean;
32-
stopBotFromMe?: boolean;
33-
keepOpen?: boolean;
34-
debounceTime?: number;
359
flowiseIdFallback?: string;
36-
ignoreJids?: any;
37-
splitMessages?: boolean;
38-
timePerChar?: number;
3910
}

src/api/integrations/chatbot/flowise/services/flowise.service.ts

Lines changed: 88 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,133 +2,135 @@
22
import { PrismaRepository } from '@api/repository/repository.service';
33
import { WAMonitoringService } from '@api/services/monitor.service';
44
import { Integration } from '@api/types/wa.types';
5-
import { Auth, ConfigService, HttpServer } from '@config/env.config';
6-
import { Flowise, FlowiseSetting, IntegrationSession } from '@prisma/client';
7-
import { sendTelemetry } from '@utils/sendTelemetry';
5+
import { ConfigService, HttpServer } from '@config/env.config';
6+
import { Flowise as FlowiseModel, IntegrationSession } from '@prisma/client';
87
import axios from 'axios';
98

109
import { BaseChatbotService } from '../../base-chatbot.service';
1110
import { OpenaiService } from '../../openai/services/openai.service';
1211

13-
export class FlowiseService extends BaseChatbotService<Flowise, FlowiseSetting> {
12+
export class FlowiseService extends BaseChatbotService<FlowiseModel> {
1413
private openaiService: OpenaiService;
14+
1515
constructor(
1616
waMonitor: WAMonitoringService,
17-
configService: ConfigService,
1817
prismaRepository: PrismaRepository,
18+
configService: ConfigService,
1919
openaiService: OpenaiService,
2020
) {
2121
super(waMonitor, prismaRepository, 'FlowiseService', configService);
2222
this.openaiService = openaiService;
2323
}
2424

25-
/**
26-
* Get the bot type identifier
27-
*/
25+
// Return the bot type for Flowise
2826
protected getBotType(): string {
2927
return 'flowise';
3028
}
3129

32-
/**
33-
* Send a message to the Flowise API
34-
*/
30+
// Process Flowise-specific bot logic
31+
public async processBot(
32+
instance: any,
33+
remoteJid: string,
34+
bot: FlowiseModel,
35+
session: IntegrationSession,
36+
settings: any,
37+
content: string,
38+
pushName?: string,
39+
msg?: any,
40+
) {
41+
await this.process(instance, remoteJid, bot, session, settings, content, pushName, msg);
42+
}
43+
44+
// Implement the abstract method to send message to Flowise API
3545
protected async sendMessageToBot(
3646
instance: any,
3747
session: IntegrationSession,
38-
settings: FlowiseSetting,
39-
bot: Flowise,
48+
settings: any,
49+
bot: FlowiseModel,
4050
remoteJid: string,
4151
pushName: string,
4252
content: string,
4353
msg?: any,
4454
): Promise<void> {
45-
try {
46-
const payload: any = {
47-
question: content,
48-
overrideConfig: {
49-
sessionId: remoteJid,
50-
vars: {
51-
remoteJid: remoteJid,
52-
pushName: pushName,
53-
instanceName: instance.instanceName,
54-
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
55-
apiKey: instance.token,
56-
},
55+
const payload: any = {
56+
question: content,
57+
overrideConfig: {
58+
sessionId: remoteJid,
59+
vars: {
60+
remoteJid: remoteJid,
61+
pushName: pushName,
62+
instanceName: instance.instanceName,
63+
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
64+
apiKey: instance.token,
5765
},
58-
};
59-
60-
if (this.isAudioMessage(content) && msg) {
61-
try {
62-
this.logger.debug(`[EvolutionBot] Downloading audio for Whisper transcription`);
63-
const transcription = await this.openaiService.speechToText(msg);
64-
if (transcription) {
65-
payload.query = transcription;
66-
} else {
67-
payload.query = '[Audio message could not be transcribed]';
68-
}
69-
} catch (err) {
70-
this.logger.error(`[EvolutionBot] Failed to transcribe audio: ${err}`);
71-
payload.query = '[Audio message could not be transcribed]';
66+
},
67+
};
68+
69+
// Handle audio messages
70+
if (this.isAudioMessage(content) && msg) {
71+
try {
72+
this.logger.debug(`[Flowise] Downloading audio for Whisper transcription`);
73+
const transcription = await this.openaiService.speechToText(msg, instance);
74+
if (transcription) {
75+
payload.question = transcription;
7276
}
77+
} catch (err) {
78+
this.logger.error(`[Flowise] Failed to transcribe audio: ${err}`);
7379
}
80+
}
7481

75-
if (this.isImageMessage(content)) {
76-
const contentSplit = content.split('|');
77-
78-
payload.uploads = [
79-
{
80-
data: contentSplit[1].split('?')[0],
81-
type: 'url',
82-
name: 'Flowise.png',
83-
mime: 'image/png',
84-
},
85-
];
86-
payload.question = contentSplit[2] || content;
87-
}
82+
if (this.isImageMessage(content)) {
83+
const contentSplit = content.split('|');
8884

89-
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
90-
await instance.client.presenceSubscribe(remoteJid);
91-
await instance.client.sendPresenceUpdate('composing', remoteJid);
92-
}
85+
payload.uploads = [
86+
{
87+
data: contentSplit[1].split('?')[0],
88+
type: 'url',
89+
name: 'Flowise.png',
90+
mime: 'image/png',
91+
},
92+
];
93+
payload.question = contentSplit[2] || content;
94+
}
9395

94-
let headers: any = {
95-
'Content-Type': 'application/json',
96-
};
96+
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
97+
await instance.client.presenceSubscribe(remoteJid);
98+
await instance.client.sendPresenceUpdate('composing', remoteJid);
99+
}
97100

98-
if (bot.apiKey) {
99-
headers = {
100-
...headers,
101-
Authorization: `Bearer ${bot.apiKey}`,
102-
};
103-
}
101+
let headers: any = {
102+
'Content-Type': 'application/json',
103+
};
104104

105-
const endpoint = bot.apiUrl;
105+
if (bot.apiKey) {
106+
headers = {
107+
...headers,
108+
Authorization: `Bearer ${bot.apiKey}`,
109+
};
110+
}
106111

107-
if (!endpoint) {
108-
this.logger.error('No Flowise endpoint defined');
109-
return;
110-
}
112+
const endpoint = bot.apiUrl;
111113

112-
const response = await axios.post(endpoint, payload, {
113-
headers,
114-
});
114+
if (!endpoint) {
115+
this.logger.error('No Flowise endpoint defined');
116+
return;
117+
}
115118

116-
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
117-
await instance.client.sendPresenceUpdate('paused', remoteJid);
118-
}
119+
const response = await axios.post(endpoint, payload, {
120+
headers,
121+
});
119122

120-
const message = response?.data?.text;
123+
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
124+
await instance.client.sendPresenceUpdate('paused', remoteJid);
125+
}
121126

122-
if (message) {
123-
// Use the base class method to send the message to WhatsApp
124-
await this.sendMessageWhatsApp(instance, remoteJid, message, settings);
125-
}
127+
const message = response?.data?.text;
126128

127-
// Send telemetry
128-
sendTelemetry('/message/sendText');
129-
} catch (error) {
130-
this.logger.error(`Error in sendMessageToBot: ${error.message || JSON.stringify(error)}`);
131-
return;
129+
if (message) {
130+
// Use the base class method to send the message to WhatsApp
131+
await this.sendMessageWhatsApp(instance, remoteJid, message, settings);
132132
}
133133
}
134+
135+
// The service is now complete with just the abstract method implementations
134136
}

src/api/integrations/chatbot/flowise/validate/flowise.schema.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export const flowiseSchema: JSONSchema7 = {
4040
keepOpen: { type: 'boolean' },
4141
debounceTime: { type: 'integer' },
4242
ignoreJids: { type: 'array', items: { type: 'string' } },
43+
splitMessages: { type: 'boolean' },
44+
timePerChar: { type: 'integer' },
4345
},
4446
required: ['enabled', 'apiUrl', 'triggerType'],
4547
...isNotEmpty('enabled', 'apiUrl', 'triggerType'),
@@ -69,7 +71,9 @@ export const flowiseSettingSchema: JSONSchema7 = {
6971
keepOpen: { type: 'boolean' },
7072
debounceTime: { type: 'integer' },
7173
ignoreJids: { type: 'array', items: { type: 'string' } },
72-
botIdFallback: { type: 'string' },
74+
flowiseIdFallback: { type: 'string' },
75+
splitMessages: { type: 'boolean' },
76+
timePerChar: { type: 'integer' },
7377
},
7478
required: [
7579
'expire',

0 commit comments

Comments
 (0)