Merge branch 'develop' of https://github.com/codephix/evolution-api into develop

This commit is contained in:
CodePhix 2023-12-06 20:19:10 -03:00
commit 760b3f5fc5
45 changed files with 14278 additions and 14267 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

28
src/config.ts Executable file → Normal file
View File

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

696
src/config/env.config.ts Executable file → Normal file
View File

@ -1,348 +1,348 @@
import { isBooleanString } from 'class-validator'; import { isBooleanString } from 'class-validator';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { load } from 'js-yaml'; import { load } from 'js-yaml';
import { join } from 'path'; import { join } from 'path';
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string }; export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string };
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE'; export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
export type Cors = { export type Cors = {
ORIGIN: string[]; ORIGIN: string[];
METHODS: HttpMethods[]; METHODS: HttpMethods[];
CREDENTIALS: boolean; CREDENTIALS: boolean;
}; };
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS'; export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS';
export type Log = { export type Log = {
LEVEL: LogLevel[]; LEVEL: LogLevel[];
COLOR: boolean; COLOR: boolean;
BAILEYS: LogBaileys; BAILEYS: LogBaileys;
}; };
export type SaveData = { export type SaveData = {
INSTANCE: boolean; INSTANCE: boolean;
NEW_MESSAGE: boolean; NEW_MESSAGE: boolean;
MESSAGE_UPDATE: boolean; MESSAGE_UPDATE: boolean;
CONTACTS: boolean; CONTACTS: boolean;
CHATS: boolean; CHATS: boolean;
}; };
export type StoreConf = { export type StoreConf = {
MESSAGES: boolean; MESSAGES: boolean;
MESSAGE_UP: boolean; MESSAGE_UP: boolean;
CONTACTS: boolean; CONTACTS: boolean;
CHATS: boolean; CHATS: boolean;
}; };
export type CleanStoreConf = { export type CleanStoreConf = {
CLEANING_INTERVAL: number; CLEANING_INTERVAL: number;
MESSAGES: boolean; MESSAGES: boolean;
MESSAGE_UP: boolean; MESSAGE_UP: boolean;
CONTACTS: boolean; CONTACTS: boolean;
CHATS: boolean; CHATS: boolean;
}; };
export type DBConnection = { export type DBConnection = {
URI: string; URI: string;
DB_PREFIX_NAME: string; DB_PREFIX_NAME: string;
DB_PREFIX_FINAL_NAME: string; DB_PREFIX_FINAL_NAME: string;
}; };
export type Database = { export type Database = {
CONNECTION: DBConnection; CONNECTION: DBConnection;
ENABLED: boolean; ENABLED: boolean;
SAVE_DATA: SaveData; SAVE_DATA: SaveData;
}; };
export type Redis = { export type Redis = {
ENABLED: boolean; ENABLED: boolean;
URI: string; URI: string;
PREFIX_KEY: string; PREFIX_KEY: string;
}; };
export type Rabbitmq = { export type Rabbitmq = {
ENABLED: boolean; ENABLED: boolean;
URI: string; URI: string;
}; };
export type Sqs = { export type Sqs = {
ENABLED: boolean; ENABLED: boolean;
ACCESS_KEY_ID: string; ACCESS_KEY_ID: string;
SECRET_ACCESS_KEY: string; SECRET_ACCESS_KEY: string;
ACCOUNT_ID: string; ACCOUNT_ID: string;
REGION: string; REGION: string;
}; };
export type Openai = { export type Openai = {
CHAVE: string; CHAVE: string;
ENABLED: boolean; ENABLED: boolean;
URI: string; URI: string;
}; };
export type Websocket = { export type Websocket = {
ENABLED: boolean; ENABLED: boolean;
}; };
export type Chatwoot = { export type Chatwoot = {
USE_REPLY_ID: boolean; USE_REPLY_ID: boolean;
}; };
export type EventsWebhook = { export type EventsWebhook = {
APPLICATION_STARTUP: boolean; APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean; QRCODE_UPDATED: boolean;
MESSAGES_SET: boolean; MESSAGES_SET: boolean;
MESSAGES_UPSERT: boolean; MESSAGES_UPSERT: boolean;
MESSAGES_UPDATE: boolean; MESSAGES_UPDATE: boolean;
MESSAGES_DELETE: boolean; MESSAGES_DELETE: boolean;
SEND_MESSAGE: boolean; SEND_MESSAGE: boolean;
CONTACTS_SET: boolean; CONTACTS_SET: boolean;
CONTACTS_UPDATE: boolean; CONTACTS_UPDATE: boolean;
CONTACTS_UPSERT: boolean; CONTACTS_UPSERT: boolean;
PRESENCE_UPDATE: boolean; PRESENCE_UPDATE: boolean;
CHATS_SET: boolean; CHATS_SET: boolean;
CHATS_UPDATE: boolean; CHATS_UPDATE: boolean;
CHATS_DELETE: boolean; CHATS_DELETE: boolean;
CHATS_UPSERT: boolean; CHATS_UPSERT: boolean;
CONNECTION_UPDATE: boolean; CONNECTION_UPDATE: boolean;
GROUPS_UPSERT: boolean; GROUPS_UPSERT: boolean;
GROUP_UPDATE: boolean; GROUP_UPDATE: boolean;
GROUP_PARTICIPANTS_UPDATE: boolean; GROUP_PARTICIPANTS_UPDATE: boolean;
CALL: boolean; CALL: boolean;
NEW_JWT_TOKEN: boolean; NEW_JWT_TOKEN: boolean;
TYPEBOT_START: boolean; TYPEBOT_START: boolean;
TYPEBOT_CHANGE_STATUS: boolean; TYPEBOT_CHANGE_STATUS: boolean;
CHAMA_AI_ACTION: boolean; CHAMA_AI_ACTION: boolean;
ERRORS: boolean; ERRORS: boolean;
ERRORS_WEBHOOK: string; ERRORS_WEBHOOK: string;
}; };
export type ApiKey = { KEY: string }; export type ApiKey = { KEY: string };
export type Jwt = { EXPIRIN_IN: number; SECRET: string }; export type Jwt = { EXPIRIN_IN: number; SECRET: string };
export type Auth = { export type Auth = {
API_KEY: ApiKey; API_KEY: ApiKey;
EXPOSE_IN_FETCH_INSTANCES: boolean; EXPOSE_IN_FETCH_INSTANCES: boolean;
JWT: Jwt; JWT: Jwt;
TYPE: 'jwt' | 'apikey'; TYPE: 'jwt' | 'apikey';
}; };
export type DelInstance = number | boolean; export type DelInstance = number | boolean;
export type GlobalWebhook = { export type GlobalWebhook = {
URL: string; URL: string;
ENABLED: boolean; ENABLED: boolean;
WEBHOOK_BY_EVENTS: boolean; WEBHOOK_BY_EVENTS: boolean;
}; };
export type SslConf = { PRIVKEY: string; FULLCHAIN: string }; export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook }; export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
export type ConfigSessionPhone = { CLIENT: string; NAME: string }; export type ConfigSessionPhone = { CLIENT: string; NAME: string };
export type QrCode = { LIMIT: number; COLOR: string }; export type QrCode = { LIMIT: number; COLOR: string };
export type Typebot = { API_VERSION: string }; export type Typebot = { API_VERSION: string };
export type Production = boolean; export type Production = boolean;
export interface Env { export interface Env {
SERVER: HttpServer; SERVER: HttpServer;
CORS: Cors; CORS: Cors;
SSL_CONF: SslConf; SSL_CONF: SslConf;
STORE: StoreConf; STORE: StoreConf;
CLEAN_STORE: CleanStoreConf; CLEAN_STORE: CleanStoreConf;
DATABASE: Database; DATABASE: Database;
REDIS: Redis; REDIS: Redis;
RABBITMQ: Rabbitmq; RABBITMQ: Rabbitmq;
SQS: Sqs; SQS: Sqs;
OPENAI: Openai; OPENAI: Openai;
WEBSOCKET: Websocket; WEBSOCKET: Websocket;
LOG: Log; LOG: Log;
DEL_INSTANCE: DelInstance; DEL_INSTANCE: DelInstance;
WEBHOOK: Webhook; WEBHOOK: Webhook;
CONFIG_SESSION_PHONE: ConfigSessionPhone; CONFIG_SESSION_PHONE: ConfigSessionPhone;
QRCODE: QrCode; QRCODE: QrCode;
TYPEBOT: Typebot; TYPEBOT: Typebot;
AUTHENTICATION: Auth; AUTHENTICATION: Auth;
PRODUCTION?: Production; PRODUCTION?: Production;
CHATWOOT?: Chatwoot; CHATWOOT?: Chatwoot;
} }
export type Key = keyof Env; export type Key = keyof Env;
export class ConfigService { export class ConfigService {
constructor() { constructor() {
this.loadEnv(); this.loadEnv();
} }
private env: Env; private env: Env;
public get<T = any>(key: Key) { public get<T = any>(key: Key) {
return this.env[key] as T; return this.env[key] as T;
} }
private loadEnv() { private loadEnv() {
this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess(); this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD'; this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
if (process.env?.DOCKER_ENV === 'true') { if (process.env?.DOCKER_ENV === 'true') {
this.env.SERVER.TYPE = 'http'; this.env.SERVER.TYPE = 'http';
this.env.SERVER.PORT = 8080; this.env.SERVER.PORT = 8080;
} }
} }
private envYaml(): Env { private envYaml(): Env {
return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env; return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env;
} }
private envProcess(): Env { private envProcess(): Env {
return { return {
SERVER: { SERVER: {
TYPE: process.env.SERVER_TYPE as 'http' | 'https', TYPE: process.env.SERVER_TYPE as 'http' | 'https',
PORT: Number.parseInt(process.env.SERVER_PORT) || 8080, PORT: Number.parseInt(process.env.SERVER_PORT) || 8080,
URL: process.env.SERVER_URL, URL: process.env.SERVER_URL,
}, },
CORS: { CORS: {
ORIGIN: process.env.CORS_ORIGIN.split(',') || ['*'], ORIGIN: process.env.CORS_ORIGIN.split(',') || ['*'],
METHODS: (process.env.CORS_METHODS.split(',') as HttpMethods[]) || ['POST', 'GET', 'PUT', 'DELETE'], METHODS: (process.env.CORS_METHODS.split(',') as HttpMethods[]) || ['POST', 'GET', 'PUT', 'DELETE'],
CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true', CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true',
}, },
SSL_CONF: { SSL_CONF: {
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '', PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '', FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '',
}, },
STORE: { STORE: {
MESSAGES: process.env?.STORE_MESSAGES === 'true', MESSAGES: process.env?.STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true', MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.STORE_CONTACTS === 'true', CONTACTS: process.env?.STORE_CONTACTS === 'true',
CHATS: process.env?.STORE_CHATS === 'true', CHATS: process.env?.STORE_CHATS === 'true',
}, },
CLEAN_STORE: { CLEAN_STORE: {
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL) CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL) ? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL)
: 7200, : 7200,
MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true', MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true', MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true', CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true',
CHATS: process.env?.CLEAN_STORE_CHATS === 'true', CHATS: process.env?.CLEAN_STORE_CHATS === 'true',
}, },
DATABASE: { DATABASE: {
CONNECTION: { CONNECTION: {
URI: process.env.DATABASE_CONNECTION_URI || '', URI: process.env.DATABASE_CONNECTION_URI || '',
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution', DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution',
DB_PREFIX_FINAL_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_FINAL_NAME || '-api', DB_PREFIX_FINAL_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_FINAL_NAME || '-api',
}, },
ENABLED: process.env?.DATABASE_ENABLED === 'true', ENABLED: process.env?.DATABASE_ENABLED === 'true',
SAVE_DATA: { SAVE_DATA: {
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true', INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true', NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true', MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true', CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true', CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
}, },
}, },
REDIS: { REDIS: {
ENABLED: process.env?.REDIS_ENABLED === 'true', ENABLED: process.env?.REDIS_ENABLED === 'true',
URI: process.env.REDIS_URI || '', URI: process.env.REDIS_URI || '',
PREFIX_KEY: process.env.REDIS_PREFIX_KEY || 'evolution', PREFIX_KEY: process.env.REDIS_PREFIX_KEY || 'evolution',
}, },
RABBITMQ: { RABBITMQ: {
ENABLED: process.env?.RABBITMQ_ENABLED === 'true', ENABLED: process.env?.RABBITMQ_ENABLED === 'true',
URI: process.env.RABBITMQ_URI || '', URI: process.env.RABBITMQ_URI || '',
}, },
SQS: { SQS: {
ENABLED: process.env?.SQS_ENABLED === 'true', ENABLED: process.env?.SQS_ENABLED === 'true',
ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '', ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '',
SECRET_ACCESS_KEY: process.env.SQS_SECRET_ACCESS_KEY || '', SECRET_ACCESS_KEY: process.env.SQS_SECRET_ACCESS_KEY || '',
ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '', ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '',
REGION: process.env.SQS_REGION || '', REGION: process.env.SQS_REGION || '',
}, },
OPENAI: { OPENAI: {
CHAVE: process.env?.OPENAI_ENABLED || '', CHAVE: process.env?.OPENAI_ENABLED || '',
ENABLED: process.env?.OPENAI_ENABLED === 'true', ENABLED: process.env?.OPENAI_ENABLED === 'true',
URI: process.env.OPENAI_URI || '', URI: process.env.OPENAI_URI || '',
}, },
WEBSOCKET: { WEBSOCKET: {
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true', ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
}, },
LOG: { LOG: {
LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [ LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [
'ERROR', 'ERROR',
'WARN', 'WARN',
'DEBUG', 'DEBUG',
'INFO', 'INFO',
'LOG', 'LOG',
'VERBOSE', 'VERBOSE',
'DARK', 'DARK',
'WEBHOOKS', 'WEBHOOKS',
], ],
COLOR: process.env?.LOG_COLOR === 'true', COLOR: process.env?.LOG_COLOR === 'true',
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error', BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
}, },
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE) DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
? process.env.DEL_INSTANCE === 'true' ? process.env.DEL_INSTANCE === 'true'
: Number.parseInt(process.env.DEL_INSTANCE) || false, : Number.parseInt(process.env.DEL_INSTANCE) || false,
WEBHOOK: { WEBHOOK: {
GLOBAL: { GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL || '', URL: process.env?.WEBHOOK_GLOBAL_URL || '',
ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true', ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true',
WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true', WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true',
}, },
EVENTS: { EVENTS: {
APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true', APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true',
QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true', QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true',
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true', MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true', MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true',
MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true', MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true',
MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true', MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true',
SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true', SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true',
CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true', CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true',
CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true', CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true', CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true',
PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true', PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true',
CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true', CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true',
CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true', CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true',
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true', CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true', CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true', CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true', GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true', GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true', GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true', CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true',
NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true', NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true',
TYPEBOT_START: process.env?.WEBHOOK_EVENTS_TYPEBOT_START === 'true', TYPEBOT_START: process.env?.WEBHOOK_EVENTS_TYPEBOT_START === 'true',
TYPEBOT_CHANGE_STATUS: process.env?.WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS === 'true', TYPEBOT_CHANGE_STATUS: process.env?.WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
CHAMA_AI_ACTION: process.env?.WEBHOOK_EVENTS_CHAMA_AI_ACTION === 'true', CHAMA_AI_ACTION: process.env?.WEBHOOK_EVENTS_CHAMA_AI_ACTION === 'true',
ERRORS: process.env?.WEBHOOK_EVENTS_ERRORS === 'true', ERRORS: process.env?.WEBHOOK_EVENTS_ERRORS === 'true',
ERRORS_WEBHOOK: process.env?.WEBHOOK_EVENTS_ERRORS_WEBHOOK || '', ERRORS_WEBHOOK: process.env?.WEBHOOK_EVENTS_ERRORS_WEBHOOK || '',
}, },
}, },
CONFIG_SESSION_PHONE: { CONFIG_SESSION_PHONE: {
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API', CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API',
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'Chrome', NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'Chrome',
}, },
QRCODE: { QRCODE: {
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30, LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30,
COLOR: process.env.QRCODE_COLOR || '#198754', COLOR: process.env.QRCODE_COLOR || '#198754',
}, },
TYPEBOT: { TYPEBOT: {
API_VERSION: process.env?.TYPEBOT_API_VERSION || 'v1', API_VERSION: process.env?.TYPEBOT_API_VERSION || 'v1',
}, },
AUTHENTICATION: { AUTHENTICATION: {
TYPE: process.env.AUTHENTICATION_TYPE as 'apikey', TYPE: process.env.AUTHENTICATION_TYPE as 'apikey',
API_KEY: { API_KEY: {
KEY: process.env.AUTHENTICATION_API_KEY || 'BQYHJGJHJ', KEY: process.env.AUTHENTICATION_API_KEY || 'BQYHJGJHJ',
}, },
EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true', EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true',
JWT: { JWT: {
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN) EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN) ? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
: 3600, : 3600,
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`', SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
}, },
}, },
CHATWOOT: { CHATWOOT: {
USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true', USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true',
}, },
}; };
} }
} }
export const configService = new ConfigService(); export const configService = new ConfigService();

358
src/dev-env.yml Executable file → Normal file
View File

@ -1,179 +1,179 @@
# ⚠️ # ⚠️
# ⚠️ ALL SETTINGS DEFINED IN THIS FILE ARE APPLIED TO ALL INSTANCES. # ⚠️ ALL SETTINGS DEFINED IN THIS FILE ARE APPLIED TO ALL INSTANCES.
# ⚠️ # ⚠️
# ⚠️ RENAME THIS FILE TO env.yml # ⚠️ RENAME THIS FILE TO env.yml
# Choose the server type for the application # Choose the server type for the application
SERVER: SERVER:
TYPE: http # https TYPE: http # https
PORT: 3333 # 443 PORT: 3333 # 443
URL: 127.0.0.1 URL: 127.0.0.1
CORS: CORS:
ORIGIN: ORIGIN:
- "*" - "*"
# - yourdomain.com # - yourdomain.com
METHODS: METHODS:
- POST - POST
- GET - GET
- PUT - PUT
- DELETE - DELETE
CREDENTIALS: true CREDENTIALS: true
# Install ssl certificate and replace string <domain> with domain name # Install ssl certificate and replace string <domain> with domain name
# Access: https://certbot.eff.org/instructions?ws=other&os=ubuntufocal # Access: https://certbot.eff.org/instructions?ws=other&os=ubuntufocal
SSL_CONF: SSL_CONF:
PRIVKEY: /etc/letsencrypt/live/<domain>/privkey.pem PRIVKEY: /etc/letsencrypt/live/<domain>/privkey.pem
FULLCHAIN: /etc/letsencrypt/live/<domain>/fullchain.pem FULLCHAIN: /etc/letsencrypt/live/<domain>/fullchain.pem
# Determine the logs to be displayed # Determine the logs to be displayed
LOG: LOG:
LEVEL: LEVEL:
- ERROR - ERROR
- WARN - WARN
- DEBUG - DEBUG
- INFO - INFO
- LOG - LOG
- VERBOSE - VERBOSE
- DARK - DARK
- WEBHOOKS - WEBHOOKS
COLOR: true COLOR: true
BAILEYS: error # fatal | error | warn | info | debug | trace BAILEYS: error # fatal | error | warn | info | debug | trace
# Determine how long the instance should be deleted from memory in case of no connection. # Determine how long the instance should be deleted from memory in case of no connection.
# Default time: 5 minutes # Default time: 5 minutes
# If you don't even want an expiration, enter the value false # If you don't even want an expiration, enter the value false
DEL_INSTANCE: false # or false DEL_INSTANCE: false # or false
# Temporary data storage # Temporary data storage
STORE: STORE:
MESSAGES: false MESSAGES: false
MESSAGE_UP: false MESSAGE_UP: false
CONTACTS: true CONTACTS: true
CHATS: true CHATS: true
CLEAN_STORE: CLEAN_STORE:
CLEANING_INTERVAL: 7200 # 7200 seconds === 2h CLEANING_INTERVAL: 7200 # 7200 seconds === 2h
MESSAGES: true MESSAGES: true
MESSAGE_UP: true MESSAGE_UP: true
CONTACTS: true CONTACTS: true
CHATS: true CHATS: true
# Permanent data storage # Permanent data storage
DATABASE: DATABASE:
ENABLED: true ENABLED: true
CONNECTION: CONNECTION:
URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true" URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true"
DB_PREFIX_NAME: whatsapp DB_PREFIX_NAME: whatsapp
DB_PREFIX_FINAL_NAME: "-api" DB_PREFIX_FINAL_NAME: "-api"
DB_PREFIX_FINAL_NAME_VENOM: "-venom" DB_PREFIX_FINAL_NAME_VENOM: "-venom"
# Choose the data you want to save in the application's database or store # Choose the data you want to save in the application's database or store
SAVE_DATA: SAVE_DATA:
INSTANCE: true INSTANCE: true
NEW_MESSAGE: false NEW_MESSAGE: false
MESSAGE_UPDATE: false MESSAGE_UPDATE: false
CONTACTS: true CONTACTS: true
CHATS: true CHATS: true
REDIS: REDIS:
ENABLED: false ENABLED: false
URI: "redis://localhost:6379" URI: "redis://localhost:6379"
PREFIX_KEY: "evolution" PREFIX_KEY: "evolution"
RABBITMQ: RABBITMQ:
ENABLED: false ENABLED: false
URI: "amqp://guest:guest@localhost:5672" URI: "amqp://guest:guest@localhost:5672"
OPENAI: OPENAI:
CHAVE: "" CHAVE: ""
ENABLED: false ENABLED: false
PROMPTS: false PROMPTS: false
URI: "" URI: ""
SQS: SQS:
ENABLED: false ENABLED: false
ACCESS_KEY_ID: "" ACCESS_KEY_ID: ""
SECRET_ACCESS_KEY: "" SECRET_ACCESS_KEY: ""
ACCOUNT_ID: "" ACCOUNT_ID: ""
REGION: "us-east-1" REGION: "us-east-1"
WEBSOCKET: WEBSOCKET:
ENABLED: false ENABLED: false
TYPEBOT: TYPEBOT:
API_VERSION: 'v1' # v1 | v2 API_VERSION: 'v1' # v1 | v2
# Global Webhook Settings # Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created # Each instance's Webhook URL and events will be requested at the time it is created
WEBHOOK: WEBHOOK:
# Define a global webhook that will listen for enabled events from all instances # Define a global webhook that will listen for enabled events from all instances
GLOBAL: GLOBAL:
URL: "" URL: ""
ENABLED: false ENABLED: false
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event # 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 WEBHOOK_BY_EVENTS: false
# Automatically maps webhook paths # Automatically maps webhook paths
# Set the events you want to hear # Set the events you want to hear
EVENTS: EVENTS:
APPLICATION_STARTUP: false APPLICATION_STARTUP: false
QRCODE_UPDATED: true QRCODE_UPDATED: true
MESSAGES_SET: true MESSAGES_SET: true
MESSAGES_UPSERT: true MESSAGES_UPSERT: true
MESSAGES_UPDATE: true MESSAGES_UPDATE: true
MESSAGES_DELETE: true MESSAGES_DELETE: true
SEND_MESSAGE: true SEND_MESSAGE: true
CONTACTS_SET: true CONTACTS_SET: true
CONTACTS_UPSERT: true CONTACTS_UPSERT: true
CONTACTS_UPDATE: true CONTACTS_UPDATE: true
PRESENCE_UPDATE: true PRESENCE_UPDATE: true
CHATS_SET: true CHATS_SET: true
CHATS_UPSERT: true CHATS_UPSERT: true
CHATS_UPDATE: true CHATS_UPDATE: true
CHATS_DELETE: true CHATS_DELETE: true
GROUPS_UPSERT: true GROUPS_UPSERT: true
GROUP_UPDATE: true GROUP_UPDATE: true
GROUP_PARTICIPANTS_UPDATE: true GROUP_PARTICIPANTS_UPDATE: true
CONNECTION_UPDATE: true CONNECTION_UPDATE: true
CALL: true CALL: true
# This event fires every time a new token is requested via the refresh route # This event fires every time a new token is requested via the refresh route
NEW_JWT_TOKEN: false NEW_JWT_TOKEN: false
# This events is used with Typebot # This events is used with Typebot
TYPEBOT_START: false TYPEBOT_START: false
TYPEBOT_CHANGE_STATUS: false TYPEBOT_CHANGE_STATUS: false
# This event is used with Chama AI # This event is used with Chama AI
CHAMA_AI_ACTION: false CHAMA_AI_ACTION: false
# This event is used to send errors to the webhook # This event is used to send errors to the webhook
ERRORS: false ERRORS: false
ERRORS_WEBHOOK: "" ERRORS_WEBHOOK: ""
CONFIG_SESSION_PHONE: CONFIG_SESSION_PHONE:
# Name that will be displayed on smartphone connection # Name that will be displayed on smartphone connection
CLIENT: "Evolution API" CLIENT: "Chat API"
NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari
# Set qrcode display limit # Set qrcode display limit
QRCODE: QRCODE:
LIMIT: 30 LIMIT: 30
COLOR: "#198754" COLOR: "#198754"
# Defines an authentication type for the api # Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token, # 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 # if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
AUTHENTICATION: AUTHENTICATION:
TYPE: apikey # jwt or apikey TYPE: apikey # jwt or apikey
# Define a global apikey to access all instances # Define a global apikey to access all instances
API_KEY: API_KEY:
# OBS: This key must be inserted in the request header to create an instance. # OBS: This key must be inserted in the request header to create an instance.
KEY: B6D711FCDE4D4FD5936544120E713976 KEY: B6D711FCDE4D4FD5936544120E713976
# Expose the api key on return from fetch instances # Expose the api key on return from fetch instances
EXPOSE_IN_FETCH_INSTANCES: true EXPOSE_IN_FETCH_INSTANCES: true
# Set the secret key to encrypt and decrypt your token and its expiration time. # Set the secret key to encrypt and decrypt your token and its expiration time.
JWT: JWT:
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
SECRET: L=0YWt]b2w[WF>#>:&E` SECRET: L=0YWt]b2w[WF>#>:&E`
# Configure to chatwoot # Configure to chatwoot
CHATWOOT: CHATWOOT:
USE_REPLY_ID: false USE_REPLY_ID: false

5194
src/docs/swagger.yaml Executable file → Normal file

File diff suppressed because it is too large Load Diff

200
src/libs/amqp.server.ts Executable file → Normal file
View File

@ -1,100 +1,100 @@
import * as amqp from 'amqplib/callback_api'; import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq, Openai } from '../config/env.config'; import { configService, Rabbitmq, Openai } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
const logger = new Logger('AMQP'); const logger = new Logger('AMQP');
let amqpChannel: amqp.Channel | null = null; let amqpChannel: amqp.Channel | null = null;
export const initAMQP = () => { export const initAMQP = () => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const uri = configService.get<Rabbitmq>('RABBITMQ').URI; const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
amqp.connect(uri, (error, connection) => { amqp.connect(uri, (error, connection) => {
if (error) { if (error) {
reject(error); reject(error);
return; return;
} }
connection.createChannel((channelError, channel) => { connection.createChannel((channelError, channel) => {
if (channelError) { if (channelError) {
reject(channelError); reject(channelError);
return; return;
} }
const exchangeName = 'evolution_exchange'; const exchangeName = 'evolution_exchange';
channel.assertExchange(exchangeName, 'topic', { channel.assertExchange(exchangeName, 'topic', {
durable: true, durable: true,
autoDelete: false, autoDelete: false,
}); });
amqpChannel = channel; amqpChannel = channel;
logger.info('AMQP initialized'); logger.info('AMQP initialized');
resolve(); resolve();
}); });
}); });
}); });
}; };
export const getAMQP = (): amqp.Channel | null => { export const getAMQP = (): amqp.Channel | null => {
return amqpChannel; return amqpChannel;
}; };
export const initQueues = (instanceName: string, events: string[]) => { export const initQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return; if (!events || !events.length) return;
const queues = events.map((event) => { const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`; return `${event.replace(/_/g, '.').toLowerCase()}`;
}); });
queues.forEach((event) => { queues.forEach((event) => {
const amqp = getAMQP(); const amqp = getAMQP();
const exchangeName = instanceName ?? 'evolution_exchange'; const exchangeName = instanceName ?? 'evolution_exchange';
amqp.assertExchange(exchangeName, 'topic', { amqp.assertExchange(exchangeName, 'topic', {
durable: true, durable: true,
autoDelete: false, autoDelete: false,
}); });
const queueName = `${instanceName}.${event}`; const queueName = `${instanceName}.${event}`;
amqp.assertQueue(queueName, { amqp.assertQueue(queueName, {
durable: true, durable: true,
autoDelete: false, autoDelete: false,
arguments: { arguments: {
'x-queue-type': 'quorum', 'x-queue-type': 'quorum',
}, },
}); });
amqp.bindQueue(queueName, exchangeName, event); amqp.bindQueue(queueName, exchangeName, event);
}); });
}; };
export const removeQueues = (instanceName: string, events: string[]) => { export const removeQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return; if (!events || !events.length) return;
const channel = getAMQP(); const channel = getAMQP();
const queues = events.map((event) => { const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`; return `${event.replace(/_/g, '.').toLowerCase()}`;
}); });
const exchangeName = instanceName ?? 'evolution_exchange'; const exchangeName = instanceName ?? 'evolution_exchange';
queues.forEach((event) => { queues.forEach((event) => {
const amqp = getAMQP(); const amqp = getAMQP();
amqp.assertExchange(exchangeName, 'topic', { amqp.assertExchange(exchangeName, 'topic', {
durable: true, durable: true,
autoDelete: false, autoDelete: false,
}); });
const queueName = `${instanceName}.${event}`; const queueName = `${instanceName}.${event}`;
amqp.deleteQueue(queueName); amqp.deleteQueue(queueName);
}); });
channel.deleteExchange(exchangeName); channel.deleteExchange(exchangeName);
}; };

