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)
### Feature

View File

@ -1,6 +1,6 @@
{
"name": "evolution-api",
"version": "1.8.1",
"version": "1.8.2",
"description": "Rest api for communication with WhatsApp",
"main": "./dist/src/main.js",
"scripts": {
@ -49,7 +49,7 @@
"amqplib": "^0.10.3",
"@aws-sdk/client-sqs": "^3.569.0",
"axios": "^1.6.5",
"@whiskeysockets/baileys": "6.7.4",
"baileys": "github:EvolutionAPI/Baileys",
"class-validator": "^0.14.1",
"compression": "^1.7.4",
"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 EventEmitter2 from 'eventemitter2';
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 {
constructor(

View File

@ -1,4 +1,4 @@
import { WAPresence } from '@whiskeysockets/baileys';
import { WAPresence } from 'baileys';
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 {
key: proto.IMessageKey;

View File

@ -8,8 +8,8 @@ import ChatwootClient, {
inbox,
} from '@figuro/chatwoot-sdk';
import { request as chatwootRequest } from '@figuro/chatwoot-sdk/dist/core/request';
import { proto } from '@whiskeysockets/baileys';
import axios from 'axios';
import { proto } from 'baileys';
import FormData from 'form-data';
import { createReadStream, unlinkSync, writeFileSync } from 'fs';
import Jimp from 'jimp';
@ -444,8 +444,7 @@ export class ChatwootService {
const searchableFields = this.getSearchableFields();
// 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);
if (contact) {
return contact;
@ -736,7 +735,12 @@ export class ChatwootService {
}
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) {
this.logger.warn('inbox not found');

View File

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

View File

@ -1,5 +1,5 @@
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 { Logger } from '../config/logger.config';
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());
const chatwootCache = new CacheService(new CacheEngine(configService, ChatwootService.name).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(
eventEmitter,

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import ffmpegPath from '@ffmpeg-installer/ffmpeg';
import { Boom } from '@hapi/boom';
import axios from 'axios';
import makeWASocket, {
AnyMessageContent,
BufferedEventData,
@ -19,6 +20,7 @@ import makeWASocket, {
GroupMetadata,
isJidBroadcast,
isJidGroup,
isJidNewsletter,
isJidUser,
makeCacheableSignalKeyStore,
MessageUpsertType,
@ -35,10 +37,9 @@ import makeWASocket, {
WAMessageUpdate,
WAPresence,
WASocket,
} from '@whiskeysockets/baileys';
import { Label } from '@whiskeysockets/baileys/lib/Types/Label';
import { LabelAssociation } from '@whiskeysockets/baileys/lib/Types/LabelAssociation';
import axios from 'axios';
} from 'baileys';
import { Label } from 'baileys/lib/Types/Label';
import { LabelAssociation } from 'baileys/lib/Types/LabelAssociation';
import { exec } from 'child_process';
import { isBase64, isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2';
@ -253,8 +254,8 @@ export class BaileysStartupService extends ChannelStartupService {
this.logger.verbose('Getting profile status');
const status = await this.client.fetchStatus(this.instance.wuid);
this.logger.verbose(`Profile status: ${status.status}`);
return status.status;
this.logger.verbose(`Profile status: ${status[0]?.status}`);
return status[0]?.status;
}
public get profilePictureUrl() {
@ -517,6 +518,143 @@ export class BaileysStartupService extends ChannelStartupService {
return await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name));
}
private async createClient(number?: string, mobile?: boolean): Promise<WASocket> {
this.instance.authState = await this.defineAuthState();
if (!mobile) {
this.mobile = false;
} else {
this.mobile = mobile;
}
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()];
browserOptions = { browser };
this.logger.info(`Browser: ${browser}`);
}
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,
mobile,
...browserOptions,
version,
markOnlineOnConnect: this.localSettings.always_online,
retryRequestDelayMs: 350,
maxMsgRetryCount: 4,
fireInitQueries: true,
connectTimeoutMs: 20_000,
keepAliveIntervalMs: 30_000,
qrTimeout: 45_000,
defaultQueryTimeoutMs: undefined,
emitOwnEvents: false,
shouldIgnoreJid: (jid) => {
const isGroupJid = this.localSettings.groups_ignore && isJidGroup(jid);
const isBroadcast = !this.localSettings.read_status && isJidBroadcast(jid);
const isNewsletter = isJidNewsletter(jid);
return isGroupJid || isBroadcast || isNewsletter;
},
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: 5, delayBetweenTriesMs: 2500 },
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.endSession = false;
this.logger.verbose('Creating socket');
this.client = makeWASocket(socketConfig);
this.logger.verbose('Socket created');
this.eventHandler();
this.logger.verbose('Socket event handler initialized');
this.phoneNumber = number;
return this.client;
}
public async connectToWhatsapp(number?: string, mobile?: boolean): Promise<WASocket> {
this.logger.verbose('Connecting to whatsapp');
try {
@ -530,126 +668,7 @@ export class BaileysStartupService extends ChannelStartupService {
this.loadProxy();
this.loadChamaai();
this.instance.authState = await this.defineAuthState();
if (!mobile) {
this.mobile = false;
} else {
this.mobile = mobile;
}
const session = this.configService.get<ConfigSessionPhone>('CONFIG_SESSION_PHONE');
const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()];
this.logger.verbose('Browser: ' + JSON.stringify(browser));
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,
mobile,
browser: number ? ['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.endSession = false;
this.logger.verbose('Creating socket');
this.client = makeWASocket(socketConfig);
this.logger.verbose('Socket created');
this.eventHandler();
this.logger.verbose('Socket event handler initialized');
this.phoneNumber = number;
return this.client;
return await this.createClient(number, mobile);
} catch (error) {
this.logger.error(error);
throw new InternalServerErrorException(error?.toString());
@ -716,106 +735,7 @@ export class BaileysStartupService extends ChannelStartupService {
public async reloadConnection(): Promise<WASocket> {
try {
this.instance.authState = await this.defineAuthState();
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;
return await this.createClient(this.phoneNumber, this.mobile);
} catch (error) {
this.logger.error(error);
throw new InternalServerErrorException(error?.toString());
@ -1053,7 +973,7 @@ export class BaileysStartupService extends ChannelStartupService {
m.messageTimestamp = m.messageTimestamp?.toNumber();
}
if (m.messageTimestamp <= timestampLimitToImport) {
if ((m.messageTimestamp as number) <= timestampLimitToImport) {
continue;
}
@ -1178,6 +1098,7 @@ export class BaileysStartupService extends ChannelStartupService {
received?.message?.videoMessage ||
received?.message?.stickerMessage ||
received?.message?.documentMessage ||
received?.message?.documentWithCaptionMessage ||
received?.message?.audioMessage;
const contentMsg = received?.message[getContentType(received.message)] as any;
@ -1729,7 +1650,7 @@ export class BaileysStartupService extends ChannelStartupService {
this.logger.verbose('Getting status');
return {
wuid: jid,
status: (await this.client.fetchStatus(jid))?.status,
status: (await this.client.fetchStatus(jid))[0]?.status,
};
} catch (error) {
this.logger.verbose('Status not found');
@ -1939,11 +1860,9 @@ export class BaileysStartupService extends ChannelStartupService {
} as unknown as AnyMessageContent,
{
...option,
cachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
? null
: this.getGroupMetadataCache,
useCachedGroupMetadata:
!!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
} as unknown as MiscMessageGenerationOptions,
);
}
@ -1959,11 +1878,9 @@ export class BaileysStartupService extends ChannelStartupService {
} as unknown as AnyMessageContent,
{
...option,
cachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
? null
: this.getGroupMetadataCache,
useCachedGroupMetadata:
!!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
} as unknown as MiscMessageGenerationOptions,
);
}
@ -1981,11 +1898,9 @@ export class BaileysStartupService extends ChannelStartupService {
},
{
...option,
cachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
? null
: this.getGroupMetadataCache,
useCachedGroupMetadata:
!!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
} as unknown as MiscMessageGenerationOptions,
);
}
@ -2009,11 +1924,9 @@ export class BaileysStartupService extends ChannelStartupService {
message as unknown as AnyMessageContent,
{
...option,
cachedGroupMetadata:
!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED
? null
: this.getGroupMetadataCache,
useCachedGroupMetadata:
!!this.configService.get<CacheConf>('CACHE').REDIS.ENABLED &&
!!this.configService.get<CacheConf>('CACHE').LOCAL.ENABLED,
} as unknown as MiscMessageGenerationOptions,
);
})();
@ -3347,6 +3260,10 @@ export class BaileysStartupService extends ChannelStartupService {
}
public async findGroup(id: GroupJid, reply: 'inner' | 'out' = 'out') {
if (this.localSettings.groups_ignore === true) {
return;
}
this.logger.verbose('Fetching group');
try {
const group = await this.client.groupMetadata(id.groupJid);
@ -3377,6 +3294,10 @@ export class BaileysStartupService extends ChannelStartupService {
}
public async fetchAllGroups(getParticipants: GetParticipant) {
if (this.localSettings.groups_ignore === true) {
return;
}
this.logger.verbose('Fetching all groups');
try {
const fetch = Object.values(await this.client.groupFetchAllParticipating());

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ info:
</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)
version: 1.8.0
version: 1.8.2
contact:
name: DavidsonGomes
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 { HttpStatus, router } from './api/routes/index.router';
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 { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config';
@ -27,9 +27,13 @@ async function bootstrap() {
const logger = new Logger('SERVER');
const app = express();
const providerFiles = new ProviderFiles(configService);
await providerFiles.onModuleInit();
logger.info('Provider:Files - ON');
let providerFiles: ProviderFiles = null;
if (configService.get<ProviderSession>('PROVIDER')?.ENABLED) {
providerFiles = new ProviderFiles(configService);
await providerFiles.onModuleInit();
logger.info('Provider:Files - ON');
}
app.use(
cors({

View File

@ -1,29 +1,55 @@
import {
AuthenticationCreds,
AuthenticationState,
BufferJSON,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { AuthenticationState, BufferJSON, initAuthCreds, WAProto as proto } from 'baileys';
import fs from 'fs/promises';
import path from 'path';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
import { INSTANCE_DIR } from '../config/path.config';
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(
coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateDb.name);
const client = dbserver.getClient();
const logger = new Logger(useMultiFileAuthStateDb.name);
const collection = client
.db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
.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 {
const dataString = JSON.stringify(data, BufferJSON.replacer);
if (key != 'creds') {
await fs.writeFile(localFile(key), dataString);
return;
}
await client.connect();
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
if (Array.isArray(msgParsed)) {
@ -37,42 +63,59 @@ export async function useMultiFileAuthStateDb(
});
} catch (error) {
logger.error(error);
return;
}
};
}
const readData = async (key: string): Promise<any> => {
async function readData(key: string): Promise<any> {
try {
await client.connect();
let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) {
data = data.content_array;
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();
let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) {
data = data.content_array;
}
const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver);
}
const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver);
} catch (error) {
logger.error(error);
return null;
}
};
}
const removeData = async (key: string) => {
async function removeData(key: string): Promise<any> {
try {
await client.connect();
return await collection.deleteOne({ _id: key });
if (key != 'creds') {
await fs.unlink(localFile(key));
} else {
await client.connect();
return await collection.deleteOne({ _id: key });
}
} catch (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 {
state: {
creds,
keys: {
get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {};
get: async (type, ids) => {
const data = {};
await Promise.all(
ids.map(async (id) => {
let value = await readData(`${type}-${id}`);
@ -83,25 +126,24 @@ export async function useMultiFileAuthStateDb(
data[id] = value;
}),
);
return data;
},
set: async (data: any) => {
const tasks: Promise<void>[] = [];
set: async (data) => {
const tasks = [];
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id];
const key = `${category}-${id}`;
tasks.push(value ? writeData(value, key) : removeData(key));
}
}
await Promise.all(tasks);
},
},
},
saveCreds: async () => {
return await writeData(creds, 'creds');
saveCreds: () => {
return writeData(creds, 'creds');
},
};
}

View File

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

View File

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