Skip to content

Commit

Permalink
New method of saving sessions to a file using worker
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidsonGomes committed Jun 1, 2024
1 parent f7c9541 commit 696261d
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 1.8.1 (develop)

### Feature
* New method of saving sessions to a file using worker, made in partnership with [codechat](https://github.com/code-chat-br/whatsapp-api)

# 1.8.0 (2024-05-27 16:10)

### Feature
Expand Down
7 changes: 2 additions & 5 deletions Docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ CLEAN_STORE_CHATS=true

# Permanent data storage
DATABASE_ENABLED=false
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin &
readPreference=primary &
ssl=false &
directConnection=true
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker

# Choose the data you want to save in the application's database or store
Expand Down Expand Up @@ -137,7 +134,7 @@ CONFIG_SESSION_PHONE_NAME=Chrome

# Set qrcode display limit
QRCODE_LIMIT=30
QRCODE_COLOR=#198754
QRCODE_COLOR='#198754'

# old | latest
TYPEBOT_API_VERSION=latest
Expand Down
4 changes: 4 additions & 0 deletions src/api/controllers/instance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { RabbitmqService } from '../integrations/rabbitmq/services/rabbitmq.serv
import { SqsService } from '../integrations/sqs/services/sqs.service';
import { TypebotService } from '../integrations/typebot/services/typebot.service';
import { WebsocketService } from '../integrations/websocket/services/websocket.service';
import { ProviderFiles } from '../provider/sessions';
import { RepositoryBroker } from '../repository/repository.manager';
import { AuthService, OldToken } from '../services/auth.service';
import { CacheService } from '../services/cache.service';
Expand Down Expand Up @@ -43,6 +44,7 @@ export class InstanceController {
private readonly cache: CacheService,
private readonly chatwootCache: CacheService,
private readonly messagesLostCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {}

private readonly logger = new Logger(InstanceController.name);
Expand Down Expand Up @@ -111,6 +113,7 @@ export class InstanceController {
this.cache,
this.chatwootCache,
this.messagesLostCache,
this.providerFiles,
);
} else {
instance = new BaileysStartupService(
Expand All @@ -120,6 +123,7 @@ export class InstanceController {
this.cache,
this.chatwootCache,
this.messagesLostCache,
this.providerFiles,
);
}

Expand Down
140 changes: 140 additions & 0 deletions src/api/provider/sessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import axios from 'axios';
import { execSync } from 'child_process';

import { 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`;
}

private readonly logger = new Logger(ProviderFiles.name);

private baseUrl: string;

private readonly config = Object.freeze(this.configService.get<ProviderSession>('PROVIDER'));

private readonly prefix = Object.freeze(this.configService.get<ProviderSession>('PROVIDER').PREFIX);

get isEnabled() {
return !!this.config?.ENABLED;
}

public async onModuleInit() {
if (this.config.ENABLED) {
const client = axios.create({
baseURL: this.baseUrl,
});
try {
const response = await client.options('/ping');
if (!response?.data?.pong) {
throw new Error('Offline file provider.');
}
} 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}/${this.prefix}`, {
instance,
});
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}/${this.prefix}/${instance}/${key}`, data);
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}/${this.prefix}/${instance}/${key}`);
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}/${this.prefix}/${instance}/${key}`);
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/${this.prefix}`);
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}/${this.prefix}/${instance}`);
return [{ status: response.status, data: response?.data }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
}
4 changes: 4 additions & 0 deletions src/api/server.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
WebsocketModel,
} from './models';
import { LabelModel } from './models/label.model';
import { ProviderFiles } from './provider/sessions';
import { AuthRepository } from './repository/auth.repository';
import { ChatRepository } from './repository/chat.repository';
import { ContactRepository } from './repository/contact.repository';
Expand Down Expand Up @@ -109,6 +110,7 @@ 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 messagesLostCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine());
const providerFiles = new ProviderFiles(configService);

export const waMonitor = new WAMonitoringService(
eventEmitter,
Expand All @@ -117,6 +119,7 @@ export const waMonitor = new WAMonitoringService(
cache,
chatwootCache,
messagesLostCache,
providerFiles,
);

const authService = new AuthService(configService, waMonitor, repository);
Expand Down Expand Up @@ -168,6 +171,7 @@ export const instanceController = new InstanceController(
cache,
chatwootCache,
messagesLostCache,
providerFiles,
);
export const sendMessageController = new SendMessageController(waMonitor);
export const chatController = new ChatController(waMonitor);
Expand Down
21 changes: 20 additions & 1 deletion src/api/services/channels/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,21 @@ import qrcode, { QRCodeToDataURLOptions } from 'qrcode';
import qrcodeTerminal from 'qrcode-terminal';
import sharp from 'sharp';

import { CacheConf, ConfigService, ConfigSessionPhone, Database, Log, QrCode } from '../../../config/env.config';
import {
CacheConf,
ConfigService,
ConfigSessionPhone,
Database,
Log,
ProviderSession,
QrCode,
} from '../../../config/env.config';
import { INSTANCE_DIR } from '../../../config/path.config';
import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../../exceptions';
import { dbserver } from '../../../libs/db.connect';
import { makeProxyAgent } from '../../../utils/makeProxyAgent';
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 {
ArchiveChatDto,
Expand Down Expand Up @@ -114,6 +123,7 @@ import { SettingsRaw } from '../../models';
import { ChatRaw } from '../../models/chat.model';
import { ContactRaw } from '../../models/contact.model';
import { MessageRaw, MessageUpdateRaw } from '../../models/message.model';
import { ProviderFiles } from '../../provider/sessions';
import { RepositoryBroker } from '../../repository/repository.manager';
import { waMonitor } from '../../server.module';
import { Events, MessageSubtype, TypeMediaMessage, wa } from '../../types/wa.types';
Expand All @@ -128,15 +138,18 @@ export class BaileysStartupService extends ChannelStartupService {
public readonly cache: CacheService,
public readonly chatwootCache: CacheService,
public readonly messagesLostCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {
super(configService, eventEmitter, repository, chatwootCache);
this.logger.verbose('BaileysStartupService initialized');
this.cleanStore();
this.instance.qrcode = { count: 0 };
this.mobile = false;
this.recoveringMessages();
this.authStateProvider = new AuthStateProvider(this.configService, this.providerFiles);
}

private authStateProvider: AuthStateProvider;
private readonly msgRetryCounterCache: CacheStore = new NodeCache();
private readonly userDevicesCache: CacheStore = new NodeCache();
private endSession = false;
Expand Down Expand Up @@ -461,6 +474,12 @@ export class BaileysStartupService extends ChannelStartupService {
const db = this.configService.get<Database>('DATABASE');
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) {
this.logger.info('Redis enabled');
return await useMultiFileAuthStateRedisDb(this.instance.name, this.cache);
Expand Down
2 changes: 2 additions & 0 deletions src/api/services/channels/whatsapp.business.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
SendTextDto,
} from '../../dto/sendMessage.dto';
import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../../models';
import { ProviderFiles } from '../../provider/sessions';
import { RepositoryBroker } from '../../repository/repository.manager';
import { Events, wa } from '../../types/wa.types';
import { CacheService } from './../cache.service';
Expand All @@ -36,6 +37,7 @@ export class BusinessStartupService extends ChannelStartupService {
public readonly cache: CacheService,
public readonly chatwootCache: CacheService,
public readonly messagesLostCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {
super(configService, eventEmitter, repository, chatwootCache);
this.logger.verbose('BusinessStartupService initialized');
Expand Down
4 changes: 4 additions & 0 deletions src/api/services/monitor.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
WebhookModel,
WebsocketModel,
} from '../models';
import { ProviderFiles } from '../provider/sessions';
import { RepositoryBroker } from '../repository/repository.manager';
import { Integration } from '../types/wa.types';
import { CacheService } from './cache.service';
Expand All @@ -36,6 +37,7 @@ export class WAMonitoringService {
private readonly cache: CacheService,
private readonly chatwootCache: CacheService,
private readonly messagesLostCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {
this.logger.verbose('instance created');

Expand Down Expand Up @@ -349,6 +351,7 @@ export class WAMonitoringService {
this.cache,
this.chatwootCache,
this.messagesLostCache,
this.providerFiles,
);

instance.instanceName = name;
Expand All @@ -360,6 +363,7 @@ export class WAMonitoringService {
this.cache,
this.chatwootCache,
this.messagesLostCache,
this.providerFiles,
);

instance.instanceName = name;
Expand Down
14 changes: 14 additions & 0 deletions src/config/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export type Log = {
BAILEYS: LogBaileys;
};

export type ProviderSession = {
ENABLED: boolean;
HOST: string;
PORT: string;
PREFIX: string;
};

export type SaveData = {
INSTANCE: boolean;
NEW_MESSAGE: boolean;
Expand Down Expand Up @@ -209,6 +216,7 @@ export interface Env {
SERVER: HttpServer;
CORS: Cors;
SSL_CONF: SslConf;
PROVIDER: ProviderSession;
STORE: StoreConf;
CLEAN_STORE: CleanStoreConf;
DATABASE: Database;
Expand Down Expand Up @@ -274,6 +282,12 @@ export class ConfigService {
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
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: {
MESSAGES: process.env?.STORE_MESSAGES === 'true',
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
Expand Down
8 changes: 8 additions & 0 deletions src/dev-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ LOG:
DEL_INSTANCE: false # or false
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
STORE:
MESSAGES: true
Expand Down
Loading

0 comments on commit 696261d

Please sign in to comment.