52
src/libs/db.connect.ts Executable file → Normal file
View File

@ -1,26 +1,26 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { configService, Database } from '../config/env.config'; import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
const logger = new Logger('MongoDB'); const logger = new Logger('MongoDB');
const db = configService.get<Database>('DATABASE'); const db = configService.get<Database>('DATABASE');
export const dbserver = (() => { export const dbserver = (() => {
if (db.ENABLED) { if (db.ENABLED) {
logger.verbose('connecting'); logger.verbose('connecting');
const dbs = mongoose.createConnection(db.CONNECTION.URI, { const dbs = mongoose.createConnection(db.CONNECTION.URI, {
dbName: db.CONNECTION.DB_PREFIX_NAME + db.CONNECTION.DB_PREFIX_FINAL_NAME + '-config', dbName: db.CONNECTION.DB_PREFIX_NAME + db.CONNECTION.DB_PREFIX_FINAL_NAME + '-config',
}); });
logger.verbose('connected in ' + db.CONNECTION.URI); logger.verbose('connected in ' + db.CONNECTION.URI);
logger.info('ON - dbName: ' + dbs['$dbName']); logger.info('ON - dbName: ' + dbs['$dbName']);
process.on('beforeExit', () => { process.on('beforeExit', () => {
logger.verbose('instance destroyed'); logger.verbose('instance destroyed');
dbserver.destroy(true, (error) => logger.error(error)); dbserver.destroy(true, (error) => logger.error(error));
}); });
return dbs; return dbs;
} }
})(); })();

68
src/libs/openai.ts Executable file → Normal file
View File

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

264
src/libs/redis.client.ts Executable file → Normal file
View File

