feat: whatsapp cloud api

This commit is contained in:
Davidson Gomes
2024-02-17 17:42:49 -03:00
parent 3a37fd9d32
commit 0525501b87
24 changed files with 4768 additions and 3231 deletions
+5
View File
@@ -51,6 +51,11 @@ RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
WEBSOCKET_ENABLED=false WEBSOCKET_ENABLED=false
WA_BUSINESS_TOKEN_WEBHOOK=evolution
WA_BUSINESS_URL=https://graph.facebook.com
WA_BUSINESS_VERSION=v18.0
WA_BUSINESS_LANGUAGE=pt_BR
SQS_ENABLED=false SQS_ENABLED=false
SQS_ACCESS_KEY_ID= SQS_ACCESS_KEY_ID=
SQS_SECRET_ACCESS_KEY= SQS_SECRET_ACCESS_KEY=
+5
View File
@@ -66,6 +66,11 @@ ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
ENV WEBSOCKET_ENABLED=false ENV WEBSOCKET_ENABLED=false
ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution
ENV WA_BUSINESS_URL=https://graph.facebook.com
ENV WA_BUSINESS_VERSION=v18.0
ENV WA_BUSINESS_LANGUAGE=pt_BR
ENV SQS_ENABLED=false ENV SQS_ENABLED=false
ENV SQS_ACCESS_KEY_ID= ENV SQS_ACCESS_KEY_ID=
ENV SQS_SECRET_ACCESS_KEY= ENV SQS_SECRET_ACCESS_KEY=
+14
View File
@@ -86,6 +86,13 @@ export type Websocket = {
ENABLED: boolean; ENABLED: boolean;
}; };
export type WaBusiness = {
TOKEN_WEBHOOK: string;
URL: string;
VERSION: string;
LANGUAGE: string;
};
export type EventsWebhook = { export type EventsWebhook = {
APPLICATION_STARTUP: boolean; APPLICATION_STARTUP: boolean;
INSTANCE_CREATE: boolean; INSTANCE_CREATE: boolean;
@@ -179,6 +186,7 @@ export interface Env {
RABBITMQ: Rabbitmq; RABBITMQ: Rabbitmq;
SQS: Sqs; SQS: Sqs;
WEBSOCKET: Websocket; WEBSOCKET: Websocket;
WA_BUSINESS: WaBusiness;
LOG: Log; LOG: Log;
DEL_INSTANCE: DelInstance; DEL_INSTANCE: DelInstance;
LANGUAGE: Language; LANGUAGE: Language;
@@ -286,6 +294,12 @@ export class ConfigService {
WEBSOCKET: { WEBSOCKET: {
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true', ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
}, },
WA_BUSINESS: {
TOKEN_WEBHOOK: process.env.WA_BUSINESS_TOKEN_WEBHOOK || '',
URL: process.env.WA_BUSINESS_URL || '',
VERSION: process.env.WA_BUSINESS_VERSION || '',
LANGUAGE: process.env.WA_BUSINESS_LANGUAGE || 'en',
},
LOG: { LOG: {
LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [ LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [
'ERROR', 'ERROR',
+7 -3
View File
@@ -12,7 +12,6 @@ SERVER:
DISABLE_MANAGER: false DISABLE_MANAGER: false
DISABLE_DOCS: false DISABLE_DOCS: false
CORS: CORS:
ORIGIN: ORIGIN:
- "*" - "*"
@@ -96,6 +95,12 @@ SQS:
WEBSOCKET: WEBSOCKET:
ENABLED: false ENABLED: false
WA_BUSINESS:
TOKEN_WEBHOOK: evolution
URL: https://graph.facebook.com
VERSION: v18.0
LANGUAGE: pt_BR
# Global Webhook Settings # Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created # Each instance's Webhook URL and events will be requested at the time it is created
WEBHOOK: WEBHOOK:
@@ -152,7 +157,7 @@ QRCODE:
COLOR: "#198754" COLOR: "#198754"
TYPEBOT: TYPEBOT:
API_VERSION: 'old' # old | latest API_VERSION: "old" # old | latest
KEEP_OPEN: false KEEP_OPEN: false
CHATWOOT: CHATWOOT:
@@ -192,5 +197,4 @@ AUTHENTICATION:
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
SECRET: L=0YWt]b2w[WF>#>:&E` SECRET: L=0YWt]b2w[WF>#>:&E`
LANGUAGE: "pt-BR" # pt-BR, en LANGUAGE: "pt-BR" # pt-BR, en
+19
View File
@@ -277,6 +277,25 @@ export const audioMessageSchema: JSONSchema7 = {
required: ['audioMessage', 'number'], required: ['audioMessage', 'number'],
}; };
export const templateMessageSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
number: { ...numberDefinition },
options: { ...optionsSchema },
templateMessage: {
type: 'object',
properties: {
name: { type: 'string' },
language: { type: 'string' },
},
required: ['name', 'language'],
...isNotEmpty('name', 'language'),
},
},
required: ['templateMessage', 'number'],
};
export const buttonMessageSchema: JSONSchema7 = { export const buttonMessageSchema: JSONSchema7 = {
$id: v4(), $id: v4(),
type: 'object', type: 'object',
+54 -10
View File
@@ -3,7 +3,7 @@ import { isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { ConfigService, HttpServer } from '../../config/env.config'; import { ConfigService, HttpServer, WaBusiness } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { BadRequestException, InternalServerErrorException } from '../../exceptions'; import { BadRequestException, InternalServerErrorException } from '../../exceptions';
import { RedisCache } from '../../libs/redis.client'; import { RedisCache } from '../../libs/redis.client';
@@ -12,6 +12,7 @@ import { RepositoryBroker } from '../repository/repository.manager';
import { AuthService, OldToken } from '../services/auth.service'; import { AuthService, OldToken } from '../services/auth.service';
import { CacheService } from '../services/cache.service'; import { CacheService } from '../services/cache.service';
import { ChatwootService } from '../services/chatwoot.service'; import { ChatwootService } from '../services/chatwoot.service';
import { IntegrationService } from '../services/integration.service';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
import { RabbitmqService } from '../services/rabbitmq.service'; import { RabbitmqService } from '../services/rabbitmq.service';
import { SettingsService } from '../services/settings.service'; import { SettingsService } from '../services/settings.service';
@@ -19,8 +20,9 @@ import { SqsService } from '../services/sqs.service';
import { TypebotService } from '../services/typebot.service'; import { TypebotService } from '../services/typebot.service';
import { WebhookService } from '../services/webhook.service'; import { WebhookService } from '../services/webhook.service';
import { WebsocketService } from '../services/websocket.service'; import { WebsocketService } from '../services/websocket.service';
import { WAStartupService } from '../services/whatsapp.service'; import { BaileysStartupService } from '../services/whatsapp.baileys.service';
import { Events, wa } from '../types/wa.types'; import { BusinessStartupService } from '../services/whatsapp.business.service';
import { Events, Integration, wa } from '../types/wa.types';
export class InstanceController { export class InstanceController {
constructor( constructor(
@@ -36,6 +38,7 @@ export class InstanceController {
private readonly rabbitmqService: RabbitmqService, private readonly rabbitmqService: RabbitmqService,
private readonly sqsService: SqsService, private readonly sqsService: SqsService,
private readonly typebotService: TypebotService, private readonly typebotService: TypebotService,
private readonly integrationService: IntegrationService,
private readonly cache: RedisCache, private readonly cache: RedisCache,
private readonly chatwootCache: CacheService, private readonly chatwootCache: CacheService,
) {} ) {}
@@ -50,6 +53,7 @@ export class InstanceController {
events, events,
qrcode, qrcode,
number, number,
integration,
token, token,
chatwoot_account_id, chatwoot_account_id,
chatwoot_token, chatwoot_token,
@@ -87,14 +91,31 @@ export class InstanceController {
this.logger.verbose('checking duplicate token'); this.logger.verbose('checking duplicate token');
await this.authService.checkDuplicateToken(token); await this.authService.checkDuplicateToken(token);
if (!token && integration !== Integration.WHATSAPP_BUSINESS) {
throw new BadRequestException('token is required');
}
this.logger.verbose('creating instance'); this.logger.verbose('creating instance');
const instance = new WAStartupService( let instance: BaileysStartupService | BusinessStartupService;
this.configService, if (integration === Integration.WHATSAPP_BUSINESS) {
this.eventEmitter, instance = new BusinessStartupService(
this.repository, this.configService,
this.cache, this.eventEmitter,
this.chatwootCache, this.repository,
); this.cache,
this.chatwootCache,
);
await this.waMonitor.saveInstance({ integration, instanceName, token, number });
} else {
instance = new BaileysStartupService(
this.configService,
this.eventEmitter,
this.repository,
this.cache,
this.chatwootCache,
);
}
instance.instanceName = instanceName; instance.instanceName = instanceName;
const instanceId = v4(); const instanceId = v4();
@@ -361,6 +382,23 @@ export class InstanceController {
this.settingsService.create(instance, settings); this.settingsService.create(instance, settings);
let webhook_wa_business = null,
access_token_wa_business = '';
if (integration === Integration.WHATSAPP_BUSINESS) {
if (!number) {
throw new BadRequestException('number is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
webhook_wa_business = `${urlServer}/webhook/whatsapp/${encodeURIComponent(instance.instanceName)}`;
access_token_wa_business = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;
}
this.integrationService.create(instance, {
integration,
number,
token,
});
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) { if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
let getQrcode: wa.QrCode; let getQrcode: wa.QrCode;
@@ -375,6 +413,9 @@ export class InstanceController {
instance: { instance: {
instanceName: instance.instanceName, instanceName: instance.instanceName,
instanceId: instanceId, instanceId: instanceId,
integration: integration,
webhook_wa_business,
access_token_wa_business,
status: 'created', status: 'created',
}, },
hash, hash,
@@ -470,6 +511,9 @@ export class InstanceController {
instance: { instance: {
instanceName: instance.instanceName, instanceName: instance.instanceName,
instanceId: instanceId, instanceId: instanceId,
integration: integration,
webhook_wa_business,
access_token_wa_business,
status: 'created', status: 'created',
}, },
hash, hash,
@@ -14,6 +14,7 @@ import {
SendReactionDto, SendReactionDto,
SendStatusDto, SendStatusDto,
SendStickerDto, SendStickerDto,
SendTemplateDto,
SendTextDto, SendTextDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
@@ -28,6 +29,11 @@ export class SendMessageController {
return await this.waMonitor.waInstances[instanceName].textMessage(data); return await this.waMonitor.waInstances[instanceName].textMessage(data);
} }
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
logger.verbose('requested sendList from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].templateMessage(data);
}
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) { public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
logger.verbose('requested sendMedia from ' + instanceName + ' instance'); logger.verbose('requested sendMedia from ' + instanceName + ' instance');
+4
View File
@@ -31,8 +31,12 @@ export class NumberBusiness {
message?: string; message?: string;
description?: string; description?: string;
email?: string; email?: string;
websites?: string[];
website?: string[]; website?: string[];
address?: string; address?: string;
about?: string;
vertical?: string;
profilehandle?: string;
} }
export class ProfileNameDto { export class ProfileNameDto {
+1
View File
@@ -3,6 +3,7 @@ export class InstanceDto {
instanceId?: string; instanceId?: string;
qrcode?: boolean; qrcode?: boolean;
number?: string; number?: string;
integration?: string;
token?: string; token?: string;
webhook?: string; webhook?: string;
webhook_by_events?: boolean; webhook_by_events?: boolean;
+5
View File
@@ -0,0 +1,5 @@
export class IntegrationDto {
integration: string;
number: string;
token: string;
}
+9
View File
@@ -142,6 +142,15 @@ export class ContactMessage {
email?: string; email?: string;
url?: string; url?: string;
} }
export class TemplateMessage {
name: string;
language: string;
}
export class SendTemplateDto extends Metadata {
templateMessage: TemplateMessage;
}
export class SendContactDto extends Metadata { export class SendContactDto extends Metadata {
contactMessage: ContactMessage[]; contactMessage: ContactMessage[];
} }
+1
View File
@@ -3,6 +3,7 @@ export * from './chamaai.model';
export * from './chat.model'; export * from './chat.model';
export * from './chatwoot.model'; export * from './chatwoot.model';
export * from './contact.model'; export * from './contact.model';
export * from './integration.model';
export * from './label.model'; export * from './label.model';
export * from './message.model'; export * from './message.model';
export * from './proxy.model'; export * from './proxy.model';
+20
View File
@@ -0,0 +1,20 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
export class IntegrationRaw {
_id?: string;
integration?: string;
number?: string;
token?: string;
}
const sqsSchema = new Schema<IntegrationRaw>({
_id: { type: String, _id: true },
integration: { type: String, required: true },
number: { type: String, required: true },
token: { type: String, required: true },
});
export const IntegrationModel = dbserver?.model(IntegrationRaw.name, sqsSchema, 'integration');
export type IntegrationModel = typeof IntegrationModel;
@@ -0,0 +1,64 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository';
import { IntegrationModel, IntegrationRaw } from '../models';
export class IntegrationRepository extends Repository {
constructor(private readonly integrationModel: IntegrationModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('IntegrationRepository');
public async create(data: IntegrationRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating integration');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving integration to db');
const insert = await this.integrationModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('integration saved to db: ' + insert.modifiedCount + ' integration');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving integration to store');
this.writeStore<IntegrationRaw>({
path: join(this.storePath, 'integration'),
fileName: instance,
data,
});
this.logger.verbose(
'integration saved to store in path: ' + join(this.storePath, 'integration') + '/' + instance,
);
this.logger.verbose('integration created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<IntegrationRaw> {
try {
this.logger.verbose('finding integration');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding integration in db');
return await this.integrationModel.findOne({ _id: instance });
}
this.logger.verbose('finding integration in store');
return JSON.parse(
readFileSync(join(this.storePath, 'integration', instance + '.json'), {
encoding: 'utf-8',
}),
) as IntegrationRaw;
} catch (error) {
return {};
}
}
}
@@ -9,6 +9,7 @@ import { ChamaaiRepository } from './chamaai.repository';
import { ChatRepository } from './chat.repository'; import { ChatRepository } from './chat.repository';
import { ChatwootRepository } from './chatwoot.repository'; import { ChatwootRepository } from './chatwoot.repository';
import { ContactRepository } from './contact.repository'; import { ContactRepository } from './contact.repository';
import { IntegrationRepository } from './integration.repository';
import { LabelRepository } from './label.repository'; import { LabelRepository } from './label.repository';
import { MessageRepository } from './message.repository'; import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository'; import { MessageUpRepository } from './messageUp.repository';
@@ -34,6 +35,7 @@ export class RepositoryBroker {
public readonly typebot: TypebotRepository, public readonly typebot: TypebotRepository,
public readonly proxy: ProxyRepository, public readonly proxy: ProxyRepository,
public readonly chamaai: ChamaaiRepository, public readonly chamaai: ChamaaiRepository,
public readonly integration: IntegrationRepository,
public readonly auth: AuthRepository, public readonly auth: AuthRepository,
public readonly labels: LabelRepository, public readonly labels: LabelRepository,
private configService: ConfigService, private configService: ConfigService,
@@ -71,6 +73,7 @@ export class RepositoryBroker {
const typebotDir = join(storePath, 'typebot'); const typebotDir = join(storePath, 'typebot');
const proxyDir = join(storePath, 'proxy'); const proxyDir = join(storePath, 'proxy');
const chamaaiDir = join(storePath, 'chamaai'); const chamaaiDir = join(storePath, 'chamaai');
const integrationDir = join(storePath, 'integration');
const tempDir = join(storePath, 'temp'); const tempDir = join(storePath, 'temp');
if (!fs.existsSync(authDir)) { if (!fs.existsSync(authDir)) {
@@ -129,6 +132,10 @@ export class RepositoryBroker {
this.logger.verbose('creating chamaai dir: ' + chamaaiDir); this.logger.verbose('creating chamaai dir: ' + chamaaiDir);
fs.mkdirSync(chamaaiDir, { recursive: true }); fs.mkdirSync(chamaaiDir, { recursive: true });
} }
if (!fs.existsSync(integrationDir)) {
this.logger.verbose('creating integration dir: ' + integrationDir);
fs.mkdirSync(integrationDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) { if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir); this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true }); fs.mkdirSync(tempDir, { recursive: true });
@@ -12,6 +12,7 @@ import {
reactionMessageSchema, reactionMessageSchema,
statusMessageSchema, statusMessageSchema,
stickerMessageSchema, stickerMessageSchema,
templateMessageSchema,
textMessageSchema, textMessageSchema,
} from '../../validate/validate.schema'; } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
@@ -26,6 +27,7 @@ import {
SendReactionDto, SendReactionDto,
SendStatusDto, SendStatusDto,
SendStickerDto, SendStickerDto,
SendTemplateDto,
SendTextDto, SendTextDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { sendMessageController } from '../whatsapp.module'; import { sendMessageController } from '../whatsapp.module';
@@ -85,6 +87,22 @@ export class MessageRouter extends RouterBroker {
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}) })
.post(this.routerPath('sendTemplate'), ...guards, async (req, res) => {
logger.verbose('request received in sendTemplate');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<SendTemplateDto>({
request: req,
schema: templateMessageSchema,
ClassRef: SendTemplateDto,
execute: (instance, data) => sendMessageController.sendTemplate(instance, data),
});
return res.status(HttpStatus.CREATED).json(response);
})
.post(this.routerPath('sendButtons'), ...guards, async (req, res) => { .post(this.routerPath('sendButtons'), ...guards, async (req, res) => {
logger.verbose('request received in sendButtons'); logger.verbose('request received in sendButtons');
logger.verbose('request body: '); logger.verbose('request body: ');
@@ -0,0 +1,33 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { IntegrationDto } from '../dto/integration.dto';
import { IntegrationRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class IntegrationService {
constructor(private readonly waMonitor: WAMonitoringService) {}
private readonly logger = new Logger(IntegrationService.name);
public create(instance: InstanceDto, data: IntegrationDto) {
this.logger.verbose('create integration: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setIntegration(data);
return { integration: { ...instance, integration: data } };
}
public async find(instance: InstanceDto): Promise<IntegrationRaw> {
try {
this.logger.verbose('find integration: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findIntegration();
if (Object.keys(result).length === 0) {
throw new Error('Integration not found');
}
return result;
} catch (error) {
return { integration: '', number: '', token: '' };
}
}
}
+50 -13
View File
@@ -1,6 +1,6 @@
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
import { opendirSync, readdirSync, rmSync } from 'fs'; import { existsSync, mkdirSync, opendirSync, readdirSync, rmSync, writeFileSync } from 'fs';
import { Db } from 'mongodb'; import { Db } from 'mongodb';
import { Collection } from 'mongoose'; import { Collection } from 'mongoose';
import { join } from 'path'; import { join } from 'path';
@@ -24,8 +24,10 @@ import {
WebsocketModel, WebsocketModel,
} from '../models'; } from '../models';
import { RepositoryBroker } from '../repository/repository.manager'; import { RepositoryBroker } from '../repository/repository.manager';
import { Integration } from '../types/wa.types';
import { CacheService } from './cache.service'; import { CacheService } from './cache.service';
import { WAStartupService } from './whatsapp.service'; import { BaileysStartupService } from './whatsapp.baileys.service';
import { BusinessStartupService } from './whatsapp.business.service';
export class WAMonitoringService { export class WAMonitoringService {
constructor( constructor(
@@ -54,7 +56,7 @@ export class WAMonitoringService {
private dbInstance: Db; private dbInstance: Db;
private readonly logger = new Logger(WAMonitoringService.name); private readonly logger = new Logger(WAMonitoringService.name);
public readonly waInstances: Record<string, WAStartupService> = {}; public readonly waInstances: Record<string, BaileysStartupService | BusinessStartupService> = {};
public delInstanceTime(instance: string) { public delInstanceTime(instance: string) {
const time = this.configService.get<DelInstance>('DEL_INSTANCE'); const time = this.configService.get<DelInstance>('DEL_INSTANCE');
@@ -64,9 +66,11 @@ export class WAMonitoringService {
setTimeout(async () => { setTimeout(async () => {
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') { if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') { if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') {
await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance); if ((await this.waInstances[instance].findIntegration()).integration === Integration.WHATSAPP_BAILEYS) {
this.waInstances[instance]?.client?.ws?.close(); await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance);
this.waInstances[instance]?.client?.end(undefined); this.waInstances[instance]?.client?.ws?.close();
this.waInstances[instance]?.client?.end(undefined);
}
this.waInstances[instance]?.removeRabbitmqQueues(); this.waInstances[instance]?.removeRabbitmqQueues();
delete this.waInstances[instance]; delete this.waInstances[instance];
} else { } else {
@@ -353,14 +357,47 @@ export class WAMonitoringService {
} }
} }
public async saveInstance(data: any) {
this.logger.verbose('Save instance');
try {
const msgParsed = JSON.parse(JSON.stringify(data));
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
await this.repository.dbServer.connect();
await this.dbInstance.collection(data.instanceName).replaceOne({ _id: 'integration' }, msgParsed, {
upsert: true,
});
} else {
const path = join(INSTANCE_DIR, data.instanceName);
if (!existsSync(path)) mkdirSync(path, { recursive: true });
writeFileSync(path + '/integration.json', JSON.stringify(msgParsed));
}
} catch (error) {
this.logger.error(error);
}
}
private async setInstance(name: string) { private async setInstance(name: string) {
const instance = new WAStartupService( const integration = await this.repository.integration.find(name);
this.configService,
this.eventEmitter, let instance: BaileysStartupService | BusinessStartupService;
this.repository, if (integration.integration === Integration.WHATSAPP_BUSINESS) {
this.cache, instance = new BusinessStartupService(
this.chatwootCache, this.configService,
); this.eventEmitter,
this.repository,
this.cache,
this.chatwootCache,
);
} else {
instance = new BaileysStartupService(
this.configService,
this.eventEmitter,
this.repository,
this.cache,
this.chatwootCache,
);
}
instance.instanceName = name; instance.instanceName = name;
this.logger.verbose('Instance loaded: ' + name); this.logger.verbose('Instance loaded: ' + name);
await instance.connectToWhatsapp(); await instance.connectToWhatsapp();
+2 -2
View File
@@ -9,9 +9,9 @@ export class ProxyService {
private readonly logger = new Logger(ProxyService.name); private readonly logger = new Logger(ProxyService.name);
public create(instance: InstanceDto, data: ProxyDto, reload = true) { public create(instance: InstanceDto, data: ProxyDto) {
this.logger.verbose('create proxy: ' + instance.instanceName); this.logger.verbose('create proxy: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setProxy(data, reload); this.waMonitor.waInstances[instance.instanceName].setProxy(data);
return { proxy: { ...instance, proxy: data } }; return { proxy: { ...instance, proxy: data } };
} }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+11
View File
@@ -138,6 +138,12 @@ export declare namespace wa {
answerByAudio?: boolean; answerByAudio?: boolean;
}; };
export type LocalIntegration = {
integration?: string;
number?: string;
token?: string;
};
export type StateConnection = { export type StateConnection = {
instance?: string; instance?: string;
state?: WAConnectionState | 'refused'; state?: WAConnectionState | 'refused';
@@ -155,3 +161,8 @@ export const MessageSubtype = [
'viewOnceMessage', 'viewOnceMessage',
'viewOnceMessageV2', 'viewOnceMessageV2',
]; ];
export const Integration = {
WHATSAPP_BUSINESS: 'WHATSAPP-BUSINESS',
WHATSAPP_BAILEYS: 'WHATSAPP-BAILEYS',
};
+8
View File
@@ -24,6 +24,7 @@ import {
ChatModel, ChatModel,
ChatwootModel, ChatwootModel,
ContactModel, ContactModel,
IntegrationModel,
MessageModel, MessageModel,
MessageUpModel, MessageUpModel,
ProxyModel, ProxyModel,
@@ -40,6 +41,7 @@ import { ChamaaiRepository } from './repository/chamaai.repository';
import { ChatRepository } from './repository/chat.repository'; import { ChatRepository } from './repository/chat.repository';
import { ChatwootRepository } from './repository/chatwoot.repository'; import { ChatwootRepository } from './repository/chatwoot.repository';
import { ContactRepository } from './repository/contact.repository'; import { ContactRepository } from './repository/contact.repository';
import { IntegrationRepository } from './repository/integration.repository';
import { LabelRepository } from './repository/label.repository'; import { LabelRepository } from './repository/label.repository';
import { MessageRepository } from './repository/message.repository'; import { MessageRepository } from './repository/message.repository';
import { MessageUpRepository } from './repository/messageUp.repository'; import { MessageUpRepository } from './repository/messageUp.repository';
@@ -55,6 +57,7 @@ import { AuthService } from './services/auth.service';
import { CacheService } from './services/cache.service'; import { CacheService } from './services/cache.service';
import { ChamaaiService } from './services/chamaai.service'; import { ChamaaiService } from './services/chamaai.service';
import { ChatwootService } from './services/chatwoot.service'; import { ChatwootService } from './services/chatwoot.service';
import { IntegrationService } from './services/integration.service';
import { WAMonitoringService } from './services/monitor.service'; import { WAMonitoringService } from './services/monitor.service';
import { ProxyService } from './services/proxy.service'; import { ProxyService } from './services/proxy.service';
import { RabbitmqService } from './services/rabbitmq.service'; import { RabbitmqService } from './services/rabbitmq.service';
@@ -77,6 +80,7 @@ const proxyRepository = new ProxyRepository(ProxyModel, configService);
const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService); const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService);
const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService); const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService);
const sqsRepository = new SqsRepository(SqsModel, configService); const sqsRepository = new SqsRepository(SqsModel, configService);
const integrationRepository = new IntegrationRepository(IntegrationModel, configService);
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService); const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
const settingsRepository = new SettingsRepository(SettingsModel, configService); const settingsRepository = new SettingsRepository(SettingsModel, configService);
const authRepository = new AuthRepository(AuthModel, configService); const authRepository = new AuthRepository(AuthModel, configService);
@@ -96,6 +100,7 @@ export const repository = new RepositoryBroker(
typebotRepository, typebotRepository,
proxyRepository, proxyRepository,
chamaaiRepository, chamaaiRepository,
integrationRepository,
authRepository, authRepository,
labelRepository, labelRepository,
configService, configService,
@@ -138,6 +143,8 @@ const sqsService = new SqsService(waMonitor);
export const sqsController = new SqsController(sqsService); export const sqsController = new SqsController(sqsService);
const integrationService = new IntegrationService(waMonitor);
const chatwootService = new ChatwootService(waMonitor, configService, repository, chatwootCache); const chatwootService = new ChatwootService(waMonitor, configService, repository, chatwootCache);
export const chatwootController = new ChatwootController(chatwootService, configService, repository); export const chatwootController = new ChatwootController(chatwootService, configService, repository);
@@ -159,6 +166,7 @@ export const instanceController = new InstanceController(
rabbitmqService, rabbitmqService,
sqsService, sqsService,
typebotService, typebotService,
integrationService,
cache, cache,
chatwootCache, chatwootCache,
); );