Compare commits

...

29 Commits
main ... 1.8.2

Author SHA1 Message Date
Davidson Gomes
b8fe5603fd Merge branch 'release/1.8.2' 2024-07-03 16:10:38 -03:00
Davidson Gomes
762453c0e3 chore: Add workflow to publish Docker image
A new untracked file '.github/workflows/publish\_docker\_image\_latest.yml' was added. This workflow will handle the process of publishing the latest Docker image. This change allows for easier and more automated deployment of the Node.js project.
2024-07-03 16:10:23 -03:00
Davidson Gomes
af1b5caa29 Merge tag '1.8.2' into develop
* Corretion in globall rabbitmq queue name
* Improvement in the use of mongodb database for credentials
* Fixed base64 in webhook for documentWithCaption
* Fixed Generate pairing code
2024-07-03 16:09:25 -03:00
Davidson Gomes
50b3379d88 Merge branch 'release/1.8.2' 2024-07-03 16:09:17 -03:00
Davidson Gomes
c9ac5984ec Merge tag '1.8.2' into develop
* Corretion in globall rabbitmq queue name
* Improvement in the use of mongodb database for credentials
* Fixed base64 in webhook for documentWithCaption
* Fixed Generate pairing code
2024-07-03 16:06:24 -03:00
Davidson Gomes
29ba63d621 Merge branch 'release/1.8.2' 2024-07-03 16:06:16 -03:00
Davidson Gomes
2ce64af502 Merge tag '1.8.2' into develop
* Corretion in globall rabbitmq queue name
* Improvement in the use of mongodb database for credentials
* Fixed base64 in webhook for documentWithCaption
* Fixed Generate pairing code
2024-07-03 13:51:36 -03:00
Davidson Gomes
28c517a3d5 Merge branch 'release/1.8.2' 2024-07-03 13:51:28 -03:00
Davidson Gomes
18ebe27bc3 "chore: Updated package and documentation versions to v1.8.2"
Explanation:
This commit updates the package and documentation versions to v1.8.2. The package.json and swagger.yaml files were modified accordingly. These changes are mainly for maintenance purposes and do not affect the functionality of the application.
2024-07-03 13:51:16 -03:00
Davidson Gomes
0f7a39a08f chore(changelog): Update changelog to v1.8.2
This commit updates the changelog to reflect the release of version 1.8.2. The date of the release has been updated in the header of the changelog. No functional changes were made in this release.

Modified: CHANGELOG.md
2024-07-03 13:50:30 -03:00
Davidson Gomes
a8121d7fe6 fix: Correction in global RabbitMQ queue name
Fix global RabbitMQ queue name in `channel.service.ts` and update CHANGELOG.md.
The queue name has been changed from `transformedWe` to `event`.
This fix prevents queue errors and ensures correct functionality of inter-service communication.
2024-07-03 13:47:25 -03:00
Davidson Gomes
b63b7b0b7b fix: Generate pairing code and update Baileys repository
Update package.json to use the new Baileys repository and modify the Whatsapp Baileys service to generate a pairing code. This change fixes the issue with the previous Baileys repository and improves the pairing process for Whatsapp.

