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"
dotenv.config()
export const config = {
openAI: {
apiToken: process.env.OPENAI_API_KEY,
},
redis: {
host: process.env.REDIS_HOST || "localhost",
port: (process.env.REDIS_PORT as unknown as number) || 6379,
db: (process.env.REDIS_DB as unknown as number) || 0,
},
}
import dotenv from "dotenv"
dotenv.config()
export const config = {
openAI: {
apiToken: process.env.OPENAI_API_KEY,
},
redis: {
host: process.env.REDIS_HOST || "localhost",
port: (process.env.REDIS_PORT as unknown as number) || 6379,
db: (process.env.REDIS_DB as unknown as number) || 0,
},
}

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

@ -1,348 +1,348 @@
import { isBooleanString } from 'class-validator';
import { readFileSync } from 'fs';
import { load } from 'js-yaml';
import { join } from 'path';
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string };
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
export type Cors = {
ORIGIN: string[];
METHODS: HttpMethods[];
CREDENTIALS: boolean;
};
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS';
export type Log = {
LEVEL: LogLevel[];
COLOR: boolean;
BAILEYS: LogBaileys;
};
export type SaveData = {
INSTANCE: boolean;
NEW_MESSAGE: boolean;
MESSAGE_UPDATE: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type StoreConf = {
MESSAGES: boolean;
MESSAGE_UP: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type CleanStoreConf = {
CLEANING_INTERVAL: number;
MESSAGES: boolean;
MESSAGE_UP: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type DBConnection = {
URI: string;
DB_PREFIX_NAME: string;
DB_PREFIX_FINAL_NAME: string;
};
export type Database = {
CONNECTION: DBConnection;
ENABLED: boolean;
SAVE_DATA: SaveData;
};
export type Redis = {
ENABLED: boolean;
URI: string;
PREFIX_KEY: string;
};
export type Rabbitmq = {
ENABLED: boolean;
URI: string;
};
export type Sqs = {
ENABLED: boolean;
ACCESS_KEY_ID: string;
SECRET_ACCESS_KEY: string;
ACCOUNT_ID: string;
REGION: string;
};
export type Openai = {
CHAVE: string;
ENABLED: boolean;
URI: string;
};
export type Websocket = {
ENABLED: boolean;
};
export type Chatwoot = {
USE_REPLY_ID: boolean;
};
export type EventsWebhook = {
APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean;
MESSAGES_SET: boolean;
MESSAGES_UPSERT: boolean;
MESSAGES_UPDATE: boolean;
MESSAGES_DELETE: boolean;
SEND_MESSAGE: boolean;
CONTACTS_SET: boolean;
CONTACTS_UPDATE: boolean;
CONTACTS_UPSERT: boolean;
PRESENCE_UPDATE: boolean;
CHATS_SET: boolean;
CHATS_UPDATE: boolean;
CHATS_DELETE: boolean;
CHATS_UPSERT: boolean;
CONNECTION_UPDATE: boolean;
GROUPS_UPSERT: boolean;
GROUP_UPDATE: boolean;
GROUP_PARTICIPANTS_UPDATE: boolean;
CALL: boolean;
NEW_JWT_TOKEN: boolean;
TYPEBOT_START: boolean;
TYPEBOT_CHANGE_STATUS: boolean;
CHAMA_AI_ACTION: boolean;
ERRORS: boolean;
ERRORS_WEBHOOK: string;
};
export type ApiKey = { KEY: string };
export type Jwt = { EXPIRIN_IN: number; SECRET: string };
export type Auth = {
API_KEY: ApiKey;
EXPOSE_IN_FETCH_INSTANCES: boolean;
JWT: Jwt;
TYPE: 'jwt' | 'apikey';
};
export type DelInstance = number | boolean;
export type GlobalWebhook = {
URL: string;
ENABLED: boolean;
WEBHOOK_BY_EVENTS: boolean;
};
export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
export type ConfigSessionPhone = { CLIENT: string; NAME: string };
export type QrCode = { LIMIT: number; COLOR: string };
export type Typebot = { API_VERSION: string };
export type Production = boolean;
export interface Env {
SERVER: HttpServer;
CORS: Cors;
SSL_CONF: SslConf;
STORE: StoreConf;
CLEAN_STORE: CleanStoreConf;
DATABASE: Database;
REDIS: Redis;
RABBITMQ: Rabbitmq;
SQS: Sqs;
OPENAI: Openai;
WEBSOCKET: Websocket;
LOG: Log;
DEL_INSTANCE: DelInstance;
WEBHOOK: Webhook;
CONFIG_SESSION_PHONE: ConfigSessionPhone;
QRCODE: QrCode;
TYPEBOT: Typebot;
AUTHENTICATION: Auth;
PRODUCTION?: Production;
CHATWOOT?: Chatwoot;
}
export type Key = keyof Env;
export class ConfigService {
constructor() {
this.loadEnv();
}
private env: Env;
public get<T = any>(key: Key) {
return this.env[key] as T;
}
private loadEnv() {
this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
if (process.env?.DOCKER_ENV === 'true') {
this.env.SERVER.TYPE = 'http';
this.env.SERVER.PORT = 8080;
}
}
private envYaml(): Env {
return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env;
}
private envProcess(): Env {
return {
SERVER: {
TYPE: process.env.SERVER_TYPE as 'http' | 'https',
PORT: Number.parseInt(process.env.SERVER_PORT) || 8080,
URL: process.env.SERVER_URL,
},
CORS: {
ORIGIN: process.env.CORS_ORIGIN.split(',') || ['*'],
METHODS: (process.env.CORS_METHODS.split(',') as HttpMethods[]) || ['POST', 'GET', 'PUT', 'DELETE'],
CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true',
},
SSL_CONF: {
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '',
},
STORE: {
MESSAGES: process.env?.STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.STORE_CONTACTS === 'true',
CHATS: process.env?.STORE_CHATS === 'true',
},
CLEAN_STORE: {
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL)
: 7200,
MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true',
CHATS: process.env?.CLEAN_STORE_CHATS === 'true',
},
DATABASE: {
CONNECTION: {
URI: process.env.DATABASE_CONNECTION_URI || '',
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution',
DB_PREFIX_FINAL_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_FINAL_NAME || '-api',
},
ENABLED: process.env?.DATABASE_ENABLED === 'true',
SAVE_DATA: {
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
},
},
REDIS: {
ENABLED: process.env?.REDIS_ENABLED === 'true',
URI: process.env.REDIS_URI || '',
PREFIX_KEY: process.env.REDIS_PREFIX_KEY || 'evolution',
},
RABBITMQ: {
ENABLED: process.env?.RABBITMQ_ENABLED === 'true',
URI: process.env.RABBITMQ_URI || '',
},
SQS: {
ENABLED: process.env?.SQS_ENABLED === 'true',
ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '',
SECRET_ACCESS_KEY: process.env.SQS_SECRET_ACCESS_KEY || '',
ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '',
REGION: process.env.SQS_REGION || '',
},
OPENAI: {
CHAVE: process.env?.OPENAI_ENABLED || '',
ENABLED: process.env?.OPENAI_ENABLED === 'true',
URI: process.env.OPENAI_URI || '',
},
WEBSOCKET: {
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
},
LOG: {
LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [
'ERROR',
'WARN',
'DEBUG',
'INFO',
'LOG',
'VERBOSE',
'DARK',
'WEBHOOKS',
],
COLOR: process.env?.LOG_COLOR === 'true',
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
},
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
? process.env.DEL_INSTANCE === 'true'
: Number.parseInt(process.env.DEL_INSTANCE) || false,
WEBHOOK: {
GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL || '',
ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true',
WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true',
},
EVENTS: {
APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true',
QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true',
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true',
MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true',
MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true',
SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true',
CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true',
CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true',
PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true',
CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true',
CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true',
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true',
NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true',
TYPEBOT_START: process.env?.WEBHOOK_EVENTS_TYPEBOT_START === 'true',
TYPEBOT_CHANGE_STATUS: process.env?.WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
CHAMA_AI_ACTION: process.env?.WEBHOOK_EVENTS_CHAMA_AI_ACTION === 'true',
ERRORS: process.env?.WEBHOOK_EVENTS_ERRORS === 'true',
ERRORS_WEBHOOK: process.env?.WEBHOOK_EVENTS_ERRORS_WEBHOOK || '',
},
},
CONFIG_SESSION_PHONE: {
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API',
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'Chrome',
},
QRCODE: {
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30,
COLOR: process.env.QRCODE_COLOR || '#198754',
},
TYPEBOT: {
API_VERSION: process.env?.TYPEBOT_API_VERSION || 'v1',
},
AUTHENTICATION: {
TYPE: process.env.AUTHENTICATION_TYPE as 'apikey',
API_KEY: {
KEY: process.env.AUTHENTICATION_API_KEY || 'BQYHJGJHJ',
},
EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true',
JWT: {
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
: 3600,
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
},
},
CHATWOOT: {
USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true',
},
};
}
}
export const configService = new ConfigService();
import { isBooleanString } from 'class-validator';
import { readFileSync } from 'fs';
import { load } from 'js-yaml';
import { join } from 'path';
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string };
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
export type Cors = {
ORIGIN: string[];
METHODS: HttpMethods[];
CREDENTIALS: boolean;
};
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS';
export type Log = {
LEVEL: LogLevel[];
COLOR: boolean;
BAILEYS: LogBaileys;
};
export type SaveData = {
INSTANCE: boolean;
NEW_MESSAGE: boolean;
MESSAGE_UPDATE: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type StoreConf = {
MESSAGES: boolean;
MESSAGE_UP: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type CleanStoreConf = {
CLEANING_INTERVAL: number;
MESSAGES: boolean;
MESSAGE_UP: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type DBConnection = {
URI: string;
DB_PREFIX_NAME: string;
DB_PREFIX_FINAL_NAME: string;
};
export type Database = {
CONNECTION: DBConnection;
ENABLED: boolean;
SAVE_DATA: SaveData;
};
export type Redis = {
ENABLED: boolean;
URI: string;
PREFIX_KEY: string;
};
export type Rabbitmq = {
ENABLED: boolean;
URI: string;
};
export type Sqs = {
ENABLED: boolean;
ACCESS_KEY_ID: string;
SECRET_ACCESS_KEY: string;
ACCOUNT_ID: string;
REGION: string;
};
export type Openai = {
CHAVE: string;
ENABLED: boolean;
URI: string;
};
export type Websocket = {
ENABLED: boolean;
};
export type Chatwoot = {
USE_REPLY_ID: boolean;
};
export type EventsWebhook = {
APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean;
MESSAGES_SET: boolean;
MESSAGES_UPSERT: boolean;
MESSAGES_UPDATE: boolean;
MESSAGES_DELETE: boolean;
SEND_MESSAGE: boolean;
CONTACTS_SET: boolean;
CONTACTS_UPDATE: boolean;
CONTACTS_UPSERT: boolean;
PRESENCE_UPDATE: boolean;
CHATS_SET: boolean;
CHATS_UPDATE: boolean;
CHATS_DELETE: boolean;
CHATS_UPSERT: boolean;
CONNECTION_UPDATE: boolean;
GROUPS_UPSERT: boolean;
GROUP_UPDATE: boolean;
GROUP_PARTICIPANTS_UPDATE: boolean;
CALL: boolean;
NEW_JWT_TOKEN: boolean;
TYPEBOT_START: boolean;
TYPEBOT_CHANGE_STATUS: boolean;
CHAMA_AI_ACTION: boolean;
ERRORS: boolean;
ERRORS_WEBHOOK: string;
};
export type ApiKey = { KEY: string };
export type Jwt = { EXPIRIN_IN: number; SECRET: string };
export type Auth = {
API_KEY: ApiKey;
EXPOSE_IN_FETCH_INSTANCES: boolean;
JWT: Jwt;
TYPE: 'jwt' | 'apikey';
};
export type DelInstance = number | boolean;
export type GlobalWebhook = {
URL: string;
ENABLED: boolean;
WEBHOOK_BY_EVENTS: boolean;
};
export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
export type ConfigSessionPhone = { CLIENT: string; NAME: string };
export type QrCode = { LIMIT: number; COLOR: string };
export type Typebot = { API_VERSION: string };
export type Production = boolean;
export interface Env {
SERVER: HttpServer;
CORS: Cors;
SSL_CONF: SslConf;
STORE: StoreConf;
CLEAN_STORE: CleanStoreConf;
DATABASE: Database;
REDIS: Redis;
RABBITMQ: Rabbitmq;
SQS: Sqs;
OPENAI: Openai;
WEBSOCKET: Websocket;
LOG: Log;
DEL_INSTANCE: DelInstance;
WEBHOOK: Webhook;
CONFIG_SESSION_PHONE: ConfigSessionPhone;
QRCODE: QrCode;
TYPEBOT: Typebot;
AUTHENTICATION: Auth;
PRODUCTION?: Production;
CHATWOOT?: Chatwoot;
}
export type Key = keyof Env;
export class ConfigService {
constructor() {
this.loadEnv();
}
private env: Env;
public get<T = any>(key: Key) {
return this.env[key] as T;
}
private loadEnv() {
this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
if (process.env?.DOCKER_ENV === 'true') {
this.env.SERVER.TYPE = 'http';
this.env.SERVER.PORT = 8080;
}
}
private envYaml(): Env {
return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env;
}
private envProcess(): Env {
return {
SERVER: {
TYPE: process.env.SERVER_TYPE as 'http' | 'https',
PORT: Number.parseInt(process.env.SERVER_PORT) || 8080,
URL: process.env.SERVER_URL,
},
CORS: {
ORIGIN: process.env.CORS_ORIGIN.split(',') || ['*'],
METHODS: (process.env.CORS_METHODS.split(',') as HttpMethods[]) || ['POST', 'GET', 'PUT', 'DELETE'],
CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true',
},
SSL_CONF: {
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '',
},
STORE: {
MESSAGES: process.env?.STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.STORE_CONTACTS === 'true',
CHATS: process.env?.STORE_CHATS === 'true',
},
CLEAN_STORE: {
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL)
: 7200,
MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true',
CHATS: process.env?.CLEAN_STORE_CHATS === 'true',
},
DATABASE: {
CONNECTION: {
URI: process.env.DATABASE_CONNECTION_URI || '',
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution',
DB_PREFIX_FINAL_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_FINAL_NAME || '-api',
},
ENABLED: process.env?.DATABASE_ENABLED === 'true',
SAVE_DATA: {
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
},
},
REDIS: {
ENABLED: process.env?.REDIS_ENABLED === 'true',
URI: process.env.REDIS_URI || '',
PREFIX_KEY: process.env.REDIS_PREFIX_KEY || 'evolution',
},
RABBITMQ: {
ENABLED: process.env?.RABBITMQ_ENABLED === 'true',
URI: process.env.RABBITMQ_URI || '',
},
SQS: {
ENABLED: process.env?.SQS_ENABLED === 'true',
ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '',
SECRET_ACCESS_KEY: process.env.SQS_SECRET_ACCESS_KEY || '',
ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '',
REGION: process.env.SQS_REGION || '',
},
OPENAI: {
CHAVE: process.env?.OPENAI_ENABLED || '',
ENABLED: process.env?.OPENAI_ENABLED === 'true',
URI: process.env.OPENAI_URI || '',
},
WEBSOCKET: {
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
},
LOG: {
LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [
'ERROR',
'WARN',
'DEBUG',
'INFO',
'LOG',
'VERBOSE',
'DARK',
'WEBHOOKS',
],
COLOR: process.env?.LOG_COLOR === 'true',
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
},
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
? process.env.DEL_INSTANCE === 'true'
: Number.parseInt(process.env.DEL_INSTANCE) || false,
WEBHOOK: {
GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL || '',
ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true',
WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true',
},
EVENTS: {
APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true',
QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true',
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true',
MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true',
MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true',
SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true',
CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true',
CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true',
PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true',
CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true',
CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true',
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true',
NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true',
TYPEBOT_START: process.env?.WEBHOOK_EVENTS_TYPEBOT_START === 'true',
TYPEBOT_CHANGE_STATUS: process.env?.WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
CHAMA_AI_ACTION: process.env?.WEBHOOK_EVENTS_CHAMA_AI_ACTION === 'true',
ERRORS: process.env?.WEBHOOK_EVENTS_ERRORS === 'true',
ERRORS_WEBHOOK: process.env?.WEBHOOK_EVENTS_ERRORS_WEBHOOK || '',
},
},
CONFIG_SESSION_PHONE: {
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API',
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'Chrome',
},
QRCODE: {
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30,
COLOR: process.env.QRCODE_COLOR || '#198754',
},
TYPEBOT: {
API_VERSION: process.env?.TYPEBOT_API_VERSION || 'v1',
},
AUTHENTICATION: {
TYPE: process.env.AUTHENTICATION_TYPE as 'apikey',
API_KEY: {
KEY: process.env.AUTHENTICATION_API_KEY || 'BQYHJGJHJ',
},
EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true',
JWT: {
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
: 3600,
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
},
},
CHATWOOT: {
USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true',
},
};
}
}
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.
# ⚠️
# ⚠️ RENAME THIS FILE TO env.yml
# Choose the server type for the application
SERVER:
TYPE: http # https
PORT: 3333 # 443
URL: 127.0.0.1
CORS:
ORIGIN:
- "*"
# - yourdomain.com
METHODS:
- POST
- GET
- PUT
- DELETE
CREDENTIALS: true
# Install ssl certificate and replace string <domain> with domain name
# Access: https://certbot.eff.org/instructions?ws=other&os=ubuntufocal
SSL_CONF:
PRIVKEY: /etc/letsencrypt/live/<domain>/privkey.pem
FULLCHAIN: /etc/letsencrypt/live/<domain>/fullchain.pem
# Determine the logs to be displayed
LOG:
LEVEL:
- ERROR
- WARN
- DEBUG
- INFO
- LOG
- VERBOSE
- DARK
- WEBHOOKS
COLOR: true
BAILEYS: error # fatal | error | warn | info | debug | trace
# Determine how long the instance should be deleted from memory in case of no connection.
# Default time: 5 minutes
# If you don't even want an expiration, enter the value false
DEL_INSTANCE: false # or false
# Temporary data storage
STORE:
MESSAGES: false
MESSAGE_UP: false
CONTACTS: true
CHATS: true
CLEAN_STORE:
CLEANING_INTERVAL: 7200 # 7200 seconds === 2h
MESSAGES: true
MESSAGE_UP: true
CONTACTS: true
CHATS: true
# Permanent data storage
DATABASE:
ENABLED: true
CONNECTION:
URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true"
DB_PREFIX_NAME: whatsapp
DB_PREFIX_FINAL_NAME: "-api"
DB_PREFIX_FINAL_NAME_VENOM: "-venom"
# Choose the data you want to save in the application's database or store
SAVE_DATA:
INSTANCE: true
NEW_MESSAGE: false
MESSAGE_UPDATE: false
CONTACTS: true
CHATS: true
REDIS:
ENABLED: false
URI: "redis://localhost:6379"
PREFIX_KEY: "evolution"
RABBITMQ:
ENABLED: false
URI: "amqp://guest:guest@localhost:5672"
OPENAI:
CHAVE: ""
ENABLED: false
PROMPTS: false
URI: ""
SQS:
ENABLED: false
ACCESS_KEY_ID: ""
SECRET_ACCESS_KEY: ""
ACCOUNT_ID: ""
REGION: "us-east-1"
WEBSOCKET:
ENABLED: false
TYPEBOT:
API_VERSION: 'v1' # v1 | v2
# Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created
WEBHOOK:
# Define a global webhook that will listen for enabled events from all instances
GLOBAL:
URL: ""
ENABLED: false
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
WEBHOOK_BY_EVENTS: false
# Automatically maps webhook paths
# Set the events you want to hear
EVENTS:
APPLICATION_STARTUP: false
QRCODE_UPDATED: true
MESSAGES_SET: true
MESSAGES_UPSERT: true
MESSAGES_UPDATE: true
MESSAGES_DELETE: true
SEND_MESSAGE: true
CONTACTS_SET: true
CONTACTS_UPSERT: true
CONTACTS_UPDATE: true
PRESENCE_UPDATE: true
CHATS_SET: true
CHATS_UPSERT: true
CHATS_UPDATE: true
CHATS_DELETE: true
GROUPS_UPSERT: true
GROUP_UPDATE: true
GROUP_PARTICIPANTS_UPDATE: true
CONNECTION_UPDATE: true
CALL: true
# This event fires every time a new token is requested via the refresh route
NEW_JWT_TOKEN: false
# This events is used with Typebot
TYPEBOT_START: false
TYPEBOT_CHANGE_STATUS: false
# This event is used with Chama AI
CHAMA_AI_ACTION: false
# This event is used to send errors to the webhook
ERRORS: false
ERRORS_WEBHOOK: ""
CONFIG_SESSION_PHONE:
# Name that will be displayed on smartphone connection
CLIENT: "Evolution API"
NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari
# Set qrcode display limit
QRCODE:
LIMIT: 30
COLOR: "#198754"
# Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token,
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
AUTHENTICATION:
TYPE: apikey # jwt or apikey
# Define a global apikey to access all instances
API_KEY:
# OBS: This key must be inserted in the request header to create an instance.
KEY: B6D711FCDE4D4FD5936544120E713976
# Expose the api key on return from fetch instances
EXPOSE_IN_FETCH_INSTANCES: true
# Set the secret key to encrypt and decrypt your token and its expiration time.
JWT:
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
SECRET: L=0YWt]b2w[WF>#>:&E`
# Configure to chatwoot
CHATWOOT:
USE_REPLY_ID: false
# ⚠️
# ⚠️ ALL SETTINGS DEFINED IN THIS FILE ARE APPLIED TO ALL INSTANCES.
# ⚠️
# ⚠️ RENAME THIS FILE TO env.yml
# Choose the server type for the application
SERVER:
TYPE: http # https
PORT: 3333 # 443
URL: 127.0.0.1
CORS:
ORIGIN:
- "*"
# - yourdomain.com
METHODS:
- POST
- GET
- PUT
- DELETE
CREDENTIALS: true
# Install ssl certificate and replace string <domain> with domain name
# Access: https://certbot.eff.org/instructions?ws=other&os=ubuntufocal
SSL_CONF:
PRIVKEY: /etc/letsencrypt/live/<domain>/privkey.pem
FULLCHAIN: /etc/letsencrypt/live/<domain>/fullchain.pem
# Determine the logs to be displayed
LOG:
LEVEL:
- ERROR
- WARN
- DEBUG
- INFO
- LOG
- VERBOSE
- DARK
- WEBHOOKS
COLOR: true
BAILEYS: error # fatal | error | warn | info | debug | trace
# Determine how long the instance should be deleted from memory in case of no connection.
# Default time: 5 minutes
# If you don't even want an expiration, enter the value false
DEL_INSTANCE: false # or false
# Temporary data storage
STORE:
MESSAGES: false
MESSAGE_UP: false
CONTACTS: true
CHATS: true
CLEAN_STORE:
CLEANING_INTERVAL: 7200 # 7200 seconds === 2h
MESSAGES: true
MESSAGE_UP: true
CONTACTS: true
CHATS: true
# Permanent data storage
DATABASE:
ENABLED: true
CONNECTION:
URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true"
DB_PREFIX_NAME: whatsapp
DB_PREFIX_FINAL_NAME: "-api"
DB_PREFIX_FINAL_NAME_VENOM: "-venom"
# Choose the data you want to save in the application's database or store
SAVE_DATA:
INSTANCE: true
NEW_MESSAGE: false
MESSAGE_UPDATE: false
CONTACTS: true
CHATS: true
REDIS:
ENABLED: false
URI: "redis://localhost:6379"
PREFIX_KEY: "evolution"
RABBITMQ:
ENABLED: false
URI: "amqp://guest:guest@localhost:5672"
OPENAI:
CHAVE: ""
ENABLED: false
PROMPTS: false
URI: ""
SQS:
ENABLED: false
ACCESS_KEY_ID: ""
SECRET_ACCESS_KEY: ""
ACCOUNT_ID: ""
REGION: "us-east-1"
WEBSOCKET:
ENABLED: false
TYPEBOT:
API_VERSION: 'v1' # v1 | v2
# Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created
WEBHOOK:
# Define a global webhook that will listen for enabled events from all instances
GLOBAL:
URL: ""
ENABLED: false
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
WEBHOOK_BY_EVENTS: false
# Automatically maps webhook paths
# Set the events you want to hear
EVENTS:
APPLICATION_STARTUP: false
QRCODE_UPDATED: true
MESSAGES_SET: true
MESSAGES_UPSERT: true
MESSAGES_UPDATE: true
MESSAGES_DELETE: true
SEND_MESSAGE: true
CONTACTS_SET: true
CONTACTS_UPSERT: true
CONTACTS_UPDATE: true
PRESENCE_UPDATE: true
CHATS_SET: true
CHATS_UPSERT: true
CHATS_UPDATE: true
CHATS_DELETE: true
GROUPS_UPSERT: true
GROUP_UPDATE: true
GROUP_PARTICIPANTS_UPDATE: true
CONNECTION_UPDATE: true
CALL: true
# This event fires every time a new token is requested via the refresh route
NEW_JWT_TOKEN: false
# This events is used with Typebot
TYPEBOT_START: false
TYPEBOT_CHANGE_STATUS: false
# This event is used with Chama AI
CHAMA_AI_ACTION: false
# This event is used to send errors to the webhook
ERRORS: false
ERRORS_WEBHOOK: ""
CONFIG_SESSION_PHONE:
# Name that will be displayed on smartphone connection
CLIENT: "Chat API"
NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari
# Set qrcode display limit
QRCODE:
LIMIT: 30
COLOR: "#198754"
# Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token,
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
AUTHENTICATION:
TYPE: apikey # jwt or apikey
# Define a global apikey to access all instances
API_KEY:
# OBS: This key must be inserted in the request header to create an instance.
KEY: B6D711FCDE4D4FD5936544120E713976
# Expose the api key on return from fetch instances
EXPOSE_IN_FETCH_INSTANCES: true
# Set the secret key to encrypt and decrypt your token and its expiration time.
JWT:
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
SECRET: L=0YWt]b2w[WF>#>:&E`
# Configure to chatwoot
CHATWOOT:
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 { configService, Rabbitmq, Openai } from '../config/env.config';
import { Logger } from '../config/logger.config';
const logger = new Logger('AMQP');
let amqpChannel: amqp.Channel | null = null;
export const initAMQP = () => {
return new Promise<void>((resolve, reject) => {
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
amqp.connect(uri, (error, connection) => {
if (error) {
reject(error);
return;
}
connection.createChannel((channelError, channel) => {
if (channelError) {
reject(channelError);
return;
}
const exchangeName = 'evolution_exchange';
channel.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
amqpChannel = channel;
logger.info('AMQP initialized');
resolve();
});
});
});
};
export const getAMQP = (): amqp.Channel | null => {
return amqpChannel;
};
export const initQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`;
});
queues.forEach((event) => {
const amqp = getAMQP();
const exchangeName = instanceName ?? 'evolution_exchange';
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
const queueName = `${instanceName}.${event}`;
amqp.assertQueue(queueName, {
durable: true,
autoDelete: false,
arguments: {
'x-queue-type': 'quorum',
},
});
amqp.bindQueue(queueName, exchangeName, event);
});
};
export const removeQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
const channel = getAMQP();
const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`;
});
const exchangeName = instanceName ?? 'evolution_exchange';
queues.forEach((event) => {
const amqp = getAMQP();
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
const queueName = `${instanceName}.${event}`;
amqp.deleteQueue(queueName);
});
channel.deleteExchange(exchangeName);
};
import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq, Openai } from '../config/env.config';
import { Logger } from '../config/logger.config';
const logger = new Logger('AMQP');
let amqpChannel: amqp.Channel | null = null;
export const initAMQP = () => {
return new Promise<void>((resolve, reject) => {
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
amqp.connect(uri, (error, connection) => {
if (error) {
reject(error);
return;
}
connection.createChannel((channelError, channel) => {
if (channelError) {
reject(channelError);
return;
}
const exchangeName = 'evolution_exchange';
channel.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
amqpChannel = channel;
logger.info('AMQP initialized');
resolve();
});
});
});
};
export const getAMQP = (): amqp.Channel | null => {
return amqpChannel;
};
export const initQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`;
});
queues.forEach((event) => {
const amqp = getAMQP();
const exchangeName = instanceName ?? 'evolution_exchange';
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
const queueName = `${instanceName}.${event}`;
amqp.assertQueue(queueName, {
durable: true,
autoDelete: false,
arguments: {
'x-queue-type': 'quorum',
},
});
amqp.bindQueue(queueName, exchangeName, event);
});
};
export const removeQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
const channel = getAMQP();
const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`;
});
const exchangeName = instanceName ?? 'evolution_exchange';
queues.forEach((event) => {
const amqp = getAMQP();
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
const queueName = `${instanceName}.${event}`;
amqp.deleteQueue(queueName);
});
channel.deleteExchange(exchangeName);
};

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

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

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

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

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

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

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

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

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

