This commit is contained in:
Alan Mosko 2023-07-26 11:12:00 -03:00
parent 0a851b935e
commit dc3d59bae1
69 changed files with 9028 additions and 9104 deletions

View File

@ -4,7 +4,7 @@ module.exports = {
singleQuote: true, singleQuote: true,
printWidth: 120, printWidth: 120,
arrowParens: 'always', arrowParens: 'always',
tabWidth: 4, tabWidth: 2,
useTabs: false, useTabs: false,
bracketSameLine: false, bracketSameLine: false,
bracketSpacing: true, bracketSpacing: true,

View File

@ -7,9 +7,9 @@ export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string };
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE'; export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
export type Cors = { export type Cors = {
ORIGIN: string[]; ORIGIN: string[];
METHODS: HttpMethods[]; METHODS: HttpMethods[];
CREDENTIALS: boolean; CREDENTIALS: boolean;
}; };
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
@ -17,90 +17,90 @@ export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS'; export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS';
export type Log = { export type Log = {
LEVEL: LogLevel[]; LEVEL: LogLevel[];
COLOR: boolean; COLOR: boolean;
BAILEYS: LogBaileys; BAILEYS: LogBaileys;
}; };
export type SaveData = { export type SaveData = {
INSTANCE: boolean; INSTANCE: boolean;
NEW_MESSAGE: boolean; NEW_MESSAGE: boolean;
MESSAGE_UPDATE: boolean; MESSAGE_UPDATE: boolean;
CONTACTS: boolean; CONTACTS: boolean;
CHATS: boolean; CHATS: boolean;
}; };
export type StoreConf = { export type StoreConf = {
MESSAGES: boolean; MESSAGES: boolean;
MESSAGE_UP: boolean; MESSAGE_UP: boolean;
CONTACTS: boolean; CONTACTS: boolean;
CHATS: boolean; CHATS: boolean;
}; };
export type CleanStoreConf = { export type CleanStoreConf = {
CLEANING_INTERVAL: number; CLEANING_INTERVAL: number;
MESSAGES: boolean; MESSAGES: boolean;
MESSAGE_UP: boolean; MESSAGE_UP: boolean;
CONTACTS: boolean; CONTACTS: boolean;
CHATS: boolean; CHATS: boolean;
}; };
export type DBConnection = { export type DBConnection = {
URI: string; URI: string;
DB_PREFIX_NAME: string; DB_PREFIX_NAME: string;
}; };
export type Database = { export type Database = {
CONNECTION: DBConnection; CONNECTION: DBConnection;
ENABLED: boolean; ENABLED: boolean;
SAVE_DATA: SaveData; SAVE_DATA: SaveData;
}; };
export type Redis = { export type Redis = {
ENABLED: boolean; ENABLED: boolean;
URI: string; URI: string;
PREFIX_KEY: string; PREFIX_KEY: string;
}; };
export type EventsWebhook = { export type EventsWebhook = {
APPLICATION_STARTUP: boolean; APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean; QRCODE_UPDATED: boolean;
MESSAGES_SET: boolean; MESSAGES_SET: boolean;
MESSAGES_UPSERT: boolean; MESSAGES_UPSERT: boolean;
MESSAGES_UPDATE: boolean; MESSAGES_UPDATE: boolean;
MESSAGES_DELETE: boolean; MESSAGES_DELETE: boolean;
SEND_MESSAGE: boolean; SEND_MESSAGE: boolean;
CONTACTS_SET: boolean; CONTACTS_SET: boolean;
CONTACTS_UPDATE: boolean; CONTACTS_UPDATE: boolean;
CONTACTS_UPSERT: boolean; CONTACTS_UPSERT: boolean;
PRESENCE_UPDATE: boolean; PRESENCE_UPDATE: boolean;
CHATS_SET: boolean; CHATS_SET: boolean;
CHATS_UPDATE: boolean; CHATS_UPDATE: boolean;
CHATS_DELETE: boolean; CHATS_DELETE: boolean;
CHATS_UPSERT: boolean; CHATS_UPSERT: boolean;
CONNECTION_UPDATE: boolean; CONNECTION_UPDATE: boolean;
GROUPS_UPSERT: boolean; GROUPS_UPSERT: boolean;
GROUP_UPDATE: boolean; GROUP_UPDATE: boolean;
GROUP_PARTICIPANTS_UPDATE: boolean; GROUP_PARTICIPANTS_UPDATE: boolean;
CALL: boolean; CALL: boolean;
NEW_JWT_TOKEN: boolean; NEW_JWT_TOKEN: boolean;
}; };
export type ApiKey = { KEY: string }; export type ApiKey = { KEY: string };
export type Jwt = { EXPIRIN_IN: number; SECRET: string }; export type Jwt = { EXPIRIN_IN: number; SECRET: string };
export type Auth = { export type Auth = {
API_KEY: ApiKey; API_KEY: ApiKey;
EXPOSE_IN_FETCH_INSTANCES: boolean; EXPOSE_IN_FETCH_INSTANCES: boolean;
JWT: Jwt; JWT: Jwt;
TYPE: 'jwt' | 'apikey'; TYPE: 'jwt' | 'apikey';
}; };
export type DelInstance = number | boolean; export type DelInstance = number | boolean;
export type GlobalWebhook = { export type GlobalWebhook = {
URL: string; URL: string;
ENABLED: boolean; ENABLED: boolean;
WEBHOOK_BY_EVENTS: boolean; WEBHOOK_BY_EVENTS: boolean;
}; };
export type SslConf = { PRIVKEY: string; FULLCHAIN: string }; export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook }; export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
@ -109,158 +109,158 @@ export type QrCode = { LIMIT: number };
export type Production = boolean; export type Production = boolean;
export interface Env { export interface Env {
SERVER: HttpServer; SERVER: HttpServer;
CORS: Cors; CORS: Cors;
SSL_CONF: SslConf; SSL_CONF: SslConf;
STORE: StoreConf; STORE: StoreConf;
CLEAN_STORE: CleanStoreConf; CLEAN_STORE: CleanStoreConf;
DATABASE: Database; DATABASE: Database;
REDIS: Redis; REDIS: Redis;
LOG: Log; LOG: Log;
DEL_INSTANCE: DelInstance; DEL_INSTANCE: DelInstance;
WEBHOOK: Webhook; WEBHOOK: Webhook;
CONFIG_SESSION_PHONE: ConfigSessionPhone; CONFIG_SESSION_PHONE: ConfigSessionPhone;
QRCODE: QrCode; QRCODE: QrCode;
AUTHENTICATION: Auth; AUTHENTICATION: Auth;
PRODUCTION?: Production; PRODUCTION?: Production;
} }
export type Key = keyof Env; export type Key = keyof Env;
export class ConfigService { export class ConfigService {
constructor() { constructor() {
this.loadEnv(); this.loadEnv();
} }
private env: Env; private env: Env;
public get<T = any>(key: Key) { public get<T = any>(key: Key) {
return this.env[key] as T; return this.env[key] as T;
} }
private loadEnv() { private loadEnv() {
this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess(); this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD'; this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
if (process.env?.DOCKER_ENV === 'true') { if (process.env?.DOCKER_ENV === 'true') {
this.env.SERVER.TYPE = 'http'; this.env.SERVER.TYPE = 'http';
this.env.SERVER.PORT = 8080; this.env.SERVER.PORT = 8080;
}
} }
}
private envYaml(): Env { private envYaml(): Env {
return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env; return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env;
} }
private envProcess(): Env { private envProcess(): Env {
return { return {
SERVER: { SERVER: {
TYPE: process.env.SERVER_TYPE as 'http' | 'https', TYPE: process.env.SERVER_TYPE as 'http' | 'https',
PORT: Number.parseInt(process.env.SERVER_PORT), PORT: Number.parseInt(process.env.SERVER_PORT),
URL: process.env.SERVER_URL, URL: process.env.SERVER_URL,
}, },
CORS: { CORS: {
ORIGIN: process.env.CORS_ORIGIN.split(','), ORIGIN: process.env.CORS_ORIGIN.split(','),
METHODS: process.env.CORS_METHODS.split(',') as HttpMethods[], METHODS: process.env.CORS_METHODS.split(',') as HttpMethods[],
CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true', CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true',
}, },
SSL_CONF: { SSL_CONF: {
PRIVKEY: process.env?.SSL_CONF_PRIVKEY, PRIVKEY: process.env?.SSL_CONF_PRIVKEY,
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN, FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN,
}, },
STORE: { STORE: {
MESSAGES: process.env?.STORE_MESSAGES === 'true', MESSAGES: process.env?.STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true', MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.STORE_CONTACTS === 'true', CONTACTS: process.env?.STORE_CONTACTS === 'true',
CHATS: process.env?.STORE_CHATS === 'true', CHATS: process.env?.STORE_CHATS === 'true',
}, },
CLEAN_STORE: { CLEAN_STORE: {
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL) CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL) ? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL)
: 7200, : 7200,
MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true', MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true', MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true',
CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true', CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true',
CHATS: process.env?.CLEAN_STORE_CHATS === 'true', CHATS: process.env?.CLEAN_STORE_CHATS === 'true',
}, },
DATABASE: { DATABASE: {
CONNECTION: { CONNECTION: {
URI: process.env.DATABASE_CONNECTION_URI, URI: process.env.DATABASE_CONNECTION_URI,
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME, DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME,
}, },
ENABLED: process.env?.DATABASE_ENABLED === 'true', ENABLED: process.env?.DATABASE_ENABLED === 'true',
SAVE_DATA: { SAVE_DATA: {
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true', INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true', NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true', MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true', CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true', CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
}, },
}, },
REDIS: { REDIS: {
ENABLED: process.env?.REDIS_ENABLED === 'true', ENABLED: process.env?.REDIS_ENABLED === 'true',
URI: process.env.REDIS_URI, URI: process.env.REDIS_URI,
PREFIX_KEY: process.env.REDIS_PREFIX_KEY, PREFIX_KEY: process.env.REDIS_PREFIX_KEY,
}, },
LOG: { LOG: {
LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[], LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[],
COLOR: process.env?.LOG_COLOR === 'true', COLOR: process.env?.LOG_COLOR === 'true',
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error', BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
}, },
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE) DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
? process.env.DEL_INSTANCE === 'true' ? process.env.DEL_INSTANCE === 'true'
: Number.parseInt(process.env.DEL_INSTANCE) || false, : Number.parseInt(process.env.DEL_INSTANCE) || false,
WEBHOOK: { WEBHOOK: {
GLOBAL: { GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL, URL: process.env?.WEBHOOK_GLOBAL_URL,
ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true', ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true',
WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true', WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true',
}, },
EVENTS: { EVENTS: {
APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true', APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true',
QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true', QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true',
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true', MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true', MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true',
MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true', MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true',
MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true', MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true',
SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true', SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true',
CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true', CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true',
CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true', CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true', CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true',
PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true', PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true',
CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true', CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true',
CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true', CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true',
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true', CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true', CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true', CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true', GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true', GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true', GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true', CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true',
NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true', NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true',
}, },
}, },
CONFIG_SESSION_PHONE: { CONFIG_SESSION_PHONE: {
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API', CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API',
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'chrome', NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'chrome',
}, },
QRCODE: { QRCODE: {
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30, LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30,
}, },
AUTHENTICATION: { AUTHENTICATION: {
TYPE: process.env.AUTHENTICATION_TYPE as 'jwt', TYPE: process.env.AUTHENTICATION_TYPE as 'jwt',
API_KEY: { API_KEY: {
KEY: process.env.AUTHENTICATION_API_KEY, KEY: process.env.AUTHENTICATION_API_KEY,
}, },
EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true', EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true',
JWT: { JWT: {
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN) EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN) ? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
: 3600, : 3600,
SECRET: process.env.AUTHENTICATION_JWT_SECRET, SECRET: process.env.AUTHENTICATION_JWT_SECRET,
}, },
}, },
}; };
} }
} }
export const configService = new ConfigService(); export const configService = new ConfigService();

View File

@ -1,21 +1,21 @@
import { Logger } from './logger.config'; import { Logger } from './logger.config';
export function onUnexpectedError() { export function onUnexpectedError() {
process.on('uncaughtException', (error, origin) => { process.on('uncaughtException', (error, origin) => {
const logger = new Logger('uncaughtException'); const logger = new Logger('uncaughtException');
logger.error({ logger.error({
origin, origin,
stderr: process.stderr.fd, stderr: process.stderr.fd,
error, error,
});
}); });
});
process.on('unhandledRejection', (error, origin) => { process.on('unhandledRejection', (error, origin) => {
const logger = new Logger('unhandledRejection'); const logger = new Logger('unhandledRejection');
logger.error({ logger.error({
origin, origin,
stderr: process.stderr.fd, stderr: process.stderr.fd,
error, error,
});
}); });
});
} }

View File

@ -1,7 +1,7 @@
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
export const eventEmitter = new EventEmitter2({ export const eventEmitter = new EventEmitter2({
delimiter: '.', delimiter: '.',
newListener: false, newListener: false,
ignoreErrors: false, ignoreErrors: false,
}); });

View File

@ -3,135 +3,135 @@ import dayjs from 'dayjs';
import { configService, Log } from './env.config'; import { configService, Log } from './env.config';
const formatDateLog = (timestamp: number) => const formatDateLog = (timestamp: number) =>
dayjs(timestamp) dayjs(timestamp)
.toDate() .toDate()
.toString() .toString()
.replace(/\sGMT.+/, ''); .replace(/\sGMT.+/, '');
enum Color { enum Color {
LOG = '\x1b[32m', LOG = '\x1b[32m',
INFO = '\x1b[34m', INFO = '\x1b[34m',
WARN = '\x1b[33m', WARN = '\x1b[33m',
ERROR = '\x1b[31m', ERROR = '\x1b[31m',
DEBUG = '\x1b[36m', DEBUG = '\x1b[36m',
VERBOSE = '\x1b[37m', VERBOSE = '\x1b[37m',
DARK = '\x1b[30m', DARK = '\x1b[30m',
} }
enum Command { enum Command {
RESET = '\x1b[0m', RESET = '\x1b[0m',
BRIGHT = '\x1b[1m', BRIGHT = '\x1b[1m',
UNDERSCORE = '\x1b[4m', UNDERSCORE = '\x1b[4m',
} }
enum Level { enum Level {
LOG = Color.LOG + '%s' + Command.RESET, LOG = Color.LOG + '%s' + Command.RESET,
DARK = Color.DARK + '%s' + Command.RESET, DARK = Color.DARK + '%s' + Command.RESET,
INFO = Color.INFO + '%s' + Command.RESET, INFO = Color.INFO + '%s' + Command.RESET,
WARN = Color.WARN + '%s' + Command.RESET, WARN = Color.WARN + '%s' + Command.RESET,
ERROR = Color.ERROR + '%s' + Command.RESET, ERROR = Color.ERROR + '%s' + Command.RESET,
DEBUG = Color.DEBUG + '%s' + Command.RESET, DEBUG = Color.DEBUG + '%s' + Command.RESET,
VERBOSE = Color.VERBOSE + '%s' + Command.RESET, VERBOSE = Color.VERBOSE + '%s' + Command.RESET,
} }
enum Type { enum Type {
LOG = 'LOG', LOG = 'LOG',
WARN = 'WARN', WARN = 'WARN',
INFO = 'INFO', INFO = 'INFO',
DARK = 'DARK', DARK = 'DARK',
ERROR = 'ERROR', ERROR = 'ERROR',
DEBUG = 'DEBUG', DEBUG = 'DEBUG',
VERBOSE = 'VERBOSE', VERBOSE = 'VERBOSE',
} }
enum Background { enum Background {
LOG = '\x1b[42m', LOG = '\x1b[42m',
INFO = '\x1b[44m', INFO = '\x1b[44m',
WARN = '\x1b[43m', WARN = '\x1b[43m',
DARK = '\x1b[40m', DARK = '\x1b[40m',
ERROR = '\x1b[41m', ERROR = '\x1b[41m',
DEBUG = '\x1b[46m', DEBUG = '\x1b[46m',
VERBOSE = '\x1b[47m', VERBOSE = '\x1b[47m',
} }
export class Logger { export class Logger {
private readonly configService = configService; private readonly configService = configService;
constructor(private context = 'Logger') {} constructor(private context = 'Logger') {}
public setContext(value: string) { public setContext(value: string) {
this.context = value; this.context = value;
}
private console(value: any, type: Type) {
const types: Type[] = [];
this.configService.get<Log>('LOG').LEVEL.forEach((level) => types.push(Type[level]));
const typeValue = typeof value;
if (types.includes(type)) {
if (configService.get<Log>('LOG').COLOR) {
console.log(
/*Command.UNDERSCORE +*/ Command.BRIGHT + Level[type],
'[Evolution API]',
Command.BRIGHT + Color[type],
process.pid.toString(),
Command.RESET,
Command.BRIGHT + Color[type],
'-',
Command.BRIGHT + Color.VERBOSE,
`${formatDateLog(Date.now())} `,
Command.RESET,
Color[type] + Background[type] + Command.BRIGHT,
`${type} ` + Command.RESET,
Color.WARN + Command.BRIGHT,
`[${this.context}]` + Command.RESET,
Color[type] + Command.BRIGHT,
`[${typeValue}]` + Command.RESET,
Color[type],
typeValue !== 'object' ? value : '',
Command.RESET,
);
typeValue === 'object' ? console.log(/*Level.DARK,*/ value, '\n') : '';
} else {
console.log(
'[Evolution API]',
process.pid.toString(),
'-',
`${formatDateLog(Date.now())} `,
`${type} `,
`[${this.context}]`,
`[${typeValue}]`,
value,
);
}
} }
}
private console(value: any, type: Type) { public log(value: any) {
const types: Type[] = []; this.console(value, Type.LOG);
}
this.configService.get<Log>('LOG').LEVEL.forEach((level) => types.push(Type[level])); public info(value: any) {
this.console(value, Type.INFO);
}
const typeValue = typeof value; public warn(value: any) {
if (types.includes(type)) { this.console(value, Type.WARN);
if (configService.get<Log>('LOG').COLOR) { }
console.log(
/*Command.UNDERSCORE +*/ Command.BRIGHT + Level[type],
'[Evolution API]',
Command.BRIGHT + Color[type],
process.pid.toString(),
Command.RESET,
Command.BRIGHT + Color[type],
'-',
Command.BRIGHT + Color.VERBOSE,
`${formatDateLog(Date.now())} `,
Command.RESET,
Color[type] + Background[type] + Command.BRIGHT,
`${type} ` + Command.RESET,
Color.WARN + Command.BRIGHT,
`[${this.context}]` + Command.RESET,
Color[type] + Command.BRIGHT,
`[${typeValue}]` + Command.RESET,
Color[type],
typeValue !== 'object' ? value : '',
Command.RESET,
);
typeValue === 'object' ? console.log(/*Level.DARK,*/ value, '\n') : '';
} else {
console.log(
'[Evolution API]',
process.pid.toString(),
'-',
`${formatDateLog(Date.now())} `,
`${type} `,
`[${this.context}]`,
`[${typeValue}]`,
value,
);
}
}
}
public log(value: any) { public error(value: any) {
this.console(value, Type.LOG); this.console(value, Type.ERROR);
} }
public info(value: any) { public verbose(value: any) {
this.console(value, Type.INFO); this.console(value, Type.VERBOSE);
} }
public warn(value: any) { public debug(value: any) {
this.console(value, Type.WARN); this.console(value, Type.DEBUG);
} }
public error(value: any) { public dark(value: any) {
this.console(value, Type.ERROR); this.console(value, Type.DARK);
} }
public verbose(value: any) {
this.console(value, Type.VERBOSE);
}
public debug(value: any) {
this.console(value, Type.DEBUG);
}
public dark(value: any) {
this.console(value, Type.DARK);
}
} }

View File

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

View File

@ -5,101 +5,101 @@ import { Redis } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
export class RedisCache { export class RedisCache {
constructor() { constructor() {
this.logger.verbose('instance created'); this.logger.verbose('instance created');
process.on('beforeExit', async () => { process.on('beforeExit', async () => {
this.logger.verbose('instance destroyed'); this.logger.verbose('instance destroyed');
if (this.statusConnection) { if (this.statusConnection) {
this.logger.verbose('instance disconnect'); this.logger.verbose('instance disconnect');
await this.client.disconnect(); await this.client.disconnect();
} }
}); });
}
private statusConnection = false;
private 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');
this.client = createClient({ url: redisEnv.URI });
this.logger.verbose('connected in ' + redisEnv.URI);
await this.client.connect();
this.statusConnection = true;
this.redisEnv = redisEnv;
}
private readonly logger = new Logger(RedisCache.name);
private client: RedisClientType;
public async instanceKeys(): Promise<string[]> {
try {
this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']);
} catch (error) {
this.logger.error(error);
} }
}
private statusConnection = false; public async keyExists(key?: string) {
private instanceName: string; if (key) {
private redisEnv: Redis; this.logger.verbose('keyExists: ' + key);
return !!(await this.instanceKeys()).find((i) => i === key);
public set reference(reference: string) {
this.logger.verbose('set reference: ' + reference);
this.instanceName = reference;
} }
this.logger.verbose('keyExists: ' + this.instanceName);
return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
}
public async connect(redisEnv: Redis) { public async writeData(field: string, data: any) {
this.logger.verbose('connecting'); try {
this.client = createClient({ url: redisEnv.URI }); this.logger.verbose('writeData: ' + field);
this.logger.verbose('connected in ' + redisEnv.URI); const json = JSON.stringify(data, BufferJSON.replacer);
await this.client.connect();
this.statusConnection = true; return await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json);
this.redisEnv = redisEnv; } catch (error) {
this.logger.error(error);
} }
}
private readonly logger = new Logger(RedisCache.name); public async readData(field: string) {
private client: RedisClientType; try {
this.logger.verbose('readData: ' + field);
const data = await this.client.hGet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field);
public async instanceKeys(): Promise<string[]> { if (data) {
try { this.logger.verbose('readData: ' + field + ' success');
this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*'); return JSON.parse(data, BufferJSON.reviver);
return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']); }
} catch (error) {
this.logger.error(error); this.logger.verbose('readData: ' + field + ' not found');
} return null;
} catch (error) {
this.logger.error(error);
} }
}
public async keyExists(key?: string) { public async removeData(field: string) {
if (key) { try {
this.logger.verbose('keyExists: ' + key); this.logger.verbose('removeData: ' + field);
return !!(await this.instanceKeys()).find((i) => i === key); return await this.client.hDel(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field);
} } catch (error) {
this.logger.verbose('keyExists: ' + this.instanceName); this.logger.error(error);
return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
} }
}
public async writeData(field: string, data: any) { public async delAll(hash?: string) {
try { try {
this.logger.verbose('writeData: ' + field); this.logger.verbose('instance delAll: ' + hash);
const json = JSON.stringify(data, BufferJSON.replacer); const result = await this.client.del(hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName);
return await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json); return result;
} catch (error) { } catch (error) {
this.logger.error(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);
}
} }
}
} }

View File

@ -1,11 +1,11 @@
import { HttpStatus } from '../whatsapp/routers/index.router'; import { HttpStatus } from '../whatsapp/routers/index.router';
export class BadRequestException { export class BadRequestException {
constructor(...objectError: any[]) { constructor(...objectError: any[]) {
throw { throw {
status: HttpStatus.BAD_REQUEST, status: HttpStatus.BAD_REQUEST,
error: 'Bad Request', error: 'Bad Request',
message: objectError.length > 0 ? objectError : undefined, message: objectError.length > 0 ? objectError : undefined,
}; };
} }
} }

View File

@ -1,11 +1,11 @@
import { HttpStatus } from '../whatsapp/routers/index.router'; import { HttpStatus } from '../whatsapp/routers/index.router';
export class UnauthorizedException { export class UnauthorizedException {
constructor(...objectError: any[]) { constructor(...objectError: any[]) {
throw { throw {
status: HttpStatus.UNAUTHORIZED, status: HttpStatus.UNAUTHORIZED,
error: 'Unauthorized', error: 'Unauthorized',
message: objectError.length > 0 ? objectError : undefined, message: objectError.length > 0 ? objectError : undefined,
}; };
} }
} }

View File

@ -1,11 +1,11 @@
import { HttpStatus } from '../whatsapp/routers/index.router'; import { HttpStatus } from '../whatsapp/routers/index.router';
export class ForbiddenException { export class ForbiddenException {
constructor(...objectError: any[]) { constructor(...objectError: any[]) {
throw { throw {
status: HttpStatus.FORBIDDEN, status: HttpStatus.FORBIDDEN,
error: 'Forbidden', error: 'Forbidden',
message: objectError.length > 0 ? objectError : undefined, message: objectError.length > 0 ? objectError : undefined,
}; };
} }
} }

View File

@ -1,11 +1,11 @@
import { HttpStatus } from '../whatsapp/routers/index.router'; import { HttpStatus } from '../whatsapp/routers/index.router';
export class NotFoundException { export class NotFoundException {
constructor(...objectError: any[]) { constructor(...objectError: any[]) {
throw { throw {
status: HttpStatus.NOT_FOUND, status: HttpStatus.NOT_FOUND,
error: 'Not Found', error: 'Not Found',
message: objectError.length > 0 ? objectError : undefined, message: objectError.length > 0 ? objectError : undefined,
}; };
} }
} }

View File

