mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-13 07:04:50 -06:00

This commit refines the Dify integration by updating the controller and service logic for better functionality and maintainability. Key changes include: - Added Dify-specific validation in the createBot method to prevent duplicate entries. - Simplified comments for clarity and removed unused methods in DifyController. - Enhanced message processing in DifyService to handle audio messages more effectively and improve error handling. - Updated DifyDto and DifySettingDto to streamline properties and improve clarity. These updates contribute to a more robust and maintainable Dify integration.
269 lines
8.4 KiB
TypeScript
269 lines
8.4 KiB
TypeScript
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 { Dify, DifySetting, IntegrationSession } from '@prisma/client';
|
|
import axios from 'axios';
|
|
|
|
import { BaseChatbotService } from '../../base-chatbot.service';
|
|
import { OpenaiService } from '../../openai/services/openai.service';
|
|
|
|
export class DifyService extends BaseChatbotService<Dify, DifySetting> {
|
|
private openaiService: OpenaiService;
|
|
|
|
constructor(
|
|
waMonitor: WAMonitoringService,
|
|
prismaRepository: PrismaRepository,
|
|
configService: ConfigService,
|
|
openaiService: OpenaiService,
|
|
) {
|
|
super(waMonitor, prismaRepository, 'DifyService', configService);
|
|
this.openaiService = openaiService;
|
|
}
|
|
|
|
/**
|
|
* Return the bot type for Dify
|
|
*/
|
|
protected getBotType(): string {
|
|
return 'dify';
|
|
}
|
|
|
|
protected async sendMessageToBot(
|
|
instance: any,
|
|
session: IntegrationSession,
|
|
settings: DifySetting,
|
|
dify: Dify,
|
|
remoteJid: string,
|
|
pushName: string,
|
|
content: string,
|
|
msg?: any,
|
|
): Promise<void> {
|
|
try {
|
|
let endpoint: string = dify.apiUrl;
|
|
|
|
if (!endpoint) {
|
|
this.logger.error('No Dify endpoint defined');
|
|
return;
|
|
}
|
|
|
|
// Handle audio messages - transcribe using OpenAI Whisper
|
|
let processedContent = content;
|
|
if (this.isAudioMessage(content) && msg) {
|
|
try {
|
|
this.logger.debug(`[Dify] Downloading audio for Whisper transcription`);
|
|
const transcription = await this.openaiService.speechToText(msg, instance);
|
|
if (transcription) {
|
|
processedContent = `[audio] ${transcription}`;
|
|
}
|
|
} catch (err) {
|
|
this.logger.error(`[Dify] Failed to transcribe audio: ${err}`);
|
|
}
|
|
}
|
|
|
|
if (dify.botType === 'chatBot') {
|
|
endpoint += '/chat-messages';
|
|
const payload: any = {
|
|
inputs: {
|
|
remoteJid: remoteJid,
|
|
pushName: pushName,
|
|
instanceName: instance.instanceName,
|
|
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
|
|
apiKey: instance.token,
|
|
},
|
|
query: processedContent,
|
|
response_mode: 'blocking',
|
|
conversation_id: session.sessionId === remoteJid ? undefined : session.sessionId,
|
|
user: remoteJid,
|
|
};
|
|
|
|
// Handle image messages
|
|
if (this.isImageMessage(content)) {
|
|
const contentSplit = content.split('|');
|
|
payload.files = [
|
|
{
|
|
type: 'image',
|
|
transfer_method: 'remote_url',
|
|
url: contentSplit[1].split('?')[0],
|
|
},
|
|
];
|
|
payload.query = contentSplit[2] || content;
|
|
}
|
|
|
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
|
await instance.client.presenceSubscribe(remoteJid);
|
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
|
}
|
|
|
|
const response = await axios.post(endpoint, payload, {
|
|
headers: {
|
|
Authorization: `Bearer ${dify.apiKey}`,
|
|
},
|
|
});
|
|
|
|
if (instance.integration === Integration.WHATSAPP_BAILEYS)
|
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
|
|
|
const message = response?.data?.answer;
|
|
const conversationId = response?.data?.conversation_id;
|
|
|
|
if (message) {
|
|
await this.sendMessageWhatsApp(instance, remoteJid, message, settings);
|
|
}
|
|
|
|
await this.prismaRepository.integrationSession.update({
|
|
where: {
|
|
id: session.id,
|
|
},
|
|
data: {
|
|
status: 'opened',
|
|
awaitUser: true,
|
|
sessionId: session.sessionId === remoteJid ? conversationId : session.sessionId,
|
|
},
|
|
});
|
|
}
|
|
|
|
if (dify.botType === 'textGenerator') {
|
|
endpoint += '/completion-messages';
|
|
const payload: any = {
|
|
inputs: {
|
|
query: processedContent,
|
|
pushName: pushName,
|
|
remoteJid: remoteJid,
|
|
instanceName: instance.instanceName,
|
|
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
|
|
apiKey: instance.token,
|
|
},
|
|
response_mode: 'blocking',
|
|
conversation_id: session.sessionId === remoteJid ? undefined : session.sessionId,
|
|
user: remoteJid,
|
|
};
|
|
|
|
// Handle image messages
|
|
if (this.isImageMessage(content)) {
|
|
const contentSplit = content.split('|');
|
|
payload.files = [
|
|
{
|
|
type: 'image',
|
|
transfer_method: 'remote_url',
|
|
url: contentSplit[1].split('?')[0],
|
|
},
|
|
];
|
|
payload.inputs.query = contentSplit[2] || content;
|
|
}
|
|
|
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
|
await instance.client.presenceSubscribe(remoteJid);
|
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
|
}
|
|
|
|
const response = await axios.post(endpoint, payload, {
|
|
headers: {
|
|
Authorization: `Bearer ${dify.apiKey}`,
|
|
},
|
|
});
|
|
|
|
if (instance.integration === Integration.WHATSAPP_BAILEYS)
|
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
|
|
|
const message = response?.data?.answer;
|
|
const conversationId = response?.data?.conversation_id;
|
|
|
|
if (message) {
|
|
await this.sendMessageWhatsApp(instance, remoteJid, message, settings);
|
|
}
|
|
|
|
await this.prismaRepository.integrationSession.update({
|
|
where: {
|
|
id: session.id,
|
|
},
|
|
data: {
|
|
status: 'opened',
|
|
awaitUser: true,
|
|
sessionId: session.sessionId === remoteJid ? conversationId : session.sessionId,
|
|
},
|
|
});
|
|
}
|
|
|
|
if (dify.botType === 'agent') {
|
|
endpoint += '/chat-messages';
|
|
const payload: any = {
|
|
inputs: {
|
|
remoteJid: remoteJid,
|
|
pushName: pushName,
|
|
instanceName: instance.instanceName,
|
|
serverUrl: this.configService.get<HttpServer>('SERVER').URL,
|
|
apiKey: instance.token,
|
|
},
|
|
query: processedContent,
|
|
response_mode: 'streaming',
|
|
conversation_id: session.sessionId === remoteJid ? undefined : session.sessionId,
|
|
user: remoteJid,
|
|
};
|
|
|
|
// Handle image messages
|
|
if (this.isImageMessage(content)) {
|
|
const contentSplit = content.split('|');
|
|
payload.files = [
|
|
{
|
|
type: 'image',
|
|
transfer_method: 'remote_url',
|
|
url: contentSplit[1].split('?')[0],
|
|
},
|
|
];
|
|
payload.query = contentSplit[2] || content;
|
|
}
|
|
|
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
|
await instance.client.presenceSubscribe(remoteJid);
|
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
|
}
|
|
|
|
const response = await axios.post(endpoint, payload, {
|
|
headers: {
|
|
Authorization: `Bearer ${dify.apiKey}`,
|
|
},
|
|
});
|
|
|
|
let conversationId;
|
|
let answer = '';
|
|
|
|
const data = response.data.replaceAll('data: ', '');
|
|
const events = data.split('\n').filter((line) => line.trim() !== '');
|
|
|
|
for (const eventString of events) {
|
|
if (eventString.trim().startsWith('{')) {
|
|
const event = JSON.parse(eventString);
|
|
|
|
if (event?.event === 'agent_message') {
|
|
console.log('event:', event);
|
|
conversationId = conversationId ?? event?.conversation_id;
|
|
answer += event?.answer;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (instance.integration === Integration.WHATSAPP_BAILEYS)
|
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
|
|
|
if (answer) {
|
|
await this.sendMessageWhatsApp(instance, remoteJid, answer, settings);
|
|
}
|
|
|
|
await this.prismaRepository.integrationSession.update({
|
|
where: {
|
|
id: session.id,
|
|
},
|
|
data: {
|
|
status: 'opened',
|
|
awaitUser: true,
|
|
sessionId: session.sessionId === remoteJid ? conversationId : session.sessionId,
|
|
},
|
|
});
|
|
}
|
|
} catch (error) {
|
|
this.logger.error(error.response?.data || error);
|
|
return;
|
|
}
|
|
}
|
|
}
|