Changes:
- Update package.json to use the new Baileys repository
- Modify Whatsapp Baileys service to generate a pairing code
- Fix issue with previous Baileys repository
- Improve pairing process for Whatsapp
2024-07-03 13:45:05 -03:00
Davidson Gomes
14ea5d959f
Merge pull request #662 from drauber/cwId_missing
Add -cwId- when findByName
2024-06-26 10:32:09 -03:00
Douglas Rauber at Nitro
86b2999fcf Add -cwId- when findByName 2024-06-24 10:37:54 -03:00
Davidson Gomes
f5bd11fc19 feat: add support for documentWithCaptionMessage in WhatsApp Baileys service
Modified whatsapp.baileys.service.ts to include handling for documentWithCaptionMessage. This change ensures that messages with documents having captions are properly processed, enhancing the service's message handling capabilities. No impact on existing functionalities.
2024-06-18 11:34:47 -03:00
Davidson Gomes
c060d330de fix: normalize event names in channel.service.ts
Normalized event names by replacing underscores with dots and converting to lowercase. This ensures consistent naming conventions and prevents potential issues with queue bindings.
2024-06-18 11:32:50 -03:00
Davidson Gomes
1e320f7904 fix: update use-multi-file-auth-state-db.ts to handle edge cases
Refactored the use-multi-file-auth-state-db.ts to better handle edge cases in multi-file authentication state management. This change improves reliability and ensures more robust error handling, reducing potential issues during authentication.
2024-06-17 11:19:22 -03:00
Davidson Gomes
975b3ee528 fix: reorder imports to resolve linting issues
Reordered imports in multiple files to resolve linting issues and improve code readability. This change does not impact the functionality but ensures the code adheres to the project's coding standards.
2024-06-16 17:19:45 -03:00
Davidson Gomes
5b47bc9ef0 feat: update dependencies and improve caching logic
Updated package.json to include latest dependencies. Enhanced caching logic in cache.service.ts and rediscache.ts for better performance. Improved DTOs in chat.dto.ts, instance.dto.ts, and sendMessage.dto.ts for more robust data handling. Refined instance.controller.ts and chatwoot.service.ts to streamline API integrations. Adjusted authentication state management in use-multi-file-auth-state-db.ts, use-multi-file-auth-state-provider-files.ts, and use-multi-file-auth-state-redis-db.ts. These changes aim to optimize the system's performance and reliability.
2024-06-16 17:17:55 -03:00
Davidson Gomes
053a7981d1 fix: update default values for WA_BUSINESS environment variables
Updated default values for WA_BUSINESS_TOKEN_WEBHOOK, WA_BUSINESS_URL, and WA_BUSINESS_VERSION in env.config.ts to 'evolution', 'https://graph.facebook.com', and 'v19.0' respectively. This change ensures that the application uses more appropriate defaults if environment variables are not set, improving reliability and consistency.
2024-06-13 18:51:18 -03:00
Davidson Gomes
5f1f025d65 Merge tag '1.8.1' into develop
v
2024-06-12 19:49:40 -03:00
Davidson Gomes
dfb003fd72 Merge branch 'release/1.8.1' 2024-06-12 19:49:37 -03:00
Davidson Gomes
5239e431c2 feat: update server and monitor services
Updated server.module.ts and monitor.service.ts to improve service initialization and monitoring logic. Modified main.ts to integrate changes. This enhances the application's performance and reliability.
2024-06-12 19:48:56 -03:00
Davidson Gomes
53cc6132f5 Merge tag '1.8.1' into develop
v
2024-06-12 19:03:36 -03:00
Davidson Gomes
0bc1b78db9 Merge branch 'release/1.8.1' 2024-06-12 19:03:30 -03:00
Davidson Gomes
2a9412c81a chore: adjust socket configuration and update baileys version
Updated package.json to include the latest version of baileys for improved functionality. Modified whatsapp.baileys.service.ts to adjust socket configuration, enhancing the stability and performance of the service.
2024-06-12 19:02:36 -03:00
Davidson Gomes
f707cf4109 Merge tag '1.8.1' into develop
v
2024-06-09 14:22:20 -03:00
Davidson Gomes
410cfc8bcb Merge branch 'release/1.8.1' 2024-06-09 14:22:17 -03:00
Davidson Gomes
f1a3fd872f git actions v2 2024-06-09 14:20:48 -03:00
23 changed files with 397 additions and 327 deletions

View File

@ -0,0 +1,48 @@
name: Build Docker image
on:
push:
branches:
- main
jobs:
build_deploy:
name: Build and Deploy
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: atendai/evolution-api
tags: latest
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View File

@ -0,0 +1,48 @@
name: Build Docker image
on:
push:
branches:
- v2.0.0
jobs:
build_deploy:
name: Build and Deploy
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: atendai/evolution-api
tags: v2.0.0-alpha
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View File

@ -1,3 +1,12 @@
# 1.8.2 (2024-07-03 13:50)
### Fixed
* Corretion in globall rabbitmq queue name
* Improvement in the use of mongodb database for credentials
* Fixed base64 in webhook for documentWithCaption
* Fixed Generate pairing code
# 1.8.1 (2024-06-08 21:32) # 1.8.1 (2024-06-08 21:32)
### Feature ### Feature

View File

