refactor(chatbot): streamline media message handling across chatbot services

- Removed redundant instance name references in EvolutionStartupService to enhance data consistency.
- Updated media message processing in various chatbot services to utilize base64 and mediaUrl more effectively, ensuring better handling of image messages.
- Improved overall code readability and maintainability by simplifying media handling logic.
This commit is contained in:
Davidson Gomes 2025-09-18 17:46:47 -03:00
parent 5f44da61fb
commit 407d254cf7
7 changed files with 215 additions and 96 deletions

View File

@ -331,7 +331,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
} else if (message?.mediaType === 'video') { } else if (message?.mediaType === 'video') {
@ -346,7 +345,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
} else if (message?.mediaType === 'audio') { } else if (message?.mediaType === 'audio') {
@ -361,7 +359,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
@ -383,7 +380,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
} else if (message.buttonMessage) { } else if (message.buttonMessage) {
@ -400,7 +396,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
} else if (message.listMessage) { } else if (message.listMessage) {
@ -414,7 +409,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
} else { } else {
@ -428,7 +422,6 @@ export class EvolutionStartupService extends ChannelStartupService {
messageTimestamp: Math.round(new Date().getTime() / 1000), messageTimestamp: Math.round(new Date().getTime() / 1000),
webhookUrl, webhookUrl,
source: 'unknown', source: 'unknown',
instanceName: this.instance.name,
instanceId: this.instanceId, instanceId: this.instanceId,
}; };
} }

View File