@ -1,132 +1,132 @@
import { createClient, RedisClientType } from '@redis/client'; import { createClient, RedisClientType } from '@redis/client';
import { BufferJSON } from '@whiskeysockets/baileys'; import { BufferJSON } from '@whiskeysockets/baileys';
import { Redis } from '../config/env.config'; import { Redis } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
export class RedisCache { export class RedisCache {
constructor() { constructor() {
this.logger.verbose('RedisCache instance created'); this.logger.verbose('RedisCache instance created');
process.on('beforeExit', () => { process.on('beforeExit', () => {
this.logger.verbose('RedisCache instance destroyed'); this.logger.verbose('RedisCache instance destroyed');
this.disconnect(); this.disconnect();
}); });
} }
// constructor() { // constructor() {
// this.logger.verbose('instance created'); // this.logger.verbose('instance created');
// process.on('beforeExit', async () => { // process.on('beforeExit', async () => {
// this.logger.verbose('instance destroyed'); // this.logger.verbose('instance destroyed');
// if (this.statusConnection) { // if (this.statusConnection) {
// this.logger.verbose('instance disconnect'); // this.logger.verbose('instance disconnect');
// await this.client.disconnect(); // await this.client.disconnect();
// } // }
// }); // });
// } // }
private statusConnection = false; private statusConnection = false;
private instanceName: string; private instanceName: string;
private redisEnv: Redis; private redisEnv: Redis;
public set reference(reference: string) { public set reference(reference: string) {
this.logger.verbose('set reference: ' + reference); this.logger.verbose('set reference: ' + reference);
this.instanceName = reference; this.instanceName = reference;
} }
public async connect(redisEnv: Redis) { public async connect(redisEnv: Redis) {
this.logger.verbose('Connecting to Redis...'); this.logger.verbose('Connecting to Redis...');
this.client = createClient({ url: redisEnv.URI }); this.client = createClient({ url: redisEnv.URI });
//this.logger.verbose('connected in ' + redisEnv.URI); //this.logger.verbose('connected in ' + redisEnv.URI);
this.client.on('error', (err) => this.logger.error('Redis Client Error ' + err)); this.client.on('error', (err) => this.logger.error('Redis Client Error ' + err));
await this.client.connect(); await this.client.connect();
this.statusConnection = true; this.statusConnection = true;
this.redisEnv = redisEnv; this.redisEnv = redisEnv;
this.logger.verbose(`Connected to ${redisEnv.URI}`); this.logger.verbose(`Connected to ${redisEnv.URI}`);
} }
private readonly logger = new Logger(RedisCache.name); private readonly logger = new Logger(RedisCache.name);
private client: RedisClientType; private client: RedisClientType;
public async disconnect() { public async disconnect() {
if (this.statusConnection) { if (this.statusConnection) {
await this.client.disconnect(); await this.client.disconnect();
this.statusConnection = false; this.statusConnection = false;
this.logger.verbose('Redis client disconnected'); this.logger.verbose('Redis client disconnected');
} }
} }
public async instanceKeys(): Promise<string[]> { public async instanceKeys(): Promise<string[]> {
const keys: string[] = []; const keys: string[] = [];
try { try {
//this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*'); //this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
//return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']); //return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']);
this.logger.verbose('Fetching instance keys'); this.logger.verbose('Fetching instance keys');
for await (const key of this.client.scanIterator({ MATCH: `${this.redisEnv.PREFIX_KEY}:*` })) { for await (const key of this.client.scanIterator({ MATCH: `${this.redisEnv.PREFIX_KEY}:*` })) {
keys.push(key); keys.push(key);
} }
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
this.logger.error('Error fetching instance keys ' + error); this.logger.error('Error fetching instance keys ' + error);
} }
return keys; return keys;
} }
public async keyExists(key?: string) { public async keyExists(key?: string) {
if (key) { if (key) {
this.logger.verbose('keyExists: ' + key); this.logger.verbose('keyExists: ' + key);
return !!(await this.instanceKeys()).find((i) => i === key); return !!(await this.instanceKeys()).find((i) => i === key);
} }
this.logger.verbose('keyExists: ' + this.instanceName); this.logger.verbose('keyExists: ' + this.instanceName);
return !!(await this.instanceKeys()).find((i) => i === this.instanceName); return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
} }
public async writeData(field: string, data: any) { public async writeData(field: string, data: any) {
try { try {
this.logger.verbose('writeData: ' + field); this.logger.verbose('writeData: ' + field);
const json = JSON.stringify(data, BufferJSON.replacer); const json = JSON.stringify(data, BufferJSON.replacer);
return await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json); return await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json);
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} }
public async readData(field: string) { public async readData(field: string) {
try { try {
this.logger.verbose('readData: ' + field); this.logger.verbose('readData: ' + field);
const data = await this.client.hGet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field); const data = await this.client.hGet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field);
if (data) { if (data) {
this.logger.verbose('readData: ' + field + ' success'); this.logger.verbose('readData: ' + field + ' success');
return JSON.parse(data, BufferJSON.reviver); return JSON.parse(data, BufferJSON.reviver);
} }
this.logger.verbose('readData: ' + field + ' not found'); this.logger.verbose('readData: ' + field + ' not found');
return null; return null;
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} }
public async removeData(field: string) { public async removeData(field: string) {
try { try {
this.logger.verbose('removeData: ' + field); this.logger.verbose('removeData: ' + field);
return await this.client.hDel(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field); return await this.client.hDel(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field);
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} }
public async delAll(hash?: string) { public async delAll(hash?: string) {
try { try {
this.logger.verbose('instance delAll: ' + hash); this.logger.verbose('instance delAll: ' + hash);
const result = await this.client.del(hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName); const result = await this.client.del(hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName);
return result; return result;
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} }
} }

18
src/libs/redis.ts Executable file → Normal file
View File

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

192
src/libs/sqs.server.ts Executable file → Normal file
View File

@ -1,97 +1,97 @@
import { SQS } from 'aws-sdk'; import { SQS } from 'aws-sdk';
import { configService, Sqs } from '../config/env.config'; import { configService, Sqs } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
const logger = new Logger('SQS'); const logger = new Logger('SQS');
let sqs: SQS; let sqs: SQS;
export const initSQS = () => { export const initSQS = () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const awsConfig = configService.get<Sqs>('SQS'); const awsConfig = configService.get<Sqs>('SQS');
sqs = new SQS({ sqs = new SQS({
accessKeyId: awsConfig.ACCESS_KEY_ID, accessKeyId: awsConfig.ACCESS_KEY_ID,
secretAccessKey: awsConfig.SECRET_ACCESS_KEY, secretAccessKey: awsConfig.SECRET_ACCESS_KEY,
region: awsConfig.REGION, region: awsConfig.REGION,
}); });
logger.info('SQS initialized'); logger.info('SQS initialized');
resolve(); resolve();
}); });
}; };
export const getSQS = (): SQS => { export const getSQS = (): SQS => {
return sqs; return sqs;
}; };
export const initQueues = (instanceName: string, events: string[]) => { export const initQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return; if (!events || !events.length) return;
const queues = events.map((event) => { const queues = events.map((event) => {
return `${event.replace(/_/g, '_').toLowerCase()}`; return `${event.replace(/_/g, '_').toLowerCase()}`;
}); });
const sqs = getSQS(); const sqs = getSQS();
queues.forEach((event) => { queues.forEach((event) => {
const queueName = `${instanceName}_${event}.fifo`; const queueName = `${instanceName}_${event}.fifo`;
sqs.createQueue( sqs.createQueue(
{ {
QueueName: queueName, QueueName: queueName,
Attributes: { Attributes: {
FifoQueue: 'true', FifoQueue: 'true',
}, },
}, },
(err, data) => { (err, data) => {
if (err) { if (err) {
logger.error(`Error creating queue ${queueName}: ${err.message}`); logger.error(`Error creating queue ${queueName}: ${err.message}`);
} else { } else {
logger.info(`Queue ${queueName} created: ${data.QueueUrl}`); logger.info(`Queue ${queueName} created: ${data.QueueUrl}`);
} }
}, },
); );
}); });
}; };
export const removeQueues = (instanceName: string, events: string[]) => { export const removeQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return; if (!events || !events.length) return;
const sqs = getSQS(); const sqs = getSQS();
const queues = events.map((event) => { const queues = events.map((event) => {
return `${event.replace(/_/g, '_').toLowerCase()}`; return `${event.replace(/_/g, '_').toLowerCase()}`;
}); });
queues.forEach((event) => { queues.forEach((event) => {
const queueName = `${instanceName}_${event}.fifo`; const queueName = `${instanceName}_${event}.fifo`;
sqs.getQueueUrl( sqs.getQueueUrl(
{ {
QueueName: queueName, QueueName: queueName,
}, },
(err, data) => { (err, data) => {
if (err) { if (err) {
logger.error(`Error getting queue URL for ${queueName}: ${err.message}`); logger.error(`Error getting queue URL for ${queueName}: ${err.message}`);
} else { } else {
const queueUrl = data.QueueUrl; const queueUrl = data.QueueUrl;
sqs.deleteQueue( sqs.deleteQueue(
{ {
QueueUrl: queueUrl, QueueUrl: queueUrl,
}, },
(deleteErr) => { (deleteErr) => {
if (deleteErr) { if (deleteErr) {
logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`); logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`);
} else { } else {
logger.info(`Queue ${queueName} deleted`); logger.info(`Queue ${queueName} deleted`);
} }
}, },
); );
} }
}, },
); );
}); });
}; };

274
src/main.ts Executable file → Normal file
View File

@ -1,137 +1,137 @@
import 'express-async-errors'; import 'express-async-errors';
import axios from 'axios'; import axios from 'axios';
import compression from 'compression'; import compression from 'compression';
import cors from 'cors'; import cors from 'cors';
import express, { json, NextFunction, Request, Response, urlencoded } from 'express'; import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
import { join } from 'path'; import { join } from 'path';
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config'; import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
import { onUnexpectedError } from './config/error.config'; import { onUnexpectedError } from './config/error.config';
import { Logger } from './config/logger.config'; import { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config'; import { ROOT_DIR } from './config/path.config';
import { swaggerRouter } from './docs/swagger.conf'; import { swaggerRouter } from './docs/swagger.conf';
import { initAMQP } from './libs/amqp.server'; import { initAMQP } from './libs/amqp.server';
import { initIO } from './libs/socket.server'; import { initIO } from './libs/socket.server';
import { initSQS } from './libs/sqs.server'; import { initSQS } from './libs/sqs.server';
import { ServerUP } from './utils/server-up'; import { ServerUP } from './utils/server-up';
import { HttpStatus, router } from './whatsapp/routers/index.router'; import { HttpStatus, router } from './whatsapp/routers/index.router';
import { waMonitor } from './whatsapp/whatsapp.module'; import { waMonitor } from './whatsapp/whatsapp.module';
function initWA() { function initWA() {
waMonitor.loadInstance(); waMonitor.loadInstance();
} }
function bootstrap() { function bootstrap() {
const logger = new Logger('SERVER'); const logger = new Logger('SERVER');
const app = express(); const app = express();
app.use( app.use(
cors({ cors({
origin(requestOrigin, callback) { origin(requestOrigin, callback) {
const { ORIGIN } = configService.get<Cors>('CORS'); const { ORIGIN } = configService.get<Cors>('CORS');
if (ORIGIN.includes('*')) { if (ORIGIN.includes('*')) {
return callback(null, true); return callback(null, true);
} }
if (ORIGIN.indexOf(requestOrigin) !== -1) { if (ORIGIN.indexOf(requestOrigin) !== -1) {
return callback(null, true); return callback(null, true);
} }
return callback(new Error('Not allowed by CORS')); return callback(new Error('Not allowed by CORS'));
}, },
methods: [...configService.get<Cors>('CORS').METHODS], methods: [...configService.get<Cors>('CORS').METHODS],
credentials: configService.get<Cors>('CORS').CREDENTIALS, credentials: configService.get<Cors>('CORS').CREDENTIALS,
}), }),
urlencoded({ extended: true, limit: '136mb' }), urlencoded({ extended: true, limit: '136mb' }),
json({ limit: '136mb' }), json({ limit: '136mb' }),
compression(), compression(),
); );
app.set('view engine', 'hbs'); app.set('view engine', 'hbs');
app.set('views', join(ROOT_DIR, 'views')); app.set('views', join(ROOT_DIR, 'views'));
app.use(express.static(join(ROOT_DIR, 'public'))); app.use(express.static(join(ROOT_DIR, 'public')));
app.use('/store', express.static(join(ROOT_DIR, 'store'))); app.use('/store', express.static(join(ROOT_DIR, 'store')));
app.use('/', router); app.use('/', router);
app.use(swaggerRouter); app.use(swaggerRouter);
app.use( app.use(
(err: Error, req: Request, res: Response, next: NextFunction) => { (err: Error, req: Request, res: Response, next: NextFunction) => {
if (err) { if (err) {
const webhook = configService.get<Webhook>('WEBHOOK'); const webhook = configService.get<Webhook>('WEBHOOK');
if (webhook.EVENTS.ERRORS_WEBHOOK && webhook.EVENTS.ERRORS_WEBHOOK != '' && webhook.EVENTS.ERRORS) { if (webhook.EVENTS.ERRORS_WEBHOOK && webhook.EVENTS.ERRORS_WEBHOOK != '' && webhook.EVENTS.ERRORS) {
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const localISOTime = new Date(Date.now() - tzoffset).toISOString(); const localISOTime = new Date(Date.now() - tzoffset).toISOString();
const now = localISOTime; const now = localISOTime;
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY; const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
const serverUrl = configService.get<HttpServer>('SERVER').URL; const serverUrl = configService.get<HttpServer>('SERVER').URL;
const errorData = { const errorData = {
event: 'error', event: 'error',
data: { data: {
error: err['error'] || 'Internal Server Error', error: err['error'] || 'Internal Server Error',
message: err['message'] || 'Internal Server Error', message: err['message'] || 'Internal Server Error',
status: err['status'] || 500, status: err['status'] || 500,
response: { response: {
message: err['message'] || 'Internal Server Error', message: err['message'] || 'Internal Server Error',
}, },
}, },
date_time: now, date_time: now,
api_key: globalApiKey, api_key: globalApiKey,
server_url: serverUrl, server_url: serverUrl,
}; };
logger.error(errorData); logger.error(errorData);
const baseURL = webhook.EVENTS.ERRORS_WEBHOOK; const baseURL = webhook.EVENTS.ERRORS_WEBHOOK;
const httpService = axios.create({ baseURL }); const httpService = axios.create({ baseURL });
httpService.post('', errorData); httpService.post('', errorData);
} }
return res.status(err['status'] || 500).json({ return res.status(err['status'] || 500).json({
status: err['status'] || 500, status: err['status'] || 500,
error: err['error'] || 'Internal Server Error', error: err['error'] || 'Internal Server Error',
response: { response: {
message: err['message'] || 'Internal Server Error', message: err['message'] || 'Internal Server Error',
}, },
}); });
} }
next(); next();
}, },
(req: Request, res: Response, next: NextFunction) => { (req: Request, res: Response, next: NextFunction) => {
const { method, url } = req; const { method, url } = req;
res.status(HttpStatus.NOT_FOUND).json({ res.status(HttpStatus.NOT_FOUND).json({
status: HttpStatus.NOT_FOUND, status: HttpStatus.NOT_FOUND,
error: 'Not Found', error: 'Not Found',
response: { response: {
message: [`Cannot ${method.toUpperCase()} ${url}`], message: [`Cannot ${method.toUpperCase()} ${url}`],
}, },
}); });
next(); next();
}, },
); );
const httpServer = configService.get<HttpServer>('SERVER'); const httpServer = configService.get<HttpServer>('SERVER');
ServerUP.app = app; ServerUP.app = app;
const server = ServerUP[httpServer.TYPE]; const server = ServerUP[httpServer.TYPE];
server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT)); server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT));
initWA(); initWA();
initIO(server); initIO(server);
if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP(); if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
if (configService.get<Sqs>('SQS')?.ENABLED) initSQS(); if (configService.get<Sqs>('SQS')?.ENABLED) initSQS();
onUnexpectedError(); onUnexpectedError();
} }
bootstrap(); bootstrap();

98
src/prompts/contabilAgent.ts Executable file → Normal file
View File

@ -1,49 +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. 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 é: O roteiro de atendimento é:
1. Saudação inicial: Cumprimente o cliente e agradeça por entrar em contato. 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. 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. 3. Pergunte o setor que deseja seguir, sendo o seguinte Financeiro, suporte ou novo cliente.
4. Serviços de Contabilidade: 4. Serviços de Contabilidade:
4.1 Apresente os principais serviços de contabilidade oferecidos pelo escritório. 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? 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. Perguntas Frequentes (FAQ):
5.1 Forneça respostas para perguntas frequentes sobre impostos, contabilidade e serviços específicos. 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: [ 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? 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ê. 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? 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. 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. Agendamento de Consulta:
5.2 Permita que os usuários agendem uma consulta com um contador. 5.2 Permita que os usuários agendem uma consulta com um contador.
5.3 Pergunte sobre a data e hora preferidas. 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ê. 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. Impostos:
6.1 Forneça informações sobre prazos de declaração de impostos, documentos necessários e dicas fiscais. 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. 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. 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.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]. 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. 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.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?" 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. Despedida:
9.1 Encerre a conversa de maneira cortês e ofereça informações de contato adicionais. 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!" 9.2 Obrigado por usar nossos serviços! Se precisar de assistência futura, estamos à disposição. Tenha um ótimo dia!"
10. Feedback: 10. Feedback:
10.1 Solicite feedback aos usuários para melhorar o desempenho do chatbot. 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? 10.2 Gostaríamos de ouvir sua opinião! Em uma escala de 1 a 5, quão útil você achou nosso chatbot hoje?
` `