@ -1,11 +1,11 @@
import { HttpStatus } from '../whatsapp/routers/index.router'; import { HttpStatus } from '../whatsapp/routers/index.router';
export class InternalServerErrorException { export class InternalServerErrorException {
constructor(...objectError: any[]) { constructor(...objectError: any[]) {
throw { throw {
status: HttpStatus.INTERNAL_SERVER_ERROR, status: HttpStatus.INTERNAL_SERVER_ERROR,
error: 'Internal Server Error', error: 'Internal Server Error',
message: objectError.length > 0 ? objectError : undefined, message: objectError.length > 0 ? objectError : undefined,
}; };
} }
} }

View File

@ -15,94 +15,94 @@ import { HttpStatus, router } from './whatsapp/routers/index.router';
import { waMonitor } from './whatsapp/whatsapp.module'; import { waMonitor } from './whatsapp/whatsapp.module';
function initWA() { function initWA() {
waMonitor.loadInstance(); waMonitor.loadInstance();
} }
function bootstrap() { function bootstrap() {
const logger = new Logger('SERVER'); const logger = new Logger('SERVER');
const app = express(); const app = express();
// Sentry.init({ // Sentry.init({
// dsn: '', // dsn: '',
// integrations: [ // integrations: [
// // enable HTTP calls tracing // // enable HTTP calls tracing
// new Sentry.Integrations.Http({ tracing: true }), // new Sentry.Integrations.Http({ tracing: true }),
// // enable Express.js middleware tracing // // enable Express.js middleware tracing
// new Sentry.Integrations.Express({ app }), // new Sentry.Integrations.Express({ app }),
// // Automatically instrument Node.js libraries and frameworks // // Automatically instrument Node.js libraries and frameworks
// ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(), // ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),
// ], // ],
// // Set tracesSampleRate to 1.0 to capture 100% // // Set tracesSampleRate to 1.0 to capture 100%
// // of transactions for performance monitoring. // // of transactions for performance monitoring.
// // We recommend adjusting this value in production // // We recommend adjusting this value in production
// tracesSampleRate: 1.0, // tracesSampleRate: 1.0,
// }); // });
// app.use(Sentry.Handlers.requestHandler()); // app.use(Sentry.Handlers.requestHandler());
// app.use(Sentry.Handlers.tracingHandler()); // app.use(Sentry.Handlers.tracingHandler());
app.use( app.use(
cors({ cors({
origin(requestOrigin, callback) { origin(requestOrigin, callback) {
const { ORIGIN } = configService.get<Cors>('CORS'); const { ORIGIN } = configService.get<Cors>('CORS');
!requestOrigin ? (requestOrigin = '*') : undefined; !requestOrigin ? (requestOrigin = '*') : undefined;
if (ORIGIN.indexOf(requestOrigin) !== -1) { if (ORIGIN.indexOf(requestOrigin) !== -1) {
return callback(null, true); return callback(null, true);
} }
return callback(new Error('Not allowed by CORS')); return callback(new Error('Not allowed by CORS'));
}, },
methods: [...configService.get<Cors>('CORS').METHODS], methods: [...configService.get<Cors>('CORS').METHODS],
credentials: configService.get<Cors>('CORS').CREDENTIALS, credentials: configService.get<Cors>('CORS').CREDENTIALS,
}), }),
urlencoded({ extended: true, limit: '136mb' }), urlencoded({ extended: true, limit: '136mb' }),
json({ limit: '136mb' }), json({ limit: '136mb' }),
compression(), compression(),
); );
app.set('view engine', 'hbs'); app.set('view engine', 'hbs');
app.set('views', join(ROOT_DIR, 'views')); app.set('views', join(ROOT_DIR, 'views'));
app.use(express.static(join(ROOT_DIR, 'public'))); app.use(express.static(join(ROOT_DIR, 'public')));
app.use('/', router); app.use('/', router);
// app.use(Sentry.Handlers.errorHandler()); // app.use(Sentry.Handlers.errorHandler());
// app.use(function onError(err, req, res, next) { // app.use(function onError(err, req, res, next) {
// res.statusCode = 500; // res.statusCode = 500;
// res.end(res.sentry + '\n'); // res.end(res.sentry + '\n');
// }); // });
app.use( app.use(
(err: Error, req: Request, res: Response) => { (err: Error, req: Request, res: Response) => {
if (err) { if (err) {
return res.status(err['status'] || 500).json(err); return res.status(err['status'] || 500).json(err);
} }
}, },
(req: Request, res: Response, next: NextFunction) => { (req: Request, res: Response, next: NextFunction) => {
const { method, url } = req; const { method, url } = req;
res.status(HttpStatus.NOT_FOUND).json({ res.status(HttpStatus.NOT_FOUND).json({
status: HttpStatus.NOT_FOUND, status: HttpStatus.NOT_FOUND,
message: `Cannot ${method.toUpperCase()} ${url}`, message: `Cannot ${method.toUpperCase()} ${url}`,
error: 'Not Found', error: 'Not Found',
}); });
next(); next();
}, },
); );
const httpServer = configService.get<HttpServer>('SERVER'); const httpServer = configService.get<HttpServer>('SERVER');
ServerUP.app = app; ServerUP.app = app;
const server = ServerUP[httpServer.TYPE]; const server = ServerUP[httpServer.TYPE];
server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT)); server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT));
initWA(); initWA();
onUnexpectedError(); onUnexpectedError();
} }
bootstrap(); bootstrap();

View File

@ -6,24 +6,24 @@ import * as https from 'https';
import { configService, SslConf } from '../config/env.config'; import { configService, SslConf } from '../config/env.config';
export class ServerUP { export class ServerUP {
static #app: Express; static #app: Express;
static set app(e: Express) { static set app(e: Express) {
this.#app = e; this.#app = e;
} }
static get https() { static get https() {
const { FULLCHAIN, PRIVKEY } = configService.get<SslConf>('SSL_CONF'); const { FULLCHAIN, PRIVKEY } = configService.get<SslConf>('SSL_CONF');
return https.createServer( return https.createServer(
{ {
cert: readFileSync(FULLCHAIN), cert: readFileSync(FULLCHAIN),
key: readFileSync(PRIVKEY), key: readFileSync(PRIVKEY),
}, },
ServerUP.#app, ServerUP.#app,
); );
} }
static get http() { static get http() {
return http.createServer(ServerUP.#app); return http.createServer(ServerUP.#app);
} }
} }

View File

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

View File

@ -1,84 +1,84 @@
import { import {
AuthenticationCreds, AuthenticationCreds,
AuthenticationState, AuthenticationState,
initAuthCreds, initAuthCreds,
proto, proto,
SignalDataTypeMap, SignalDataTypeMap,
} from '@whiskeysockets/baileys'; } from '@whiskeysockets/baileys';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
import { RedisCache } from '../db/redis.client'; import { RedisCache } from '../db/redis.client';
export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
state: AuthenticationState; state: AuthenticationState;
saveCreds: () => Promise<void>; saveCreds: () => Promise<void>;
}> { }> {
const logger = new Logger(useMultiFileAuthStateRedisDb.name); const logger = new Logger(useMultiFileAuthStateRedisDb.name);
const writeData = async (data: any, key: string): Promise<any> => { const writeData = async (data: any, key: string): Promise<any> => {
try { try {
return await cache.writeData(key, data); return await cache.writeData(key, data);
} catch (error) { } catch (error) {
return logger.error({ localError: 'writeData', error }); return logger.error({ localError: 'writeData', error });
} }
}; };
const readData = async (key: string): Promise<any> => { const readData = async (key: string): Promise<any> => {
try { try {
return await cache.readData(key); return await cache.readData(key);
} catch (error) { } catch (error) {
logger.error({ readData: 'writeData', error }); logger.error({ readData: 'writeData', error });
return; return;
} }
}; };
const removeData = async (key: string) => { const removeData = async (key: string) => {
try { try {
return await cache.removeData(key); return await cache.removeData(key);
} catch (error) { } catch (error) {
logger.error({ readData: 'removeData', error }); logger.error({ readData: 'removeData', error });
} }
}; };
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds(); const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return { return {
state: { state: {
creds, creds,
keys: { keys: {
get: async (type, ids: string[]) => { get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {}; const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all( await Promise.all(
ids.map(async (id) => { ids.map(async (id) => {
let value = await readData(`${type}-${id}`); let value = await readData(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) { if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value); value = proto.Message.AppStateSyncKeyData.fromObject(value);
} }
data[id] = value; data[id] = value;
}), }),
); );
return data; return data;
},
set: async (data: any) => {
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 ? await writeData(value, key) : await removeData(key));
}
}
await Promise.all(tasks);
},
},
}, },
saveCreds: async () => { set: async (data: any) => {
return await writeData(creds, 'creds'); 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 ? await writeData(value, key) : await removeData(key));
}
}
await Promise.all(tasks);
}, },
}; },
},
saveCreds: async () => {
return await writeData(creds, 'creds');
},
};
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,61 +7,61 @@ import { ROOT_DIR } from '../../config/path.config';
export type IInsert = { insertCount: number }; export type IInsert = { insertCount: number };
export interface IRepository { export interface IRepository {
insert(data: any, instanceName: string, saveDb?: boolean): Promise<IInsert>; insert(data: any, instanceName: string, saveDb?: boolean): Promise<IInsert>;
update(data: any, instanceName: string, saveDb?: boolean): Promise<IInsert>; update(data: any, instanceName: string, saveDb?: boolean): Promise<IInsert>;
find(query: any): Promise<any>; find(query: any): Promise<any>;
delete(query: any, force?: boolean): Promise<any>; delete(query: any, force?: boolean): Promise<any>;
dbSettings: Database; dbSettings: Database;
readonly storePath: string; readonly storePath: string;
} }
type WriteStore<U> = { type WriteStore<U> = {
path: string; path: string;
fileName: string; fileName: string;
data: U; data: U;
}; };
export abstract class Repository implements IRepository { export abstract class Repository implements IRepository {
constructor(configService: ConfigService) { constructor(configService: ConfigService) {
this.dbSettings = configService.get<Database>('DATABASE'); this.dbSettings = configService.get<Database>('DATABASE');
}
dbSettings: Database;
readonly storePath = join(ROOT_DIR, 'store');
public writeStore = <T = any>(create: WriteStore<T>) => {
if (!existsSync(create.path)) {
mkdirSync(create.path, { recursive: true });
} }
try {
writeFileSync(join(create.path, create.fileName + '.json'), JSON.stringify({ ...create.data }), {
encoding: 'utf-8',
});
dbSettings: Database; return { message: 'create - success' };
readonly storePath = join(ROOT_DIR, 'store'); } finally {
create.data = undefined;
}
};
public writeStore = <T = any>(create: WriteStore<T>) => { // eslint-disable-next-line
if (!existsSync(create.path)) {
mkdirSync(create.path, { recursive: true });
}
try {
writeFileSync(join(create.path, create.fileName + '.json'), JSON.stringify({ ...create.data }), {
encoding: 'utf-8',
});
return { message: 'create - success' };
} finally {
create.data = undefined;
}
};
// eslint-disable-next-line
public insert(data: any, instanceName: string, saveDb = false): Promise<IInsert> { public insert(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line // eslint-disable-next-line
public update(data: any, instanceName: string, saveDb = false): Promise<IInsert> { public update(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line // eslint-disable-next-line
public find(query: any): Promise<any> { public find(query: any): Promise<any> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line // eslint-disable-next-line
delete(query: any, force?: boolean): Promise<any> { delete(query: any, force?: boolean): Promise<any> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
} }

View File

@ -10,214 +10,211 @@ import { GetParticipant, GroupInvite, GroupJid } from '../dto/group.dto';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
type DataValidate<T> = { type DataValidate<T> = {
request: Request; request: Request;
schema: JSONSchema7; schema: JSONSchema7;
ClassRef: any; ClassRef: any;
execute: (instance: InstanceDto, data: T) => Promise<any>; execute: (instance: InstanceDto, data: T) => Promise<any>;
}; };
const logger = new Logger('Validate'); const logger = new Logger('Validate');
export abstract class RouterBroker { export abstract class RouterBroker {
constructor() {} constructor() {}
public routerPath(path: string, param = true) { public routerPath(path: string, param = true) {
// const route = param ? '/:instanceName/' + path : '/' + path; // const route = param ? '/:instanceName/' + path : '/' + path;
let route = '/' + path; let route = '/' + path;
param ? (route += '/:instanceName') : null; param ? (route += '/:instanceName') : null;
return route; return route;
}
public async dataValidate<T>(args: DataValidate<T>) {
const { request, schema, ClassRef, execute } = args;
const ref = new ClassRef();
const body = request.body;
const instance = request.params as unknown as InstanceDto;
if (request?.query && Object.keys(request.query).length > 0) {
Object.assign(instance, request.query);
} }
public async dataValidate<T>(args: DataValidate<T>) { if (request.originalUrl.includes('/instance/create')) {
const { request, schema, ClassRef, execute } = args; Object.assign(instance, body);
const ref = new ClassRef();
const body = request.body;
const instance = request.params as unknown as InstanceDto;
if (request?.query && Object.keys(request.query).length > 0) {
Object.assign(instance, request.query);
}
if (request.originalUrl.includes('/instance/create')) {
Object.assign(instance, body);
}
Object.assign(ref, body);
const v = schema ? validate(ref, schema) : { valid: true, errors: [] };
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
}
return await execute(instance, ref);
} }
public async groupNoValidate<T>(args: DataValidate<T>) { Object.assign(ref, body);
const { request, ClassRef, schema, execute } = args;
const instance = request.params as unknown as InstanceDto; const v = schema ? validate(ref, schema) : { valid: true, errors: [] };
const ref = new ClassRef(); if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
Object.assign(ref, request.body); let message: string;
if (schema['description']) {
const v = validate(ref, schema); message = schema['description'];
} else {
if (!v.valid) { message = stack.replace('instance.', '');
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
} }
return {
return await execute(instance, ref); property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
} }
public async groupValidate<T>(args: DataValidate<T>) { return await execute(instance, ref);
const { request, ClassRef, schema, execute } = args; }
const groupJid = request.query as unknown as GroupJid; public async groupNoValidate<T>(args: DataValidate<T>) {
const { request, ClassRef, schema, execute } = args;
if (!groupJid?.groupJid) { const instance = request.params as unknown as InstanceDto;
throw new BadRequestException(
'The group id needs to be informed in the query', const ref = new ClassRef();
'ex: "groupJid=120362@g.us"',
); Object.assign(ref, request.body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
} }
return {
const instance = request.params as unknown as InstanceDto; property: property.replace('instance.', ''),
const body = request.body; message,
};
const ref = new ClassRef(); });
logger.error([...message]);
Object.assign(body, groupJid); throw new BadRequestException(...message);
Object.assign(ref, body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
}
return await execute(instance, ref);
} }
public async inviteCodeValidate<T>(args: DataValidate<T>) { return await execute(instance, ref);
const { request, ClassRef, schema, execute } = args; }
const inviteCode = request.query as unknown as GroupInvite; public async groupValidate<T>(args: DataValidate<T>) {
const { request, ClassRef, schema, execute } = args;
if (!inviteCode?.inviteCode) { const groupJid = request.query as unknown as GroupJid;
throw new BadRequestException(
'The group invite code id needs to be informed in the query',
'ex: "inviteCode=F1EX5QZxO181L3TMVP31gY" (Obtained from group join link)',
);
}
const instance = request.params as unknown as InstanceDto; if (!groupJid?.groupJid) {
const body = request.body; throw new BadRequestException('The group id needs to be informed in the query', 'ex: "groupJid=120362@g.us"');
const ref = new ClassRef();
Object.assign(body, inviteCode);
Object.assign(ref, body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
}
return await execute(instance, ref);
} }
public async getParticipantsValidate<T>(args: DataValidate<T>) { const instance = request.params as unknown as InstanceDto;
const { request, ClassRef, schema, execute } = args; const body = request.body;
const getParticipants = request.query as unknown as GetParticipant; const ref = new ClassRef();
if (!getParticipants?.getParticipants) { Object.assign(body, groupJid);
throw new BadRequestException('The getParticipants needs to be informed in the query'); Object.assign(ref, body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
} }
return {
const instance = request.params as unknown as InstanceDto; property: property.replace('instance.', ''),
const body = request.body; message,
};
const ref = new ClassRef(); });
logger.error([...message]);
Object.assign(body, getParticipants); throw new BadRequestException(...message);
Object.assign(ref, body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
}
return await execute(instance, ref);
} }
return await execute(instance, ref);
}
public async inviteCodeValidate<T>(args: DataValidate<T>) {
const { request, ClassRef, schema, execute } = args;
const inviteCode = request.query as unknown as GroupInvite;
if (!inviteCode?.inviteCode) {
throw new BadRequestException(
'The group invite code id needs to be informed in the query',
'ex: "inviteCode=F1EX5QZxO181L3TMVP31gY" (Obtained from group join link)',
);
}
const instance = request.params as unknown as InstanceDto;
const body = request.body;
const ref = new ClassRef();
Object.assign(body, inviteCode);
Object.assign(ref, body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
}
return await execute(instance, ref);
}
public async getParticipantsValidate<T>(args: DataValidate<T>) {
const { request, ClassRef, schema, execute } = args;
const getParticipants = request.query as unknown as GetParticipant;
if (!getParticipants?.getParticipants) {
throw new BadRequestException('The getParticipants needs to be informed in the query');
}
const instance = request.params as unknown as InstanceDto;
const body = request.body;
const ref = new ClassRef();
Object.assign(body, getParticipants);
Object.assign(ref, body);
const v = validate(ref, schema);
if (!v.valid) {
const message: any[] = v.errors.map(({ property, stack, schema }) => {
let message: string;
if (schema['description']) {
message = schema['description'];
} else {
message = stack.replace('instance.', '');
}
return {
property: property.replace('instance.', ''),
message,
};
});
logger.error([...message]);
throw new BadRequestException(...message);
}
return await execute(instance, ref);
}
} }

View File

