mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-23 17:08:44 -06:00
Merge branch 'release/1.8.1'
This commit is contained in:
commit
bd069200ea
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,3 +1,13 @@
|
|||||||
|
# 1.8.1 (2024-06-08 21:32)
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* New method of saving sessions to a file using worker, made in partnership with [codechat](https://github.com/code-chat-br/whatsapp-api)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Correction of variables breaking lines in typebot
|
||||||
|
|
||||||
# 1.8.0 (2024-05-27 16:10)
|
# 1.8.0 (2024-05-27 16:10)
|
||||||
|
|
||||||
### Feature
|
### Feature
|
||||||
@ -7,12 +17,14 @@
|
|||||||
* Build in docker for linux/amd64, linux/arm64 platforms
|
* Build in docker for linux/amd64, linux/arm64 platforms
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Correction in message formatting when generated by AI as markdown in typebot
|
* Correction in message formatting when generated by AI as markdown in typebot
|
||||||
* Security fix in fetch instance with client key when not connected to mongodb
|
* Security fix in fetch instance with client key when not connected to mongodb
|
||||||
|
|
||||||
# 1.7.5 (2024-05-21 08:50)
|
# 1.7.5 (2024-05-21 08:50)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Add merge_brazil_contacts function to solve nine digit in brazilian numbers
|
* Add merge_brazil_contacts function to solve nine digit in brazilian numbers
|
||||||
* Optimize ChatwootService method for updating contact
|
* Optimize ChatwootService method for updating contact
|
||||||
* Fix swagger auth
|
* Fix swagger auth
|
||||||
@ -24,6 +36,7 @@
|
|||||||
# 1.7.4 (2024-04-28 09:46)
|
# 1.7.4 (2024-04-28 09:46)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Adjusts in proxy on fetchAgent
|
* Adjusts in proxy on fetchAgent
|
||||||
* Recovering messages lost with redis cache
|
* Recovering messages lost with redis cache
|
||||||
* Log when init redis cache service
|
* Log when init redis cache service
|
||||||
@ -34,6 +47,7 @@
|
|||||||
# 1.7.3 (2024-04-18 12:07)
|
# 1.7.3 (2024-04-18 12:07)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Revert fix audio encoding
|
* Revert fix audio encoding
|
||||||
* Recovering messages lost with redis cache
|
* Recovering messages lost with redis cache
|
||||||
* Adjusts in redis for save instances
|
* Adjusts in redis for save instances
|
||||||
|
@ -33,10 +33,7 @@ CLEAN_STORE_CHATS=true
|
|||||||
|
|
||||||
# Permanent data storage
|
# Permanent data storage
|
||||||
DATABASE_ENABLED=false
|
DATABASE_ENABLED=false
|
||||||
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin &
|
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
||||||
readPreference=primary &
|
|
||||||
ssl=false &
|
|
||||||
directConnection=true
|
|
||||||
DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker
|
DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker
|
||||||
|
|
||||||
# Choose the data you want to save in the application's database or store
|
# Choose the data you want to save in the application's database or store
|
||||||
@ -137,7 +134,7 @@ CONFIG_SESSION_PHONE_NAME=Chrome
|
|||||||
|
|
||||||
# Set qrcode display limit
|
# Set qrcode display limit
|
||||||
QRCODE_LIMIT=30
|
QRCODE_LIMIT=30
|
||||||
QRCODE_COLOR=#198754
|
QRCODE_COLOR='#198754'
|
||||||
|
|
||||||
# old | latest
|
# old | latest
|
||||||
TYPEBOT_API_VERSION=latest
|
TYPEBOT_API_VERSION=latest
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "evolution-api",
|
"name": "evolution-api",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"description": "Rest api for communication with WhatsApp",
|
"description": "Rest api for communication with WhatsApp",
|
||||||
"main": "./dist/src/main.js",
|
"main": "./dist/src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -49,7 +49,7 @@
|
|||||||
"amqplib": "^0.10.3",
|
"amqplib": "^0.10.3",
|
||||||
"@aws-sdk/client-sqs": "^3.569.0",
|
"@aws-sdk/client-sqs": "^3.569.0",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
"@whiskeysockets/baileys": "^6.7.2",
|
"@whiskeysockets/baileys": "6.7.4",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
@ -12,6 +12,7 @@ import { RabbitmqService } from '../integrations/rabbitmq/services/rabbitmq.serv
|
|||||||
import { SqsService } from '../integrations/sqs/services/sqs.service';
|
import { SqsService } from '../integrations/sqs/services/sqs.service';
|
||||||
import { TypebotService } from '../integrations/typebot/services/typebot.service';
|
import { TypebotService } from '../integrations/typebot/services/typebot.service';
|
||||||
import { WebsocketService } from '../integrations/websocket/services/websocket.service';
|
import { WebsocketService } from '../integrations/websocket/services/websocket.service';
|
||||||
|
import { ProviderFiles } from '../provider/sessions';
|
||||||
import { RepositoryBroker } from '../repository/repository.manager';
|
import { RepositoryBroker } from '../repository/repository.manager';
|
||||||
import { AuthService, OldToken } from '../services/auth.service';
|
import { AuthService, OldToken } from '../services/auth.service';
|
||||||
import { CacheService } from '../services/cache.service';
|
import { CacheService } from '../services/cache.service';
|
||||||
@ -42,7 +43,8 @@ export class InstanceController {
|
|||||||
private readonly proxyService: ProxyController,
|
private readonly proxyService: ProxyController,
|
||||||
private readonly cache: CacheService,
|
private readonly cache: CacheService,
|
||||||
private readonly chatwootCache: CacheService,
|
private readonly chatwootCache: CacheService,
|
||||||
private readonly messagesLostCache: CacheService,
|
private readonly baileysCache: CacheService,
|
||||||
|
private readonly providerFiles: ProviderFiles,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private readonly logger = new Logger(InstanceController.name);
|
private readonly logger = new Logger(InstanceController.name);
|
||||||
@ -110,7 +112,8 @@ export class InstanceController {
|
|||||||
this.repository,
|
this.repository,
|
||||||
this.cache,
|
this.cache,
|
||||||
this.chatwootCache,
|
this.chatwootCache,
|
||||||
this.messagesLostCache,
|
this.baileysCache,
|
||||||
|
this.providerFiles,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
instance = new BaileysStartupService(
|
instance = new BaileysStartupService(
|
||||||
@ -119,7 +122,8 @@ export class InstanceController {
|
|||||||
this.repository,
|
this.repository,
|
||||||
this.cache,
|
this.cache,
|
||||||
this.chatwootCache,
|
this.chatwootCache,
|
||||||
this.messagesLostCache,
|
this.baileysCache,
|
||||||
|
this.providerFiles,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,7 +753,7 @@ export class InstanceController {
|
|||||||
this.logger.verbose('deleting instance: ' + instanceName);
|
this.logger.verbose('deleting instance: ' + instanceName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.waMonitor.waInstances[instanceName].sendDataWebhook(Events.INSTANCE_DELETE, {
|
this.waMonitor.waInstances[instanceName]?.sendDataWebhook(Events.INSTANCE_DELETE, {
|
||||||
instanceName,
|
instanceName,
|
||||||
instanceId: (await this.repository.auth.find(instanceName))?.instanceId,
|
instanceId: (await this.repository.auth.find(instanceName))?.instanceId,
|
||||||
});
|
});
|
||||||
|
@ -1092,6 +1092,10 @@ export class ChatwootService {
|
|||||||
return messageSent;
|
return messageSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'image' && parsedMedia && parsedMedia?.ext === '.gif') {
|
||||||
|
type = 'document';
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.verbose('send media to instance: ' + waInstance.instanceName);
|
this.logger.verbose('send media to instance: ' + waInstance.instanceName);
|
||||||
const data: SendMediaDto = {
|
const data: SendMediaDto = {
|
||||||
number: number,
|
number: number,
|
||||||
|
@ -525,10 +525,14 @@ export class TypebotService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.type === 'p') {
|
if (element.type === 'p' && element.type !== 'inline-variable') {
|
||||||
text = text.trim() + '\n';
|
text = text.trim() + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (element.type === 'inline-variable') {
|
||||||
|
text = text.trim();
|
||||||
|
}
|
||||||
|
|
||||||
if (element.type === 'ol') {
|
if (element.type === 'ol') {
|
||||||
text =
|
text =
|
||||||
'\n' +
|
'\n' +
|
||||||
@ -582,6 +586,8 @@ export class TypebotService {
|
|||||||
|
|
||||||
formattedText = formattedText.replace(/\*\*/g, '').replace(/__/, '').replace(/~~/, '').replace(/\n$/, '');
|
formattedText = formattedText.replace(/\*\*/g, '').replace(/__/, '').replace(/~~/, '').replace(/\n$/, '');
|
||||||
|
|
||||||
|
formattedText = formattedText.replace(/\n$/, '');
|
||||||
|
|
||||||
await instance.textMessage({
|
await instance.textMessage({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
options: {
|
options: {
|
||||||
|
150
src/api/provider/sessions.ts
Normal file
150
src/api/provider/sessions.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
import { Auth, ConfigService, ProviderSession } from '../../config/env.config';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
type ResponseSuccess = { status: number; data?: any };
|
||||||
|
type ResponseProvider = Promise<[ResponseSuccess?, Error?]>;
|
||||||
|
|
||||||
|
export class ProviderFiles {
|
||||||
|
constructor(private readonly configService: ConfigService) {
|
||||||
|
this.baseUrl = `http://${this.config.HOST}:${this.config.PORT}/session/${this.config.PREFIX}`;
|
||||||
|
this.globalApiToken = this.configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly logger = new Logger(ProviderFiles.name);
|
||||||
|
|
||||||
|
private baseUrl: string;
|
||||||
|
private globalApiToken: string;
|
||||||
|
|
||||||
|
private readonly config = Object.freeze(this.configService.get<ProviderSession>('PROVIDER'));
|
||||||
|
|
||||||
|
get isEnabled() {
|
||||||
|
return !!this.config?.ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onModuleInit() {
|
||||||
|
if (this.config.ENABLED) {
|
||||||
|
const url = `http://${this.config.HOST}:${this.config.PORT}`;
|
||||||
|
try {
|
||||||
|
const response = await axios.options(url + '/ping');
|
||||||
|
if (response?.data != 'pong') {
|
||||||
|
throw new Error('Offline file provider.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await axios.post(`${url}/session`, { group: this.config.PREFIX }, { headers: { apikey: this.globalApiToken } });
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(['Failed to connect to the file server', error?.message, error?.stack]);
|
||||||
|
const pid = process.pid;
|
||||||
|
execSync(`kill -9 ${pid}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onModuleDestroy() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public async create(instance: string): ResponseProvider {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${this.baseUrl}`,
|
||||||
|
{
|
||||||
|
instance,
|
||||||
|
},
|
||||||
|
{ headers: { apikey: this.globalApiToken } },
|
||||||
|
);
|
||||||
|
return [{ status: response.status, data: response?.data }];
|
||||||
|
} catch (error) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
status: error?.response?.status,
|
||||||
|
data: error?.response?.data,
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async write(instance: string, key: string, data: any): ResponseProvider {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(`${this.baseUrl}/${instance}/${key}`, data, {
|
||||||
|
headers: { apikey: this.globalApiToken },
|
||||||
|
});
|
||||||
|
return [{ status: response.status, data: response?.data }];
|
||||||
|
} catch (error) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
status: error?.response?.status,
|
||||||
|
data: error?.response?.data,
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async read(instance: string, key: string): ResponseProvider {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${this.baseUrl}/${instance}/${key}`, {
|
||||||
|
headers: { apikey: this.globalApiToken },
|
||||||
|
});
|
||||||
|
return [{ status: response.status, data: response?.data }];
|
||||||
|
} catch (error) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
status: error?.response?.status,
|
||||||
|
data: error?.response?.data,
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete(instance: string, key: string): ResponseProvider {
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(`${this.baseUrl}/${instance}/${key}`, {
|
||||||
|
headers: { apikey: this.globalApiToken },
|
||||||
|
});
|
||||||
|
return [{ status: response.status, data: response?.data }];
|
||||||
|
} catch (error) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
status: error?.response?.status,
|
||||||
|
data: error?.response?.data,
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async allInstances(): ResponseProvider {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${this.baseUrl}/list-instances`, { headers: { apikey: this.globalApiToken } });
|
||||||
|
return [{ status: response.status, data: response?.data as string[] }];
|
||||||
|
} catch (error) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
status: error?.response?.status,
|
||||||
|
data: error?.response?.data,
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeSession(instance: string): ResponseProvider {
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(`${this.baseUrl}/${instance}`, { headers: { apikey: this.globalApiToken } });
|
||||||
|
return [{ status: response.status, data: response?.data }];
|
||||||
|
} catch (error) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
status: error?.response?.status,
|
||||||
|
data: error?.response?.data,
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,7 @@ import {
|
|||||||
WebsocketModel,
|
WebsocketModel,
|
||||||
} from './models';
|
} from './models';
|
||||||
import { LabelModel } from './models/label.model';
|
import { LabelModel } from './models/label.model';
|
||||||
|
import { ProviderFiles } from './provider/sessions';
|
||||||
import { AuthRepository } from './repository/auth.repository';
|
import { AuthRepository } from './repository/auth.repository';
|
||||||
import { ChatRepository } from './repository/chat.repository';
|
import { ChatRepository } from './repository/chat.repository';
|
||||||
import { ContactRepository } from './repository/contact.repository';
|
import { ContactRepository } from './repository/contact.repository';
|
||||||
@ -108,7 +109,8 @@ export const repository = new RepositoryBroker(
|
|||||||
|
|
||||||
export const cache = new CacheService(new CacheEngine(configService, 'instance').getEngine());
|
export const cache = new CacheService(new CacheEngine(configService, 'instance').getEngine());
|
||||||
const chatwootCache = new CacheService(new CacheEngine(configService, ChatwootService.name).getEngine());
|
const chatwootCache = new CacheService(new CacheEngine(configService, ChatwootService.name).getEngine());
|
||||||
const messagesLostCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine());
|
const baileysCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine());
|
||||||
|
const providerFiles = new ProviderFiles(configService);
|
||||||
|
|
||||||
export const waMonitor = new WAMonitoringService(
|
export const waMonitor = new WAMonitoringService(
|
||||||
eventEmitter,
|
eventEmitter,
|
||||||
@ -116,7 +118,8 @@ export const waMonitor = new WAMonitoringService(
|
|||||||
repository,
|
repository,
|
||||||
cache,
|
cache,
|
||||||
chatwootCache,
|
chatwootCache,
|
||||||
messagesLostCache,
|
baileysCache,
|
||||||
|
providerFiles,
|
||||||
);
|
);
|
||||||
|
|
||||||
const authService = new AuthService(configService, waMonitor, repository);
|
const authService = new AuthService(configService, waMonitor, repository);
|
||||||
@ -167,7 +170,8 @@ export const instanceController = new InstanceController(
|
|||||||
proxyController,
|
proxyController,
|
||||||
cache,
|
cache,
|
||||||
chatwootCache,
|
chatwootCache,
|
||||||
messagesLostCache,
|
baileysCache,
|
||||||
|
providerFiles,
|
||||||
);
|
);
|
||||||
export const sendMessageController = new SendMessageController(waMonitor);
|
export const sendMessageController = new SendMessageController(waMonitor);
|
||||||
export const chatController = new ChatController(waMonitor);
|
export const chatController = new ChatController(waMonitor);
|
||||||
|
@ -55,12 +55,23 @@ import qrcode, { QRCodeToDataURLOptions } from 'qrcode';
|
|||||||
import qrcodeTerminal from 'qrcode-terminal';
|
import qrcodeTerminal from 'qrcode-terminal';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
|
|
||||||
import { CacheConf, ConfigService, ConfigSessionPhone, Database, Log, QrCode } from '../../../config/env.config';
|
import { CacheEngine } from '../../../cache/cacheengine';
|
||||||
|
import {
|
||||||
|
CacheConf,
|
||||||
|
ConfigService,
|
||||||
|
configService,
|
||||||
|
ConfigSessionPhone,
|
||||||
|
Database,
|
||||||
|
Log,
|
||||||
|
ProviderSession,
|
||||||
|
QrCode,
|
||||||
|
} from '../../../config/env.config';
|
||||||
import { INSTANCE_DIR } from '../../../config/path.config';
|
import { INSTANCE_DIR } from '../../../config/path.config';
|
||||||
import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../../exceptions';
|
import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../../exceptions';
|
||||||
import { dbserver } from '../../../libs/db.connect';
|
import { dbserver } from '../../../libs/db.connect';
|
||||||
import { makeProxyAgent } from '../../../utils/makeProxyAgent';
|
import { makeProxyAgent } from '../../../utils/makeProxyAgent';
|
||||||
import { useMultiFileAuthStateDb } from '../../../utils/use-multi-file-auth-state-db';
|
import { useMultiFileAuthStateDb } from '../../../utils/use-multi-file-auth-state-db';
|
||||||
|
import { AuthStateProvider } from '../../../utils/use-multi-file-auth-state-provider-files';
|
||||||
import { useMultiFileAuthStateRedisDb } from '../../../utils/use-multi-file-auth-state-redis-db';
|
import { useMultiFileAuthStateRedisDb } from '../../../utils/use-multi-file-auth-state-redis-db';
|
||||||
import {
|
import {
|
||||||
ArchiveChatDto,
|
ArchiveChatDto,
|
||||||
@ -114,12 +125,15 @@ import { SettingsRaw } from '../../models';
|
|||||||
import { ChatRaw } from '../../models/chat.model';
|
import { ChatRaw } from '../../models/chat.model';
|
||||||
import { ContactRaw } from '../../models/contact.model';
|
import { ContactRaw } from '../../models/contact.model';
|
||||||
import { MessageRaw, MessageUpdateRaw } from '../../models/message.model';
|
import { MessageRaw, MessageUpdateRaw } from '../../models/message.model';
|
||||||
|
import { ProviderFiles } from '../../provider/sessions';
|
||||||
import { RepositoryBroker } from '../../repository/repository.manager';
|
import { RepositoryBroker } from '../../repository/repository.manager';
|
||||||
import { waMonitor } from '../../server.module';
|
import { waMonitor } from '../../server.module';
|
||||||
import { Events, MessageSubtype, TypeMediaMessage, wa } from '../../types/wa.types';
|
import { Events, MessageSubtype, TypeMediaMessage, wa } from '../../types/wa.types';
|
||||||
import { CacheService } from './../cache.service';
|
import { CacheService } from './../cache.service';
|
||||||
import { ChannelStartupService } from './../channel.service';
|
import { ChannelStartupService } from './../channel.service';
|
||||||
|
|
||||||
|
const groupMetadataCache = new CacheService(new CacheEngine(configService, 'groups').getEngine());
|
||||||
|
|
||||||
export class BaileysStartupService extends ChannelStartupService {
|
export class BaileysStartupService extends ChannelStartupService {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly configService: ConfigService,
|
public readonly configService: ConfigService,
|
||||||
@ -127,7 +141,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
public readonly repository: RepositoryBroker,
|
public readonly repository: RepositoryBroker,
|
||||||
public readonly cache: CacheService,
|
public readonly cache: CacheService,
|
||||||
public readonly chatwootCache: CacheService,
|
public readonly chatwootCache: CacheService,
|
||||||
public readonly messagesLostCache: CacheService,
|
public readonly baileysCache: CacheService,
|
||||||
|
private readonly providerFiles: ProviderFiles,
|
||||||
) {
|
) {
|
||||||
super(configService, eventEmitter, repository, chatwootCache);
|
super(configService, eventEmitter, repository, chatwootCache);
|
||||||
this.logger.verbose('BaileysStartupService initialized');
|
this.logger.verbose('BaileysStartupService initialized');
|
||||||
@ -135,8 +150,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
this.instance.qrcode = { count: 0 };
|
this.instance.qrcode = { count: 0 };
|
||||||
this.mobile = false;
|
this.mobile = false;
|
||||||
this.recoveringMessages();
|
this.recoveringMessages();
|
||||||
|
this.forceUpdateGroupMetadataCache();
|
||||||
|
|
||||||
|
this.authStateProvider = new AuthStateProvider(this.providerFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private authStateProvider: AuthStateProvider;
|
||||||
private readonly msgRetryCounterCache: CacheStore = new NodeCache();
|
private readonly msgRetryCounterCache: CacheStore = new NodeCache();
|
||||||
private readonly userDevicesCache: CacheStore = new NodeCache();
|
private readonly userDevicesCache: CacheStore = new NodeCache();
|
||||||
private endSession = false;
|
private endSession = false;
|
||||||
@ -153,9 +172,9 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) {
|
if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
this.messagesLostCache.keys().then((keys) => {
|
this.baileysCache.keys().then((keys) => {
|
||||||
keys.forEach(async (key) => {
|
keys.forEach(async (key) => {
|
||||||
const message = await this.messagesLostCache.get(key.split(':')[2]);
|
const message = await this.baileysCache.get(key.split(':')[2]);
|
||||||
|
|
||||||
if (message.messageStubParameters && message.messageStubParameters[0] === 'Message absent from node') {
|
if (message.messageStubParameters && message.messageStubParameters[0] === 'Message absent from node') {
|
||||||
this.logger.info('Message absent from node, retrying to send, key: ' + key.split(':')[2]);
|
this.logger.info('Message absent from node, retrying to send, key: ' + key.split(':')[2]);
|
||||||
@ -167,6 +186,23 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async forceUpdateGroupMetadataCache() {
|
||||||
|
if (
|
||||||
|
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
|
||||||
|
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
this.logger.verbose('Forcing update group metadata cache');
|
||||||
|
const groups = await this.fetchAllGroups({ getParticipants: 'false' });
|
||||||
|
|
||||||
|
for (const group of groups) {
|
||||||
|
await this.updateGroupMetadataCache(group.id);
|
||||||
|
}
|
||||||
|
}, 3600000);
|
||||||
|
}
|
||||||
|
|
||||||
public get connectionStatus() {
|
public get connectionStatus() {
|
||||||
this.logger.verbose('Getting connection status');
|
this.logger.verbose('Getting connection status');
|
||||||
return this.stateConnection;
|
return this.stateConnection;
|
||||||
@ -461,6 +497,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
const db = this.configService.get<Database>('DATABASE');
|
const db = this.configService.get<Database>('DATABASE');
|
||||||
const cache = this.configService.get<CacheConf>('CACHE');
|
const cache = this.configService.get<CacheConf>('CACHE');
|
||||||
|
|
||||||
|
const provider = this.configService.get<ProviderSession>('PROVIDER');
|
||||||
|
|
||||||
|
if (provider?.ENABLED) {
|
||||||
|
return await this.authStateProvider.authStateProvider(this.instance.name);
|
||||||
|
}
|
||||||
|
|
||||||
if (cache?.REDIS.ENABLED && cache?.REDIS.SAVE_INSTANCES) {
|
if (cache?.REDIS.ENABLED && cache?.REDIS.SAVE_INSTANCES) {
|
||||||
this.logger.info('Redis enabled');
|
this.logger.info('Redis enabled');
|
||||||
return await useMultiFileAuthStateRedisDb(this.instance.name, this.cache);
|
return await useMultiFileAuthStateRedisDb(this.instance.name, this.cache);
|
||||||
@ -628,12 +670,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('phoneNumber', phoneNumber);
|
|
||||||
|
|
||||||
const parsedPhoneNumber = parsePhoneNumber(phoneNumber);
|
const parsedPhoneNumber = parsePhoneNumber(phoneNumber);
|
||||||
|
|
||||||
console.log('parsedPhoneNumber', parsedPhoneNumber);
|
|
||||||
|
|
||||||
if (!parsedPhoneNumber?.isValid()) {
|
if (!parsedPhoneNumber?.isValid()) {
|
||||||
this.logger.error('Phone number invalid');
|
this.logger.error('Phone number invalid');
|
||||||
return;
|
return;
|
||||||
@ -655,7 +693,6 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
try {
|
try {
|
||||||
const response = await this.client.requestRegistrationCode(registration);
|
const response = await this.client.requestRegistrationCode(registration);
|
||||||
|
|
||||||
console.log('response', response);
|
|
||||||
if (['ok', 'sent'].includes(response?.status)) {
|
if (['ok', 'sent'].includes(response?.status)) {
|
||||||
this.logger.verbose('Registration code sent successfully');
|
this.logger.verbose('Registration code sent successfully');
|
||||||
|
|
||||||
@ -669,9 +706,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
public async receiveMobileCode(code: string) {
|
public async receiveMobileCode(code: string) {
|
||||||
await this.client
|
await this.client
|
||||||
.register(code.replace(/["']/g, '').trim().toLowerCase())
|
.register(code.replace(/["']/g, '').trim().toLowerCase())
|
||||||
.then(async (response) => {
|
.then(async () => {
|
||||||
this.logger.verbose('Registration code received successfully');
|
this.logger.verbose('Registration code received successfully');
|
||||||
console.log(response);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
@ -1105,15 +1141,15 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
if (received.messageStubParameters && received.messageStubParameters[0] === 'Message absent from node') {
|
if (received.messageStubParameters && received.messageStubParameters[0] === 'Message absent from node') {
|
||||||
this.logger.info('Recovering message lost');
|
this.logger.info('Recovering message lost');
|
||||||
|
|
||||||
await this.messagesLostCache.set(received.key.id, received);
|
await this.baileysCache.set(received.key.id, received);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const retryCache = (await this.messagesLostCache.get(received.key.id)) || null;
|
const retryCache = (await this.baileysCache.get(received.key.id)) || null;
|
||||||
|
|
||||||
if (retryCache) {
|
if (retryCache) {
|
||||||
this.logger.info('Recovered message lost');
|
this.logger.info('Recovered message lost');
|
||||||
await this.messagesLostCache.delete(received.key.id);
|
await this.baileysCache.delete(received.key.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -1402,6 +1438,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
this.logger.verbose('Sending data to webhook in event GROUPS_UPDATE');
|
this.logger.verbose('Sending data to webhook in event GROUPS_UPDATE');
|
||||||
this.sendDataWebhook(Events.GROUPS_UPDATE, groupMetadataUpdate);
|
this.sendDataWebhook(Events.GROUPS_UPDATE, groupMetadataUpdate);
|
||||||
|
|
||||||
|
groupMetadataUpdate.forEach((group) => {
|
||||||
|
if (isJidGroup(group.id)) {
|
||||||
|
this.updateGroupMetadataCache(group.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'group-participants.update': (participantsUpdate: {
|
'group-participants.update': (participantsUpdate: {
|
||||||
@ -1459,7 +1501,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
this.logger.verbose('Sending data to webhook in event LABELS_ASSOCIATION');
|
this.logger.verbose('Sending data to webhook in event LABELS_ASSOCIATION');
|
||||||
|
|
||||||
// Atualiza labels nos chats
|
// Atualiza labels nos chats
|
||||||
if (database.SAVE_DATA.CHATS) {
|
if (database.ENABLED && database.SAVE_DATA.CHATS) {
|
||||||
const chats = await this.repository.chat.find({
|
const chats = await this.repository.chat.find({
|
||||||
where: {
|
where: {
|
||||||
owner: this.instance.name,
|
owner: this.instance.name,
|
||||||
@ -1838,7 +1880,11 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
let mentions: string[];
|
let mentions: string[];
|
||||||
if (isJidGroup(sender)) {
|
if (isJidGroup(sender)) {
|
||||||
try {
|
try {
|
||||||
const group = await this.findGroup({ groupJid: sender }, 'inner');
|
let group;
|
||||||
|
|
||||||
|
const cache = this.configService.get<CacheConf>('CACHE');
|
||||||
|
if (!cache.REDIS.ENABLED && !cache.LOCAL.ENABLED) group = await this.findGroup({ groupJid: sender }, 'inner');
|
||||||
|
else group = await this.getGroupMetadataCache(sender);
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
throw new NotFoundException('Group not found');
|
throw new NotFoundException('Group not found');
|
||||||
@ -1891,7 +1937,14 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
key: message['reactionMessage']['key'],
|
key: message['reactionMessage']['key'],
|
||||||
},
|
},
|
||||||
} as unknown as AnyMessageContent,
|
} as unknown as AnyMessageContent,
|
||||||
option as unknown as MiscMessageGenerationOptions,
|
{
|
||||||
|
...option,
|
||||||
|
cachedGroupMetadata:
|
||||||
|
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
|
||||||
|
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
|
||||||
|
? null
|
||||||
|
: this.getGroupMetadataCache,
|
||||||
|
} as unknown as MiscMessageGenerationOptions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1904,7 +1957,14 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
mentions,
|
mentions,
|
||||||
linkPreview: linkPreview,
|
linkPreview: linkPreview,
|
||||||
} as unknown as AnyMessageContent,
|
} as unknown as AnyMessageContent,
|
||||||
option as unknown as MiscMessageGenerationOptions,
|
{
|
||||||
|
...option,
|
||||||
|
cachedGroupMetadata:
|
||||||
|
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
|
||||||
|
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
|
||||||
|
? null
|
||||||
|
: this.getGroupMetadataCache,
|
||||||
|
} as unknown as MiscMessageGenerationOptions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1919,7 +1979,14 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
},
|
},
|
||||||
mentions,
|
mentions,
|
||||||
},
|
},
|
||||||
option as unknown as MiscMessageGenerationOptions,
|
{
|
||||||
|
...option,
|
||||||
|
cachedGroupMetadata:
|
||||||
|
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
|
||||||
|
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
|
||||||
|
? null
|
||||||
|
: this.getGroupMetadataCache,
|
||||||
|
} as unknown as MiscMessageGenerationOptions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1940,7 +2007,14 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
return await this.client.sendMessage(
|
return await this.client.sendMessage(
|
||||||
sender,
|
sender,
|
||||||
message as unknown as AnyMessageContent,
|
message as unknown as AnyMessageContent,
|
||||||
option as unknown as MiscMessageGenerationOptions,
|
{
|
||||||
|
...option,
|
||||||
|
cachedGroupMetadata:
|
||||||
|
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
|
||||||
|
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
|
||||||
|
? null
|
||||||
|
: this.getGroupMetadataCache,
|
||||||
|
} as unknown as MiscMessageGenerationOptions,
|
||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -3145,6 +3219,38 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Group
|
// Group
|
||||||
|
private async updateGroupMetadataCache(groupJid: string) {
|
||||||
|
try {
|
||||||
|
const meta = await this.client.groupMetadata(groupJid);
|
||||||
|
await groupMetadataCache.set(groupJid, {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
data: meta,
|
||||||
|
});
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGroupMetadataCache(groupJid: string) {
|
||||||
|
if (!isJidGroup(groupJid)) return null;
|
||||||
|
|
||||||
|
if (await groupMetadataCache.has(groupJid)) {
|
||||||
|
console.log('Has cache for group: ' + groupJid);
|
||||||
|
const meta = await groupMetadataCache.get(groupJid);
|
||||||
|
|
||||||
|
if (Date.now() - meta.timestamp > 3600000) {
|
||||||
|
await this.updateGroupMetadataCache(groupJid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.updateGroupMetadataCache(groupJid);
|
||||||
|
}
|
||||||
|
|
||||||
public async createGroup(create: CreateGroupDto) {
|
public async createGroup(create: CreateGroupDto) {
|
||||||
this.logger.verbose('Creating group: ' + create.subject);
|
this.logger.verbose('Creating group: ' + create.subject);
|
||||||
try {
|
try {
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
SendTextDto,
|
SendTextDto,
|
||||||
} from '../../dto/sendMessage.dto';
|
} from '../../dto/sendMessage.dto';
|
||||||
import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../../models';
|
import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../../models';
|
||||||
|
import { ProviderFiles } from '../../provider/sessions';
|
||||||
import { RepositoryBroker } from '../../repository/repository.manager';
|
import { RepositoryBroker } from '../../repository/repository.manager';
|
||||||
import { Events, wa } from '../../types/wa.types';
|
import { Events, wa } from '../../types/wa.types';
|
||||||
import { CacheService } from './../cache.service';
|
import { CacheService } from './../cache.service';
|
||||||
@ -35,7 +36,8 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
public readonly repository: RepositoryBroker,
|
public readonly repository: RepositoryBroker,
|
||||||
public readonly cache: CacheService,
|
public readonly cache: CacheService,
|
||||||
public readonly chatwootCache: CacheService,
|
public readonly chatwootCache: CacheService,
|
||||||
public readonly messagesLostCache: CacheService,
|
public readonly baileysCache: CacheService,
|
||||||
|
private readonly providerFiles: ProviderFiles,
|
||||||
) {
|
) {
|
||||||
super(configService, eventEmitter, repository, chatwootCache);
|
super(configService, eventEmitter, repository, chatwootCache);
|
||||||
this.logger.verbose('BusinessStartupService initialized');
|
this.logger.verbose('BusinessStartupService initialized');
|
||||||
|
@ -5,7 +5,15 @@ import { Db } from 'mongodb';
|
|||||||
import { Collection } from 'mongoose';
|
import { Collection } from 'mongoose';
|
||||||
import { join } from 'path';
|
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 { Logger } from '../../config/logger.config';
|
||||||
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
|
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
|
||||||
import { NotFoundException } from '../../exceptions';
|
import { NotFoundException } from '../../exceptions';
|
||||||
@ -22,6 +30,7 @@ import {
|
|||||||
WebhookModel,
|
WebhookModel,
|
||||||
WebsocketModel,
|
WebsocketModel,
|
||||||
} from '../models';
|
} from '../models';
|
||||||
|
import { ProviderFiles } from '../provider/sessions';
|
||||||
import { RepositoryBroker } from '../repository/repository.manager';
|
import { RepositoryBroker } from '../repository/repository.manager';
|
||||||
import { Integration } from '../types/wa.types';
|
import { Integration } from '../types/wa.types';
|
||||||
import { CacheService } from './cache.service';
|
import { CacheService } from './cache.service';
|
||||||
@ -35,7 +44,8 @@ export class WAMonitoringService {
|
|||||||
private readonly repository: RepositoryBroker,
|
private readonly repository: RepositoryBroker,
|
||||||
private readonly cache: CacheService,
|
private readonly cache: CacheService,
|
||||||
private readonly chatwootCache: CacheService,
|
private readonly chatwootCache: CacheService,
|
||||||
private readonly messagesLostCache: CacheService,
|
private readonly baileysCache: CacheService,
|
||||||
|
private readonly providerFiles: ProviderFiles,
|
||||||
) {
|
) {
|
||||||
this.logger.verbose('instance created');
|
this.logger.verbose('instance created');
|
||||||
|
|
||||||
@ -58,6 +68,8 @@ export class WAMonitoringService {
|
|||||||
private readonly logger = new Logger(WAMonitoringService.name);
|
private readonly logger = new Logger(WAMonitoringService.name);
|
||||||
public readonly waInstances: Record<string, BaileysStartupService | BusinessStartupService> = {};
|
public readonly waInstances: Record<string, BaileysStartupService | BusinessStartupService> = {};
|
||||||
|
|
||||||
|
private readonly providerSession = Object.freeze(this.configService.get<ProviderSession>('PROVIDER'));
|
||||||
|
|
||||||
public delInstanceTime(instance: string) {
|
public delInstanceTime(instance: string) {
|
||||||
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
|
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
|
||||||
if (typeof time === 'number' && time > 0) {
|
if (typeof time === 'number' && time > 0) {
|
||||||
@ -257,12 +269,18 @@ export class WAMonitoringService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logger.verbose('cleaning up instance in files: ' + instanceName);
|
this.logger.verbose('cleaning up instance in files: ' + instanceName);
|
||||||
|
if (this.providerSession?.ENABLED) {
|
||||||
|
await this.providerFiles.removeSession(instanceName);
|
||||||
|
}
|
||||||
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
public async cleaningStoreFiles(instanceName: string) {
|
public async cleaningStoreFiles(instanceName: string) {
|
||||||
if (!this.db.ENABLED) {
|
if (!this.db.ENABLED) {
|
||||||
this.logger.verbose('cleaning store files instance: ' + instanceName);
|
this.logger.verbose('cleaning store files instance: ' + instanceName);
|
||||||
|
if (this.providerSession?.ENABLED) {
|
||||||
|
await this.providerFiles.removeSession(instanceName);
|
||||||
|
}
|
||||||
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
||||||
|
|
||||||
execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`);
|
execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`);
|
||||||
@ -305,7 +323,9 @@ export class WAMonitoringService {
|
|||||||
this.logger.verbose('Loading instances');
|
this.logger.verbose('Loading instances');
|
||||||
|
|
||||||
try {
|
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();
|
await this.loadInstancesFromRedis();
|
||||||
} else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
} else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
||||||
await this.loadInstancesFromDatabase();
|
await this.loadInstancesFromDatabase();
|
||||||
@ -348,7 +368,8 @@ export class WAMonitoringService {
|
|||||||
this.repository,
|
this.repository,
|
||||||
this.cache,
|
this.cache,
|
||||||
this.chatwootCache,
|
this.chatwootCache,
|
||||||
this.messagesLostCache,
|
this.baileysCache,
|
||||||
|
this.providerFiles,
|
||||||
);
|
);
|
||||||
|
|
||||||
instance.instanceName = name;
|
instance.instanceName = name;
|
||||||
@ -359,7 +380,8 @@ export class WAMonitoringService {
|
|||||||
this.repository,
|
this.repository,
|
||||||
this.cache,
|
this.cache,
|
||||||
this.chatwootCache,
|
this.chatwootCache,
|
||||||
this.messagesLostCache,
|
this.baileysCache,
|
||||||
|
this.providerFiles,
|
||||||
);
|
);
|
||||||
|
|
||||||
instance.instanceName = name;
|
instance.instanceName = name;
|
||||||
@ -401,6 +423,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() {
|
private async loadInstancesFromFiles() {
|
||||||
this.logger.verbose('Store in files enabled');
|
this.logger.verbose('Store in files enabled');
|
||||||
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
|
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
|
||||||
|
@ -28,6 +28,13 @@ export type Log = {
|
|||||||
BAILEYS: LogBaileys;
|
BAILEYS: LogBaileys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ProviderSession = {
|
||||||
|
ENABLED: boolean;
|
||||||
|
HOST: string;
|
||||||
|
PORT: string;
|
||||||
|
PREFIX: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type SaveData = {
|
export type SaveData = {
|
||||||
INSTANCE: boolean;
|
INSTANCE: boolean;
|
||||||
NEW_MESSAGE: boolean;
|
NEW_MESSAGE: boolean;
|
||||||
@ -209,6 +216,7 @@ export interface Env {
|
|||||||
SERVER: HttpServer;
|
SERVER: HttpServer;
|
||||||
CORS: Cors;
|
CORS: Cors;
|
||||||
SSL_CONF: SslConf;
|
SSL_CONF: SslConf;
|
||||||
|
PROVIDER: ProviderSession;
|
||||||
STORE: StoreConf;
|
STORE: StoreConf;
|
||||||
CLEAN_STORE: CleanStoreConf;
|
CLEAN_STORE: CleanStoreConf;
|
||||||
DATABASE: Database;
|
DATABASE: Database;
|
||||||
@ -274,6 +282,12 @@ export class ConfigService {
|
|||||||
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
|
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
|
||||||
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '',
|
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '',
|
||||||
},
|
},
|
||||||
|
PROVIDER: {
|
||||||
|
ENABLED: process.env?.PROVIDER_ENABLED === 'true',
|
||||||
|
HOST: process.env.PROVIDER_HOST,
|
||||||
|
PORT: process.env?.PROVIDER_PORT || '5656',
|
||||||
|
PREFIX: process.env?.PROVIDER_PREFIX || 'evolution',
|
||||||
|
},
|
||||||
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',
|
||||||
|
@ -49,6 +49,14 @@ LOG:
|
|||||||
DEL_INSTANCE: false # or false
|
DEL_INSTANCE: false # or false
|
||||||
DEL_TEMP_INSTANCES: true # Delete instances with status closed on start
|
DEL_TEMP_INSTANCES: true # Delete instances with status closed on start
|
||||||
|
|
||||||
|
# Seesion Files Providers
|
||||||
|
# Provider responsible for managing credentials files and WhatsApp sessions.
|
||||||
|
PROVIDER:
|
||||||
|
ENABLED: true
|
||||||
|
HOST: 127.0.0.1
|
||||||
|
PORT: 5656
|
||||||
|
PREFIX: evolution
|
||||||
|
|
||||||
# Temporary data storage
|
# Temporary data storage
|
||||||
STORE:
|
STORE:
|
||||||
MESSAGES: true
|
MESSAGES: true
|
||||||
|
@ -9,6 +9,7 @@ import { join } from 'path';
|
|||||||
import { initAMQP, initGlobalQueues } from './api/integrations/rabbitmq/libs/amqp.server';
|
import { initAMQP, initGlobalQueues } from './api/integrations/rabbitmq/libs/amqp.server';
|
||||||
import { initSQS } from './api/integrations/sqs/libs/sqs.server';
|
import { initSQS } from './api/integrations/sqs/libs/sqs.server';
|
||||||
import { initIO } from './api/integrations/websocket/libs/socket.server';
|
import { initIO } from './api/integrations/websocket/libs/socket.server';
|
||||||
|
import { ProviderFiles } from './api/provider/sessions';
|
||||||
import { HttpStatus, router } from './api/routes/index.router';
|
import { HttpStatus, router } from './api/routes/index.router';
|
||||||
import { waMonitor } from './api/server.module';
|
import { waMonitor } from './api/server.module';
|
||||||
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
|
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
|
||||||
@ -22,10 +23,14 @@ function initWA() {
|
|||||||
waMonitor.loadInstance();
|
waMonitor.loadInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
function bootstrap() {
|
async function bootstrap() {
|
||||||
const logger = new Logger('SERVER');
|
const logger = new Logger('SERVER');
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
const providerFiles = new ProviderFiles(configService);
|
||||||
|
await providerFiles.onModuleInit();
|
||||||
|
logger.info('Provider:Files - ON');
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
cors({
|
cors({
|
||||||
origin(requestOrigin, callback) {
|
origin(requestOrigin, callback) {
|
||||||
|
139
src/utils/use-multi-file-auth-state-provider-files.ts
Normal file
139
src/utils/use-multi-file-auth-state-provider-files.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
* ┌──────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
* │ @author jrCleber │
|
||||||
|
* │ @filename use-multi-file-auth-state-provider-files.ts │
|
||||||
|
* │ Developed by: Cleber Wilson │
|
||||||
|
* │ Creation date: May 31, 2024 │
|
||||||
|
* │ Contact: contato@codechat.dev │
|
||||||
|
* ├──────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
* │ @copyright © Cleber Wilson 2023. All rights reserved. │
|
||||||
|
* │ Licensed under the Apache License, Version 2.0 │
|
||||||
|
* │ │
|
||||||
|
* │ @license "https://github.com/code-chat-br/whatsapp-api/blob/main/LICENSE" │
|
||||||
|
* │ │
|
||||||
|
* │ You may not use this file except in compliance with the License. │
|
||||||
|
* │ You may obtain a copy of the License at │
|
||||||
|
* │ │
|
||||||
|
* │ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||||
|
* │ │
|
||||||
|
* │ Unless required by applicable law or agreed to in writing, software │
|
||||||
|
* │ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||||
|
* │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||||
|
* │ │
|
||||||
|
* │ See the License for the specific language governing permissions and │
|
||||||
|
* │ limitations under the License. │
|
||||||
|
* │ │
|
||||||
|
* │ @type {AuthState} │
|
||||||
|
* │ @function useMultiFileAuthStateRedisDb │
|
||||||
|
* │ @returns {Promise<AuthState>} │
|
||||||
|
* ├──────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
* │ @important │
|
||||||
|
* │ For any future changes to the code in this file, it is recommended to │
|
||||||
|
* │ contain, together with the modification, the information of the developer │
|
||||||
|
* │ who changed it and the date of modification. │
|
||||||
|
* └──────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
AuthenticationCreds,
|
||||||
|
AuthenticationState,
|
||||||
|
BufferJSON,
|
||||||
|
initAuthCreds,
|
||||||
|
proto,
|
||||||
|
SignalDataTypeMap,
|
||||||
|
} from '@whiskeysockets/baileys';
|
||||||
|
import { isNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
import { ProviderFiles } from '../api/provider/sessions';
|
||||||
|
import { Logger } from '../config/logger.config';
|
||||||
|
|
||||||
|
export type AuthState = { state: AuthenticationState; saveCreds: () => Promise<void> };
|
||||||
|
|
||||||
|
export class AuthStateProvider {
|
||||||
|
constructor(private readonly providerFiles: ProviderFiles) {}
|
||||||
|
|
||||||
|
private readonly logger = new Logger(AuthStateProvider.name);
|
||||||
|
|
||||||
|
public async authStateProvider(instance: string): Promise<AuthState> {
|
||||||
|
const [, error] = await this.providerFiles.create(instance);
|
||||||
|
if (error) {
|
||||||
|
this.logger.error(['Failed to create folder on file server', error?.message, error?.stack]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const writeData = async (data: any, key: string): Promise<any> => {
|
||||||
|
const json = JSON.stringify(data, BufferJSON.replacer);
|
||||||
|
const [response, error] = await this.providerFiles.write(instance, key, {
|
||||||
|
data: json,
|
||||||
|
});
|
||||||
|
if (error) {
|
||||||
|
// this.logger.error(['writeData', error?.message, error?.stack]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readData = async (key: string): Promise<any> => {
|
||||||
|
const [response, error] = await this.providerFiles.read(instance, key);
|
||||||
|
if (error) {
|
||||||
|
// this.logger.error(['readData', error?.message, error?.stack]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isNotEmpty(response?.data)) {
|
||||||
|
return JSON.parse(JSON.stringify(response.data), BufferJSON.reviver);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeData = async (key: string) => {
|
||||||
|
const [response, error] = await this.providerFiles.delete(instance, key);
|
||||||
|
if (error) {
|
||||||
|
// this.logger.error(['removeData', error?.message, error?.stack]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
|
||||||
|
|
||||||
|
return {
|
||||||
|
state: {
|
||||||
|
creds,
|
||||||
|
keys: {
|
||||||
|
get: async (type, ids: string[]) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const data: { [_: string]: SignalDataTypeMap[type] } = {};
|
||||||
|
await Promise.all(
|
||||||
|
ids.map(async (id) => {
|
||||||
|
let value = await readData(`${type}-${id}`);
|
||||||
|
if (type === 'app-state-sync-key' && value) {
|
||||||
|
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[id] = value;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
set: async (data: any) => {
|
||||||
|
const tasks: Promise<void>[] = [];
|
||||||
|
for (const category in data) {
|
||||||
|
for (const id in data[category]) {
|
||||||
|
const value = data[category][id];
|
||||||
|
const key = `${category}-${id}`;
|
||||||
|
tasks.push(value ? await writeData(value, key) : await removeData(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(tasks);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
saveCreds: async () => {
|
||||||
|
return await writeData(creds, 'creds');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user