docs:Update code comments, add JSDoc annotations

This commit is contained in:
mareanalitica 2023-10-06 06:41:30 -03:00
parent bddd6408ac
commit aef6deb89b
12 changed files with 471 additions and 81 deletions

View File

@ -3,19 +3,28 @@ import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq } from '../config/env.config';
import { Logger } from '../config/logger.config';
// Create a logger instance specifically for AMQP-related logs.
const logger = new Logger('AMQP');
// Declare an AMQP channel, initially set to null.
let amqpChannel: amqp.Channel | null = null;
/**
* Initializes the AMQP (Advanced Message Queuing Protocol) connection.
* @returns {Promise<void>} A promise that resolves when the AMQP connection is established.
*/
export const initAMQP = () => {
return new Promise<void>((resolve, reject) => {
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
// Connect to the RabbitMQ server.
amqp.connect(uri, (error, connection) => {
if (error) {
reject(error);
return;
}
// Create an AMQP channel for communication.
connection.createChannel((channelError, channel) => {
if (channelError) {
reject(channelError);
@ -24,6 +33,7 @@ export const initAMQP = () => {
const exchangeName = 'evolution_exchange';
// Declare an exchange with topic routing.
channel.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
@ -38,13 +48,23 @@ export const initAMQP = () => {
});
};
/**
* Get the initialized AMQP channel.
* @returns {amqp.Channel | null} The initialized AMQP channel or null if not initialized.
*/
export const getAMQP = (): amqp.Channel | null => {
return amqpChannel;
};
/**
* Initializes queues for specified events.
* @param {string} instanceName - The name of the instance.
* @param {string[]} events - An array of event names.
*/
export const initQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
// Transform event names into queue names.
const queues = events.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`;
});
@ -53,6 +73,7 @@ export const initQueues = (instanceName: string, events: string[]) => {
const amqp = getAMQP();
const exchangeName = instanceName ?? 'evolution_exchange';
// Assert the exchange with topic routing.
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
@ -60,6 +81,7 @@ export const initQueues = (instanceName: string, events: string[]) => {
const queueName = `${instanceName}.${event}`;
// Assert a queue with quorum support.
amqp.assertQueue(queueName, {
durable: true,
autoDelete: false,
@ -68,6 +90,7 @@ export const initQueues = (instanceName: string, events: string[]) => {
},
});
// Bind the queue to the exchange with the corresponding event.
amqp.bindQueue(queueName, exchangeName, event);
});
};

View File

@ -3,23 +3,73 @@ import mongoose from 'mongoose';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
/**
* Object for logging MongoDB-related messages.
* @type {Logger}
*/
const logger = new Logger('MongoDB');
/**
* Database settings retrieved from the configuration file.
* @type {Database}
*/
const db = configService.get<Database>('DATABASE');
/**
* Function that creates and returns a connection to MongoDB using Mongoose.
* @returns {mongoose.Connection | undefined} MongoDB connection or `undefined` if the connection is not enabled.
*/
export const dbserver = (() => {
if (db.ENABLED) {
/**
* Log message indicating an attempt to connect to MongoDB.
*/
logger.verbose('connecting');
const dbs = mongoose.createConnection(db.CONNECTION.URI, {
/**
* Options for the MongoDB connection.
* @type {mongoose.ConnectionOptions}
*/
const connectionOptions = {
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
});
};
/**
* Creation of the MongoDB connection.
* @type {mongoose.Connection}
*/
const dbs = mongoose.createConnection(db.CONNECTION.URI, connectionOptions);
/**
* Log message indicating the successful connection to MongoDB.
*/
logger.verbose('connected in ' + db.CONNECTION.URI);
/**
* Informative log message about the connected database name.
*/
logger.info('ON - dbName: ' + dbs['$dbName']);
/**
* Registers an event handler for the beforeExit process event.
*/
process.on('beforeExit', () => {
/**
* Log message indicating the destruction of the MongoDB connection instance.
*/
logger.verbose('instance destroyed');
/**
* Destroys the MongoDB connection.
* @param {boolean} [force=false] - Indicates whether the destruction should be forced.
* @param {function(Error)} [callback] - Callback function to handle errors during destruction.
*/
dbserver.destroy(true, (error) => logger.error(error));
});
/**
* Returns the MongoDB connection.
*/
return dbs;
}
})();

View File

@ -4,13 +4,24 @@ import { BufferJSON } from '@whiskeysockets/baileys';
import { Redis } from '../config/env.config';
import { Logger } from '../config/logger.config';
/**
* Class representing a Redis cache.
*/
export class RedisCache {
/**
* Disconnects from the Redis server.
*/
async disconnect() {
await this.client.disconnect();
this.statusConnection = false;
}
/**
* Creates a new instance of RedisCache.
*/
constructor() {
this.logger.verbose('instance created');
process.on('beforeExit', async () => {
this.logger.verbose('instance destroyed');
if (this.statusConnection) {
@ -24,11 +35,19 @@ export class RedisCache {
private instanceName: string;
private redisEnv: Redis;
/**
* Sets the reference for the Redis instance.
* @param {string} reference - The reference to set.
*/
public set reference(reference: string) {
this.logger.verbose('set reference: ' + reference);
this.instanceName = reference;
}
/**
* Connects to the Redis server.
* @param {Redis} redisEnv - The Redis configuration.
*/
public async connect(redisEnv: Redis) {
this.logger.verbose('connecting');
this.client = createClient({ url: redisEnv.URI });
@ -41,6 +60,10 @@ export class RedisCache {
private readonly logger = new Logger(RedisCache.name);
private client: RedisClientType;
/**
* Retrieves keys for the Redis instance.
* @returns {Promise<string[]>} An array of keys.
*/
public async instanceKeys(): Promise<string[]> {
try {
this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
@ -50,6 +73,11 @@ export class RedisCache {
}
}
/**
* Checks if a specific key exists.
* @param {string} key - The key to check.
* @returns {Promise<boolean>} `true` if the key exists, otherwise `false`.
*/
public async keyExists(key?: string) {
if (key) {
this.logger.verbose('keyExists: ' + key);
@ -59,6 +87,12 @@ export class RedisCache {
return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
}
/**
* Writes data to Redis cache.
* @param {string} field - The field to write data to.
* @param {any} data - The data to write.
* @returns {Promise<boolean>} `true` if the write is successful, otherwise `false`.
*/
public async writeData(field: string, data: any) {
try {
this.logger.verbose('writeData: ' + field);
@ -70,6 +104,11 @@ export class RedisCache {
}
}
/**
* Reads data from Redis cache.
* @param {string} field - The field to read data from.
* @returns {Promise<any | null>} The data if found, otherwise `null`.
*/
public async readData(field: string) {
try {
this.logger.verbose('readData: ' + field);
@ -87,6 +126,11 @@ export class RedisCache {
}
}
/**
* Removes data from Redis cache.
* @param {string} field - The field to remove data from.
* @returns {Promise<boolean>} `true` if the removal is successful, otherwise `false`.
*/
public async removeData(field: string) {
try {
this.logger.verbose('removeData: ' + field);
@ -96,6 +140,11 @@ export class RedisCache {
}
}
/**
* Deletes all data associated with the Redis instance.
* @param {string} hash - The hash to delete, defaults to the instance name.
* @returns {Promise<boolean>} `true` if the deletion is successful, otherwise `false`.
*/
public async delAll(hash?: string) {
try {
this.logger.verbose('instance delAll: ' + hash);

View File

@ -4,23 +4,35 @@ import { Server as SocketIO } from 'socket.io';
import { configService, Cors, Websocket } from '../config/env.config';
import { Logger } from '../config/logger.config';
// Create a logger instance specifically for socket-related logs.
const logger = new Logger('Socket');
// Declare the Socket.IO instance.
let io: SocketIO;
const cors = configService.get<Cors>('CORS').ORIGIN;
// Get the allowed origins from the configuration.
const corsOrigins = configService.get<Cors>('CORS').ORIGIN;
export const initIO = (httpServer: Server) => {
/**
* Initialize Socket.IO with the provided HTTP server.
* @param {Server} httpServer - The HTTP server to attach Socket.IO to.
* @returns {SocketIO | null} The Socket.IO instance if enabled, or null if disabled.
*/
export const initIO = (httpServer: Server): SocketIO | null => {
// Check if WebSocket is enabled in the configuration.
if (configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
// Create a new Socket.IO instance with CORS configuration.
io = new SocketIO(httpServer, {
cors: {
origin: cors,
origin: corsOrigins,
},
});
// Handle the 'connection' event when a user connects.
io.on('connection', (socket) => {
logger.info('User connected');
// Handle the 'disconnect' event when a user disconnects.
socket.on('disconnect', () => {
logger.info('User disconnected');
});
@ -29,12 +41,20 @@ export const initIO = (httpServer: Server) => {
logger.info('Socket.io initialized');
return io;
}
// WebSocket is disabled, return null.
return null;
};
/**
* Get the Socket.IO instance.
* @throws {Error} Throws an error if Socket.IO is not initialized.
* @returns {SocketIO} The initialized Socket.IO instance.
*/
export const getIO = (): SocketIO => {
logger.verbose('Getting Socket.io');
// If Socket.IO is not initialized, throw an error.
if (!io) {
logger.error('Socket.io not initialized');
throw new Error('Socket.io not initialized');

View File

@ -16,16 +16,27 @@ import { ServerUP } from './utils/server-up';
import { HttpStatus, router } from './whatsapp/routers/index.router';
import { waMonitor } from './whatsapp/whatsapp.module';
/**
* Initializes WhatsApp monitoring.
*/
function initWA() {
waMonitor.loadInstance();
}
/**
* Bootstrap the application.
*/
function bootstrap() {
const logger = new Logger('SERVER');
const app = express();
app.use(
cors({
/**
* Determine if the request origin is allowed by CORS.
* @param requestOrigin The origin of the incoming request.
* @param callback The callback function to invoke.
*/
origin(requestOrigin, callback) {
const { ORIGIN } = configService.get<Cors>('CORS');
if (ORIGIN.includes('*')) {
@ -53,12 +64,19 @@ function bootstrap() {
app.use('/', router);
app.use(
/**
* Error handling middleware.
* @param err The error object.
* @param req The Express request object.
* @param res The Express response object.
* @param next The next function to invoke.
*/
(err: Error, req: Request, res: Response, next: NextFunction) => {
if (err) {
const webhook = configService.get<Webhook>('WEBHOOK');
if (webhook.EVENTS.ERRORS_WEBHOOK && webhook.EVENTS.ERRORS_WEBHOOK != '' && webhook.EVENTS.ERRORS) {
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const tzoffset = new Date().getTimezoneOffset() * 60000; // offset in milliseconds
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
const now = localISOTime;
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
@ -98,6 +116,12 @@ function bootstrap() {
next();
},
/**
* 404 Not Found middleware.
* @param req The Express request object.
* @param res The Express response object.
* @param next The next function to invoke.
*/
(req: Request, res: Response, next: NextFunction) => {
const { method, url } = req;

View File

@ -8,22 +8,38 @@ import { configService, SslConf } from '../config/env.config';
export class ServerUP {
static #app: Express;
/**
* Set the Express application instance.
* @param {Express} e - The Express application instance.
*/
static set app(e: Express) {
this.#app = e;
}
/**
* Get an HTTPS server instance with SSL configuration.
* @returns {https.Server} An HTTPS server instance.
*/
static get https() {
// Retrieve SSL certificate and private key paths from configuration.
const { FULLCHAIN, PRIVKEY } = configService.get<SslConf>('SSL_CONF');
// Create an HTTPS server using the SSL certificate and private key.
return https.createServer(
{
cert: readFileSync(FULLCHAIN),
key: readFileSync(PRIVKEY),
cert: readFileSync(FULLCHAIN), // Read SSL certificate file.
key: readFileSync(PRIVKEY), // Read private key file.
},
ServerUP.#app,
);
}
/**
* Get an HTTP server instance.
* @returns {http.Server} An HTTP server instance.
*/
static get http() {
// Create an HTTP server using the Express application instance.
return http.createServer(ServerUP.#app);
}
}

View File

@ -11,17 +11,25 @@ import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
import { dbserver } from '../libs/db.connect';
/**
* Provides a function to handle AuthenticationState and credentials using a MongoDB collection.
* @param {string} coll - The name of the MongoDB collection.
* @returns {Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }>} An object with AuthenticationState and saveCreds function.
*/
export async function useMultiFileAuthStateDb(
coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateDb.name);
// Get the MongoDB client from the database server connection.
const client = dbserver.getClient();
// Construct the collection name based on the database prefix.
const collection = client
.db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
.collection(coll);
// Helper function to write data to the MongoDB collection.
const writeData = async (data: any, key: string): Promise<any> => {
try {
await client.connect();
@ -33,6 +41,7 @@ export async function useMultiFileAuthStateDb(
}
};
// Helper function to read data from the MongoDB collection.
const readData = async (key: string): Promise<any> => {
try {
await client.connect();
@ -44,6 +53,7 @@ export async function useMultiFileAuthStateDb(
}
};
// Helper function to remove data from the MongoDB collection.
const removeData = async (key: string) => {
try {
await client.connect();
@ -53,6 +63,7 @@ export async function useMultiFileAuthStateDb(
}
};
// Initialize AuthenticationCreds using stored or default values.
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return {
@ -60,8 +71,6 @@ export async function useMultiFileAuthStateDb(
creds,
keys: {
get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all(
ids.map(async (id) => {
@ -90,6 +99,7 @@ export async function useMultiFileAuthStateDb(
},
},
},
// Save the credentials to the MongoDB collection.
saveCreds: async () => {
return writeData(creds, 'creds');
},

View File

@ -9,6 +9,11 @@ import {
import { Logger } from '../config/logger.config';
import { RedisCache } from '../libs/redis.client';
/**
* Provides a function to handle AuthenticationState and credentials using a Redis cache.
* @param {RedisCache} cache - The RedisCache instance to store and retrieve data.
* @returns {Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }>} An object with AuthenticationState and saveCreds function.
*/
export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
state: AuthenticationState;
saveCreds: () => Promise<void>;
@ -27,11 +32,12 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
try {
return await cache.readData(key);
} catch (error) {
logger.error({ readData: 'writeData', error });
logger.error({ readData: 'readData', error });
return;
}
};
// Helper function to remove data from the Redis cache.
const removeData = async (key: string) => {
try {
return await cache.removeData(key);
@ -40,6 +46,7 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
}
};
// Initialize AuthenticationCreds using stored or default values.
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return {
@ -77,6 +84,7 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
},
},
},
// Save the credentials to the Redis cache.
saveCreds: async () => {
return await writeData(creds, 'creds');
},

View File

@ -1,14 +1,20 @@
import { JSONSchema7, JSONSchema7Definition } from 'json-schema';
import { v4 } from 'uuid';
/**
* Represents a utility function to generate JSON schema for non-empty properties.
*
* @param {string[]} propertyNames - The names of properties to check for non-emptiness.
* @returns {JSONSchema7} A JSON schema with non-empty property checks.
*/
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
@ -21,6 +27,11 @@ const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
};
// Instance Schema
/**
* JSON schema for an instance with properties like instanceName, webhook, and more.
*
* @type {JSONSchema7}
*/
export const instanceNameSchema: JSONSchema7 = {
$id: v4(),
type: 'object',

View File

@ -11,6 +11,9 @@ import { InstanceDto } from '../dto/instance.dto';
import { RepositoryBroker } from '../repository/repository.manager';
import { WAMonitoringService } from './monitor.service';
/**
* Represents the payload of a JWT token.
*/
export type JwtPayload = {
instanceName: string;
apiName: string;
@ -19,19 +22,36 @@ export type JwtPayload = {
tokenId: string;
};
/**
* Represents the structure of an old JWT token.
*/
export class OldToken {
oldToken: string;
}
/**
* Service responsible for authentication-related operations.
*/
export class AuthService {
/**
* Creates an instance of AuthService.
* @param configService The configuration service.
* @param waMonitor The monitoring service for WhatsApp instances.
* @param repository The repository manager for database operations.
*/
constructor(
private readonly configService: ConfigService,
private readonly waMonitor: WAMonitoringService,
private readonly repository: RepositoryBroker,
) {}
) { }
private readonly logger = new Logger(AuthService.name);
/**
* Generates a JWT token for a given instance.
* @param instance The instance DTO for which to generate the token.
* @returns An object containing the generated JWT token.
* @throws BadRequestException if an error occurs during token generation.
*/
private async jwt(instance: InstanceDto) {
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
const token = sign(
@ -61,6 +81,13 @@ export class AuthService {
return { jwt: token };
}
/**
* Generates an API key for a given instance.
* @param instance The instance DTO for which to generate the API key.
* @param token (Optional) An existing API key to use.
* @returns An object containing the generated or defined API key.
* @throws BadRequestException if an error occurs during API key generation.
*/
private async apikey(instance: InstanceDto, token?: string) {
const apikey = token ? token : v4().toUpperCase();
@ -81,6 +108,11 @@ export class AuthService {
return { apikey };
}
/**
* Checks for the existence of a duplicate token among instances.
* @param token The token to check for duplication.
* @returns `true` if the token is not duplicated among instances, otherwise throws a BadRequestException.
*/
public async checkDuplicateToken(token: string) {
const instances = await this.waMonitor.instanceInfo();
@ -97,6 +129,12 @@ export class AuthService {
return true;
}
/**
* Generates an authentication hash (JWT token or API key) based on the authentication type.
* @param instance The instance DTO for which to generate the hash.
* @param token (Optional) An existing token to use (for API key generation).
* @returns An object containing the generated authentication hash (JWT token or API key).
*/
public async generateHash(instance: InstanceDto, token?: string) {
const options = this.configService.get<Auth>('AUTHENTICATION');
@ -105,6 +143,12 @@ export class AuthService {
return (await this[options.TYPE](instance, token)) as { jwt: string } | { apikey: string };
}
/**
* Refreshes a JWT token based on an old JWT token.
* @param oldToken An old JWT token to refresh.
* @returns An object containing the refreshed JWT token and instanceName.
* @throws BadRequestException if the oldToken is invalid or an error occurs during token refresh.
*/
public async refreshToken({ oldToken }: OldToken) {
this.logger.verbose('refreshing token');

View File

@ -24,41 +24,95 @@ import {
import { RepositoryBroker } from '../repository/repository.manager';
import { WAStartupService } from './whatsapp.service';
/**
* Represents a service for monitoring WhatsApp instances.
*/
export class WAMonitoringService {
/**
* Creates an instance of WAMonitoringService.
* @param {EventEmitter2} eventEmitter - Event emitter for handling events.
* @param {ConfigService} configService - Configuration service.
* @param {RepositoryBroker} repository - Repository broker for database operations.
* @param {RedisCache} cache - Redis cache for storing data.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly configService: ConfigService,
private readonly repository: RepositoryBroker,
private readonly cache: RedisCache,
) {
/**
* Logger instance for logging service activities.
* @private
* @type {Logger}
*/
this.logger.verbose('instance created');
// Initialize service functionalities
this.removeInstance();
this.noConnection();
this.delInstanceFiles();
// Load configuration settings
Object.assign(this.db, configService.get<Database>('DATABASE'));
Object.assign(this.redis, configService.get<Redis>('REDIS'));
// Initialize the MongoDB database instance
this.dbInstance = this.db.ENABLED
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
: undefined;
}
/**
* Configuration settings for the database.
* @private
* @type {Partial<Database>}
*/
private readonly db: Partial<Database> = {};
/**
* Configuration settings for Redis.
* @private
* @type {Partial<Redis>}
*/
private readonly redis: Partial<Redis> = {};
/**
* Database instance for instance management.
* @private
* @type {Db}
*/
private dbInstance: Db;
/**
* Database store connection.
* @private
* @type {Db}
*/
private dbStore = dbserver;
/**
* Logger instance for logging service activities.
* @private
* @type {Logger}
*/
private readonly logger = new Logger(WAMonitoringService.name);
/**
* A dictionary of WhatsApp instances being monitored.
* @public
* @type {Record<string, WAStartupService>}
*/
public readonly waInstances: Record<string, WAStartupService> = {};
/**
* Initiates a timer to remove an instance after a specific time of inactivity.
* @param {string} instance - The name of the instance to monitor.
*/
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`);
this.logger.verbose(`Instance "${instance}" doesn't have a connection, will be removed in ${time} minutes`);
setTimeout(async () => {
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
@ -75,67 +129,74 @@ export class WAMonitoringService {
}, 1000 * 60 * time);
}
}
/* ocultado por francis inicio
public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info');
/* ocultado por francis inicio
public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info');
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const instances: any[] = await Promise.all(
Object.entries(this.waInstances).map(async ([key, value]) => {
const status = value?.connectionStatus?.state || 'unknown';
const instances: any[] = await Promise.all(
Object.entries(this.waInstances).map(async ([key, value]) => {
const status = value?.connectionStatus?.state || 'unknown';
if (status === 'unknown') {
return null;
}
if (status === 'open') {
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
}
const instanceData: any = {
instance: {
instanceName: key,
owner: value.wuid,
profileName: (await value.getProfileName()) || 'not loaded',
profilePictureUrl: value.profilePictureUrl,
profileStatus: (await value.getProfileStatus()) || '',
status: status,
},
};
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance.serverUrl = urlServer;
instanceData.instance.apikey = (await this.repository.auth.find(key))?.apikey;
const findChatwoot = await this.waInstances[key].findChatwoot();
if (findChatwoot && findChatwoot.enabled) {
instanceData.instance.chatwoot = {
...findChatwoot,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`,
};
if (status === 'unknown') {
return null;
}
}
return instanceData;
}),
).then((results) => results.filter((instance) => instance !== null));
if (status === 'open') {
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
}
this.logger.verbose('return instance info: ' + instances.length);
const instanceData: any = {
instance: {
instanceName: key,
owner: value.wuid,
profileName: (await value.getProfileName()) || 'not loaded',
profilePictureUrl: value.profilePictureUrl,
profileStatus: (await value.getProfileStatus()) || '',
status: status,
},
};
if (instanceName) {
const instance = instances.find((i) => i.instance.instanceName === instanceName);
return instance || [];
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance.serverUrl = urlServer;
instanceData.instance.apikey = (await this.repository.auth.find(key))?.apikey;
const findChatwoot = await this.waInstances[key].findChatwoot();
if (findChatwoot && findChatwoot.enabled) {
instanceData.instance.chatwoot = {
...findChatwoot,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`,
};
}
}
return instanceData;
}),
).then((results) => results.filter((instance) => instance !== null));
this.logger.verbose('return instance info: ' + instances.length);
if (instanceName) {
const instance = instances.find((i) => i.instance.instanceName === instanceName);
return instance || [];
}
return instances;
}
return instances;
}
ocultado por francis fim */
ocultado por francis fim */
// inserido por francis inicio
// inserido por francis inicio
public async instanceInfo(instanceName?: string) {
/**
* Retrieves information about WhatsApp instances, including details about the connection, owner, and status.
* @param instanceName The name of the WhatsApp instance to retrieve information for. Optional.
* @returns An array of objects containing information about WhatsApp instances.
* @throws NotFoundException If `instanceName` is specified, and the instance is not found.
*/
public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info');
if (instanceName && !this.waInstances[instanceName]) {
throw new NotFoundException(`Instance "${instanceName}" not found`);
@ -212,8 +273,7 @@ public async instanceInfo(instanceName?: string) {
// inserido por francis fim
// inserido por francis fim
@ -221,6 +281,9 @@ public async instanceInfo(instanceName?: string) {
/**
* Initializes a cron job to delete instance files at regular intervals.
*/
private delInstanceFiles() {
this.logger.verbose('cron to delete instance files started');
setInterval(async () => {
@ -255,6 +318,10 @@ public async instanceInfo(instanceName?: string) {
}, 3600 * 1000 * 2);
}
/**
* Cleans data for a specific WhatsApp instance, including the database, cache, or files, depending on the configuration.
* @param instanceName The name of the WhatsApp instance to clean up.
*/
public async cleaningUp(instanceName: string) {
this.logger.verbose('cleaning up instance: ' + instanceName);
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
@ -278,6 +345,11 @@ public async instanceInfo(instanceName?: string) {
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
}
/**
* Cleans storage files for a specific WhatsApp instance, including messages, contacts, etc.
*
* @param instanceName The name of the WhatsApp instance to clean storage files for.
*/
public async cleaningStoreFiles(instanceName: string) {
if (!this.db.ENABLED) {
this.logger.verbose('cleaning store files instance: ' + instanceName);
@ -314,7 +386,9 @@ public async instanceInfo(instanceName?: string) {
return;
}
/**
* Loads WhatsApp instances based on storage settings, such as Redis, a database, or local files.
*/
public async loadInstance() {
this.logger.verbose('Loading instances');
@ -330,7 +404,11 @@ public async instanceInfo(instanceName?: string) {
this.logger.error(error);
}
}
/**
* Configures a new WhatsApp instance and connects it to the WhatsApp service.
*
* @param name The name of the WhatsApp instance to configure.
*/
private async setInstance(name: string) {
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
instance.instanceName = name;
@ -394,7 +472,11 @@ public async instanceInfo(instanceName?: string) {
}),
);
}
/**
* Removes a WhatsApp instance from memory and performs associated data cleanup.
*
* @param instanceName The name of the WhatsApp instance to remove.
*/
private removeInstance() {
this.eventEmitter.on('remove.instance', async (instanceName: string) => {
this.logger.verbose('remove instance: ' + instanceName);
@ -423,7 +505,9 @@ public async instanceInfo(instanceName?: string) {
}
});
}
/**
* Checks for WhatsApp instances without a connection and takes appropriate actions, such as logout and connection closure.
*/
private noConnection() {
this.logger.verbose('checking instances without connection');
this.eventEmitter.on('no.connection', async (instanceName) => {

View File

@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-namespace */
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
/**
* Enumeration of various application events.
*/
export enum Events {
APPLICATION_STARTUP = 'application.startup',
QRCODE_UPDATED = 'qrcode.updated',
@ -28,13 +30,23 @@ export enum Events {
CHAMA_AI_ACTION = 'chama-ai.action',
}
/**
* Namespace containing various WhatsApp-related types.
*/
export declare namespace wa {
/**
* Represents a QR code for pairing with WhatsApp.
*/
export type QrCode = {
count?: number;
pairingCode?: string;
base64?: string;
code?: string;
};
/**
* Represents information about a WhatsApp instance.
*/
export type Instance = {
qrcode?: QrCode;
pairingCode?: string;
@ -45,6 +57,9 @@ export declare namespace wa {
profilePictureUrl?: string;
};
/**
* Represents local webhook settings.
*/
export type LocalWebHook = {
enabled?: boolean;
url?: string;
@ -52,6 +67,9 @@ export declare namespace wa {
webhook_by_events?: boolean;
};
/**
* Represents local Chatwoot settings.
*/
export type LocalChatwoot = {
enabled?: boolean;
account_id?: string;
@ -64,6 +82,9 @@ export declare namespace wa {
conversation_pending?: boolean;
};
/**
* Represents local settings.
*/
export type LocalSettings = {
reject_call?: boolean;
msg_call?: string;
@ -73,22 +94,34 @@ export declare namespace wa {
read_status?: boolean;
};
/**
* Represents local WebSocket settings.
*/
export type LocalWebsocket = {
enabled?: boolean;
events?: string[];
};
/**
* Represents local RabbitMQ settings.
*/
export type LocalRabbitmq = {
enabled?: boolean;
events?: string[];
};
/**
* Represents a session within a Typebot instance.
*/
type Session = {
remoteJid?: string;
sessionId?: string;
createdAt?: number;
};
/**
* Represents local Typebot settings.
*/
export type LocalTypebot = {
enabled?: boolean;
url?: string;
@ -101,11 +134,17 @@ export declare namespace wa {
sessions?: Session[];
};
/**
* Represents local proxy settings.
*/
export type LocalProxy = {
enabled?: boolean;
proxy?: string;
};
/**
* Represents local Chamaai settings.
*/
export type LocalChamaai = {
enabled?: boolean;
url?: string;
@ -114,17 +153,29 @@ export declare namespace wa {
answerByAudio?: boolean;
};
/**
* Represents the state of a connection with a WhatsApp instance.
*/
export type StateConnection = {
instance?: string;
state?: WAConnectionState | 'refused';
statusReason?: number;
};
/**
* Represents a status message type.
*/
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
}
/**
* Array of media message types.
*/
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage'];
/**
* Array of message subtype types.
*/
export const MessageSubtype = [
'ephemeralMessage',
'documentWithCaptionMessage',