diff --git a/package.json b/package.json index b6f78480..8b847e18 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "amqplib": "^0.10.3", "@aws-sdk/client-sqs": "^3.569.0", "axios": "^1.6.5", - "@whiskeysockets/baileys": "^6.7.2", + "@whiskeysockets/baileys": "6.6.0", "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", diff --git a/src/api/provider/sessions.ts b/src/api/provider/sessions.ts index d3e63291..6997d9ff 100644 --- a/src/api/provider/sessions.ts +++ b/src/api/provider/sessions.ts @@ -9,7 +9,7 @@ type ResponseProvider = Promise<[ResponseSuccess?, Error?]>; export class ProviderFiles { constructor(private readonly configService: ConfigService) { - this.baseUrl = `http://${this.config.HOST}:${this.config.PORT}/session`; + this.baseUrl = `http://${this.config.HOST}:${this.config.PORT}/session/${this.config.PREFIX}`; } private readonly logger = new Logger(ProviderFiles.name); @@ -18,8 +18,6 @@ export class ProviderFiles { private readonly config = Object.freeze(this.configService.get('PROVIDER')); - private readonly prefix = Object.freeze(this.configService.get('PROVIDER').PREFIX); - get isEnabled() { return !!this.config?.ENABLED; } @@ -30,7 +28,7 @@ export class ProviderFiles { baseURL: this.baseUrl, }); try { - const response = await client.options(`/${this.prefix}/ping`); + const response = await client.options('/ping'); if (!response?.data?.pong) { throw new Error('Offline file provider.'); } @@ -48,7 +46,7 @@ export class ProviderFiles { public async create(instance: string): ResponseProvider { try { - const response = await axios.post(`${this.baseUrl}/${this.prefix}`, { + const response = await axios.post(`${this.baseUrl}`, { instance, }); return [{ status: response.status, data: response?.data }]; @@ -65,7 +63,7 @@ export class ProviderFiles { public async write(instance: string, key: string, data: any): ResponseProvider { try { - const response = await axios.post(`${this.baseUrl}/${this.prefix}/${instance}/${key}`, data); + const response = await axios.post(`${this.baseUrl}/${instance}/${key}`, data); return [{ status: response.status, data: response?.data }]; } catch (error) { return [ @@ -80,7 +78,7 @@ export class ProviderFiles { public async read(instance: string, key: string): ResponseProvider { try { - const response = await axios.get(`${this.baseUrl}/${this.prefix}/${instance}/${key}`); + const response = await axios.get(`${this.baseUrl}/${instance}/${key}`); return [{ status: response.status, data: response?.data }]; } catch (error) { return [ @@ -95,7 +93,7 @@ export class ProviderFiles { public async delete(instance: string, key: string): ResponseProvider { try { - const response = await axios.delete(`${this.baseUrl}/${this.prefix}/${instance}/${key}`); + const response = await axios.delete(`${this.baseUrl}/${instance}/${key}`); return [{ status: response.status, data: response?.data }]; } catch (error) { return [ @@ -110,7 +108,7 @@ export class ProviderFiles { public async allInstances(): ResponseProvider { try { - const response = await axios.get(`${this.baseUrl}/${this.prefix}/list-instances`); + const response = await axios.get(`${this.baseUrl}/list-instances`); return [{ status: response.status, data: response?.data as string[] }]; } catch (error) { return [ @@ -125,7 +123,7 @@ export class ProviderFiles { public async removeSession(instance: string): ResponseProvider { try { - const response = await axios.delete(`${this.baseUrl}/${this.prefix}/${instance}`); + const response = await axios.delete(`${this.baseUrl}/${instance}`); return [{ status: response.status, data: response?.data }]; } catch (error) { return [ diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index 4e9ef4f6..911f4c66 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -146,7 +146,7 @@ export class BaileysStartupService extends ChannelStartupService { this.instance.qrcode = { count: 0 }; this.mobile = false; this.recoveringMessages(); - this.authStateProvider = new AuthStateProvider(this.configService, this.providerFiles); + this.authStateProvider = new AuthStateProvider(this.providerFiles); } private authStateProvider: AuthStateProvider; @@ -1486,7 +1486,7 @@ export class BaileysStartupService extends ChannelStartupService { }); const chat = chats.find((c) => c.id === data.association.chatId); if (chat) { - let labels = [...chat.labels]; + let labels = [...chat?.labels]; if (data.type === 'remove') { labels = labels.filter((label) => label !== data.association.labelId); } else if (data.type === 'add') { diff --git a/src/api/services/monitor.service.ts b/src/api/services/monitor.service.ts index 51ccc201..c197b843 100644 --- a/src/api/services/monitor.service.ts +++ b/src/api/services/monitor.service.ts @@ -5,7 +5,15 @@ import { Db } from 'mongodb'; import { Collection } from 'mongoose'; import { join } from 'path'; -import { Auth, CacheConf, ConfigService, Database, DelInstance, HttpServer } from '../../config/env.config'; +import { + Auth, + CacheConf, + ConfigService, + Database, + DelInstance, + HttpServer, + ProviderSession, +} from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config'; import { NotFoundException } from '../../exceptions'; @@ -60,6 +68,8 @@ export class WAMonitoringService { private readonly logger = new Logger(WAMonitoringService.name); public readonly waInstances: Record = {}; + private readonly providerSession = Object.freeze(this.configService.get('PROVIDER')); + public delInstanceTime(instance: string) { const time = this.configService.get('DEL_INSTANCE'); if (typeof time === 'number' && time > 0) { @@ -259,13 +269,21 @@ export class WAMonitoringService { } this.logger.verbose('cleaning up instance in files: ' + instanceName); - rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + if (this.providerSession?.ENABLED) { + await this.providerFiles.removeSession(instanceName); + } else { + rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + } } public async cleaningStoreFiles(instanceName: string) { if (!this.db.ENABLED) { this.logger.verbose('cleaning store files instance: ' + instanceName); - rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + if (this.providerSession?.ENABLED) { + await this.providerFiles.removeSession(instanceName); + } else { + rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + } execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`); execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`); @@ -307,7 +325,9 @@ export class WAMonitoringService { this.logger.verbose('Loading instances'); try { - if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) { + if (this.providerSession.ENABLED) { + await this.loadInstancesFromProvider(); + } else if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) { await this.loadInstancesFromRedis(); } else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { await this.loadInstancesFromDatabase(); @@ -405,6 +425,18 @@ export class WAMonitoringService { } } + private async loadInstancesFromProvider() { + this.logger.verbose('Provider in files enabled'); + const [instances] = await this.providerFiles.allInstances(); + + if (!instances?.data) { + this.logger.verbose('No instances found'); + return; + } + + await Promise.all(instances?.data?.map(async (instanceName: string) => this.setInstance(instanceName))); + } + private async loadInstancesFromFiles() { this.logger.verbose('Store in files enabled'); const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); diff --git a/src/utils/use-multi-file-auth-state-provider-files.ts b/src/utils/use-multi-file-auth-state-provider-files.ts index 89ea4767..54100463 100644 --- a/src/utils/use-multi-file-auth-state-provider-files.ts +++ b/src/utils/use-multi-file-auth-state-provider-files.ts @@ -1,9 +1,9 @@ /** * ┌──────────────────────────────────────────────────────────────────────────────┐ * │ @author jrCleber │ - * │ @filename use-multi-file-auth-state-redis-db.ts │ + * │ @filename use-multi-file-auth-state-provider-files.ts │ * │ Developed by: Cleber Wilson │ - * │ Creation date: Apr 09, 2023 │ + * │ Creation date: May 31, 2024 │ * │ Contact: contato@codechat.dev │ * ├──────────────────────────────────────────────────────────────────────────────┤ * │ @copyright © Cleber Wilson 2023. All rights reserved. │ @@ -45,13 +45,12 @@ import { import { isNotEmpty } from 'class-validator'; import { ProviderFiles } from '../api/provider/sessions'; -import { ConfigService } from '../config/env.config'; import { Logger } from '../config/logger.config'; export type AuthState = { state: AuthenticationState; saveCreds: () => Promise }; export class AuthStateProvider { - constructor(private readonly configService: ConfigService, private readonly providerFiles: ProviderFiles) {} + constructor(private readonly providerFiles: ProviderFiles) {} private readonly logger = new Logger(AuthStateProvider.name); @@ -68,7 +67,7 @@ export class AuthStateProvider { data: json, }); if (error) { - this.logger.error([error?.message, error?.stack]); + this.logger.error(['writeData', error?.message, error?.stack]); return; } return response; @@ -77,7 +76,7 @@ export class AuthStateProvider { const readData = async (key: string): Promise => { const [response, error] = await this.providerFiles.read(instance, key); if (error) { - this.logger.error([error?.message, error?.stack]); + this.logger.error(['readData', error?.message, error?.stack]); return; } if (isNotEmpty(response?.data)) { @@ -88,7 +87,7 @@ export class AuthStateProvider { const removeData = async (key: string) => { const [response, error] = await this.providerFiles.delete(instance, key); if (error) { - this.logger.error([error?.message, error?.stack]); + this.logger.error(['removeData', error?.message, error?.stack]); return; }