@ -1,15 +1,15 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
ArchiveChatDto, ArchiveChatDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
NumberDto, NumberDto,
PrivacySettingDto, PrivacySettingDto,
ProfileNameDto, ProfileNameDto,
ProfilePictureDto, ProfilePictureDto,
ProfileStatusDto, ProfileStatusDto,
ReadMessageDto, ReadMessageDto,
WhatsAppNumberDto, WhatsAppNumberDto,
} from '../dto/chat.dto'; } from '../dto/chat.dto';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { ContactQuery } from '../repository/contact.repository'; import { ContactQuery } from '../repository/contact.repository';
@ -20,95 +20,95 @@ import { WAMonitoringService } from '../services/monitor.service';
const logger = new Logger('ChatController'); const logger = new Logger('ChatController');
export class ChatController { export class ChatController {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}
public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) { public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) {
logger.verbose('requested whatsappNumber from ' + instanceName + ' instance'); logger.verbose('requested whatsappNumber from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].whatsappNumber(data); return await this.waMonitor.waInstances[instanceName].whatsappNumber(data);
} }
public async readMessage({ instanceName }: InstanceDto, data: ReadMessageDto) { public async readMessage({ instanceName }: InstanceDto, data: ReadMessageDto) {
logger.verbose('requested readMessage from ' + instanceName + ' instance'); logger.verbose('requested readMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].markMessageAsRead(data); return await this.waMonitor.waInstances[instanceName].markMessageAsRead(data);
} }
public async archiveChat({ instanceName }: InstanceDto, data: ArchiveChatDto) { public async archiveChat({ instanceName }: InstanceDto, data: ArchiveChatDto) {
logger.verbose('requested archiveChat from ' + instanceName + ' instance'); logger.verbose('requested archiveChat from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].archiveChat(data); return await this.waMonitor.waInstances[instanceName].archiveChat(data);
} }
public async deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) { public async deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) {
logger.verbose('requested deleteMessage from ' + instanceName + ' instance'); logger.verbose('requested deleteMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].deleteMessage(data); return await this.waMonitor.waInstances[instanceName].deleteMessage(data);
} }
public async fetchProfilePicture({ instanceName }: InstanceDto, data: NumberDto) { public async fetchProfilePicture({ instanceName }: InstanceDto, data: NumberDto) {
logger.verbose('requested fetchProfilePicture from ' + instanceName + ' instance'); logger.verbose('requested fetchProfilePicture from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].profilePicture(data.number); return await this.waMonitor.waInstances[instanceName].profilePicture(data.number);
} }
public async fetchProfile({ instanceName }: InstanceDto, data: NumberDto) { public async fetchProfile({ instanceName }: InstanceDto, data: NumberDto) {
logger.verbose('requested fetchProfile from ' + instanceName + ' instance'); logger.verbose('requested fetchProfile from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchProfile(instanceName, data.number); return await this.waMonitor.waInstances[instanceName].fetchProfile(instanceName, data.number);
} }
public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) { public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) {
logger.verbose('requested fetchContacts from ' + instanceName + ' instance'); logger.verbose('requested fetchContacts from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchContacts(query); return await this.waMonitor.waInstances[instanceName].fetchContacts(query);
} }
public async getBase64FromMediaMessage({ instanceName }: InstanceDto, data: getBase64FromMediaMessageDto) { public async getBase64FromMediaMessage({ instanceName }: InstanceDto, data: getBase64FromMediaMessageDto) {
logger.verbose('requested getBase64FromMediaMessage from ' + instanceName + ' instance'); logger.verbose('requested getBase64FromMediaMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data); return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data);
} }
public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) { public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) {
logger.verbose('requested fetchMessages from ' + instanceName + ' instance'); logger.verbose('requested fetchMessages from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchMessages(query); return await this.waMonitor.waInstances[instanceName].fetchMessages(query);
} }
public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) { public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) {
logger.verbose('requested fetchStatusMessage from ' + instanceName + ' instance'); logger.verbose('requested fetchStatusMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query); return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query);
} }
public async fetchChats({ instanceName }: InstanceDto) { public async fetchChats({ instanceName }: InstanceDto) {
logger.verbose('requested fetchChats from ' + instanceName + ' instance'); logger.verbose('requested fetchChats from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchChats(); return await this.waMonitor.waInstances[instanceName].fetchChats();
} }
public async fetchPrivacySettings({ instanceName }: InstanceDto) { public async fetchPrivacySettings({ instanceName }: InstanceDto) {
logger.verbose('requested fetchPrivacySettings from ' + instanceName + ' instance'); logger.verbose('requested fetchPrivacySettings from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings(); return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings();
} }
public async updatePrivacySettings({ instanceName }: InstanceDto, data: PrivacySettingDto) { public async updatePrivacySettings({ instanceName }: InstanceDto, data: PrivacySettingDto) {
logger.verbose('requested updatePrivacySettings from ' + instanceName + ' instance'); logger.verbose('requested updatePrivacySettings from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].updatePrivacySettings(data); return await this.waMonitor.waInstances[instanceName].updatePrivacySettings(data);
} }
public async fetchBusinessProfile({ instanceName }: InstanceDto, data: ProfilePictureDto) { public async fetchBusinessProfile({ instanceName }: InstanceDto, data: ProfilePictureDto) {
logger.verbose('requested fetchBusinessProfile from ' + instanceName + ' instance'); logger.verbose('requested fetchBusinessProfile from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile(data.number); return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile(data.number);
} }
public async updateProfileName({ instanceName }: InstanceDto, data: ProfileNameDto) { public async updateProfileName({ instanceName }: InstanceDto, data: ProfileNameDto) {
logger.verbose('requested updateProfileName from ' + instanceName + ' instance'); logger.verbose('requested updateProfileName from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].updateProfileName(data.name); return await this.waMonitor.waInstances[instanceName].updateProfileName(data.name);
} }
public async updateProfileStatus({ instanceName }: InstanceDto, data: ProfileStatusDto) { public async updateProfileStatus({ instanceName }: InstanceDto, data: ProfileStatusDto) {
logger.verbose('requested updateProfileStatus from ' + instanceName + ' instance'); logger.verbose('requested updateProfileStatus from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].updateProfileStatus(data.status); return await this.waMonitor.waInstances[instanceName].updateProfileStatus(data.status);
} }
public async updateProfilePicture({ instanceName }: InstanceDto, data: ProfilePictureDto) { public async updateProfilePicture({ instanceName }: InstanceDto, data: ProfilePictureDto) {
logger.verbose('requested updateProfilePicture from ' + instanceName + ' instance'); logger.verbose('requested updateProfilePicture from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].updateProfilePicture(data.picture); return await this.waMonitor.waInstances[instanceName].updateProfilePicture(data.picture);
} }
public async removeProfilePicture({ instanceName }: InstanceDto) { public async removeProfilePicture({ instanceName }: InstanceDto) {
logger.verbose('requested removeProfilePicture from ' + instanceName + ' instance'); logger.verbose('requested removeProfilePicture from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].removeProfilePicture(); return await this.waMonitor.waInstances[instanceName].removeProfilePicture();
} }
} }

View File

@ -11,89 +11,89 @@ import { waMonitor } from '../whatsapp.module';
const logger = new Logger('ChatwootController'); const logger = new Logger('ChatwootController');
export class ChatwootController { export class ChatwootController {
constructor(private readonly chatwootService: ChatwootService, private readonly configService: ConfigService) {} constructor(private readonly chatwootService: ChatwootService, private readonly configService: ConfigService) {}
public async createChatwoot(instance: InstanceDto, data: ChatwootDto) { public async createChatwoot(instance: InstanceDto, data: ChatwootDto) {
logger.verbose('requested createChatwoot from ' + instance.instanceName + ' instance'); logger.verbose('requested createChatwoot from ' + instance.instanceName + ' instance');
if (data.enabled) { if (data.enabled) {
if (!isURL(data.url, { require_tld: false })) { if (!isURL(data.url, { require_tld: false })) {
throw new BadRequestException('url is not valid'); throw new BadRequestException('url is not valid');
} }
if (!data.account_id) { if (!data.account_id) {
throw new BadRequestException('account_id is required'); throw new BadRequestException('account_id is required');
} }
if (!data.token) { if (!data.token) {
throw new BadRequestException('token is required'); throw new BadRequestException('token is required');
} }
if (data.sign_msg !== true && data.sign_msg !== false) { if (data.sign_msg !== true && data.sign_msg !== false) {
throw new BadRequestException('sign_msg is required'); throw new BadRequestException('sign_msg is required');
} }
}
if (!data.enabled) {
logger.verbose('chatwoot disabled');
data.account_id = '';
data.token = '';
data.url = '';
data.sign_msg = false;
data.reopen_conversation = false;
data.conversation_pending = false;
}
data.name_inbox = instance.instanceName;
const result = this.chatwootService.create(instance, data);
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const response = {
...result,
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
};
return response;
} }
public async findChatwoot(instance: InstanceDto) { if (!data.enabled) {
logger.verbose('requested findChatwoot from ' + instance.instanceName + ' instance'); logger.verbose('chatwoot disabled');
const result = await this.chatwootService.find(instance); data.account_id = '';
data.token = '';
const urlServer = this.configService.get<HttpServer>('SERVER').URL; data.url = '';
data.sign_msg = false;
if (Object.keys(result).length === 0) { data.reopen_conversation = false;
return { data.conversation_pending = false;
enabled: false,
url: '',
account_id: '',
token: '',
sign_msg: false,
name_inbox: '',
webhook_url: '',
};
}
const response = {
...result,
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
};
return response;
} }
public async receiveWebhook(instance: InstanceDto, data: any) { data.name_inbox = instance.instanceName;
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
const chatwootService = new ChatwootService(waMonitor, this.configService);
return chatwootService.receiveWebhook(instance, data); const result = this.chatwootService.create(instance, data);
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const response = {
...result,
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
};
return response;
}
public async findChatwoot(instance: InstanceDto) {
logger.verbose('requested findChatwoot from ' + instance.instanceName + ' instance');
const result = await this.chatwootService.find(instance);
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
if (Object.keys(result).length === 0) {
return {
enabled: false,
url: '',
account_id: '',
token: '',
sign_msg: false,
name_inbox: '',
webhook_url: '',
};
} }
public async newInstance(data: any) { const response = {
const chatwootService = new ChatwootService(waMonitor, this.configService); ...result,
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
};
return chatwootService.newInstance(data); return response;
} }
public async receiveWebhook(instance: InstanceDto, data: any) {
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
const chatwootService = new ChatwootService(waMonitor, this.configService);
return chatwootService.receiveWebhook(instance, data);
}
public async newInstance(data: any) {
const chatwootService = new ChatwootService(waMonitor, this.configService);
return chatwootService.newInstance(data);
}
} }

View File

@ -1,16 +1,16 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
CreateGroupDto, CreateGroupDto,
GetParticipant, GetParticipant,
GroupDescriptionDto, GroupDescriptionDto,
GroupInvite, GroupInvite,
GroupJid, GroupJid,
GroupPictureDto, GroupPictureDto,
GroupSendInvite, GroupSendInvite,
GroupSubjectDto, GroupSubjectDto,
GroupToggleEphemeralDto, GroupToggleEphemeralDto,
GroupUpdateParticipantDto, GroupUpdateParticipantDto,
GroupUpdateSettingDto, GroupUpdateSettingDto,
} from '../dto/group.dto'; } from '../dto/group.dto';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
@ -18,80 +18,80 @@ import { WAMonitoringService } from '../services/monitor.service';
const logger = new Logger('ChatController'); const logger = new Logger('ChatController');
export class GroupController { export class GroupController {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}
public async createGroup(instance: InstanceDto, create: CreateGroupDto) { public async createGroup(instance: InstanceDto, create: CreateGroupDto) {
logger.verbose('requested createGroup from ' + instance.instanceName + ' instance'); logger.verbose('requested createGroup from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].createGroup(create); return await this.waMonitor.waInstances[instance.instanceName].createGroup(create);
} }
public async updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) { public async updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) {
logger.verbose('requested updateGroupPicture from ' + instance.instanceName + ' instance'); logger.verbose('requested updateGroupPicture from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture(update); return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture(update);
} }
public async updateGroupSubject(instance: InstanceDto, update: GroupSubjectDto) { public async updateGroupSubject(instance: InstanceDto, update: GroupSubjectDto) {
logger.verbose('requested updateGroupSubject from ' + instance.instanceName + ' instance'); logger.verbose('requested updateGroupSubject from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject(update); return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject(update);
} }
public async updateGroupDescription(instance: InstanceDto, update: GroupDescriptionDto) { public async updateGroupDescription(instance: InstanceDto, update: GroupDescriptionDto) {
logger.verbose('requested updateGroupDescription from ' + instance.instanceName + ' instance'); logger.verbose('requested updateGroupDescription from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription(update); return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription(update);
} }
public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) { public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) {
logger.verbose('requested findGroupInfo from ' + instance.instanceName + ' instance'); logger.verbose('requested findGroupInfo from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].findGroup(groupJid); return await this.waMonitor.waInstances[instance.instanceName].findGroup(groupJid);
} }
public async fetchAllGroups(instance: InstanceDto, getPaticipants: GetParticipant) { public async fetchAllGroups(instance: InstanceDto, getPaticipants: GetParticipant) {
logger.verbose('requested fetchAllGroups from ' + instance.instanceName + ' instance'); logger.verbose('requested fetchAllGroups from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups(getPaticipants); return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups(getPaticipants);
} }
public async inviteCode(instance: InstanceDto, groupJid: GroupJid) { public async inviteCode(instance: InstanceDto, groupJid: GroupJid) {
logger.verbose('requested inviteCode from ' + instance.instanceName + ' instance'); logger.verbose('requested inviteCode from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].inviteCode(groupJid); return await this.waMonitor.waInstances[instance.instanceName].inviteCode(groupJid);
} }
public async inviteInfo(instance: InstanceDto, inviteCode: GroupInvite) { public async inviteInfo(instance: InstanceDto, inviteCode: GroupInvite) {
logger.verbose('requested inviteInfo from ' + instance.instanceName + ' instance'); logger.verbose('requested inviteInfo from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].inviteInfo(inviteCode); return await this.waMonitor.waInstances[instance.instanceName].inviteInfo(inviteCode);
} }
public async sendInvite(instance: InstanceDto, data: GroupSendInvite) { public async sendInvite(instance: InstanceDto, data: GroupSendInvite) {
logger.verbose('requested sendInvite from ' + instance.instanceName + ' instance'); logger.verbose('requested sendInvite from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data); return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data);
} }
public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) { public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) {
logger.verbose('requested revokeInviteCode from ' + instance.instanceName + ' instance'); logger.verbose('requested revokeInviteCode from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(groupJid); return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(groupJid);
} }
public async findParticipants(instance: InstanceDto, groupJid: GroupJid) { public async findParticipants(instance: InstanceDto, groupJid: GroupJid) {
logger.verbose('requested findParticipants from ' + instance.instanceName + ' instance'); logger.verbose('requested findParticipants from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].findParticipants(groupJid); return await this.waMonitor.waInstances[instance.instanceName].findParticipants(groupJid);
} }
public async updateGParticipate(instance: InstanceDto, update: GroupUpdateParticipantDto) { public async updateGParticipate(instance: InstanceDto, update: GroupUpdateParticipantDto) {
logger.verbose('requested updateGParticipate from ' + instance.instanceName + ' instance'); logger.verbose('requested updateGParticipate from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant(update); return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant(update);
} }
public async updateGSetting(instance: InstanceDto, update: GroupUpdateSettingDto) { public async updateGSetting(instance: InstanceDto, update: GroupUpdateSettingDto) {
logger.verbose('requested updateGSetting from ' + instance.instanceName + ' instance'); logger.verbose('requested updateGSetting from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update); return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update);
} }
public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) { public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) {
logger.verbose('requested toggleEphemeral from ' + instance.instanceName + ' instance'); logger.verbose('requested toggleEphemeral from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(update); return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(update);
} }
public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) { public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) {
logger.verbose('requested leaveGroup from ' + instance.instanceName + ' instance'); logger.verbose('requested leaveGroup from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid); return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid);
} }
} }

View File

@ -17,340 +17,340 @@ import { WAStartupService } from '../services/whatsapp.service';
import { wa } from '../types/wa.types'; import { wa } from '../types/wa.types';
export class InstanceController { export class InstanceController {
constructor( constructor(
private readonly waMonitor: WAMonitoringService, private readonly waMonitor: WAMonitoringService,
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly repository: RepositoryBroker, private readonly repository: RepositoryBroker,
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly authService: AuthService, private readonly authService: AuthService,
private readonly webhookService: WebhookService, private readonly webhookService: WebhookService,
private readonly chatwootService: ChatwootService, private readonly chatwootService: ChatwootService,
private readonly settingsService: SettingsService, private readonly settingsService: SettingsService,
private readonly cache: RedisCache, private readonly cache: RedisCache,
) {} ) {}
private readonly logger = new Logger(InstanceController.name); private readonly logger = new Logger(InstanceController.name);
public async createInstance({ public async createInstance({
instanceName, instanceName,
webhook,
webhook_by_events,
events,
qrcode,
number,
token,
chatwoot_account_id,
chatwoot_token,
chatwoot_url,
chatwoot_sign_msg,
chatwoot_reopen_conversation,
chatwoot_conversation_pending,
reject_call,
msg_call,
groups_ignore,
always_online,
read_messages,
read_status,
}: InstanceDto) {
try {
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
if (instanceName !== instanceName.toLowerCase().replace(/[^a-z0-9]/g, '')) {
throw new BadRequestException('The instance name must be lowercase and without special characters');
}
this.logger.verbose('checking duplicate token');
await this.authService.checkDuplicateToken(token);
this.logger.verbose('creating instance');
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
instance.instanceName = instanceName
.toLowerCase()
.replace(/[^a-z0-9]/g, '')
.replace(' ', '');
this.logger.verbose('instance: ' + instance.instanceName + ' created');
this.waMonitor.waInstances[instance.instanceName] = instance;
this.waMonitor.delInstanceTime(instance.instanceName);
this.logger.verbose('generating hash');
const hash = await this.authService.generateHash(
{
instanceName: instance.instanceName,
},
token,
);
this.logger.verbose('hash: ' + hash + ' generated');
let getEvents: string[];
if (webhook) {
if (!isURL(webhook, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property in webhook');
}
this.logger.verbose('creating webhook');
try {
this.webhookService.create(instance, {
enabled: true,
url: webhook,
events,
webhook_by_events,
});
getEvents = (await this.webhookService.find(instance)).events;
} catch (error) {
this.logger.log(error);
}
}
this.logger.verbose('creating settings');
const settings: wa.LocalSettings = {
reject_call: reject_call || false,
msg_call: msg_call || '',
groups_ignore: groups_ignore || false,
always_online: always_online || false,
read_messages: read_messages || false,
read_status: read_status || false,
};
this.logger.verbose('settings: ' + JSON.stringify(settings));
this.settingsService.create(instance, settings);
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
let getQrcode: wa.QrCode;
if (qrcode) {
this.logger.verbose('creating qrcode');
await instance.connectToWhatsapp(number);
await delay(5000);
getQrcode = instance.qrCode;
}
const result = {
instance: {
instanceName: instance.instanceName,
status: 'created',
},
hash,
webhook,
webhook_by_events,
events: getEvents,
settings,
qrcode: getQrcode,
};
this.logger.verbose('instance created');
this.logger.verbose(result);
return result;
}
if (!chatwoot_account_id) {
throw new BadRequestException('account_id is required');
}
if (!chatwoot_token) {
throw new BadRequestException('token is required');
}
if (!chatwoot_url) {
throw new BadRequestException('url is required');
}
if (!isURL(chatwoot_url, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property in chatwoot');
}
if (chatwoot_sign_msg !== true && chatwoot_sign_msg !== false) {
throw new BadRequestException('sign_msg is required');
}
if (chatwoot_reopen_conversation !== true && chatwoot_reopen_conversation !== false) {
throw new BadRequestException('reopen_conversation is required');
}
if (chatwoot_conversation_pending !== true && chatwoot_conversation_pending !== false) {
throw new BadRequestException('conversation_pending is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
try {
this.chatwootService.create(instance, {
enabled: true,
account_id: chatwoot_account_id,
token: chatwoot_token,
url: chatwoot_url,
sign_msg: chatwoot_sign_msg || false,
name_inbox: instance.instanceName,
number,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
});
this.chatwootService.initInstanceChatwoot(
instance,
instance.instanceName,
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
qrcode,
number,
);
} catch (error) {
this.logger.log(error);
}
return {
instance: {
instanceName: instance.instanceName,
status: 'created',
},
hash,
webhook, webhook,
webhook_by_events, webhook_by_events,
events, events: getEvents,
qrcode, settings,
number, chatwoot: {
token, enabled: true,
chatwoot_account_id, account_id: chatwoot_account_id,
chatwoot_token, token: chatwoot_token,
chatwoot_url, url: chatwoot_url,
chatwoot_sign_msg, sign_msg: chatwoot_sign_msg || false,
chatwoot_reopen_conversation, reopen_conversation: chatwoot_reopen_conversation || false,
chatwoot_conversation_pending, conversation_pending: chatwoot_conversation_pending || false,
reject_call, number,
msg_call, name_inbox: instance.instanceName,
groups_ignore, webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
always_online, },
read_messages, };
read_status, } catch (error) {
}: InstanceDto) { console.log(error);
try { return { error: true, message: error.toString() };
this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); }
}
if (instanceName !== instanceName.toLowerCase().replace(/[^a-z0-9]/g, '')) { public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) {
throw new BadRequestException('The instance name must be lowercase and without special characters'); try {
} this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance');
this.logger.verbose('checking duplicate token'); const instance = this.waMonitor.waInstances[instanceName];
await this.authService.checkDuplicateToken(token); const state = instance?.connectionStatus?.state;
this.logger.verbose('creating instance'); this.logger.verbose('state: ' + state);
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
instance.instanceName = instanceName
.toLowerCase()
.replace(/[^a-z0-9]/g, '')
.replace(' ', '');
this.logger.verbose('instance: ' + instance.instanceName + ' created'); if (state == 'open') {
return await this.connectionState({ instanceName });
}
this.waMonitor.waInstances[instance.instanceName] = instance; if (state == 'connecting') {
this.waMonitor.delInstanceTime(instance.instanceName); return instance.qrCode;
}
this.logger.verbose('generating hash'); if (state == 'close') {
const hash = await this.authService.generateHash( this.logger.verbose('connecting');
{ await instance.connectToWhatsapp(number);
instanceName: instance.instanceName,
},
token,
);
this.logger.verbose('hash: ' + hash + ' generated'); await delay(5000);
return instance.qrCode;
}
let getEvents: string[]; return {
instance: {
instanceName: instanceName,
status: state,
},
qrcode: instance?.qrCode,
};
} catch (error) {
this.logger.error(error);
}
}
if (webhook) { public async restartInstance({ instanceName }: InstanceDto) {
if (!isURL(webhook, { require_tld: false })) { try {
throw new BadRequestException('Invalid "url" property in webhook'); this.logger.verbose('requested restartInstance from ' + instanceName + ' instance');
}
this.logger.verbose('creating webhook'); this.logger.verbose('logging out instance: ' + instanceName);
try { this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
this.webhookService.create(instance, {
enabled: true,
url: webhook,
events,
webhook_by_events,
});
getEvents = (await this.webhookService.find(instance)).events; return { error: false, message: 'Instance restarted' };
} catch (error) { } catch (error) {
this.logger.log(error); this.logger.error(error);
} }
} }
this.logger.verbose('creating settings'); public async connectionState({ instanceName }: InstanceDto) {
const settings: wa.LocalSettings = { this.logger.verbose('requested connectionState from ' + instanceName + ' instance');
reject_call: reject_call || false, return {
msg_call: msg_call || '', instance: {
groups_ignore: groups_ignore || false, instanceName: instanceName,
always_online: always_online || false, state: this.waMonitor.waInstances[instanceName]?.connectionStatus?.state,
read_messages: read_messages || false, },
read_status: read_status || false, };
}; }
this.logger.verbose('settings: ' + JSON.stringify(settings)); public async fetchInstances({ instanceName }: InstanceDto) {
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
this.settingsService.create(instance, settings); if (instanceName) {
this.logger.verbose('instanceName: ' + instanceName);
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) { return this.waMonitor.instanceInfo(instanceName);
let getQrcode: wa.QrCode;
if (qrcode) {
this.logger.verbose('creating qrcode');
await instance.connectToWhatsapp(number);
await delay(5000);
getQrcode = instance.qrCode;
}
const result = {
instance: {
instanceName: instance.instanceName,
status: 'created',
},
hash,
webhook,
webhook_by_events,
events: getEvents,
settings,
qrcode: getQrcode,
};
this.logger.verbose('instance created');
this.logger.verbose(result);
return result;
}
if (!chatwoot_account_id) {
throw new BadRequestException('account_id is required');
}
if (!chatwoot_token) {
throw new BadRequestException('token is required');
}
if (!chatwoot_url) {
throw new BadRequestException('url is required');
}
if (!isURL(chatwoot_url, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property in chatwoot');
}
if (chatwoot_sign_msg !== true && chatwoot_sign_msg !== false) {
throw new BadRequestException('sign_msg is required');
}
if (chatwoot_reopen_conversation !== true && chatwoot_reopen_conversation !== false) {
throw new BadRequestException('reopen_conversation is required');
}
if (chatwoot_conversation_pending !== true && chatwoot_conversation_pending !== false) {
throw new BadRequestException('conversation_pending is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
try {
this.chatwootService.create(instance, {
enabled: true,
account_id: chatwoot_account_id,
token: chatwoot_token,
url: chatwoot_url,
sign_msg: chatwoot_sign_msg || false,
name_inbox: instance.instanceName,
number,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
});
this.chatwootService.initInstanceChatwoot(
instance,
instance.instanceName,
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
qrcode,
number,
);
} catch (error) {
this.logger.log(error);
}
return {
instance: {
instanceName: instance.instanceName,
status: 'created',
},
hash,
webhook,
webhook_by_events,
events: getEvents,
settings,
chatwoot: {
enabled: true,
account_id: chatwoot_account_id,
token: chatwoot_token,
url: chatwoot_url,
sign_msg: chatwoot_sign_msg || false,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
number,
name_inbox: instance.instanceName,
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
},
};
} catch (error) {
console.log(error);
return { error: true, message: error.toString() };
}
} }
public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) { return this.waMonitor.instanceInfo();
try { }
this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance');
const instance = this.waMonitor.waInstances[instanceName]; public async logout({ instanceName }: InstanceDto) {
const state = instance?.connectionStatus?.state; this.logger.verbose('requested logout from ' + instanceName + ' instance');
const { instance } = await this.connectionState({ instanceName });
this.logger.verbose('state: ' + state); if (instance.state === 'close') {
throw new BadRequestException('The "' + instanceName + '" instance is not connected');
if (state == 'open') {
return await this.connectionState({ instanceName });
}
if (state == 'connecting') {
return instance.qrCode;
}
if (state == 'close') {
this.logger.verbose('connecting');
await instance.connectToWhatsapp(number);
await delay(5000);
return instance.qrCode;
}
return {
instance: {
instanceName: instanceName,
status: state,
},
qrcode: instance?.qrCode,
};
} catch (error) {
this.logger.error(error);
}
} }
public async restartInstance({ instanceName }: InstanceDto) { try {
try { this.logger.verbose('logging out instance: ' + instanceName);
this.logger.verbose('requested restartInstance from ' + instanceName + ' instance'); await this.waMonitor.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName);
this.logger.verbose('logging out instance: ' + instanceName); this.logger.verbose('close connection instance: ' + instanceName);
this.waMonitor.waInstances[instanceName]?.client?.ws?.close(); this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
return { error: false, message: 'Instance restarted' }; return { error: false, message: 'Instance logged out' };
} catch (error) { } catch (error) {
this.logger.error(error); throw new InternalServerErrorException(error.toString());
}
} }
}
public async connectionState({ instanceName }: InstanceDto) { public async deleteInstance({ instanceName }: InstanceDto) {
this.logger.verbose('requested connectionState from ' + instanceName + ' instance'); this.logger.verbose('requested deleteInstance from ' + instanceName + ' instance');
return { const { instance } = await this.connectionState({ instanceName });
instance: {
instanceName: instanceName, if (instance.state === 'open') {
state: this.waMonitor.waInstances[instanceName]?.connectionStatus?.state, throw new BadRequestException('The "' + instanceName + '" instance needs to be disconnected');
},
};
} }
try {
if (instance.state === 'connecting') {
this.logger.verbose('logging out instance: ' + instanceName);
public async fetchInstances({ instanceName }: InstanceDto) { await this.logout({ instanceName });
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance'); delete this.waMonitor.waInstances[instanceName];
if (instanceName) { return { error: false, message: 'Instance deleted' };
this.logger.verbose('instanceName: ' + instanceName); } else {
return this.waMonitor.instanceInfo(instanceName); this.logger.verbose('deleting instance: ' + instanceName);
}
return this.waMonitor.instanceInfo(); delete this.waMonitor.waInstances[instanceName];
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
return { error: false, message: 'Instance deleted' };
}
} catch (error) {
throw new BadRequestException(error.toString());
} }
}
public async logout({ instanceName }: InstanceDto) { public async refreshToken(_: InstanceDto, oldToken: OldToken) {
this.logger.verbose('requested logout from ' + instanceName + ' instance'); this.logger.verbose('requested refreshToken');
const { instance } = await this.connectionState({ instanceName }); return await this.authService.refreshToken(oldToken);
}
if (instance.state === 'close') {
throw new BadRequestException('The "' + instanceName + '" instance is not connected');
}
try {
this.logger.verbose('logging out instance: ' + instanceName);
await this.waMonitor.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName);
this.logger.verbose('close connection instance: ' + instanceName);
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
return { error: false, message: 'Instance logged out' };
} catch (error) {
throw new InternalServerErrorException(error.toString());
}
}
public async deleteInstance({ instanceName }: InstanceDto) {
this.logger.verbose('requested deleteInstance from ' + instanceName + ' instance');
const { instance } = await this.connectionState({ instanceName });
if (instance.state === 'open') {
throw new BadRequestException('The "' + instanceName + '" instance needs to be disconnected');
}
try {
if (instance.state === 'connecting') {
this.logger.verbose('logging out instance: ' + instanceName);
await this.logout({ instanceName });
delete this.waMonitor.waInstances[instanceName];
return { error: false, message: 'Instance deleted' };
} else {
this.logger.verbose('deleting instance: ' + instanceName);
delete this.waMonitor.waInstances[instanceName];
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
return { error: false, message: 'Instance deleted' };
}
} catch (error) {
throw new BadRequestException(error.toString());
}
}
public async refreshToken(_: InstanceDto, oldToken: OldToken) {
this.logger.verbose('requested refreshToken');
return await this.authService.refreshToken(oldToken);
}
} }

View File

@ -4,112 +4,108 @@ import { Logger } from '../../config/logger.config';
import { BadRequestException } from '../../exceptions'; import { BadRequestException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { import {
SendAudioDto, SendAudioDto,
SendButtonDto, SendButtonDto,
SendContactDto, SendContactDto,
SendListDto, SendListDto,
SendLocationDto, SendLocationDto,
SendMediaDto, SendMediaDto,
SendPollDto, SendPollDto,
SendReactionDto, SendReactionDto,
SendStatusDto, SendStatusDto,
SendStickerDto, SendStickerDto,
SendTextDto, SendTextDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
const logger = new Logger('MessageRouter'); const logger = new Logger('MessageRouter');
export class SendMessageController { export class SendMessageController {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) { public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
logger.verbose('requested sendText from ' + instanceName + ' instance'); logger.verbose('requested sendText from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].textMessage(data); return await this.waMonitor.waInstances[instanceName].textMessage(data);
}
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
logger.verbose('requested sendMedia from ' + instanceName + ' instance');
if (
isBase64(data?.mediaMessage?.media) &&
!data?.mediaMessage?.fileName &&
data?.mediaMessage?.mediatype === 'document'
) {
throw new BadRequestException('For base64 the file name must be informed.');
} }
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) { logger.verbose('isURL: ' + isURL(data?.mediaMessage?.media) + ', isBase64: ' + isBase64(data?.mediaMessage?.media));
logger.verbose('requested sendMedia from ' + instanceName + ' instance'); if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) {
return await this.waMonitor.waInstances[instanceName].mediaMessage(data);
if (
isBase64(data?.mediaMessage?.media) &&
!data?.mediaMessage?.fileName &&
data?.mediaMessage?.mediatype === 'document'
) {
throw new BadRequestException('For base64 the file name must be informed.');
}
logger.verbose(
'isURL: ' + isURL(data?.mediaMessage?.media) + ', isBase64: ' + isBase64(data?.mediaMessage?.media),
);
if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) {
return await this.waMonitor.waInstances[instanceName].mediaMessage(data);
}
throw new BadRequestException('Owned media must be a url or base64');
} }
throw new BadRequestException('Owned media must be a url or base64');
}
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto) { public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto) {
logger.verbose('requested sendSticker from ' + instanceName + ' instance'); logger.verbose('requested sendSticker from ' + instanceName + ' instance');
logger.verbose( logger.verbose(
'isURL: ' + isURL(data?.stickerMessage?.image) + ', isBase64: ' + isBase64(data?.stickerMessage?.image), 'isURL: ' + isURL(data?.stickerMessage?.image) + ', isBase64: ' + isBase64(data?.stickerMessage?.image),
); );
if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) { if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) {
return await this.waMonitor.waInstances[instanceName].mediaSticker(data); return await this.waMonitor.waInstances[instanceName].mediaSticker(data);
}
throw new BadRequestException('Owned media must be a url or base64');
} }
throw new BadRequestException('Owned media must be a url or base64');
}
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) { public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) {
logger.verbose('requested sendWhatsAppAudio from ' + instanceName + ' instance'); logger.verbose('requested sendWhatsAppAudio from ' + instanceName + ' instance');
logger.verbose( logger.verbose('isURL: ' + isURL(data?.audioMessage?.audio) + ', isBase64: ' + isBase64(data?.audioMessage?.audio));
'isURL: ' + isURL(data?.audioMessage?.audio) + ', isBase64: ' + isBase64(data?.audioMessage?.audio), if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) {
); return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data);
if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) {
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data);
}
throw new BadRequestException('Owned media must be a url or base64');
} }
throw new BadRequestException('Owned media must be a url or base64');
}
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) { public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) {
logger.verbose('requested sendButtons from ' + instanceName + ' instance'); logger.verbose('requested sendButtons from ' + instanceName + ' instance');
if (isBase64(data.buttonMessage.mediaMessage?.media) && !data.buttonMessage.mediaMessage?.fileName) { if (isBase64(data.buttonMessage.mediaMessage?.media) && !data.buttonMessage.mediaMessage?.fileName) {
throw new BadRequestException('For bse64 the file name must be informed.'); throw new BadRequestException('For bse64 the file name must be informed.');
}
return await this.waMonitor.waInstances[instanceName].buttonMessage(data);
} }
return await this.waMonitor.waInstances[instanceName].buttonMessage(data);
}
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) { public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
logger.verbose('requested sendLocation from ' + instanceName + ' instance'); logger.verbose('requested sendLocation from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].locationMessage(data); return await this.waMonitor.waInstances[instanceName].locationMessage(data);
} }
public async sendList({ instanceName }: InstanceDto, data: SendListDto) { public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
logger.verbose('requested sendList from ' + instanceName + ' instance'); logger.verbose('requested sendList from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].listMessage(data); return await this.waMonitor.waInstances[instanceName].listMessage(data);
} }
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) { public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
logger.verbose('requested sendContact from ' + instanceName + ' instance'); logger.verbose('requested sendContact from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].contactMessage(data); return await this.waMonitor.waInstances[instanceName].contactMessage(data);
} }
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) { public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
logger.verbose('requested sendReaction from ' + instanceName + ' instance'); logger.verbose('requested sendReaction from ' + instanceName + ' instance');
if (!data.reactionMessage.reaction.match(/[^()\w\sà-ú"-+]+/)) { if (!data.reactionMessage.reaction.match(/[^()\w\sà-ú"-+]+/)) {
throw new BadRequestException('"reaction" must be an emoji'); throw new BadRequestException('"reaction" must be an emoji');
}
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
} }
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
}
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) { public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
logger.verbose('requested sendPoll from ' + instanceName + ' instance'); logger.verbose('requested sendPoll from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].pollMessage(data); return await this.waMonitor.waInstances[instanceName].pollMessage(data);
} }
public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto) { public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto) {
logger.verbose('requested sendStatus from ' + instanceName + ' instance'); logger.verbose('requested sendStatus from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].statusMessage(data); return await this.waMonitor.waInstances[instanceName].statusMessage(data);
} }
} }