@ -1,137 +1,137 @@
import 'express-async-errors';
import axios from 'axios';
import compression from 'compression';
import cors from 'cors';
import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
import { join } from 'path';
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
import { onUnexpectedError } from './config/error.config';
import { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config';
import { swaggerRouter } from './docs/swagger.conf';
import { initAMQP } from './libs/amqp.server';
import { initIO } from './libs/socket.server';
import { initSQS } from './libs/sqs.server';
import { ServerUP } from './utils/server-up';
import { HttpStatus, router } from './whatsapp/routers/index.router';
import { waMonitor } from './whatsapp/whatsapp.module';
function initWA() {
waMonitor.loadInstance();
}
function bootstrap() {
const logger = new Logger('SERVER');
const app = express();
app.use(
cors({
origin(requestOrigin, callback) {
const { ORIGIN } = configService.get<Cors>('CORS');
if (ORIGIN.includes('*')) {
return callback(null, true);
}
if (ORIGIN.indexOf(requestOrigin) !== -1) {
return callback(null, true);
}
return callback(new Error('Not allowed by CORS'));
},
methods: [...configService.get<Cors>('CORS').METHODS],
credentials: configService.get<Cors>('CORS').CREDENTIALS,
}),
urlencoded({ extended: true, limit: '136mb' }),
json({ limit: '136mb' }),
compression(),
);
app.set('view engine', 'hbs');
app.set('views', join(ROOT_DIR, 'views'));
app.use(express.static(join(ROOT_DIR, 'public')));
app.use('/store', express.static(join(ROOT_DIR, 'store')));
app.use('/', router);
app.use(swaggerRouter);
app.use(
(err: Error, req: Request, res: Response, next: NextFunction) => {
if (err) {
const webhook = configService.get<Webhook>('WEBHOOK');
if (webhook.EVENTS.ERRORS_WEBHOOK && webhook.EVENTS.ERRORS_WEBHOOK != '' && webhook.EVENTS.ERRORS) {
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
const now = localISOTime;
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
const serverUrl = configService.get<HttpServer>('SERVER').URL;
const errorData = {
event: 'error',
data: {
error: err['error'] || 'Internal Server Error',
message: err['message'] || 'Internal Server Error',
status: err['status'] || 500,
response: {
message: err['message'] || 'Internal Server Error',
},
},
date_time: now,
api_key: globalApiKey,
server_url: serverUrl,
};
logger.error(errorData);
const baseURL = webhook.EVENTS.ERRORS_WEBHOOK;
const httpService = axios.create({ baseURL });
httpService.post('', errorData);
}
return res.status(err['status'] || 500).json({
status: err['status'] || 500,
error: err['error'] || 'Internal Server Error',
response: {
message: err['message'] || 'Internal Server Error',
},
});
}
next();
},
(req: Request, res: Response, next: NextFunction) => {
const { method, url } = req;
res.status(HttpStatus.NOT_FOUND).json({
status: HttpStatus.NOT_FOUND,
error: 'Not Found',
response: {
message: [`Cannot ${method.toUpperCase()} ${url}`],
},
});
next();
},
);
const httpServer = configService.get<HttpServer>('SERVER');
ServerUP.app = app;
const server = ServerUP[httpServer.TYPE];
server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT));
initWA();
initIO(server);
if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
if (configService.get<Sqs>('SQS')?.ENABLED) initSQS();
onUnexpectedError();
}
bootstrap();
import 'express-async-errors';
import axios from 'axios';
import compression from 'compression';
import cors from 'cors';
import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
import { join } from 'path';
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
import { onUnexpectedError } from './config/error.config';
import { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config';
import { swaggerRouter } from './docs/swagger.conf';
import { initAMQP } from './libs/amqp.server';
import { initIO } from './libs/socket.server';
import { initSQS } from './libs/sqs.server';
import { ServerUP } from './utils/server-up';
import { HttpStatus, router } from './whatsapp/routers/index.router';
import { waMonitor } from './whatsapp/whatsapp.module';
function initWA() {
waMonitor.loadInstance();
}
function bootstrap() {
const logger = new Logger('SERVER');
const app = express();
app.use(
cors({
origin(requestOrigin, callback) {
const { ORIGIN } = configService.get<Cors>('CORS');
if (ORIGIN.includes('*')) {
return callback(null, true);
}
if (ORIGIN.indexOf(requestOrigin) !== -1) {
return callback(null, true);
}
return callback(new Error('Not allowed by CORS'));
},
methods: [...configService.get<Cors>('CORS').METHODS],
credentials: configService.get<Cors>('CORS').CREDENTIALS,
}),
urlencoded({ extended: true, limit: '136mb' }),
json({ limit: '136mb' }),
compression(),
);
app.set('view engine', 'hbs');
app.set('views', join(ROOT_DIR, 'views'));
app.use(express.static(join(ROOT_DIR, 'public')));
app.use('/store', express.static(join(ROOT_DIR, 'store')));
app.use('/', router);
app.use(swaggerRouter);
app.use(
(err: Error, req: Request, res: Response, next: NextFunction) => {
if (err) {
const webhook = configService.get<Webhook>('WEBHOOK');
if (webhook.EVENTS.ERRORS_WEBHOOK && webhook.EVENTS.ERRORS_WEBHOOK != '' && webhook.EVENTS.ERRORS) {
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
const now = localISOTime;
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
const serverUrl = configService.get<HttpServer>('SERVER').URL;
const errorData = {
event: 'error',
data: {
error: err['error'] || 'Internal Server Error',
message: err['message'] || 'Internal Server Error',
status: err['status'] || 500,
response: {
message: err['message'] || 'Internal Server Error',
},
},
date_time: now,
api_key: globalApiKey,
server_url: serverUrl,
};
logger.error(errorData);
const baseURL = webhook.EVENTS.ERRORS_WEBHOOK;
const httpService = axios.create({ baseURL });
httpService.post('', errorData);
}
return res.status(err['status'] || 500).json({
status: err['status'] || 500,
error: err['error'] || 'Internal Server Error',
response: {
message: err['message'] || 'Internal Server Error',
},
});
}
next();
},
(req: Request, res: Response, next: NextFunction) => {
const { method, url } = req;
res.status(HttpStatus.NOT_FOUND).json({
status: HttpStatus.NOT_FOUND,
error: 'Not Found',
response: {
message: [`Cannot ${method.toUpperCase()} ${url}`],
},
});
next();
},
);
const httpServer = configService.get<HttpServer>('SERVER');
ServerUP.app = app;
const server = ServerUP[httpServer.TYPE];
server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT));
initWA();
initIO(server);
if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
if (configService.get<Sqs>('SQS')?.ENABLED) initSQS();
onUnexpectedError();
}
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.
O roteiro de atendimento é:
1. Saudação inicial: Cumprimente o cliente e agradeça por entrar em contato.
2. Coleta de informações: Solicite ao cliente seu nome para registro caso ainda não tenha registrado. Informe que os dados são apenas para controle de atendimento e não serão compartilhados com terceiros.
3. Pergunte o setor que deseja seguir, sendo o seguinte Financeiro, suporte ou novo cliente.
4. Serviços de Contabilidade:
4.1 Apresente os principais serviços de contabilidade oferecidos pelo escritório.
4.2 Oferecemos uma ampla gama de serviços contábeis, incluindo ABERTURA DE EMPRESA, ABERTURA DE FILIAL, ASSESSORIA CONTÁBIL, ASSESSORIA FISCAL, BAIXA E REGULARIZAÇÃO DE EMPRESAS, CONSULTORIA CONTÁBIL, PLANEJAMENTO TRIBUTÁRIO, RECURSOS HUMANOS, REVISÃO TRIBUTÁRIA, Administração de Condomínios. Como posso ajudá-lo com seus desafios financeiros hoje?
5. Perguntas Frequentes (FAQ):
5.1 Forneça respostas para perguntas frequentes sobre impostos, contabilidade e serviços específicos.
5.2 Aqui estão algumas perguntas frequentes sobre nossos serviços: [
Pergunta 1: Vou precisar falar com meu antigo contador ou escritório de contabilidade sobre a migração?
Resposta 1: Não. Fique tranquilo pois a Anexo Gestão Contábil cuida disso para você. Você precisará enviar o seu Certificado Digital. Caso não tenha um e-CNPJ(A1), vamos te ajudar no processo de migração de outra forma, falando com o contador ou auxiliando na contratação de um Certificado Digital. Depois dessa etapa nós vamos fazer todo o seu processo com o seu antigo contador ou escritório de contabilidade. É simples, transparente e sem burocracia para você.
Pergunta 2: Quanto tempo demora para mudar para a Anexo Gestão Contábil?
Resposta 2: O processo é rápido, prático e você não precisa sair de casa. Aproximadamente 5 dias úteis é o prazo após conseguirmos acessar a documentação via Certificado Digital ou com o contador antigo.
].
5. Agendamento de Consulta:
5.2 Permita que os usuários agendem uma consulta com um contador.
5.3 Pergunte sobre a data e hora preferidas.
5.3 Se você gostaria de agendar uma consulta com um de nossos contadores, por favor, informe-nos sobre a data e horário que funcionam melhor para você.
6. Impostos:
6.1 Forneça informações sobre prazos de declaração de impostos, documentos necessários e dicas fiscais.
6.2 Os prazos para a declaração de impostos estão se aproximando. Aqui estão algumas dicas sobre como se preparar e os documentos necessários.
7. Contato e Localização:
7.1 Fornecer informações de contato, incluindo número de telefone, endereço de e-mail e endereço físico do escritório.
7.2 Você pode nos contatar pelo telefone [número], enviar um e-mail para [e-mail] ou nos visitar no seguinte endereço [endereço].
8. Encaminhamento para um Contador:
8.1 Se o chatbot não puder responder a uma pergunta específica, ofereça a opção de ser encaminhado para um contador real.
8.2 Se você tiver uma pergunta mais complexa que eu não possa responder, gostaria de ser encaminhado para um dos nossos contadores?"
9. Despedida:
9.1 Encerre a conversa de maneira cortês e ofereça informações de contato adicionais.
9.2 Obrigado por usar nossos serviços! Se precisar de assistência futura, estamos à disposição. Tenha um ótimo dia!"
10. Feedback:
10.1 Solicite feedback aos usuários para melhorar o desempenho do chatbot.
10.2 Gostaríamos de ouvir sua opinião! Em uma escala de 1 a 5, quão útil você achou nosso chatbot hoje?
`
export const promptContabil = `Você é uma assistente virtual de atendimento de um escritorio de contabilidade chamado {{ storeName }}. Você deve ser educada, atenciosa, amigável, cordial e muito paciente.
O roteiro de atendimento é:
1. Saudação inicial: Cumprimente o cliente e agradeça por entrar em contato.
2. Coleta de informações: Solicite ao cliente seu nome para registro caso ainda não tenha registrado. Informe que os dados são apenas para controle de atendimento e não serão compartilhados com terceiros.
3. Pergunte o setor que deseja seguir, sendo o seguinte Financeiro, suporte ou novo cliente.
4. Serviços de Contabilidade:
4.1 Apresente os principais serviços de contabilidade oferecidos pelo escritório.
4.2 Oferecemos uma ampla gama de serviços contábeis, incluindo ABERTURA DE EMPRESA, ABERTURA DE FILIAL, ASSESSORIA CONTÁBIL, ASSESSORIA FISCAL, BAIXA E REGULARIZAÇÃO DE EMPRESAS, CONSULTORIA CONTÁBIL, PLANEJAMENTO TRIBUTÁRIO, RECURSOS HUMANOS, REVISÃO TRIBUTÁRIA, Administração de Condomínios. Como posso ajudá-lo com seus desafios financeiros hoje?
5. Perguntas Frequentes (FAQ):
5.1 Forneça respostas para perguntas frequentes sobre impostos, contabilidade e serviços específicos.
5.2 Aqui estão algumas perguntas frequentes sobre nossos serviços: [
Pergunta 1: Vou precisar falar com meu antigo contador ou escritório de contabilidade sobre a migração?
Resposta 1: Não. Fique tranquilo pois a Anexo Gestão Contábil cuida disso para você. Você precisará enviar o seu Certificado Digital. Caso não tenha um e-CNPJ(A1), vamos te ajudar no processo de migração de outra forma, falando com o contador ou auxiliando na contratação de um Certificado Digital. Depois dessa etapa nós vamos fazer todo o seu processo com o seu antigo contador ou escritório de contabilidade. É simples, transparente e sem burocracia para você.
Pergunta 2: Quanto tempo demora para mudar para a Anexo Gestão Contábil?
Resposta 2: O processo é rápido, prático e você não precisa sair de casa. Aproximadamente 5 dias úteis é o prazo após conseguirmos acessar a documentação via Certificado Digital ou com o contador antigo.
].
5. Agendamento de Consulta:
5.2 Permita que os usuários agendem uma consulta com um contador.
5.3 Pergunte sobre a data e hora preferidas.
5.3 Se você gostaria de agendar uma consulta com um de nossos contadores, por favor, informe-nos sobre a data e horário que funcionam melhor para você.
6. Impostos:
6.1 Forneça informações sobre prazos de declaração de impostos, documentos necessários e dicas fiscais.
6.2 Os prazos para a declaração de impostos estão se aproximando. Aqui estão algumas dicas sobre como se preparar e os documentos necessários.
7. Contato e Localização:
7.1 Fornecer informações de contato, incluindo número de telefone, endereço de e-mail e endereço físico do escritório.
7.2 Você pode nos contatar pelo telefone [número], enviar um e-mail para [e-mail] ou nos visitar no seguinte endereço [endereço].
8. Encaminhamento para um Contador:
8.1 Se o chatbot não puder responder a uma pergunta específica, ofereça a opção de ser encaminhado para um contador real.
8.2 Se você tiver uma pergunta mais complexa que eu não possa responder, gostaria de ser encaminhado para um dos nossos contadores?"
9. Despedida:
9.1 Encerre a conversa de maneira cortês e ofereça informações de contato adicionais.
9.2 Obrigado por usar nossos serviços! Se precisar de assistência futura, estamos à disposição. Tenha um ótimo dia!"
10. Feedback:
10.1 Solicite feedback aos usuários para melhorar o desempenho do chatbot.
10.2 Gostaríamos de ouvir sua opinião! Em uma escala de 1 a 5, quão útil você achou nosso chatbot hoje?
`

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

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

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

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

@ -1,112 +1,112 @@
import {
AuthenticationCreds,
AuthenticationState,
BufferJSON,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
import { dbserver } from '../libs/db.connect';
export async function useMultiFileAuthStateDb(
coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateDb.name);
const client = dbserver.getClient();
const collection = client
.db(
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME +
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_FINAL_NAME
)
.collection(coll);
const writeData = async (data: any, key: string): Promise<any> => {
try {
await client.connect();
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
if (Array.isArray(msgParsed)) {
msgParsed = {
_id: key,
content_array: msgParsed,
};
}
return await collection.replaceOne({ _id: key }, msgParsed, {
//return await collection.replaceOne({ _id: key }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), {
upsert: true,
});
} catch (error) {
logger.error(error);
}
};
const readData = async (key: string): Promise<any> => {
try {
await client.connect();
let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) {
data = data.content_array;
}
//const data = await collection.findOne({ _id: key });
const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver);
} catch (error) {
logger.error(error);
}
};
const removeData = async (key: string) => {
try {
await client.connect();
return await collection.deleteOne({ _id: key });
} catch (error) {
logger.error(error);
}
};
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return {
state: {
creds,
keys: {
get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all(
ids.map(async (id) => {
let value = await readData(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value);
}
data[id] = value;
}),
);
return data;
},
set: async (data: any) => {
const tasks: Promise<void>[] = [];
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id];
const key = `${category}-${id}`;
tasks.push(value ? writeData(value, key) : removeData(key));
}
}
await Promise.all(tasks);
},
},
},
saveCreds: async () => {
return writeData(creds, 'creds');
},
};
}
import {
AuthenticationCreds,
AuthenticationState,
BufferJSON,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
import { dbserver } from '../libs/db.connect';
export async function useMultiFileAuthStateDb(
coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateDb.name);
const client = dbserver.getClient();
const collection = client
.db(
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME +
configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_FINAL_NAME
)
.collection(coll);
const writeData = async (data: any, key: string): Promise<any> => {
try {
await client.connect();
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
if (Array.isArray(msgParsed)) {
msgParsed = {
_id: key,
content_array: msgParsed,
};
}
return await collection.replaceOne({ _id: key }, msgParsed, {
//return await collection.replaceOne({ _id: key }, JSON.parse(JSON.stringify(data, BufferJSON.replacer)), {
upsert: true,
});
} catch (error) {
logger.error(error);
}
};
const readData = async (key: string): Promise<any> => {
try {
await client.connect();
let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) {
data = data.content_array;
}
//const data = await collection.findOne({ _id: key });
const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver);
} catch (error) {
logger.error(error);
}
};
const removeData = async (key: string) => {
try {
await client.connect();
return await collection.deleteOne({ _id: key });
} catch (error) {
logger.error(error);
}
};
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return {
state: {
creds,
keys: {
get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all(
ids.map(async (id) => {
let value = await readData(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value);
}
data[id] = value;
}),
);
return data;
},
set: async (data: any) => {
const tasks: Promise<void>[] = [];
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id];
const key = `${category}-${id}`;
tasks.push(value ? writeData(value, key) : removeData(key));
}
}
await Promise.all(tasks);
},
},
},
saveCreds: async () => {
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 { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { OpenaiService } from '../services/openai.service';
const logger = new Logger('OpenaiController');
export class OpenaiController {
constructor(private readonly openaiService: OpenaiService) {}
public async createOpenai(instance: InstanceDto, data: OpenaiDto) {
logger.verbose('requested createOpenai from ' + instance.instanceName + ' instance');
if (!data.chave) {
logger.verbose('openai sem chave');
data.chave = '';
}
if (!data.enabled) {
logger.verbose('openai disabled');
data.events = [];
}
if (data.events?.length === 0) {
logger.verbose('openai events empty');
data.events = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
];
}
return this.openaiService.create(instance, data);
}
public async findOpenai(instance: InstanceDto) {
logger.verbose('requested findOpenai from ' + instance.instanceName + ' instance');
return this.openaiService.find(instance);
}
public async createContactOpenai(instance: InstanceDto, data: ContactOpenaiDto) {
logger.verbose('requested createOpenai from ' + instance.instanceName + ' instance');
if (!data.contact) {
logger.verbose('openai sem chave');
data.contact = '';
}
if (!data.enabled) {
logger.verbose('openai disabled');
data.enabled = false;
}
data.owner = instance.instanceName;
return this.openaiService.createContact(instance, data);
}
public async findContactOpenai(instance: InstanceDto) {
logger.verbose('requested findOpenai from ' + instance.instanceName + ' instance');
return this.openaiService.findContact(instance);
}
}
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { OpenaiService } from '../services/openai.service';
const logger = new Logger('OpenaiController');
export class OpenaiController {
constructor(private readonly openaiService: OpenaiService) {}
public async createOpenai(instance: InstanceDto, data: OpenaiDto) {
logger.verbose('requested createOpenai from ' + instance.instanceName + ' instance');
if (!data.chave) {
logger.verbose('openai sem chave');
data.chave = '';
}
if (!data.enabled) {
logger.verbose('openai disabled');
data.events = [];
}
if (data.events?.length === 0) {
logger.verbose('openai events empty');
data.events = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
];
}
return this.openaiService.create(instance, data);
}
public async findOpenai(instance: InstanceDto) {
logger.verbose('requested findOpenai from ' + instance.instanceName + ' instance');
return this.openaiService.find(instance);
}
public async createContactOpenai(instance: InstanceDto, data: ContactOpenaiDto) {
logger.verbose('requested createOpenai from ' + instance.instanceName + ' instance');
if (!data.contact) {
logger.verbose('openai sem chave');
data.contact = '';
}
if (!data.enabled) {
logger.verbose('openai disabled');
data.enabled = false;
}
data.owner = instance.instanceName;
return this.openaiService.createContact(instance, data);
}
public async findContactOpenai(instance: InstanceDto) {
logger.verbose('requested findOpenai from ' + instance.instanceName + ' instance');
return this.openaiService.findContact(instance);
}
}

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

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

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

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

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

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

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

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

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

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

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

@ -1,15 +1,15 @@
export * from './auth.model';
export * from './chamaai.model';
export * from './chat.model';
export * from './chatwoot.model';
export * from './contact.model';
export * from './message.model';
export * from './proxy.model';
export * from './rabbitmq.model';
export * from './settings.model';
export * from './sqs.model';
export * from './typebot.model';
export * from './webhook.model';
export * from './websocket.model';
export * from './openai.model';
export * from './contactOpenai.model';
export * from './auth.model';
export * from './chamaai.model';
export * from './chat.model';
export * from './chatwoot.model';
export * from './contact.model';
export * from './message.model';
export * from './proxy.model';
export * from './rabbitmq.model';
export * from './settings.model';
export * from './sqs.model';
export * from './typebot.model';
export * from './webhook.model';
export * from './websocket.model';
export * from './openai.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 { dbserver } from '../../libs/db.connect';
export class OpenaiRaw {
_id?: string;
chave?: string;
prompts?: string;
enabled?: boolean;
events?: string[];
}
const openaiSchema = new Schema<OpenaiRaw>({
_id: { type: String, _id: true },
chave: { type: String, required: true },
prompts: { type: String, required: false },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
});
export const OpenaiModel = dbserver?.model(OpenaiRaw.name, openaiSchema, 'openai');
export type IOpenaiModel = typeof OpenaiModel;
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
export class OpenaiRaw {
_id?: string;
chave?: string;
prompts?: string;
enabled?: boolean;
events?: string[];
}
const openaiSchema = new Schema<OpenaiRaw>({
_id: { type: String, _id: true },
chave: { type: String, required: true },
prompts: { type: String, required: false },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
});
export const OpenaiModel = dbserver?.model(OpenaiRaw.name, openaiSchema, 'openai');
export type IOpenaiModel = typeof OpenaiModel;

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

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

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

@ -1,169 +1,169 @@
import fs from 'fs';
import { MongoClient } from 'mongodb';
import { join } from 'path';
import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { AuthRepository } from './auth.repository';
import { ChamaaiRepository } from './chamaai.repository';
import { ChatRepository } from './chat.repository';
import { ChatwootRepository } from './chatwoot.repository';
import { ContactRepository } from './contact.repository';
import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository';
import { ProxyRepository } from './proxy.repository';
import { RabbitmqRepository } from './rabbitmq.repository';
import { OpenaiRepository } from './openai.repository';
import { SettingsRepository } from './settings.repository';
import { SqsRepository } from './sqs.repository';
import { TypebotRepository } from './typebot.repository';
import { WebhookRepository } from './webhook.repository';
import { WebsocketRepository } from './websocket.repository';
export class RepositoryBroker {
constructor(
public readonly message: MessageRepository,
public readonly chat: ChatRepository,
public readonly contact: ContactRepository,
public readonly messageUpdate: MessageUpRepository,
public readonly webhook: WebhookRepository,
public readonly chatwoot: ChatwootRepository,
public readonly settings: SettingsRepository,
public readonly websocket: WebsocketRepository,
public readonly rabbitmq: RabbitmqRepository,
public readonly openai: OpenaiRepository,
public readonly openai_contact: OpenaiRepository,
public readonly sqs: SqsRepository,
public readonly typebot: TypebotRepository,
public readonly proxy: ProxyRepository,
public readonly chamaai: ChamaaiRepository,
public readonly auth: AuthRepository,
private configService: ConfigService,
dbServer?: MongoClient,
) {
this.dbClient = dbServer;
this.__init_repo_without_db__();
}
private dbClient?: MongoClient;
private readonly logger = new Logger('RepositoryBroker');
public get dbServer() {
return this.dbClient;
}
private __init_repo_without_db__() {
this.logger.verbose('initializing repository without db');
if (!this.configService.get<Database>('DATABASE').ENABLED) {
const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath);
try {
const authDir = join(storePath, 'auth', this.configService.get<Auth>('AUTHENTICATION').TYPE);
const chatsDir = join(storePath, 'chats');
const contactsDir = join(storePath, 'contacts');
const messagesDir = join(storePath, 'messages');
const messageUpDir = join(storePath, 'message-up');
const webhookDir = join(storePath, 'webhook');
const chatwootDir = join(storePath, 'chatwoot');
const settingsDir = join(storePath, 'settings');
const sqsDir = join(storePath, 'sqs');
const websocketDir = join(storePath, 'websocket');
const rabbitmqDir = join(storePath, 'rabbitmq');
const openaiDir = join(storePath, 'openai');
const typebotDir = join(storePath, 'typebot');
const proxyDir = join(storePath, 'proxy');
const chamaaiDir = join(storePath, 'chamaai');
const tempDir = join(storePath, 'temp');
if (!fs.existsSync(authDir)) {
this.logger.verbose('creating auth dir: ' + authDir);
fs.mkdirSync(authDir, { recursive: true });
}
if (!fs.existsSync(chatsDir)) {
this.logger.verbose('creating chats dir: ' + chatsDir);
fs.mkdirSync(chatsDir, { recursive: true });
}
if (!fs.existsSync(contactsDir)) {
this.logger.verbose('creating contacts dir: ' + contactsDir);
fs.mkdirSync(contactsDir, { recursive: true });
}
if (!fs.existsSync(messagesDir)) {
this.logger.verbose('creating messages dir: ' + messagesDir);
fs.mkdirSync(messagesDir, { recursive: true });
}
if (!fs.existsSync(messageUpDir)) {
this.logger.verbose('creating message-up dir: ' + messageUpDir);
fs.mkdirSync(messageUpDir, { recursive: true });
}
if (!fs.existsSync(webhookDir)) {
this.logger.verbose('creating webhook dir: ' + webhookDir);
fs.mkdirSync(webhookDir, { recursive: true });
}
if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true });
}
if (!fs.existsSync(settingsDir)) {
this.logger.verbose('creating settings dir: ' + settingsDir);
fs.mkdirSync(settingsDir, { recursive: true });
}
if (!fs.existsSync(sqsDir)) {
this.logger.verbose('creating sqs dir: ' + sqsDir);
fs.mkdirSync(sqsDir, { recursive: true });
}
if (!fs.existsSync(websocketDir)) {
this.logger.verbose('creating websocket dir: ' + websocketDir);
fs.mkdirSync(websocketDir, { recursive: true });
}
if (!fs.existsSync(rabbitmqDir)) {
this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir);
fs.mkdirSync(rabbitmqDir, { recursive: true });
}
if (!fs.existsSync(openaiDir)) {
this.logger.verbose('creating openai dir: ' + openaiDir);
fs.mkdirSync(openaiDir, { recursive: true });
}
if (!fs.existsSync(typebotDir)) {
this.logger.verbose('creating typebot dir: ' + typebotDir);
fs.mkdirSync(typebotDir, { recursive: true });
}
if (!fs.existsSync(proxyDir)) {
this.logger.verbose('creating proxy dir: ' + proxyDir);
fs.mkdirSync(proxyDir, { recursive: true });
}
if (!fs.existsSync(chamaaiDir)) {
this.logger.verbose('creating chamaai dir: ' + chamaaiDir);
fs.mkdirSync(chamaaiDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true });
}
} catch (error) {
this.logger.error(error);
}
} else {
try {
const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath);
const tempDir = join(storePath, 'temp');
const chatwootDir = join(storePath, 'chatwoot');
if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true });
}
} catch (error) {
this.logger.error(error);
}
}
}
}
import fs from 'fs';
import { MongoClient } from 'mongodb';
import { join } from 'path';
import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { AuthRepository } from './auth.repository';
import { ChamaaiRepository } from './chamaai.repository';
import { ChatRepository } from './chat.repository';
import { ChatwootRepository } from './chatwoot.repository';
import { ContactRepository } from './contact.repository';
import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository';
import { ProxyRepository } from './proxy.repository';
import { RabbitmqRepository } from './rabbitmq.repository';
import { OpenaiRepository } from './openai.repository';
import { SettingsRepository } from './settings.repository';
import { SqsRepository } from './sqs.repository';
import { TypebotRepository } from './typebot.repository';
import { WebhookRepository } from './webhook.repository';
import { WebsocketRepository } from './websocket.repository';
export class RepositoryBroker {
constructor(
public readonly message: MessageRepository,
public readonly chat: ChatRepository,
public readonly contact: ContactRepository,
public readonly messageUpdate: MessageUpRepository,
public readonly webhook: WebhookRepository,
public readonly chatwoot: ChatwootRepository,
public readonly settings: SettingsRepository,
public readonly websocket: WebsocketRepository,
public readonly rabbitmq: RabbitmqRepository,
public readonly openai: OpenaiRepository,
public readonly openai_contact: OpenaiRepository,
public readonly sqs: SqsRepository,
public readonly typebot: TypebotRepository,
public readonly proxy: ProxyRepository,
public readonly chamaai: ChamaaiRepository,
public readonly auth: AuthRepository,
private configService: ConfigService,
dbServer?: MongoClient,
) {
this.dbClient = dbServer;
this.__init_repo_without_db__();
}
private dbClient?: MongoClient;
private readonly logger = new Logger('RepositoryBroker');
public get dbServer() {
return this.dbClient;
}
private __init_repo_without_db__() {
this.logger.verbose('initializing repository without db');
if (!this.configService.get<Database>('DATABASE').ENABLED) {
const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath);
try {
const authDir = join(storePath, 'auth', this.configService.get<Auth>('AUTHENTICATION').TYPE);
const chatsDir = join(storePath, 'chats');
const contactsDir = join(storePath, 'contacts');
const messagesDir = join(storePath, 'messages');
const messageUpDir = join(storePath, 'message-up');
const webhookDir = join(storePath, 'webhook');
const chatwootDir = join(storePath, 'chatwoot');
const settingsDir = join(storePath, 'settings');
const sqsDir = join(storePath, 'sqs');
const websocketDir = join(storePath, 'websocket');
const rabbitmqDir = join(storePath, 'rabbitmq');
const openaiDir = join(storePath, 'openai');
const typebotDir = join(storePath, 'typebot');
const proxyDir = join(storePath, 'proxy');
const chamaaiDir = join(storePath, 'chamaai');
const tempDir = join(storePath, 'temp');
if (!fs.existsSync(authDir)) {
this.logger.verbose('creating auth dir: ' + authDir);
fs.mkdirSync(authDir, { recursive: true });
}
if (!fs.existsSync(chatsDir)) {
this.logger.verbose('creating chats dir: ' + chatsDir);
fs.mkdirSync(chatsDir, { recursive: true });
}
if (!fs.existsSync(contactsDir)) {
this.logger.verbose('creating contacts dir: ' + contactsDir);
fs.mkdirSync(contactsDir, { recursive: true });
}
if (!fs.existsSync(messagesDir)) {
this.logger.verbose('creating messages dir: ' + messagesDir);
fs.mkdirSync(messagesDir, { recursive: true });
}
if (!fs.existsSync(messageUpDir)) {
this.logger.verbose('creating message-up dir: ' + messageUpDir);
fs.mkdirSync(messageUpDir, { recursive: true });
}
if (!fs.existsSync(webhookDir)) {
this.logger.verbose('creating webhook dir: ' + webhookDir);
fs.mkdirSync(webhookDir, { recursive: true });
}
if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true });
}
if (!fs.existsSync(settingsDir)) {
this.logger.verbose('creating settings dir: ' + settingsDir);
fs.mkdirSync(settingsDir, { recursive: true });
}
if (!fs.existsSync(sqsDir)) {
this.logger.verbose('creating sqs dir: ' + sqsDir);
fs.mkdirSync(sqsDir, { recursive: true });
}
if (!fs.existsSync(websocketDir)) {
this.logger.verbose('creating websocket dir: ' + websocketDir);
fs.mkdirSync(websocketDir, { recursive: true });
}
if (!fs.existsSync(rabbitmqDir)) {
this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir);
fs.mkdirSync(rabbitmqDir, { recursive: true });
}
if (!fs.existsSync(openaiDir)) {
this.logger.verbose('creating openai dir: ' + openaiDir);
fs.mkdirSync(openaiDir, { recursive: true });
}
if (!fs.existsSync(typebotDir)) {
this.logger.verbose('creating typebot dir: ' + typebotDir);
fs.mkdirSync(typebotDir, { recursive: true });
}
if (!fs.existsSync(proxyDir)) {
this.logger.verbose('creating proxy dir: ' + proxyDir);
fs.mkdirSync(proxyDir, { recursive: true });
}
if (!fs.existsSync(chamaaiDir)) {
this.logger.verbose('creating chamaai dir: ' + chamaaiDir);
fs.mkdirSync(chamaaiDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true });
}
} catch (error) {
this.logger.error(error);
}
} else {
try {
const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath);
const tempDir = join(storePath, 'temp');
const chatwootDir = join(storePath, 'chatwoot');
if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true });
}
} catch (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 { join } from 'path';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository';
import { ISqsModel, SqsRaw } from '../models';
export class SqsRepository extends Repository {
constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('SqsRepository');
public async create(data: SqsRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating sqs');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving sqs to db');
const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving sqs to store');
this.writeStore<SqsRaw>({
path: join(this.storePath, 'sqs'),
fileName: instance,
data,
});
this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance);
this.logger.verbose('sqs created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<SqsRaw> {
try {
this.logger.verbose('finding sqs');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding sqs in db');
return await this.sqsModel.findOne({ _id: instance });
}
this.logger.verbose('finding sqs in store');
return JSON.parse(
readFileSync(join(this.storePath, 'sqs', instance + '.json'), {
encoding: 'utf-8',
}),
) as SqsRaw;
} catch (error) {
return {};
}
}
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository';
import { ISqsModel, SqsRaw } from '../models';
export class SqsRepository extends Repository {
constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('SqsRepository');
public async create(data: SqsRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating sqs');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving sqs to db');
const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving sqs to store');
this.writeStore<SqsRaw>({
path: join(this.storePath, 'sqs'),
fileName: instance,
data,
});
this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance);
this.logger.verbose('sqs created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<SqsRaw> {
try {
this.logger.verbose('finding sqs');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding sqs in db');
return await this.sqsModel.findOne({ _id: instance });
}
this.logger.verbose('finding sqs in store');
return JSON.parse(
readFileSync(join(this.storePath, 'sqs', instance + '.json'), {
encoding: 'utf-8',
}),
) as SqsRaw;
} catch (error) {
return {};
}
}
}

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

@ -1,63 +1,63 @@
import { Router } from 'express';
import fs from 'fs';
import { Auth, configService } from '../../config/env.config';
import { authGuard } from '../guards/auth.guard';
import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard';
import { ChamaaiRouter } from './chamaai.router';
import { ChatRouter } from './chat.router';
import { ChatwootRouter } from './chatwoot.router';
import { GroupRouter } from './group.router';
import { InstanceRouter } from './instance.router';
import { ProxyRouter } from './proxy.router';
import { RabbitmqRouter } from './rabbitmq.router';
import { OpenaiRouter } from './openai.router';
import { MessageRouter } from './sendMessage.router';
import { SettingsRouter } from './settings.router';
import { SqsRouter } from './sqs.router';
import { TypebotRouter } from './typebot.router';
import { ViewsRouter } from './view.router';
import { WebhookRouter } from './webhook.router';
import { WebsocketRouter } from './websocket.router';
enum HttpStatus {
OK = 200,
CREATED = 201,
NOT_FOUND = 404,
FORBIDDEN = 403,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
INTERNAL_SERVER_ERROR = 500,
}
const router = Router();
const authType = configService.get<Auth>('AUTHENTICATION').TYPE;
const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
router
.get('/', (req, res) => {
res.status(HttpStatus.OK).json({
status: HttpStatus.OK,
message: 'Api',
// version: packageJson.version,
// documentation: `${req.protocol}://${req.get('host')}/docs`,
});
})
.use('/instance', new InstanceRouter(configService, ...guards).router)
.use('/manager', new ViewsRouter().router)
.use('/message', new MessageRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router)
.use('/webhook', new WebhookRouter(...guards).router)
.use('/chatwoot', new ChatwootRouter(...guards).router)
.use('/settings', new SettingsRouter(...guards).router)
.use('/websocket', new WebsocketRouter(...guards).router)
.use('/rabbitmq', new RabbitmqRouter(...guards).router)
.use('/openai', new OpenaiRouter(...guards).router)
.use('/typebot', new TypebotRouter(...guards).router)
.use('/proxy', new ProxyRouter(...guards).router)
.use('/chamaai', new ChamaaiRouter(...guards).router);
export { HttpStatus, router };
import { Router } from 'express';
import fs from 'fs';
import { Auth, configService } from '../../config/env.config';
import { authGuard } from '../guards/auth.guard';
import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard';
import { ChamaaiRouter } from './chamaai.router';
import { ChatRouter } from './chat.router';
import { ChatwootRouter } from './chatwoot.router';
import { GroupRouter } from './group.router';
import { InstanceRouter } from './instance.router';
import { ProxyRouter } from './proxy.router';
import { RabbitmqRouter } from './rabbitmq.router';
import { OpenaiRouter } from './openai.router';
import { MessageRouter } from './sendMessage.router';
import { SettingsRouter } from './settings.router';
import { SqsRouter } from './sqs.router';
import { TypebotRouter } from './typebot.router';
import { ViewsRouter } from './view.router';
import { WebhookRouter } from './webhook.router';
import { WebsocketRouter } from './websocket.router';
enum HttpStatus {
OK = 200,
CREATED = 201,
NOT_FOUND = 404,
FORBIDDEN = 403,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
INTERNAL_SERVER_ERROR = 500,
}
const router = Router();
const authType = configService.get<Auth>('AUTHENTICATION').TYPE;
const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
router
.get('/', (req, res) => {
res.status(HttpStatus.OK).json({
status: HttpStatus.OK,
message: 'Api',
// version: packageJson.version,
// documentation: `${req.protocol}://${req.get('host')}/docs`,
});
})
.use('/instance', new InstanceRouter(configService, ...guards).router)
.use('/manager', new ViewsRouter().router)
.use('/message', new MessageRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router)
.use('/webhook', new WebhookRouter(...guards).router)
.use('/chatwoot', new ChatwootRouter(...guards).router)
.use('/settings', new SettingsRouter(...guards).router)
.use('/websocket', new WebsocketRouter(...guards).router)
.use('/rabbitmq', new RabbitmqRouter(...guards).router)
.use('/openai', new OpenaiRouter(...guards).router)
.use('/typebot', new TypebotRouter(...guards).router)
.use('/proxy', new ProxyRouter(...guards).router)
.use('/chamaai', new ChamaaiRouter(...guards).router);
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 { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { dbserver } from '../../libs/db.connect';
import { instanceNameSchema, oldTokenSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
import { OldToken } from '../services/auth.service';
import { instanceController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('InstanceRouter');
export class InstanceRouter extends RouterBroker {
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
super();
const auth = configService.get<Auth>('AUTHENTICATION');
this.router
.post('/create', ...guards, async (req, res) => {
logger.verbose('request received in createInstance');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.createInstance(instance),
});
if (req.query['qrcode']) {
return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response.qrcode.base64,
});
} else {
return res.status(HttpStatus.CREATED).json(response);
}
//return res.status(HttpStatus.CREATED).json(response);
})
.put(this.routerPath('restart'), ...guards, async (req, res) => {
logger.verbose('request received in restartInstance');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.restartInstance(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('qr'), ...guards, async (req, res) => {
logger.verbose('request received in get qrCode');
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.qrInstance(instance),
});
return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response,
});
})
.get(this.routerPath('connect'), ...guards, async (req, res) => {
logger.verbose('request received in connectInstance');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.connectToWhatsapp(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('connectionState'), ...guards, async (req, res) => {
logger.verbose('request received in connectionState');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.connectionState(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => {
logger.verbose('request received in fetchInstances');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: null,
ClassRef: InstanceDto,
execute: (instance) => instanceController.fetchInstances(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.delete(this.routerPath('logout'), ...guards, async (req, res) => {
logger.verbose('request received in logoutInstances');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.logout(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.delete(this.routerPath('delete'), ...guards, async (req, res) => {
logger.verbose('request received in deleteInstances');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.deleteInstance(instance),
});
return res.status(HttpStatus.OK).json(response);
});
if (auth.TYPE === 'jwt') {
this.router.put('/refreshToken', async (req, res) => {
logger.verbose('request received in refreshToken');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<OldToken>({
request: req,
schema: oldTokenSchema,
ClassRef: OldToken,
execute: (_, data) => instanceController.refreshToken(_, data),
});
return res.status(HttpStatus.CREATED).json(response);
});
}
this.router.delete('/deleteDatabase', async (req, res) => {
logger.verbose('request received in deleteDatabase');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const db = this.configService.get<Database>('DATABASE');
if (db.ENABLED) {
try {
await dbserver.dropDatabase();
return res
.status(HttpStatus.CREATED)
.json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } });
} 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: 'Database is not enabled' });
});
}
public readonly router = Router();
}
import { RequestHandler, Router } from 'express';
import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { dbserver } from '../../libs/db.connect';
import { instanceNameSchema, oldTokenSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
import { OldToken } from '../services/auth.service';
import { instanceController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('InstanceRouter');
export class InstanceRouter extends RouterBroker {
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
super();
const auth = configService.get<Auth>('AUTHENTICATION');
this.router
.post('/create', ...guards, async (req, res) => {
logger.verbose('request received in createInstance');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.createInstance(instance),
});
if (req.query['qrcode']) {
return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response.qrcode.base64,
});
} else {
return res.status(HttpStatus.CREATED).json(response);
}
//return res.status(HttpStatus.CREATED).json(response);
})
.put(this.routerPath('restart'), ...guards, async (req, res) => {
logger.verbose('request received in restartInstance');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.restartInstance(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('qr'), ...guards, async (req, res) => {
logger.verbose('request received in get qrCode');
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.qrInstance(instance),
});
return res.status(HttpStatus.OK).render('qrcode', {
qrcode: response,
});
})
.get(this.routerPath('connect'), ...guards, async (req, res) => {
logger.verbose('request received in connectInstance');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.connectToWhatsapp(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('connectionState'), ...guards, async (req, res) => {
logger.verbose('request received in connectionState');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.connectionState(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => {
logger.verbose('request received in fetchInstances');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: null,
ClassRef: InstanceDto,
execute: (instance) => instanceController.fetchInstances(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.delete(this.routerPath('logout'), ...guards, async (req, res) => {
logger.verbose('request received in logoutInstances');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.logout(instance),
});
return res.status(HttpStatus.OK).json(response);
})
.delete(this.routerPath('delete'), ...guards, async (req, res) => {
logger.verbose('request received in deleteInstances');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => instanceController.deleteInstance(instance),
});
return res.status(HttpStatus.OK).json(response);
});
if (auth.TYPE === 'jwt') {
this.router.put('/refreshToken', async (req, res) => {
logger.verbose('request received in refreshToken');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<OldToken>({
request: req,
schema: oldTokenSchema,
ClassRef: OldToken,
execute: (_, data) => instanceController.refreshToken(_, data),
});
return res.status(HttpStatus.CREATED).json(response);
});
}
this.router.delete('/deleteDatabase', async (req, res) => {
logger.verbose('request received in deleteDatabase');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const db = this.configService.get<Database>('DATABASE');
if (db.ENABLED) {
try {
await dbserver.dropDatabase();
return res
.status(HttpStatus.CREATED)
.json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } });
} 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: 'Database is not enabled' });
});
}
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 { Logger } from '../../config/logger.config';
import { instanceNameSchema, openaiSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { openaiController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('OpenaiRouter');
export class OpenaiRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) {
super();
this.router
.post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<OpenaiDto>({
request: req,
schema: openaiSchema,
ClassRef: OpenaiDto,
execute: (instance, data) => openaiController.createOpenai(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => openaiController.findOpenai(instance),
});
res.status(HttpStatus.OK).json(response);
})
.post(this.routerPath('contact'), ...guards, async (req, res) => {
logger.verbose('request received in setOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<ContactOpenaiDto>({
request: req,
schema: openaiSchema,
ClassRef: ContactOpenaiDto,
execute: (instance, data) => openaiController.createContactOpenai(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('findcontact'), ...guards, async (req, res) => {
logger.verbose('request received in findOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => openaiController.findContactOpenai(instance),
});
res.status(HttpStatus.OK).json(response);
});
}
public readonly router = Router();
}
import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config';
import { instanceNameSchema, openaiSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
import { OpenaiDto } from '../dto/openai.dto';
import { ContactOpenaiDto } from '../dto/contactopenai.dto';
import { openaiController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('OpenaiRouter');
export class OpenaiRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) {
super();
this.router
.post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<OpenaiDto>({
request: req,
schema: openaiSchema,
ClassRef: OpenaiDto,
execute: (instance, data) => openaiController.createOpenai(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => openaiController.findOpenai(instance),
});
res.status(HttpStatus.OK).json(response);
})
.post(this.routerPath('contact'), ...guards, async (req, res) => {
logger.verbose('request received in setOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<ContactOpenaiDto>({
request: req,
schema: openaiSchema,
ClassRef: ContactOpenaiDto,
execute: (instance, data) => openaiController.createContactOpenai(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('findcontact'), ...guards, async (req, res) => {
logger.verbose('request received in findOpenai');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({
request: req,
schema: instanceNameSchema,
ClassRef: InstanceDto,
execute: (instance) => openaiController.findContactOpenai(instance),
});
res.status(HttpStatus.OK).json(response);
});
}
public readonly router = Router();
}

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

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

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

@ -1,35 +1,35 @@
import { Logger } from '../../config/logger.config';
import { initQueues } from '../../libs/sqs.server';
import { InstanceDto } from '../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto';
import { SqsRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class SqsService {
constructor(private readonly waMonitor: WAMonitoringService) { }
private readonly logger = new Logger(SqsService.name);
public create(instance: InstanceDto, data: SqsDto) {
this.logger.verbose('create sqs: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setSqs(data);
initQueues(instance.instanceName, data.events);
return { sqs: { ...instance, sqs: data } };
}
public async find(instance: InstanceDto): Promise<SqsRaw> {
try {
this.logger.verbose('find sqs: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findSqs();
if (Object.keys(result).length === 0) {
throw new Error('Sqs not found');
}
return result;
} catch (error) {
return { enabled: false, events: [] };
}
}
import { Logger } from '../../config/logger.config';
import { initQueues } from '../../libs/sqs.server';
import { InstanceDto } from '../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto';
import { SqsRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class SqsService {
constructor(private readonly waMonitor: WAMonitoringService) { }
private readonly logger = new Logger(SqsService.name);
public create(instance: InstanceDto, data: SqsDto) {
this.logger.verbose('create sqs: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setSqs(data);
initQueues(instance.instanceName, data.events);
return { sqs: { ...instance, sqs: data } };
}
public async find(instance: InstanceDto): Promise<SqsRaw> {
try {
this.logger.verbose('find sqs: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findSqs();
if (Object.keys(result).length === 0) {
throw new Error('Sqs not found');
}
return result;
} catch (error) {
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 */
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
export enum Events {
APPLICATION_STARTUP = 'application.startup',
QRCODE_UPDATED = 'qrcode.updated',
CONNECTION_UPDATE = 'connection.update',
STATUS_INSTANCE = 'status.instance',
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_UPDATE = 'chats.update',
CHATS_UPSERT = 'chats.upsert',
CHATS_DELETE = 'chats.delete',
GROUPS_UPSERT = 'groups.upsert',
GROUPS_UPDATE = 'groups.update',
GROUP_PARTICIPANTS_UPDATE = 'group-participants.update',
CALL = 'call',
TYPEBOT_START = 'typebot.start',
TYPEBOT_CHANGE_STATUS = 'typebot.change-status',
CHAMA_AI_ACTION = 'chama-ai.action',
}
export declare namespace wa {
export type QrCode = {
count?: number;
pairingCode?: string;
base64?: string;
code?: string;
};
export type Instance = {
qrcode?: QrCode;
pairingCode?: string;
authState?: { state: AuthenticationState; saveCreds: () => void };
name?: string;
wuid?: string;
profileName?: string;
profilePictureUrl?: string;
};
export type LocalWebHook = {
enabled?: boolean;
url?: string;
events?: string[];
webhook_by_events?: boolean;
webhook_base64?: boolean;
};
export type LocalChatwoot = {
enabled?: boolean;
account_id?: string;
token?: string;
url?: string;
name_inbox?: string;
sign_msg?: boolean;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
};
export type LocalSettings = {
reject_call?: boolean;
msg_call?: string;
groups_ignore?: boolean;
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
};
export type LocalWebsocket = {
enabled?: boolean;
events?: string[];
};
export type LocalRabbitmq = {
enabled?: boolean;
events?: string[];
};
export type LocalSqs = {
enabled?: boolean;
events?: string[];
};
export type LocalOpenai = {
chave?: string;
enabled?: boolean;
events?: string[];
};
type Session = {
remoteJid?: string;
sessionId?: string;
createdAt?: number;
};
export type LocalTypebot = {
enabled?: boolean;
url?: string;
typebot?: string;
expire?: number;
keyword_finish?: string;
delay_message?: number;
unknown_message?: string;
listening_from_me?: boolean;
sessions?: Session[];
};
export type LocalProxy = {
enabled?: boolean;
proxy?: string;
};
export type LocalChamaai = {
enabled?: boolean;
url?: string;
token?: string;
waNumber?: string;
answerByAudio?: boolean;
};
export type StateConnection = {
instance?: string;
state?: WAConnectionState | 'refused';
statusReason?: number;
};
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
}
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage'];
export const MessageSubtype = [
'ephemeralMessage',
'documentWithCaptionMessage',
'viewOnceMessage',
'viewOnceMessageV2',
];
/* eslint-disable @typescript-eslint/no-namespace */
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
export enum Events {
APPLICATION_STARTUP = 'application.startup',
QRCODE_UPDATED = 'qrcode.updated',
CONNECTION_UPDATE = 'connection.update',
STATUS_INSTANCE = 'status.instance',
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_UPDATE = 'chats.update',
CHATS_UPSERT = 'chats.upsert',
CHATS_DELETE = 'chats.delete',
GROUPS_UPSERT = 'groups.upsert',
GROUPS_UPDATE = 'groups.update',
GROUP_PARTICIPANTS_UPDATE = 'group-participants.update',
CALL = 'call',
TYPEBOT_START = 'typebot.start',
TYPEBOT_CHANGE_STATUS = 'typebot.change-status',
CHAMA_AI_ACTION = 'chama-ai.action',
}
export declare namespace wa {
export type QrCode = {
count?: number;
pairingCode?: string;
base64?: string;
code?: string;
};
export type Instance = {
qrcode?: QrCode;
pairingCode?: string;
authState?: { state: AuthenticationState; saveCreds: () => void };
name?: string;
wuid?: string;
profileName?: string;
profilePictureUrl?: string;
};
export type LocalWebHook = {
enabled?: boolean;
url?: string;
events?: string[];
webhook_by_events?: boolean;
webhook_base64?: boolean;
};
export type LocalChatwoot = {
enabled?: boolean;
account_id?: string;
token?: string;
url?: string;
name_inbox?: string;
sign_msg?: boolean;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
};
export type LocalSettings = {
reject_call?: boolean;
msg_call?: string;
groups_ignore?: boolean;
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
};
export type LocalWebsocket = {
enabled?: boolean;
events?: string[];
};
export type LocalRabbitmq = {
enabled?: boolean;
events?: string[];
};
export type LocalSqs = {
enabled?: boolean;
events?: string[];
};
export type LocalOpenai = {
chave?: string;
enabled?: boolean;
events?: string[];
};
type Session = {
remoteJid?: string;
sessionId?: string;
createdAt?: number;
};
export type LocalTypebot = {
enabled?: boolean;
url?: string;
typebot?: string;
expire?: number;
keyword_finish?: string;
delay_message?: number;
unknown_message?: string;
listening_from_me?: boolean;
sessions?: Session[];
};
export type LocalProxy = {
enabled?: boolean;
proxy?: string;
};
export type LocalChamaai = {
enabled?: boolean;
url?: string;
token?: string;
waNumber?: string;
answerByAudio?: boolean;
};
export type StateConnection = {
instance?: string;
state?: WAConnectionState | 'refused';
statusReason?: number;
};
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
}
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage'];
export const MessageSubtype = [
'ephemeralMessage',
'documentWithCaptionMessage',
'viewOnceMessage',
'viewOnceMessageV2',
];

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

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