188
src/prompts/pizzaAgent.ts Executable file → Normal file
View File

@ -1,94 +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. 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. 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 código do pedido é: {{ orderCode }}
O roteiro de atendimento é: O roteiro de atendimento é:
1. Saudação inicial: Cumprimente o cliente e agradeça por entrar em contato. 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. 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. 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. 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.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.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.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.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.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. 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. 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.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.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.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. 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. 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.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. 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. 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.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. 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. 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.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. 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. 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.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. 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. 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.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.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. 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. 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.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.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. 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. 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.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.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.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.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. 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. 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): 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 - 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 - 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 - 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: 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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): 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 - 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 - 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 - 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): Extras/Adicionais (os valores estão separados por tamanho - Broto, Médio e Grande):
- Catupiry: R$ 5,00 / R$ 7,00 / R$ 9,00 - Catupiry: R$ 5,00 / R$ 7,00 / R$ 9,00
Bordas (os valores estão separados por tamanho - Broto, Médio e Grande): Bordas (os valores estão separados por tamanho - Broto, Médio e Grande):
- Chocolate: R$ 5,00 / R$ 7,00 / R$ 9,00 - Chocolate: R$ 5,00 / R$ 7,00 / R$ 9,00
- Cheddar: 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 - Catupiry: R$ 5,00 / R$ 7,00 / R$ 9,00
Bebidas: Bebidas:
- Coca-Cola 2L: R$ 10,00 - Coca-Cola 2L: R$ 10,00
- Coca-Cola Lata: R$ 8,00 - Coca-Cola Lata: R$ 8,00
- Guaraná 2L: R$ 10,00 - Guaraná 2L: R$ 10,00
- Guaraná Lata: R$ 7,00 - Guaraná Lata: R$ 7,00
- Água com Gás 500 ml: R$ 5,00 - Água com Gás 500 ml: R$ 5,00
- Água sem Gás 500 ml: R$ 4,00 - Água sem Gás 500 ml: R$ 4,00
` `

26
src/utils/initPrompt.ts Executable file → Normal file
View File

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

224
src/utils/use-multi-file-auth-state-db.ts Executable file → Normal file
View File

@ -1,112 +1,112 @@
import { import {
AuthenticationCreds, AuthenticationCreds,
AuthenticationState, AuthenticationState,
BufferJSON, BufferJSON,
initAuthCreds, initAuthCreds,
proto, proto,
SignalDataTypeMap, SignalDataTypeMap,
} from '@whiskeysockets/baileys'; } from '@whiskeysockets/baileys';
import { configService, Database } from '../config/env.config'; import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
import { dbserver } from '../libs/db.connect'; import { dbserver } from '../libs/db.connect';
export async function useMultiFileAuthStateDb( export async function useMultiFileAuthStateDb(
coll: string, coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> { ): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateDb.name); const logger = new Logger(useMultiFileAuthStateDb.name);
const client = dbserver.getClient(); const client = dbserver.getClient();
const collection = client const collection = client
.db( .db(
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME +
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_FINAL_NAME configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_FINAL_NAME
) )
.collection(coll); .collection(coll);
const writeData = async (data: any, key: string): Promise<any> => { const writeData = async (data: any, key: string): Promise<any> => {
try { try {
await client.connect(); await client.connect();
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer)); let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
if (Array.isArray(msgParsed)) { if (Array.isArray(msgParsed)) {
msgParsed = { msgParsed = {
_id: key, _id: key,
content_array: msgParsed, content_array: msgParsed,
}; };
} }
return await collection.replaceOne({ _id: key }, msgParsed, { return await collection.replaceOne({ _id: key }, msgParsed, {
//return await collection.replaceOne({ _id: key }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), { //return await collection.replaceOne({ _id: key }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), {
upsert: true, upsert: true,
}); });
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
} }
}; };
const readData = async (key: string): Promise<any> => { const readData = async (key: string): Promise<any> => {
try { try {
await client.connect(); await client.connect();
let data = (await collection.findOne({ _id: key })) as any; let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) { if (data?.content_array) {
data = data.content_array; data = data.content_array;
} }
//const data = await collection.findOne({ _id: key }); //const data = await collection.findOne({ _id: key });
const creds = JSON.stringify(data); const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver); return JSON.parse(creds, BufferJSON.reviver);
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
} }
}; };
const removeData = async (key: string) => { const removeData = async (key: string) => {
try { try {
await client.connect(); await client.connect();
return await collection.deleteOne({ _id: key }); return await collection.deleteOne({ _id: key });
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
} }
}; };
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds(); const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return { return {
state: { state: {
creds, creds,
keys: { keys: {
get: async (type, ids: string[]) => { get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {}; const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all( await Promise.all(
ids.map(async (id) => { ids.map(async (id) => {
let value = await readData(`${type}-${id}`); let value = await readData(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) { if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value); value = proto.Message.AppStateSyncKeyData.fromObject(value);
} }
data[id] = value; data[id] = value;
}), }),
); );
return data; return data;
}, },
set: async (data: any) => { set: async (data: any) => {
const tasks: Promise<void>[] = []; const tasks: Promise<void>[] = [];
for (const category in data) { for (const category in data) {
for (const id in data[category]) { for (const id in data[category]) {
const value = data[category][id]; const value = data[category][id];
const key = `${category}-${id}`; const key = `${category}-${id}`;
tasks.push(value ? writeData(value, key) : removeData(key)); tasks.push(value ? writeData(value, key) : removeData(key));
} }
} }
await Promise.all(tasks); await Promise.all(tasks);
}, },
}, },
}, },
saveCreds: async () => { saveCreds: async () => {
return writeData(creds, 'creds'); return await writeData(creds, 'creds');
}, },
}; };
} }

2280
src/validate/validate.schema.ts Executable file → Normal file

File diff suppressed because it is too large Load Diff

1480
src/whatsapp/controllers/instance.controller.ts Executable file → Normal file

File diff suppressed because it is too large Load Diff

170
src/whatsapp/controllers/openai.controller.ts Executable file → Normal file
View File

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

110
src/whatsapp/controllers/sqs.controller.ts Executable file → Normal file
View File

@ -1,56 +1,56 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto'; import { SqsDto } from '../dto/sqs.dto';
import { SqsService } from '../services/sqs.service'; import { SqsService } from '../services/sqs.service';
const logger = new Logger('SqsController'); const logger = new Logger('SqsController');
export class SqsController { export class SqsController {
constructor(private readonly sqsService: SqsService) { } constructor(private readonly sqsService: SqsService) { }
public async createSqs(instance: InstanceDto, data: SqsDto) { public async createSqs(instance: InstanceDto, data: SqsDto) {
logger.verbose('requested createSqs from ' + instance.instanceName + ' instance'); logger.verbose('requested createSqs from ' + instance.instanceName + ' instance');
if (!data.enabled) { if (!data.enabled) {
logger.verbose('sqs disabled'); logger.verbose('sqs disabled');
data.events = []; data.events = [];
} }
if (data.events.length === 0) { if (data.events.length === 0) {
logger.verbose('sqs events empty'); logger.verbose('sqs events empty');
data.events = [ data.events = [
'APPLICATION_STARTUP', 'APPLICATION_STARTUP',
'QRCODE_UPDATED', 'QRCODE_UPDATED',
'MESSAGES_SET', 'MESSAGES_SET',
'MESSAGES_UPSERT', 'MESSAGES_UPSERT',
'MESSAGES_UPDATE', 'MESSAGES_UPDATE',
'MESSAGES_DELETE', 'MESSAGES_DELETE',
'SEND_MESSAGE', 'SEND_MESSAGE',
'CONTACTS_SET', 'CONTACTS_SET',
'CONTACTS_UPSERT', 'CONTACTS_UPSERT',
'CONTACTS_UPDATE', 'CONTACTS_UPDATE',
'PRESENCE_UPDATE', 'PRESENCE_UPDATE',
'CHATS_SET', 'CHATS_SET',
'CHATS_UPSERT', 'CHATS_UPSERT',
'CHATS_UPDATE', 'CHATS_UPDATE',
'CHATS_DELETE', 'CHATS_DELETE',
'GROUPS_UPSERT', 'GROUPS_UPSERT',
'GROUP_UPDATE', 'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE', 'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE', 'CONNECTION_UPDATE',
'CALL', 'CALL',
'NEW_JWT_TOKEN', 'NEW_JWT_TOKEN',
'TYPEBOT_START', 'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS', 'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION', 'CHAMA_AI_ACTION',
]; ];
} }
return this.sqsService.create(instance, data); return this.sqsService.create(instance, data);
} }
public async findSqs(instance: InstanceDto) { public async findSqs(instance: InstanceDto) {
logger.verbose('requested findSqs from ' + instance.instanceName + ' instance'); logger.verbose('requested findSqs from ' + instance.instanceName + ' instance');
return this.sqsService.find(instance); return this.sqsService.find(instance);
} }
} }

10
src/whatsapp/dto/contactopenai.dto.ts Executable file → Normal file
View File

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

88
src/whatsapp/dto/instance.dto.ts Executable file → Normal file
View File

@ -1,44 +1,44 @@
export class InstanceDto { export class InstanceDto {
instanceName: string; instanceName: string;
qrcode?: boolean; qrcode?: boolean;
number?: string; number?: string;
token?: string; token?: string;
webhook?: string; webhook?: string;
webhook_by_events?: boolean; webhook_by_events?: boolean;
webhook_base64?: boolean; webhook_base64?: boolean;
events?: string[]; events?: string[];
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;
groups_ignore?: boolean; groups_ignore?: boolean;
always_online?: boolean; always_online?: boolean;
read_messages?: boolean; read_messages?: boolean;
read_status?: boolean; read_status?: boolean;
chatwoot_account_id?: string; chatwoot_account_id?: string;
chatwoot_token?: string; chatwoot_token?: string;
chatwoot_url?: string; chatwoot_url?: string;
chatwoot_sign_msg?: boolean; chatwoot_sign_msg?: boolean;
chatwoot_reopen_conversation?: boolean; chatwoot_reopen_conversation?: boolean;
chatwoot_conversation_pending?: boolean; chatwoot_conversation_pending?: boolean;
websocket_enabled?: boolean; websocket_enabled?: boolean;
websocket_events?: string[]; websocket_events?: string[];
rabbitmq_enabled?: boolean; rabbitmq_enabled?: boolean;
rabbitmq_events?: string[]; rabbitmq_events?: string[];
openai_chave?: boolean; openai_chave?: boolean;
openai_prompts?: string; openai_prompts?: string;
openai_enabled?: boolean; openai_enabled?: boolean;
openai_events?: string[]; openai_events?: string[];
sqs_enabled?: boolean; sqs_enabled?: boolean;
sqs_events?: string[]; sqs_events?: string[];
typebot_url?: string; typebot_url?: string;
typebot?: string; typebot?: string;
typebot_expire?: number; typebot_expire?: number;
typebot_keyword_finish?: string; typebot_keyword_finish?: string;
typebot_delay_message?: number; typebot_delay_message?: number;
typebot_unknown_message?: string; typebot_unknown_message?: string;
typebot_listening_from_me?: boolean; typebot_listening_from_me?: boolean;
proxy_enabled?: boolean; proxy_enabled?: boolean;
proxy?: string; proxy?: string;
} }

12
src/whatsapp/dto/openai.dto.ts Executable file → Normal file
View File

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

6
src/whatsapp/dto/sqs.dto.ts Executable file → Normal file
View File

@ -1,4 +1,4 @@
export class SqsDto { export class SqsDto {
enabled: boolean; enabled: boolean;
events?: string[]; events?: string[];
} }

152
src/whatsapp/guards/instance.guard.ts Executable file → Normal file
View File

@ -1,76 +1,76 @@
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { configService, Database, Redis } from '../../config/env.config'; import { configService, Database, Redis } from '../../config/env.config';
import { INSTANCE_DIR } from '../../config/path.config'; import { INSTANCE_DIR } from '../../config/path.config';
import { import {
BadRequestException, BadRequestException,
ForbiddenException, ForbiddenException,
InternalServerErrorException, InternalServerErrorException,
NotFoundException, NotFoundException,
} from '../../exceptions'; } from '../../exceptions';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../libs/db.connect';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { cache, waMonitor } from '../whatsapp.module'; import { cache, waMonitor } from '../whatsapp.module';
async function getInstance(instanceName: string) { async function getInstance(instanceName: string) {
try { try {
const db = configService.get<Database>('DATABASE'); const db = configService.get<Database>('DATABASE');
const redisConf = configService.get<Redis>('REDIS'); const redisConf = configService.get<Redis>('REDIS');
const exists = !!waMonitor.waInstances[instanceName]; const exists = !!waMonitor.waInstances[instanceName];
if (redisConf.ENABLED) { if (redisConf.ENABLED) {
const keyExists = await cache.keyExists(); const keyExists = await cache.keyExists();
return exists || keyExists; return exists || keyExists;
} }
if (db.ENABLED) { if (db.ENABLED) {
const collection = dbserver const collection = dbserver
.getClient() .getClient()
.db( .db(
db.CONNECTION.DB_PREFIX_NAME + db.CONNECTION.DB_PREFIX_NAME +
db.CONNECTION.DB_PREFIX_FINAL_NAME) db.CONNECTION.DB_PREFIX_FINAL_NAME)
.collection(instanceName); .collection(instanceName);
return exists || (await collection.find({}).toArray()).length > 0; return exists || (await collection.find({}).toArray()).length > 0;
} }
return exists || existsSync(join(INSTANCE_DIR, instanceName)); return exists || existsSync(join(INSTANCE_DIR, instanceName));
} catch (error) { } catch (error) {
throw new InternalServerErrorException(error?.toString()); throw new InternalServerErrorException(error?.toString());
} }
} }
export async function instanceExistsGuard(req: Request, _: Response, next: NextFunction) { export async function instanceExistsGuard(req: Request, _: Response, next: NextFunction) {
if (req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) { if (req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) {
return next(); return next();
} }
const param = req.params as unknown as InstanceDto; const param = req.params as unknown as InstanceDto;
if (!param?.instanceName) { if (!param?.instanceName) {
throw new BadRequestException('"instanceName" not provided.'); throw new BadRequestException('"instanceName" not provided.');
} }
if (!(await getInstance(param.instanceName))) { if (!(await getInstance(param.instanceName))) {
throw new NotFoundException(`The "${param.instanceName}" instance does not exist`); throw new NotFoundException(`The "${param.instanceName}" instance does not exist`);
} }
next(); next();
} }
export async function instanceLoggedGuard(req: Request, _: Response, next: NextFunction) { export async function instanceLoggedGuard(req: Request, _: Response, next: NextFunction) {
if (req.originalUrl.includes('/instance/create')) { if (req.originalUrl.includes('/instance/create')) {
const instance = req.body as InstanceDto; const instance = req.body as InstanceDto;
if (await getInstance(instance.instanceName)) { if (await getInstance(instance.instanceName)) {
throw new ForbiddenException(`This name "${instance.instanceName}" is already in use.`); throw new ForbiddenException(`This name "${instance.instanceName}" is already in use.`);
} }
if (waMonitor.waInstances[instance.instanceName]) { if (waMonitor.waInstances[instance.instanceName]) {
waMonitor.waInstances[instance.instanceName]?.removeRabbitmqQueues(); waMonitor.waInstances[instance.instanceName]?.removeRabbitmqQueues();
delete waMonitor.waInstances[instance.instanceName]; delete waMonitor.waInstances[instance.instanceName];
} }
} }
next(); next();
} }

40
src/whatsapp/models/contactOpenai.model.ts Executable file → Normal file
View File

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

30
src/whatsapp/models/index.ts Executable file → Normal file
View File

@ -1,15 +1,15 @@
export * from './auth.model'; export * from './auth.model';
export * from './chamaai.model'; export * from './chamaai.model';
export * from './chat.model'; export * from './chat.model';
export * from './chatwoot.model'; export * from './chatwoot.model';
export * from './contact.model'; export * from './contact.model';
export * from './message.model'; export * from './message.model';
export * from './proxy.model'; export * from './proxy.model';
export * from './rabbitmq.model'; export * from './rabbitmq.model';
export * from './settings.model'; export * from './settings.model';
export * from './sqs.model'; export * from './sqs.model';
export * from './typebot.model'; export * from './typebot.model';
export * from './webhook.model'; export * from './webhook.model';
export * from './websocket.model'; export * from './websocket.model';
export * from './openai.model'; export * from './openai.model';
export * from './contactOpenai.model'; export * from './contactOpenai.model';

