mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-26 18:38:39 -06:00
Salvando-historico do usuario
This commit is contained in:
parent
2982958c05
commit
cd673107b8
@ -17,81 +17,200 @@ import {
|
|||||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
import { BadRequestException } from '@exceptions';
|
import { BadRequestException } from '@exceptions';
|
||||||
import { isBase64, isURL } from 'class-validator';
|
import { isBase64, isURL } from 'class-validator';
|
||||||
|
import { Logger } from '@config/logger.config';
|
||||||
|
|
||||||
export class SendMessageController {
|
export class SendMessageController {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
|
private readonly logger = new Logger('SendMessageController');
|
||||||
|
|
||||||
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
|
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].templateMessage(data);
|
this.logger.log(`[sendTemplate] [${instanceName}] - Iniciando envio de template...`);
|
||||||
|
this.logger.debug(`[sendTemplate] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].templateMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendTemplate] [${instanceName}] - Envio concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendTemplate] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
|
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].textMessage(data);
|
this.logger.log(`[sendText] [${instanceName}] - Iniciando envio de texto...`);
|
||||||
|
this.logger.debug(`[sendText] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].textMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendText] [${instanceName}] - Envio concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendText] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto, file?: any) {
|
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto, file?: any) {
|
||||||
|
this.logger.log(`[sendMedia] [${instanceName}] - Iniciando envio de mídia...`);
|
||||||
|
this.logger.debug(`[sendMedia] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (isBase64(data?.media) && !data?.fileName && data?.mediatype === 'document') {
|
if (isBase64(data?.media) && !data?.fileName && data?.mediatype === 'document') {
|
||||||
|
this.logger.error(
|
||||||
|
`[sendMedia] [${instanceName}] - Falha: Para base64, é necessário informar o nome do arquivo.`
|
||||||
|
);
|
||||||
throw new BadRequestException('For base64 the file name must be informed.');
|
throw new BadRequestException('For base64 the file name must be informed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file || isURL(data?.media) || isBase64(data?.media)) {
|
if (file || isURL(data?.media) || isBase64(data?.media)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].mediaMessage(data, file);
|
const result = await this.waMonitor.waInstances[instanceName].mediaMessage(data, file);
|
||||||
|
this.logger.log(`[sendMedia] [${instanceName}] - Envio de mídia concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendMedia] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.error(
|
||||||
|
`[sendMedia] [${instanceName}] - Falha: Mídia deve ser uma URL ou base64.`
|
||||||
|
);
|
||||||
throw new BadRequestException('Owned media must be a url or base64');
|
throw new BadRequestException('Owned media must be a url or base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendPtv({ instanceName }: InstanceDto, data: SendPtvDto, file?: any) {
|
public async sendPtv({ instanceName }: InstanceDto, data: SendPtvDto, file?: any) {
|
||||||
|
this.logger.log(`[sendPtv] [${instanceName}] - Iniciando envio de vídeo (PTV)...`);
|
||||||
|
this.logger.debug(`[sendPtv] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (file || isURL(data?.video) || isBase64(data?.video)) {
|
if (file || isURL(data?.video) || isBase64(data?.video)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].ptvMessage(data, file);
|
const result = await this.waMonitor.waInstances[instanceName].ptvMessage(data, file);
|
||||||
|
this.logger.log(`[sendPtv] [${instanceName}] - Envio de vídeo (PTV) concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendPtv] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.error(
|
||||||
|
`[sendPtv] [${instanceName}] - Falha: Vídeo deve ser uma URL ou base64.`
|
||||||
|
);
|
||||||
throw new BadRequestException('Owned media must be a url or base64');
|
throw new BadRequestException('Owned media must be a url or base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto, file?: any) {
|
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto, file?: any) {
|
||||||
|
this.logger.log(`[sendSticker] [${instanceName}] - Iniciando envio de sticker...`);
|
||||||
|
this.logger.debug(`[sendSticker] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (file || isURL(data.sticker) || isBase64(data.sticker)) {
|
if (file || isURL(data.sticker) || isBase64(data.sticker)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].mediaSticker(data, file);
|
const result = await this.waMonitor.waInstances[instanceName].mediaSticker(data, file);
|
||||||
|
this.logger.log(`[sendSticker] [${instanceName}] - Envio de sticker concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendSticker] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.error(
|
||||||
|
`[sendSticker] [${instanceName}] - Falha: Sticker deve ser uma URL ou base64.`
|
||||||
|
);
|
||||||
throw new BadRequestException('Owned media must be a url or base64');
|
throw new BadRequestException('Owned media must be a url or base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto, file?: any) {
|
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto, file?: any) {
|
||||||
|
this.logger.log(`[sendWhatsAppAudio] [${instanceName}] - Iniciando envio de áudio...`);
|
||||||
|
this.logger.debug(`[sendWhatsAppAudio] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (file?.buffer || isURL(data.audio) || isBase64(data.audio)) {
|
if (file?.buffer || isURL(data.audio) || isBase64(data.audio)) {
|
||||||
// Si file existe y tiene buffer, o si es una URL o Base64, continúa
|
const result = await this.waMonitor.waInstances[instanceName].audioWhatsapp(data, file);
|
||||||
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data, file);
|
this.logger.log(`[sendWhatsAppAudio] [${instanceName}] - Envio de áudio concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendWhatsAppAudio] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
} else {
|
} else {
|
||||||
console.error('El archivo no tiene buffer o el audio no es una URL o Base64 válida');
|
this.logger.error(
|
||||||
throw new BadRequestException('Owned media must be a url, base64, or valid file with buffer');
|
`[sendWhatsAppAudio] [${instanceName}] - Falha: O arquivo não possui buffer, ou o áudio não é uma URL/base64 válida.`
|
||||||
|
);
|
||||||
|
throw new BadRequestException(
|
||||||
|
'Owned media must be a url, base64, or valid file with buffer'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonsDto) {
|
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonsDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].buttonMessage(data);
|
this.logger.log(`[sendButtons] [${instanceName}] - Iniciando envio de botões...`);
|
||||||
|
this.logger.debug(`[sendButtons] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].buttonMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendButtons] [${instanceName}] - Envio de botões concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendButtons] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
|
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].locationMessage(data);
|
this.logger.log(`[sendLocation] [${instanceName}] - Iniciando envio de localização...`);
|
||||||
|
this.logger.debug(`[sendLocation] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].locationMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendLocation] [${instanceName}] - Envio de localização concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendLocation] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
|
public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].listMessage(data);
|
this.logger.log(`[sendList] [${instanceName}] - Iniciando envio de lista...`);
|
||||||
|
this.logger.debug(`[sendList] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].listMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendList] [${instanceName}] - Envio de lista concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendList] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
|
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].contactMessage(data);
|
this.logger.log(`[sendContact] [${instanceName}] - Iniciando envio de contato...`);
|
||||||
|
this.logger.debug(`[sendContact] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].contactMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendContact] [${instanceName}] - Envio de contato concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendContact] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
||||||
|
this.logger.log(`[sendReaction] [${instanceName}] - Iniciando envio de reação...`);
|
||||||
|
this.logger.debug(`[sendReaction] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (!data.reaction.match(/[^()\w\sà-ú"-+]+/)) {
|
if (!data.reaction.match(/[^()\w\sà-ú"-+]+/)) {
|
||||||
|
this.logger.error(`[sendReaction] [${instanceName}] - Falha: "reaction" deve ser um emoji.`);
|
||||||
throw new BadRequestException('"reaction" must be an emoji');
|
throw new BadRequestException('"reaction" must be an emoji');
|
||||||
}
|
}
|
||||||
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendReaction] [${instanceName}] - Envio de reação concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendReaction] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
|
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].pollMessage(data);
|
this.logger.log(`[sendPoll] [${instanceName}] - Iniciando envio de enquete (poll)...`);
|
||||||
|
this.logger.debug(`[sendPoll] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].pollMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendPoll] [${instanceName}] - Envio de enquete concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendPoll] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto, file?: any) {
|
public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto, file?: any) {
|
||||||
return await this.waMonitor.waInstances[instanceName].statusMessage(data, file);
|
this.logger.log(`[sendStatus] [${instanceName}] - Iniciando envio de Status...`);
|
||||||
|
this.logger.debug(`[sendStatus] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].statusMessage(data, file);
|
||||||
|
|
||||||
|
this.logger.log(`[sendStatus] [${instanceName}] - Envio de Status concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendStatus] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,6 @@ export class Metadata {
|
|||||||
export class SendTextDto extends Metadata {
|
export class SendTextDto extends Metadata {
|
||||||
text: string; // Conteúdo do texto
|
text: string; // Conteúdo do texto
|
||||||
number: string; // WhatsApp ou 'webwidget:...'
|
number: string; // WhatsApp ou 'webwidget:...'
|
||||||
channel?: string; // ex: 'Channel::WebWidget' ou outro
|
|
||||||
inbox_id?: number; // se quiser mandar explicitamente ID=3
|
|
||||||
delay?: number;
|
delay?: number;
|
||||||
quoted?: any;
|
quoted?: any;
|
||||||
linkPreview?: boolean;
|
linkPreview?: boolean;
|
||||||
|
@ -347,7 +347,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async sendMessageWithTyping(
|
protected async sendMessageWithTyping(
|
||||||
number: string,
|
number: string, // remoteJid
|
||||||
message: any,
|
message: any,
|
||||||
options?: Options,
|
options?: Options,
|
||||||
isIntegration = false,
|
isIntegration = false,
|
||||||
@ -393,18 +393,34 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// debug message
|
// debug message
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] Mensagem a ser enviada de numero: ${number}`);
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`[sendMessageWithTyping] Mensagem a ser enviada: ${JSON.stringify(message)}`,
|
`[sendMessageWithTyping] Mensagem a ser enviada: ${JSON.stringify(message)}`,
|
||||||
);
|
);
|
||||||
let messageRaw: any = {
|
let messageRaw: any = {
|
||||||
key: { fromMe: true, id: messageId, remoteJid: number, channel: message.channel, inbox_id: message.inbox_id },
|
key: {
|
||||||
|
fromMe: true,
|
||||||
|
id: messageId,
|
||||||
|
remoteJid: number,
|
||||||
|
},
|
||||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||||
webhookUrl,
|
webhookUrl,
|
||||||
source: 'unknown',
|
source: 'unknown',
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
status: status[1],
|
status: status[1],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Salvando chatwootConversationId para mensagens webwidget
|
||||||
|
if (number && number.startsWith('webwidget:')) {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Detectado número webwidget...');
|
||||||
|
const conversationIdStr = number.split(':')[1] || '0';
|
||||||
|
const conversation_id = parseInt(conversationIdStr, 10);
|
||||||
|
messageRaw.source = 'web';
|
||||||
|
messageRaw.chatwootConversationId = conversation_id;
|
||||||
|
}
|
||||||
|
|
||||||
// debug messageRaw
|
// debug messageRaw
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] messageRaw a ser enviada: ${number}`);
|
||||||
this.logger.debug(`[sendMessageWithTyping] messageRaw a ser enviada: ${JSON.stringify(messageRaw)}`);
|
this.logger.debug(`[sendMessageWithTyping] messageRaw a ser enviada: ${JSON.stringify(messageRaw)}`);
|
||||||
|
|
||||||
// Verifica o tipo de mídia para compor a mensagem
|
// Verifica o tipo de mídia para compor a mensagem
|
||||||
@ -498,7 +514,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('[sendMessageWithTyping] Salvando mensagem no Prisma...');
|
this.logger.debug(`[sendMessageWithTyping] Salvando mensagem no Prisma: ${JSON.stringify(messageRaw)}`);
|
||||||
await this.prismaRepository.message.create({
|
await this.prismaRepository.message.create({
|
||||||
data: messageRaw,
|
data: messageRaw,
|
||||||
});
|
});
|
||||||
@ -520,8 +536,6 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
data2.number,
|
data2.number,
|
||||||
{
|
{
|
||||||
conversation: data2.text,
|
conversation: data2.text,
|
||||||
channel: data2.channel, // passa channel aqui
|
|
||||||
inbox_id: data2.inbox_id, // e inbox_id aqui
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
delay: data2?.delay,
|
delay: data2?.delay,
|
||||||
|
@ -1365,6 +1365,9 @@ public async createConversation(instance: InstanceDto, body: any) {
|
|||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
|
||||||
const client = await this.clientCw(instance);
|
const client = await this.clientCw(instance);
|
||||||
|
const chatId =
|
||||||
|
body.conversation?.meta?.sender?.identifier ||
|
||||||
|
body.conversation?.meta?.sender?.phone_number?.replace('+', '');
|
||||||
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
this.logger.warn('[receiveWebhook] Client não encontrado');
|
this.logger.warn('[receiveWebhook] Client não encontrado');
|
||||||
@ -1382,6 +1385,82 @@ public async createConversation(instance: InstanceDto, body: any) {
|
|||||||
this.cache.delete(keyToDelete);
|
this.cache.delete(keyToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugar intancia
|
||||||
|
this.logger.debug(`[receiveWebhook] Instance recebido: ${JSON.stringify(instance)}`);
|
||||||
|
|
||||||
|
// Salva a mensagem do usuário no banco de dados
|
||||||
|
if (body.message_type === 'incoming' && body.event === 'message_created') {
|
||||||
|
this.logger.debug('[receiveWebhook] Salvando Mensagem do usuário');
|
||||||
|
|
||||||
|
// Id da conversa no Chatwoot
|
||||||
|
const conversationId = body.conversation?.id;
|
||||||
|
// Conteúdo textual da mensagem enviada pelo usuário (ex.: "fsddsfsdf")
|
||||||
|
const content = body.content;
|
||||||
|
|
||||||
|
if (!conversationId || !content) {
|
||||||
|
this.logger.error('[receiveWebhook] Dados insuficientes para salvar a mensagem.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Monta os dados para inserir no Prisma
|
||||||
|
const dataToSave = {
|
||||||
|
key: {
|
||||||
|
// Você pode gerar um ID único como quiser, por exemplo:
|
||||||
|
id: String(body.id) || 'algum-id-unico',
|
||||||
|
remoteJid: `webwidget:${conversationId}`,
|
||||||
|
fromMe: false,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
// A parte textual da conversa
|
||||||
|
conversation: content,
|
||||||
|
},
|
||||||
|
messageType: 'conversation',
|
||||||
|
source: 'unknown', // se for enum, ajuste conforme seu modelo
|
||||||
|
messageTimestamp: Math.floor(Date.now() / 1000),
|
||||||
|
|
||||||
|
// Informações específicas do Chatwoot que podem ser úteis
|
||||||
|
chatwootMessageId: body.id, // ID da mensagem no Chatwoot
|
||||||
|
chatwootConversationId: conversationId, // ID da conversa no Chatwoot
|
||||||
|
chatwootInboxId: body.inbox?.id ?? null, // ID da inbox que recebeu a mensagem
|
||||||
|
chatwootContactInboxSourceId: body.conversation?.contact_inbox?.source_id ?? null,
|
||||||
|
chatwootIsRead: false, // ou true, dependendo da sua lógica
|
||||||
|
|
||||||
|
//instace
|
||||||
|
Instance: {
|
||||||
|
connect: { name: instance.instanceName }
|
||||||
|
},
|
||||||
|
|
||||||
|
// Relacionamento com sua instância
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
|
||||||
|
// Caso queira salvar o nome do usuário (pushName)
|
||||||
|
pushName: body.sender?.name ?? null,
|
||||||
|
|
||||||
|
// Se quiser salvar o “participant”, pode usar “identifier” ou “phone_number”
|
||||||
|
participant:
|
||||||
|
body.sender?.identifier ??
|
||||||
|
body.sender?.phone_number ??
|
||||||
|
null,
|
||||||
|
|
||||||
|
// Se você tiver algum status a aplicar
|
||||||
|
status: 'pending', // ou qualquer outro valor que faça sentido
|
||||||
|
};
|
||||||
|
|
||||||
|
this.logger.debug(`[receiveWebhook] Dados para salvar: ${JSON.stringify(dataToSave)}`);
|
||||||
|
|
||||||
|
const savedMsg = await this.prismaRepository.message.create({
|
||||||
|
data: dataToSave,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.debug(`[receiveWebhook] Mensagem do usuário salva: ${JSON.stringify(savedMsg)}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`[receiveWebhook] Erro ao salvar a mensagem: ${error.message}`);
|
||||||
|
// Lógica adicional de erro, se necessário
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!body?.conversation ||
|
!body?.conversation ||
|
||||||
body.private ||
|
body.private ||
|
||||||
@ -1436,9 +1515,6 @@ public async createConversation(instance: InstanceDto, body: any) {
|
|||||||
return { message: 'webwidget_incoming_ok' };
|
return { message: 'webwidget_incoming_ok' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatId =
|
|
||||||
body.conversation?.meta?.sender?.identifier ||
|
|
||||||
body.conversation?.meta?.sender?.phone_number?.replace('+', '');
|
|
||||||
const messageReceived = body.content
|
const messageReceived = body.content
|
||||||
? body.content
|
? body.content
|
||||||
.replaceAll(/(?<!\*)\*((?!\s)([^\n*]+?)(?<!\s))\*(?!\*)/g, '_$1_')
|
.replaceAll(/(?<!\*)\*((?!\s)([^\n*]+?)(?<!\s))\*(?!\*)/g, '_$1_')
|
||||||
@ -1629,6 +1705,7 @@ public async createConversation(instance: InstanceDto, body: any) {
|
|||||||
|
|
||||||
let messageSent: any;
|
let messageSent: any;
|
||||||
try {
|
try {
|
||||||
|
this.logger.debug('[receiveWebhook] Mensagem Pura - Enviando mensagem de texto para WhatsApp1');
|
||||||
messageSent = await waInstance?.textMessage(data, true);
|
messageSent = await waInstance?.textMessage(data, true);
|
||||||
if (!messageSent) {
|
if (!messageSent) {
|
||||||
throw new Error('Message not sent');
|
throw new Error('Message not sent');
|
||||||
@ -1724,6 +1801,7 @@ public async createConversation(instance: InstanceDto, body: any) {
|
|||||||
|
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
|
|
||||||
|
this.logger.debug('[receiveWebhook] Tempalte Enviando mensagem de texto para WhatsApp');
|
||||||
await waInstance?.textMessage(data2);
|
await waInstance?.textMessage(data2);
|
||||||
const result = await this.chatwootService.receiveWebhook(instance, dataToSend);
|
const result = await this.chatwootService.receiveWebhook(instance, dataToSend);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { WAMonitoringService } from '@api/services/monitor.service';
|
|||||||
import { Integration } from '@api/types/wa.types';
|
import { Integration } from '@api/types/wa.types';
|
||||||
import { ConfigService, Language } from '@config/env.config';
|
import { ConfigService, Language } from '@config/env.config';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
import { IntegrationSession, OpenaiBot, OpenaiCreds, OpenaiSetting } from '@prisma/client';
|
import { IntegrationSession, OpenaiBot, OpenaiCreds, OpenaiSetting, Message } 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';
|
||||||
@ -25,11 +25,12 @@ export class OpenaiService {
|
|||||||
private readonly logger = new Logger('OpenaiService');
|
private readonly logger = new Logger('OpenaiService');
|
||||||
|
|
||||||
private async sendMessageToBot(instance: any, openaiBot: OpenaiBot, remoteJid: string, content: string) {
|
private async sendMessageToBot(instance: any, openaiBot: OpenaiBot, remoteJid: string, content: string) {
|
||||||
this.logger.debug('Enviando mensagem para o bot (sendMessageToBot).');
|
this.logger.debug('[sendMessageToBot] Enviando mensagem para o bot.');
|
||||||
this.logger.debug(`RemoteJid: ${remoteJid}, Content: ${content}`);
|
this.logger.debug(`[sendMessageToBot] RemoteJid: ${remoteJid}, Content: ${content}`);
|
||||||
|
|
||||||
|
// System messages
|
||||||
const systemMessages: any = openaiBot.systemMessages;
|
const systemMessages: any = openaiBot.systemMessages;
|
||||||
this.logger.debug(`SystemMessages recuperadas: ${systemMessages}`);
|
this.logger.debug(`[sendMessageToBot] SystemMessages recuperadas: ${systemMessages}`);
|
||||||
|
|
||||||
const messagesSystem: any[] = systemMessages.map((message) => {
|
const messagesSystem: any[] = systemMessages.map((message) => {
|
||||||
return {
|
return {
|
||||||
@ -38,8 +39,9 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Assistant messages
|
||||||
const assistantMessages: any = openaiBot.assistantMessages;
|
const assistantMessages: any = openaiBot.assistantMessages;
|
||||||
this.logger.debug(`AssistantMessages recuperadas: ${assistantMessages}`);
|
this.logger.debug(`[sendMessageToBot] AssistantMessages recuperadas: ${assistantMessages}`);
|
||||||
|
|
||||||
const messagesAssistant: any[] = assistantMessages.map((message) => {
|
const messagesAssistant: any[] = assistantMessages.map((message) => {
|
||||||
return {
|
return {
|
||||||
@ -48,8 +50,9 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User messages
|
||||||
const userMessages: any = openaiBot.userMessages;
|
const userMessages: any = openaiBot.userMessages;
|
||||||
this.logger.debug(`UserMessages recuperadas: ${userMessages}`);
|
this.logger.debug(`[sendMessageToBot] UserMessages recuperadas: ${userMessages}`);
|
||||||
|
|
||||||
const messagesUser: any[] = userMessages.map((message) => {
|
const messagesUser: any[] = userMessages.map((message) => {
|
||||||
return {
|
return {
|
||||||
@ -58,17 +61,18 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Imagem messages
|
||||||
const messageData: any = {
|
const messageData: any = {
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: [{ type: 'text', text: content }],
|
content: content,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.isImageMessage(content)) {
|
if (this.isImageMessage(content)) {
|
||||||
this.logger.debug('Identificada mensagem de imagem no texto.');
|
this.logger.debug('[sendMessageToBot] Identificada mensagem de imagem no texto.');
|
||||||
const contentSplit = content.split('|');
|
const contentSplit = content.split('|');
|
||||||
|
|
||||||
const url = contentSplit[1].split('?')[0];
|
const url = contentSplit[1].split('?')[0];
|
||||||
this.logger.debug(`URL da imagem extraída: ${url}`);
|
this.logger.debug(`[sendMessageToBot] URL da imagem extraída: ${url}`);
|
||||||
|
|
||||||
messageData.content = [
|
messageData.content = [
|
||||||
{ type: 'text', text: contentSplit[2] || content },
|
{ type: 'text', text: contentSplit[2] || content },
|
||||||
@ -81,17 +85,111 @@ export class OpenaiService {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages: any[] = [...messagesSystem, ...messagesAssistant, ...messagesUser, messageData];
|
// History Mensagens
|
||||||
// o logo precisa formatar messages em joson
|
// Define aqui o limite máximo de caracteres do histórico
|
||||||
this.logger.debug(`Mensagens que serão enviadas para a API da OpenAI: ${JSON.stringify(messages)}`);
|
const MAX_HISTORY_CHARS = 30000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrai o texto principal de um objeto de mensagem.
|
||||||
|
* @param msg Mensagem recebida do banco (tipo `Message`).
|
||||||
|
* @returns Texto extraído da mensagem ou um JSON.stringify como fallback.
|
||||||
|
*/
|
||||||
|
function extrairTextoDaMensagem(msg: Message): string {
|
||||||
|
// Se não houver conteúdo
|
||||||
|
if (!msg?.message) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso seja mensagem de texto simples
|
||||||
|
if (msg.message.conversation) {
|
||||||
|
return msg.message.conversation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso seja extendedTextMessage
|
||||||
|
if (msg.message.extendedTextMessage?.text) {
|
||||||
|
return msg.message.extendedTextMessage.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso seja imagem com caption
|
||||||
|
if (msg.message.imageMessage?.caption) {
|
||||||
|
return msg.message.imageMessage.caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: retorna o objeto como JSON
|
||||||
|
return JSON.stringify(msg.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
let historyArray: any[] = [];
|
||||||
|
|
||||||
|
if (remoteJid && remoteJid.startsWith('webwidget:')) {
|
||||||
|
// Extrai o ID da conversa a partir do remoteJid (ex: 'webwidget:12345')
|
||||||
|
const conversationId = remoteJid.split(':')[1] || '0';
|
||||||
|
this.logger.debug(`[sendMessageToBot] RemoteJid é webwidget. Buscando histórico da conversa: ${conversationId}`);
|
||||||
|
|
||||||
|
// Busca todas as mensagens, sem limite de quantidade
|
||||||
|
let conversationHistory = await this.prismaRepository.message.findMany({
|
||||||
|
where: {
|
||||||
|
chatwootConversationId: parseInt(conversationId),
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
messageTimestamp: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.logger.debug(`[sendMessageToBot] Histórico da conversa recuperado: ${conversationHistory.length} mensagens`);
|
||||||
|
|
||||||
|
if (conversationHistory.length > 0) {
|
||||||
|
// Inverte para ficar das mais antigas às mais recentes
|
||||||
|
conversationHistory = conversationHistory.reverse();
|
||||||
|
|
||||||
|
// Mapeia cada mensagem para uma linha (role + texto)
|
||||||
|
let lines = conversationHistory.map((msg) => {
|
||||||
|
const textoExtraido = extrairTextoDaMensagem(msg);
|
||||||
|
// Se a mensagem for "fromMe", consideramos como 'assistant'; senão, 'user'
|
||||||
|
const roleOpenAI = msg.key?.fromMe ? 'assistant' : 'user';
|
||||||
|
return `${roleOpenAI}: ${textoExtraido}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Monta o histórico inicial com todas as linhas
|
||||||
|
let conversationString = lines.join('\n');
|
||||||
|
|
||||||
|
// Se exceder o limite de caracteres, remover mensagens mais antigas
|
||||||
|
while (conversationString.length > MAX_HISTORY_CHARS && lines.length > 0) {
|
||||||
|
// Remove a primeira linha (mais antiga) do array
|
||||||
|
lines.shift();
|
||||||
|
conversationString = lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
historyArray = [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: `This is the conversation history so far:\n\n${conversationString}`,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// Caso não haja histórico
|
||||||
|
historyArray = [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: 'Não há histórico de conversa ainda.',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug(`[sendMessageToBot] HistoryMessages: ${JSON.stringify(historyArray)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug historyMessages
|
||||||
|
this.logger.debug(`[sendMessageToBot] HistoryMessages: ${JSON.stringify(historyArray)}`);
|
||||||
|
const messages: any[] = [...messagesSystem, ...messagesAssistant, ...messagesUser, ...historyArray, messageData];
|
||||||
|
this.logger.debug(`[sendMessageToBot] Mensagens que serão enviadas para a API da OpenAI: ${JSON.stringify(messages)}`);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença para WHATSAPP_BAILEYS (composing).');
|
this.logger.debug('[sendMessageToBot] Atualizando presença para WHATSAPP_BAILEYS (composing).');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Chamando a API da OpenAI (chat.completions.create).');
|
this.logger.debug('[sendMessageToBot] Chamando a API da OpenAI (chat.completions.create).');
|
||||||
const completions = await this.client.chat.completions.create({
|
const completions = await this.client.chat.completions.create({
|
||||||
model: openaiBot.model,
|
model: openaiBot.model,
|
||||||
messages: messages,
|
messages: messages,
|
||||||
@ -99,12 +197,12 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença para WHATSAPP_BAILEYS (paused).');
|
this.logger.debug('[sendMessageToBot] Atualizando presença para WHATSAPP_BAILEYS (paused).');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = completions.choices[0].message.content;
|
const message = completions.choices[0].message.content;
|
||||||
this.logger.debug(`Resposta obtida da OpenAI (sendMessageToBot): ${message}`);
|
this.logger.debug(`[sendMessageToBot] Resposta obtida da OpenAI: ${message}`);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -118,8 +216,8 @@ export class OpenaiService {
|
|||||||
content: string,
|
content: string,
|
||||||
threadId: string,
|
threadId: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug('Enviando mensagem para o assistente (sendMessageToAssistant).');
|
this.logger.debug('[sendMessageToAssistant] Enviando mensagem para o assistente.');
|
||||||
this.logger.debug(`RemoteJid: ${remoteJid}, ThreadId: ${threadId}, Content: ${content}`);
|
this.logger.debug(`[sendMessageToAssistant] RemoteJid: ${remoteJid}, ThreadId: ${threadId}, Content: ${content}`);
|
||||||
|
|
||||||
const messageData: any = {
|
const messageData: any = {
|
||||||
role: fromMe ? 'assistant' : 'user',
|
role: fromMe ? 'assistant' : 'user',
|
||||||
@ -127,11 +225,11 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (this.isImageMessage(content)) {
|
if (this.isImageMessage(content)) {
|
||||||
this.logger.debug('Identificada mensagem de imagem no texto para Assistant.');
|
this.logger.debug('[sendMessageToAssistant] Identificada mensagem de imagem no texto.');
|
||||||
const contentSplit = content.split('|');
|
const contentSplit = content.split('|');
|
||||||
|
|
||||||
const url = contentSplit[1].split('?')[0];
|
const url = contentSplit[1].split('?')[0];
|
||||||
this.logger.debug(`URL da imagem extraída: ${url}`);
|
this.logger.debug(`[sendMessageToAssistant] URL da imagem extraída: ${url}`);
|
||||||
|
|
||||||
messageData.content = [
|
messageData.content = [
|
||||||
{ type: 'text', text: contentSplit[2] || content },
|
{ type: 'text', text: contentSplit[2] || content },
|
||||||
@ -144,36 +242,36 @@ export class OpenaiService {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Criando mensagem no thread do Assistant.');
|
this.logger.debug('[sendMessageToAssistant] Criando mensagem no thread do Assistant.');
|
||||||
await this.client.beta.threads.messages.create(threadId, messageData);
|
await this.client.beta.threads.messages.create(threadId, messageData);
|
||||||
|
|
||||||
if (fromMe) {
|
if (fromMe) {
|
||||||
this.logger.debug('Mensagem enviada foi do próprio bot (fromMe). Enviando Telemetry.');
|
this.logger.debug('[sendMessageToAssistant] Mensagem enviada foi do próprio bot (fromMe). Enviando Telemetry.');
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Iniciando corrida (run) do Assistant com ID do assistant configurado.');
|
this.logger.debug('[sendMessageToAssistant] Iniciando corrida (run) do Assistant com ID do assistant configurado.');
|
||||||
const runAssistant = await this.client.beta.threads.runs.create(threadId, {
|
const runAssistant = await this.client.beta.threads.runs.create(threadId, {
|
||||||
assistant_id: openaiBot.assistantId,
|
assistant_id: openaiBot.assistantId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença para WHATSAPP_BAILEYS (composing).');
|
this.logger.debug('[sendMessageToAssistant] Atualizando presença para WHATSAPP_BAILEYS (composing).');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Aguardando resposta do Assistant (getAIResponse).');
|
this.logger.debug('[sendMessageToAssistant] Aguardando resposta do Assistant (getAIResponse).');
|
||||||
const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl, remoteJid, pushName);
|
const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl, remoteJid, pushName);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença para WHATSAPP_BAILEYS (paused).');
|
this.logger.debug('[sendMessageToAssistant] Atualizando presença para WHATSAPP_BAILEYS (paused).');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = response?.data[0].content[0].text.value;
|
const message = response?.data[0].content[0].text.value;
|
||||||
this.logger.debug(`Resposta obtida do Assistant (sendMessageToAssistant): ${message}`);
|
this.logger.debug(`[sendMessageToAssistant] Resposta obtida do Assistant: ${message}`);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -185,8 +283,8 @@ export class OpenaiService {
|
|||||||
settings: OpenaiSetting,
|
settings: OpenaiSetting,
|
||||||
message: string,
|
message: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug('Enviando mensagem para o WhatsApp (sendMessageWhatsapp).');
|
this.logger.debug('[sendMessageWhatsapp] Enviando mensagem para o WhatsApp.');
|
||||||
this.logger.debug(`RemoteJid: ${remoteJid}, Mensagem: ${message}`);
|
this.logger.debug(`[sendMessageWhatsapp] RemoteJid: ${remoteJid}, Mensagem: ${message}`);
|
||||||
|
|
||||||
const linkRegex = /(!?)\[(.*?)\]\((.*?)\)/g;
|
const linkRegex = /(!?)\[(.*?)\]\((.*?)\)/g;
|
||||||
let textBuffer = '';
|
let textBuffer = '';
|
||||||
@ -209,10 +307,10 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Processa links (ou mídia) dentro do texto
|
// Processa links (ou mídia) dentro do texto
|
||||||
this.logger.debug('Verificando se a mensagem contém mídia (links) no formato [altText](url).');
|
this.logger.debug('[sendMessageWhatsapp] Verificando se a mensagem contém mídia (links) no formato [altText](url).');
|
||||||
while ((match = linkRegex.exec(message)) !== null) {
|
while ((match = linkRegex.exec(message)) !== null) {
|
||||||
const [fullMatch, exclMark, altText, url] = match;
|
const [fullMatch, exclMark, altText, url] = match;
|
||||||
this.logger.debug(`Match encontrado: ${fullMatch}, url: ${url}, altText: ${altText}`);
|
this.logger.debug(`[sendMessageWhatsapp] Match encontrado: ${fullMatch}, url: ${url}, altText: ${altText}`);
|
||||||
const mediaType = getMediaType(url);
|
const mediaType = getMediaType(url);
|
||||||
|
|
||||||
const beforeText = message.slice(lastIndex, match.index);
|
const beforeText = message.slice(lastIndex, match.index);
|
||||||
@ -236,14 +334,14 @@ export class OpenaiService {
|
|||||||
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença (composing) antes de enviar texto em partes.');
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (composing) antes de enviar texto em partes.');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
this.logger.debug(`Enviando texto (splitMessage): ${message}`);
|
this.logger.debug(`[sendMessageWhatsapp] Enviando texto (splitMessage): ${part}`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -257,12 +355,12 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença (paused) após enviar parte do texto.');
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (paused) após enviar parte do texto.');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`Enviando texto inteiro do buffer: ${textBuffer.trim()}`);
|
this.logger.debug(`[sendMessageWhatsapp] Enviando texto inteiro do buffer: ${textBuffer.trim()}`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -275,9 +373,9 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug(`Identificado arquivo de mídia do tipo: ${mediaType}`);
|
this.logger.debug(`[sendMessageWhatsapp] Identificado arquivo de mídia do tipo: ${mediaType}`);
|
||||||
if (mediaType === 'audio') {
|
if (mediaType === 'audio') {
|
||||||
this.logger.debug('Enviando arquivo de áudio para o WhatsApp.');
|
this.logger.debug('[sendMessageWhatsapp] Enviando arquivo de áudio para o WhatsApp.');
|
||||||
await instance.audioWhatsapp({
|
await instance.audioWhatsapp({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
delay: settings?.delayMessage || 1000,
|
delay: settings?.delayMessage || 1000,
|
||||||
@ -285,7 +383,7 @@ export class OpenaiService {
|
|||||||
caption: altText,
|
caption: altText,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug('Enviando arquivo de mídia (imagem, vídeo ou documento) para o WhatsApp.');
|
this.logger.debug('[sendMessageWhatsapp] Enviando arquivo de mídia (imagem, vídeo ou documento) para o WhatsApp.');
|
||||||
await instance.mediaMessage(
|
await instance.mediaMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -299,7 +397,7 @@ export class OpenaiService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug('Não é um tipo de mídia suportado. Adicionando link no buffer de texto.');
|
this.logger.debug('[sendMessageWhatsapp] Não é um tipo de mídia suportado. Adicionando link no buffer de texto.');
|
||||||
textBuffer += `[${altText}](${url})`;
|
textBuffer += `[${altText}](${url})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,14 +427,14 @@ export class OpenaiService {
|
|||||||
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença (composing) antes de enviar resto do texto em partes.');
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (composing) antes de enviar resto do texto em partes.');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
this.logger.debug(`Enviando texto (splitMessage): ${message}`);
|
this.logger.debug(`[sendMessageWhatsapp] Enviando texto (splitMessage): ${part}`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -350,12 +448,12 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
this.logger.debug('Atualizando presença (paused) após enviar parte final do texto.');
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (paused) após enviar parte final do texto.');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`Enviando todo o texto restante no buffer: ${textBuffer.trim()}`);
|
this.logger.debug(`[sendMessageWhatsapp] Enviando todo o texto restante no buffer: ${textBuffer.trim()}`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -368,10 +466,10 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Enviando telemetria após envio de texto.');
|
this.logger.debug('[sendMessageWhatsapp] Enviando telemetria após envio de texto.');
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
|
|
||||||
this.logger.debug(`Atualizando sessão (id: ${session.id}) para 'opened' e 'awaitUser: true'.`);
|
this.logger.debug(`[sendMessageWhatsapp] Atualizando sessão (id: ${session.id}) para 'opened' e 'awaitUser: true'.`);
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -384,11 +482,11 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createAssistantNewSession(instance: InstanceDto, data: any) {
|
public async createAssistantNewSession(instance: InstanceDto, data: any) {
|
||||||
this.logger.debug('Iniciando criação de nova sessão do Assistant (createAssistantNewSession).');
|
this.logger.debug('[createAssistantNewSession] Iniciando criação de nova sessão do Assistant.');
|
||||||
this.logger.debug(`Dados recebidos: ${JSON.stringify(data)}`);
|
this.logger.debug(`[createAssistantNewSession] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (data.remoteJid === 'status@broadcast') {
|
if (data.remoteJid === 'status@broadcast') {
|
||||||
this.logger.debug('remoteJid é status@broadcast, abortando criação de sessão.');
|
this.logger.debug('[createAssistantNewSession] remoteJid é status@broadcast, abortando criação de sessão.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,23 +497,23 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) {
|
if (!creds) {
|
||||||
this.logger.error('Openai Creds não encontrados, lançando erro.');
|
this.logger.error('[createAssistantNewSession] Openai Creds não encontrados, lançando erro.');
|
||||||
throw new Error('Openai Creds not found');
|
throw new Error('Openai Creds not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.logger.debug('Instanciando cliente OpenAI para Assistant.');
|
this.logger.debug('[createAssistantNewSession] Instanciando cliente OpenAI para Assistant.');
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug('Criando thread (beta.threads.create).');
|
this.logger.debug('[createAssistantNewSession] Criando thread (beta.threads.create).');
|
||||||
const thread = await this.client.beta.threads.create({});
|
const thread = await this.client.beta.threads.create({});
|
||||||
const threadId = thread.id;
|
const threadId = thread.id;
|
||||||
|
|
||||||
let session = null;
|
let session = null;
|
||||||
if (threadId) {
|
if (threadId) {
|
||||||
this.logger.debug('Thread criada com sucesso. Salvando sessão no banco de dados.');
|
this.logger.debug('[createAssistantNewSession] Thread criada com sucesso. Salvando sessão no banco de dados.');
|
||||||
session = await this.prismaRepository.integrationSession.create({
|
session = await this.prismaRepository.integrationSession.create({
|
||||||
data: {
|
data: {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
@ -431,7 +529,7 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
return { session };
|
return { session };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Erro ao criar nova sessão do Assistant: ${error}`);
|
this.logger.error(`[createAssistantNewSession] Erro ao criar nova sessão do Assistant: ${error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -446,8 +544,8 @@ export class OpenaiService {
|
|||||||
session: IntegrationSession,
|
session: IntegrationSession,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug('Iniciando sessão do Assistant (initAssistantNewSession).');
|
this.logger.debug('[initAssistantNewSession] Iniciando sessão do Assistant.');
|
||||||
this.logger.debug(`RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}`);
|
this.logger.debug(`[initAssistantNewSession] RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}`);
|
||||||
|
|
||||||
const data = await this.createAssistantNewSession(instance, {
|
const data = await this.createAssistantNewSession(instance, {
|
||||||
remoteJid,
|
remoteJid,
|
||||||
@ -458,10 +556,10 @@ export class OpenaiService {
|
|||||||
|
|
||||||
if (data.session) {
|
if (data.session) {
|
||||||
session = data.session;
|
session = data.session;
|
||||||
this.logger.debug(`Sessão criada com sucesso. ID: ${session.id}`);
|
this.logger.debug(`[initAssistantNewSession] Sessão criada com sucesso. ID: ${session.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Enviando mensagem para Assistant para iniciar conversa.');
|
this.logger.debug('[initAssistantNewSession] Enviando mensagem para Assistant para iniciar conversa.');
|
||||||
const message = await this.sendMessageToAssistant(
|
const message = await this.sendMessageToAssistant(
|
||||||
instance,
|
instance,
|
||||||
openaiBot,
|
openaiBot,
|
||||||
@ -472,9 +570,9 @@ export class OpenaiService {
|
|||||||
session.sessionId,
|
session.sessionId,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.debug(`Retorno do Assistant: ${message}`);
|
this.logger.debug(`[initAssistantNewSession] Retorno do Assistant: ${message}`);
|
||||||
if (message) {
|
if (message) {
|
||||||
this.logger.debug('Enviando mensagem do Assistant para WhatsApp.');
|
this.logger.debug('[initAssistantNewSession] Enviando mensagem do Assistant para WhatsApp.');
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,12 +595,12 @@ export class OpenaiService {
|
|||||||
remoteJid: string,
|
remoteJid: string,
|
||||||
pushName: string,
|
pushName: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug(`Consultando run do Assistant (getAIResponse). ThreadId: ${threadId}, RunId: ${runId}`);
|
this.logger.debug(`[getAIResponse] Consultando run do Assistant. ThreadId: ${threadId}, RunId: ${runId}`);
|
||||||
const getRun = await this.client.beta.threads.runs.retrieve(threadId, runId);
|
const getRun = await this.client.beta.threads.runs.retrieve(threadId, runId);
|
||||||
let toolCalls;
|
let toolCalls;
|
||||||
switch (getRun.status) {
|
switch (getRun.status) {
|
||||||
case 'requires_action':
|
case 'requires_action':
|
||||||
this.logger.debug('Run requer ação (requires_action). Verificando chamadas de ferramenta (tool_calls).');
|
this.logger.debug('[getAIResponse] Run requer ação. Verificando chamadas de ferramenta (tool_calls).');
|
||||||
toolCalls = getRun?.required_action?.submit_tool_outputs?.tool_calls;
|
toolCalls = getRun?.required_action?.submit_tool_outputs?.tool_calls;
|
||||||
|
|
||||||
if (toolCalls) {
|
if (toolCalls) {
|
||||||
@ -514,7 +612,7 @@ export class OpenaiService {
|
|||||||
: toolCall?.function?.arguments;
|
: toolCall?.function?.arguments;
|
||||||
|
|
||||||
let output = null;
|
let output = null;
|
||||||
this.logger.debug(`Chamando função externa: ${functionName} com argumentos:`, functionArgument);
|
this.logger.debug(`[getAIResponse] Chamando função externa: ${functionName} com argumentos:`, functionArgument);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(functionUrl, {
|
const { data } = await axios.post(functionUrl, {
|
||||||
@ -529,9 +627,9 @@ export class OpenaiService {
|
|||||||
.replace(/\n/g, '\\n')
|
.replace(/\n/g, '\\n')
|
||||||
.replace(/\r/g, '\\r')
|
.replace(/\r/g, '\\r')
|
||||||
.replace(/\t/g, '\\t');
|
.replace(/\t/g, '\\t');
|
||||||
this.logger.debug(`Resposta da função externa (${functionName}):`, data);
|
this.logger.debug(`[getAIResponse] Resposta da função externa (${functionName}):`, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Erro ao chamar função externa (${functionName}):`, error);
|
this.logger.error(`[getAIResponse] Erro ao chamar função externa (${functionName}):`, error);
|
||||||
output = JSON.stringify(error)
|
output = JSON.stringify(error)
|
||||||
.replace(/\\/g, '\\\\')
|
.replace(/\\/g, '\\\\')
|
||||||
.replace(/"/g, '\\"')
|
.replace(/"/g, '\\"')
|
||||||
@ -540,7 +638,7 @@ export class OpenaiService {
|
|||||||
.replace(/\t/g, '\\t');
|
.replace(/\t/g, '\\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Submetendo output para a run do Assistant (submitToolOutputs).');
|
this.logger.debug('[getAIResponse] Submetendo output para a run do Assistant (submitToolOutputs).');
|
||||||
await this.client.beta.threads.runs.submitToolOutputs(threadId, runId, {
|
await this.client.beta.threads.runs.submitToolOutputs(threadId, runId, {
|
||||||
tool_outputs: [
|
tool_outputs: [
|
||||||
{
|
{
|
||||||
@ -552,18 +650,18 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Repetindo chamada getAIResponse até status diferente de requires_action.');
|
this.logger.debug('[getAIResponse] Repetindo chamada getAIResponse até status diferente de requires_action.');
|
||||||
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
||||||
case 'queued':
|
case 'queued':
|
||||||
this.logger.debug('Run está em fila (queued). Aguardando 1 segundo antes de tentar novamente.');
|
this.logger.debug('[getAIResponse] Run está em fila (queued). Aguardando 1 segundo antes de tentar novamente.');
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
||||||
case 'in_progress':
|
case 'in_progress':
|
||||||
this.logger.debug('Run está em progresso (in_progress). Aguardando 1 segundo antes de tentar novamente.');
|
this.logger.debug('[getAIResponse] Run está em progresso (in_progress). Aguardando 1 segundo antes de tentar novamente.');
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
||||||
case 'completed':
|
case 'completed':
|
||||||
this.logger.debug('Run concluída (completed). Recuperando última mensagem.');
|
this.logger.debug('[getAIResponse] Run concluída (completed). Recuperando última mensagem.');
|
||||||
return await this.client.beta.threads.messages.list(threadId, {
|
return await this.client.beta.threads.messages.list(threadId, {
|
||||||
run_id: runId,
|
run_id: runId,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
@ -585,27 +683,27 @@ export class OpenaiService {
|
|||||||
settings: OpenaiSetting,
|
settings: OpenaiSetting,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug('Processando mensagem para o Assistant (processOpenaiAssistant).');
|
this.logger.debug('[processOpenaiAssistant] Processando mensagem para o Assistant.');
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`RemoteJid: ${remoteJid}, pushName: ${pushName}, fromMe: ${fromMe}, content: ${content}`,
|
`[processOpenaiAssistant] RemoteJid: ${remoteJid}, pushName: ${pushName}, fromMe: ${fromMe}, content: ${content}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (session && session.status === 'closed') {
|
if (session && session.status === 'closed') {
|
||||||
this.logger.debug('A sessão está fechada, não será processada.');
|
this.logger.debug('[processOpenaiAssistant] A sessão está fechada, não será processada.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session && settings.expire && settings.expire > 0) {
|
if (session && settings.expire && settings.expire > 0) {
|
||||||
this.logger.debug('Verificando tempo de expiração da sessão...');
|
this.logger.debug('[processOpenaiAssistant] Verificando tempo de expiração da sessão...');
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
||||||
const diff = now - sessionUpdatedAt;
|
const diff = now - sessionUpdatedAt;
|
||||||
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
||||||
|
|
||||||
if (diffInMinutes > settings.expire) {
|
if (diffInMinutes > settings.expire) {
|
||||||
this.logger.debug(`Sessão expirada há ${diffInMinutes} minutos.`);
|
this.logger.debug(`[processOpenaiAssistant] Sessão expirada há ${diffInMinutes} minutos.`);
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
this.logger.debug('Atualizando status da sessão para CLOSED.');
|
this.logger.debug('[processOpenaiAssistant] Atualizando status da sessão para CLOSED.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -615,7 +713,7 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug('Deletando sessão do banco de dados.');
|
this.logger.debug('[processOpenaiAssistant] Deletando sessão do banco de dados.');
|
||||||
await this.prismaRepository.integrationSession.deleteMany({
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
botId: openaiBot.id,
|
botId: openaiBot.id,
|
||||||
@ -624,7 +722,7 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Recriando nova sessão de Assistant...');
|
this.logger.debug('[processOpenaiAssistant] Recriando nova sessão de Assistant...');
|
||||||
await this.initAssistantNewSession(
|
await this.initAssistantNewSession(
|
||||||
instance,
|
instance,
|
||||||
remoteJid,
|
remoteJid,
|
||||||
@ -640,13 +738,13 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
this.logger.debug('Nenhuma sessão ativa encontrada, criando nova sessão de Assistant...');
|
this.logger.debug('[processOpenaiAssistant] Nenhuma sessão ativa encontrada, criando nova sessão de Assistant...');
|
||||||
await this.initAssistantNewSession(instance, remoteJid, pushName, fromMe, openaiBot, settings, session, content);
|
await this.initAssistantNewSession(instance, remoteJid, pushName, fromMe, openaiBot, settings, session, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.status !== 'paused') {
|
if (session.status !== 'paused') {
|
||||||
this.logger.debug('Marcando sessão como aberta e awaitUser = false.');
|
this.logger.debug('[processOpenaiAssistant] Marcando sessão como aberta e awaitUser = false.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -659,9 +757,9 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
this.logger.debug('Não há conteúdo na mensagem. Verificando se existe unknownMessage para retorno.');
|
this.logger.debug('[processOpenaiAssistant] Não há conteúdo na mensagem. Verificando se existe unknownMessage para retorno.');
|
||||||
if (settings.unknownMessage) {
|
if (settings.unknownMessage) {
|
||||||
this.logger.debug(`Enviando unknownMessage para o remoteJid: ${remoteJid}`);
|
this.logger.debug(`[processOpenaiAssistant] Enviando unknownMessage para o remoteJid: ${remoteJid}`);
|
||||||
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -676,7 +774,7 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
||||||
this.logger.debug('Keyword finish detectada. Encerrando sessão.');
|
this.logger.debug('[processOpenaiAssistant] Keyword finish detectada. Encerrando sessão.');
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
@ -697,7 +795,7 @@ export class OpenaiService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Buscando OpenaiCreds no banco...');
|
this.logger.debug('[processOpenaiAssistant] Buscando OpenaiCreds no banco...');
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: openaiBot.openaiCredsId,
|
id: openaiBot.openaiCredsId,
|
||||||
@ -705,17 +803,17 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) {
|
if (!creds) {
|
||||||
this.logger.error('Openai Creds não encontrados, lançando erro.');
|
this.logger.error('[processOpenaiAssistant] Openai Creds não encontrados, lançando erro.');
|
||||||
throw new Error('Openai Creds not found');
|
throw new Error('Openai Creds not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Instanciando cliente OpenAI para processar a mensagem no Assistant.');
|
this.logger.debug('[processOpenaiAssistant] Instanciando cliente OpenAI para processar a mensagem no Assistant.');
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const threadId = session.sessionId;
|
const threadId = session.sessionId;
|
||||||
this.logger.debug(`Enviando mensagem ao Assistant (threadId: ${threadId}).`);
|
this.logger.debug(`[processOpenaiAssistant] Enviando mensagem ao Assistant (threadId: ${threadId}).`);
|
||||||
const message = await this.sendMessageToAssistant(
|
const message = await this.sendMessageToAssistant(
|
||||||
instance,
|
instance,
|
||||||
openaiBot,
|
openaiBot,
|
||||||
@ -727,7 +825,7 @@ export class OpenaiService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
this.logger.debug(`Resposta do Assistant recebida. Enviando para WhatsApp: ${message}`);
|
this.logger.debug(`[processOpenaiAssistant] Resposta do Assistant recebida. Enviando para WhatsApp: ${message}`);
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,16 +833,16 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createChatCompletionNewSession(instance: InstanceDto, data: any) {
|
public async createChatCompletionNewSession(instance: InstanceDto, data: any) {
|
||||||
this.logger.debug('Iniciando criação de nova sessão de chatCompletion (createChatCompletionNewSession).');
|
this.logger.debug('[createChatCompletionNewSession] Iniciando criação de nova sessão de chatCompletion.');
|
||||||
this.logger.debug(`Dados recebidos: ${JSON.stringify(data)}`);
|
this.logger.debug(`[createChatCompletionNewSession] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (data.remoteJid === 'status@broadcast') {
|
if (data.remoteJid === 'status@broadcast') {
|
||||||
this.logger.debug('remoteJid é status@broadcast, abortando criação de sessão.');
|
this.logger.debug('[createChatCompletionNewSession] remoteJid é status@broadcast, abortando criação de sessão.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = Math.floor(Math.random() * 10000000000).toString();
|
const id = Math.floor(Math.random() * 10000000000).toString();
|
||||||
this.logger.debug(`Gerando ID pseudo-aleatório da sessão: ${id}`);
|
this.logger.debug(`[createChatCompletionNewSession] Gerando ID pseudo-aleatório da sessão: ${id}`);
|
||||||
|
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -753,12 +851,12 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) {
|
if (!creds) {
|
||||||
this.logger.error('Openai Creds não encontrados, lançando erro.');
|
this.logger.error('[createChatCompletionNewSession] Openai Creds não encontrados, lançando erro.');
|
||||||
throw new Error('Openai Creds not found');
|
throw new Error('Openai Creds not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.logger.debug('Criando sessão no banco de dados.');
|
this.logger.debug('[createChatCompletionNewSession] Criando sessão no banco de dados.');
|
||||||
const session = await this.prismaRepository.integrationSession.create({
|
const session = await this.prismaRepository.integrationSession.create({
|
||||||
data: {
|
data: {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
@ -774,7 +872,7 @@ export class OpenaiService {
|
|||||||
|
|
||||||
return { session, creds };
|
return { session, creds };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Erro ao criar nova sessão de chatCompletion: ${error}`);
|
this.logger.error(`[createChatCompletionNewSession] Erro ao criar nova sessão de chatCompletion: ${error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -788,8 +886,8 @@ export class OpenaiService {
|
|||||||
session: IntegrationSession,
|
session: IntegrationSession,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
this.logger.debug('Iniciando sessão de chatCompletion (initChatCompletionNewSession).');
|
this.logger.debug('[initChatCompletionNewSession] Iniciando sessão de chatCompletion.');
|
||||||
this.logger.debug(`RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}`);
|
this.logger.debug(`[initChatCompletionNewSession] RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}`);
|
||||||
|
|
||||||
const data = await this.createChatCompletionNewSession(instance, {
|
const data = await this.createChatCompletionNewSession(instance, {
|
||||||
remoteJid,
|
remoteJid,
|
||||||
@ -800,18 +898,18 @@ export class OpenaiService {
|
|||||||
|
|
||||||
session = data.session;
|
session = data.session;
|
||||||
const creds = data.creds;
|
const creds = data.creds;
|
||||||
this.logger.debug(`Sessão criada com sucesso (ID: ${session.id}). Instanciando cliente OpenAI.`);
|
this.logger.debug(`[initChatCompletionNewSession] Sessão criada com sucesso (ID: ${session.id}). Instanciando cliente OpenAI.`);
|
||||||
|
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug('Enviando mensagem para o Bot usando chatCompletion.');
|
this.logger.debug('[initChatCompletionNewSession] Enviando mensagem para o Bot usando chatCompletion.');
|
||||||
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
||||||
|
|
||||||
this.logger.debug(`Resposta do Bot: ${message}`);
|
this.logger.debug(`[initChatCompletionNewSession] Resposta do Bot: ${message}`);
|
||||||
if (message) {
|
if (message) {
|
||||||
this.logger.debug('Enviando resposta para o WhatsApp.');
|
this.logger.debug('[initChatCompletionNewSession] Enviando resposta para o WhatsApp.');
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -833,21 +931,21 @@ export class OpenaiService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (session && session.status !== 'opened') {
|
if (session && session.status !== 'opened') {
|
||||||
this.logger.debug('Sessão existente não está aberta. Não será processado.');
|
this.logger.debug('[processOpenaiChatCompletion] Sessão existente não está aberta. Não será processado.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session && settings.expire && settings.expire > 0) {
|
if (session && settings.expire && settings.expire > 0) {
|
||||||
this.logger.debug('Verificando tempo de expiração da sessão...');
|
this.logger.debug('[processOpenaiChatCompletion] Verificando tempo de expiração da sessão...');
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
||||||
const diff = now - sessionUpdatedAt;
|
const diff = now - sessionUpdatedAt;
|
||||||
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
||||||
|
|
||||||
if (diffInMinutes > settings.expire) {
|
if (diffInMinutes > settings.expire) {
|
||||||
this.logger.debug(`Sessão expirada há ${diffInMinutes} minutos.`);
|
this.logger.debug(`[processOpenaiChatCompletion] Sessão expirada há ${diffInMinutes} minutos.`);
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
this.logger.debug('Atualizando status da sessão para CLOSED.');
|
this.logger.debug('[processOpenaiChatCompletion] Atualizando status da sessão para CLOSED.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -857,7 +955,7 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug('Deletando sessão do banco de dados.');
|
this.logger.debug('[processOpenaiChatCompletion] Deletando sessão do banco de dados.');
|
||||||
await this.prismaRepository.integrationSession.deleteMany({
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
botId: openaiBot.id,
|
botId: openaiBot.id,
|
||||||
@ -866,19 +964,19 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Recriando nova sessão de chatCompletion...');
|
this.logger.debug('[processOpenaiChatCompletion] Recriando nova sessão de chatCompletion...');
|
||||||
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
this.logger.debug('Nenhuma sessão encontrada. Criando nova sessão de chatCompletion...');
|
this.logger.debug('[processOpenaiChatCompletion] Nenhuma sessão encontrada. Criando nova sessão de chatCompletion...');
|
||||||
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Marcando sessão como aberta e awaitUser = false.');
|
this.logger.debug('[processOpenaiChatCompletion] Marcando sessão como aberta e awaitUser = false.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -890,9 +988,9 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
this.logger.debug('Não há conteúdo na mensagem. Verificando se existe unknownMessage para retorno.');
|
this.logger.debug('[processOpenaiChatCompletion] Não há conteúdo na mensagem. Verificando se existe unknownMessage para retorno.');
|
||||||
if (settings.unknownMessage) {
|
if (settings.unknownMessage) {
|
||||||
this.logger.debug(`Enviando unknownMessage para o remoteJid: ${remoteJid}`);
|
this.logger.debug(`[processOpenaiChatCompletion] Enviando unknownMessage para o remoteJid: ${remoteJid}`);
|
||||||
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -907,7 +1005,7 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
||||||
this.logger.debug('Keyword finish detectada. Encerrando sessão.');
|
this.logger.debug('[processOpenaiChatCompletion] Keyword finish detectada. Encerrando sessão.');
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
@ -928,7 +1026,7 @@ export class OpenaiService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Buscando OpenaiCreds no banco...');
|
this.logger.debug('[processOpenaiChatCompletion] Buscando OpenaiCreds no banco...');
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: openaiBot.openaiCredsId,
|
id: openaiBot.openaiCredsId,
|
||||||
@ -936,21 +1034,21 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) {
|
if (!creds) {
|
||||||
this.logger.error('Openai Creds não encontrados, lançando erro.');
|
this.logger.error('[processOpenaiChatCompletion] Openai Creds não encontrados, lançando erro.');
|
||||||
throw new Error('Openai Creds not found');
|
throw new Error('Openai Creds not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Instanciando cliente OpenAI para processar a mensagem (ChatCompletion).');
|
this.logger.debug('[processOpenaiChatCompletion] Instanciando cliente OpenAI para processar a mensagem (ChatCompletion).');
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug('Enviando mensagem para o Bot usando chatCompletion.');
|
this.logger.debug('[processOpenaiChatCompletion] Enviando mensagem para o Bot usando chatCompletion.');
|
||||||
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
||||||
|
|
||||||
this.logger.debug(`Resposta do Bot: ${message}`);
|
this.logger.debug(`[processOpenaiChatCompletion] Resposta do Bot: ${message}`);
|
||||||
if (message) {
|
if (message) {
|
||||||
this.logger.debug('Enviando resposta para o WhatsApp.');
|
this.logger.debug('[processOpenaiChatCompletion] Enviando resposta para o WhatsApp.');
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,17 +1056,17 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async speechToText(creds: OpenaiCreds, msg: any, updateMediaMessage: any) {
|
public async speechToText(creds: OpenaiCreds, msg: any, updateMediaMessage: any) {
|
||||||
this.logger.debug('Iniciando conversão de fala em texto (speechToText).');
|
this.logger.debug('[speechToText] Iniciando conversão de fala em texto.');
|
||||||
|
|
||||||
let audio;
|
let audio;
|
||||||
|
|
||||||
if (msg?.message?.mediaUrl) {
|
if (msg?.message?.mediaUrl) {
|
||||||
this.logger.debug('Baixando áudio via URL (mediaUrl).');
|
this.logger.debug('[speechToText] Baixando áudio via URL (mediaUrl).');
|
||||||
audio = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' }).then((response) => {
|
audio = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' }).then((response) => {
|
||||||
return Buffer.from(response.data, 'binary');
|
return Buffer.from(response.data, 'binary');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug('Baixando áudio via downloadMediaMessage (baileys).');
|
this.logger.debug('[speechToText] Baixando áudio via downloadMediaMessage (baileys).');
|
||||||
audio = await downloadMediaMessage(
|
audio = await downloadMediaMessage(
|
||||||
{ key: msg.key, message: msg?.message },
|
{ key: msg.key, message: msg?.message },
|
||||||
'buffer',
|
'buffer',
|
||||||
@ -983,14 +1081,14 @@ export class OpenaiService {
|
|||||||
const lang = this.configService.get<Language>('LANGUAGE').includes('pt')
|
const lang = this.configService.get<Language>('LANGUAGE').includes('pt')
|
||||||
? 'pt'
|
? 'pt'
|
||||||
: this.configService.get<Language>('LANGUAGE');
|
: this.configService.get<Language>('LANGUAGE');
|
||||||
this.logger.debug(`Definindo idioma da transcrição como: ${lang}`);
|
this.logger.debug(`[speechToText] Definindo idioma da transcrição como: ${lang}`);
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', audio, 'audio.ogg');
|
formData.append('file', audio, 'audio.ogg');
|
||||||
formData.append('model', 'whisper-1');
|
formData.append('model', 'whisper-1');
|
||||||
formData.append('language', lang);
|
formData.append('language', lang);
|
||||||
|
|
||||||
this.logger.debug('Enviando requisição POST para a API de transcrição do OpenAI.');
|
this.logger.debug('[speechToText] Enviando requisição POST para a API de transcrição do OpenAI.');
|
||||||
const response = await axios.post('https://api.openai.com/v1/audio/transcriptions', formData, {
|
const response = await axios.post('https://api.openai.com/v1/audio/transcriptions', formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
@ -998,7 +1096,7 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug(`Status da requisição: ${response.status}`);
|
this.logger.debug(`[speechToText] Status da requisição: ${response.status}`);
|
||||||
return response?.data?.text;
|
return response?.data?.text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user