@ -91,19 +91,19 @@ export class ChatbotController {
pushName, pushName,
isIntegration, isIntegration,
}; };
await evolutionBotController.emit(emitData); evolutionBotController.emit(emitData);
await typebotController.emit(emitData); typebotController.emit(emitData);
await openaiController.emit(emitData); openaiController.emit(emitData);
await difyController.emit(emitData); difyController.emit(emitData);
await n8nController.emit(emitData); n8nController.emit(emitData);
await evoaiController.emit(emitData); evoaiController.emit(emitData);
await flowiseController.emit(emitData); flowiseController.emit(emitData);
} }
public processDebounce( public processDebounce(

View File

@ -4,6 +4,7 @@ import { Integration } from '@api/types/wa.types';
import { ConfigService, HttpServer } from '@config/env.config'; import { ConfigService, HttpServer } from '@config/env.config';
import { Dify, DifySetting, IntegrationSession } from '@prisma/client'; import { Dify, DifySetting, IntegrationSession } from '@prisma/client';
import axios from 'axios'; import axios from 'axios';
import { isURL } from 'class-validator';
import { BaseChatbotService } from '../../base-chatbot.service'; import { BaseChatbotService } from '../../base-chatbot.service';
import { OpenaiService } from '../../openai/services/openai.service'; import { OpenaiService } from '../../openai/services/openai.service';
@ -78,15 +79,35 @@ export class DifyService extends BaseChatbotService<Dify, DifySetting> {
// Handle image messages // Handle image messages
if (this.isImageMessage(content)) { if (this.isImageMessage(content)) {
const contentSplit = content.split('|'); const media = content.split('|');
payload.files = [
{ if (msg.message.mediaUrl || msg.message.base64) {
type: 'image', let mediaBase64 = msg.message.base64 || null;
transfer_method: 'remote_url',
url: contentSplit[1].split('?')[0], if (msg.message.mediaUrl && isURL(msg.message.mediaUrl)) {
}, const result = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' });
]; mediaBase64 = Buffer.from(result.data).toString('base64');
payload.query = contentSplit[2] || content; }
if (mediaBase64) {
payload.files = [
{
type: 'image',
transfer_method: 'remote_url',
url: mediaBase64,
},
];
}
} else {
payload.files = [
{
type: 'image',
transfer_method: 'remote_url',
url: media[1].split('?')[0],
},
];
}
payload.query = media[2] || content;
} }
if (instance.integration === Integration.WHATSAPP_BAILEYS) { if (instance.integration === Integration.WHATSAPP_BAILEYS) {
@ -140,15 +161,35 @@ export class DifyService extends BaseChatbotService<Dify, DifySetting> {
// Handle image messages // Handle image messages
if (this.isImageMessage(content)) { if (this.isImageMessage(content)) {
const contentSplit = content.split('|'); const media = content.split('|');
payload.files = [
{ if (msg.message.mediaUrl || msg.message.base64) {
type: 'image', let mediaBase64 = msg.message.base64 || null;
transfer_method: 'remote_url',
url: contentSplit[1].split('?')[0], if (msg.message.mediaUrl && isURL(msg.message.mediaUrl)) {
}, const result = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' });
]; mediaBase64 = Buffer.from(result.data).toString('base64');
payload.inputs.query = contentSplit[2] || content; }
if (mediaBase64) {
payload.files = [
{
type: 'image',
transfer_method: 'remote_url',
url: mediaBase64,
},
];
}
} else {
payload.files = [
{
type: 'image',
transfer_method: 'remote_url',
url: media[1].split('?')[0],
},
];
payload.inputs.query = media[2] || content;
}
} }
if (instance.integration === Integration.WHATSAPP_BAILEYS) { if (instance.integration === Integration.WHATSAPP_BAILEYS) {
@ -202,15 +243,26 @@ export class DifyService extends BaseChatbotService<Dify, DifySetting> {
// Handle image messages // Handle image messages
if (this.isImageMessage(content)) { if (this.isImageMessage(content)) {
const contentSplit = content.split('|'); const media = content.split('|');
payload.files = [
{ if (msg.message.mediaUrl || msg.message.base64) {
type: 'image', payload.files = [
transfer_method: 'remote_url', {
url: contentSplit[1].split('?')[0], type: 'image',
}, transfer_method: 'remote_url',
]; url: msg.message.mediaUrl || msg.message.base64,
payload.query = contentSplit[2] || content; },
];
} else {
payload.files = [
{
type: 'image',
transfer_method: 'remote_url',
url: media[1].split('?')[0],
},
];
payload.query = media[2] || content;
}
} }
if (instance.integration === Integration.WHATSAPP_BAILEYS) { if (instance.integration === Integration.WHATSAPP_BAILEYS) {

View File

@ -5,6 +5,7 @@ import { ConfigService, HttpServer } from '@config/env.config';
import { Evoai, EvoaiSetting, IntegrationSession } from '@prisma/client'; import { Evoai, EvoaiSetting, IntegrationSession } from '@prisma/client';
import axios from 'axios'; import axios from 'axios';
import { downloadMediaMessage } from 'baileys'; import { downloadMediaMessage } from 'baileys';
import { isURL } from 'class-validator';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { BaseChatbotService } from '../../base-chatbot.service'; import { BaseChatbotService } from '../../base-chatbot.service';
@ -82,23 +83,43 @@ export class EvoaiService extends BaseChatbotService<Evoai, EvoaiSetting> {
// Handle image message if present // Handle image message if present
if (this.isImageMessage(content) && msg) { if (this.isImageMessage(content) && msg) {
const contentSplit = content.split('|'); const media = content.split('|');
parts[0].text = contentSplit[2] || content; parts[0].text = media[2] || content;
try { try {
// Download the image if (msg.message.mediaUrl || msg.message.base64) {
const mediaBuffer = await downloadMediaMessage(msg, 'buffer', {}); let mediaBase64 = msg.message.base64 || null;
const fileContent = Buffer.from(mediaBuffer).toString('base64');
const fileName = contentSplit[2] || `${msg.key?.id || 'image'}.jpg`;
parts.push({ if (msg.message.mediaUrl && isURL(msg.message.mediaUrl)) {
type: 'file', const result = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' });
file: { mediaBase64 = Buffer.from(result.data).toString('base64');
name: fileName, }
mimeType: 'image/jpeg',
bytes: fileContent, if (mediaBase64) {
}, parts.push({
} as any); type: 'file',
file: {
name: msg.key.id + '.jpeg',
mimeType: 'image/jpeg',
bytes: mediaBase64,
},
} as any);
}
} else {
// Download the image
const mediaBuffer = await downloadMediaMessage(msg, 'buffer', {});
const fileContent = Buffer.from(mediaBuffer).toString('base64');
const fileName = media[2] || `${msg.key?.id || 'image'}.jpg`;
parts.push({
type: 'file',
file: {
name: fileName,
mimeType: 'image/jpeg',
bytes: fileContent,
},
} as any);
}
} catch (fileErr) { } catch (fileErr) {
this.logger.error(`[EvoAI] Failed to process image: ${fileErr}`); this.logger.error(`[EvoAI] Failed to process image: ${fileErr}`);
} }

View File

@ -6,6 +6,7 @@ import { ConfigService, HttpServer } from '@config/env.config';
import { EvolutionBot, EvolutionBotSetting, IntegrationSession } from '@prisma/client'; import { EvolutionBot, EvolutionBotSetting, IntegrationSession } from '@prisma/client';
import { sendTelemetry } from '@utils/sendTelemetry'; import { sendTelemetry } from '@utils/sendTelemetry';
import axios from 'axios'; import axios from 'axios';
import { isURL } from 'class-validator';
import { BaseChatbotService } from '../../base-chatbot.service'; import { BaseChatbotService } from '../../base-chatbot.service';
import { OpenaiService } from '../../openai/services/openai.service'; import { OpenaiService } from '../../openai/services/openai.service';
@ -71,16 +72,26 @@ export class EvolutionBotService extends BaseChatbotService<EvolutionBot, Evolut
} }
} }
if (this.isImageMessage(content)) { if (this.isImageMessage(content) && msg) {
const contentSplit = content.split('|'); const media = content.split('|');
payload.files = [ if (msg.message.mediaUrl || msg.message.base64) {
{ payload.files = [
type: 'image', {
url: contentSplit[1].split('?')[0], type: 'image',
}, url: msg.message.base64 || msg.message.mediaUrl,
]; },
payload.query = contentSplit[2] || content; ];
} else {
payload.files = [
{
type: 'image',
url: media[1].split('?')[0],
},
];
}
payload.query = media[2] || content;
} }
if (instance.integration === Integration.WHATSAPP_BAILEYS) { if (instance.integration === Integration.WHATSAPP_BAILEYS) {

View File

@ -5,6 +5,7 @@ import { Integration } from '@api/types/wa.types';
import { ConfigService, HttpServer } from '@config/env.config'; import { ConfigService, HttpServer } from '@config/env.config';
import { Flowise as FlowiseModel, IntegrationSession } from '@prisma/client'; import { Flowise as FlowiseModel, IntegrationSession } from '@prisma/client';
import axios from 'axios'; import axios from 'axios';
import { isURL } from 'class-validator';
import { BaseChatbotService } from '../../base-chatbot.service'; import { BaseChatbotService } from '../../base-chatbot.service';
import { OpenaiService } from '../../openai/services/openai.service'; import { OpenaiService } from '../../openai/services/openai.service';
@ -82,17 +83,28 @@ export class FlowiseService extends BaseChatbotService<FlowiseModel> {
} }
if (this.isImageMessage(content)) { if (this.isImageMessage(content)) {
const contentSplit = content.split('|'); const media = content.split('|');
payload.uploads = [ if (msg.message.mediaUrl || msg.message.base64) {
{ payload.uploads = [
data: contentSplit[1].split('?')[0], {
type: 'url', data: msg.message.base64 || msg.message.mediaUrl,
name: 'Flowise.png', type: 'url',
mime: 'image/png', name: 'Flowise.png',
}, mime: 'image/png',
]; },
payload.question = contentSplit[2] || content; ];
} else {
payload.uploads = [
{
data: media[1].split('?')[0],
type: 'url',
name: 'Flowise.png',
mime: 'image/png',
},
];
payload.question = media[2] || content;
}
} }
if (instance.integration === Integration.WHATSAPP_BAILEYS) { if (instance.integration === Integration.WHATSAPP_BAILEYS) {

View File

@ -6,6 +6,7 @@ import { IntegrationSession, OpenaiBot, OpenaiSetting } from '@prisma/client';
import { sendTelemetry } from '@utils/sendTelemetry'; import { sendTelemetry } from '@utils/sendTelemetry';
import axios from 'axios'; import axios from 'axios';
import { downloadMediaMessage } from 'baileys'; import { downloadMediaMessage } from 'baileys';
import { isURL } from 'class-validator';
import FormData from 'form-data'; import FormData from 'form-data';
import OpenAI from 'openai'; import OpenAI from 'openai';
import P from 'pino'; import P from 'pino';
@ -173,7 +174,7 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
} }
// Process with the appropriate API based on bot type // Process with the appropriate API based on bot type
await this.sendMessageToBot(instance, session, settings, openaiBot, remoteJid, pushName || '', content); await this.sendMessageToBot(instance, session, settings, openaiBot, remoteJid, pushName || '', content, msg);
} catch (error) { } catch (error) {
this.logger.error(`Error in process: ${error.message || JSON.stringify(error)}`); this.logger.error(`Error in process: ${error.message || JSON.stringify(error)}`);
return; return;
@ -191,6 +192,7 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
remoteJid: string, remoteJid: string,
pushName: string, pushName: string,
content: string, content: string,
msg?: any,
): Promise<void> { ): Promise<void> {
this.logger.log(`Sending message to bot for remoteJid: ${remoteJid}, bot type: ${openaiBot.botType}`); this.logger.log(`Sending message to bot for remoteJid: ${remoteJid}, bot type: ${openaiBot.botType}`);
@ -222,10 +224,11 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
pushName, pushName,
false, // Not fromMe false, // Not fromMe
content, content,
msg,
); );
} else { } else {
this.logger.log('Processing with ChatCompletion API'); this.logger.log('Processing with ChatCompletion API');
message = await this.processChatCompletionMessage(instance, openaiBot, remoteJid, content); message = await this.processChatCompletionMessage(instance, openaiBot, remoteJid, content, msg);
} }
this.logger.log(`Got response from OpenAI: ${message?.substring(0, 50)}${message?.length > 50 ? '...' : ''}`); this.logger.log(`Got response from OpenAI: ${message?.substring(0, 50)}${message?.length > 50 ? '...' : ''}`);
@ -268,6 +271,7 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
pushName: string, pushName: string,
fromMe: boolean, fromMe: boolean,
content: string, content: string,
msg?: any,
): Promise<string> { ): Promise<string> {
const messageData: any = { const messageData: any = {
role: fromMe ? 'assistant' : 'user', role: fromMe ? 'assistant' : 'user',
@ -276,18 +280,35 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
// Handle image messages // Handle image messages
if (this.isImageMessage(content)) { if (this.isImageMessage(content)) {
const contentSplit = content.split('|'); const media = content.split('|');
const url = contentSplit[1].split('?')[0];
messageData.content = [ if (msg.message.mediaUrl || msg.message.base64) {
{ type: 'text', text: contentSplit[2] || content }, let mediaBase64 = msg.message.base64 || null;
{
type: 'image_url', if (msg.message.mediaUrl && isURL(msg.message.mediaUrl)) {
image_url: { const result = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' });
url: url, mediaBase64 = Buffer.from(result.data).toString('base64');
}
if (mediaBase64) {
messageData.content = [
{ type: 'text', text: media[2] || content },
{ type: 'image_url', image_url: { url: mediaBase64 } },
];
}
} else {
const url = media[1].split('?')[0];
messageData.content = [
{ type: 'text', text: media[2] || content },
{
type: 'image_url',
image_url: {
url: url,
},
}, },
}, ];
]; }
} }
// Get thread ID from session or create new thread // Get thread ID from session or create new thread
@ -376,6 +397,7 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
openaiBot: OpenaiBot, openaiBot: OpenaiBot,
remoteJid: string, remoteJid: string,
content: string, content: string,
msg?: any,
): Promise<string> { ): Promise<string> {
this.logger.log('Starting processChatCompletionMessage'); this.logger.log('Starting processChatCompletionMessage');
@ -468,18 +490,26 @@ export class OpenaiService extends BaseChatbotService<OpenaiBot, OpenaiSetting>
// Handle image messages // Handle image messages
if (this.isImageMessage(content)) { if (this.isImageMessage(content)) {
this.logger.log('Found image message'); this.logger.log('Found image message');
const contentSplit = content.split('|'); const media = content.split('|');
const url = contentSplit[1].split('?')[0];
messageData.content = [ if (msg.message.mediaUrl || msg.message.base64) {
{ type: 'text', text: contentSplit[2] || content }, messageData.content = [
{ { type: 'text', text: media[2] || content },
type: 'image_url', { type: 'image_url', image_url: { url: msg.message.base64 || msg.message.mediaUrl } },
image_url: { ];
url: url, } else {
const url = media[1].split('?')[0];
messageData.content = [
{ type: 'text', text: media[2] || content },
{
type: 'image_url',
image_url: {
url: url,
},
}, },
}, ];
]; }
} }
// Combine all messages: system messages, pre-defined messages, conversation history, and current message // Combine all messages: system messages, pre-defined messages, conversation history, and current message