Merge branch 'ev2' into v2.0.0

This commit is contained in:
Stênio Aníbal
2024-08-20 11:26:34 -03:00
21 changed files with 675 additions and 398 deletions

View File

@@ -5,6 +5,7 @@ import { WAMonitoringService } from '@api/services/monitor.service';
import { Auth, ConfigService, HttpServer, S3 } from '@config/env.config';
import { Logger } from '@config/logger.config';
import { Dify, DifySetting, IntegrationSession, Message } from '@prisma/client';
import { advancedOperatorsSearch } from '@utils/advancedOperatorsSearch';
import { sendTelemetry } from '@utils/sendTelemetry';
import axios from 'axios';
import { Readable } from 'stream';
@@ -114,6 +115,23 @@ export class DifyService {
}
}
if (data.triggerType === 'advanced') {
if (!data.triggerValue) {
throw new Error('Trigger value is required');
}
const checkDuplicate = await this.prismaRepository.dify.findFirst({
where: {
triggerValue: data.triggerValue,
instanceId: instanceId,
},
});
if (checkDuplicate) {
throw new Error('Trigger already exists');
}
}
try {
const dify = await this.prismaRepository.dify.create({
data: {
@@ -239,9 +257,25 @@ export class DifyService {
where: {
triggerOperator: data.triggerOperator,
triggerValue: data.triggerValue,
id: {
not: difyId,
},
id: { not: difyId },
instanceId: instanceId,
},
});
if (checkDuplicate) {
throw new Error('Trigger already exists');
}
}
if (data.triggerType === 'advanced') {
if (!data.triggerValue) {
throw new Error('Trigger value is required');
}
const checkDuplicate = await this.prismaRepository.dify.findFirst({
where: {
triggerValue: data.triggerValue,
id: { not: difyId },
instanceId: instanceId,
},
});
@@ -727,6 +761,19 @@ export class DifyService {
if (findTriggerAll) return findTriggerAll;
const findTriggerAdvanced = await this.prismaRepository.dify.findMany({
where: {
enabled: true,
triggerType: 'advanced',
instanceId: instanceId,
},
});
for (const advanced of findTriggerAdvanced) {
if (advancedOperatorsSearch(content, advanced.triggerValue)) {
return advanced;
}
}
// Check for exact match
const findTriggerEquals = await this.prismaRepository.dify.findFirst({
where: {

View File

@@ -29,7 +29,7 @@ export const difySchema: JSONSchema7 = {
botType: { type: 'string', enum: ['chatBot', 'textGenerator', 'agent', 'workflow'] },
apiUrl: { type: 'string' },
apiKey: { type: 'string' },
triggerType: { type: 'string', enum: ['all', 'keyword', 'none'] },
triggerType: { type: 'string', enum: ['all', 'keyword', 'none', 'advanced'] },
triggerOperator: { type: 'string', enum: ['equals', 'contains', 'startsWith', 'endsWith', 'regex'] },
triggerValue: { type: 'string' },
expire: { type: 'integer' },

View File

@@ -10,6 +10,7 @@ import { WAMonitoringService } from '@api/services/monitor.service';
import { ConfigService, Language, S3 } from '@config/env.config';
import { Logger } from '@config/logger.config';
import { IntegrationSession, Message, OpenaiBot, OpenaiCreds, OpenaiSetting } from '@prisma/client';
import { advancedOperatorsSearch } from '@utils/advancedOperatorsSearch';
import { sendTelemetry } from '@utils/sendTelemetry';
import axios from 'axios';
import { downloadMediaMessage } from 'baileys';
@@ -238,6 +239,23 @@ export class OpenaiService {
}
}
if (data.triggerType === 'advanced') {
if (!data.triggerValue) {
throw new Error('Trigger value is required');
}
const checkDuplicate = await this.prismaRepository.openaiBot.findFirst({
where: {
triggerValue: data.triggerValue,
instanceId: instanceId,
},
});
if (checkDuplicate) {
throw new Error('Trigger already exists');
}
}
try {
const openaiBot = await this.prismaRepository.openaiBot.create({
data: {
@@ -390,9 +408,25 @@ export class OpenaiService {
where: {
triggerOperator: data.triggerOperator,
triggerValue: data.triggerValue,
id: {
not: openaiBotId,
},
id: { not: openaiBotId },
instanceId: instanceId,
},
});
if (checkDuplicate) {
throw new Error('Trigger already exists');
}
}
if (data.triggerType === 'advanced') {
if (!data.triggerValue) {
throw new Error('Trigger value is required');
}
const checkDuplicate = await this.prismaRepository.openaiBot.findFirst({
where: {
triggerValue: data.triggerValue,
id: { not: openaiBotId },
instanceId: instanceId,
},
});
@@ -452,7 +486,7 @@ export class OpenaiService {
const openaiBots = await this.prismaRepository.openaiBot.findMany({
where: {
instanceId: instanceId,
instanceId,
},
include: {
sessions: true,
@@ -600,13 +634,14 @@ export class OpenaiService {
public async fetchDefaultSettings(instance: InstanceDto) {
try {
const instanceId = await this.prismaRepository.instance
.findFirst({
const instanceId = (
await this.prismaRepository.instance.findFirst({
select: { id: true },
where: {
name: instance.instanceName,
},
})
.then((instance) => instance.id);
)?.id;
const settings = await this.prismaRepository.openaiSetting.findFirst({
where: {
@@ -931,6 +966,19 @@ export class OpenaiService {
if (findTriggerAll) return findTriggerAll;
const findTriggerAdvanced = await this.prismaRepository.openaiBot.findMany({
where: {
enabled: true,
triggerType: 'advanced',
instanceId: instanceId,
},
});
for (const advanced of findTriggerAdvanced) {
if (advancedOperatorsSearch(content, advanced.triggerValue)) {
return advanced;
}
}
// Check for exact match
const findTriggerEquals = await this.prismaRepository.openaiBot.findFirst({
where: {
@@ -1070,7 +1118,7 @@ export class OpenaiService {
}, debounceTime * 1000);
}
public async sendOpenai(instance: InstanceDto, remoteJid: string, msg: Message) {
public async sendOpenai(instance: InstanceDto, remoteJid: string, pushName: string, msg: Message) {
try {
const settings = await this.prismaRepository.openaiSetting.findFirst({
where: {
@@ -1193,7 +1241,7 @@ export class OpenaiService {
};
if (stopBotFromMe && key.fromMe && session) {
await this.prismaRepository.integrationSession.update({
session = await this.prismaRepository.integrationSession.update({
where: {
id: session.id,
},
@@ -1201,7 +1249,6 @@ export class OpenaiService {
status: 'paused',
},
});
return;
}
if (!listeningFromMe && key.fromMe) {
@@ -1214,6 +1261,8 @@ export class OpenaiService {
await this.processOpenaiAssistant(
this.waMonitor.waInstances[instance.instanceName],
remoteJid,
pushName,
key.fromMe,
findOpenai,
session,
settings,
@@ -1237,6 +1286,8 @@ export class OpenaiService {
await this.processOpenaiAssistant(
this.waMonitor.waInstances[instance.instanceName],
remoteJid,
pushName,
key.fromMe,
findOpenai,
session,
settings,
@@ -1304,6 +1355,8 @@ export class OpenaiService {
private async initAssistantNewSession(
instance: any,
remoteJid: string,
pushName: string,
fromMe: boolean,
openaiBot: OpenaiBot,
settings: OpenaiSetting,
session: IntegrationSession,
@@ -1320,7 +1373,7 @@ export class OpenaiService {
}
const messageData: any = {
role: 'user',
role: fromMe ? 'assistant' : 'user',
content: [{ type: 'text', text: content }],
};
@@ -1342,6 +1395,11 @@ export class OpenaiService {
await this.client.beta.threads.messages.create(data.session.sessionId, messageData);
if (fromMe) {
sendTelemetry('/message/sendText');
return;
}
const runAssistant = await this.client.beta.threads.runs.create(data.session.sessionId, {
assistant_id: openaiBot.assistantId,
});
@@ -1350,7 +1408,13 @@ export class OpenaiService {
await instance.client.sendPresenceUpdate('composing', remoteJid);
const response = await this.getAIResponse(data.session.sessionId, runAssistant.id, openaiBot.functionUrl);
const response = await this.getAIResponse(
data.session.sessionId,
runAssistant.id,
openaiBot.functionUrl,
remoteJid,
pushName,
);
await instance.client.sendPresenceUpdate('paused', remoteJid);
@@ -1426,10 +1490,15 @@ export class OpenaiService {
}
}
private async getAIResponse(threadId: string, runId: string, functionUrl: string) {
private async getAIResponse(
threadId: string,
runId: string,
functionUrl: string,
remoteJid: string,
pushName: string,
) {
const getRun = await this.client.beta.threads.runs.retrieve(threadId, runId);
let toolCalls;
switch (getRun.status) {
case 'requires_action':
toolCalls = getRun?.required_action?.submit_tool_outputs?.tool_calls;
@@ -1447,7 +1516,7 @@ export class OpenaiService {
try {
const { data } = await axios.post(functionUrl, {
name: functionName,
arguments: functionArgument,
arguments: { ...functionArgument, remoteJid, pushName },
});
output = JSON.stringify(data)
@@ -1476,13 +1545,13 @@ export class OpenaiService {
}
}
return this.getAIResponse(threadId, runId, functionUrl);
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
case 'queued':
await new Promise((resolve) => setTimeout(resolve, 1000));
return this.getAIResponse(threadId, runId, functionUrl);
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
case 'in_progress':
await new Promise((resolve) => setTimeout(resolve, 1000));
return this.getAIResponse(threadId, runId, functionUrl);
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
case 'completed':
return await this.client.beta.threads.messages.list(threadId, {
run_id: runId,
@@ -1498,12 +1567,14 @@ export class OpenaiService {
private async processOpenaiAssistant(
instance: any,
remoteJid: string,
pushName: string,
fromMe: boolean,
openaiBot: OpenaiBot,
session: IntegrationSession,
settings: OpenaiSetting,
content: string,
) {
if (session && session.status !== 'opened') {
if (session && session.status === 'closed') {
return;
}
@@ -1535,25 +1606,35 @@ export class OpenaiService {
});
}
await this.initAssistantNewSession(instance, remoteJid, openaiBot, settings, session, content);
await this.initAssistantNewSession(
instance,
remoteJid,
pushName,
fromMe,
openaiBot,
settings,
session,
content,
);
return;
}
}
if (!session) {
await this.initAssistantNewSession(instance, remoteJid, openaiBot, settings, session, content);
await this.initAssistantNewSession(instance, remoteJid, pushName, fromMe, openaiBot, settings, session, content);
return;
}
await this.prismaRepository.integrationSession.update({
where: {
id: session.id,
},
data: {
status: 'opened',
awaitUser: false,
},
});
if (session.status !== 'paused')
await this.prismaRepository.integrationSession.update({
where: {
id: session.id,
},
data: {
status: 'opened',
awaitUser: false,
},
});
if (!content) {
if (settings.unknownMessage) {
@@ -1607,7 +1688,7 @@ export class OpenaiService {
const threadId = session.sessionId;
const messageData: any = {
role: 'user',
role: fromMe ? 'assistant' : 'user',
content: [{ type: 'text', text: content }],
};
@@ -1629,15 +1710,24 @@ export class OpenaiService {
await this.client.beta.threads.messages.create(threadId, messageData);
if (fromMe || session?.status === 'paused') {
sendTelemetry('/message/sendText');
return;
}
const runAssistant = await this.client.beta.threads.runs.create(threadId, {
assistant_id: openaiBot.assistantId,
additional_instructions: `WhatsappApiInfo:
Name: ${pushName}
RemoteJid: ${remoteJid}
`,
});
await instance.client.presenceSubscribe(remoteJid);
await instance.client.sendPresenceUpdate('composing', remoteJid);
const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl);
const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl, remoteJid, pushName);
await instance.client.sendPresenceUpdate('paused', remoteJid);

View File

@@ -35,7 +35,7 @@ export const openaiSchema: JSONSchema7 = {
assistantMessages: { type: 'array', items: { type: 'string' } },
userMessages: { type: 'array', items: { type: 'string' } },
maxTokens: { type: 'integer' },
triggerType: { type: 'string', enum: ['all', 'keyword', 'none'] },
triggerType: { type: 'string', enum: ['all', 'keyword', 'none', 'advanced'] },
triggerOperator: { type: 'string', enum: ['equals', 'contains', 'startsWith', 'endsWith', 'regex'] },
triggerValue: { type: 'string' },
expire: { type: 'integer' },

View File

@@ -6,6 +6,7 @@ import { Events } from '@api/types/wa.types';
import { Auth, ConfigService, HttpServer, S3, Typebot } from '@config/env.config';
import { Logger } from '@config/logger.config';
import { Instance, IntegrationSession, Message, Typebot as TypebotModel } from '@prisma/client';
import { advancedOperatorsSearch } from '@utils/advancedOperatorsSearch';
import { sendTelemetry } from '@utils/sendTelemetry';
import axios from 'axios';
@@ -113,6 +114,23 @@ export class TypebotService {
}
}
if (data.triggerType === 'advanced') {
if (!data.triggerValue) {
throw new Error('Trigger value is required');
}
const checkDuplicate = await this.prismaRepository.typebot.findFirst({
where: {
triggerValue: data.triggerValue,
instanceId: instanceId,
},
});
if (checkDuplicate) {
throw new Error('Trigger already exists');
}
}
try {
const typebot = await this.prismaRepository.typebot.create({
data: {
@@ -250,6 +268,24 @@ export class TypebotService {
}
}
if (data.triggerType === 'advanced') {
if (!data.triggerValue) {
throw new Error('Trigger value is required');
}
const checkDuplicate = await this.prismaRepository.typebot.findFirst({
where: {
triggerValue: data.triggerValue,
id: { not: typebotId },
instanceId: instanceId,
},
});
if (checkDuplicate) {
throw new Error('Trigger already exists');
}
}
try {
const typebot = await this.prismaRepository.typebot.update({
where: {
@@ -1287,6 +1323,19 @@ export class TypebotService {
if (findTriggerAll) return findTriggerAll;
const findTriggerAdvanced = await this.prismaRepository.typebot.findMany({
where: {
enabled: true,
triggerType: 'advanced',
instanceId: instanceId,
},
});
for (const advanced of findTriggerAdvanced) {
if (advancedOperatorsSearch(content, advanced.triggerValue)) {
return advanced;
}
}
// Check for exact match
const findTriggerEquals = await this.prismaRepository.typebot.findFirst({
where: {

View File

@@ -28,7 +28,7 @@ export const typebotSchema: JSONSchema7 = {
description: { type: 'string' },
url: { type: 'string' },
typebot: { type: 'string' },
triggerType: { type: 'string', enum: ['all', 'keyword', 'none'] },
triggerType: { type: 'string', enum: ['all', 'keyword', 'none', 'advanced'] },
triggerOperator: { type: 'string', enum: ['equals', 'contains', 'startsWith', 'endsWith', 'regex'] },
triggerValue: { type: 'string' },
expire: { type: 'integer' },