Implementação com o Chat GPT e inbox Chatwoot

Realizei a implementação com o ChatGPT e com o id do inbox do chatwoot
This commit is contained in:
CodePhix 2023-12-06 15:53:32 -03:00
parent f8d874453c
commit ca34f34eba
48 changed files with 1599 additions and 115 deletions

5
dev-env Normal file
View File

@ -0,0 +1,5 @@
# OPENAI_API_KEY=""
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
REDIS_PASS=0

14
src/config.ts Normal file
View File

@ -0,0 +1,14 @@
import dotenv from "dotenv"
dotenv.config()
export const config = {
openAI: {
apiToken: process.env.OPENAI_API_KEY,
},
redis: {
host: process.env.REDIS_HOST || "localhost",
port: (process.env.REDIS_PORT as unknown as number) || 6379,
db: (process.env.REDIS_DB as unknown as number) || 0,
},
}

View File

@ -48,6 +48,7 @@ export type CleanStoreConf = {
export type DBConnection = {
URI: string;
DB_PREFIX_NAME: string;
DB_PREFIX_FINAL_NAME: string;
};
export type Database = {
CONNECTION: DBConnection;
@ -74,6 +75,12 @@ export type Sqs = {
REGION: string;
};
export type Openai = {
CHAVE: string;
ENABLED: boolean;
URI: string;
};
export type Websocket = {
ENABLED: boolean;
};
@ -145,6 +152,7 @@ export interface Env {
REDIS: Redis;
RABBITMQ: Rabbitmq;
SQS: Sqs;
OPENAI: Openai;
WEBSOCKET: Websocket;
LOG: Log;
DEL_INSTANCE: DelInstance;
@ -218,6 +226,7 @@ export class ConfigService {
CONNECTION: {
URI: process.env.DATABASE_CONNECTION_URI || '',
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution',
DB_PREFIX_FINAL_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_FINAL_NAME || '-api',
},
ENABLED: process.env?.DATABASE_ENABLED === 'true',
SAVE_DATA: {
@ -244,6 +253,12 @@ export class ConfigService {
ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '',
REGION: process.env.SQS_REGION || '',
},
OPENAI: {
CHAVE: process.env?.OPENAI_ENABLED || '',
ENABLED: process.env?.OPENAI_ENABLED === 'true',
URI: process.env.OPENAI_URI || '',
},
WEBSOCKET: {
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
},

View File

@ -7,8 +7,8 @@
# Choose the server type for the application
SERVER:
TYPE: http # https
PORT: 8080 # 443
URL: localhost
PORT: 3333 # 443
URL: 127.0.0.1
CORS:
ORIGIN:
@ -48,8 +48,8 @@ DEL_INSTANCE: false # or false
# Temporary data storage
STORE:
MESSAGES: true
MESSAGE_UP: true
MESSAGES: false
MESSAGE_UP: false
CONTACTS: true
CHATS: true
@ -62,17 +62,20 @@ CLEAN_STORE:
# Permanent data storage
DATABASE:
ENABLED: false
ENABLED: true
CONNECTION:
URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true"
DB_PREFIX_NAME: evolution
DB_PREFIX_NAME: whatsapp
DB_PREFIX_FINAL_NAME: "-api"
DB_PREFIX_FINAL_NAME_VENOM: "-venom"
# Choose the data you want to save in the application's database or store
SAVE_DATA:
INSTANCE: false
INSTANCE: true
NEW_MESSAGE: false
MESSAGE_UPDATE: false
CONTACTS: false
CHATS: false
CONTACTS: true
CHATS: true
REDIS:
ENABLED: false
@ -83,8 +86,14 @@ RABBITMQ:
ENABLED: false
URI: "amqp://guest:guest@localhost:5672"
OPENAI:
CHAVE: ""
ENABLED: false
PROMPTS: false
URI: ""
SQS:
ENABLED: true
ENABLED: false
ACCESS_KEY_ID: ""
SECRET_ACCESS_KEY: ""
ACCOUNT_ID: ""
@ -93,12 +102,15 @@ SQS:
WEBSOCKET:
ENABLED: false
TYPEBOT:
API_VERSION: 'v1' # v1 | v2
# Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created
WEBHOOK:
# Define a global webhook that will listen for enabled events from all instances
GLOBAL:
URL: <url>
URL: ""
ENABLED: false
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
WEBHOOK_BY_EVENTS: false
@ -134,11 +146,11 @@ WEBHOOK:
CHAMA_AI_ACTION: false
# This event is used to send errors to the webhook
ERRORS: false
ERRORS_WEBHOOK: <url>
ERRORS_WEBHOOK: ""
CONFIG_SESSION_PHONE:
# Name that will be displayed on smartphone connection
CLIENT: "Evolution API"
CLIENT: "Chat API"
NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari
# Set qrcode display limit
@ -146,9 +158,6 @@ QRCODE:
LIMIT: 30
COLOR: "#198754"
TYPEBOT:
API_VERSION: 'v1' # v1 | v2
# Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token,
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token

View File

@ -25,7 +25,7 @@ info:
</font>
[![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442)
version: 1.5.5
version: 1.5.4
contact:
name: DavidsonGomes
email: contato@agenciadgcode.com

View File

@ -1,6 +1,6 @@
import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq } from '../config/env.config';
import { configService, Rabbitmq, Openai } from '../config/env.config';
import { Logger } from '../config/logger.config';
const logger = new Logger('AMQP');

View File

@ -10,7 +10,8 @@ export const dbserver = (() => {
if (db.ENABLED) {
logger.verbose('connecting');
const dbs = mongoose.createConnection(db.CONNECTION.URI, {
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
dbName: db.CONNECTION.DB_PREFIX_NAME + db.CONNECTION.DB_PREFIX_FINAL_NAME + '-config',
});
logger.verbose('connected in ' + db.CONNECTION.URI);
logger.info('ON - dbName: ' + dbs['$dbName']);

34
src/libs/openai.ts Normal file
View File

@ -0,0 +1,34 @@
import { Configuration, OpenAIApi } from "openai"
import { config } from "../config"
const configuration = new Configuration({
apiKey: config.openAI.apiToken,
})
export const openai = new OpenAIApi(configuration)
export class OpenAIService {
constructor(
private readonly apikey: String,
) {
}
private WaOpenai: OpenAIApi;
public SetOpenai() {
const configuration = new Configuration({
apiKey: config.openAI.apiToken,
})
this.WaOpenai = new OpenAIApi(configuration)
}
public openai() {
return this.WaOpenai;
}
}

View File

@ -5,11 +5,6 @@ import { Redis } from '../config/env.config';
import { Logger } from '../config/logger.config';
export class RedisCache {
private readonly logger = new Logger(RedisCache.name);
private client: RedisClientType;
private statusConnection = false;
private instanceName: string;
private redisEnv: Redis;
constructor() {
this.logger.verbose('RedisCache instance created');
@ -19,6 +14,21 @@ export class RedisCache {
});
}
// constructor() {
// this.logger.verbose('instance created');
// process.on('beforeExit', async () => {
// this.logger.verbose('instance destroyed');
// if (this.statusConnection) {
// this.logger.verbose('instance disconnect');
// await this.client.disconnect();
// }
// });
// }
private statusConnection = false;
private instanceName: string;
private redisEnv: Redis;
public set reference(reference: string) {
this.logger.verbose('set reference: ' + reference);
this.instanceName = reference;
@ -27,14 +37,18 @@ export class RedisCache {
public async connect(redisEnv: Redis) {
this.logger.verbose('Connecting to Redis...');
this.client = createClient({ url: redisEnv.URI });
//this.logger.verbose('connected in ' + redisEnv.URI);
this.client.on('error', (err) => this.logger.error('Redis Client Error ' + err));
await this.client.connect();
this.statusConnection = true;
this.redisEnv = redisEnv;
this.logger.verbose(`Connected to ${redisEnv.URI}`);
}
private readonly logger = new Logger(RedisCache.name);
private client: RedisClientType;
public async disconnect() {
if (this.statusConnection) {
await this.client.disconnect();
@ -46,11 +60,14 @@ export class RedisCache {
public async instanceKeys(): Promise<string[]> {
const keys: string[] = [];
try {
//this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
//return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']);
this.logger.verbose('Fetching instance keys');
for await (const key of this.client.scanIterator({ MATCH: `${this.redisEnv.PREFIX_KEY}:*` })) {
keys.push(key);
}
} catch (error) {
this.logger.error(error);
this.logger.error('Error fetching instance keys ' + error);
}
return keys;

9
src/libs/redis.ts Normal file
View File

@ -0,0 +1,9 @@
import { Redis } from "ioredis"
import { config } from "../config"
export const redis = new Redis({
host: config.redis.host,
port: config.redis.port,
db: config.redis.db,
})

View File

@ -0,0 +1,49 @@
export const promptContabil = `Você é uma assistente virtual de atendimento de um escritorio de contabilidade chamado {{ storeName }}. Você deve ser educada, atenciosa, amigável, cordial e muito paciente.
O roteiro de atendimento é:
1. Saudação inicial: Cumprimente o cliente e agradeça por entrar em contato.
2. Coleta de informações: Solicite ao cliente seu nome para registro caso ainda não tenha registrado. Informe que os dados são apenas para controle de atendimento e não serão compartilhados com terceiros.
3. Pergunte o setor que deseja seguir, sendo o seguinte Financeiro, suporte ou novo cliente.
4. Serviços de Contabilidade:
4.1 Apresente os principais serviços de contabilidade oferecidos pelo escritório.
4.2 Oferecemos uma ampla gama de serviços contábeis, incluindo ABERTURA DE EMPRESA, ABERTURA DE FILIAL, ASSESSORIA CONTÁBIL, ASSESSORIA FISCAL, BAIXA E REGULARIZAÇÃO DE EMPRESAS, CONSULTORIA CONTÁBIL, PLANEJAMENTO TRIBUTÁRIO, RECURSOS HUMANOS, REVISÃO TRIBUTÁRIA, Administração de Condomínios. Como posso ajudá-lo com seus desafios financeiros hoje?
5. Perguntas Frequentes (FAQ):
5.1 Forneça respostas para perguntas frequentes sobre impostos, contabilidade e serviços específicos.
5.2 Aqui estão algumas perguntas frequentes sobre nossos serviços: [
Pergunta 1: Vou precisar falar com meu antigo contador ou escritório de contabilidade sobre a migração?
Resposta 1: Não. Fique tranquilo pois a Anexo Gestão Contábil cuida disso para você. Você precisará enviar o seu Certificado Digital. Caso não tenha um e-CNPJ(A1), vamos te ajudar no processo de migração de outra forma, falando com o contador ou auxiliando na contratação de um Certificado Digital. Depois dessa etapa nós vamos fazer todo o seu processo com o seu antigo contador ou escritório de contabilidade. É simples, transparente e sem burocracia para você.
Pergunta 2: Quanto tempo demora para mudar para a Anexo Gestão Contábil?
Resposta 2: O processo é rápido, prático e você não precisa sair de casa. Aproximadamente 5 dias úteis é o prazo após conseguirmos acessar a documentação via Certificado Digital ou com o contador antigo.
].
5. Agendamento de Consulta:
5.2 Permita que os usuários agendem uma consulta com um contador.
5.3 Pergunte sobre a data e hora preferidas.
5.3 Se você gostaria de agendar uma consulta com um de nossos contadores, por favor, informe-nos sobre a data e horário que funcionam melhor para você.
6. Impostos:
6.1 Forneça informações sobre prazos de declaração de impostos, documentos necessários e dicas fiscais.
6.2 Os prazos para a declaração de impostos estão se aproximando. Aqui estão algumas dicas sobre como se preparar e os documentos necessários.
7. Contato e Localização:
7.1 Fornecer informações de contato, incluindo número de telefone, endereço de e-mail e endereço físico do escritório.
7.2 Você pode nos contatar pelo telefone [número], enviar um e-mail para [e-mail] ou nos visitar no seguinte endereço [endereço].
8. Encaminhamento para um Contador:
8.1 Se o chatbot não puder responder a uma pergunta específica, ofereça a opção de ser encaminhado para um contador real.
8.2 Se você tiver uma pergunta mais complexa que eu não possa responder, gostaria de ser encaminhado para um dos nossos contadores?"
9. Despedida:
9.1 Encerre a conversa de maneira cortês e ofereça informações de contato adicionais.
9.2 Obrigado por usar nossos serviços! Se precisar de assistência futura, estamos à disposição. Tenha um ótimo dia!"
10. Feedback:
10.1 Solicite feedback aos usuários para melhorar o desempenho do chatbot.
10.2 Gostaríamos de ouvir sua opinião! Em uma escala de 1 a 5, quão útil você achou nosso chatbot hoje?
`

94
src/prompts/pizzaAgent.ts Normal file
View File

@ -0,0 +1,94 @@
export const promptPizza = `Você é uma assistente virtual de atendimento de uma pizzaria chamada {{ storeName }}. Você deve ser educada, atenciosa, amigável, cordial e muito paciente.
Você não pode oferecer nenhum item ou sabor que não esteja em nosso cardápio. Siga estritamente as listas de opções.
O código do pedido é: {{ orderCode }}
O roteiro de atendimento é:
1. Saudação inicial: Cumprimente o cliente e agradeça por entrar em contato.
2. Coleta de informações: Solicite ao cliente seu nome para registro caso ainda não tenha registrado. Informe que os dados são apenas para controle de pedidos e não serão compartilhados com terceiros.
3. Quantidade de pizzas: Pergunte ao cliente quantas pizzas ele deseja pedir.
4. Sabores: Envie a lista resumida apenas com os nomes de sabores salgados e doces e pergunte ao cliente quais sabores de pizza ele deseja pedir.
4.1 O cliente pode escolher a pizza fracionada em até 2 sabores na mesma pizza.
4.2 Se o cliente escolher mais de uma pizza, pergunte se ele deseja que os sabores sejam repetidos ou diferentes.
4.3 Se o cliente escolher sabores diferentes, pergunte quais são os sabores de cada pizza.
4.4 Se o cliente escolher sabores repetidos, pergunte quantas pizzas de cada sabor ele deseja.
4.5 Se o cliente estiver indeciso, ofereça sugestões de sabores ou se deseja receber o cardápio completo.
4.6 Se o sabor não estiver no cardápio, não deve prosseguir com o atendimento. Nesse caso informe que o sabor não está disponível e agradeça o cliente.
5. Tamanho: Pergunte ao cliente qual o tamanho das pizzas.
5.1 Se o cliente escolher mais de um tamanho, pergunte se ele deseja que os tamanhos sejam repetidos ou diferentes.
5.2 Se o cliente escolher tamanhos diferentes, pergunte qual o tamanho de cada pizza.
5.3 Se o cliente escolher tamanhos repetidos, pergunte quantas pizzas de cada tamanho ele deseja.
5.4 Se o cliente estiver indeciso, ofereça sugestões de tamanhos. Se for para 1 pessoa o tamanho pequeno é ideal, para 2 pessoas o tamanho médio é ideal e para 3 ou mais pessoas o tamanho grande é ideal.
6. Ingredientes adicionais: Pergunte ao cliente se ele deseja adicionar algum ingrediente extra.
6.1 Se o cliente escolher ingredientes extras, pergunte quais são os ingredientes adicionais de cada pizza.
6.2 Se o cliente estiver indeciso, ofereça sugestões de ingredientes extras.
7. Remover ingredientes: Pergunte ao cliente se ele deseja remover algum ingrediente, por exemplo, cebola.
7.1 Se o cliente escolher ingredientes para remover, pergunte quais são os ingredientes que ele deseja remover de cada pizza.
7.2 Não é possível remover ingredientes que não existam no cardápio.
8. Borda: Pergunte ao cliente se ele deseja borda recheada.
8.1 Se o cliente escolher borda recheada, pergunte qual o sabor da borda recheada.
8.2 Se o cliente estiver indeciso, ofereça sugestões de sabores de borda recheada. Uma dica é oferecer a borda como sobremesa com sabor de chocolate.
9. Bebidas: Pergunte ao cliente se ele deseja pedir alguma bebida.
9.1 Se o cliente escolher bebidas, pergunte quais são as bebidas que ele deseja pedir.
9.2 Se o cliente estiver indeciso, ofereça sugestões de bebidas.
10. Entrega: Pergunte ao cliente se ele deseja receber o pedido em casa ou se prefere retirar no balcão.
10.1 Se o cliente escolher entrega, pergunte qual o endereço de entrega. O endereço deverá conter Rua, Número, Bairro e CEP.
10.2 Os CEPs de 12.220-000 até 12.330-000 possuem uma taxa de entrega de R$ 10,00.
10.3 Se o cliente escolher retirar no balcão, informe o endereço da pizzaria e o horário de funcionamento: Rua Abaeté, 123, Centro, São José dos Campos, SP. Horário de funcionamento: 18h às 23h.
11. Forma de pagamento: Pergunte ao cliente qual a forma de pagamento desejada, oferecendo opções como dinheiro, PIX, cartão de crédito ou débito na entrega.
11.1 Se o cliente escolher dinheiro, pergunte o valor em mãos e calcule o troco. O valor informado não pode ser menor que o valor total do pedido.
11.2 Se o cliente escolher PIX, forneça a chave PIX CNPJ: 1234
11.3 Se o cliente escolher cartão de crédito/débito, informe que a máquininha será levada pelo entregador.
12. Mais alguma coisa? Pergunte ao cliente se ele deseja pedir mais alguma coisa.
12.1 Se o cliente desejar pedir mais alguma coisa, pergunte o que ele deseja pedir.
12.2 Se o cliente não desejar pedir mais nada, informe o resumo do pedido: Dados do cliente, quantidade de pizzas, sabores, tamanhos, ingredientes adicionais, ingredientes removidos, borda, bebidas, endereço de entrega, forma de pagamento e valor total.
12.3 Confirmação do pedido: Pergunte ao cliente se o pedido está correto.
12.4 Se o cliente confirmar o pedido, informe o tempo de entrega médio de 45 minutos e agradeça.
12.5 Se o cliente não confirmar o pedido, pergunte o que está errado e corrija o pedido.
13. Despedida: Agradeça o cliente por entrar em contato. É muito importante que se despeça informando o número do pedido.
Cardápio de pizzas salgadas (os valores estão separados por tamanho - Broto, Médio e Grande):
- Muzzarella: Queijo mussarela, tomate e orégano. R$ 25,00 / R$ 30,00 / R$ 35,00
- Calabresa: Calabresa, cebola e orégano. R$ 30,00 / R$ 35,00 / R$ 40,00
- Nordestina: Carne de sol, cebola e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Frango: Frango desfiado, milho e orégano. R$ 30,00 / R$ 35,00 / R$ 40,00
- Frango c/ Catupiry: Frango desfiado, catupiry e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- A moda da Casa: Carne de sol, bacon, cebola e orégano. R$ 40,00 / R$ 45,00 / R$ 50,00
- Presunto: Presunto, queijo mussarela e orégano. R$ 30,00 / R$ 35,00 / R$ 40,00
- Quatro Estações: Presunto, queijo mussarela, ervilha, milho, palmito e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Mista: Presunto, queijo mussarela, calabresa, cebola e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Toscana: Calabresa, bacon, cebola e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Portuguesa: Presunto, queijo mussarela, calabresa, ovo, cebola e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Dois Queijos: Queijo mussarela, catupiry e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Quatro Queijos: Queijo mussarela, provolone, catupiry, parmesão e orégano. R$ 40,00 / R$ 45,00 / R$ 50,00
- Salame: Salame, queijo mussarela e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
- Atum: Atum, cebola e orégano. R$ 35,00 / R$ 40,00 / R$ 45,00
Cardápio de pizzas doces (os valores estão separados por tamanho - Broto, Médio e Grande):
- Chocolate: Chocolate ao leite e granulado. R$ 30,00 / R$ 35,00 / R$ 40,00
- Romeu e Julieta: Goiabada e queijo mussarela. R$ 30,00 / R$ 35,00 / R$ 40,00
- California: Banana, canela e açúcar. R$ 30,00 / R$ 35,00 / R$ 40,00
Extras/Adicionais (os valores estão separados por tamanho - Broto, Médio e Grande):
- Catupiry: R$ 5,00 / R$ 7,00 / R$ 9,00
Bordas (os valores estão separados por tamanho - Broto, Médio e Grande):
- Chocolate: R$ 5,00 / R$ 7,00 / R$ 9,00
- Cheddar: R$ 5,00 / R$ 7,00 / R$ 9,00
- Catupiry: R$ 5,00 / R$ 7,00 / R$ 9,00
Bebidas:
- Coca-Cola 2L: R$ 10,00
- Coca-Cola Lata: R$ 8,00
- Guaraná 2L: R$ 10,00
- Guaraná Lata: R$ 7,00
- Água com Gás 500 ml: R$ 5,00
- Água sem Gás 500 ml: R$ 4,00
`

13
src/utils/initPrompt.ts Normal file
View File

@ -0,0 +1,13 @@
import { promptPizza } from "../prompts/pizzaAgent"
export function initPrompt(storeName: string, orderCode: string, prompt?: string ): string {
if(prompt){
return prompt
.replace(/{{[\s]?storeName[\s]?}}/g, storeName)
.replace(/{{[\s]?orderCode[\s]?}}/g, orderCode)
}else{
return promptPizza
.replace(/{{[\s]?storeName[\s]?}}/g, storeName)
.replace(/{{[\s]?orderCode[\s]?}}/g, orderCode)
}
}

View File

@ -19,7 +19,10 @@ export async function useMultiFileAuthStateDb(
const client = dbserver.getClient();
const collection = client
.db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
.db(
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME +
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_FINAL_NAME
)
.collection(coll);
const writeData = async (data: any, key: string): Promise<any> => {
@ -33,6 +36,7 @@ export async function useMultiFileAuthStateDb(
};
}
return await collection.replaceOne({ _id: key }, msgParsed, {
//return await collection.replaceOne({ _id: key }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), {
upsert: true,
});
} catch (error) {
@ -47,6 +51,7 @@ export async function useMultiFileAuthStateDb(
if (data?.content_array) {
data = data.content_array;
}
//const data = await collection.findOne({ _id: key });
const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver);
} catch (error) {

View File

@ -987,6 +987,51 @@ export const rabbitmqSchema: JSONSchema7 = {
...isNotEmpty('enabled'),
};
export const openaiSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
chave: { type: 'string' },
enabled: { type: 'boolean', enum: [true, false] },
events: {
type: 'array',
minItems: 0,
items: {
type: 'string',
enum: [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
],
},
},
},
required: ['enabled'],
...isNotEmpty('enabled'),
};
export const sqsSchema: JSONSchema7 = {
$id: v4(),
type: 'object',

View File

@ -72,6 +72,7 @@ export class ChatwootController {
token: '',
sign_msg: false,
name_inbox: '',
id_inbox: '',
webhook_url: '',
};
}
@ -86,6 +87,7 @@ export class ChatwootController {
public async receiveWebhook(instance: InstanceDto, data: any) {
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
const chatwootService = new ChatwootService(waMonitor, this.configService);
return chatwootService.receiveWebhook(instance, data);

View File

@ -13,6 +13,7 @@ import { ChatwootService } from '../services/chatwoot.service';
import { WAMonitoringService } from '../services/monitor.service';
import { ProxyService } from '../services/proxy.service';
import { RabbitmqService } from '../services/rabbitmq.service';
import { OpenaiService } from '../services/openai.service';
import { SettingsService } from '../services/settings.service';
import { SqsService } from '../services/sqs.service';
import { TypebotService } from '../services/typebot.service';
@ -33,6 +34,7 @@ export class InstanceController {
private readonly settingsService: SettingsService,
private readonly websocketService: WebsocketService,
private readonly rabbitmqService: RabbitmqService,
private readonly openaiService: OpenaiService,
private readonly proxyService: ProxyService,
private readonly sqsService: SqsService,
private readonly typebotService: TypebotService,
@ -66,8 +68,14 @@ export class InstanceController {
websocket_events,
rabbitmq_enabled,
rabbitmq_events,
openai_chave,
openai_enabled,
openai_events,
sqs_enabled,
sqs_events,
typebot_url,
typebot,
typebot_expire,
@ -81,6 +89,7 @@ export class InstanceController {
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
this.logger.verbose('checking duplicate token');
await this.authService.checkDuplicateToken(token);
this.logger.verbose('creating instance');
@ -250,6 +259,56 @@ export class InstanceController {
}
}
let openaiEvents: string[];
if (openai_enabled) {
this.logger.verbose('creating openai');
try {
let newChave: string = "";
let newEvents: string[] = [];
if (openai_events.length === 0) {
newEvents = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
];
} else {
newEvents = openai_events;
}
this.openaiService.create(instance, {
chave: newChave,
enabled: true,
events: newEvents,
});
openaiEvents = (await this.openaiService.find(instance)).events;
} catch (error) {
this.logger.log(error);
}
}
if (proxy) {
this.logger.verbose('creating proxy');
try {
@ -266,6 +325,7 @@ export class InstanceController {
}
}
let sqsEvents: string[];
if (sqs_enabled) {
@ -380,6 +440,11 @@ export class InstanceController {
enabled: rabbitmq_enabled,
events: rabbitmqEvents,
},
openai: {
chave: openai_chave,
enabled: openai_enabled,
events: openaiEvents,
},
sqs: {
enabled: sqs_enabled,
events: sqsEvents,
@ -396,7 +461,6 @@ export class InstanceController {
},
settings,
qrcode: getQrcode,
proxy,
};
this.logger.verbose('instance created');
@ -479,6 +543,11 @@ export class InstanceController {
enabled: rabbitmq_enabled,
events: rabbitmqEvents,
},
openai: {
chave: openai_chave,
enabled: openai_enabled,
events: openaiEvents,
},
sqs: {
enabled: sqs_enabled,
events: sqsEvents,
@ -506,7 +575,6 @@ export class InstanceController {
name_inbox: instance.instanceName,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
},
proxy,
};
} catch (error) {
this.logger.error(error.message[0]);
@ -514,6 +582,23 @@ export class InstanceController {
}
}
public async qrInstance({ instanceName }: InstanceDto) {
try {
this.logger.verbose('requested qrInstance from ' + instanceName + ' instance');
this.logger.verbose('logging out instance: ' + instanceName);
///this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
const qrcode = await this.waMonitor.waInstances[instanceName]?.instance.qrcode;
return qrcode.base64;
} catch (error) {
this.logger.error(error);
return '';
}
}
public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) {
try {
this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance');
@ -628,18 +713,21 @@ export class InstanceController {
}
try {
this.waMonitor.waInstances[instanceName]?.removeRabbitmqQueues();
this.waMonitor.waInstances[instanceName]?.removeOpenaiQueues();
if (instance.state === 'connecting') {
this.logger.verbose('logging out instance: ' + instanceName);
await this.logout({ instanceName });
delete this.waMonitor.waInstances[instanceName];
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
} else {
this.logger.verbose('deleting instance: ' + instanceName);
delete this.waMonitor.waInstances[instanceName];
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
}
this.logger.verbose('deleting instance: ' + instanceName);
delete this.waMonitor.waInstances[instanceName];
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
} catch (error) {
throw new BadRequestException(error.toString());
}

View File

@ -0,0 +1,85 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { OpenaiService } from '../services/openai.service';
const logger = new Logger('OpenaiController');
export class OpenaiController {
constructor(private readonly openaiService: OpenaiService) {}
public async createOpenai(instance: InstanceDto, data: OpenaiDto) {
logger.verbose('requested createOpenai from ' + instance.instanceName + ' instance');
if (!data.chave) {
logger.verbose('openai sem chave');
data.chave = '';
}
if (!data.enabled) {
logger.verbose('openai disabled');
data.events = [];
}
if (data.events?.length === 0) {
logger.verbose('openai events empty');
data.events = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
];
}
return this.openaiService.create(instance, data);
}
public async findOpenai(instance: InstanceDto) {
logger.verbose('requested findOpenai from ' + instance.instanceName + ' instance');
return this.openaiService.find(instance);
}
public async createContactOpenai(instance: InstanceDto, data: ContactOpenaiDto) {
logger.verbose('requested createOpenai from ' + instance.instanceName + ' instance');
if (!data.contact) {
logger.verbose('openai sem chave');
data.contact = '';
}
if (!data.enabled) {
logger.verbose('openai disabled');
data.enabled = false;
}
data.owner = instance.instanceName;
return this.openaiService.createContact(instance, data);
}
public async findContactOpenai(instance: InstanceDto) {
logger.verbose('requested findOpenai from ' + instance.instanceName + ' instance');
return this.openaiService.findContact(instance);
}
}

View File

@ -6,7 +6,7 @@ import { SqsService } from '../services/sqs.service';
const logger = new Logger('SqsController');
export class SqsController {
constructor(private readonly sqsService: SqsService) {}
constructor(private readonly sqsService: SqsService) { }
public async createSqs(instance: InstanceDto, data: SqsDto) {
logger.verbose('requested createSqs from ' + instance.instanceName + ' instance');

View File

@ -4,6 +4,7 @@ export class ChatwootDto {
token?: string;
url?: string;
name_inbox?: string;
id_inbox?: string;
sign_msg?: boolean;
number?: string;
reopen_conversation?: boolean;

View File

@ -0,0 +1,5 @@
export class ContactOpenaiDto {
contact?: string;
enabled: boolean;
owner: string;
}

View File

@ -23,8 +23,15 @@ export class InstanceDto {
websocket_events?: string[];
rabbitmq_enabled?: boolean;
rabbitmq_events?: string[];
openai_chave?: boolean;
openai_prompts?: string;
openai_enabled?: boolean;
openai_events?: string[];
sqs_enabled?: boolean;
sqs_events?: string[];
typebot_url?: string;
typebot?: string;
typebot_expire?: number;
@ -32,5 +39,6 @@ export class InstanceDto {
typebot_delay_message?: number;
typebot_unknown_message?: string;
typebot_listening_from_me?: boolean;
proxy_enabled?: boolean;
proxy?: string;
}

View File

@ -0,0 +1,6 @@
export class OpenaiDto {
chave?: string;
enabled: boolean;
prompts?: string;
events?: string[];
}

View File

@ -29,7 +29,9 @@ async function getInstance(instanceName: string) {
if (db.ENABLED) {
const collection = dbserver
.getClient()
.db(db.CONNECTION.DB_PREFIX_NAME + '-instances')
.db(
db.CONNECTION.DB_PREFIX_NAME +
db.CONNECTION.DB_PREFIX_FINAL_NAME)
.collection(instanceName);
return exists || (await collection.find({}).toArray()).length > 0;
}

View File

@ -9,6 +9,7 @@ export class ChatwootRaw {
token?: string;
url?: string;
name_inbox?: string;
id_inbox?: string;
sign_msg?: boolean;
number?: string;
reopen_conversation?: boolean;
@ -22,6 +23,7 @@ const chatwootSchema = new Schema<ChatwootRaw>({
token: { type: String, required: true },
url: { type: String, required: true },
name_inbox: { type: String, required: true },
id_inbox: { type: String, required: true },
sign_msg: { type: Boolean, required: true },
number: { type: String, required: true },
reopen_conversation: { type: Boolean, required: true },

View File

@ -0,0 +1,20 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
export class ContactOpenaiRaw {
_id?: string;
contact?: string;
enabled?: boolean;
owner: string;
}
const contactOpenaiSchema = new Schema<ContactOpenaiRaw>({
_id: { type: String, _id: true },
contact: { type: String, required: true, minlength: 1 },
enabled: { type: Boolean, required: true },
owner: { type: String, required: true, minlength: 1 },
});
export const ContactOpenaiModel = dbserver?.model(ContactOpenaiRaw.name, contactOpenaiSchema, 'openai_contacts');
export type IContactOpenaiModel = typeof ContactOpenaiModel;

View File

@ -11,3 +11,5 @@ export * from './sqs.model';
export * from './typebot.model';
export * from './webhook.model';
export * from './websocket.model';
export * from './openai.model';
export * from './contactOpenai.model';

View File

@ -0,0 +1,22 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
export class OpenaiRaw {
_id?: string;
chave?: string;
prompts?: string;
enabled?: boolean;
events?: string[];
}
const openaiSchema = new Schema<OpenaiRaw>({
_id: { type: String, _id: true },
chave: { type: String, required: true },
prompts: { type: String, required: false },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
});
export const OpenaiModel = dbserver?.model(OpenaiRaw.name, openaiSchema, 'openai');
export type IOpenaiModel = typeof OpenaiModel;

View File

@ -0,0 +1,153 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository';
import { IContactOpenaiModel, ContactOpenaiRaw, IOpenaiModel, OpenaiRaw } from '../models';
export class OpenaiRepository extends Repository {
constructor(
private readonly openaiModel: IOpenaiModel,
private readonly contactopenaiModel: IContactOpenaiModel,
private readonly configService: ConfigService
) {
super(configService);
}
private readonly logger = new Logger('OpenaiRepository');
public async create(data: OpenaiRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating openai');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving openai to db');
const insert = await this.openaiModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('openai saved to db: ' + insert.modifiedCount + ' openai');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving openai to store');
this.writeStore<OpenaiRaw>({
path: join(this.storePath, 'openai'),
fileName: instance,
data,
});
this.logger.verbose('openai saved to store in path: ' + join(this.storePath, 'openai') + '/' + instance);
this.logger.verbose('openai created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async createContact(data: ContactOpenaiRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating contact openai');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving openai to db');
var resultado = await this.openaiModel.findOne({ owner: instance, contact: data.contact });
if(!resultado){
const insert = await this.contactopenaiModel.insertMany({ ...data });
this.logger.verbose('openai saved to db: ' + insert.length + ' openai_contacts');
return { insertCount: insert.length };
}else{
const contacts = []
contacts[0] = {
updateOne: {
filter: { owner: data.owner, contact: data.contact },
update: { ...data },
upsert: true,
},
};
const { nModified } = await this.contactopenaiModel.bulkWrite(contacts);
this.logger.verbose('contacts updated in db: ' + nModified + ' contacts');
return { insertCount: nModified };
}
}
this.logger.verbose('saving openai to store');
this.writeStore<OpenaiRaw>({
path: join(this.storePath, 'openai_contact'),
fileName: instance,
data,
});
this.logger.verbose('openai contact saved to store in path: ' + join(this.storePath, 'openai_contact') + '/' + instance);
this.logger.verbose('openai contact created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<OpenaiRaw> {
try {
this.logger.verbose('finding openai');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding openai in db');
return await this.openaiModel.findOne({ _id: instance });
}
this.logger.verbose('finding openai in store');
return JSON.parse(
readFileSync(join(this.storePath, 'openai', instance + '.json'), {
encoding: 'utf-8',
}),
) as OpenaiRaw;
} catch (error) {
return {};
}
}
public async findContact(instance: string, contact: string): Promise<ContactOpenaiRaw> {
try {
this.logger.verbose('finding openai');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding openai in db');
return await this.contactopenaiModel.findOne({ owner: instance,contact: contact});
}
this.logger.verbose('finding openai in store');
return JSON.parse(
readFileSync(join(this.storePath, 'openai_contact', instance + '.json'), {
encoding: 'utf-8',
}),
) as ContactOpenaiRaw;
} catch (error) {
return ;
}
}
public async findContactAll(instance: string): Promise<any> {
try {
this.logger.verbose('finding openai');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding openai in db');
return await this.contactopenaiModel.find({ owner: instance });
}
this.logger.verbose('finding openai in store');
return JSON.parse(
readFileSync(join(this.storePath, 'openai_contact', instance + '.json'), {
encoding: 'utf-8',
}),
) as ContactOpenaiRaw;
} catch (error) {
return;
}
}
}

View File

@ -13,6 +13,7 @@ import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository';
import { ProxyRepository } from './proxy.repository';
import { RabbitmqRepository } from './rabbitmq.repository';
import { OpenaiRepository } from './openai.repository';
import { SettingsRepository } from './settings.repository';
import { SqsRepository } from './sqs.repository';
import { TypebotRepository } from './typebot.repository';
@ -29,6 +30,8 @@ export class RepositoryBroker {
public readonly settings: SettingsRepository,
public readonly websocket: WebsocketRepository,
public readonly rabbitmq: RabbitmqRepository,
public readonly openai: OpenaiRepository,
public readonly openai_contact: OpenaiRepository,
public readonly sqs: SqsRepository,
public readonly typebot: TypebotRepository,
public readonly proxy: ProxyRepository,
@ -63,9 +66,10 @@ export class RepositoryBroker {
const webhookDir = join(storePath, 'webhook');
const chatwootDir = join(storePath, 'chatwoot');
const settingsDir = join(storePath, 'settings');
const sqsDir = join(storePath, 'sqs');
const websocketDir = join(storePath, 'websocket');
const rabbitmqDir = join(storePath, 'rabbitmq');
const sqsDir = join(storePath, 'sqs');
const openaiDir = join(storePath, 'openai');
const typebotDir = join(storePath, 'typebot');
const proxyDir = join(storePath, 'proxy');
const chamaaiDir = join(storePath, 'chamaai');
@ -103,6 +107,12 @@ export class RepositoryBroker {
this.logger.verbose('creating settings dir: ' + settingsDir);
fs.mkdirSync(settingsDir, { recursive: true });
}
if (!fs.existsSync(sqsDir)) {
this.logger.verbose('creating sqs dir: ' + sqsDir);
fs.mkdirSync(sqsDir, { recursive: true });
}
if (!fs.existsSync(websocketDir)) {
this.logger.verbose('creating websocket dir: ' + websocketDir);
fs.mkdirSync(websocketDir, { recursive: true });
@ -111,9 +121,9 @@ export class RepositoryBroker {
this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir);
fs.mkdirSync(rabbitmqDir, { recursive: true });
}
if (!fs.existsSync(sqsDir)) {
this.logger.verbose('creating sqs dir: ' + sqsDir);
fs.mkdirSync(sqsDir, { recursive: true });
if (!fs.existsSync(openaiDir)) {
this.logger.verbose('creating openai dir: ' + openaiDir);
fs.mkdirSync(openaiDir, { recursive: true });
}
if (!fs.existsSync(typebotDir)) {
this.logger.verbose('creating typebot dir: ' + typebotDir);

View File

@ -11,6 +11,7 @@ import { GroupRouter } from './group.router';
import { InstanceRouter } from './instance.router';
import { ProxyRouter } from './proxy.router';
import { RabbitmqRouter } from './rabbitmq.router';
import { OpenaiRouter } from './openai.router';
import { MessageRouter } from './sendMessage.router';
import { SettingsRouter } from './settings.router';
import { SqsRouter } from './sqs.router';
@ -39,9 +40,9 @@ router
.get('/', (req, res) => {
res.status(HttpStatus.OK).json({
status: HttpStatus.OK,
message: 'Welcome to the Evolution API, it is working!',
version: packageJson.version,
documentation: `${req.protocol}://${req.get('host')}/docs`,
message: 'Api',
// version: packageJson.version,
// documentation: `${req.protocol}://${req.get('host')}/docs`,
});
})
.use('/instance', new InstanceRouter(configService, ...guards).router)
@ -54,7 +55,7 @@ router
.use('/settings', new SettingsRouter(...guards).router)
.use('/websocket', new WebsocketRouter(...guards).router)
.use('/rabbitmq', new RabbitmqRouter(...guards).router)
.use('/sqs', new SqsRouter(...guards).router)
.use('/openai', new OpenaiRouter(...guards).router)
.use('/typebot', new TypebotRouter(...guards).router)
.use('/proxy', new ProxyRouter(...guards).router)
.use('/chamaai', new ChamaaiRouter(...guards).router);

View File

@ -32,7 +32,15 @@ export class InstanceRouter extends RouterBroker {
execute: (instance) => instanceController.createInstance(instance),
});
return res.status(HttpStatus.CREATED).json(response);
if (req.query['qrcode']) {
return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response.qrcode.base64,
});
} else {
return res.status(HttpStatus.CREATED).json(response);
}
//return res.status(HttpStatus.CREATED).json(response);
})
.put(this.routerPath('restart'), ...guards, async (req, res) => {
logger.verbose('request received in restartInstance');
@ -50,6 +58,22 @@ export class InstanceRouter extends RouterBroker {
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('qr'), ...guards, async (req, res) => {
logger.verbose('request received in get qrCode');
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.qrInstance(instance),
});
return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response,
});
})
.get(this.routerPath('connect'), ...guards, async (req, res) => {
logger.verbose('request received in connectInstance');
logger.verbose('request body: ');

View File

@ -0,0 +1,86 @@
import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config';
import { instanceNameSchema, openaiSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { openaiController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('OpenaiRouter');
export class OpenaiRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) {
super();
this.router
.post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<OpenaiDto>({
request: req,
schema: openaiSchema,
ClassRef: OpenaiDto,
execute: (instance, data) => openaiController.createOpenai(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => openaiController.findOpenai(instance),
});
res.status(HttpStatus.OK).json(response);
})
.post(this.routerPath('contact'), ...guards, async (req, res) => {
logger.verbose('request received in setOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<ContactOpenaiDto>({
request: req,
schema: openaiSchema,
ClassRef: ContactOpenaiDto,
execute: (instance, data) => openaiController.createContactOpenai(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('findcontact'), ...guards, async (req, res) => {
logger.verbose('request received in findOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => openaiController.findContactOpenai(instance),
});
res.status(HttpStatus.OK).json(response);
});
}
public readonly router = Router();
}

Binary file not shown.

View File

@ -211,8 +211,8 @@ export class ChatwootService {
'123456',
inboxId,
false,
'EvolutionAPI',
'https://evolution-api.com/files/evolution-api-favicon.png',
'Nex API',
'https://nex-api.com.br/shared/themes/site/files/images/logo-purple.pnghttps://evolution-api.com/files/evolution-api-favicon.png',
)) as any);
if (!contact) {
@ -345,11 +345,17 @@ export class ChatwootService {
data,
});
// const contact = await client.contacts.update({
// accountId: this.provider.account_id,
// id,
// data,
// });
this.logger.verbose('contact updated');
return contact;
} catch (error) {
this.logger.error(error);
}
}
public async findContact(instance: InstanceDto, phoneNumber: string) {
@ -415,11 +421,18 @@ export class ChatwootService {
nameContact = !body.key.fromMe ? body.pushName : chatId;
this.logger.verbose('get inbox to instance: ' + instance.instanceName);
const filterInbox = await this.getInbox(instance);
if (!filterInbox) {
this.logger.warn('inbox not found');
return null;
var idInboxChat = 0;
if(this.provider?.id_inbox){
idInboxChat = this.provider?.id_inbox;
}else{
const filterInbox = await this.getInbox(instance);
if (!filterInbox) {
this.logger.warn('inbox not found');
return null;
}
idInboxChat = filterInbox.id;
}
if (isGroup) {
@ -443,11 +456,14 @@ export class ChatwootService {
avatar_url: picture_url.profilePictureUrl || null,
});
}
// if (!contact) {
// contact = await this.findContact(instance, chatId);
// }
} else {
await this.createContact(
instance,
body.key.participant.split('@')[0],
filterInbox.id,
idInboxChat,
false,
body.pushName,
picture_url.profilePictureUrl || null,
@ -473,7 +489,7 @@ export class ChatwootService {
contact = await this.createContact(
instance,
chatId,
filterInbox.id,
idInboxChat,
isGroup,
nameContact,
picture_url.profilePictureUrl || null,
@ -492,15 +508,12 @@ export class ChatwootService {
avatar_url: picture_url.profilePictureUrl || null,
});
}
if (!contact) {
contact = await this.findContact(instance, chatId);
}
} else {
const jid = isGroup ? null : body.key.remoteJid;
contact = await this.createContact(
instance,
chatId,
filterInbox.id,
idInboxChat,
isGroup,
nameContact,
picture_url.profilePictureUrl || null,
@ -532,7 +545,7 @@ export class ChatwootService {
if (contactConversations) {
let conversation: any;
if (this.provider.reopen_conversation) {
conversation = contactConversations.payload.find((conversation) => conversation.inbox_id == filterInbox.id);
conversation = contactConversations.payload.find((conversation) => conversation.inbox_id == idInboxChat);
if (this.provider.conversation_pending) {
await client.conversations.toggleStatus({
@ -545,7 +558,7 @@ export class ChatwootService {
}
} else {
conversation = contactConversations.payload.find(
(conversation) => conversation.status !== 'resolved' && conversation.inbox_id == filterInbox.id,
(conversation) => conversation.status !== 'resolved' && conversation.inbox_id == idInboxChat,
);
}
this.logger.verbose('return conversation if exists');
@ -559,7 +572,7 @@ export class ChatwootService {
this.logger.verbose('create conversation in chatwoot');
const data = {
contact_id: contactId.toString(),
inbox_id: filterInbox.id.toString(),
inbox_id: idInboxChat.toString(),
};
if (this.provider.conversation_pending) {
@ -685,20 +698,31 @@ export class ChatwootService {
return null;
}
this.logger.verbose('get inbox to instance: ' + instance.instanceName);
const filterInbox = await this.getInbox(instance);
if(this.provider?.id_inbox){
if (!filterInbox) {
this.logger.warn('inbox not found');
return null;
this.logger.verbose('find conversation in chatwoot');
var findConversation = await client.conversations.list({
accountId: this.provider.account_id,
inboxId: this.provider?.id_inbox,
});
}else{
this.logger.verbose('get inbox to instance: ' + instance.instanceName);
const filterInbox = await this.getInbox(instance);
if (!filterInbox) {
this.logger.warn('inbox not found');
return null;
}
this.logger.verbose('find conversation in chatwoot');
var findConversation = await client.conversations.list({
accountId: this.provider.account_id,
inboxId: filterInbox.id,
});
}
this.logger.verbose('find conversation in chatwoot');
const findConversation = await client.conversations.list({
accountId: this.provider.account_id,
inboxId: filterInbox.id,
});
if (!findConversation) {
this.logger.warn('conversation not found');
return null;
@ -805,20 +829,32 @@ export class ChatwootService {
return null;
}
this.logger.verbose('get inbox to instance: ' + instance.instanceName);
const filterInbox = await this.getInbox(instance);
if (!filterInbox) {
this.logger.warn('inbox not found');
return null;
if (this.provider?.id_inbox) {
this.logger.verbose('find conversation in chatwoot');
var findConversation = await client.conversations.list({
accountId: this.provider.account_id,
inboxId: this.provider?.id_inbox,
});
} else {
this.logger.verbose('get inbox to instance: ' + instance.instanceName);
const filterInbox = await this.getInbox(instance);
if (!filterInbox) {
this.logger.warn('inbox not found');
return null;
}
this.logger.verbose('find conversation in chatwoot');
var findConversation = await client.conversations.list({
accountId: this.provider.account_id,
inboxId: filterInbox.id,
});
}
this.logger.verbose('find conversation in chatwoot');
const findConversation = await client.conversations.list({
accountId: this.provider.account_id,
inboxId: filterInbox.id,
});
if (!findConversation) {
this.logger.warn('conversation not found');
return null;
@ -1053,6 +1089,29 @@ export class ChatwootService {
formatText = this.provider.sign_msg ? `*${senderName}:*\n${messageReceived}` : messageReceived;
}
if (body.content_attributes.in_reply_to){
var idResposta = (body.content_attributes.in_reply_to-1);
var conversationId = body.conversation.contact_inbox.inbox_id;
let config = {
method: 'get',
maxBodyLength: Infinity,
url: `${this.provider.url}/api/v1/accounts/${this.provider.account_id}/conversations/${conversationId}/messages?after=` + idResposta,
headers: {
'api_access_token': this.provider.token
}
};
try {
const { data } = await axios.request(config);
if (data.payload[0] && data.payload[0].id == body.content_attributes.in_reply_to) {
formatText = formatText +`\n\nResposta à: \n` + data.payload[0].content + ``;
}
this.logger.verbose('data sent');
} catch (error) {
this.logger.error(error);
}
}
for (const message of body.conversation.messages) {
this.logger.verbose('check if message is media');
if (message.attachments && message.attachments.length > 0) {
@ -1522,14 +1581,20 @@ export class ChatwootService {
if (event === 'status.instance') {
this.logger.verbose('event status.instance');
const data = body;
const inbox = await this.getInbox(instance);
if (!inbox) {
this.logger.warn('inbox not found');
return;
if (this.provider?.id_inbox) {
var msgStatus = `⚡️ Instance status ${this.provider?.name_inbox}: ${data.status}`;
} else {
const inbox = await this.getInbox(instance);
if (!inbox) {
this.logger.warn('inbox not found');
return;
}
var msgStatus = `⚡️ Instance status ${inbox.name}: ${data.status}`;
}
const msgStatus = `⚡️ Instance status ${inbox.name}: ${data.status}`;
this.logger.verbose('send message to chatwoot');
await this.createBotMessage(instance, msgStatus, 'incoming');

View File

@ -24,6 +24,7 @@ import {
TypebotModel,
WebhookModel,
WebsocketModel,
} from '../models';
import { RepositoryBroker } from '../repository/repository.manager';
import { WAStartupService } from './whatsapp.service';
@ -45,7 +46,10 @@ export class WAMonitoringService {
Object.assign(this.redis, configService.get<Redis>('REDIS'));
this.dbInstance = this.db.ENABLED
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
? this.repository.dbServer?.db(
this.db.CONNECTION.DB_PREFIX_NAME +
this.db.CONNECTION.DB_PREFIX_FINAL_NAME
)
: undefined;
}
@ -239,19 +243,21 @@ export class WAMonitoringService {
this.logger.verbose('cleaning store database instance: ' + instanceName);
await AuthModel.deleteMany({ owner: instanceName });
await ChatModel.deleteMany({ owner: instanceName });
await ContactModel.deleteMany({ owner: instanceName });
await MessageUpModel.deleteMany({ owner: instanceName });
await MessageModel.deleteMany({ owner: instanceName });
await AuthModel.deleteMany({ _id: instanceName });
await MessageUpModel.deleteMany({ owner: instanceName });
await WebhookModel.deleteMany({ _id: instanceName });
await ChatwootModel.deleteMany({ _id: instanceName });
await ChamaaiModel.deleteMany({ _id: instanceName });
await ProxyModel.deleteMany({ _id: instanceName });
await RabbitmqModel.deleteMany({ _id: instanceName });
await TypebotModel.deleteMany({ _id: instanceName });
await WebsocketModel.deleteMany({ _id: instanceName });
await SettingsModel.deleteMany({ _id: instanceName });
return;
@ -277,6 +283,7 @@ export class WAMonitoringService {
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
instance.instanceName = name;
this.logger.verbose('Instance loaded: ' + name);
await instance.connectToWhatsapp();
this.logger.verbose('connectToWhatsapp: ' + name);

View File

@ -0,0 +1,57 @@
import { Logger } from '../../config/logger.config';
import { initQueues } from '../../libs/amqp.server';
import { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { OpenaiRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class OpenaiService {
constructor(private readonly waMonitor: WAMonitoringService) {}
private readonly logger = new Logger(OpenaiService.name);
public create(instance: InstanceDto, data: OpenaiDto) {
this.logger.verbose('create openai: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setOpenai(data);
return { openai: { ...instance, openai: data } };
}
public async find(instance: InstanceDto): Promise<OpenaiRaw> {
try {
this.logger.verbose('find openai: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findOpenai();
if (Object.keys(result).length === 0) {
throw new Error('openai not found');
}
return result;
} catch (error) {
return { chave: '', enabled: false, events: [] };
}
}
public createContact(instance: InstanceDto, data: ContactOpenaiDto) {
this.logger.verbose('create openai: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setContactOpenai(data);
return { openai: { ...instance, openai: data } };
}
public async findContact(instance: InstanceDto): Promise<OpenaiRaw> {
try {
this.logger.verbose('find openai: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findContactOpenai();
if (Object.keys(result).length === 0) {
throw new Error('openai not found');
}
return result;
} catch (error) {
return { chave: '', enabled: false, events: [] };
}
}
}

View File

@ -6,7 +6,7 @@ import { SqsRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class SqsService {
constructor(private readonly waMonitor: WAMonitoringService) {}
constructor(private readonly waMonitor: WAMonitoringService) { }
private readonly logger = new Logger(SqsService.name);

View File

@ -9,7 +9,8 @@ import { Events } from '../types/wa.types';
import { WAMonitoringService } from './monitor.service';
export class TypebotService {
constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {}
//constructor(private readonly waMonitor: WAMonitoringService) {}
constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) { }
private readonly logger = new Logger(TypebotService.name);
@ -177,6 +178,8 @@ export class TypebotService {
};
try {
//const request = await axios.post(data.url + '/api/v1/sendMessage', reqData);
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData);
@ -267,6 +270,8 @@ export class TypebotService {
};
try {
//const request = await axios.post(data.url + '/api/v1/sendMessage', reqData);
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData);
@ -386,7 +391,7 @@ export class TypebotService {
}
if (element.underline) {
text = `*${text}*`;
text = `~${text}~`;
}
if (element.url) {
@ -571,6 +576,7 @@ export class TypebotService {
};
try {
//const request = await axios.post(url + '/api/v1/sendMessage', reqData);
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
const request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData);
@ -658,9 +664,9 @@ export class TypebotService {
let request: any;
try {
//request = await axios.post(url + '/api/v1/sendMessage', reqData);
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
request = await axios.post(`${data.url}/api/${version}/sendMessage`, reqData);
await this.sendWAMessage(
instance,
remoteJid,
@ -739,6 +745,7 @@ export class TypebotService {
sessionId: session.sessionId.split('-')[1],
};
//const request = await axios.post(url + '/api/v1/sendMessage', reqData);
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
const request = await axios.post(`${url}/api/${version}/sendMessage`, reqData);
@ -755,5 +762,249 @@ export class TypebotService {
this.logger.error(error);
return;
}
/*
if (session && expire && expire > 0) {
const now = Date.now();
const diff = now - session.updateAt;
const diffInMinutes = Math.floor(diff / 1000 / 60);
if (diffInMinutes > expire) {
//sessions.splice(sessions.indexOf(session), 1);
const newSessions = await this.clearSessions(instance, remoteJid);
const data = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
//sessions: sessions,
sessions: newSessions,
remoteJid: remoteJid,
pushName: msg.pushName,
});
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
if (data.messages.length === 0) {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
sessions.splice(sessions.indexOf(session), 1);
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions,
};
this.create(instance, typebotData);
return;
}
const reqData = {
message: content,
sessionId: data.sessionId,
};
const request = await axios.post(url + '/api/v1/sendMessage', reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
}
return;
}
}
if (session && session.status !== 'opened') {
return;
}
if (!session) {
const data = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: sessions,
remoteJid: remoteJid,
pushName: msg.pushName,
});
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
if (data.messages.length === 0) {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
sessions.splice(sessions.indexOf(session), 1);
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions,
};
this.create(instance, typebotData);
return;
}
const reqData = {
message: content,
sessionId: data.sessionId,
};
const request = await axios.post(url + '/api/v1/sendMessage', reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
}
return;
}
sessions.map((session) => {
if (session.remoteJid === remoteJid) {
session.updateAt = Date.now();
}
});
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions,
};
this.create(instance, typebotData);
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
sessions.splice(sessions.indexOf(session), 1);
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions,
};
this.create(instance, typebotData);
return;
}
const reqData = {
message: content,
sessionId: session.sessionId.split('-')[1],
};
const request = await axios.post(url + '/api/v1/sendMessage', reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
return;
*/
}
}

View File

@ -115,7 +115,8 @@ import {
SendTextDto,
StatusMessage,
} from '../dto/sendMessage.dto';
import { ChamaaiRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, SqsRaw, TypebotRaw } from '../models';
import { ChamaaiRaw, ProxyRaw, RabbitmqRaw, OpenaiRaw, SettingsRaw, SqsRaw, TypebotRaw, ContactOpenaiRaw } from '../models';
import { ChatRaw } from '../models/chat.model';
import { ChatwootRaw } from '../models/chatwoot.model';
import { ContactRaw } from '../models/contact.model';
@ -130,7 +131,51 @@ import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types'
import { waMonitor } from '../whatsapp.module';
import { ChamaaiService } from './chamaai.service';
import { ChatwootService } from './chatwoot.service';
//import { SocksProxyAgent } from './socks-proxy-agent';
import { TypebotService } from './typebot.service';
import { Configuration, OpenAIApi, ChatCompletionRequestMessage } from "openai"
import { openai, OpenAIService } from "../../libs/openai"
import { redis } from "../../libs/redis"
import { initPrompt } from "../../utils/initPrompt"
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
// https://wa.me/+5512982754592
interface CustomerChat {
status?: "open" | "closed"
orderCode: string
chatAt: string
customer: {
name: string
phone: string
}
messages: ChatCompletionRequestMessage[]
orderSummary?: string
}
async function completion(
apiKey: string,
messages: ChatCompletionRequestMessage[]
): Promise<string | undefined> {
const configuration = new Configuration({
apiKey: apiKey,
})
const openai = new OpenAIApi(configuration)
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
temperature: 0,
max_tokens: 256,
messages,
})
return completion.data.choices[0].message?.content
}
export class WAStartupService {
constructor(
private readonly configService: ConfigService,
@ -151,6 +196,7 @@ export class WAStartupService {
private readonly localSettings: wa.LocalSettings = {};
private readonly localWebsocket: wa.LocalWebsocket = {};
private readonly localRabbitmq: wa.LocalRabbitmq = {};
private readonly localOpenai: wa.LocalOpenai = {};
private readonly localSqs: wa.LocalSqs = {};
public readonly localTypebot: wa.LocalTypebot = {};
private readonly localProxy: wa.LocalProxy = {};
@ -166,6 +212,7 @@ export class WAStartupService {
private chatwootService = new ChatwootService(waMonitor, this.configService);
//private typebotService = new TypebotService(waMonitor);
private typebotService = new TypebotService(waMonitor, this.configService);
private chamaaiService = new ChamaaiService(waMonitor, this.configService);
@ -216,7 +263,10 @@ export class WAStartupService {
this.logger.verbose('Database enabled, trying to get from database');
const collection = dbserver
.getClient()
.db(this.configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
.db(
this.configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME +
this.configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_FINAL_NAME
)
.collection(this.instanceName);
const data = await collection.findOne({ _id: 'creds' });
if (data) {
@ -465,6 +515,72 @@ export class WAStartupService {
return data;
}
private async loadOpenai() {
this.logger.verbose('Loading openai');
const data = await this.repository.openai.find(this.instanceName);
this.localOpenai.chave = data?.chave;
this.logger.verbose(`Openai chave: ${this.localOpenai.chave}`);
this.localOpenai.enabled = data?.enabled;
this.logger.verbose(`Openai enabled: ${this.localOpenai.enabled}`);
this.localOpenai.events = data?.events;
this.logger.verbose(`Openai events: ${this.localOpenai.events}`);
this.logger.verbose('Openai loaded');
}
public async setOpenai(data: OpenaiRaw) {
this.logger.verbose('Setting openai');
await this.repository.openai.create(data, this.instanceName);
this.logger.verbose(`Openai events: ${data.events}`);
Object.assign(this.localOpenai, data);
this.logger.verbose('Openai set');
}
public async setContactOpenai(data: ContactOpenaiRaw) {
this.logger.verbose('Setting openai');
await this.repository.openai.createContact(data, this.instanceName);
this.logger.verbose(`Openai events: ${data.enabled}`);
Object.assign(this.localOpenai, data);
this.logger.verbose('Openai set');
}
public async findContactOpenai() {
this.logger.verbose('Finding openai');
const data = await this.repository.openai.findContactAll(this.instanceName);
if (!data) {
this.logger.verbose('Openai not found');
throw new NotFoundException('Openai not found');
}
return data;
}
public async findOpenai() {
this.logger.verbose('Finding openai');
const data = await this.repository.openai.find(this.instanceName);
if (!data) {
this.logger.verbose('Openai not found');
throw new NotFoundException('Openai not found');
}
this.logger.verbose(`Openai events: ${data.events}`);
return data;
}
public async removeOpenaiQueues() {
this.logger.verbose('Removing openai');
if (this.localOpenai.enabled) {
removeQueues(this.instanceName, this.localOpenai.events);
}
}
private async loadRabbitmq() {
this.logger.verbose('Loading rabbitmq');
const data = await this.repository.rabbitmq.find(this.instanceName);
@ -507,6 +623,7 @@ export class WAStartupService {
}
}
private async loadSqs() {
this.logger.verbose('Loading sqs');
const data = await this.repository.sqs.find(this.instanceName);
@ -626,10 +743,11 @@ export class WAStartupService {
this.logger.verbose(`Proxy proxy: ${data.proxy}`);
Object.assign(this.localProxy, data);
this.logger.verbose('Proxy set');
//this.reloadConnection();
if (reload) {
this.reloadConnection();
}
//this.client?.ws?.close();
}
public async findProxy() {
@ -768,6 +886,7 @@ export class WAStartupService {
}
}
if (this.localSqs.enabled) {
const sqs = getSQS();
@ -1299,14 +1418,18 @@ export class WAStartupService {
let options;
if (this.localProxy.enabled) {
this.logger.info('Proxy enabled: ' + this.localProxy.proxy);
this.logger.verbose('Proxy enabled');
// options = {
// agent: new ProxyAgent(this.localProxy.proxy as any),
// fetchAgent: new ProxyAgent(this.localProxy.proxy as any),
// };
if (this.localProxy.proxy.includes('proxyscrape')) {
const response = await axios.get(this.localProxy.proxy);
const text = response.data;
const proxyUrls = text.split('\r\n');
const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length));
const proxyUrl = 'http://' + proxyUrls[rand];
console.log(proxyUrl);
options = {
agent: new ProxyAgent(proxyUrl as any),
};
@ -1656,9 +1779,132 @@ export class WAStartupService {
let messageRaw: MessageRaw;
if (received.key.remoteJid.includes('@g.us')) {
}else{
if (messages[0].message.conversation){
const openai = await this.repository.openai_contact.find(this.instance.name);
if (openai && openai?.chave && openai?.enabled == true && openai?.prompts){
const customerPhone = `+${received.key.remoteJid.replace("@s.whatsapp.net", "")}`
const customerName = messages[0].pushName
const contactOpenaiC = await this.repository.openai_contact.findContact(this.instance.name, customerPhone);
if (!contactOpenaiC){
var data = new ContactOpenaiDto;
data.contact = customerPhone;
data.enabled = true;
data.owner = this.instance.name;
await this.repository.openai_contact.createContact(data, this.instance.name)
}
const contactOpenai = await this.repository.openai_contact.findContact(this.instance.name, customerPhone);
if(contactOpenai?.enabled == true){
const customerKey = `instancia:${this.instance.name}customer:${customerPhone}:chat`
const orderCode = `#sk-${("00000" + Math.random()).slice(-5)}`
const lastChat = JSON.parse((await redis.get(customerKey)) || "{}")
var storeName = 'Nome';
const customerChat: CustomerChat =
lastChat?.status === "open"
? (lastChat as CustomerChat)
: {
status: "open",
orderCode,
chatAt: new Date().toISOString(),
customer: {
name: customerName,
phone: customerPhone,
},
messages: [
{
role: "system",
content: initPrompt(storeName, orderCode, openai?.prompts),
},
],
orderSummary: "",
}
this.logger.verbose(customerPhone+" 👤 "+ messages[0].message.conversation)
customerChat.messages.push({
role: "user",
content: messages[0].message.conversation,
})
const content =
(await completion(openai.chave,
customerChat.messages)) || "Não entendi..."
customerChat.messages.push({
role: "assistant",
content,
})
this.logger.verbose(customerPhone + " 🤖 "+ content)
var dadosMessage = {
textMessage: received.message.conversation,
};
let mentions: string[];
const linkPreview = true;
let quoted: WAMessage;
const option = {
quoted,
};
this.client.sendMessage(
received.key.remoteJid,
{
text: content,
mentions,
linkPreview: linkPreview,
} as unknown as AnyMessageContent,
option as unknown as MiscMessageGenerationOptions,
);
if (
customerChat.status === "open" &&
content.match(customerChat.orderCode)
) {
customerChat.status = "closed"
customerChat.messages.push({
role: "user",
content:
"Gere um resumo de pedido para registro no sistema da pizzaria, quem está solicitando é um robô.",
})
const content =
(await completion(openai.chave,customerChat.messages)) || "Não entendi..."
this.logger.verbose(customerPhone + " 📦 "+ content)
customerChat.orderSummary = content
}
redis.set(customerKey, JSON.stringify(customerChat))
this.logger.verbose(received);
}
}
}
}
if (
(this.localWebhook.webhook_base64 === true && received?.message.documentMessage) ||
received?.message?.imageMessage
received?.message.imageMessage
) {
const buffer = await downloadMediaMessage(
{ key: received.key, message: received?.message },
@ -1714,6 +1960,7 @@ export class WAStartupService {
);
}
//if (this.localTypebot.enabled) {
const typebotSessionRemoteJid = this.localTypebot.sessions?.find(
(session) => session.remoteJid === received.key.remoteJid,
);
@ -2060,7 +2307,9 @@ export class WAStartupService {
private createJid(number: string): string {
this.logger.verbose('Creating jid with number: ' + number);
//if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) {
if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) {
//this.logger.verbose('Number already contains @g.us or @s.whatsapp.net');
this.logger.verbose('Number already contains @g.us or @s.whatsapp.net or @lid');
return number;
}
@ -2285,6 +2534,7 @@ export class WAStartupService {
!message['conversation'] &&
sender !== 'status@broadcast'
) {
if (message['reactionMessage']) {
this.logger.verbose('Sending reaction');
return await this.client.sendMessage(
@ -2314,6 +2564,7 @@ export class WAStartupService {
);
}
}
if (message['conversation']) {
this.logger.verbose('Sending message');
return await this.client.sendMessage(
@ -2573,9 +2824,15 @@ export class WAStartupService {
let mimetype: string;
// if (isURL(mediaMessage.media)) {
// mimetype = getMIMEType(mediaMessage.media);
// } else {
// mimetype = getMIMEType(mediaMessage.fileName);
// }
if (mediaMessage.mimetype) {
mimetype = mediaMessage.mimetype;
} else {
}else{
if (isURL(mediaMessage.media)) {
mimetype = getMIMEType(mediaMessage.media);
} else {
@ -3229,6 +3486,7 @@ export class WAStartupService {
await this.client.updateGroupsAddPrivacy(settings.privacySettings.groupadd);
this.logger.verbose('Groups add privacy updated');
//this.client?.ws?.close();
this.reloadConnection();
return {

View File

@ -89,6 +89,12 @@ export declare namespace wa {
events?: string[];
};
export type LocalOpenai = {
chave?: string;
enabled?: boolean;
events?: string[];
};
type Session = {
remoteJid?: string;
sessionId?: string;

View File

@ -10,6 +10,7 @@ import { GroupController } from './controllers/group.controller';
import { InstanceController } from './controllers/instance.controller';
import { ProxyController } from './controllers/proxy.controller';
import { RabbitmqController } from './controllers/rabbitmq.controller';
import { OpenaiController } from './controllers/openai.controller';
import { SendMessageController } from './controllers/sendMessage.controller';
import { SettingsController } from './controllers/settings.controller';
import { SqsController } from './controllers/sqs.controller';
@ -27,6 +28,8 @@ import {
MessageUpModel,
ProxyModel,
RabbitmqModel,
OpenaiModel,
ContactOpenaiModel,
SettingsModel,
SqsModel,
TypebotModel,
@ -42,6 +45,7 @@ import { MessageRepository } from './repository/message.repository';
import { MessageUpRepository } from './repository/messageUp.repository';
import { ProxyRepository } from './repository/proxy.repository';
import { RabbitmqRepository } from './repository/rabbitmq.repository';
import { OpenaiRepository } from './repository/openai.repository';
import { RepositoryBroker } from './repository/repository.manager';
import { SettingsRepository } from './repository/settings.repository';
import { SqsRepository } from './repository/sqs.repository';
@ -54,6 +58,7 @@ import { ChatwootService } from './services/chatwoot.service';
import { WAMonitoringService } from './services/monitor.service';
import { ProxyService } from './services/proxy.service';
import { RabbitmqService } from './services/rabbitmq.service';
import { OpenaiService } from './services/openai.service';
import { SettingsService } from './services/settings.service';
import { SqsService } from './services/sqs.service';
import { TypebotService } from './services/typebot.service';
@ -72,9 +77,10 @@ const websocketRepository = new WebsocketRepository(WebsocketModel, configServic
const proxyRepository = new ProxyRepository(ProxyModel, configService);
const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService);
const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService);
const sqsRepository = new SqsRepository(SqsModel, configService);
const openaiRepository = new OpenaiRepository(OpenaiModel,ContactOpenaiModel, configService);
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
const settingsRepository = new SettingsRepository(SettingsModel, configService);
const sqsRepository = new SqsRepository(SqsModel, configService);
const authRepository = new AuthRepository(AuthModel, configService);
export const repository = new RepositoryBroker(
@ -87,6 +93,8 @@ export const repository = new RepositoryBroker(
settingsRepository,
websocketRepository,
rabbitmqRepository,
openaiRepository,
openaiRepository,
sqsRepository,
typebotRepository,
proxyRepository,
@ -130,6 +138,10 @@ const sqsService = new SqsService(waMonitor);
export const sqsController = new SqsController(sqsService);
const openaiService = new OpenaiService(waMonitor);
export const openaiController = new OpenaiController(openaiService);
const chatwootService = new ChatwootService(waMonitor, configService);
export const chatwootController = new ChatwootController(chatwootService, configService);
@ -149,6 +161,7 @@ export const instanceController = new InstanceController(
settingsService,
websocketService,
rabbitmqService,
openaiService,
proxyService,
sqsService,
typebotService,