View File

@ -9,16 +9,16 @@ import { SettingsService } from '../services/settings.service';
const logger = new Logger('SettingsController'); const logger = new Logger('SettingsController');
export class SettingsController { export class SettingsController {
constructor(private readonly settingsService: SettingsService) {} constructor(private readonly settingsService: SettingsService) {}
public async createSettings(instance: InstanceDto, data: SettingsDto) { public async createSettings(instance: InstanceDto, data: SettingsDto) {
logger.verbose('requested createSettings from ' + instance.instanceName + ' instance'); logger.verbose('requested createSettings from ' + instance.instanceName + ' instance');
return this.settingsService.create(instance, data); return this.settingsService.create(instance, data);
} }
public async findSettings(instance: InstanceDto) { public async findSettings(instance: InstanceDto) {
logger.verbose('requested findSettings from ' + instance.instanceName + ' instance'); logger.verbose('requested findSettings from ' + instance.instanceName + ' instance');
return this.settingsService.find(instance); return this.settingsService.find(instance);
} }
} }

View File

@ -7,20 +7,20 @@ import { HttpStatus } from '../routers/index.router';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
export class ViewsController { export class ViewsController {
constructor(private readonly waMonit: WAMonitoringService, private readonly configService: ConfigService) {} constructor(private readonly waMonit: WAMonitoringService, private readonly configService: ConfigService) {}
public async qrcode(request: Request, response: Response) { public async qrcode(request: Request, response: Response) {
try { try {
const param = request.params as unknown as InstanceDto; const param = request.params as unknown as InstanceDto;
const instance = this.waMonit.waInstances[param.instanceName]; const instance = this.waMonit.waInstances[param.instanceName];
if (instance.connectionStatus.state === 'open') { if (instance.connectionStatus.state === 'open') {
throw new BadRequestException('The instance is already connected'); throw new BadRequestException('The instance is already connected');
} }
const type = this.configService.get<Auth>('AUTHENTICATION').TYPE; const type = this.configService.get<Auth>('AUTHENTICATION').TYPE;
return response.status(HttpStatus.OK).render('qrcode', { type, ...param }); return response.status(HttpStatus.OK).render('qrcode', { type, ...param });
} catch (error) { } catch (error) {
console.log('ERROR: ', error); console.log('ERROR: ', error);
}
} }
}
} }

View File

@ -9,26 +9,26 @@ import { WebhookService } from '../services/webhook.service';
const logger = new Logger('WebhookController'); const logger = new Logger('WebhookController');
export class WebhookController { export class WebhookController {
constructor(private readonly webhookService: WebhookService) {} constructor(private readonly webhookService: WebhookService) {}
public async createWebhook(instance: InstanceDto, data: WebhookDto) { public async createWebhook(instance: InstanceDto, data: WebhookDto) {
logger.verbose('requested createWebhook from ' + instance.instanceName + ' instance'); logger.verbose('requested createWebhook from ' + instance.instanceName + ' instance');
if (data.enabled && !isURL(data.url, { require_tld: false })) { if (data.enabled && !isURL(data.url, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property'); throw new BadRequestException('Invalid "url" property');
}
if (!data.enabled) {
logger.verbose('webhook disabled');
data.url = '';
data.events = [];
}
return this.webhookService.create(instance, data);
} }
public async findWebhook(instance: InstanceDto) { if (!data.enabled) {
logger.verbose('requested findWebhook from ' + instance.instanceName + ' instance'); logger.verbose('webhook disabled');
return this.webhookService.find(instance); data.url = '';
data.events = [];
} }
return this.webhookService.create(instance, data);
}
public async findWebhook(instance: InstanceDto) {
logger.verbose('requested findWebhook from ' + instance.instanceName + ' instance');
return this.webhookService.find(instance);
}
} }

View File

@ -1,84 +1,84 @@
import { proto, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '@whiskeysockets/baileys'; import { proto, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '@whiskeysockets/baileys';
export class OnWhatsAppDto { export class OnWhatsAppDto {
constructor(public readonly jid: string, public readonly exists: boolean, public readonly name?: string) {} constructor(public readonly jid: string, public readonly exists: boolean, public readonly name?: string) {}
} }
export class getBase64FromMediaMessageDto { export class getBase64FromMediaMessageDto {
message: proto.WebMessageInfo; message: proto.WebMessageInfo;
convertToMp4?: boolean; convertToMp4?: boolean;
} }
export class WhatsAppNumberDto { export class WhatsAppNumberDto {
numbers: string[]; numbers: string[];
} }
export class NumberDto { export class NumberDto {
number: string; number: string;
} }
export class NumberBusiness { export class NumberBusiness {
wid?: string; wid?: string;
jid?: string; jid?: string;
exists?: boolean; exists?: boolean;
isBusiness: boolean; isBusiness: boolean;
name?: string; name?: string;
message?: string; message?: string;
description?: string; description?: string;
email?: string; email?: string;
website?: string[]; website?: string[];
address?: string; address?: string;
} }
export class ProfileNameDto { export class ProfileNameDto {
name: string; name: string;
} }
export class ProfileStatusDto { export class ProfileStatusDto {
status: string; status: string;
} }
export class ProfilePictureDto { export class ProfilePictureDto {
number?: string; number?: string;
// url or base64 // url or base64
picture?: string; picture?: string;
} }
class Key { class Key {
id: string; id: string;
fromMe: boolean; fromMe: boolean;
remoteJid: string; remoteJid: string;
} }
export class ReadMessageDto { export class ReadMessageDto {
read_messages: Key[]; read_messages: Key[];
} }
class LastMessage { class LastMessage {
key: Key; key: Key;
messageTimestamp?: number; messageTimestamp?: number;
} }
export class ArchiveChatDto { export class ArchiveChatDto {
lastMessage: LastMessage; lastMessage: LastMessage;
archive: boolean; archive: boolean;
} }
class PrivacySetting { class PrivacySetting {
readreceipts: WAReadReceiptsValue; readreceipts: WAReadReceiptsValue;
profile: WAPrivacyValue; profile: WAPrivacyValue;
status: WAPrivacyValue; status: WAPrivacyValue;
online: WAPrivacyOnlineValue; online: WAPrivacyOnlineValue;
last: WAPrivacyValue; last: WAPrivacyValue;
groupadd: WAPrivacyValue; groupadd: WAPrivacyValue;
} }
export class PrivacySettingDto { export class PrivacySettingDto {
privacySettings: PrivacySetting; privacySettings: PrivacySetting;
} }
export class DeleteMessage { export class DeleteMessage {
id: string; id: string;
fromMe: boolean; fromMe: boolean;
remoteJid: string; remoteJid: string;
participant?: string; participant?: string;
} }

View File

@ -1,11 +1,11 @@
export class ChatwootDto { export class ChatwootDto {
enabled?: boolean; enabled?: boolean;
account_id?: string; account_id?: string;
token?: string; token?: string;
url?: string; url?: string;
name_inbox?: string; name_inbox?: string;
sign_msg?: boolean; sign_msg?: boolean;
number?: string; number?: string;
reopen_conversation?: boolean; reopen_conversation?: boolean;
conversation_pending?: boolean; conversation_pending?: boolean;
} }

View File

@ -1,52 +1,52 @@
export class CreateGroupDto { export class CreateGroupDto {
subject: string; subject: string;
participants: string[]; participants: string[];
description?: string; description?: string;
promoteParticipants?: boolean; promoteParticipants?: boolean;
} }
export class GroupPictureDto { export class GroupPictureDto {
groupJid: string; groupJid: string;
image: string; image: string;
} }
export class GroupSubjectDto { export class GroupSubjectDto {
groupJid: string; groupJid: string;
subject: string; subject: string;
} }
export class GroupDescriptionDto { export class GroupDescriptionDto {
groupJid: string; groupJid: string;
description: string; description: string;
} }
export class GroupJid { export class GroupJid {
groupJid: string; groupJid: string;
} }
export class GetParticipant { export class GetParticipant {
getParticipants: string; getParticipants: string;
} }
export class GroupInvite { export class GroupInvite {
inviteCode: string; inviteCode: string;
} }
export class GroupSendInvite { export class GroupSendInvite {
groupJid: string; groupJid: string;
description: string; description: string;
numbers: string[]; numbers: string[];
} }
export class GroupUpdateParticipantDto extends GroupJid { export class GroupUpdateParticipantDto extends GroupJid {
action: 'add' | 'remove' | 'promote' | 'demote'; action: 'add' | 'remove' | 'promote' | 'demote';
participants: string[]; participants: string[];
} }
export class GroupUpdateSettingDto extends GroupJid { export class GroupUpdateSettingDto extends GroupJid {
action: 'announcement' | 'not_announcement' | 'unlocked' | 'locked'; action: 'announcement' | 'not_announcement' | 'unlocked' | 'locked';
} }
export class GroupToggleEphemeralDto extends GroupJid { export class GroupToggleEphemeralDto extends GroupJid {
expiration: 0 | 86400 | 604800 | 7776000; expiration: 0 | 86400 | 604800 | 7776000;
} }

View File

@ -1,21 +1,21 @@
export class InstanceDto { export class InstanceDto {
instanceName: string; instanceName: string;
qrcode?: boolean; qrcode?: boolean;
number?: string; number?: string;
token?: string; token?: string;
webhook?: string; webhook?: string;
webhook_by_events?: boolean; webhook_by_events?: boolean;
events?: string[]; events?: string[];
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;
groups_ignore?: boolean; groups_ignore?: boolean;
always_online?: boolean; always_online?: boolean;
read_messages?: boolean; read_messages?: boolean;
read_status?: boolean; read_status?: boolean;
chatwoot_account_id?: string; chatwoot_account_id?: string;
chatwoot_token?: string; chatwoot_token?: string;
chatwoot_url?: string; chatwoot_url?: string;
chatwoot_sign_msg?: boolean; chatwoot_sign_msg?: boolean;
chatwoot_reopen_conversation?: boolean; chatwoot_reopen_conversation?: boolean;
chatwoot_conversation_pending?: boolean; chatwoot_conversation_pending?: boolean;
} }

View File

@ -1,150 +1,150 @@
import { proto, WAPresence } from '@whiskeysockets/baileys'; import { proto, WAPresence } from '@whiskeysockets/baileys';
export class Quoted { export class Quoted {
key: proto.IMessageKey; key: proto.IMessageKey;
message: proto.IMessage; message: proto.IMessage;
} }
export class Mentions { export class Mentions {
everyOne: boolean; everyOne: boolean;
mentioned: string[]; mentioned: string[];
} }
export class Options { export class Options {
delay?: number; delay?: number;
presence?: WAPresence; presence?: WAPresence;
quoted?: Quoted; quoted?: Quoted;
mentions?: Mentions; mentions?: Mentions;
linkPreview?: boolean; linkPreview?: boolean;
encoding?: boolean; encoding?: boolean;
} }
class OptionsMessage { class OptionsMessage {
options: Options; options: Options;
} }
export class Metadata extends OptionsMessage { export class Metadata extends OptionsMessage {
number: string; number: string;
} }
class TextMessage { class TextMessage {
text: string; text: string;
} }
export class StatusMessage { export class StatusMessage {
type: string; type: string;
content: string; content: string;
statusJidList?: string[]; statusJidList?: string[];
allContacts?: boolean; allContacts?: boolean;
caption?: string; caption?: string;
backgroundColor?: string; backgroundColor?: string;
font?: number; font?: number;
} }
class PollMessage { class PollMessage {
name: string; name: string;
selectableCount: number; selectableCount: number;
values: string[]; values: string[];
messageSecret?: Uint8Array; messageSecret?: Uint8Array;
} }
export class SendTextDto extends Metadata { export class SendTextDto extends Metadata {
textMessage: TextMessage; textMessage: TextMessage;
} }
export class SendStatusDto extends Metadata { export class SendStatusDto extends Metadata {
statusMessage: StatusMessage; statusMessage: StatusMessage;
} }
export class SendPollDto extends Metadata { export class SendPollDto extends Metadata {
pollMessage: PollMessage; pollMessage: PollMessage;
} }
export type MediaType = 'image' | 'document' | 'video' | 'audio'; export type MediaType = 'image' | 'document' | 'video' | 'audio';
export class MediaMessage { export class MediaMessage {
mediatype: MediaType; mediatype: MediaType;
caption?: string; caption?: string;
// for document // for document
fileName?: string; fileName?: string;
// url or base64 // url or base64
media: string; media: string;
} }
export class SendMediaDto extends Metadata { export class SendMediaDto extends Metadata {
mediaMessage: MediaMessage; mediaMessage: MediaMessage;
} }
class Sticker { class Sticker {
image: string; image: string;
} }
export class SendStickerDto extends Metadata { export class SendStickerDto extends Metadata {
stickerMessage: Sticker; stickerMessage: Sticker;
} }
class Audio { class Audio {
audio: string; audio: string;
} }
export class SendAudioDto extends Metadata { export class SendAudioDto extends Metadata {
audioMessage: Audio; audioMessage: Audio;
} }
class Button { class Button {
buttonText: string; buttonText: string;
buttonId: string; buttonId: string;
} }
class ButtonMessage { class ButtonMessage {
title: string; title: string;
description: string; description: string;
footerText?: string; footerText?: string;
buttons: Button[]; buttons: Button[];
mediaMessage?: MediaMessage; mediaMessage?: MediaMessage;
} }
export class SendButtonDto extends Metadata { export class SendButtonDto extends Metadata {
buttonMessage: ButtonMessage; buttonMessage: ButtonMessage;
} }
class LocationMessage { class LocationMessage {
latitude: number; latitude: number;
longitude: number; longitude: number;
name?: string; name?: string;
address?: string; address?: string;
} }
export class SendLocationDto extends Metadata { export class SendLocationDto extends Metadata {
locationMessage: LocationMessage; locationMessage: LocationMessage;
} }
class Row { class Row {
title: string; title: string;
description: string; description: string;
rowId: string; rowId: string;
} }
class Section { class Section {
title: string; title: string;
rows: Row[]; rows: Row[];
} }
class ListMessage { class ListMessage {
title: string; title: string;
description: string; description: string;
footerText?: string; footerText?: string;
buttonText: string; buttonText: string;
sections: Section[]; sections: Section[];
} }
export class SendListDto extends Metadata { export class SendListDto extends Metadata {
listMessage: ListMessage; listMessage: ListMessage;
} }
export class ContactMessage { export class ContactMessage {
fullName: string; fullName: string;
wuid: string; wuid: string;
phoneNumber: string; phoneNumber: string;
organization?: string; organization?: string;
email?: string; email?: string;
url?: string; url?: string;
} }
export class SendContactDto extends Metadata { export class SendContactDto extends Metadata {
contactMessage: ContactMessage[]; contactMessage: ContactMessage[];
} }
class ReactionMessage { class ReactionMessage {
key: proto.IMessageKey; key: proto.IMessageKey;
reaction: string; reaction: string;
} }
export class SendReactionDto { export class SendReactionDto {
reactionMessage: ReactionMessage; reactionMessage: ReactionMessage;
} }

View File

@ -1,8 +1,8 @@
export class SettingsDto { export class SettingsDto {
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;
groups_ignore?: boolean; groups_ignore?: boolean;
always_online?: boolean; always_online?: boolean;
read_messages?: boolean; read_messages?: boolean;
read_status?: boolean; read_status?: boolean;
} }

View File

@ -1,6 +1,6 @@
export class WebhookDto { export class WebhookDto {
enabled?: boolean; enabled?: boolean;
url?: string; url?: string;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
} }

View File

@ -13,77 +13,71 @@ import { repository } from '../whatsapp.module';
const logger = new Logger('GUARD'); const logger = new Logger('GUARD');
async function jwtGuard(req: Request, res: Response, next: NextFunction) { async function jwtGuard(req: Request, res: Response, next: NextFunction) {
const key = req.get('apikey'); const key = req.get('apikey');
if (key && configService.get<Auth>('AUTHENTICATION').API_KEY.KEY !== key) { if (key && configService.get<Auth>('AUTHENTICATION').API_KEY.KEY !== key) {
throw new UnauthorizedException(); throw new UnauthorizedException();
}
if (configService.get<Auth>('AUTHENTICATION').API_KEY.KEY === key) {
return next();
}
if ((req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && !key) {
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
}
const jwtOpts = configService.get<Auth>('AUTHENTICATION').JWT;
try {
const [bearer, token] = req.get('authorization').split(' ');
if (bearer.toLowerCase() !== 'bearer') {
throw new UnauthorizedException();
} }
if (configService.get<Auth>('AUTHENTICATION').API_KEY.KEY === key) { if (!isJWT(token)) {
return next(); throw new UnauthorizedException();
} }
if ( const param = req.params as unknown as InstanceDto;
(req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && const decode = jwt.verify(token, jwtOpts.SECRET, {
!key ignoreExpiration: jwtOpts.EXPIRIN_IN === 0,
) { }) as JwtPayload;
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
if (param.instanceName !== decode.instanceName || name !== decode.apiName) {
throw new UnauthorizedException();
} }
const jwtOpts = configService.get<Auth>('AUTHENTICATION').JWT; return next();
try { } catch (error) {
const [bearer, token] = req.get('authorization').split(' '); logger.error(error);
throw new UnauthorizedException();
if (bearer.toLowerCase() !== 'bearer') { }
throw new UnauthorizedException();
}
if (!isJWT(token)) {
throw new UnauthorizedException();
}
const param = req.params as unknown as InstanceDto;
const decode = jwt.verify(token, jwtOpts.SECRET, {
ignoreExpiration: jwtOpts.EXPIRIN_IN === 0,
}) as JwtPayload;
if (param.instanceName !== decode.instanceName || name !== decode.apiName) {
throw new UnauthorizedException();
}
return next();
} catch (error) {
logger.error(error);
throw new UnauthorizedException();
}
} }
async function apikey(req: Request, res: Response, next: NextFunction) { async function apikey(req: Request, res: Response, next: NextFunction) {
const env = configService.get<Auth>('AUTHENTICATION').API_KEY; const env = configService.get<Auth>('AUTHENTICATION').API_KEY;
const key = req.get('apikey'); const key = req.get('apikey');
if (env.KEY === key) { if (env.KEY === key) {
return next(); return next();
}
if ((req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && !key) {
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
}
try {
const param = req.params as unknown as InstanceDto;
const instanceKey = await repository.auth.find(param.instanceName);
if (instanceKey.apikey === key) {
return next();
} }
} catch (error) {
logger.error(error);
}
if ( throw new UnauthorizedException();
(req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) &&
!key
) {
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
}
try {
const param = req.params as unknown as InstanceDto;
const instanceKey = await repository.auth.find(param.instanceName);
if (instanceKey.apikey === key) {
return next();
}
} catch (error) {
logger.error(error);
}
throw new UnauthorizedException();
} }
export const authGuard = { jwt: jwtGuard, apikey }; export const authGuard = { jwt: jwtGuard, apikey };

View File

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

View File

@ -3,15 +3,15 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
export class AuthRaw { export class AuthRaw {
_id?: string; _id?: string;
jwt?: string; jwt?: string;
apikey?: string; apikey?: string;
} }
const authSchema = new Schema<AuthRaw>({ const authSchema = new Schema<AuthRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
jwt: { type: String, minlength: 1 }, jwt: { type: String, minlength: 1 },
apikey: { type: String, minlength: 1 }, apikey: { type: String, minlength: 1 },
}); });
export const AuthModel = dbserver?.model(AuthRaw.name, authSchema, 'authentication'); export const AuthModel = dbserver?.model(AuthRaw.name, authSchema, 'authentication');

View File

@ -3,16 +3,16 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
export class ChatRaw { export class ChatRaw {
_id?: string; _id?: string;
id?: string; id?: string;
owner: string; owner: string;
lastMsgTimestamp?: number; lastMsgTimestamp?: number;
} }
const chatSchema = new Schema<ChatRaw>({ const chatSchema = new Schema<ChatRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
id: { type: String, required: true, minlength: 1 }, id: { type: String, required: true, minlength: 1 },
owner: { type: String, required: true, minlength: 1 }, owner: { type: String, required: true, minlength: 1 },
}); });
export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats'); export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats');

View File

@ -3,27 +3,27 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
export class ChatwootRaw { export class ChatwootRaw {
_id?: string; _id?: string;
enabled?: boolean; enabled?: boolean;
account_id?: string; account_id?: string;
token?: string; token?: string;
url?: string; url?: string;
name_inbox?: string; name_inbox?: string;
sign_msg?: boolean; sign_msg?: boolean;
number?: string; number?: string;
reopen_conversation?: boolean; reopen_conversation?: boolean;
conversation_pending?: boolean; conversation_pending?: boolean;
} }
const chatwootSchema = new Schema<ChatwootRaw>({ const chatwootSchema = new Schema<ChatwootRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
enabled: { type: Boolean, required: true }, enabled: { type: Boolean, required: true },
account_id: { type: String, required: true }, account_id: { type: String, required: true },
token: { type: String, required: true }, token: { type: String, required: true },
url: { type: String, required: true }, url: { type: String, required: true },
name_inbox: { type: String, required: true }, name_inbox: { type: String, required: true },
sign_msg: { type: Boolean, required: true }, sign_msg: { type: Boolean, required: true },
number: { type: String, required: true }, number: { type: String, required: true },
}); });
export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot'); export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');