44
src/whatsapp/models/openai.model.ts Executable file → Normal file
View File

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

34
src/whatsapp/models/sqs.model.ts Executable file → Normal file
View File

@ -1,18 +1,18 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../libs/db.connect';
export class SqsRaw { export class SqsRaw {
_id?: string; _id?: string;
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
} }
const sqsSchema = new Schema<SqsRaw>({ const sqsSchema = new Schema<SqsRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
enabled: { type: Boolean, required: true }, enabled: { type: Boolean, required: true },
events: { type: [String], required: true }, events: { type: [String], required: true },
}); });
export const SqsModel = dbserver?.model(SqsRaw.name, sqsSchema, 'sqs'); export const SqsModel = dbserver?.model(SqsRaw.name, sqsSchema, 'sqs');
export type ISqsModel = typeof SqsModel; export type ISqsModel = typeof SqsModel;

306
src/whatsapp/repository/openai.repository.ts Executable file → Normal file
View File

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

338
src/whatsapp/repository/repository.manager.ts Executable file → Normal file
View File

@ -1,169 +1,169 @@
import fs from 'fs'; import fs from 'fs';
import { MongoClient } from 'mongodb'; import { MongoClient } from 'mongodb';
import { join } from 'path'; import { join } from 'path';
import { Auth, ConfigService, Database } from '../../config/env.config'; import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { AuthRepository } from './auth.repository'; import { AuthRepository } from './auth.repository';
import { ChamaaiRepository } from './chamaai.repository'; import { ChamaaiRepository } from './chamaai.repository';
import { ChatRepository } from './chat.repository'; import { ChatRepository } from './chat.repository';
import { ChatwootRepository } from './chatwoot.repository'; import { ChatwootRepository } from './chatwoot.repository';
import { ContactRepository } from './contact.repository'; import { ContactRepository } from './contact.repository';
import { MessageRepository } from './message.repository'; import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository'; import { MessageUpRepository } from './messageUp.repository';
import { ProxyRepository } from './proxy.repository'; import { ProxyRepository } from './proxy.repository';
import { RabbitmqRepository } from './rabbitmq.repository'; import { RabbitmqRepository } from './rabbitmq.repository';
import { OpenaiRepository } from './openai.repository'; import { OpenaiRepository } from './openai.repository';
import { SettingsRepository } from './settings.repository'; import { SettingsRepository } from './settings.repository';
import { SqsRepository } from './sqs.repository'; import { SqsRepository } from './sqs.repository';
import { TypebotRepository } from './typebot.repository'; import { TypebotRepository } from './typebot.repository';
import { WebhookRepository } from './webhook.repository'; import { WebhookRepository } from './webhook.repository';
import { WebsocketRepository } from './websocket.repository'; import { WebsocketRepository } from './websocket.repository';
export class RepositoryBroker { export class RepositoryBroker {
constructor( constructor(
public readonly message: MessageRepository, public readonly message: MessageRepository,
public readonly chat: ChatRepository, public readonly chat: ChatRepository,
public readonly contact: ContactRepository, public readonly contact: ContactRepository,
public readonly messageUpdate: MessageUpRepository, public readonly messageUpdate: MessageUpRepository,
public readonly webhook: WebhookRepository, public readonly webhook: WebhookRepository,
public readonly chatwoot: ChatwootRepository, public readonly chatwoot: ChatwootRepository,
public readonly settings: SettingsRepository, public readonly settings: SettingsRepository,
public readonly websocket: WebsocketRepository, public readonly websocket: WebsocketRepository,
public readonly rabbitmq: RabbitmqRepository, public readonly rabbitmq: RabbitmqRepository,
public readonly openai: OpenaiRepository, public readonly openai: OpenaiRepository,
public readonly openai_contact: OpenaiRepository, public readonly openai_contact: OpenaiRepository,
public readonly sqs: SqsRepository, public readonly sqs: SqsRepository,
public readonly typebot: TypebotRepository, public readonly typebot: TypebotRepository,
public readonly proxy: ProxyRepository, public readonly proxy: ProxyRepository,
public readonly chamaai: ChamaaiRepository, public readonly chamaai: ChamaaiRepository,
public readonly auth: AuthRepository, public readonly auth: AuthRepository,
private configService: ConfigService, private configService: ConfigService,
dbServer?: MongoClient, dbServer?: MongoClient,
) { ) {
this.dbClient = dbServer; this.dbClient = dbServer;
this.__init_repo_without_db__(); this.__init_repo_without_db__();
} }
private dbClient?: MongoClient; private dbClient?: MongoClient;
private readonly logger = new Logger('RepositoryBroker'); private readonly logger = new Logger('RepositoryBroker');
public get dbServer() { public get dbServer() {
return this.dbClient; return this.dbClient;
} }
private __init_repo_without_db__() { private __init_repo_without_db__() {
this.logger.verbose('initializing repository without db'); this.logger.verbose('initializing repository without db');
if (!this.configService.get<Database>('DATABASE').ENABLED) { if (!this.configService.get<Database>('DATABASE').ENABLED) {
const storePath = join(process.cwd(), 'store'); const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath); this.logger.verbose('creating store path: ' + storePath);
try { try {
const authDir = join(storePath, 'auth', this.configService.get<Auth>('AUTHENTICATION').TYPE); const authDir = join(storePath, 'auth', this.configService.get<Auth>('AUTHENTICATION').TYPE);
const chatsDir = join(storePath, 'chats'); const chatsDir = join(storePath, 'chats');
const contactsDir = join(storePath, 'contacts'); const contactsDir = join(storePath, 'contacts');
const messagesDir = join(storePath, 'messages'); const messagesDir = join(storePath, 'messages');
const messageUpDir = join(storePath, 'message-up'); const messageUpDir = join(storePath, 'message-up');
const webhookDir = join(storePath, 'webhook'); const webhookDir = join(storePath, 'webhook');
const chatwootDir = join(storePath, 'chatwoot'); const chatwootDir = join(storePath, 'chatwoot');
const settingsDir = join(storePath, 'settings'); const settingsDir = join(storePath, 'settings');
const sqsDir = join(storePath, 'sqs'); const sqsDir = join(storePath, 'sqs');
const websocketDir = join(storePath, 'websocket'); const websocketDir = join(storePath, 'websocket');
const rabbitmqDir = join(storePath, 'rabbitmq'); const rabbitmqDir = join(storePath, 'rabbitmq');
const openaiDir = join(storePath, 'openai'); const openaiDir = join(storePath, 'openai');
const typebotDir = join(storePath, 'typebot'); const typebotDir = join(storePath, 'typebot');
const proxyDir = join(storePath, 'proxy'); const proxyDir = join(storePath, 'proxy');
const chamaaiDir = join(storePath, 'chamaai'); const chamaaiDir = join(storePath, 'chamaai');
const tempDir = join(storePath, 'temp'); const tempDir = join(storePath, 'temp');
if (!fs.existsSync(authDir)) { if (!fs.existsSync(authDir)) {
this.logger.verbose('creating auth dir: ' + authDir); this.logger.verbose('creating auth dir: ' + authDir);
fs.mkdirSync(authDir, { recursive: true }); fs.mkdirSync(authDir, { recursive: true });
} }
if (!fs.existsSync(chatsDir)) { if (!fs.existsSync(chatsDir)) {
this.logger.verbose('creating chats dir: ' + chatsDir); this.logger.verbose('creating chats dir: ' + chatsDir);
fs.mkdirSync(chatsDir, { recursive: true }); fs.mkdirSync(chatsDir, { recursive: true });
} }
if (!fs.existsSync(contactsDir)) { if (!fs.existsSync(contactsDir)) {
this.logger.verbose('creating contacts dir: ' + contactsDir); this.logger.verbose('creating contacts dir: ' + contactsDir);
fs.mkdirSync(contactsDir, { recursive: true }); fs.mkdirSync(contactsDir, { recursive: true });
} }
if (!fs.existsSync(messagesDir)) { if (!fs.existsSync(messagesDir)) {
this.logger.verbose('creating messages dir: ' + messagesDir); this.logger.verbose('creating messages dir: ' + messagesDir);
fs.mkdirSync(messagesDir, { recursive: true }); fs.mkdirSync(messagesDir, { recursive: true });
} }
if (!fs.existsSync(messageUpDir)) { if (!fs.existsSync(messageUpDir)) {
this.logger.verbose('creating message-up dir: ' + messageUpDir); this.logger.verbose('creating message-up dir: ' + messageUpDir);
fs.mkdirSync(messageUpDir, { recursive: true }); fs.mkdirSync(messageUpDir, { recursive: true });
} }
if (!fs.existsSync(webhookDir)) { if (!fs.existsSync(webhookDir)) {
this.logger.verbose('creating webhook dir: ' + webhookDir); this.logger.verbose('creating webhook dir: ' + webhookDir);
fs.mkdirSync(webhookDir, { recursive: true }); fs.mkdirSync(webhookDir, { recursive: true });
} }
if (!fs.existsSync(chatwootDir)) { if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir); this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true }); fs.mkdirSync(chatwootDir, { recursive: true });
} }
if (!fs.existsSync(settingsDir)) { if (!fs.existsSync(settingsDir)) {
this.logger.verbose('creating settings dir: ' + settingsDir); this.logger.verbose('creating settings dir: ' + settingsDir);
fs.mkdirSync(settingsDir, { recursive: true }); fs.mkdirSync(settingsDir, { recursive: true });
} }
if (!fs.existsSync(sqsDir)) { if (!fs.existsSync(sqsDir)) {
this.logger.verbose('creating sqs dir: ' + sqsDir); this.logger.verbose('creating sqs dir: ' + sqsDir);
fs.mkdirSync(sqsDir, { recursive: true }); fs.mkdirSync(sqsDir, { recursive: true });
} }
if (!fs.existsSync(websocketDir)) { if (!fs.existsSync(websocketDir)) {
this.logger.verbose('creating websocket dir: ' + websocketDir); this.logger.verbose('creating websocket dir: ' + websocketDir);
fs.mkdirSync(websocketDir, { recursive: true }); fs.mkdirSync(websocketDir, { recursive: true });
} }
if (!fs.existsSync(rabbitmqDir)) { if (!fs.existsSync(rabbitmqDir)) {
this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir); this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir);
fs.mkdirSync(rabbitmqDir, { recursive: true }); fs.mkdirSync(rabbitmqDir, { recursive: true });
} }
if (!fs.existsSync(openaiDir)) { if (!fs.existsSync(openaiDir)) {
this.logger.verbose('creating openai dir: ' + openaiDir); this.logger.verbose('creating openai dir: ' + openaiDir);
fs.mkdirSync(openaiDir, { recursive: true }); fs.mkdirSync(openaiDir, { recursive: true });
} }
if (!fs.existsSync(typebotDir)) { if (!fs.existsSync(typebotDir)) {
this.logger.verbose('creating typebot dir: ' + typebotDir); this.logger.verbose('creating typebot dir: ' + typebotDir);
fs.mkdirSync(typebotDir, { recursive: true }); fs.mkdirSync(typebotDir, { recursive: true });
} }
if (!fs.existsSync(proxyDir)) { if (!fs.existsSync(proxyDir)) {
this.logger.verbose('creating proxy dir: ' + proxyDir); this.logger.verbose('creating proxy dir: ' + proxyDir);
fs.mkdirSync(proxyDir, { recursive: true }); fs.mkdirSync(proxyDir, { recursive: true });
} }
if (!fs.existsSync(chamaaiDir)) { if (!fs.existsSync(chamaaiDir)) {
this.logger.verbose('creating chamaai dir: ' + chamaaiDir); this.logger.verbose('creating chamaai dir: ' + chamaaiDir);
fs.mkdirSync(chamaaiDir, { recursive: true }); fs.mkdirSync(chamaaiDir, { recursive: true });
} }
if (!fs.existsSync(tempDir)) { if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir); this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true }); fs.mkdirSync(tempDir, { recursive: true });
} }
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} else { } else {
try { try {
const storePath = join(process.cwd(), 'store'); const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath); this.logger.verbose('creating store path: ' + storePath);
const tempDir = join(storePath, 'temp'); const tempDir = join(storePath, 'temp');
const chatwootDir = join(storePath, 'chatwoot'); const chatwootDir = join(storePath, 'chatwoot');
if (!fs.existsSync(chatwootDir)) { if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir); this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true }); fs.mkdirSync(chatwootDir, { recursive: true });
} }
if (!fs.existsSync(tempDir)) { if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir); this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true }); fs.mkdirSync(tempDir, { recursive: true });
} }
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} }
} }
} }

122
src/whatsapp/repository/sqs.repository.ts Executable file → Normal file
View File

@ -1,62 +1,62 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../abstract/abstract.repository';
import { ISqsModel, SqsRaw } from '../models'; import { ISqsModel, SqsRaw } from '../models';
export class SqsRepository extends Repository { export class SqsRepository extends Repository {
constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) { constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) {
super(configService); super(configService);
} }
private readonly logger = new Logger('SqsRepository'); private readonly logger = new Logger('SqsRepository');
public async create(data: SqsRaw, instance: string): Promise<IInsert> { public async create(data: SqsRaw, instance: string): Promise<IInsert> {
try { try {
this.logger.verbose('creating sqs'); this.logger.verbose('creating sqs');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('saving sqs to db'); this.logger.verbose('saving sqs to db');
const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true }); const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs'); this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs');
return { insertCount: insert.modifiedCount }; return { insertCount: insert.modifiedCount };
} }
this.logger.verbose('saving sqs to store'); this.logger.verbose('saving sqs to store');
this.writeStore<SqsRaw>({ this.writeStore<SqsRaw>({
path: join(this.storePath, 'sqs'), path: join(this.storePath, 'sqs'),
fileName: instance, fileName: instance,
data, data,
}); });
this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance); this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance);
this.logger.verbose('sqs created'); this.logger.verbose('sqs created');
return { insertCount: 1 }; return { insertCount: 1 };
} catch (error) { } catch (error) {
return error; return error;
} }
} }
public async find(instance: string): Promise<SqsRaw> { public async find(instance: string): Promise<SqsRaw> {
try { try {
this.logger.verbose('finding sqs'); this.logger.verbose('finding sqs');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('finding sqs in db'); this.logger.verbose('finding sqs in db');
return await this.sqsModel.findOne({ _id: instance }); return await this.sqsModel.findOne({ _id: instance });
} }
this.logger.verbose('finding sqs in store'); this.logger.verbose('finding sqs in store');
return JSON.parse( return JSON.parse(
readFileSync(join(this.storePath, 'sqs', instance + '.json'), { readFileSync(join(this.storePath, 'sqs', instance + '.json'), {
encoding: 'utf-8', encoding: 'utf-8',
}), }),
) as SqsRaw; ) as SqsRaw;
} catch (error) { } catch (error) {
return {}; return {};
} }
} }
} }

126
src/whatsapp/routers/index.router.ts Executable file → Normal file
View File