@ -1,6 +1,6 @@
{ {
"name": "evolution-api", "name": "evolution-api",
"version": "1.8.1", "version": "1.8.2",
"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.4", "baileys": "github:EvolutionAPI/Baileys",
"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",

View File

@ -1,4 +1,4 @@
import { delay } from '@whiskeysockets/baileys'; import { delay } from 'baileys';
import { isURL } from 'class-validator'; import { isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
import { v4 } from 'uuid'; import { v4 } from 'uuid';

View File

@ -1,4 +1,4 @@
import { proto, WAPresence, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '@whiskeysockets/baileys'; import { proto, WAPresence, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from 'baileys';
export class OnWhatsAppDto { export class OnWhatsAppDto {
constructor( constructor(

View File

@ -1,4 +1,4 @@
import { WAPresence } from '@whiskeysockets/baileys'; import { WAPresence } from 'baileys';
import { ProxyDto } from './proxy.dto'; import { ProxyDto } from './proxy.dto';

View File

@ -1,4 +1,4 @@
import { proto, WAPresence } from '@whiskeysockets/baileys'; import { proto, WAPresence } from 'baileys';
export class Quoted { export class Quoted {
key: proto.IMessageKey; key: proto.IMessageKey;

View File

@ -8,8 +8,8 @@ import ChatwootClient, {
inbox, inbox,
} from '@figuro/chatwoot-sdk'; } from '@figuro/chatwoot-sdk';
import { request as chatwootRequest } from '@figuro/chatwoot-sdk/dist/core/request'; import { request as chatwootRequest } from '@figuro/chatwoot-sdk/dist/core/request';
import { proto } from '@whiskeysockets/baileys';
import axios from 'axios'; import axios from 'axios';
import { proto } from 'baileys';
import FormData from 'form-data'; import FormData from 'form-data';
import { createReadStream, unlinkSync, writeFileSync } from 'fs'; import { createReadStream, unlinkSync, writeFileSync } from 'fs';
import Jimp from 'jimp'; import Jimp from 'jimp';
@ -445,7 +445,6 @@ export class ChatwootService {
// eslint-disable-next-line prettier/prettier // eslint-disable-next-line prettier/prettier
if (contacts.length === 2 && this.getClientCwConfig().merge_brazil_contacts && query.startsWith('+55')) { if (contacts.length === 2 && this.getClientCwConfig().merge_brazil_contacts && query.startsWith('+55')) {
const contact = this.mergeBrazilianContacts(contacts); const contact = this.mergeBrazilianContacts(contacts);
if (contact) { if (contact) {
return contact; return contact;
@ -736,7 +735,12 @@ export class ChatwootService {
} }
this.logger.verbose('find inbox by name'); this.logger.verbose('find inbox by name');
const findByName = inbox.payload.find((inbox) => inbox.name === this.getClientCwConfig().name_inbox); let findByName = inbox.payload.find((inbox) => inbox.name === this.getClientCwConfig().name_inbox);
if (!findByName) {
findByName = inbox.payload.find((inbox) => inbox.name === this.getClientCwConfig().name_inbox.split('-cwId-')[0]);
}
if (!findByName) { if (!findByName) {
this.logger.warn('inbox not found'); this.logger.warn('inbox not found');

View File

@ -1,5 +1,5 @@
import { inbox } from '@figuro/chatwoot-sdk'; import { inbox } from '@figuro/chatwoot-sdk';
import { proto } from '@whiskeysockets/baileys'; import { proto } from 'baileys';
import { InstanceDto } from '../../../../api/dto/instance.dto'; import { InstanceDto } from '../../../../api/dto/instance.dto';
import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../../api/models'; import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../../api/models';

View File

@ -1,5 +1,5 @@
import { CacheEngine } from '../cache/cacheengine'; import { CacheEngine } from '../cache/cacheengine';
import { configService } from '../config/env.config'; import { configService, ProviderSession } from '../config/env.config';
import { eventEmitter } from '../config/event.config'; import { eventEmitter } from '../config/event.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
import { dbserver } from '../libs/db.connect'; import { dbserver } from '../libs/db.connect';
@ -110,7 +110,12 @@ 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 baileysCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine()); const baileysCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine());
const providerFiles = new ProviderFiles(configService);
let providerFiles: ProviderFiles = null;
if (configService.get<ProviderSession>('PROVIDER')?.ENABLED) {
providerFiles = new ProviderFiles(configService);
}
export const waMonitor = new WAMonitoringService( export const waMonitor = new WAMonitoringService(
eventEmitter, eventEmitter,

View File

@ -1,4 +1,4 @@
import { BufferJSON } from '@whiskeysockets/baileys'; import { BufferJSON } from 'baileys';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { ICache } from '../abstract/abstract.cache'; import { ICache } from '../abstract/abstract.cache';

View File

@ -1,5 +1,5 @@
import { WASocket } from '@whiskeysockets/baileys';
import axios from 'axios'; import axios from 'axios';
import { WASocket } from 'baileys';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { isURL } from 'class-validator'; import { isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
@ -721,7 +721,9 @@ export class ChannelStartupService {
autoDelete: false, autoDelete: false,
}); });
const queueName = `${this.instanceName}.${event}`; const eventName = event.replace(/_/g, '.').toLowerCase();
const queueName = `${this.instanceName}.${eventName}`;
await amqp.assertQueue(queueName, { await amqp.assertQueue(queueName, {
durable: true, durable: true,
@ -731,7 +733,7 @@ export class ChannelStartupService {
}, },
}); });
await amqp.bindQueue(queueName, exchangeName, event); await amqp.bindQueue(queueName, exchangeName, eventName);
const message = { const message = {
event, event,
@ -786,7 +788,7 @@ export class ChannelStartupService {
autoDelete: false, autoDelete: false,
}); });
const queueName = transformedWe; const queueName = event;
await amqp.assertQueue(queueName, { await amqp.assertQueue(queueName, {
durable: true, durable: true,

View File

@ -1,5 +1,6 @@
import ffmpegPath from '@ffmpeg-installer/ffmpeg'; import ffmpegPath from '@ffmpeg-installer/ffmpeg';
import { Boom } from '@hapi/boom'; import { Boom } from '@hapi/boom';
import axios from 'axios';
import makeWASocket, { import makeWASocket, {
AnyMessageContent, AnyMessageContent,
BufferedEventData, BufferedEventData,
@ -19,6 +20,7 @@ import makeWASocket, {
GroupMetadata, GroupMetadata,
isJidBroadcast, isJidBroadcast,
isJidGroup, isJidGroup,
isJidNewsletter,
isJidUser, isJidUser,
makeCacheableSignalKeyStore, makeCacheableSignalKeyStore,
MessageUpsertType, MessageUpsertType,
@ -35,10 +37,9 @@ import makeWASocket, {
WAMessageUpdate, WAMessageUpdate,
WAPresence, WAPresence,
WASocket, WASocket,
} from '@whiskeysockets/baileys'; } from 'baileys';
import { Label } from '@whiskeysockets/baileys/lib/Types/Label'; import { Label } from 'baileys/lib/Types/Label';
import { LabelAssociation } from '@whiskeysockets/baileys/lib/Types/LabelAssociation'; import { LabelAssociation } from 'baileys/lib/Types/LabelAssociation';
import axios from 'axios';
import { exec } from 'child_process'; import { exec } from 'child_process';
import { isBase64, isURL } from 'class-validator'; import { isBase64, isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
@ -253,8 +254,8 @@ export class BaileysStartupService extends ChannelStartupService {
this.logger.verbose('Getting profile status'); this.logger.verbose('Getting profile status');
const status = await this.client.fetchStatus(this.instance.wuid); const status = await this.client.fetchStatus(this.instance.wuid);
this.logger.verbose(`Profile status: ${status.status}`); this.logger.verbose(`Profile status: ${status[0]?.status}`);
return status.status; return status[0]?.status;
} }
public get profilePictureUrl() { public get profilePictureUrl() {
@ -517,19 +518,7 @@ export class BaileysStartupService extends ChannelStartupService {
return await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name)); return await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name));
} }
public async connectToWhatsapp(number?: string, mobile?: boolean): Promise<WASocket> { private async createClient(number?: string, mobile?: boolean): Promise<WASocket> {
this.logger.verbose('Connecting to whatsapp');
try {
this.loadWebhook();
this.loadChatwoot();
this.loadSettings();
this.loadWebsocket();
this.loadRabbitmq();
this.loadSqs();
this.loadTypebot();
this.loadProxy();
this.loadChamaai();
this.instance.authState = await this.defineAuthState(); this.instance.authState = await this.defineAuthState();
if (!mobile) { if (!mobile) {
@ -539,8 +528,19 @@ export class BaileysStartupService extends ChannelStartupService {
} }
const session = this.configService.get<ConfigSessionPhone>('CONFIG_SESSION_PHONE'); const session = this.configService.get<ConfigSessionPhone>('CONFIG_SESSION_PHONE');
let browserOptions = {};
if (number || this.phoneNumber) {
this.phoneNumber = number;
this.logger.info(`Phone number: ${number}`);
} else {
const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()];
this.logger.verbose('Browser: ' + JSON.stringify(browser)); browserOptions = { browser };
this.logger.info(`Browser: ${browser}`);
}
let version; let version;
let log; let log;
@ -592,19 +592,23 @@ export class BaileysStartupService extends ChannelStartupService {
logger: P({ level: this.logBaileys }), logger: P({ level: this.logBaileys }),
printQRInTerminal: false, printQRInTerminal: false,
mobile, mobile,
browser: number ? ['Chrome (Linux)', session.NAME, release()] : browser, ...browserOptions,
version, version,
markOnlineOnConnect: this.localSettings.always_online, markOnlineOnConnect: this.localSettings.always_online,
retryRequestDelayMs: 10, retryRequestDelayMs: 350,
connectTimeoutMs: 60_000, maxMsgRetryCount: 4,
qrTimeout: 40_000, fireInitQueries: true,
connectTimeoutMs: 20_000,
keepAliveIntervalMs: 30_000,
qrTimeout: 45_000,
defaultQueryTimeoutMs: undefined, defaultQueryTimeoutMs: undefined,
emitOwnEvents: false, emitOwnEvents: false,
shouldIgnoreJid: (jid) => { shouldIgnoreJid: (jid) => {
const isGroupJid = this.localSettings.groups_ignore && isJidGroup(jid); const isGroupJid = this.localSettings.groups_ignore && isJidGroup(jid);
const isBroadcast = !this.localSettings.read_status && isJidBroadcast(jid); const isBroadcast = !this.localSettings.read_status && isJidBroadcast(jid);
const isNewsletter = isJidNewsletter(jid);
return isGroupJid || isBroadcast; return isGroupJid || isBroadcast || isNewsletter;
}, },
msgRetryCounterCache: this.msgRetryCounterCache, msgRetryCounterCache: this.msgRetryCounterCache,
getMessage: async (key) => (await this.getMessage(key)) as Promise<proto.IMessage>, getMessage: async (key) => (await this.getMessage(key)) as Promise<proto.IMessage>,
@ -614,11 +618,10 @@ export class BaileysStartupService extends ChannelStartupService {
return this.historySyncNotification(msg); return this.historySyncNotification(msg);
}, },
userDevicesCache: this.userDevicesCache, userDevicesCache: this.userDevicesCache,
transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 }, transactionOpts: { maxCommitRetries: 5, delayBetweenTriesMs: 2500 },
patchMessageBeforeSending(message) { patchMessageBeforeSending(message) {
if ( if (
message.deviceSentMessage?.message?.listMessage?.listType === message.deviceSentMessage?.message?.listMessage?.listType === proto.Message.ListMessage.ListType.PRODUCT_LIST
proto.Message.ListMessage.ListType.PRODUCT_LIST
) { ) {
message = JSON.parse(JSON.stringify(message)); message = JSON.parse(JSON.stringify(message));
@ -650,6 +653,22 @@ export class BaileysStartupService extends ChannelStartupService {
this.phoneNumber = number; this.phoneNumber = number;
return this.client; return this.client;
}
public async connectToWhatsapp(number?: string, mobile?: boolean): Promise<WASocket> {
this.logger.verbose('Connecting to whatsapp');
try {
this.loadWebhook();
this.loadChatwoot();
this.loadSettings();
this.loadWebsocket();
this.loadRabbitmq();
this.loadSqs();
this.loadTypebot();
this.loadProxy();
this.loadChamaai();
return await this.createClient(number, mobile);
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
throw new InternalServerErrorException(error?.toString()); throw new InternalServerErrorException(error?.toString());
@ -716,106 +735,7 @@ export class BaileysStartupService extends ChannelStartupService {
public async reloadConnection(): Promise<WASocket> { public async reloadConnection(): Promise<WASocket> {
try { try {
this.instance.authState = await this.defineAuthState(); return await this.createClient(this.phoneNumber, this.mobile);
const session = this.configService.get<ConfigSessionPhone>('CONFIG_SESSION_PHONE');
const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()];
let version;
let log;
if (session.VERSION) {
version = session.VERSION.split(',');
log = `Baileys version env: ${version}`;
} else {
const baileysVersion = await fetchLatestBaileysVersion();
version = baileysVersion.version;
log = `Baileys version: ${version}`;
}
this.logger.info(log);
let options;
if (this.localProxy.enabled) {
this.logger.info('Proxy enabled: ' + this.localProxy.proxy?.host);
if (this.localProxy?.proxy?.host?.includes('proxyscrape')) {
try {
const response = await axios.get(this.localProxy.proxy?.host);
const text = response.data;
const proxyUrls = text.split('\r\n');
const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length));
const proxyUrl = 'http://' + proxyUrls[rand];
options = {
agent: makeProxyAgent(proxyUrl),
fetchAgent: makeProxyAgent(proxyUrl),
};
} catch (error) {
this.localProxy.enabled = false;
}
} else {
options = {
agent: makeProxyAgent(this.localProxy.proxy),
fetchAgent: makeProxyAgent(this.localProxy.proxy),
};
}
}
const socketConfig: UserFacingSocketConfig = {
...options,
auth: {
creds: this.instance.authState.state.creds,
keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' }) as any),
},
logger: P({ level: this.logBaileys }),
printQRInTerminal: false,
browser: this.phoneNumber ? ['Chrome (Linux)', session.NAME, release()] : browser,
version,
markOnlineOnConnect: this.localSettings.always_online,
retryRequestDelayMs: 10,
connectTimeoutMs: 60_000,
qrTimeout: 40_000,
defaultQueryTimeoutMs: undefined,
emitOwnEvents: false,
shouldIgnoreJid: (jid) => {
const isGroupJid = this.localSettings.groups_ignore && isJidGroup(jid);
const isBroadcast = !this.localSettings.read_status && isJidBroadcast(jid);
return isGroupJid || isBroadcast;
},
msgRetryCounterCache: this.msgRetryCounterCache,
getMessage: async (key) => (await this.getMessage(key)) as Promise<proto.IMessage>,
generateHighQualityLinkPreview: true,
syncFullHistory: this.localSettings.sync_full_history,
shouldSyncHistoryMessage: (msg: proto.Message.IHistorySyncNotification) => {
return this.historySyncNotification(msg);
},
userDevicesCache: this.userDevicesCache,
transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 10 },
patchMessageBeforeSending(message) {
if (
message.deviceSentMessage?.message?.listMessage?.listType ===
proto.Message.ListMessage.ListType.PRODUCT_LIST
) {
message = JSON.parse(JSON.stringify(message));
message.deviceSentMessage.message.listMessage.listType = proto.Message.ListMessage.ListType.SINGLE_SELECT;
}
if (message.listMessage?.listType == proto.Message.ListMessage.ListType.PRODUCT_LIST) {
message = JSON.parse(JSON.stringify(message));
message.listMessage.listType = proto.Message.ListMessage.ListType.SINGLE_SELECT;
}
return message;
},
};
this.client = makeWASocket(socketConfig);
return this.client;
} catch (error) { } catch (error) {
this.logger.error(error); this.logger.error(error);
throw new InternalServerErrorException(error?.toString()); throw new InternalServerErrorException(error?.toString());
@ -1053,7 +973,7 @@ export class BaileysStartupService extends ChannelStartupService {
m.messageTimestamp = m.messageTimestamp?.toNumber(); m.messageTimestamp = m.messageTimestamp?.toNumber();
} }
if (m.messageTimestamp <= timestampLimitToImport) { if ((m.messageTimestamp as number) <= timestampLimitToImport) {
continue; continue;
} }
@ -1178,6 +1098,7 @@ export class BaileysStartupService extends ChannelStartupService {
received?.message?.videoMessage || received?.message?.videoMessage ||
received?.message?.stickerMessage || received?.message?.stickerMessage ||
received?.message?.documentMessage || received?.message?.documentMessage ||
received?.message?.documentWithCaptionMessage ||
received?.message?.audioMessage; received?.message?.audioMessage;
const contentMsg = received?.message[getContentType(received.message)] as any; const contentMsg = received?.message[getContentType(received.message)] as any;
@ -1729,7 +1650,7 @@ export class BaileysStartupService extends ChannelStartupService {
this.logger.verbose('Getting status'); this.logger.verbose('Getting status');
return { return {
wuid: jid, wuid: jid,
status: (await this.client.fetchStatus(jid))?.status, status: (await this.client.fetchStatus(jid))[0]?.status,
}; };
} catch (error) { } catch (error) {
this.logger.verbose('Status not found'); this.logger.verbose('Status not found');
@ -1939,11 +1860,9 @@ export class BaileysStartupService extends ChannelStartupService {
} as unknown as AnyMessageContent, } as unknown as AnyMessageContent,
{ {
...option, ...option,
cachedGroupMetadata: useCachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED && !!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED !!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
? null
: this.getGroupMetadataCache,
} as unknown as MiscMessageGenerationOptions, } as unknown as MiscMessageGenerationOptions,
); );
} }
@ -1959,11 +1878,9 @@ export class BaileysStartupService extends ChannelStartupService {
} as unknown as AnyMessageContent, } as unknown as AnyMessageContent,
{ {
...option, ...option,
cachedGroupMetadata: useCachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED && !!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED !!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
? null
: this.getGroupMetadataCache,
} as unknown as MiscMessageGenerationOptions, } as unknown as MiscMessageGenerationOptions,
); );
} }
@ -1981,11 +1898,9 @@ export class BaileysStartupService extends ChannelStartupService {
}, },
{ {
...option, ...option,
cachedGroupMetadata: useCachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED && !!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED !!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
? null
: this.getGroupMetadataCache,
} as unknown as MiscMessageGenerationOptions, } as unknown as MiscMessageGenerationOptions,
); );
} }
@ -2009,11 +1924,9 @@ export class BaileysStartupService extends ChannelStartupService {
message as unknown as AnyMessageContent, message as unknown as AnyMessageContent,
{ {
...option, ...option,
cachedGroupMetadata: useCachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED && !!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED !!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
? null
: this.getGroupMetadataCache,
} as unknown as MiscMessageGenerationOptions, } as unknown as MiscMessageGenerationOptions,
); );
})(); })();
@ -3347,6 +3260,10 @@ export class BaileysStartupService extends ChannelStartupService {
} }
public async findGroup(id: GroupJid, reply: 'inner' | 'out' = 'out') { public async findGroup(id: GroupJid, reply: 'inner' | 'out' = 'out') {
if (this.localSettings.groups_ignore === true) {
return;
}
this.logger.verbose('Fetching group'); this.logger.verbose('Fetching group');
try { try {
const group = await this.client.groupMetadata(id.groupJid); const group = await this.client.groupMetadata(id.groupJid);
@ -3377,6 +3294,10 @@ export class BaileysStartupService extends ChannelStartupService {
} }
public async fetchAllGroups(getParticipants: GetParticipant) { public async fetchAllGroups(getParticipants: GetParticipant) {
if (this.localSettings.groups_ignore === true) {
return;
}
this.logger.verbose('Fetching all groups'); this.logger.verbose('Fetching all groups');
try { try {
const fetch = Object.values(await this.client.groupFetchAllParticipating()); const fetch = Object.values(await this.client.groupFetchAllParticipating());

View File

@ -323,7 +323,7 @@ export class WAMonitoringService {
this.logger.verbose('Loading instances'); this.logger.verbose('Loading instances');
try { try {
if (this.providerSession.ENABLED) { if (this.providerSession?.ENABLED) {
await this.loadInstancesFromProvider(); await this.loadInstancesFromProvider();
} else if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) { } else if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) {
await this.loadInstancesFromRedis(); await this.loadInstancesFromRedis();

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-namespace */
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys'; import { AuthenticationState, WAConnectionState } from 'baileys';
export enum Events { export enum Events {
APPLICATION_STARTUP = 'application.startup', APPLICATION_STARTUP = 'application.startup',

View File

@ -1,4 +1,4 @@
import { BufferJSON } from '@whiskeysockets/baileys'; import { BufferJSON } from 'baileys';
import { RedisClientType } from 'redis'; import { RedisClientType } from 'redis';
import { ICache } from '../api/abstract/abstract.cache'; import { ICache } from '../api/abstract/abstract.cache';

View File

@ -366,9 +366,9 @@ export class ConfigService {
GLOBAL_EVENTS: process.env?.WEBSOCKET_GLOBAL_EVENTS === 'true', GLOBAL_EVENTS: process.env?.WEBSOCKET_GLOBAL_EVENTS === 'true',
}, },
WA_BUSINESS: { WA_BUSINESS: {
TOKEN_WEBHOOK: process.env.WA_BUSINESS_TOKEN_WEBHOOK || '', TOKEN_WEBHOOK: process.env.WA_BUSINESS_TOKEN_WEBHOOK || 'evolution',
URL: process.env.WA_BUSINESS_URL || '', URL: process.env.WA_BUSINESS_URL || 'https://graph.facebook.com',
VERSION: process.env.WA_BUSINESS_VERSION || '', VERSION: process.env.WA_BUSINESS_VERSION || 'v19.0',
LANGUAGE: process.env.WA_BUSINESS_LANGUAGE || 'en', LANGUAGE: process.env.WA_BUSINESS_LANGUAGE || 'en',
}, },
LOG: { LOG: {

View File

@ -25,7 +25,7 @@ info:
</font> </font>
[![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442) [![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442)
version: 1.8.0 version: 1.8.2
contact: contact:
name: DavidsonGomes name: DavidsonGomes
email: contato@agenciadgcode.com email: contato@agenciadgcode.com

View File

@ -12,7 +12,7 @@ import { initIO } from './api/integrations/websocket/libs/socket.server';
import { ProviderFiles } from './api/provider/sessions'; 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, ProviderSession, Rabbitmq, Sqs, Webhook } from './config/env.config';
import { onUnexpectedError } from './config/error.config'; import { onUnexpectedError } from './config/error.config';
import { Logger } from './config/logger.config'; import { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config'; import { ROOT_DIR } from './config/path.config';
@ -27,9 +27,13 @@ async function bootstrap() {
const logger = new Logger('SERVER'); const logger = new Logger('SERVER');
const app = express(); const app = express();
const providerFiles = new ProviderFiles(configService); let providerFiles: ProviderFiles = null;
if (configService.get<ProviderSession>('PROVIDER')?.ENABLED) {
providerFiles = new ProviderFiles(configService);
await providerFiles.onModuleInit(); await providerFiles.onModuleInit();
logger.info('Provider:Files - ON'); logger.info('Provider:Files - ON');
}
app.use( app.use(
cors({ cors({

View File

@ -1,29 +1,55 @@
import { import { AuthenticationState, BufferJSON, initAuthCreds, WAProto as proto } from 'baileys';
AuthenticationCreds, import fs from 'fs/promises';
AuthenticationState, import path from 'path';
BufferJSON,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { configService, Database } from '../config/env.config'; import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
import { INSTANCE_DIR } from '../config/path.config';
import { dbserver } from '../libs/db.connect'; import { dbserver } from '../libs/db.connect';
const fixFileName = (file) => {
if (!file) {
return undefined;
}
const replacedSlash = file.replace(/\//g, '__');
const replacedColon = replacedSlash.replace(/:/g, '-');
return replacedColon;
};
async function fileExists(file) {
try {
const stat = await fs.stat(file);
if (stat.isFile()) return true;
} catch (error) {
return;
}
}
export async function useMultiFileAuthStateDb( export async function useMultiFileAuthStateDb(
coll: string, coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> { ): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateDb.name);
const client = dbserver.getClient(); const client = dbserver.getClient();
const logger = new Logger(useMultiFileAuthStateDb.name);
const collection = client const collection = client
.db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances') .db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
.collection(coll); .collection(coll);
const writeData = async (data: any, key: string): Promise<any> => { const sessionId = coll;
const localFolder = path.join(INSTANCE_DIR, sessionId);
const localFile = (key: string) => path.join(localFolder, fixFileName(key) + '.json');
await fs.mkdir(localFolder, { recursive: true });
async function writeData(data: any, key: string): Promise<any> {
try { try {
const dataString = JSON.stringify(data, BufferJSON.replacer);
if (key != 'creds') {
await fs.writeFile(localFile(key), dataString);
return;
}
await client.connect(); await client.connect();
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer)); let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
if (Array.isArray(msgParsed)) { if (Array.isArray(msgParsed)) {
@ -37,11 +63,19 @@ export async function useMultiFileAuthStateDb(
}); });
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
return;
}
} }
};
const readData = async (key: string): Promise<any> => { async function readData(key: string): Promise<any> {
try { try {
if (key != 'creds') {
if (!(await fileExists(localFile(key)))) return null;
const rawData = await fs.readFile(localFile(key), { encoding: 'utf-8' });
const parsedData = JSON.parse(rawData, BufferJSON.reviver);
return parsedData;
} else {
await client.connect(); await client.connect();
let data = (await collection.findOne({ _id: key })) as any; let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) { if (data?.content_array) {
@ -49,30 +83,39 @@ export async function useMultiFileAuthStateDb(
} }
const creds = JSON.stringify(data); const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver); return JSON.parse(creds, BufferJSON.reviver);
}
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
return null;
}
} }
};
const removeData = async (key: string) => { async function removeData(key: string): Promise<any> {
try { try {
if (key != 'creds') {
await fs.unlink(localFile(key));
} else {
await client.connect(); await client.connect();
return await collection.deleteOne({ _id: key }); return await collection.deleteOne({ _id: key });
}
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
return;
}
} }
};
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds(); let creds = await readData('creds');
if (!creds) {
creds = initAuthCreds();
await writeData(creds, 'creds');
}
return { return {
state: { state: {
creds, creds,
keys: { keys: {
get: async (type, ids: string[]) => { get: async (type, ids) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment const data = {};
// @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all( await Promise.all(
ids.map(async (id) => { ids.map(async (id) => {
let value = await readData(`${type}-${id}`); let value = await readData(`${type}-${id}`);
@ -83,25 +126,24 @@ export async function useMultiFileAuthStateDb(
data[id] = value; data[id] = value;
}), }),
); );
return data; return data;
}, },
set: async (data: any) => { set: async (data) => {
const tasks: Promise<void>[] = []; const tasks = [];
for (const category in data) { for (const category in data) {
for (const id in data[category]) { for (const id in data[category]) {
const value = data[category][id]; const value = data[category][id];
const key = `${category}-${id}`; const key = `${category}-${id}`;
tasks.push(value ? writeData(value, key) : removeData(key)); tasks.push(value ? writeData(value, key) : removeData(key));
} }
} }
await Promise.all(tasks); await Promise.all(tasks);
}, },
}, },
}, },
saveCreds: async () => { saveCreds: () => {
return await writeData(creds, 'creds'); return writeData(creds, 'creds');
}, },
}; };
} }

View File

@ -34,14 +34,7 @@
* *
*/ */
import { import { AuthenticationCreds, AuthenticationState, BufferJSON, initAuthCreds, proto, SignalDataTypeMap } from 'baileys';
AuthenticationCreds,
AuthenticationState,
BufferJSON,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { isNotEmpty } from 'class-validator'; import { isNotEmpty } from 'class-validator';
import { ProviderFiles } from '../api/provider/sessions'; import { ProviderFiles } from '../api/provider/sessions';

View File

@ -1,10 +1,4 @@
import { import { AuthenticationCreds, AuthenticationState, initAuthCreds, proto, SignalDataTypeMap } from 'baileys';
AuthenticationCreds,
AuthenticationState,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { CacheService } from '../api/services/cache.service'; import { CacheService } from '../api/services/cache.service';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';