View File

@ -3,19 +3,19 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
export class ContactRaw { export class ContactRaw {
_id?: string; _id?: string;
pushName?: string; pushName?: string;
id?: string; id?: string;
profilePictureUrl?: string; profilePictureUrl?: string;
owner: string; owner: string;
} }
const contactSchema = new Schema<ContactRaw>({ const contactSchema = new Schema<ContactRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
pushName: { type: String, minlength: 1 }, pushName: { type: String, minlength: 1 },
id: { type: String, required: true, minlength: 1 }, id: { type: String, required: true, minlength: 1 },
profilePictureUrl: { type: String, minlength: 1 }, profilePictureUrl: { type: String, minlength: 1 },
owner: { type: String, required: true, minlength: 1 }, owner: { type: String, required: true, minlength: 1 },
}); });
export const ContactModel = dbserver?.model(ContactRaw.name, contactSchema, 'contacts'); export const ContactModel = dbserver?.model(ContactRaw.name, contactSchema, 'contacts');

View File

@ -4,65 +4,65 @@ import { dbserver } from '../../db/db.connect';
import { wa } from '../types/wa.types'; import { wa } from '../types/wa.types';
class Key { class Key {
id?: string; id?: string;
remoteJid?: string; remoteJid?: string;
fromMe?: boolean; fromMe?: boolean;
participant?: string; participant?: string;
} }
export class MessageRaw { export class MessageRaw {
_id?: string; _id?: string;
key?: Key; key?: Key;
pushName?: string; pushName?: string;
participant?: string; participant?: string;
message?: object; message?: object;
messageType?: string; messageType?: string;
messageTimestamp?: number | Long.Long; messageTimestamp?: number | Long.Long;
owner: string; owner: string;
source?: 'android' | 'web' | 'ios'; source?: 'android' | 'web' | 'ios';
} }
const messageSchema = new Schema<MessageRaw>({ const messageSchema = new Schema<MessageRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
key: { key: {
id: { type: String, required: true, minlength: 1 }, id: { type: String, required: true, minlength: 1 },
remoteJid: { type: String, required: true, minlength: 1 }, remoteJid: { type: String, required: true, minlength: 1 },
fromMe: { type: Boolean, required: true }, fromMe: { type: Boolean, required: true },
participant: { type: String, minlength: 1 }, participant: { type: String, minlength: 1 },
}, },
pushName: { type: String }, pushName: { type: String },
participant: { type: String }, participant: { type: String },
messageType: { type: String }, messageType: { type: String },
message: { type: Object }, message: { type: Object },
source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] }, source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] },
messageTimestamp: { type: Number, required: true }, messageTimestamp: { type: Number, required: true },
owner: { type: String, required: true, minlength: 1 }, owner: { type: String, required: true, minlength: 1 },
}); });
export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages'); export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages');
export type IMessageModel = typeof MessageModel; export type IMessageModel = typeof MessageModel;
export class MessageUpdateRaw { export class MessageUpdateRaw {
_id?: string; _id?: string;
remoteJid?: string; remoteJid?: string;
id?: string; id?: string;
fromMe?: boolean; fromMe?: boolean;
participant?: string; participant?: string;
datetime?: number; datetime?: number;
status?: wa.StatusMessage; status?: wa.StatusMessage;
owner: string; owner: string;
pollUpdates?: any; pollUpdates?: any;
} }
const messageUpdateSchema = new Schema<MessageUpdateRaw>({ const messageUpdateSchema = new Schema<MessageUpdateRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
remoteJid: { type: String, required: true, min: 1 }, remoteJid: { type: String, required: true, min: 1 },
id: { type: String, required: true, min: 1 }, id: { type: String, required: true, min: 1 },
fromMe: { type: Boolean, required: true }, fromMe: { type: Boolean, required: true },
participant: { type: String, min: 1 }, participant: { type: String, min: 1 },
datetime: { type: Number, required: true, min: 1 }, datetime: { type: Number, required: true, min: 1 },
status: { type: String, required: true }, status: { type: String, required: true },
owner: { type: String, required: true, min: 1 }, owner: { type: String, required: true, min: 1 },
}); });
export const MessageUpModel = dbserver?.model(MessageUpdateRaw.name, messageUpdateSchema, 'messageUpdate'); export const MessageUpModel = dbserver?.model(MessageUpdateRaw.name, messageUpdateSchema, 'messageUpdate');

View File

@ -3,23 +3,23 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
export class SettingsRaw { export class SettingsRaw {
_id?: string; _id?: string;
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;
groups_ignore?: boolean; groups_ignore?: boolean;
always_online?: boolean; always_online?: boolean;
read_messages?: boolean; read_messages?: boolean;
read_status?: boolean; read_status?: boolean;
} }
const settingsSchema = new Schema<SettingsRaw>({ const settingsSchema = new Schema<SettingsRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
reject_call: { type: Boolean, required: true }, reject_call: { type: Boolean, required: true },
msg_call: { type: String, required: true }, msg_call: { type: String, required: true },
groups_ignore: { type: Boolean, required: true }, groups_ignore: { type: Boolean, required: true },
always_online: { type: Boolean, required: true }, always_online: { type: Boolean, required: true },
read_messages: { type: Boolean, required: true }, read_messages: { type: Boolean, required: true },
read_status: { type: Boolean, required: true }, read_status: { type: Boolean, required: true },
}); });
export const SettingsModel = dbserver?.model(SettingsRaw.name, settingsSchema, 'settings'); export const SettingsModel = dbserver?.model(SettingsRaw.name, settingsSchema, 'settings');

View File

@ -3,19 +3,19 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
export class WebhookRaw { export class WebhookRaw {
_id?: string; _id?: string;
url?: string; url?: string;
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
} }
const webhookSchema = new Schema<WebhookRaw>({ const webhookSchema = new Schema<WebhookRaw>({
_id: { type: String, _id: true }, _id: { type: String, _id: true },
url: { type: String, required: true }, url: { type: String, required: true },
enabled: { type: Boolean, required: true }, enabled: { type: Boolean, required: true },
events: { type: [String], required: true }, events: { type: [String], required: true },
webhook_by_events: { type: Boolean, required: true }, webhook_by_events: { type: Boolean, required: true },
}); });
export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook'); export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook');

View File

@ -8,58 +8,58 @@ import { IInsert, Repository } from '../abstract/abstract.repository';
import { AuthRaw, IAuthModel } from '../models'; import { AuthRaw, IAuthModel } from '../models';
export class AuthRepository extends Repository { export class AuthRepository extends Repository {
constructor(private readonly authModel: IAuthModel, readonly configService: ConfigService) { constructor(private readonly authModel: IAuthModel, readonly configService: ConfigService) {
super(configService); super(configService);
this.auth = configService.get<Auth>('AUTHENTICATION'); this.auth = configService.get<Auth>('AUTHENTICATION');
}
private readonly auth: Auth;
private readonly logger = new Logger('AuthRepository');
public async create(data: AuthRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving auth to db');
const insert = await this.authModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving auth to store');
this.writeStore<AuthRaw>({
path: join(AUTH_DIR, this.auth.TYPE),
fileName: instance,
data,
});
this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, this.auth.TYPE) + '/' + instance);
this.logger.verbose('auth created');
return { insertCount: 1 };
} catch (error) {
return { error } as any;
} }
}
private readonly auth: Auth; public async find(instance: string): Promise<AuthRaw> {
private readonly logger = new Logger('AuthRepository'); try {
this.logger.verbose('finding auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
return await this.authModel.findOne({ _id: instance });
}
public async create(data: AuthRaw, instance: string): Promise<IInsert> { this.logger.verbose('finding auth in store');
try {
this.logger.verbose('creating auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving auth to db');
const insert = await this.authModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth'); return JSON.parse(
return { insertCount: insert.modifiedCount }; readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), {
} encoding: 'utf-8',
}),
this.logger.verbose('saving auth to store'); ) as AuthRaw;
} catch (error) {
this.writeStore<AuthRaw>({ return {};
path: join(AUTH_DIR, this.auth.TYPE),
fileName: instance,
data,
});
this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, this.auth.TYPE) + '/' + instance);
this.logger.verbose('auth created');
return { insertCount: 1 };
} catch (error) {
return { error } as any;
}
}
public async find(instance: string): Promise<AuthRaw> {
try {
this.logger.verbose('finding auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
return await this.authModel.findOne({ _id: instance });
}
this.logger.verbose('finding auth in store');
return JSON.parse(
readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), {
encoding: 'utf-8',
}),
) as AuthRaw;
} catch (error) {
return {};
}
} }
}
} }

View File

@ -7,111 +7,111 @@ import { IInsert, Repository } from '../abstract/abstract.repository';
import { ChatRaw, IChatModel } from '../models'; import { ChatRaw, IChatModel } from '../models';
export class ChatQuery { export class ChatQuery {
where: ChatRaw; where: ChatRaw;
} }
export class ChatRepository extends Repository { export class ChatRepository extends Repository {
constructor(private readonly chatModel: IChatModel, private readonly configService: ConfigService) { constructor(private readonly chatModel: IChatModel, private readonly configService: ConfigService) {
super(configService); super(configService);
}
private readonly logger = new Logger('ChatRepository');
public async insert(data: ChatRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting chats');
if (data.length === 0) {
this.logger.verbose('no chats to insert');
return;
} }
private readonly logger = new Logger('ChatRepository'); try {
this.logger.verbose('saving chats to store');
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving chats to db');
const insert = await this.chatModel.insertMany([...data]);
public async insert(data: ChatRaw[], instanceName: string, saveDb = false): Promise<IInsert> { this.logger.verbose('chats saved to db: ' + insert.length + ' chats');
this.logger.verbose('inserting chats'); return { insertCount: insert.length };
if (data.length === 0) { }
this.logger.verbose('no chats to insert');
return;
}
try { this.logger.verbose('saving chats to store');
this.logger.verbose('saving chats to store');
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving chats to db');
const insert = await this.chatModel.insertMany([...data]);
this.logger.verbose('chats saved to db: ' + insert.length + ' chats'); const store = this.configService.get<StoreConf>('STORE');
return { insertCount: insert.length };
}
this.logger.verbose('saving chats to store'); if (store.CHATS) {
this.logger.verbose('saving chats to store');
data.forEach((chat) => {
this.writeStore<ChatRaw>({
path: join(this.storePath, 'chats', instanceName),
fileName: chat.id,
data: chat,
});
this.logger.verbose(
'chats saved to store in path: ' + join(this.storePath, 'chats', instanceName) + '/' + chat.id,
);
});
const store = this.configService.get<StoreConf>('STORE'); this.logger.verbose('chats saved to store');
return { insertCount: data.length };
}
if (store.CHATS) { this.logger.verbose('chats not saved to store');
this.logger.verbose('saving chats to store'); return { insertCount: 0 };
data.forEach((chat) => { } catch (error) {
this.writeStore<ChatRaw>({ return error;
path: join(this.storePath, 'chats', instanceName), } finally {
fileName: chat.id, data = undefined;
data: chat,
});
this.logger.verbose(
'chats saved to store in path: ' + join(this.storePath, 'chats', instanceName) + '/' + chat.id,
);
});
this.logger.verbose('chats saved to store');
return { insertCount: data.length };
}
this.logger.verbose('chats not saved to store');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
} }
}
public async find(query: ChatQuery): Promise<ChatRaw[]> { public async find(query: ChatQuery): Promise<ChatRaw[]> {
try { try {
this.logger.verbose('finding chats'); this.logger.verbose('finding chats');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('finding chats in db'); this.logger.verbose('finding chats in db');
return await this.chatModel.find({ owner: query.where.owner }); return await this.chatModel.find({ owner: query.where.owner });
} }
this.logger.verbose('finding chats in store'); this.logger.verbose('finding chats in store');
const chats: ChatRaw[] = []; const chats: ChatRaw[] = [];
const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner)); const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner));
for await (const dirent of openDir) { for await (const dirent of openDir) {
if (dirent.isFile()) { if (dirent.isFile()) {
chats.push( chats.push(
JSON.parse( JSON.parse(
readFileSync(join(this.storePath, 'chats', query.where.owner, dirent.name), { readFileSync(join(this.storePath, 'chats', query.where.owner, dirent.name), {
encoding: 'utf-8', encoding: 'utf-8',
}), }),
), ),
); );
}
}
this.logger.verbose('chats found in store: ' + chats.length + ' chats');
return chats;
} catch (error) {
return [];
} }
}
this.logger.verbose('chats found in store: ' + chats.length + ' chats');
return chats;
} catch (error) {
return [];
} }
}
public async delete(query: ChatQuery) { public async delete(query: ChatQuery) {
try { try {
this.logger.verbose('deleting chats'); this.logger.verbose('deleting chats');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('deleting chats in db'); this.logger.verbose('deleting chats in db');
return await this.chatModel.deleteOne({ ...query.where }); return await this.chatModel.deleteOne({ ...query.where });
} }
this.logger.verbose('deleting chats in store'); this.logger.verbose('deleting chats in store');
rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), { rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), {
force: true, force: true,
recursive: true, recursive: true,
}); });
return { deleted: { chatId: query.where.id } }; return { deleted: { chatId: query.where.id } };
} catch (error) { } catch (error) {
return { error: error?.toString() }; return { error: error?.toString() };
}
} }
}
} }

View File

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

View File

@ -7,171 +7,165 @@ import { IInsert, Repository } from '../abstract/abstract.repository';
import { ContactRaw, IContactModel } from '../models'; import { ContactRaw, IContactModel } from '../models';
export class ContactQuery { export class ContactQuery {
where: ContactRaw; where: ContactRaw;
} }
export class ContactRepository extends Repository { export class ContactRepository extends Repository {
constructor(private readonly contactModel: IContactModel, private readonly configService: ConfigService) { constructor(private readonly contactModel: IContactModel, private readonly configService: ConfigService) {
super(configService); super(configService);
}
private readonly logger = new Logger('ContactRepository');
public async insert(data: ContactRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting contacts');
if (data.length === 0) {
this.logger.verbose('no contacts to insert');
return;
} }
private readonly logger = new Logger('ContactRepository'); try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving contacts to db');
public async insert(data: ContactRaw[], instanceName: string, saveDb = false): Promise<IInsert> { const insert = await this.contactModel.insertMany([...data]);
this.logger.verbose('inserting contacts');
if (data.length === 0) { this.logger.verbose('contacts saved to db: ' + insert.length + ' contacts');
this.logger.verbose('no contacts to insert'); return { insertCount: insert.length };
return; }
}
try { this.logger.verbose('saving contacts to store');
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving contacts to db');
const insert = await this.contactModel.insertMany([...data]); const store = this.configService.get<StoreConf>('STORE');
this.logger.verbose('contacts saved to db: ' + insert.length + ' contacts'); if (store.CONTACTS) {
return { insertCount: insert.length }; this.logger.verbose('saving contacts to store');
} data.forEach((contact) => {
this.writeStore({
path: join(this.storePath, 'contacts', instanceName),
fileName: contact.id,
data: contact,
});
this.logger.verbose(
'contacts saved to store in path: ' + join(this.storePath, 'contacts', instanceName) + '/' + contact.id,
);
});
this.logger.verbose('saving contacts to store'); this.logger.verbose('contacts saved to store: ' + data.length + ' contacts');
return { insertCount: data.length };
}
const store = this.configService.get<StoreConf>('STORE'); this.logger.verbose('contacts not saved');
return { insertCount: 0 };
if (store.CONTACTS) { } catch (error) {
this.logger.verbose('saving contacts to store'); return error;
data.forEach((contact) => { } finally {
this.writeStore({ data = undefined;
path: join(this.storePath, 'contacts', instanceName),
fileName: contact.id,
data: contact,
});
this.logger.verbose(
'contacts saved to store in path: ' +
join(this.storePath, 'contacts', instanceName) +
'/' +
contact.id,
);
});
this.logger.verbose('contacts saved to store: ' + data.length + ' contacts');
return { insertCount: data.length };
}
this.logger.verbose('contacts not saved');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
} }
}
public async update(data: ContactRaw[], instanceName: string, saveDb = false): Promise<IInsert> { public async update(data: ContactRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
try { try {
this.logger.verbose('updating contacts'); this.logger.verbose('updating contacts');
if (data.length === 0) { if (data.length === 0) {
this.logger.verbose('no contacts to update'); this.logger.verbose('no contacts to update');
return; return;
} }
if (this.dbSettings.ENABLED && saveDb) { if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('updating contacts in db'); this.logger.verbose('updating contacts in db');
const contacts = data.map((contact) => { const contacts = data.map((contact) => {
return { return {
updateOne: { updateOne: {
filter: { id: contact.id }, filter: { id: contact.id },
update: { ...contact }, update: { ...contact },
upsert: true, upsert: true,
}, },
}; };
}); });
const { nModified } = await this.contactModel.bulkWrite(contacts); const { nModified } = await this.contactModel.bulkWrite(contacts);
this.logger.verbose('contacts updated in db: ' + nModified + ' contacts'); this.logger.verbose('contacts updated in db: ' + nModified + ' contacts');
return { insertCount: nModified }; return { insertCount: nModified };
} }
this.logger.verbose('updating contacts in store'); this.logger.verbose('updating contacts in store');
const store = this.configService.get<StoreConf>('STORE'); const store = this.configService.get<StoreConf>('STORE');
if (store.CONTACTS) { if (store.CONTACTS) {
this.logger.verbose('updating contacts in store'); this.logger.verbose('updating contacts in store');
data.forEach((contact) => { data.forEach((contact) => {
this.writeStore({ this.writeStore({
path: join(this.storePath, 'contacts', instanceName), path: join(this.storePath, 'contacts', instanceName),
fileName: contact.id, fileName: contact.id,
data: contact, data: contact,
}); });
this.logger.verbose( this.logger.verbose(
'contacts updated in store in path: ' + 'contacts updated in store in path: ' + join(this.storePath, 'contacts', instanceName) + '/' + contact.id,
join(this.storePath, 'contacts', instanceName) + );
'/' + });
contact.id,
);
});
this.logger.verbose('contacts updated in store: ' + data.length + ' contacts'); this.logger.verbose('contacts updated in store: ' + data.length + ' contacts');
return { insertCount: data.length }; return { insertCount: data.length };
} }
this.logger.verbose('contacts not updated'); this.logger.verbose('contacts not updated');
return { insertCount: 0 }; return { insertCount: 0 };
} catch (error) { } catch (error) {
return error; return error;
} finally { } finally {
data = undefined; data = undefined;
}
} }
}
public async find(query: ContactQuery): Promise<ContactRaw[]> { public async find(query: ContactQuery): Promise<ContactRaw[]> {
try { try {
this.logger.verbose('finding contacts'); this.logger.verbose('finding contacts');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('finding contacts in db'); this.logger.verbose('finding contacts in db');
return await this.contactModel.find({ ...query.where }); return await this.contactModel.find({ ...query.where });
} }
this.logger.verbose('finding contacts in store'); this.logger.verbose('finding contacts in store');
const contacts: ContactRaw[] = []; const contacts: ContactRaw[] = [];
if (query?.where?.id) { if (query?.where?.id) {
this.logger.verbose('finding contacts in store by id'); this.logger.verbose('finding contacts in store by id');
contacts.push( contacts.push(
JSON.parse( JSON.parse(
readFileSync(join(this.storePath, 'contacts', query.where.owner, query.where.id + '.json'), { readFileSync(join(this.storePath, 'contacts', query.where.owner, query.where.id + '.json'), {
encoding: 'utf-8', encoding: 'utf-8',
}), }),
), ),
); );
} else { } else {
this.logger.verbose('finding contacts in store by owner'); this.logger.verbose('finding contacts in store by owner');
const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), { const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), {
encoding: 'utf-8', encoding: 'utf-8',
}); });
for await (const dirent of openDir) { for await (const dirent of openDir) {
if (dirent.isFile()) { if (dirent.isFile()) {
contacts.push( contacts.push(
JSON.parse( JSON.parse(
readFileSync(join(this.storePath, 'contacts', query.where.owner, dirent.name), { readFileSync(join(this.storePath, 'contacts', query.where.owner, dirent.name), {
encoding: 'utf-8', encoding: 'utf-8',
}), }),
), ),
); );
} }
}
}
this.logger.verbose('contacts found in store: ' + contacts.length + ' contacts');
return contacts;
} catch (error) {
return [];
} }
}
this.logger.verbose('contacts found in store: ' + contacts.length + ' contacts');
return contacts;
} catch (error) {
return [];
} }
}
} }

View File