@ -1,63 +1,63 @@
import { Router } from 'express'; import { Router } from 'express';
import fs from 'fs'; import fs from 'fs';
import { Auth, configService } from '../../config/env.config'; import { Auth, configService } from '../../config/env.config';
import { authGuard } from '../guards/auth.guard'; import { authGuard } from '../guards/auth.guard';
import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard'; import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard';
import { ChamaaiRouter } from './chamaai.router'; import { ChamaaiRouter } from './chamaai.router';
import { ChatRouter } from './chat.router'; import { ChatRouter } from './chat.router';
import { ChatwootRouter } from './chatwoot.router'; import { ChatwootRouter } from './chatwoot.router';
import { GroupRouter } from './group.router'; import { GroupRouter } from './group.router';
import { InstanceRouter } from './instance.router'; import { InstanceRouter } from './instance.router';
import { ProxyRouter } from './proxy.router'; import { ProxyRouter } from './proxy.router';
import { RabbitmqRouter } from './rabbitmq.router'; import { RabbitmqRouter } from './rabbitmq.router';
import { OpenaiRouter } from './openai.router'; import { OpenaiRouter } from './openai.router';
import { MessageRouter } from './sendMessage.router'; import { MessageRouter } from './sendMessage.router';
import { SettingsRouter } from './settings.router'; import { SettingsRouter } from './settings.router';
import { SqsRouter } from './sqs.router'; import { SqsRouter } from './sqs.router';
import { TypebotRouter } from './typebot.router'; import { TypebotRouter } from './typebot.router';
import { ViewsRouter } from './view.router'; import { ViewsRouter } from './view.router';
import { WebhookRouter } from './webhook.router'; import { WebhookRouter } from './webhook.router';
import { WebsocketRouter } from './websocket.router'; import { WebsocketRouter } from './websocket.router';
enum HttpStatus { enum HttpStatus {
OK = 200, OK = 200,
CREATED = 201, CREATED = 201,
NOT_FOUND = 404, NOT_FOUND = 404,
FORBIDDEN = 403, FORBIDDEN = 403,
BAD_REQUEST = 400, BAD_REQUEST = 400,
UNAUTHORIZED = 401, UNAUTHORIZED = 401,
INTERNAL_SERVER_ERROR = 500, INTERNAL_SERVER_ERROR = 500,
} }
const router = Router(); const router = Router();
const authType = configService.get<Auth>('AUTHENTICATION').TYPE; const authType = configService.get<Auth>('AUTHENTICATION').TYPE;
const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]]; const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')); const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
router router
.get('/', (req, res) => { .get('/', (req, res) => {
res.status(HttpStatus.OK).json({ res.status(HttpStatus.OK).json({
status: HttpStatus.OK, status: HttpStatus.OK,
message: 'Api', message: 'Api',
// version: packageJson.version, // version: packageJson.version,
// documentation: `${req.protocol}://${req.get('host')}/docs`, // documentation: `${req.protocol}://${req.get('host')}/docs`,
}); });
}) })
.use('/instance', new InstanceRouter(configService, ...guards).router) .use('/instance', new InstanceRouter(configService, ...guards).router)
.use('/manager', new ViewsRouter().router) .use('/manager', new ViewsRouter().router)
.use('/message', new MessageRouter(...guards).router) .use('/message', new MessageRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router) .use('/chat', new ChatRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router) .use('/group', new GroupRouter(...guards).router)
.use('/webhook', new WebhookRouter(...guards).router) .use('/webhook', new WebhookRouter(...guards).router)
.use('/chatwoot', new ChatwootRouter(...guards).router) .use('/chatwoot', new ChatwootRouter(...guards).router)
.use('/settings', new SettingsRouter(...guards).router) .use('/settings', new SettingsRouter(...guards).router)
.use('/websocket', new WebsocketRouter(...guards).router) .use('/websocket', new WebsocketRouter(...guards).router)
.use('/rabbitmq', new RabbitmqRouter(...guards).router) .use('/rabbitmq', new RabbitmqRouter(...guards).router)
.use('/openai', new OpenaiRouter(...guards).router) .use('/openai', new OpenaiRouter(...guards).router)
.use('/typebot', new TypebotRouter(...guards).router) .use('/typebot', new TypebotRouter(...guards).router)
.use('/proxy', new ProxyRouter(...guards).router) .use('/proxy', new ProxyRouter(...guards).router)
.use('/chamaai', new ChamaaiRouter(...guards).router); .use('/chamaai', new ChamaaiRouter(...guards).router);
export { HttpStatus, router }; export { HttpStatus, router };

402
src/whatsapp/routers/instance.router.ts Executable file → Normal file
View File

@ -1,201 +1,201 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Auth, ConfigService, Database } from '../../config/env.config'; import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../libs/db.connect';
import { instanceNameSchema, oldTokenSchema } from '../../validate/validate.schema'; import { instanceNameSchema, oldTokenSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { OldToken } from '../services/auth.service'; import { OldToken } from '../services/auth.service';
import { instanceController } from '../whatsapp.module'; import { instanceController } from '../whatsapp.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
const logger = new Logger('InstanceRouter'); const logger = new Logger('InstanceRouter');
export class InstanceRouter extends RouterBroker { export class InstanceRouter extends RouterBroker {
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) { constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
super(); super();
const auth = configService.get<Auth>('AUTHENTICATION'); const auth = configService.get<Auth>('AUTHENTICATION');
this.router this.router
.post('/create', ...guards, async (req, res) => { .post('/create', ...guards, async (req, res) => {
logger.verbose('request received in createInstance'); logger.verbose('request received in createInstance');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.createInstance(instance), execute: (instance) => instanceController.createInstance(instance),
}); });
if (req.query['qrcode']) { if (req.query['qrcode']) {
return res.status(HttpStatus.OK).render('qrcode', { return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response.qrcode.base64, qrcode: response.qrcode.base64,
}); });
} else { } else {
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
} }
//return res.status(HttpStatus.CREATED).json(response); //return res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('restart'), ...guards, async (req, res) => { .put(this.routerPath('restart'), ...guards, async (req, res) => {
logger.verbose('request received in restartInstance'); logger.verbose('request received in restartInstance');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.restartInstance(instance), execute: (instance) => instanceController.restartInstance(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('qr'), ...guards, async (req, res) => { .get(this.routerPath('qr'), ...guards, async (req, res) => {
logger.verbose('request received in get qrCode'); logger.verbose('request received in get qrCode');
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.qrInstance(instance), execute: (instance) => instanceController.qrInstance(instance),
}); });
return res.status(HttpStatus.OK).render('qrcode', { return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response, qrcode: response,
}); });
}) })
.get(this.routerPath('connect'), ...guards, async (req, res) => { .get(this.routerPath('connect'), ...guards, async (req, res) => {
logger.verbose('request received in connectInstance'); logger.verbose('request received in connectInstance');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.connectToWhatsapp(instance), execute: (instance) => instanceController.connectToWhatsapp(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('connectionState'), ...guards, async (req, res) => { .get(this.routerPath('connectionState'), ...guards, async (req, res) => {
logger.verbose('request received in connectionState'); logger.verbose('request received in connectionState');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.connectionState(instance), execute: (instance) => instanceController.connectionState(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => { .get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => {
logger.verbose('request received in fetchInstances'); logger.verbose('request received in fetchInstances');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: null, schema: null,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.fetchInstances(instance), execute: (instance) => instanceController.fetchInstances(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.delete(this.routerPath('logout'), ...guards, async (req, res) => { .delete(this.routerPath('logout'), ...guards, async (req, res) => {
logger.verbose('request received in logoutInstances'); logger.verbose('request received in logoutInstances');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.logout(instance), execute: (instance) => instanceController.logout(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.delete(this.routerPath('delete'), ...guards, async (req, res) => { .delete(this.routerPath('delete'), ...guards, async (req, res) => {
logger.verbose('request received in deleteInstances'); logger.verbose('request received in deleteInstances');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.deleteInstance(instance), execute: (instance) => instanceController.deleteInstance(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}); });
if (auth.TYPE === 'jwt') { if (auth.TYPE === 'jwt') {
this.router.put('/refreshToken', async (req, res) => { this.router.put('/refreshToken', async (req, res) => {
logger.verbose('request received in refreshToken'); logger.verbose('request received in refreshToken');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<OldToken>({ const response = await this.dataValidate<OldToken>({
request: req, request: req,
schema: oldTokenSchema, schema: oldTokenSchema,
ClassRef: OldToken, ClassRef: OldToken,
execute: (_, data) => instanceController.refreshToken(_, data), execute: (_, data) => instanceController.refreshToken(_, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}); });
} }
this.router.delete('/deleteDatabase', async (req, res) => { this.router.delete('/deleteDatabase', async (req, res) => {
logger.verbose('request received in deleteDatabase'); logger.verbose('request received in deleteDatabase');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const db = this.configService.get<Database>('DATABASE'); const db = this.configService.get<Database>('DATABASE');
if (db.ENABLED) { if (db.ENABLED) {
try { try {
await dbserver.dropDatabase(); await dbserver.dropDatabase();
return res return res
.status(HttpStatus.CREATED) .status(HttpStatus.CREATED)
.json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } }); .json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } });
} catch (error) { } catch (error) {
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: error.message }); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: error.message });
} }
} }
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: 'Database is not enabled' }); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: 'Database is not enabled' });
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

172
src/whatsapp/routers/openai.router.ts Executable file → Normal file
View File

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

102
src/whatsapp/routers/sqs.router.ts Executable file → Normal file
View File

@ -1,52 +1,52 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { instanceNameSchema, sqsSchema } from '../../validate/validate.schema'; import { instanceNameSchema, sqsSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto'; import { SqsDto } from '../dto/sqs.dto';
import { sqsController } from '../whatsapp.module'; import { sqsController } from '../whatsapp.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
const logger = new Logger('SqsRouter'); const logger = new Logger('SqsRouter');
export class SqsRouter extends RouterBroker { export class SqsRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('set'), ...guards, async (req, res) => { .post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setSqs'); logger.verbose('request received in setSqs');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SqsDto>({ const response = await this.dataValidate<SqsDto>({
request: req, request: req,
schema: sqsSchema, schema: sqsSchema,
ClassRef: SqsDto, ClassRef: SqsDto,
execute: (instance, data) => sqsController.createSqs(instance, data), execute: (instance, data) => sqsController.createSqs(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.get(this.routerPath('find'), ...guards, async (req, res) => { .get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findSqs'); logger.verbose('request received in findSqs');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => sqsController.findSqs(instance), execute: (instance) => sqsController.findSqs(instance),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

Binary file not shown.

3316
src/whatsapp/services/chatwoot.service.ts Executable file → Normal file

File diff suppressed because it is too large Load Diff

796
src/whatsapp/services/monitor.service.ts Executable file → Normal file
View File

@ -1,398 +1,398 @@
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
import { opendirSync, readdirSync, rmSync } from 'fs'; import { opendirSync, readdirSync, rmSync } from 'fs';
import { Db } from 'mongodb'; import { Db } from 'mongodb';
import { join } from 'path'; import { join } from 'path';
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config'; import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config'; import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
import { NotFoundException } from '../../exceptions'; import { NotFoundException } from '../../exceptions';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../libs/db.connect';
import { RedisCache } from '../../libs/redis.client'; import { RedisCache } from '../../libs/redis.client';
import { import {
AuthModel, AuthModel,
ChamaaiModel, ChamaaiModel,
ChatModel, ChatModel,
ChatwootModel, ChatwootModel,
ContactModel, ContactModel,
MessageModel, MessageModel,
MessageUpModel, MessageUpModel,
ProxyModel, ProxyModel,
RabbitmqModel, RabbitmqModel,
SettingsModel, SettingsModel,
TypebotModel, TypebotModel,
WebhookModel, WebhookModel,
WebsocketModel, WebsocketModel,
} from '../models'; } from '../models';
import { RepositoryBroker } from '../repository/repository.manager'; import { RepositoryBroker } from '../repository/repository.manager';
import { WAStartupService } from './whatsapp.service'; import { WAStartupService } from './whatsapp.service';
export class WAMonitoringService { export class WAMonitoringService {
constructor( constructor(
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly repository: RepositoryBroker, private readonly repository: RepositoryBroker,
private readonly cache: RedisCache, private readonly cache: RedisCache,
) { ) {
this.logger.verbose('instance created'); this.logger.verbose('instance created');
this.removeInstance(); this.removeInstance();
this.noConnection(); this.noConnection();
this.delInstanceFiles(); this.delInstanceFiles();
Object.assign(this.db, configService.get<Database>('DATABASE')); Object.assign(this.db, configService.get<Database>('DATABASE'));
Object.assign(this.redis, configService.get<Redis>('REDIS')); Object.assign(this.redis, configService.get<Redis>('REDIS'));
this.dbInstance = this.db.ENABLED this.dbInstance = this.db.ENABLED
? this.repository.dbServer?.db( ? this.repository.dbServer?.db(
this.db.CONNECTION.DB_PREFIX_NAME + this.db.CONNECTION.DB_PREFIX_NAME +
this.db.CONNECTION.DB_PREFIX_FINAL_NAME this.db.CONNECTION.DB_PREFIX_FINAL_NAME
) )
: undefined; : undefined;
} }
private readonly db: Partial<Database> = {}; private readonly db: Partial<Database> = {};
private readonly redis: Partial<Redis> = {}; private readonly redis: Partial<Redis> = {};
private dbInstance: Db; private dbInstance: Db;
private dbStore = dbserver; private dbStore = dbserver;
private readonly logger = new Logger(WAMonitoringService.name); private readonly logger = new Logger(WAMonitoringService.name);
public readonly waInstances: Record<string, WAStartupService> = {}; public readonly waInstances: Record<string, WAStartupService> = {};
public delInstanceTime(instance: string) { public delInstanceTime(instance: string) {
const time = this.configService.get<DelInstance>('DEL_INSTANCE'); const time = this.configService.get<DelInstance>('DEL_INSTANCE');
if (typeof time === 'number' && time > 0) { if (typeof time === 'number' && time > 0) {
this.logger.verbose(`Instance "${instance}" don't have connection, will be removed in ${time} minutes`); this.logger.verbose(`Instance "${instance}" don't have connection, will be removed in ${time} minutes`);
setTimeout(async () => { setTimeout(async () => {
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') { if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') { if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') {
await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance); await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance);
this.waInstances[instance]?.client?.ws?.close(); this.waInstances[instance]?.client?.ws?.close();
this.waInstances[instance]?.client?.end(undefined); this.waInstances[instance]?.client?.end(undefined);
this.waInstances[instance]?.removeRabbitmqQueues(); this.waInstances[instance]?.removeRabbitmqQueues();
delete this.waInstances[instance]; delete this.waInstances[instance];
} else { } else {
this.waInstances[instance]?.removeRabbitmqQueues(); this.waInstances[instance]?.removeRabbitmqQueues();
delete this.waInstances[instance]; delete this.waInstances[instance];
this.eventEmitter.emit('remove.instance', instance, 'inner'); this.eventEmitter.emit('remove.instance', instance, 'inner');
} }
} }
}, 1000 * 60 * time); }, 1000 * 60 * time);
} }
} }
public async instanceInfo(instanceName?: string) { public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info'); this.logger.verbose('get instance info');
if (instanceName && !this.waInstances[instanceName]) { if (instanceName && !this.waInstances[instanceName]) {
throw new NotFoundException(`Instance "${instanceName}" not found`); throw new NotFoundException(`Instance "${instanceName}" not found`);
} }
const instances: any[] = []; const instances: any[] = [];
for await (const [key, value] of Object.entries(this.waInstances)) { for await (const [key, value] of Object.entries(this.waInstances)) {
if (value) { if (value) {
this.logger.verbose('get instance info: ' + key); this.logger.verbose('get instance info: ' + key);
let chatwoot: any; let chatwoot: any;
const urlServer = this.configService.get<HttpServer>('SERVER').URL; const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const findChatwoot = await this.waInstances[key].findChatwoot(); const findChatwoot = await this.waInstances[key].findChatwoot();
if (findChatwoot && findChatwoot.enabled) { if (findChatwoot && findChatwoot.enabled) {
chatwoot = { chatwoot = {
...findChatwoot, ...findChatwoot,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`, webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`,
}; };
} }
if (value.connectionStatus.state === 'open') { if (value.connectionStatus.state === 'open') {
this.logger.verbose('instance: ' + key + ' - connectionStatus: open'); this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
const instanceData = { const instanceData = {
instance: { instance: {
instanceName: key, instanceName: key,
owner: value.wuid, owner: value.wuid,
profileName: (await value.getProfileName()) || 'not loaded', profileName: (await value.getProfileName()) || 'not loaded',
profilePictureUrl: value.profilePictureUrl, profilePictureUrl: value.profilePictureUrl,
profileStatus: (await value.getProfileStatus()) || '', profileStatus: (await value.getProfileStatus()) || '',
status: value.connectionStatus.state, status: value.connectionStatus.state,
}, },
}; };
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL; instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey;
instanceData.instance['chatwoot'] = chatwoot; instanceData.instance['chatwoot'] = chatwoot;
} }
instances.push(instanceData); instances.push(instanceData);
} else { } else {
this.logger.verbose('instance: ' + key + ' - connectionStatus: ' + value.connectionStatus.state); this.logger.verbose('instance: ' + key + ' - connectionStatus: ' + value.connectionStatus.state);
const instanceData = { const instanceData = {
instance: { instance: {
instanceName: key, instanceName: key,
status: value.connectionStatus.state, status: value.connectionStatus.state,
}, },
}; };
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL; instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey;
instanceData.instance['chatwoot'] = chatwoot; instanceData.instance['chatwoot'] = chatwoot;
} }
instances.push(instanceData); instances.push(instanceData);
} }
} }
} }
this.logger.verbose('return instance info: ' + instances.length); this.logger.verbose('return instance info: ' + instances.length);
return instances.find((i) => i.instance.instanceName === instanceName) ?? instances; return instances.find((i) => i.instance.instanceName === instanceName) ?? instances;
} }
private delInstanceFiles() { private delInstanceFiles() {
this.logger.verbose('cron to delete instance files started'); this.logger.verbose('cron to delete instance files started');
setInterval(async () => { setInterval(async () => {
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
const collections = await this.dbInstance.collections(); const collections = await this.dbInstance.collections();
collections.forEach(async (collection) => { collections.forEach(async (collection) => {
const name = collection.namespace.replace(/^[\w-]+./, ''); const name = collection.namespace.replace(/^[\w-]+./, '');
await this.dbInstance.collection(name).deleteMany({ await this.dbInstance.collection(name).deleteMany({
$or: [{ _id: { $regex: /^app.state.*/ } }, { _id: { $regex: /^session-.*/ } }], $or: [{ _id: { $regex: /^app.state.*/ } }, { _id: { $regex: /^session-.*/ } }],
}); });
this.logger.verbose('instance files deleted: ' + name); this.logger.verbose('instance files deleted: ' + name);
}); });
} else if (!this.redis.ENABLED) { } else if (!this.redis.ENABLED) {
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) { for await (const dirent of dir) {
if (dirent.isDirectory()) { if (dirent.isDirectory()) {
const files = readdirSync(join(INSTANCE_DIR, dirent.name), { const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
encoding: 'utf-8', encoding: 'utf-8',
}); });
files.forEach(async (file) => { files.forEach(async (file) => {
if (file.match(/^app.state.*/) || file.match(/^session-.*/)) { if (file.match(/^app.state.*/) || file.match(/^session-.*/)) {
rmSync(join(INSTANCE_DIR, dirent.name, file), { rmSync(join(INSTANCE_DIR, dirent.name, file), {
recursive: true, recursive: true,
force: true, force: true,
}); });
} }
}); });
this.logger.verbose('instance files deleted: ' + dirent.name); this.logger.verbose('instance files deleted: ' + dirent.name);
} }
} }
} }
}, 3600 * 1000 * 2); }, 3600 * 1000 * 2);
} }
public async cleaningUp(instanceName: string) { public async cleaningUp(instanceName: string) {
this.logger.verbose('cleaning up instance: ' + instanceName); this.logger.verbose('cleaning up instance: ' + instanceName);
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
this.logger.verbose('cleaning up instance in database: ' + instanceName); this.logger.verbose('cleaning up instance in database: ' + instanceName);
await this.repository.dbServer.connect(); await this.repository.dbServer.connect();
const collections: any[] = await this.dbInstance.collections(); const collections: any[] = await this.dbInstance.collections();
if (collections.length > 0) { if (collections.length > 0) {
await this.dbInstance.dropCollection(instanceName); await this.dbInstance.dropCollection(instanceName);
} }
return; return;
} }
if (this.redis.ENABLED) { if (this.redis.ENABLED) {
this.logger.verbose('cleaning up instance in redis: ' + instanceName); this.logger.verbose('cleaning up instance in redis: ' + instanceName);
this.cache.reference = instanceName; this.cache.reference = instanceName;
await this.cache.delAll(); await this.cache.delAll();
return; return;
} }
this.logger.verbose('cleaning up instance in files: ' + instanceName); this.logger.verbose('cleaning up instance in files: ' + instanceName);
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
} }
public async cleaningStoreFiles(instanceName: string) { public async cleaningStoreFiles(instanceName: string) {
if (!this.db.ENABLED) { if (!this.db.ENABLED) {
this.logger.verbose('cleaning store files instance: ' + instanceName); this.logger.verbose('cleaning store files instance: ' + instanceName);
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`); execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`); execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`); execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`); execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`); execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`);
execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`); execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`);
execSync(`rm -rf ${join(STORE_DIR, 'chatwoot', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'chatwoot', instanceName + '*')}`);
execSync(`rm -rf ${join(STORE_DIR, 'chamaai', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'chamaai', instanceName + '*')}`);
execSync(`rm -rf ${join(STORE_DIR, 'proxy', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'proxy', instanceName + '*')}`);
execSync(`rm -rf ${join(STORE_DIR, 'rabbitmq', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'rabbitmq', instanceName + '*')}`);
execSync(`rm -rf ${join(STORE_DIR, 'typebot', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'typebot', instanceName + '*')}`);
execSync(`rm -rf ${join(STORE_DIR, 'websocket', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'websocket', instanceName + '*')}`);
execSync(`rm -rf ${join(STORE_DIR, 'settings', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'settings', instanceName + '*')}`);
return; return;
} }
this.logger.verbose('cleaning store database instance: ' + instanceName); this.logger.verbose('cleaning store database instance: ' + instanceName);
await AuthModel.deleteMany({ owner: instanceName }); await AuthModel.deleteMany({ owner: instanceName });
await ChatModel.deleteMany({ owner: instanceName }); await ChatModel.deleteMany({ owner: instanceName });
await ContactModel.deleteMany({ owner: instanceName }); await ContactModel.deleteMany({ owner: instanceName });
await MessageModel.deleteMany({ owner: instanceName }); await MessageModel.deleteMany({ owner: instanceName });
await MessageUpModel.deleteMany({ owner: instanceName }); await MessageUpModel.deleteMany({ owner: instanceName });
await WebhookModel.deleteMany({ _id: instanceName }); await WebhookModel.deleteMany({ _id: instanceName });
await ChatwootModel.deleteMany({ _id: instanceName }); await ChatwootModel.deleteMany({ _id: instanceName });
await ChamaaiModel.deleteMany({ _id: instanceName }); await ChamaaiModel.deleteMany({ _id: instanceName });
await ProxyModel.deleteMany({ _id: instanceName }); await ProxyModel.deleteMany({ _id: instanceName });
await RabbitmqModel.deleteMany({ _id: instanceName }); await RabbitmqModel.deleteMany({ _id: instanceName });
await TypebotModel.deleteMany({ _id: instanceName }); await TypebotModel.deleteMany({ _id: instanceName });
await WebsocketModel.deleteMany({ _id: instanceName }); await WebsocketModel.deleteMany({ _id: instanceName });
await SettingsModel.deleteMany({ _id: instanceName }); await SettingsModel.deleteMany({ _id: instanceName });
return; return;
} }
public async loadInstance() { public async loadInstance() {
this.logger.verbose('Loading instances'); this.logger.verbose('Loading instances');
try { try {
if (this.redis.ENABLED) { if (this.redis.ENABLED) {
await this.loadInstancesFromRedis(); await this.loadInstancesFromRedis();
} else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { } else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
await this.loadInstancesFromDatabase(); await this.loadInstancesFromDatabase();
} else { } else {
await this.loadInstancesFromFiles(); await this.loadInstancesFromFiles();
} }
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
} }
private async setInstance(name: string) { private async setInstance(name: string) {
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache); const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
instance.instanceName = name; instance.instanceName = name;
this.logger.verbose('Instance loaded: ' + name); this.logger.verbose('Instance loaded: ' + name);
await instance.connectToWhatsapp(); await instance.connectToWhatsapp();
this.logger.verbose('connectToWhatsapp: ' + name); this.logger.verbose('connectToWhatsapp: ' + name);
this.waInstances[name] = instance; this.waInstances[name] = instance;
} }
private async loadInstancesFromRedis() { private async loadInstancesFromRedis() {
this.logger.verbose('Redis enabled'); this.logger.verbose('Redis enabled');
await this.cache.connect(this.redis as Redis); await this.cache.connect(this.redis as Redis);
const keys = await this.cache.instanceKeys(); const keys = await this.cache.instanceKeys();
if (keys?.length > 0) { if (keys?.length > 0) {
this.logger.verbose('Reading instance keys and setting instances'); this.logger.verbose('Reading instance keys and setting instances');
await Promise.all(keys.map((k) => this.setInstance(k.split(':')[1]))); await Promise.all(keys.map((k) => this.setInstance(k.split(':')[1])));
} else { } else {
this.logger.verbose('No instance keys found'); this.logger.verbose('No instance keys found');
} }
} }
private async loadInstancesFromDatabase() { private async loadInstancesFromDatabase() {
this.logger.verbose('Database enabled'); this.logger.verbose('Database enabled');
await this.repository.dbServer.connect(); await this.repository.dbServer.connect();
const collections: any[] = await this.dbInstance.collections(); const collections: any[] = await this.dbInstance.collections();
if (collections.length > 0) { if (collections.length > 0) {
this.logger.verbose('Reading collections and setting instances'); this.logger.verbose('Reading collections and setting instances');
await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, '')))); await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, ''))));
} else { } else {
this.logger.verbose('No collections found'); this.logger.verbose('No collections found');
} }
} }
private async loadInstancesFromFiles() { private async loadInstancesFromFiles() {
this.logger.verbose('Store in files enabled'); this.logger.verbose('Store in files enabled');
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
const instanceDirs = []; const instanceDirs = [];
for await (const dirent of dir) { for await (const dirent of dir) {
if (dirent.isDirectory()) { if (dirent.isDirectory()) {
instanceDirs.push(dirent.name); instanceDirs.push(dirent.name);
} else { } else {
this.logger.verbose('No instance files found'); this.logger.verbose('No instance files found');
} }
} }
await Promise.all( await Promise.all(
instanceDirs.map(async (instanceName) => { instanceDirs.map(async (instanceName) => {
this.logger.verbose('Reading instance files and setting instances: ' + instanceName); this.logger.verbose('Reading instance files and setting instances: ' + instanceName);
const files = readdirSync(join(INSTANCE_DIR, instanceName), { encoding: 'utf-8' }); const files = readdirSync(join(INSTANCE_DIR, instanceName), { encoding: 'utf-8' });
if (files.length === 0) { if (files.length === 0) {
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
} else { } else {
await this.setInstance(instanceName); await this.setInstance(instanceName);
} }
}), }),
); );
} }
private removeInstance() { private removeInstance() {
this.eventEmitter.on('remove.instance', async (instanceName: string) => { this.eventEmitter.on('remove.instance', async (instanceName: string) => {
this.logger.verbose('remove instance: ' + instanceName); this.logger.verbose('remove instance: ' + instanceName);
try { try {
this.logger.verbose('instance: ' + instanceName + ' - removing from memory'); this.logger.verbose('instance: ' + instanceName + ' - removing from memory');
this.waInstances[instanceName] = undefined; this.waInstances[instanceName] = undefined;
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
try { try {
this.logger.verbose('request cleaning up instance: ' + instanceName); this.logger.verbose('request cleaning up instance: ' + instanceName);
this.cleaningUp(instanceName); this.cleaningUp(instanceName);
this.cleaningStoreFiles(instanceName); this.cleaningStoreFiles(instanceName);
} finally { } finally {
this.logger.warn(`Instance "${instanceName}" - REMOVED`); this.logger.warn(`Instance "${instanceName}" - REMOVED`);
} }
}); });
this.eventEmitter.on('logout.instance', async (instanceName: string) => { this.eventEmitter.on('logout.instance', async (instanceName: string) => {
this.logger.verbose('logout instance: ' + instanceName); this.logger.verbose('logout instance: ' + instanceName);
try { try {
this.logger.verbose('request cleaning up instance: ' + instanceName); this.logger.verbose('request cleaning up instance: ' + instanceName);
this.cleaningUp(instanceName); this.cleaningUp(instanceName);
} finally { } finally {
this.logger.warn(`Instance "${instanceName}" - LOGOUT`); this.logger.warn(`Instance "${instanceName}" - LOGOUT`);
} }
}); });
} }
private noConnection() { private noConnection() {
this.logger.verbose('checking instances without connection'); this.logger.verbose('checking instances without connection');
this.eventEmitter.on('no.connection', async (instanceName) => { this.eventEmitter.on('no.connection', async (instanceName) => {
try { try {
this.logger.verbose('logging out instance: ' + instanceName); this.logger.verbose('logging out instance: ' + instanceName);
await this.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName); await this.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName);
this.logger.verbose('close connection instance: ' + instanceName); this.logger.verbose('close connection instance: ' + instanceName);
this.waInstances[instanceName]?.client?.ws?.close(); this.waInstances[instanceName]?.client?.ws?.close();
this.waInstances[instanceName].instance.qrcode = { count: 0 }; this.waInstances[instanceName].instance.qrcode = { count: 0 };
this.waInstances[instanceName].stateConnection.state = 'close'; this.waInstances[instanceName].stateConnection.state = 'close';
} catch (error) { } catch (error) {
this.logger.error({ this.logger.error({
localError: 'noConnection', localError: 'noConnection',
warn: 'Error deleting instance from memory.', warn: 'Error deleting instance from memory.',
error, error,
}); });
} finally { } finally {
this.logger.warn(`Instance "${instanceName}" - NOT CONNECTION`); this.logger.warn(`Instance "${instanceName}" - NOT CONNECTION`);
} }
}); });
} }
} }

114
src/whatsapp/services/openai.service.ts Executable file → Normal file
View File

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

68
src/whatsapp/services/sqs.service.ts Executable file → Normal file
View File

@ -1,35 +1,35 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { initQueues } from '../../libs/sqs.server'; import { initQueues } from '../../libs/sqs.server';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto'; import { SqsDto } from '../dto/sqs.dto';
import { SqsRaw } from '../models'; import { SqsRaw } from '../models';
import { WAMonitoringService } from './monitor.service'; import { WAMonitoringService } from './monitor.service';
export class SqsService { export class SqsService {
constructor(private readonly waMonitor: WAMonitoringService) { } constructor(private readonly waMonitor: WAMonitoringService) { }
private readonly logger = new Logger(SqsService.name); private readonly logger = new Logger(SqsService.name);
public create(instance: InstanceDto, data: SqsDto) { public create(instance: InstanceDto, data: SqsDto) {
this.logger.verbose('create sqs: ' + instance.instanceName); this.logger.verbose('create sqs: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setSqs(data); this.waMonitor.waInstances[instance.instanceName].setSqs(data);
initQueues(instance.instanceName, data.events); initQueues(instance.instanceName, data.events);
return { sqs: { ...instance, sqs: data } }; return { sqs: { ...instance, sqs: data } };
} }
public async find(instance: InstanceDto): Promise<SqsRaw> { public async find(instance: InstanceDto): Promise<SqsRaw> {
try { try {
this.logger.verbose('find sqs: ' + instance.instanceName); this.logger.verbose('find sqs: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findSqs(); const result = await this.waMonitor.waInstances[instance.instanceName].findSqs();
if (Object.keys(result).length === 0) { if (Object.keys(result).length === 0) {
throw new Error('Sqs not found'); throw new Error('Sqs not found');
} }
return result; return result;
} catch (error) { } catch (error) {
return { enabled: false, events: [] }; return { enabled: false, events: [] };
} }
} }
} }

2020
src/whatsapp/services/typebot.service.ts Executable file → Normal file

File diff suppressed because it is too large Load Diff

7682
src/whatsapp/services/whatsapp.service.ts Executable file → Normal file

File diff suppressed because it is too large Load Diff

290
src/whatsapp/types/wa.types.ts Executable file → Normal file
View File

@ -1,145 +1,145 @@
/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-namespace */
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys'; import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
export enum Events { export enum Events {
APPLICATION_STARTUP = 'application.startup', APPLICATION_STARTUP = 'application.startup',
QRCODE_UPDATED = 'qrcode.updated', QRCODE_UPDATED = 'qrcode.updated',
CONNECTION_UPDATE = 'connection.update', CONNECTION_UPDATE = 'connection.update',
STATUS_INSTANCE = 'status.instance', STATUS_INSTANCE = 'status.instance',
MESSAGES_SET = 'messages.set', MESSAGES_SET = 'messages.set',
MESSAGES_UPSERT = 'messages.upsert', MESSAGES_UPSERT = 'messages.upsert',
MESSAGES_UPDATE = 'messages.update', MESSAGES_UPDATE = 'messages.update',
MESSAGES_DELETE = 'messages.delete', MESSAGES_DELETE = 'messages.delete',
SEND_MESSAGE = 'send.message', SEND_MESSAGE = 'send.message',
CONTACTS_SET = 'contacts.set', CONTACTS_SET = 'contacts.set',
CONTACTS_UPSERT = 'contacts.upsert', CONTACTS_UPSERT = 'contacts.upsert',
CONTACTS_UPDATE = 'contacts.update', CONTACTS_UPDATE = 'contacts.update',
PRESENCE_UPDATE = 'presence.update', PRESENCE_UPDATE = 'presence.update',
CHATS_SET = 'chats.set', CHATS_SET = 'chats.set',
CHATS_UPDATE = 'chats.update', CHATS_UPDATE = 'chats.update',
CHATS_UPSERT = 'chats.upsert', CHATS_UPSERT = 'chats.upsert',
CHATS_DELETE = 'chats.delete', CHATS_DELETE = 'chats.delete',
GROUPS_UPSERT = 'groups.upsert', GROUPS_UPSERT = 'groups.upsert',
GROUPS_UPDATE = 'groups.update', GROUPS_UPDATE = 'groups.update',
GROUP_PARTICIPANTS_UPDATE = 'group-participants.update', GROUP_PARTICIPANTS_UPDATE = 'group-participants.update',
CALL = 'call', CALL = 'call',
TYPEBOT_START = 'typebot.start', TYPEBOT_START = 'typebot.start',
TYPEBOT_CHANGE_STATUS = 'typebot.change-status', TYPEBOT_CHANGE_STATUS = 'typebot.change-status',
CHAMA_AI_ACTION = 'chama-ai.action', CHAMA_AI_ACTION = 'chama-ai.action',
} }
export declare namespace wa { export declare namespace wa {
export type QrCode = { export type QrCode = {
count?: number; count?: number;
pairingCode?: string; pairingCode?: string;
base64?: string; base64?: string;
code?: string; code?: string;
}; };
export type Instance = { export type Instance = {
qrcode?: QrCode; qrcode?: QrCode;
pairingCode?: string; pairingCode?: string;
authState?: { state: AuthenticationState; saveCreds: () => void }; authState?: { state: AuthenticationState; saveCreds: () => void };
name?: string; name?: string;
wuid?: string; wuid?: string;
profileName?: string; profileName?: string;
profilePictureUrl?: string; profilePictureUrl?: string;
}; };
export type LocalWebHook = { export type LocalWebHook = {
enabled?: boolean; enabled?: boolean;
url?: string; url?: string;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
webhook_base64?: boolean; webhook_base64?: boolean;
}; };
export type LocalChatwoot = { export type LocalChatwoot = {
enabled?: boolean; enabled?: boolean;
account_id?: string; account_id?: string;
token?: string; token?: string;
url?: string; url?: string;
name_inbox?: string; name_inbox?: string;
sign_msg?: boolean; sign_msg?: boolean;
number?: string; number?: string;
reopen_conversation?: boolean; reopen_conversation?: boolean;
conversation_pending?: boolean; conversation_pending?: boolean;
}; };
export type LocalSettings = { export type LocalSettings = {
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;
groups_ignore?: boolean; groups_ignore?: boolean;
always_online?: boolean; always_online?: boolean;
read_messages?: boolean; read_messages?: boolean;
read_status?: boolean; read_status?: boolean;
}; };
export type LocalWebsocket = { export type LocalWebsocket = {
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
}; };
export type LocalRabbitmq = { export type LocalRabbitmq = {
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
}; };
export type LocalSqs = { export type LocalSqs = {
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
}; };
export type LocalOpenai = { export type LocalOpenai = {
chave?: string; chave?: string;
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
}; };
type Session = { type Session = {
remoteJid?: string; remoteJid?: string;
sessionId?: string; sessionId?: string;
createdAt?: number; createdAt?: number;
}; };
export type LocalTypebot = { export type LocalTypebot = {
enabled?: boolean; enabled?: boolean;
url?: string; url?: string;
typebot?: string; typebot?: string;
expire?: number; expire?: number;
keyword_finish?: string; keyword_finish?: string;
delay_message?: number; delay_message?: number;
unknown_message?: string; unknown_message?: string;
listening_from_me?: boolean; listening_from_me?: boolean;
sessions?: Session[]; sessions?: Session[];
}; };
export type LocalProxy = { export type LocalProxy = {
enabled?: boolean; enabled?: boolean;
proxy?: string; proxy?: string;
}; };
export type LocalChamaai = { export type LocalChamaai = {
enabled?: boolean; enabled?: boolean;
url?: string; url?: string;
token?: string; token?: string;
waNumber?: string; waNumber?: string;
answerByAudio?: boolean; answerByAudio?: boolean;
}; };
export type StateConnection = { export type StateConnection = {
instance?: string; instance?: string;
state?: WAConnectionState | 'refused'; state?: WAConnectionState | 'refused';
statusReason?: number; statusReason?: number;
}; };
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED'; export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
} }
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage']; export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage'];
export const MessageSubtype = [ export const MessageSubtype = [
'ephemeralMessage', 'ephemeralMessage',
'documentWithCaptionMessage', 'documentWithCaptionMessage',
'viewOnceMessage', 'viewOnceMessage',
'viewOnceMessageV2', 'viewOnceMessageV2',
]; ];

350
src/whatsapp/whatsapp.module.ts Executable file → Normal file
View File

@ -1,175 +1,175 @@
import { configService } from '../config/env.config'; import { configService } from '../config/env.config';
import { eventEmitter } from '../config/event.config'; import { eventEmitter } from '../config/event.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
import { dbserver } from '../libs/db.connect'; import { dbserver } from '../libs/db.connect';
import { RedisCache } from '../libs/redis.client'; import { RedisCache } from '../libs/redis.client';
import { ChamaaiController } from './controllers/chamaai.controller'; import { ChamaaiController } from './controllers/chamaai.controller';
import { ChatController } from './controllers/chat.controller'; import { ChatController } from './controllers/chat.controller';
import { ChatwootController } from './controllers/chatwoot.controller'; import { ChatwootController } from './controllers/chatwoot.controller';
import { GroupController } from './controllers/group.controller'; import { GroupController } from './controllers/group.controller';
import { InstanceController } from './controllers/instance.controller'; import { InstanceController } from './controllers/instance.controller';
import { ProxyController } from './controllers/proxy.controller'; import { ProxyController } from './controllers/proxy.controller';
import { RabbitmqController } from './controllers/rabbitmq.controller'; import { RabbitmqController } from './controllers/rabbitmq.controller';
import { OpenaiController } from './controllers/openai.controller'; import { OpenaiController } from './controllers/openai.controller';
import { SendMessageController } from './controllers/sendMessage.controller'; import { SendMessageController } from './controllers/sendMessage.controller';
import { SettingsController } from './controllers/settings.controller'; import { SettingsController } from './controllers/settings.controller';
import { SqsController } from './controllers/sqs.controller'; import { SqsController } from './controllers/sqs.controller';
import { TypebotController } from './controllers/typebot.controller'; import { TypebotController } from './controllers/typebot.controller';
import { ViewsController } from './controllers/views.controller'; import { ViewsController } from './controllers/views.controller';
import { WebhookController } from './controllers/webhook.controller'; import { WebhookController } from './controllers/webhook.controller';
import { WebsocketController } from './controllers/websocket.controller'; import { WebsocketController } from './controllers/websocket.controller';
import { import {
AuthModel, AuthModel,
ChamaaiModel, ChamaaiModel,
ChatModel, ChatModel,
ChatwootModel, ChatwootModel,
ContactModel, ContactModel,
MessageModel, MessageModel,
MessageUpModel, MessageUpModel,
ProxyModel, ProxyModel,
RabbitmqModel, RabbitmqModel,
OpenaiModel, OpenaiModel,
ContactOpenaiModel, ContactOpenaiModel,
SettingsModel, SettingsModel,
SqsModel, SqsModel,
TypebotModel, TypebotModel,
WebhookModel, WebhookModel,
WebsocketModel, WebsocketModel,
} from './models'; } from './models';
import { AuthRepository } from './repository/auth.repository'; import { AuthRepository } from './repository/auth.repository';
import { ChamaaiRepository } from './repository/chamaai.repository'; import { ChamaaiRepository } from './repository/chamaai.repository';
import { ChatRepository } from './repository/chat.repository'; import { ChatRepository } from './repository/chat.repository';
import { ChatwootRepository } from './repository/chatwoot.repository'; import { ChatwootRepository } from './repository/chatwoot.repository';
import { ContactRepository } from './repository/contact.repository'; import { ContactRepository } from './repository/contact.repository';
import { MessageRepository } from './repository/message.repository'; import { MessageRepository } from './repository/message.repository';
import { MessageUpRepository } from './repository/messageUp.repository'; import { MessageUpRepository } from './repository/messageUp.repository';
import { ProxyRepository } from './repository/proxy.repository'; import { ProxyRepository } from './repository/proxy.repository';
import { RabbitmqRepository } from './repository/rabbitmq.repository'; import { RabbitmqRepository } from './repository/rabbitmq.repository';
import { OpenaiRepository } from './repository/openai.repository'; import { OpenaiRepository } from './repository/openai.repository';
import { RepositoryBroker } from './repository/repository.manager'; import { RepositoryBroker } from './repository/repository.manager';
import { SettingsRepository } from './repository/settings.repository'; import { SettingsRepository } from './repository/settings.repository';
import { SqsRepository } from './repository/sqs.repository'; import { SqsRepository } from './repository/sqs.repository';
import { TypebotRepository } from './repository/typebot.repository'; import { TypebotRepository } from './repository/typebot.repository';
import { WebhookRepository } from './repository/webhook.repository'; import { WebhookRepository } from './repository/webhook.repository';
import { WebsocketRepository } from './repository/websocket.repository'; import { WebsocketRepository } from './repository/websocket.repository';
import { AuthService } from './services/auth.service'; import { AuthService } from './services/auth.service';
import { ChamaaiService } from './services/chamaai.service'; import { ChamaaiService } from './services/chamaai.service';
import { ChatwootService } from './services/chatwoot.service'; import { ChatwootService } from './services/chatwoot.service';
import { WAMonitoringService } from './services/monitor.service'; import { WAMonitoringService } from './services/monitor.service';
import { ProxyService } from './services/proxy.service'; import { ProxyService } from './services/proxy.service';
import { RabbitmqService } from './services/rabbitmq.service'; import { RabbitmqService } from './services/rabbitmq.service';
import { OpenaiService } from './services/openai.service'; import { OpenaiService } from './services/openai.service';
import { SettingsService } from './services/settings.service'; import { SettingsService } from './services/settings.service';
import { SqsService } from './services/sqs.service'; import { SqsService } from './services/sqs.service';
import { TypebotService } from './services/typebot.service'; import { TypebotService } from './services/typebot.service';
import { WebhookService } from './services/webhook.service'; import { WebhookService } from './services/webhook.service';
import { WebsocketService } from './services/websocket.service'; import { WebsocketService } from './services/websocket.service';
const logger = new Logger('WA MODULE'); const logger = new Logger('WA MODULE');
const messageRepository = new MessageRepository(MessageModel, configService); const messageRepository = new MessageRepository(MessageModel, configService);
const chatRepository = new ChatRepository(ChatModel, configService); const chatRepository = new ChatRepository(ChatModel, configService);
const contactRepository = new ContactRepository(ContactModel, configService); const contactRepository = new ContactRepository(ContactModel, configService);
const messageUpdateRepository = new MessageUpRepository(MessageUpModel, configService); const messageUpdateRepository = new MessageUpRepository(MessageUpModel, configService);
const typebotRepository = new TypebotRepository(TypebotModel, configService); const typebotRepository = new TypebotRepository(TypebotModel, configService);
const webhookRepository = new WebhookRepository(WebhookModel, configService); const webhookRepository = new WebhookRepository(WebhookModel, configService);
const websocketRepository = new WebsocketRepository(WebsocketModel, configService); const websocketRepository = new WebsocketRepository(WebsocketModel, configService);
const proxyRepository = new ProxyRepository(ProxyModel, configService); const proxyRepository = new ProxyRepository(ProxyModel, configService);
const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService); const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService);
const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService); const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService);
const openaiRepository = new OpenaiRepository(OpenaiModel,ContactOpenaiModel, configService); const openaiRepository = new OpenaiRepository(OpenaiModel,ContactOpenaiModel, configService);
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService); const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
const settingsRepository = new SettingsRepository(SettingsModel, configService); const settingsRepository = new SettingsRepository(SettingsModel, configService);
const sqsRepository = new SqsRepository(SqsModel, configService); const sqsRepository = new SqsRepository(SqsModel, configService);
const authRepository = new AuthRepository(AuthModel, configService); const authRepository = new AuthRepository(AuthModel, configService);
export const repository = new RepositoryBroker( export const repository = new RepositoryBroker(
messageRepository, messageRepository,
chatRepository, chatRepository,
contactRepository, contactRepository,
messageUpdateRepository, messageUpdateRepository,
webhookRepository, webhookRepository,
chatwootRepository, chatwootRepository,
settingsRepository, settingsRepository,
websocketRepository, websocketRepository,
rabbitmqRepository, rabbitmqRepository,
openaiRepository, openaiRepository,
openaiRepository, openaiRepository,
sqsRepository, sqsRepository,
typebotRepository, typebotRepository,
proxyRepository, proxyRepository,
chamaaiRepository, chamaaiRepository,
authRepository, authRepository,
configService, configService,
dbserver?.getClient(), dbserver?.getClient(),
); );
export const cache = new RedisCache(); export const cache = new RedisCache();
export const waMonitor = new WAMonitoringService(eventEmitter, configService, repository, cache); export const waMonitor = new WAMonitoringService(eventEmitter, configService, repository, cache);
const authService = new AuthService(configService, waMonitor, repository); const authService = new AuthService(configService, waMonitor, repository);
const typebotService = new TypebotService(waMonitor, configService); const typebotService = new TypebotService(waMonitor, configService);
export const typebotController = new TypebotController(typebotService); export const typebotController = new TypebotController(typebotService);
const webhookService = new WebhookService(waMonitor); const webhookService = new WebhookService(waMonitor);
export const webhookController = new WebhookController(webhookService); export const webhookController = new WebhookController(webhookService);
const websocketService = new WebsocketService(waMonitor); const websocketService = new WebsocketService(waMonitor);
export const websocketController = new WebsocketController(websocketService); export const websocketController = new WebsocketController(websocketService);
const proxyService = new ProxyService(waMonitor); const proxyService = new ProxyService(waMonitor);
export const proxyController = new ProxyController(proxyService); export const proxyController = new ProxyController(proxyService);
const chamaaiService = new ChamaaiService(waMonitor, configService); const chamaaiService = new ChamaaiService(waMonitor, configService);
export const chamaaiController = new ChamaaiController(chamaaiService); export const chamaaiController = new ChamaaiController(chamaaiService);
const rabbitmqService = new RabbitmqService(waMonitor); const rabbitmqService = new RabbitmqService(waMonitor);
export const rabbitmqController = new RabbitmqController(rabbitmqService); export const rabbitmqController = new RabbitmqController(rabbitmqService);
const sqsService = new SqsService(waMonitor); const sqsService = new SqsService(waMonitor);
export const sqsController = new SqsController(sqsService); export const sqsController = new SqsController(sqsService);
const openaiService = new OpenaiService(waMonitor); const openaiService = new OpenaiService(waMonitor);
export const openaiController = new OpenaiController(openaiService); export const openaiController = new OpenaiController(openaiService);
const chatwootService = new ChatwootService(waMonitor, configService); const chatwootService = new ChatwootService(waMonitor, configService);
export const chatwootController = new ChatwootController(chatwootService, configService); export const chatwootController = new ChatwootController(chatwootService, configService);
const settingsService = new SettingsService(waMonitor); const settingsService = new SettingsService(waMonitor);
export const settingsController = new SettingsController(settingsService); export const settingsController = new SettingsController(settingsService);
export const instanceController = new InstanceController( export const instanceController = new InstanceController(
waMonitor, waMonitor,
configService, configService,
repository, repository,
eventEmitter, eventEmitter,
authService, authService,
webhookService, webhookService,
chatwootService, chatwootService,
settingsService, settingsService,
websocketService, websocketService,
rabbitmqService, rabbitmqService,
openaiService, openaiService,
proxyService, proxyService,
sqsService, sqsService,
typebotService, typebotService,
cache, cache,
); );
export const viewsController = new ViewsController(waMonitor, configService); export const viewsController = new ViewsController(waMonitor, configService);
export const sendMessageController = new SendMessageController(waMonitor); export const sendMessageController = new SendMessageController(waMonitor);
export const chatController = new ChatController(waMonitor); export const chatController = new ChatController(waMonitor);
export const groupController = new GroupController(waMonitor); export const groupController = new GroupController(waMonitor);
logger.info('Module - ON'); logger.info('Module - ON');