@ -7,145 +7,141 @@ import { IInsert, Repository } from '../abstract/abstract.repository';
import { IMessageModel, MessageRaw } from '../models'; import { IMessageModel, MessageRaw } from '../models';
export class MessageQuery { export class MessageQuery {
where: MessageRaw; where: MessageRaw;
limit?: number; limit?: number;
} }
export class MessageRepository extends Repository { export class MessageRepository extends Repository {
constructor(private readonly messageModel: IMessageModel, private readonly configService: ConfigService) { constructor(private readonly messageModel: IMessageModel, private readonly configService: ConfigService) {
super(configService); super(configService);
}
private readonly logger = new Logger('MessageRepository');
public async insert(data: MessageRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting messages');
if (!Array.isArray(data) || data.length === 0) {
this.logger.verbose('no messages to insert');
return;
} }
private readonly logger = new Logger('MessageRepository'); try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving messages to db');
const cleanedData = data.map((obj) => {
const cleanedObj = { ...obj };
if ('extendedTextMessage' in obj.message) {
const extendedTextMessage = obj.message.extendedTextMessage as {
contextInfo?: {
mentionedJid?: any;
};
};
public async insert(data: MessageRaw[], instanceName: string, saveDb = false): Promise<IInsert> { if (typeof extendedTextMessage === 'object' && extendedTextMessage !== null) {
this.logger.verbose('inserting messages'); if ('contextInfo' in extendedTextMessage) {
delete extendedTextMessage.contextInfo?.mentionedJid;
if (!Array.isArray(data) || data.length === 0) { extendedTextMessage.contextInfo = {};
this.logger.verbose('no messages to insert'); }
return;
}
try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving messages to db');
const cleanedData = data.map((obj) => {
const cleanedObj = { ...obj };
if ('extendedTextMessage' in obj.message) {
const extendedTextMessage = obj.message.extendedTextMessage as {
contextInfo?: {
mentionedJid?: any;
};
};
if (typeof extendedTextMessage === 'object' && extendedTextMessage !== null) {
if ('contextInfo' in extendedTextMessage) {
delete extendedTextMessage.contextInfo?.mentionedJid;
extendedTextMessage.contextInfo = {};
}
}
}
return cleanedObj;
});
const insert = await this.messageModel.insertMany([...cleanedData]);
this.logger.verbose('messages saved to db: ' + insert.length + ' messages');
return { insertCount: insert.length };
} }
}
return cleanedObj;
});
this.logger.verbose('saving messages to store'); const insert = await this.messageModel.insertMany([...cleanedData]);
const store = this.configService.get<StoreConf>('STORE'); this.logger.verbose('messages saved to db: ' + insert.length + ' messages');
return { insertCount: insert.length };
}
if (store.MESSAGES) { this.logger.verbose('saving messages to store');
this.logger.verbose('saving messages to store');
data.forEach((message) => { const store = this.configService.get<StoreConf>('STORE');
this.writeStore({
path: join(this.storePath, 'messages', instanceName),
fileName: message.key.id,
data: message,
});
this.logger.verbose(
'messages saved to store in path: ' +
join(this.storePath, 'messages', instanceName) +
'/' +
message.key.id,
);
});
this.logger.verbose('messages saved to store: ' + data.length + ' messages'); if (store.MESSAGES) {
return { insertCount: data.length }; this.logger.verbose('saving messages to store');
}
this.logger.verbose('messages not saved to store'); data.forEach((message) => {
return { insertCount: 0 }; this.writeStore({
} catch (error) { path: join(this.storePath, 'messages', instanceName),
console.log('ERROR: ', error); fileName: message.key.id,
return error; data: message,
} finally { });
data = undefined; this.logger.verbose(
} 'messages saved to store in path: ' + join(this.storePath, 'messages', instanceName) + '/' + message.key.id,
);
});
this.logger.verbose('messages saved to store: ' + data.length + ' messages');
return { insertCount: data.length };
}
this.logger.verbose('messages not saved to store');
return { insertCount: 0 };
} catch (error) {
console.log('ERROR: ', error);
return error;
} finally {
data = undefined;
} }
}
public async find(query: MessageQuery) { public async find(query: MessageQuery) {
try { try {
this.logger.verbose('finding messages'); this.logger.verbose('finding messages');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('finding messages in db'); this.logger.verbose('finding messages in db');
if (query?.where?.key) { if (query?.where?.key) {
for (const [k, v] of Object.entries(query.where.key)) { for (const [k, v] of Object.entries(query.where.key)) {
query.where['key.' + k] = v; query.where['key.' + k] = v;
} }
delete query?.where?.key; delete query?.where?.key;
}
return await this.messageModel
.find({ ...query.where })
.sort({ messageTimestamp: -1 })
.limit(query?.limit ?? 0);
}
this.logger.verbose('finding messages in store');
const messages: MessageRaw[] = [];
if (query?.where?.key?.id) {
this.logger.verbose('finding messages in store by id');
messages.push(
JSON.parse(
readFileSync(
join(this.storePath, 'messages', query.where.owner, query.where.key.id + '.json'),
{ encoding: 'utf-8' },
),
),
);
} else {
this.logger.verbose('finding messages in store by owner');
const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
messages.push(
JSON.parse(
readFileSync(join(this.storePath, 'messages', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
}
this.logger.verbose('messages found in store: ' + messages.length + ' messages');
return messages
.sort((x, y) => {
return (y.messageTimestamp as number) - (x.messageTimestamp as number);
})
.splice(0, query?.limit ?? messages.length);
} catch (error) {
return [];
} }
return await this.messageModel
.find({ ...query.where })
.sort({ messageTimestamp: -1 })
.limit(query?.limit ?? 0);
}
this.logger.verbose('finding messages in store');
const messages: MessageRaw[] = [];
if (query?.where?.key?.id) {
this.logger.verbose('finding messages in store by id');
messages.push(
JSON.parse(
readFileSync(join(this.storePath, 'messages', query.where.owner, query.where.key.id + '.json'), {
encoding: 'utf-8',
}),
),
);
} else {
this.logger.verbose('finding messages in store by owner');
const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
messages.push(
JSON.parse(
readFileSync(join(this.storePath, 'messages', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
}
this.logger.verbose('messages found in store: ' + messages.length + ' messages');
return messages
.sort((x, y) => {
return (y.messageTimestamp as number) - (x.messageTimestamp as number);
})
.splice(0, query?.limit ?? messages.length);
} catch (error) {
return [];
} }
}
} }

View File

@ -7,117 +7,114 @@ import { IInsert, Repository } from '../abstract/abstract.repository';
import { IMessageUpModel, MessageUpdateRaw } from '../models'; import { IMessageUpModel, MessageUpdateRaw } from '../models';
export class MessageUpQuery { export class MessageUpQuery {
where: MessageUpdateRaw; where: MessageUpdateRaw;
limit?: number; limit?: number;
} }
export class MessageUpRepository extends Repository { export class MessageUpRepository extends Repository {
constructor(private readonly messageUpModel: IMessageUpModel, private readonly configService: ConfigService) { constructor(private readonly messageUpModel: IMessageUpModel, private readonly configService: ConfigService) {
super(configService); super(configService);
}
private readonly logger = new Logger('MessageUpRepository');
public async insert(data: MessageUpdateRaw[], instanceName: string, saveDb?: boolean): Promise<IInsert> {
this.logger.verbose('inserting message up');
if (data.length === 0) {
this.logger.verbose('no message up to insert');
return;
} }
private readonly logger = new Logger('MessageUpRepository'); try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving message up to db');
const insert = await this.messageUpModel.insertMany([...data]);
public async insert(data: MessageUpdateRaw[], instanceName: string, saveDb?: boolean): Promise<IInsert> { this.logger.verbose('message up saved to db: ' + insert.length + ' message up');
this.logger.verbose('inserting message up'); return { insertCount: insert.length };
}
if (data.length === 0) { this.logger.verbose('saving message up to store');
this.logger.verbose('no message up to insert');
return;
}
try { const store = this.configService.get<StoreConf>('STORE');
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving message up to db');
const insert = await this.messageUpModel.insertMany([...data]);
this.logger.verbose('message up saved to db: ' + insert.length + ' message up'); if (store.MESSAGE_UP) {
return { insertCount: insert.length }; this.logger.verbose('saving message up to store');
} data.forEach((update) => {
this.writeStore<MessageUpdateRaw>({
path: join(this.storePath, 'message-up', instanceName),
fileName: update.id,
data: update,
});
this.logger.verbose(
'message up saved to store in path: ' + join(this.storePath, 'message-up', instanceName) + '/' + update.id,
);
});
this.logger.verbose('saving message up to store'); this.logger.verbose('message up saved to store: ' + data.length + ' message up');
return { insertCount: data.length };
}
const store = this.configService.get<StoreConf>('STORE'); this.logger.verbose('message up not saved to store');
return { insertCount: 0 };
if (store.MESSAGE_UP) { } catch (error) {
this.logger.verbose('saving message up to store'); return error;
data.forEach((update) => {
this.writeStore<MessageUpdateRaw>({
path: join(this.storePath, 'message-up', instanceName),
fileName: update.id,
data: update,
});
this.logger.verbose(
'message up saved to store in path: ' +
join(this.storePath, 'message-up', instanceName) +
'/' +
update.id,
);
});
this.logger.verbose('message up saved to store: ' + data.length + ' message up');
return { insertCount: data.length };
}
this.logger.verbose('message up not saved to store');
return { insertCount: 0 };
} catch (error) {
return error;
}
} }
}
public async find(query: MessageUpQuery) { public async find(query: MessageUpQuery) {
try { try {
this.logger.verbose('finding message up'); this.logger.verbose('finding message up');
if (this.dbSettings.ENABLED) { if (this.dbSettings.ENABLED) {
this.logger.verbose('finding message up in db'); this.logger.verbose('finding message up in db');
return await this.messageUpModel return await this.messageUpModel
.find({ ...query.where }) .find({ ...query.where })
.sort({ datetime: -1 }) .sort({ datetime: -1 })
.limit(query?.limit ?? 0); .limit(query?.limit ?? 0);
} }
this.logger.verbose('finding message up in store'); this.logger.verbose('finding message up in store');
const messageUpdate: MessageUpdateRaw[] = []; const messageUpdate: MessageUpdateRaw[] = [];
if (query?.where?.id) { if (query?.where?.id) {
this.logger.verbose('finding message up in store by id'); this.logger.verbose('finding message up in store by id');
messageUpdate.push( messageUpdate.push(
JSON.parse( JSON.parse(
readFileSync(join(this.storePath, 'message-up', query.where.owner, query.where.id + '.json'), { readFileSync(join(this.storePath, 'message-up', query.where.owner, query.where.id + '.json'), {
encoding: 'utf-8', encoding: 'utf-8',
}), }),
), ),
); );
} else { } else {
this.logger.verbose('finding message up in store by owner'); this.logger.verbose('finding message up in store by owner');
const openDir = opendirSync(join(this.storePath, 'message-up', query.where.owner), { const openDir = opendirSync(join(this.storePath, 'message-up', query.where.owner), {
encoding: 'utf-8', encoding: 'utf-8',
}); });
for await (const dirent of openDir) { for await (const dirent of openDir) {
if (dirent.isFile()) { if (dirent.isFile()) {
messageUpdate.push( messageUpdate.push(
JSON.parse( JSON.parse(
readFileSync(join(this.storePath, 'message-up', query.where.owner, dirent.name), { readFileSync(join(this.storePath, 'message-up', query.where.owner, dirent.name), {
encoding: 'utf-8', encoding: 'utf-8',
}), }),
), ),
); );
} }
}
}
this.logger.verbose('message up found in store: ' + messageUpdate.length + ' message up');
return messageUpdate
.sort((x, y) => {
return y.datetime - x.datetime;
})
.splice(0, query?.limit ?? messageUpdate.length);
} catch (error) {
return [];
} }
}
this.logger.verbose('message up found in store: ' + messageUpdate.length + ' message up');
return messageUpdate
.sort((x, y) => {
return y.datetime - x.datetime;
})
.splice(0, query?.limit ?? messageUpdate.length);
} catch (error) {
return [];
} }
}
} }

View File

@ -13,105 +13,105 @@ import { MessageUpRepository } from './messageUp.repository';
import { SettingsRepository } from './settings.repository'; import { SettingsRepository } from './settings.repository';
import { WebhookRepository } from './webhook.repository'; import { WebhookRepository } from './webhook.repository';
export class RepositoryBroker { export class RepositoryBroker {
constructor( constructor(
public readonly message: MessageRepository, public readonly message: MessageRepository,
public readonly chat: ChatRepository, public readonly chat: ChatRepository,
public readonly contact: ContactRepository, public readonly contact: ContactRepository,
public readonly messageUpdate: MessageUpRepository, public readonly messageUpdate: MessageUpRepository,
public readonly webhook: WebhookRepository, public readonly webhook: WebhookRepository,
public readonly chatwoot: ChatwootRepository, public readonly chatwoot: ChatwootRepository,
public readonly settings: SettingsRepository, public readonly settings: SettingsRepository,
public readonly auth: AuthRepository, public readonly auth: AuthRepository,
private configService: ConfigService, private configService: ConfigService,
dbServer?: MongoClient, dbServer?: MongoClient,
) { ) {
this.dbClient = dbServer; this.dbClient = dbServer;
this.__init_repo_without_db__(); this.__init_repo_without_db__();
} }
private dbClient?: MongoClient; private dbClient?: MongoClient;
private readonly logger = new Logger('RepositoryBroker'); private readonly logger = new Logger('RepositoryBroker');
public get dbServer() { public get dbServer() {
return this.dbClient; return this.dbClient;
} }
private __init_repo_without_db__() { private __init_repo_without_db__() {
this.logger.verbose('initializing repository without db'); this.logger.verbose('initializing repository without db');
if (!this.configService.get<Database>('DATABASE').ENABLED) { if (!this.configService.get<Database>('DATABASE').ENABLED) {
const storePath = join(process.cwd(), 'store'); const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath); this.logger.verbose('creating store path: ' + storePath);
try { try {
const authDir = join(storePath, 'auth', this.configService.get<Auth>('AUTHENTICATION').TYPE); const authDir = join(storePath, 'auth', this.configService.get<Auth>('AUTHENTICATION').TYPE);
const chatsDir = join(storePath, 'chats'); const chatsDir = join(storePath, 'chats');
const contactsDir = join(storePath, 'contacts'); const contactsDir = join(storePath, 'contacts');
const messagesDir = join(storePath, 'messages'); const messagesDir = join(storePath, 'messages');
const messageUpDir = join(storePath, 'message-up'); const messageUpDir = join(storePath, 'message-up');
const webhookDir = join(storePath, 'webhook'); const webhookDir = join(storePath, 'webhook');
const chatwootDir = join(storePath, 'chatwoot'); const chatwootDir = join(storePath, 'chatwoot');
const settingsDir = join(storePath, 'settings'); const settingsDir = join(storePath, 'settings');
const tempDir = join(storePath, 'temp'); const tempDir = join(storePath, 'temp');
if (!fs.existsSync(authDir)) { if (!fs.existsSync(authDir)) {
this.logger.verbose('creating auth dir: ' + authDir); this.logger.verbose('creating auth dir: ' + authDir);
fs.mkdirSync(authDir, { recursive: true }); fs.mkdirSync(authDir, { recursive: true });
}
if (!fs.existsSync(chatsDir)) {
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(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);
}
} }
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(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);
}
} }
}
} }

View File

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

View File

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

View File

@ -2,31 +2,31 @@ import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
archiveChatSchema, archiveChatSchema,
contactValidateSchema, contactValidateSchema,
deleteMessageSchema, deleteMessageSchema,
messageUpSchema, messageUpSchema,
messageValidateSchema, messageValidateSchema,
privacySettingsSchema, privacySettingsSchema,
profileNameSchema, profileNameSchema,
profilePictureSchema, profilePictureSchema,
profileSchema, profileSchema,
profileStatusSchema, profileStatusSchema,
readMessageSchema, readMessageSchema,
whatsappNumberSchema, whatsappNumberSchema,
} from '../../validate/validate.schema'; } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { import {
ArchiveChatDto, ArchiveChatDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
NumberDto, NumberDto,
PrivacySettingDto, PrivacySettingDto,
ProfileNameDto, ProfileNameDto,
ProfilePictureDto, ProfilePictureDto,
ProfileStatusDto, ProfileStatusDto,
ReadMessageDto, ReadMessageDto,
WhatsAppNumberDto, WhatsAppNumberDto,
} from '../dto/chat.dto'; } from '../dto/chat.dto';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { ContactQuery } from '../repository/contact.repository'; import { ContactQuery } from '../repository/contact.repository';
@ -38,317 +38,317 @@ import { HttpStatus } from './index.router';
const logger = new Logger('ChatRouter'); const logger = new Logger('ChatRouter');
export class ChatRouter extends RouterBroker { export class ChatRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => { .post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => {
logger.verbose('request received in whatsappNumbers'); logger.verbose('request received in whatsappNumbers');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<WhatsAppNumberDto>({ const response = await this.dataValidate<WhatsAppNumberDto>({
request: req, request: req,
schema: whatsappNumberSchema, schema: whatsappNumberSchema,
ClassRef: WhatsAppNumberDto, ClassRef: WhatsAppNumberDto,
execute: (instance, data) => chatController.whatsappNumber(instance, data), execute: (instance, data) => chatController.whatsappNumber(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => { .put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => {
logger.verbose('request received in markMessageAsRead'); logger.verbose('request received in markMessageAsRead');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ReadMessageDto>({ const response = await this.dataValidate<ReadMessageDto>({
request: req, request: req,
schema: readMessageSchema, schema: readMessageSchema,
ClassRef: ReadMessageDto, ClassRef: ReadMessageDto,
execute: (instance, data) => chatController.readMessage(instance, data), execute: (instance, data) => chatController.readMessage(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('archiveChat'), ...guards, async (req, res) => { .put(this.routerPath('archiveChat'), ...guards, async (req, res) => {
logger.verbose('request received in archiveChat'); logger.verbose('request received in archiveChat');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ArchiveChatDto>({ const response = await this.dataValidate<ArchiveChatDto>({
request: req, request: req,
schema: archiveChatSchema, schema: archiveChatSchema,
ClassRef: ArchiveChatDto, ClassRef: ArchiveChatDto,
execute: (instance, data) => chatController.archiveChat(instance, data), execute: (instance, data) => chatController.archiveChat(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.delete(this.routerPath('deleteMessageForEveryone'), ...guards, async (req, res) => { .delete(this.routerPath('deleteMessageForEveryone'), ...guards, async (req, res) => {
logger.verbose('request received in deleteMessageForEveryone'); logger.verbose('request received in deleteMessageForEveryone');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<DeleteMessage>({ const response = await this.dataValidate<DeleteMessage>({
request: req, request: req,
schema: deleteMessageSchema, schema: deleteMessageSchema,
ClassRef: DeleteMessage, ClassRef: DeleteMessage,
execute: (instance, data) => chatController.deleteMessage(instance, data), execute: (instance, data) => chatController.deleteMessage(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('fetchProfilePictureUrl'), ...guards, async (req, res) => { .post(this.routerPath('fetchProfilePictureUrl'), ...guards, async (req, res) => {
logger.verbose('request received in fetchProfilePictureUrl'); logger.verbose('request received in fetchProfilePictureUrl');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<NumberDto>({ const response = await this.dataValidate<NumberDto>({
request: req, request: req,
schema: profilePictureSchema, schema: profilePictureSchema,
ClassRef: NumberDto, ClassRef: NumberDto,
execute: (instance, data) => chatController.fetchProfilePicture(instance, data), execute: (instance, data) => chatController.fetchProfilePicture(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('fetchProfile'), ...guards, async (req, res) => { .post(this.routerPath('fetchProfile'), ...guards, async (req, res) => {
logger.verbose('request received in fetchProfile'); logger.verbose('request received in fetchProfile');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<NumberDto>({ const response = await this.dataValidate<NumberDto>({
request: req, request: req,
schema: profileSchema, schema: profileSchema,
ClassRef: NumberDto, ClassRef: NumberDto,
execute: (instance, data) => chatController.fetchProfile(instance, data), execute: (instance, data) => chatController.fetchProfile(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('findContacts'), ...guards, async (req, res) => { .post(this.routerPath('findContacts'), ...guards, async (req, res) => {
logger.verbose('request received in findContacts'); logger.verbose('request received in findContacts');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ContactQuery>({ const response = await this.dataValidate<ContactQuery>({
request: req, request: req,
schema: contactValidateSchema, schema: contactValidateSchema,
ClassRef: ContactQuery, ClassRef: ContactQuery,
execute: (instance, data) => chatController.fetchContacts(instance, data), execute: (instance, data) => chatController.fetchContacts(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => { .post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => {
logger.verbose('request received in getBase64FromMediaMessage'); logger.verbose('request received in getBase64FromMediaMessage');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<getBase64FromMediaMessageDto>({ const response = await this.dataValidate<getBase64FromMediaMessageDto>({
request: req, request: req,
schema: null, schema: null,
ClassRef: getBase64FromMediaMessageDto, ClassRef: getBase64FromMediaMessageDto,
execute: (instance, data) => chatController.getBase64FromMediaMessage(instance, data), execute: (instance, data) => chatController.getBase64FromMediaMessage(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('findMessages'), ...guards, async (req, res) => { .post(this.routerPath('findMessages'), ...guards, async (req, res) => {
logger.verbose('request received in findMessages'); logger.verbose('request received in findMessages');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<MessageQuery>({ const response = await this.dataValidate<MessageQuery>({
request: req, request: req,
schema: messageValidateSchema, schema: messageValidateSchema,
ClassRef: MessageQuery, ClassRef: MessageQuery,
execute: (instance, data) => chatController.fetchMessages(instance, data), execute: (instance, data) => chatController.fetchMessages(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('findStatusMessage'), ...guards, async (req, res) => { .post(this.routerPath('findStatusMessage'), ...guards, async (req, res) => {
logger.verbose('request received in findStatusMessage'); logger.verbose('request received in findStatusMessage');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<MessageUpQuery>({ const response = await this.dataValidate<MessageUpQuery>({
request: req, request: req,
schema: messageUpSchema, schema: messageUpSchema,
ClassRef: MessageUpQuery, ClassRef: MessageUpQuery,
execute: (instance, data) => chatController.fetchStatusMessage(instance, data), execute: (instance, data) => chatController.fetchStatusMessage(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('findChats'), ...guards, async (req, res) => { .get(this.routerPath('findChats'), ...guards, async (req, res) => {
logger.verbose('request received in findChats'); logger.verbose('request received in findChats');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: null, schema: null,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => chatController.fetchChats(instance), execute: (instance) => chatController.fetchChats(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
// Profile routes // Profile routes
.get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => { .get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => {
logger.verbose('request received in fetchPrivacySettings'); logger.verbose('request received in fetchPrivacySettings');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: null, schema: null,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => chatController.fetchPrivacySettings(instance), execute: (instance) => chatController.fetchPrivacySettings(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.put(this.routerPath('updatePrivacySettings'), ...guards, async (req, res) => { .put(this.routerPath('updatePrivacySettings'), ...guards, async (req, res) => {
logger.verbose('request received in updatePrivacySettings'); logger.verbose('request received in updatePrivacySettings');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<PrivacySettingDto>({ const response = await this.dataValidate<PrivacySettingDto>({
request: req, request: req,
schema: privacySettingsSchema, schema: privacySettingsSchema,
ClassRef: PrivacySettingDto, ClassRef: PrivacySettingDto,
execute: (instance, data) => chatController.updatePrivacySettings(instance, data), execute: (instance, data) => chatController.updatePrivacySettings(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('fetchBusinessProfile'), ...guards, async (req, res) => { .post(this.routerPath('fetchBusinessProfile'), ...guards, async (req, res) => {
logger.verbose('request received in fetchBusinessProfile'); logger.verbose('request received in fetchBusinessProfile');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ProfilePictureDto>({ const response = await this.dataValidate<ProfilePictureDto>({
request: req, request: req,
schema: profilePictureSchema, schema: profilePictureSchema,
ClassRef: ProfilePictureDto, ClassRef: ProfilePictureDto,
execute: (instance, data) => chatController.fetchBusinessProfile(instance, data), execute: (instance, data) => chatController.fetchBusinessProfile(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('updateProfileName'), ...guards, async (req, res) => { .post(this.routerPath('updateProfileName'), ...guards, async (req, res) => {
logger.verbose('request received in updateProfileName'); logger.verbose('request received in updateProfileName');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ProfileNameDto>({ const response = await this.dataValidate<ProfileNameDto>({
request: req, request: req,
schema: profileNameSchema, schema: profileNameSchema,
ClassRef: ProfileNameDto, ClassRef: ProfileNameDto,
execute: (instance, data) => chatController.updateProfileName(instance, data), execute: (instance, data) => chatController.updateProfileName(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('updateProfileStatus'), ...guards, async (req, res) => { .post(this.routerPath('updateProfileStatus'), ...guards, async (req, res) => {
logger.verbose('request received in updateProfileStatus'); logger.verbose('request received in updateProfileStatus');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ProfileStatusDto>({ const response = await this.dataValidate<ProfileStatusDto>({
request: req, request: req,
schema: profileStatusSchema, schema: profileStatusSchema,
ClassRef: ProfileStatusDto, ClassRef: ProfileStatusDto,
execute: (instance, data) => chatController.updateProfileStatus(instance, data), execute: (instance, data) => chatController.updateProfileStatus(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.put(this.routerPath('updateProfilePicture'), ...guards, async (req, res) => { .put(this.routerPath('updateProfilePicture'), ...guards, async (req, res) => {
logger.verbose('request received in updateProfilePicture'); logger.verbose('request received in updateProfilePicture');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ProfilePictureDto>({ const response = await this.dataValidate<ProfilePictureDto>({
request: req, request: req,
schema: profilePictureSchema, schema: profilePictureSchema,
ClassRef: ProfilePictureDto, ClassRef: ProfilePictureDto,
execute: (instance, data) => chatController.updateProfilePicture(instance, data), execute: (instance, data) => chatController.updateProfilePicture(instance, data),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}) })
.delete(this.routerPath('removeProfilePicture'), ...guards, async (req, res) => { .delete(this.routerPath('removeProfilePicture'), ...guards, async (req, res) => {
logger.verbose('request received in removeProfilePicture'); logger.verbose('request received in removeProfilePicture');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ProfilePictureDto>({ const response = await this.dataValidate<ProfilePictureDto>({
request: req, request: req,
schema: profilePictureSchema, schema: profilePictureSchema,
ClassRef: ProfilePictureDto, ClassRef: ProfilePictureDto,
execute: (instance) => chatController.removeProfilePicture(instance), execute: (instance) => chatController.removeProfilePicture(instance),
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -12,58 +12,58 @@ import { HttpStatus } from './index.router';
const logger = new Logger('ChatwootRouter'); const logger = new Logger('ChatwootRouter');
export class ChatwootRouter extends RouterBroker { export class ChatwootRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('set'), ...guards, async (req, res) => { .post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setChatwoot'); logger.verbose('request received in setChatwoot');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<ChatwootDto>({ const response = await this.dataValidate<ChatwootDto>({
request: req, request: req,
schema: chatwootSchema, schema: chatwootSchema,
ClassRef: ChatwootDto, ClassRef: ChatwootDto,
execute: (instance, data) => chatwootController.createChatwoot(instance, data), execute: (instance, data) => chatwootController.createChatwoot(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.get(this.routerPath('find'), ...guards, async (req, res) => { .get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findChatwoot'); logger.verbose('request received in findChatwoot');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => chatwootController.findChatwoot(instance), execute: (instance) => chatwootController.findChatwoot(instance),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('webhook'), async (req, res) => { .post(this.routerPath('webhook'), async (req, res) => {
logger.verbose('request received in findChatwoot'); logger.verbose('request received in findChatwoot');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance, data) => chatwootController.receiveWebhook(instance, data), execute: (instance, data) => chatwootController.receiveWebhook(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -2,31 +2,31 @@ import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
createGroupSchema, createGroupSchema,
getParticipantsSchema, getParticipantsSchema,
groupInviteSchema, groupInviteSchema,
groupJidSchema, groupJidSchema,
groupSendInviteSchema, groupSendInviteSchema,
toggleEphemeralSchema, toggleEphemeralSchema,
updateGroupDescriptionSchema, updateGroupDescriptionSchema,
updateGroupPictureSchema, updateGroupPictureSchema,
updateGroupSubjectSchema, updateGroupSubjectSchema,
updateParticipantsSchema, updateParticipantsSchema,
updateSettingsSchema, updateSettingsSchema,
} from '../../validate/validate.schema'; } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { import {
CreateGroupDto, CreateGroupDto,
GetParticipant, GetParticipant,
GroupDescriptionDto, GroupDescriptionDto,
GroupInvite, GroupInvite,
GroupJid, GroupJid,
GroupPictureDto, GroupPictureDto,
GroupSendInvite, GroupSendInvite,
GroupSubjectDto, GroupSubjectDto,
GroupToggleEphemeralDto, GroupToggleEphemeralDto,
GroupUpdateParticipantDto, GroupUpdateParticipantDto,
GroupUpdateSettingDto, GroupUpdateSettingDto,
} from '../dto/group.dto'; } from '../dto/group.dto';
import { groupController } from '../whatsapp.module'; import { groupController } from '../whatsapp.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
@ -34,251 +34,251 @@ import { HttpStatus } from './index.router';
const logger = new Logger('GroupRouter'); const logger = new Logger('GroupRouter');
export class GroupRouter extends RouterBroker { export class GroupRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('create'), ...guards, async (req, res) => { .post(this.routerPath('create'), ...guards, async (req, res) => {
logger.verbose('request received in createGroup'); logger.verbose('request received in createGroup');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<CreateGroupDto>({ const response = await this.dataValidate<CreateGroupDto>({
request: req, request: req,
schema: createGroupSchema, schema: createGroupSchema,
ClassRef: CreateGroupDto, ClassRef: CreateGroupDto,
execute: (instance, data) => groupController.createGroup(instance, data), execute: (instance, data) => groupController.createGroup(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('updateGroupSubject'), ...guards, async (req, res) => { .put(this.routerPath('updateGroupSubject'), ...guards, async (req, res) => {
logger.verbose('request received in updateGroupSubject'); logger.verbose('request received in updateGroupSubject');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupSubjectDto>({ const response = await this.groupValidate<GroupSubjectDto>({
request: req, request: req,
schema: updateGroupSubjectSchema, schema: updateGroupSubjectSchema,
ClassRef: GroupSubjectDto, ClassRef: GroupSubjectDto,
execute: (instance, data) => groupController.updateGroupSubject(instance, data), execute: (instance, data) => groupController.updateGroupSubject(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('updateGroupPicture'), ...guards, async (req, res) => { .put(this.routerPath('updateGroupPicture'), ...guards, async (req, res) => {
logger.verbose('request received in updateGroupPicture'); logger.verbose('request received in updateGroupPicture');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupPictureDto>({ const response = await this.groupValidate<GroupPictureDto>({
request: req, request: req,
schema: updateGroupPictureSchema, schema: updateGroupPictureSchema,
ClassRef: GroupPictureDto, ClassRef: GroupPictureDto,
execute: (instance, data) => groupController.updateGroupPicture(instance, data), execute: (instance, data) => groupController.updateGroupPicture(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('updateGroupDescription'), ...guards, async (req, res) => { .put(this.routerPath('updateGroupDescription'), ...guards, async (req, res) => {
logger.verbose('request received in updateGroupDescription'); logger.verbose('request received in updateGroupDescription');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupDescriptionDto>({ const response = await this.groupValidate<GroupDescriptionDto>({
request: req, request: req,
schema: updateGroupDescriptionSchema, schema: updateGroupDescriptionSchema,
ClassRef: GroupDescriptionDto, ClassRef: GroupDescriptionDto,
execute: (instance, data) => groupController.updateGroupDescription(instance, data), execute: (instance, data) => groupController.updateGroupDescription(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.get(this.routerPath('findGroupInfos'), ...guards, async (req, res) => { .get(this.routerPath('findGroupInfos'), ...guards, async (req, res) => {
logger.verbose('request received in findGroupInfos'); logger.verbose('request received in findGroupInfos');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupJid>({ const response = await this.groupValidate<GroupJid>({
request: req, request: req,
schema: groupJidSchema, schema: groupJidSchema,
ClassRef: GroupJid, ClassRef: GroupJid,
execute: (instance, data) => groupController.findGroupInfo(instance, data), execute: (instance, data) => groupController.findGroupInfo(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('fetchAllGroups'), ...guards, async (req, res) => { .get(this.routerPath('fetchAllGroups'), ...guards, async (req, res) => {
logger.verbose('request received in fetchAllGroups'); logger.verbose('request received in fetchAllGroups');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.getParticipantsValidate<GetParticipant>({ const response = await this.getParticipantsValidate<GetParticipant>({
request: req, request: req,
schema: getParticipantsSchema, schema: getParticipantsSchema,
ClassRef: GetParticipant, ClassRef: GetParticipant,
execute: (instance, data) => groupController.fetchAllGroups(instance, data), execute: (instance, data) => groupController.fetchAllGroups(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('participants'), ...guards, async (req, res) => { .get(this.routerPath('participants'), ...guards, async (req, res) => {
logger.verbose('request received in participants'); logger.verbose('request received in participants');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupJid>({ const response = await this.groupValidate<GroupJid>({
request: req, request: req,
schema: groupJidSchema, schema: groupJidSchema,
ClassRef: GroupJid, ClassRef: GroupJid,
execute: (instance, data) => groupController.findParticipants(instance, data), execute: (instance, data) => groupController.findParticipants(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('inviteCode'), ...guards, async (req, res) => { .get(this.routerPath('inviteCode'), ...guards, async (req, res) => {
logger.verbose('request received in inviteCode'); logger.verbose('request received in inviteCode');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupJid>({ const response = await this.groupValidate<GroupJid>({
request: req, request: req,
schema: groupJidSchema, schema: groupJidSchema,
ClassRef: GroupJid, ClassRef: GroupJid,
execute: (instance, data) => groupController.inviteCode(instance, data), execute: (instance, data) => groupController.inviteCode(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.get(this.routerPath('inviteInfo'), ...guards, async (req, res) => { .get(this.routerPath('inviteInfo'), ...guards, async (req, res) => {
logger.verbose('request received in inviteInfo'); logger.verbose('request received in inviteInfo');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.inviteCodeValidate<GroupInvite>({ const response = await this.inviteCodeValidate<GroupInvite>({
request: req, request: req,
schema: groupInviteSchema, schema: groupInviteSchema,
ClassRef: GroupInvite, ClassRef: GroupInvite,
execute: (instance, data) => groupController.inviteInfo(instance, data), execute: (instance, data) => groupController.inviteInfo(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.post(this.routerPath('sendInvite'), ...guards, async (req, res) => { .post(this.routerPath('sendInvite'), ...guards, async (req, res) => {
logger.verbose('request received in sendInvite'); logger.verbose('request received in sendInvite');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupNoValidate<GroupSendInvite>({ const response = await this.groupNoValidate<GroupSendInvite>({
request: req, request: req,
schema: groupSendInviteSchema, schema: groupSendInviteSchema,
ClassRef: GroupSendInvite, ClassRef: GroupSendInvite,
execute: (instance, data) => groupController.sendInvite(instance, data), execute: (instance, data) => groupController.sendInvite(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}) })
.put(this.routerPath('revokeInviteCode'), ...guards, async (req, res) => { .put(this.routerPath('revokeInviteCode'), ...guards, async (req, res) => {
logger.verbose('request received in revokeInviteCode'); logger.verbose('request received in revokeInviteCode');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupJid>({ const response = await this.groupValidate<GroupJid>({
request: req, request: req,
schema: groupJidSchema, schema: groupJidSchema,
ClassRef: GroupJid, ClassRef: GroupJid,
execute: (instance, data) => groupController.revokeInviteCode(instance, data), execute: (instance, data) => groupController.revokeInviteCode(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('updateParticipant'), ...guards, async (req, res) => { .put(this.routerPath('updateParticipant'), ...guards, async (req, res) => {
logger.verbose('request received in updateParticipant'); logger.verbose('request received in updateParticipant');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupUpdateParticipantDto>({ const response = await this.groupValidate<GroupUpdateParticipantDto>({
request: req, request: req,
schema: updateParticipantsSchema, schema: updateParticipantsSchema,
ClassRef: GroupUpdateParticipantDto, ClassRef: GroupUpdateParticipantDto,
execute: (instance, data) => groupController.updateGParticipate(instance, data), execute: (instance, data) => groupController.updateGParticipate(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('updateSetting'), ...guards, async (req, res) => { .put(this.routerPath('updateSetting'), ...guards, async (req, res) => {
logger.verbose('request received in updateSetting'); logger.verbose('request received in updateSetting');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupUpdateSettingDto>({ const response = await this.groupValidate<GroupUpdateSettingDto>({
request: req, request: req,
schema: updateSettingsSchema, schema: updateSettingsSchema,
ClassRef: GroupUpdateSettingDto, ClassRef: GroupUpdateSettingDto,
execute: (instance, data) => groupController.updateGSetting(instance, data), execute: (instance, data) => groupController.updateGSetting(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.put(this.routerPath('toggleEphemeral'), ...guards, async (req, res) => { .put(this.routerPath('toggleEphemeral'), ...guards, async (req, res) => {
logger.verbose('request received in toggleEphemeral'); logger.verbose('request received in toggleEphemeral');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupToggleEphemeralDto>({ const response = await this.groupValidate<GroupToggleEphemeralDto>({
request: req, request: req,
schema: toggleEphemeralSchema, schema: toggleEphemeralSchema,
ClassRef: GroupToggleEphemeralDto, ClassRef: GroupToggleEphemeralDto,
execute: (instance, data) => groupController.toggleEphemeral(instance, data), execute: (instance, data) => groupController.toggleEphemeral(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => { .delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => {
logger.verbose('request received in leaveGroup'); logger.verbose('request received in leaveGroup');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.groupValidate<GroupJid>({ const response = await this.groupValidate<GroupJid>({
request: req, request: req,
schema: {}, schema: {},
ClassRef: GroupJid, ClassRef: GroupJid,
execute: (instance, data) => groupController.leaveGroup(instance, data), execute: (instance, data) => groupController.leaveGroup(instance, data),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -14,13 +14,13 @@ import { ViewsRouter } from './view.router';
import { WebhookRouter } from './webhook.router'; import { WebhookRouter } from './webhook.router';
enum HttpStatus { enum HttpStatus {
OK = 200, OK = 200,
CREATED = 201, CREATED = 201,
NOT_FOUND = 404, NOT_FOUND = 404,
FORBIDDEN = 403, FORBIDDEN = 403,
BAD_REQUEST = 400, BAD_REQUEST = 400,
UNAUTHORIZED = 401, UNAUTHORIZED = 401,
INTERNAL_SERVER_ERROR = 500, INTERNAL_SERVER_ERROR = 500,
} }
const router = Router(); const router = Router();
@ -30,19 +30,19 @@ const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')); const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
router router
.get('/', (req, res) => { .get('/', (req, res) => {
res.status(HttpStatus.OK).json({ res.status(HttpStatus.OK).json({
status: HttpStatus.OK, status: HttpStatus.OK,
message: 'Welcome to the Evolution API, it is working!', message: 'Welcome to the Evolution API, it is working!',
version: packageJson.version, version: packageJson.version,
}); });
}) })
.use('/instance', new InstanceRouter(configService, ...guards).router, new ViewsRouter(instanceExistsGuard).router) .use('/instance', new InstanceRouter(configService, ...guards).router, new ViewsRouter(instanceExistsGuard).router)
.use('/message', new MessageRouter(...guards).router) .use('/message', new MessageRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router) .use('/chat', new ChatRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router) .use('/group', new GroupRouter(...guards).router)
.use('/webhook', new WebhookRouter(...guards).router) .use('/webhook', new WebhookRouter(...guards).router)
.use('/chatwoot', new ChatwootRouter(...guards).router) .use('/chatwoot', new ChatwootRouter(...guards).router)
.use('/settings', new SettingsRouter(...guards).router); .use('/settings', new SettingsRouter(...guards).router);
export { HttpStatus, router }; export { HttpStatus, router };

View File

@ -13,165 +13,163 @@ import { HttpStatus } from './index.router';
const logger = new Logger('InstanceRouter'); const logger = new Logger('InstanceRouter');
export class InstanceRouter extends RouterBroker { export class InstanceRouter extends RouterBroker {
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) { constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
super(); super();
const auth = configService.get<Auth>('AUTHENTICATION'); const auth = configService.get<Auth>('AUTHENTICATION');
this.router this.router
.post('/create', ...guards, async (req, res) => { .post('/create', ...guards, async (req, res) => {
logger.verbose('request received in createInstance'); logger.verbose('request received in createInstance');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => instanceController.createInstance(instance), execute: (instance) => instanceController.createInstance(instance),
});
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('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({ error: false, 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' });
}); });
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('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);
});
} }
public readonly router = Router(); 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({ error: false, 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();
} }

View File

@ -2,31 +2,31 @@ import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
audioMessageSchema, audioMessageSchema,
buttonMessageSchema, buttonMessageSchema,
contactMessageSchema, contactMessageSchema,
listMessageSchema, listMessageSchema,
locationMessageSchema, locationMessageSchema,
mediaMessageSchema, mediaMessageSchema,
pollMessageSchema, pollMessageSchema,
reactionMessageSchema, reactionMessageSchema,
statusMessageSchema, statusMessageSchema,
stickerMessageSchema, stickerMessageSchema,
textMessageSchema, textMessageSchema,
} from '../../validate/validate.schema'; } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { import {
SendAudioDto, SendAudioDto,
SendButtonDto, SendButtonDto,
SendContactDto, SendContactDto,
SendListDto, SendListDto,
SendLocationDto, SendLocationDto,
SendMediaDto, SendMediaDto,
SendPollDto, SendPollDto,
SendReactionDto, SendReactionDto,
SendStatusDto, SendStatusDto,
SendStickerDto, SendStickerDto,
SendTextDto, SendTextDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { sendMessageController } from '../whatsapp.module'; import { sendMessageController } from '../whatsapp.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
@ -34,186 +34,186 @@ import { HttpStatus } from './index.router';
const logger = new Logger('MessageRouter'); const logger = new Logger('MessageRouter');
export class MessageRouter extends RouterBroker { export class MessageRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('sendText'), ...guards, async (req, res) => { .post(this.routerPath('sendText'), ...guards, async (req, res) => {
logger.verbose('request received in sendText'); logger.verbose('request received in sendText');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendTextDto>({ const response = await this.dataValidate<SendTextDto>({
request: req, request: req,
schema: textMessageSchema, schema: textMessageSchema,
ClassRef: SendTextDto, ClassRef: SendTextDto,
execute: (instance, data) => sendMessageController.sendText(instance, data), execute: (instance, data) => sendMessageController.sendText(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendMedia'), ...guards, async (req, res) => { .post(this.routerPath('sendMedia'), ...guards, async (req, res) => {
logger.verbose('request received in sendMedia'); logger.verbose('request received in sendMedia');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendMediaDto>({ const response = await this.dataValidate<SendMediaDto>({
request: req, request: req,
schema: mediaMessageSchema, schema: mediaMessageSchema,
ClassRef: SendMediaDto, ClassRef: SendMediaDto,
execute: (instance, data) => sendMessageController.sendMedia(instance, data), execute: (instance, data) => sendMessageController.sendMedia(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendWhatsAppAudio'), ...guards, async (req, res) => { .post(this.routerPath('sendWhatsAppAudio'), ...guards, async (req, res) => {
logger.verbose('request received in sendWhatsAppAudio'); logger.verbose('request received in sendWhatsAppAudio');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendAudioDto>({ const response = await this.dataValidate<SendAudioDto>({
request: req, request: req,
schema: audioMessageSchema, schema: audioMessageSchema,
ClassRef: SendMediaDto, ClassRef: SendMediaDto,
execute: (instance, data) => sendMessageController.sendWhatsAppAudio(instance, data), execute: (instance, data) => sendMessageController.sendWhatsAppAudio(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendButtons'), ...guards, async (req, res) => { .post(this.routerPath('sendButtons'), ...guards, async (req, res) => {
logger.verbose('request received in sendButtons'); logger.verbose('request received in sendButtons');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendButtonDto>({ const response = await this.dataValidate<SendButtonDto>({
request: req, request: req,
schema: buttonMessageSchema, schema: buttonMessageSchema,
ClassRef: SendButtonDto, ClassRef: SendButtonDto,
execute: (instance, data) => sendMessageController.sendButtons(instance, data), execute: (instance, data) => sendMessageController.sendButtons(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendLocation'), ...guards, async (req, res) => { .post(this.routerPath('sendLocation'), ...guards, async (req, res) => {
logger.verbose('request received in sendLocation'); logger.verbose('request received in sendLocation');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendLocationDto>({ const response = await this.dataValidate<SendLocationDto>({
request: req, request: req,
schema: locationMessageSchema, schema: locationMessageSchema,
ClassRef: SendLocationDto, ClassRef: SendLocationDto,
execute: (instance, data) => sendMessageController.sendLocation(instance, data), execute: (instance, data) => sendMessageController.sendLocation(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendList'), ...guards, async (req, res) => { .post(this.routerPath('sendList'), ...guards, async (req, res) => {
logger.verbose('request received in sendList'); logger.verbose('request received in sendList');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendListDto>({ const response = await this.dataValidate<SendListDto>({
request: req, request: req,
schema: listMessageSchema, schema: listMessageSchema,
ClassRef: SendListDto, ClassRef: SendListDto,
execute: (instance, data) => sendMessageController.sendList(instance, data), execute: (instance, data) => sendMessageController.sendList(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendContact'), ...guards, async (req, res) => { .post(this.routerPath('sendContact'), ...guards, async (req, res) => {
logger.verbose('request received in sendContact'); logger.verbose('request received in sendContact');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendContactDto>({ const response = await this.dataValidate<SendContactDto>({
request: req, request: req,
schema: contactMessageSchema, schema: contactMessageSchema,
ClassRef: SendContactDto, ClassRef: SendContactDto,
execute: (instance, data) => sendMessageController.sendContact(instance, data), execute: (instance, data) => sendMessageController.sendContact(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendReaction'), ...guards, async (req, res) => { .post(this.routerPath('sendReaction'), ...guards, async (req, res) => {
logger.verbose('request received in sendReaction'); logger.verbose('request received in sendReaction');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendReactionDto>({ const response = await this.dataValidate<SendReactionDto>({
request: req, request: req,
schema: reactionMessageSchema, schema: reactionMessageSchema,
ClassRef: SendReactionDto, ClassRef: SendReactionDto,
execute: (instance, data) => sendMessageController.sendReaction(instance, data), execute: (instance, data) => sendMessageController.sendReaction(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendPoll'), ...guards, async (req, res) => { .post(this.routerPath('sendPoll'), ...guards, async (req, res) => {
logger.verbose('request received in sendPoll'); logger.verbose('request received in sendPoll');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendPollDto>({ const response = await this.dataValidate<SendPollDto>({
request: req, request: req,
schema: pollMessageSchema, schema: pollMessageSchema,
ClassRef: SendPollDto, ClassRef: SendPollDto,
execute: (instance, data) => sendMessageController.sendPoll(instance, data), execute: (instance, data) => sendMessageController.sendPoll(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendStatus'), ...guards, async (req, res) => { .post(this.routerPath('sendStatus'), ...guards, async (req, res) => {
logger.verbose('request received in sendStatus'); logger.verbose('request received in sendStatus');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendStatusDto>({ const response = await this.dataValidate<SendStatusDto>({
request: req, request: req,
schema: statusMessageSchema, schema: statusMessageSchema,
ClassRef: SendStatusDto, ClassRef: SendStatusDto,
execute: (instance, data) => sendMessageController.sendStatus(instance, data), execute: (instance, data) => sendMessageController.sendStatus(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendSticker'), ...guards, async (req, res) => { .post(this.routerPath('sendSticker'), ...guards, async (req, res) => {
logger.verbose('request received in sendSticker'); logger.verbose('request received in sendSticker');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SendStickerDto>({ const response = await this.dataValidate<SendStickerDto>({
request: req, request: req,
schema: stickerMessageSchema, schema: stickerMessageSchema,
ClassRef: SendStickerDto, ClassRef: SendStickerDto,
execute: (instance, data) => sendMessageController.sendSticker(instance, data), execute: (instance, data) => sendMessageController.sendSticker(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -12,42 +12,42 @@ import { HttpStatus } from './index.router';
const logger = new Logger('SettingsRouter'); const logger = new Logger('SettingsRouter');
export class SettingsRouter extends RouterBroker { export class SettingsRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('set'), ...guards, async (req, res) => { .post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setSettings'); logger.verbose('request received in setSettings');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<SettingsDto>({ const response = await this.dataValidate<SettingsDto>({
request: req, request: req,
schema: settingsSchema, schema: settingsSchema,
ClassRef: SettingsDto, ClassRef: SettingsDto,
execute: (instance, data) => settingsController.createSettings(instance, data), execute: (instance, data) => settingsController.createSettings(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.get(this.routerPath('find'), ...guards, async (req, res) => { .get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findSettings'); logger.verbose('request received in findSettings');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => settingsController.findSettings(instance), execute: (instance) => settingsController.findSettings(instance),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -4,13 +4,13 @@ import { RouterBroker } from '../abstract/abstract.router';
import { viewsController } from '../whatsapp.module'; import { viewsController } from '../whatsapp.module';
export class ViewsRouter extends RouterBroker { export class ViewsRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router.get(this.routerPath('qrcode'), ...guards, (req, res) => { this.router.get(this.routerPath('qrcode'), ...guards, (req, res) => {
return viewsController.qrcode(req, res); return viewsController.qrcode(req, res);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -11,42 +11,42 @@ import { HttpStatus } from './index.router';
const logger = new Logger('WebhookRouter'); const logger = new Logger('WebhookRouter');
export class WebhookRouter extends RouterBroker { export class WebhookRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) { constructor(...guards: RequestHandler[]) {
super(); super();
this.router this.router
.post(this.routerPath('set'), ...guards, async (req, res) => { .post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setWebhook'); logger.verbose('request received in setWebhook');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<WebhookDto>({ const response = await this.dataValidate<WebhookDto>({
request: req, request: req,
schema: webhookSchema, schema: webhookSchema,
ClassRef: WebhookDto, ClassRef: WebhookDto,
execute: (instance, data) => webhookController.createWebhook(instance, data), execute: (instance, data) => webhookController.createWebhook(instance, data),
}); });
res.status(HttpStatus.CREATED).json(response); res.status(HttpStatus.CREATED).json(response);
}) })
.get(this.routerPath('find'), ...guards, async (req, res) => { .get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findWebhook'); logger.verbose('request received in findWebhook');
logger.verbose('request body: '); logger.verbose('request body: ');
logger.verbose(req.body); logger.verbose(req.body);
logger.verbose('request query: '); logger.verbose('request query: ');
logger.verbose(req.query); logger.verbose(req.query);
const response = await this.dataValidate<InstanceDto>({ const response = await this.dataValidate<InstanceDto>({
request: req, request: req,
schema: instanceNameSchema, schema: instanceNameSchema,
ClassRef: InstanceDto, ClassRef: InstanceDto,
execute: (instance) => webhookController.findWebhook(instance), execute: (instance) => webhookController.findWebhook(instance),
}); });
res.status(HttpStatus.OK).json(response); res.status(HttpStatus.OK).json(response);
}); });
} }
public readonly router = Router(); public readonly router = Router();
} }

View File

@ -12,166 +12,166 @@ import { RepositoryBroker } from '../repository/repository.manager';
import { WAMonitoringService } from './monitor.service'; import { WAMonitoringService } from './monitor.service';
export type JwtPayload = { export type JwtPayload = {
instanceName: string; instanceName: string;
apiName: string; apiName: string;
jwt?: string; jwt?: string;
apikey?: string; apikey?: string;
tokenId: string; tokenId: string;
}; };
export class OldToken { export class OldToken {
oldToken: string; oldToken: string;
} }
export class AuthService { export class AuthService {
constructor( constructor(
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly waMonitor: WAMonitoringService, private readonly waMonitor: WAMonitoringService,
private readonly repository: RepositoryBroker, private readonly repository: RepositoryBroker,
) {} ) {}
private readonly logger = new Logger(AuthService.name); private readonly logger = new Logger(AuthService.name);
private async jwt(instance: InstanceDto) { private async jwt(instance: InstanceDto) {
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT; const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
const token = sign( const token = sign(
{
instanceName: instance.instanceName,
apiName,
tokenId: v4(),
},
jwtOpts.SECRET,
{ expiresIn: jwtOpts.EXPIRIN_IN, encoding: 'utf8', subject: 'g-t' },
);
this.logger.verbose('JWT token created: ' + token);
const auth = await this.repository.auth.create({ jwt: token }, instance.instanceName);
this.logger.verbose('JWT token saved in database');
if (auth['error']) {
this.logger.error({
localError: AuthService.name + '.jwt',
error: auth['error'],
});
throw new BadRequestException('Authentication error', auth['error']?.toString());
}
return { jwt: token };
}
private async apikey(instance: InstanceDto, token?: string) {
const apikey = token ? token : v4().toUpperCase();
this.logger.verbose(token ? 'APIKEY defined: ' + apikey : 'APIKEY created: ' + apikey);
const auth = await this.repository.auth.create({ apikey }, instance.instanceName);
this.logger.verbose('APIKEY saved in database');
if (auth['error']) {
this.logger.error({
localError: AuthService.name + '.apikey',
error: auth['error'],
});
throw new BadRequestException('Authentication error', auth['error']?.toString());
}
return { apikey };
}
public async checkDuplicateToken(token: string) {
const instances = await this.waMonitor.instanceInfo();
this.logger.verbose('checking duplicate token');
const instance = instances.find((instance) => instance.instance.apikey === token);
if (instance) {
throw new BadRequestException('Token already exists');
}
this.logger.verbose('available token');
return true;
}
public async generateHash(instance: InstanceDto, token?: string) {
const options = this.configService.get<Auth>('AUTHENTICATION');
this.logger.verbose('generating hash ' + options.TYPE + ' to instance: ' + instance.instanceName);
return (await this[options.TYPE](instance, token)) as { jwt: string } | { apikey: string };
}
public async refreshToken({ oldToken }: OldToken) {
this.logger.verbose('refreshing token');
if (!isJWT(oldToken)) {
throw new BadRequestException('Invalid "oldToken"');
}
try {
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
this.logger.verbose('checking oldToken');
const decode = verify(oldToken, jwtOpts.SECRET, {
ignoreExpiration: true,
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
this.logger.verbose('checking token in database');
const tokenStore = await this.repository.auth.find(decode.instanceName);
const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, {
ignoreExpiration: true,
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
this.logger.verbose('checking tokenId');
if (decode.tokenId !== decodeTokenStore.tokenId) {
throw new BadRequestException('Invalid "oldToken"');
}
this.logger.verbose('generating new token');
const token = {
jwt: (await this.jwt({ instanceName: decode.instanceName })).jwt,
instanceName: decode.instanceName,
};
try {
this.logger.verbose('checking webhook');
const webhook = await this.repository.webhook.find(decode.instanceName);
if (webhook?.enabled && this.configService.get<Webhook>('WEBHOOK').EVENTS.NEW_JWT_TOKEN) {
this.logger.verbose('sending webhook');
const httpService = axios.create({ baseURL: webhook.url });
await httpService.post(
'',
{ {
instanceName: instance.instanceName, event: 'new.jwt',
apiName, instance: decode.instanceName,
tokenId: v4(), data: token,
}, },
jwtOpts.SECRET, { params: { owner: this.waMonitor.waInstances[decode.instanceName].wuid } },
{ expiresIn: jwtOpts.EXPIRIN_IN, encoding: 'utf8', subject: 'g-t' }, );
);
this.logger.verbose('JWT token created: ' + token);
const auth = await this.repository.auth.create({ jwt: token }, instance.instanceName);
this.logger.verbose('JWT token saved in database');
if (auth['error']) {
this.logger.error({
localError: AuthService.name + '.jwt',
error: auth['error'],
});
throw new BadRequestException('Authentication error', auth['error']?.toString());
} }
} catch (error) {
this.logger.error(error);
}
return { jwt: token }; this.logger.verbose('token refreshed');
}
return token;
private async apikey(instance: InstanceDto, token?: string) { } catch (error) {
const apikey = token ? token : v4().toUpperCase(); this.logger.error({
localError: AuthService.name + '.refreshToken',
this.logger.verbose(token ? 'APIKEY defined: ' + apikey : 'APIKEY created: ' + apikey); error,
});
const auth = await this.repository.auth.create({ apikey }, instance.instanceName); throw new BadRequestException('Invalid "oldToken"');
this.logger.verbose('APIKEY saved in database');
if (auth['error']) {
this.logger.error({
localError: AuthService.name + '.apikey',
error: auth['error'],
});
throw new BadRequestException('Authentication error', auth['error']?.toString());
}
return { apikey };
}
public async checkDuplicateToken(token: string) {
const instances = await this.waMonitor.instanceInfo();
this.logger.verbose('checking duplicate token');
const instance = instances.find((instance) => instance.instance.apikey === token);
if (instance) {
throw new BadRequestException('Token already exists');
}
this.logger.verbose('available token');
return true;
}
public async generateHash(instance: InstanceDto, token?: string) {
const options = this.configService.get<Auth>('AUTHENTICATION');
this.logger.verbose('generating hash ' + options.TYPE + ' to instance: ' + instance.instanceName);
return (await this[options.TYPE](instance, token)) as { jwt: string } | { apikey: string };
}
public async refreshToken({ oldToken }: OldToken) {
this.logger.verbose('refreshing token');
if (!isJWT(oldToken)) {
throw new BadRequestException('Invalid "oldToken"');
}
try {
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
this.logger.verbose('checking oldToken');
const decode = verify(oldToken, jwtOpts.SECRET, {
ignoreExpiration: true,
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
this.logger.verbose('checking token in database');
const tokenStore = await this.repository.auth.find(decode.instanceName);
const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, {
ignoreExpiration: true,
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
this.logger.verbose('checking tokenId');
if (decode.tokenId !== decodeTokenStore.tokenId) {
throw new BadRequestException('Invalid "oldToken"');
}
this.logger.verbose('generating new token');
const token = {
jwt: (await this.jwt({ instanceName: decode.instanceName })).jwt,
instanceName: decode.instanceName,
};
try {
this.logger.verbose('checking webhook');
const webhook = await this.repository.webhook.find(decode.instanceName);
if (webhook?.enabled && this.configService.get<Webhook>('WEBHOOK').EVENTS.NEW_JWT_TOKEN) {
this.logger.verbose('sending webhook');
const httpService = axios.create({ baseURL: webhook.url });
await httpService.post(
'',
{
event: 'new.jwt',
instance: decode.instanceName,
data: token,
},
{ params: { owner: this.waMonitor.waInstances[decode.instanceName].wuid } },
);
}
} catch (error) {
this.logger.error(error);
}
this.logger.verbose('token refreshed');
return token;
} catch (error) {
this.logger.error({
localError: AuthService.name + '.refreshToken',
error,
});
throw new BadRequestException('Invalid "oldToken"');
}
} }
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -11,344 +11,344 @@ import { dbserver } from '../../db/db.connect';
import { RedisCache } from '../../db/redis.client'; import { RedisCache } from '../../db/redis.client';
import { NotFoundException } from '../../exceptions'; import { NotFoundException } from '../../exceptions';
import { import {
AuthModel, AuthModel,
ChatwootModel, ChatwootModel,
ContactModel, ContactModel,
MessageModel, MessageModel,
MessageUpModel, MessageUpModel,
SettingsModel, SettingsModel,
WebhookModel, WebhookModel,
} from '../models'; } from '../models';
import { RepositoryBroker } from '../repository/repository.manager'; import { RepositoryBroker } from '../repository/repository.manager';
import { WAStartupService } from './whatsapp.service'; import { WAStartupService } from './whatsapp.service';
export class WAMonitoringService { export class WAMonitoringService {
constructor( constructor(
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly repository: RepositoryBroker, private readonly repository: RepositoryBroker,
private readonly cache: RedisCache, private readonly cache: RedisCache,
) { ) {
this.logger.verbose('instance created'); this.logger.verbose('instance created');
this.removeInstance(); this.removeInstance();
this.noConnection(); this.noConnection();
this.delInstanceFiles(); this.delInstanceFiles();
Object.assign(this.db, configService.get<Database>('DATABASE')); Object.assign(this.db, configService.get<Database>('DATABASE'));
Object.assign(this.redis, configService.get<Redis>('REDIS')); Object.assign(this.redis, configService.get<Redis>('REDIS'));
this.dbInstance = this.db.ENABLED this.dbInstance = this.db.ENABLED
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances') ? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
: undefined; : 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);
delete this.waInstances[instance];
} else {
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`);
} }
private readonly db: Partial<Database> = {}; const instances: any[] = [];
private readonly redis: Partial<Redis> = {};
private dbInstance: Db; for await (const [key, value] of Object.entries(this.waInstances)) {
if (value) {
this.logger.verbose('get instance info: ' + key);
let chatwoot: any;
private dbStore = dbserver; const urlServer = this.configService.get<HttpServer>('SERVER').URL;
private readonly logger = new Logger(WAMonitoringService.name); const findChatwoot = await this.waInstances[key].findChatwoot();
public readonly waInstances: Record<string, WAStartupService> = {};
public delInstanceTime(instance: string) { if (findChatwoot && findChatwoot.enabled) {
const time = this.configService.get<DelInstance>('DEL_INSTANCE'); chatwoot = {
if (typeof time === 'number' && time > 0) { ...findChatwoot,
this.logger.verbose(`Instance "${instance}" don't have connection, will be removed in ${time} minutes`); webhook_url: `${urlServer}/chatwoot/webhook/${key}`,
};
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);
delete this.waInstances[instance];
} else {
delete this.waInstances[instance];
this.eventEmitter.emit('remove.instance', instance, 'inner');
}
}
}, 1000 * 60 * time);
} }
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);
}
}
} }
public async instanceInfo(instanceName?: string) { this.logger.verbose('return instance info: ' + instances.length);
this.logger.verbose('get instance info');
if (instanceName && !this.waInstances[instanceName]) {
throw new NotFoundException(`Instance "${instanceName}" not found`);
}
const instances: any[] = []; return instances.find((i) => i.instance.instanceName === instanceName) ?? instances;
}
for await (const [key, value] of Object.entries(this.waInstances)) { private delInstanceFiles() {
if (value) { this.logger.verbose('cron to delete instance files started');
this.logger.verbose('get instance info: ' + key); setInterval(async () => {
let chatwoot: any; if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
const collections = await this.dbInstance.collections();
const urlServer = this.configService.get<HttpServer>('SERVER').URL; collections.forEach(async (collection) => {
const name = collection.namespace.replace(/^[\w-]+./, '');
const findChatwoot = await this.waInstances[key].findChatwoot(); await this.dbInstance.collection(name).deleteMany({
$or: [{ _id: { $regex: /^app.state.*/ } }, { _id: { $regex: /^session-.*/ } }],
if (findChatwoot && findChatwoot.enabled) { });
chatwoot = { this.logger.verbose('instance files deleted: ' + name);
...findChatwoot, });
webhook_url: `${urlServer}/chatwoot/webhook/${key}`, // } else if (this.redis.ENABLED) {
}; } else {
} const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) {
if (value.connectionStatus.state === 'open') { if (dirent.isDirectory()) {
this.logger.verbose('instance: ' + key + ' - connectionStatus: open'); const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
encoding: 'utf-8',
const instanceData = { });
instance: { files.forEach(async (file) => {
instanceName: key, if (file.match(/^app.state.*/) || file.match(/^session-.*/)) {
owner: value.wuid, rmSync(join(INSTANCE_DIR, dirent.name, file), {
profileName: (await value.getProfileName()) || 'not loaded', recursive: true,
profilePictureUrl: value.profilePictureUrl, force: true,
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) { }
} else { });
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); this.logger.verbose('instance files deleted: ' + dirent.name);
for await (const dirent of dir) { }
if (dirent.isDirectory()) { }
const files = readdirSync(join(INSTANCE_DIR, dirent.name), { }
encoding: 'utf-8', }, 3600 * 1000 * 2);
}); }
files.forEach(async (file) => {
if (file.match(/^app.state.*/) || file.match(/^session-.*/)) { public async cleaningUp(instanceName: string) {
rmSync(join(INSTANCE_DIR, dirent.name, file), { this.logger.verbose('cleaning up instance: ' + instanceName);
recursive: true, if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
force: true, 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) {
this.logger.verbose('instance files deleted: ' + dirent.name); await this.dbInstance.dropCollection(instanceName);
} }
} return;
}
}, 3600 * 1000 * 2);
} }
public async cleaningUp(instanceName: string) { if (this.redis.ENABLED) {
this.logger.verbose('cleaning up instance: ' + instanceName); this.logger.verbose('cleaning up instance in redis: ' + instanceName);
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { this.cache.reference = instanceName;
this.logger.verbose('cleaning up instance in database: ' + instanceName); await this.cache.delAll();
await this.repository.dbServer.connect(); return;
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) { this.logger.verbose('cleaning up instance in files: ' + instanceName);
if (!this.db.ENABLED) { rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
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)}`); public async cleaningStoreFiles(instanceName: string) {
execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`); if (!this.db.ENABLED) {
execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`); this.logger.verbose('cleaning store files instance: ' + instanceName);
execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`); execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`); execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'chatwoot', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`);
execSync(`rm -rf ${join(STORE_DIR, 'settings', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`);
return; 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, 'settings', instanceName + '*')}`);
return;
}
this.logger.verbose('cleaning store database instance: ' + instanceName);
await AuthModel.deleteMany({ owner: instanceName });
await ContactModel.deleteMany({ owner: instanceName });
await MessageModel.deleteMany({ owner: instanceName });
await MessageUpModel.deleteMany({ owner: instanceName });
await AuthModel.deleteMany({ _id: instanceName });
await WebhookModel.deleteMany({ _id: instanceName });
await ChatwootModel.deleteMany({ _id: instanceName });
await SettingsModel.deleteMany({ _id: instanceName });
return;
}
public async loadInstance() {
this.logger.verbose('load instances');
const set = async (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;
};
try {
if (this.redis.ENABLED) {
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');
keys.forEach(async (k) => await set(k.split(':')[1]));
} else {
this.logger.verbose('no instance keys found');
} }
this.logger.verbose('cleaning store database instance: ' + instanceName);
await AuthModel.deleteMany({ owner: instanceName });
await ContactModel.deleteMany({ owner: instanceName });
await MessageModel.deleteMany({ owner: instanceName });
await MessageUpModel.deleteMany({ owner: instanceName });
await AuthModel.deleteMany({ _id: instanceName });
await WebhookModel.deleteMany({ _id: instanceName });
await ChatwootModel.deleteMany({ _id: instanceName });
await SettingsModel.deleteMany({ _id: instanceName });
return; return;
} }
public async loadInstance() { if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
this.logger.verbose('load instances'); this.logger.verbose('database enabled');
const set = async (name: string) => { await this.repository.dbServer.connect();
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache); const collections: any[] = await this.dbInstance.collections();
instance.instanceName = name; if (collections.length > 0) {
this.logger.verbose('instance loaded: ' + name); this.logger.verbose('reading collections and setting instances');
collections.forEach(async (coll) => await set(coll.namespace.replace(/^[\w-]+\./, '')));
await instance.connectToWhatsapp(); } else {
this.logger.verbose('connectToWhatsapp: ' + name); this.logger.verbose('no collections found');
this.waInstances[name] = instance;
};
try {
if (this.redis.ENABLED) {
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');
keys.forEach(async (k) => await set(k.split(':')[1]));
} else {
this.logger.verbose('no instance keys found');
}
return;
}
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
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');
collections.forEach(async (coll) => await set(coll.namespace.replace(/^[\w-]+\./, '')));
} else {
this.logger.verbose('no collections found');
}
return;
}
this.logger.verbose('store in files enabled');
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) {
if (dirent.isDirectory()) {
this.logger.verbose('reading instance files and setting instances');
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
encoding: 'utf-8',
});
if (files.length === 0) {
rmSync(join(INSTANCE_DIR, dirent.name), { recursive: true, force: true });
break;
}
await set(dirent.name);
} else {
this.logger.verbose('no instance files found');
}
}
} catch (error) {
this.logger.error(error);
} }
return;
}
this.logger.verbose('store in files enabled');
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) {
if (dirent.isDirectory()) {
this.logger.verbose('reading instance files and setting instances');
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
encoding: 'utf-8',
});
if (files.length === 0) {
rmSync(join(INSTANCE_DIR, dirent.name), { recursive: true, force: true });
break;
}
await set(dirent.name);
} else {
this.logger.verbose('no instance files found');
}
}
} catch (error) {
this.logger.error(error);
} }
}
private removeInstance() { private removeInstance() {
this.eventEmitter.on('remove.instance', async (instanceName: string) => { this.eventEmitter.on('remove.instance', async (instanceName: string) => {
this.logger.verbose('remove instance: ' + instanceName); this.logger.verbose('remove instance: ' + instanceName);
try { try {
this.logger.verbose('instance: ' + instanceName + ' - removing from memory'); this.logger.verbose('instance: ' + instanceName + ' - removing from memory');
this.waInstances[instanceName] = undefined; this.waInstances[instanceName] = undefined;
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
} }
try { try {
this.logger.verbose('request cleaning up instance: ' + instanceName); this.logger.verbose('request cleaning up instance: ' + instanceName);
this.cleaningUp(instanceName); this.cleaningUp(instanceName);
this.cleaningStoreFiles(instanceName); this.cleaningStoreFiles(instanceName);
} finally { } finally {
this.logger.warn(`Instance "${instanceName}" - REMOVED`); this.logger.warn(`Instance "${instanceName}" - REMOVED`);
} }
});
this.eventEmitter.on('logout.instance', async (instanceName: string) => {
this.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('instance: ' + instanceName + ' - removing from memory');
this.waInstances[instanceName] = undefined;
this.logger.verbose('request cleaning up instance: ' + instanceName);
this.cleaningUp(instanceName);
} catch (error) {
this.logger.error({
localError: 'noConnection',
warn: 'Error deleting instance from memory.',
error,
}); });
this.eventEmitter.on('logout.instance', async (instanceName: string) => { } finally {
this.logger.verbose('logout instance: ' + instanceName); this.logger.warn(`Instance "${instanceName}" - NOT CONNECTION`);
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('instance: ' + instanceName + ' - removing from memory');
this.waInstances[instanceName] = undefined;
this.logger.verbose('request cleaning up instance: ' + instanceName);
this.cleaningUp(instanceName);
} catch (error) {
this.logger.error({
localError: 'noConnection',
warn: 'Error deleting instance from memory.',
error,
});
} finally {
this.logger.warn(`Instance "${instanceName}" - NOT CONNECTION`);
}
});
}
} }

View File

@ -4,29 +4,29 @@ import { SettingsDto } from '../dto/settings.dto';
import { WAMonitoringService } from './monitor.service'; import { WAMonitoringService } from './monitor.service';
export class SettingsService { export class SettingsService {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}
private readonly logger = new Logger(SettingsService.name); private readonly logger = new Logger(SettingsService.name);
public create(instance: InstanceDto, data: SettingsDto) { public create(instance: InstanceDto, data: SettingsDto) {
this.logger.verbose('create settings: ' + instance.instanceName); this.logger.verbose('create settings: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setSettings(data); this.waMonitor.waInstances[instance.instanceName].setSettings(data);
return { settings: { ...instance, settings: data } }; return { settings: { ...instance, settings: data } };
} }
public async find(instance: InstanceDto): Promise<SettingsDto> { public async find(instance: InstanceDto): Promise<SettingsDto> {
try { try {
this.logger.verbose('find settings: ' + instance.instanceName); this.logger.verbose('find settings: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findSettings(); const result = await this.waMonitor.waInstances[instance.instanceName].findSettings();
if (Object.keys(result).length === 0) { if (Object.keys(result).length === 0) {
throw new Error('Settings not found'); throw new Error('Settings not found');
} }
return result; return result;
} catch (error) { } catch (error) {
return { reject_call: false, msg_call: '', groups_ignore: false }; return { reject_call: false, msg_call: '', groups_ignore: false };
}
} }
}
} }

View File

@ -4,29 +4,29 @@ import { WebhookDto } from '../dto/webhook.dto';
import { WAMonitoringService } from './monitor.service'; import { WAMonitoringService } from './monitor.service';
export class WebhookService { export class WebhookService {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}
private readonly logger = new Logger(WebhookService.name); private readonly logger = new Logger(WebhookService.name);
public create(instance: InstanceDto, data: WebhookDto) { public create(instance: InstanceDto, data: WebhookDto) {
this.logger.verbose('create webhook: ' + instance.instanceName); this.logger.verbose('create webhook: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setWebhook(data); this.waMonitor.waInstances[instance.instanceName].setWebhook(data);
return { webhook: { ...instance, webhook: data } }; return { webhook: { ...instance, webhook: data } };
} }
public async find(instance: InstanceDto): Promise<WebhookDto> { public async find(instance: InstanceDto): Promise<WebhookDto> {
try { try {
this.logger.verbose('find webhook: ' + instance.instanceName); this.logger.verbose('find webhook: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findWebhook(); const result = await this.waMonitor.waInstances[instance.instanceName].findWebhook();
if (Object.keys(result).length === 0) { if (Object.keys(result).length === 0) {
throw new Error('Webhook not found'); throw new Error('Webhook not found');
} }
return result; return result;
} catch (error) { } catch (error) {
return { enabled: false, url: '', events: [], webhook_by_events: false }; return { enabled: false, url: '', events: [], webhook_by_events: false };
}
} }
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,88 +2,88 @@
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys'; import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
export enum Events { export enum Events {
APPLICATION_STARTUP = 'application.startup', APPLICATION_STARTUP = 'application.startup',
QRCODE_UPDATED = 'qrcode.updated', QRCODE_UPDATED = 'qrcode.updated',
CONNECTION_UPDATE = 'connection.update', CONNECTION_UPDATE = 'connection.update',
STATUS_INSTANCE = 'status.instance', STATUS_INSTANCE = 'status.instance',
MESSAGES_SET = 'messages.set', MESSAGES_SET = 'messages.set',
MESSAGES_UPSERT = 'messages.upsert', MESSAGES_UPSERT = 'messages.upsert',
MESSAGES_UPDATE = 'messages.update', MESSAGES_UPDATE = 'messages.update',
MESSAGES_DELETE = 'messages.delete', MESSAGES_DELETE = 'messages.delete',
SEND_MESSAGE = 'send.message', SEND_MESSAGE = 'send.message',
CONTACTS_SET = 'contacts.set', CONTACTS_SET = 'contacts.set',
CONTACTS_UPSERT = 'contacts.upsert', CONTACTS_UPSERT = 'contacts.upsert',
CONTACTS_UPDATE = 'contacts.update', CONTACTS_UPDATE = 'contacts.update',
PRESENCE_UPDATE = 'presence.update', PRESENCE_UPDATE = 'presence.update',
CHATS_SET = 'chats.set', CHATS_SET = 'chats.set',
CHATS_UPDATE = 'chats.update', CHATS_UPDATE = 'chats.update',
CHATS_UPSERT = 'chats.upsert', CHATS_UPSERT = 'chats.upsert',
CHATS_DELETE = 'chats.delete', CHATS_DELETE = 'chats.delete',
GROUPS_UPSERT = 'groups.upsert', GROUPS_UPSERT = 'groups.upsert',
GROUPS_UPDATE = 'groups.update', GROUPS_UPDATE = 'groups.update',
GROUP_PARTICIPANTS_UPDATE = 'group-participants.update', GROUP_PARTICIPANTS_UPDATE = 'group-participants.update',
CALL = 'call', CALL = 'call',
} }
export declare namespace wa { export declare namespace wa {
export type QrCode = { export type QrCode = {
count?: number; count?: number;
pairingCode?: string; pairingCode?: string;
base64?: string; base64?: string;
code?: string; code?: string;
}; };
export type Instance = { export type Instance = {
qrcode?: QrCode; qrcode?: QrCode;
pairingCode?: string; pairingCode?: string;
authState?: { state: AuthenticationState; saveCreds: () => void }; authState?: { state: AuthenticationState; saveCreds: () => void };
name?: string; name?: string;
wuid?: string; wuid?: string;
profileName?: string; profileName?: string;
profilePictureUrl?: string; profilePictureUrl?: string;
}; };
export type LocalWebHook = { export type LocalWebHook = {
enabled?: boolean; enabled?: boolean;
url?: string; url?: string;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
}; };
export type LocalChatwoot = { export type LocalChatwoot = {
enabled?: boolean; enabled?: boolean;
account_id?: string; account_id?: string;
token?: string; token?: string;
url?: string; url?: string;
name_inbox?: string; name_inbox?: string;
sign_msg?: boolean; sign_msg?: boolean;
number?: string; number?: string;
reopen_conversation?: boolean; reopen_conversation?: boolean;
conversation_pending?: boolean; conversation_pending?: boolean;
}; };
export type LocalSettings = { export type LocalSettings = {
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;
groups_ignore?: boolean; groups_ignore?: boolean;
always_online?: boolean; always_online?: boolean;
read_messages?: boolean; read_messages?: boolean;
read_status?: boolean; read_status?: boolean;
}; };
export type StateConnection = { export type StateConnection = {
instance?: string; instance?: string;
state?: WAConnectionState | 'refused'; state?: WAConnectionState | 'refused';
statusReason?: number; statusReason?: number;
}; };
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED'; export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
} }
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage']; export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage'];
export const MessageSubtype = [ export const MessageSubtype = [
'ephemeralMessage', 'ephemeralMessage',
'documentWithCaptionMessage', 'documentWithCaptionMessage',
'viewOnceMessage', 'viewOnceMessage',
'viewOnceMessageV2', 'viewOnceMessageV2',
]; ];

View File

@ -12,14 +12,14 @@ import { SettingsController } from './controllers/settings.controller';
import { ViewsController } from './controllers/views.controller'; import { ViewsController } from './controllers/views.controller';
import { WebhookController } from './controllers/webhook.controller'; import { WebhookController } from './controllers/webhook.controller';
import { import {
AuthModel, AuthModel,
ChatModel, ChatModel,
ChatwootModel, ChatwootModel,
ContactModel, ContactModel,
MessageModel, MessageModel,
MessageUpModel, MessageUpModel,
SettingsModel, SettingsModel,
WebhookModel, WebhookModel,
} from './models'; } from './models';
import { AuthRepository } from './repository/auth.repository'; import { AuthRepository } from './repository/auth.repository';
import { ChatRepository } from './repository/chat.repository'; import { ChatRepository } from './repository/chat.repository';
@ -48,16 +48,16 @@ const settingsRepository = new SettingsRepository(SettingsModel, configService);
const authRepository = new AuthRepository(AuthModel, configService); const authRepository = new AuthRepository(AuthModel, configService);
export const repository = new RepositoryBroker( export const repository = new RepositoryBroker(
messageRepository, messageRepository,
chatRepository, chatRepository,
contactRepository, contactRepository,
messageUpdateRepository, messageUpdateRepository,
webhookRepository, webhookRepository,
chatwootRepository, chatwootRepository,
settingsRepository, settingsRepository,
authRepository, authRepository,
configService, configService,
dbserver?.getClient(), dbserver?.getClient(),
); );
export const cache = new RedisCache(); export const cache = new RedisCache();
@ -79,15 +79,15 @@ const settingsService = new SettingsService(waMonitor);
export const settingsController = new SettingsController(settingsService); export const settingsController = new SettingsController(settingsService);
export const instanceController = new InstanceController( export const instanceController = new InstanceController(
waMonitor, waMonitor,
configService, configService,
repository, repository,
eventEmitter, eventEmitter,
authService, authService,
webhookService, webhookService,
chatwootService, chatwootService,
settingsService, settingsService,
cache, cache,
); );
export const viewsController = new ViewsController(waMonitor, configService); export const viewsController = new ViewsController(waMonitor, configService);
export const sendMessageController = new SendMessageController(waMonitor); export const sendMessageController = new SendMessageController(waMonitor);