mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-13 15:14:49 -06:00
Enhance settings and integrate Baileys controller for WhatsApp functionality
- Added `wavoipToken` field to `Setting` model in both MySQL and PostgreSQL schemas. - Updated `package.json` and `package-lock.json` to include `mime-types` and `socket.io-client` dependencies. - Introduced `BaileysController` and `BaileysRouter` for handling WhatsApp interactions. - Refactored media type handling to use `mime-types` instead of `mime` across various services. - Updated DTOs and validation schemas to accommodate the new `wavoipToken` field. - Implemented voice call functionalities using the Wavoip service in the Baileys integration. - Enhanced event handling in the WebSocket controller to support new features.
This commit is contained in:
parent
616ae0a7eb
commit
540467293c
95
package-lock.json
generated
95
package-lock.json
generated
@ -39,6 +39,7 @@
|
||||
"long": "^5.2.3",
|
||||
"mediainfo.js": "^0.3.4",
|
||||
"mime": "^4.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"minio": "^8.0.3",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-cache": "^5.1.2",
|
||||
@ -53,6 +54,7 @@
|
||||
"redis": "^4.7.0",
|
||||
"sharp": "^0.32.6",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"tsup": "^8.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -61,6 +63,7 @@
|
||||
"@types/express": "^4.17.18",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@types/mime": "^4.0.0",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
@ -3819,6 +3822,12 @@
|
||||
"mime": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime-types": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
|
||||
"integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mysql": {
|
||||
"version": "2.15.26",
|
||||
"resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz",
|
||||
@ -6089,6 +6098,54 @@
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz",
|
||||
"integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
@ -10728,6 +10785,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
@ -12102,6 +12189,14 @@
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
@ -79,6 +79,7 @@
|
||||
"long": "^5.2.3",
|
||||
"mediainfo.js": "^0.3.4",
|
||||
"mime": "^4.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"minio": "^8.0.3",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-cache": "^5.1.2",
|
||||
@ -93,6 +94,7 @@
|
||||
"redis": "^4.7.0",
|
||||
"sharp": "^0.32.6",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"tsup": "^8.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -101,6 +103,7 @@
|
||||
"@types/express": "^4.17.18",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@types/mime": "^4.0.0",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
|
@ -264,6 +264,7 @@ model Setting {
|
||||
readMessages Boolean @default(false)
|
||||
readStatus Boolean @default(false)
|
||||
syncFullHistory Boolean @default(false)
|
||||
wavoipToken String? @db.VarChar(100)
|
||||
createdAt DateTime? @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp
|
||||
updatedAt DateTime @updatedAt @db.Timestamp
|
||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||
|
@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[remoteJid,instanceId]` on the table `Chat` will be added. If there are existing duplicate values, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Setting" ADD COLUMN "wavoipToken" VARCHAR(100);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Chat_remoteJid_instanceId_key" ON "Chat"("remoteJid", "instanceId");
|
@ -265,6 +265,7 @@ model Setting {
|
||||
readMessages Boolean @default(false) @db.Boolean
|
||||
readStatus Boolean @default(false) @db.Boolean
|
||||
syncFullHistory Boolean @default(false) @db.Boolean
|
||||
wavoipToken String? @db.VarChar(100)
|
||||
createdAt DateTime? @default(now()) @db.Timestamp
|
||||
updatedAt DateTime @updatedAt @db.Timestamp
|
||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||
|
@ -119,6 +119,7 @@ export class InstanceController {
|
||||
readMessages: instanceData.readMessages === true,
|
||||
readStatus: instanceData.readStatus === true,
|
||||
syncFullHistory: instanceData.syncFullHistory === true,
|
||||
wavoipToken: instanceData.wavoipToken || '',
|
||||
};
|
||||
|
||||
await this.settingsService.create(instance, settings);
|
||||
|
@ -19,6 +19,7 @@ export class InstanceDto extends IntegrationDto {
|
||||
readMessages?: boolean;
|
||||
readStatus?: boolean;
|
||||
syncFullHistory?: boolean;
|
||||
wavoipToken?: string;
|
||||
// proxy
|
||||
proxyHost?: string;
|
||||
proxyPort?: string;
|
||||
|
@ -6,4 +6,5 @@ export class SettingsDto {
|
||||
readMessages?: boolean;
|
||||
readStatus?: boolean;
|
||||
syncFullHistory?: boolean;
|
||||
wavoipToken?: string;
|
||||
}
|
||||
|
@ -2,14 +2,16 @@ import { Router } from 'express';
|
||||
|
||||
import { EvolutionRouter } from './evolution/evolution.router';
|
||||
import { MetaRouter } from './meta/meta.router';
|
||||
import { BaileysRouter } from './whatsapp/baileys.router';
|
||||
|
||||
export class ChannelRouter {
|
||||
public readonly router: Router;
|
||||
|
||||
constructor(configService: any) {
|
||||
constructor(configService: any, ...guards: any[]) {
|
||||
this.router = Router();
|
||||
|
||||
this.router.use('/', new EvolutionRouter(configService).router);
|
||||
this.router.use('/', new MetaRouter(configService).router);
|
||||
this.router.use('/baileys', new BaileysRouter(...guards).router);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import { BadRequestException, InternalServerErrorException } from '@exceptions';
|
||||
import { status } from '@utils/renderStatus';
|
||||
import { isURL } from 'class-validator';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
import mime from 'mime';
|
||||
import mimeTypes from 'mime-types';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export class EvolutionStartupService extends ChannelStartupService {
|
||||
@ -396,7 +396,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
mediaMessage.fileName = 'video.mp4';
|
||||
}
|
||||
|
||||
let mimetype: string;
|
||||
let mimetype: string | false;
|
||||
|
||||
const prepareMedia: any = {
|
||||
caption: mediaMessage?.caption,
|
||||
@ -407,9 +407,9 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
};
|
||||
|
||||
if (isURL(mediaMessage.media)) {
|
||||
mimetype = mime.getType(mediaMessage.media);
|
||||
mimetype = mimeTypes.lookup(mediaMessage.media);
|
||||
} else {
|
||||
mimetype = mime.getType(mediaMessage.fileName);
|
||||
mimetype = mimeTypes.lookup(mediaMessage.fileName);
|
||||
}
|
||||
|
||||
prepareMedia.mimetype = mimetype;
|
||||
@ -449,7 +449,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
number = number.replace(/\D/g, '');
|
||||
const hash = `${number}-${new Date().getTime()}`;
|
||||
|
||||
let mimetype: string;
|
||||
let mimetype: string | false;
|
||||
|
||||
const prepareMedia: any = {
|
||||
fileName: `${hash}.mp4`,
|
||||
@ -458,9 +458,9 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
};
|
||||
|
||||
if (isURL(audio)) {
|
||||
mimetype = mime.getType(audio);
|
||||
mimetype = mimeTypes.lookup(audio);
|
||||
} else {
|
||||
mimetype = mime.getType(prepareMedia.fileName);
|
||||
mimetype = mimeTypes.lookup(prepareMedia.fileName);
|
||||
}
|
||||
|
||||
prepareMedia.mimetype = mimetype;
|
||||
|
@ -28,7 +28,7 @@ import { arrayUnique, isURL } from 'class-validator';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
import FormData from 'form-data';
|
||||
import { createReadStream } from 'fs';
|
||||
import mime from 'mime';
|
||||
import mimeTypes from 'mime-types';
|
||||
import { join } from 'path';
|
||||
|
||||
export class BusinessStartupService extends ChannelStartupService {
|
||||
@ -1017,7 +1017,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
mediaMessage.fileName = 'video.mp4';
|
||||
}
|
||||
|
||||
let mimetype: string;
|
||||
let mimetype: string | false;
|
||||
|
||||
const prepareMedia: any = {
|
||||
caption: mediaMessage?.caption,
|
||||
@ -1028,11 +1028,11 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
};
|
||||
|
||||
if (isURL(mediaMessage.media)) {
|
||||
mimetype = mime.getType(mediaMessage.media);
|
||||
mimetype = mimeTypes.lookup(mediaMessage.media);
|
||||
prepareMedia.id = mediaMessage.media;
|
||||
prepareMedia.type = 'link';
|
||||
} else {
|
||||
mimetype = mime.getType(mediaMessage.fileName);
|
||||
mimetype = mimeTypes.lookup(mediaMessage.fileName);
|
||||
const id = await this.getIdMedia(prepareMedia);
|
||||
prepareMedia.id = id;
|
||||
prepareMedia.type = 'id';
|
||||
@ -1075,7 +1075,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
number = number.replace(/\D/g, '');
|
||||
const hash = `${number}-${new Date().getTime()}`;
|
||||
|
||||
let mimetype: string;
|
||||
let mimetype: string | false;
|
||||
|
||||
const prepareMedia: any = {
|
||||
fileName: `${hash}.mp3`,
|
||||
@ -1084,11 +1084,11 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
};
|
||||
|
||||
if (isURL(audio)) {
|
||||
mimetype = mime.getType(audio);
|
||||
mimetype = mimeTypes.lookup(audio);
|
||||
prepareMedia.id = audio;
|
||||
prepareMedia.type = 'link';
|
||||
} else {
|
||||
mimetype = mime.getType(prepareMedia.fileName);
|
||||
mimetype = mimeTypes.lookup(prepareMedia.fileName);
|
||||
const id = await this.getIdMedia(prepareMedia);
|
||||
prepareMedia.id = id;
|
||||
prepareMedia.type = 'id';
|
||||
|
60
src/api/integrations/channel/whatsapp/baileys.controller.ts
Normal file
60
src/api/integrations/channel/whatsapp/baileys.controller.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
|
||||
export class BaileysController {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
public async onWhatsapp({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysOnWhatsapp(body?.jid);
|
||||
}
|
||||
|
||||
public async profilePictureUrl({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysProfilePictureUrl(body?.jid, body?.type, body?.timeoutMs);
|
||||
}
|
||||
|
||||
public async assertSessions({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysAssertSessions(body?.jids, body?.force);
|
||||
}
|
||||
|
||||
public async createParticipantNodes({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysCreateParticipantNodes(body?.jids, body?.message, body?.extraAttrs);
|
||||
}
|
||||
|
||||
public async getUSyncDevices({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysGetUSyncDevices(body?.jids, body?.useCache, body?.ignoreZeroDevices);
|
||||
}
|
||||
|
||||
public async generateMessageTag({ instanceName }: InstanceDto) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysGenerateMessageTag();
|
||||
}
|
||||
|
||||
public async sendNode({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysSendNode(body?.stanza);
|
||||
}
|
||||
|
||||
public async signalRepositoryDecryptMessage({ instanceName }: InstanceDto, body: any) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysSignalRepositoryDecryptMessage(body?.jid, body?.type, body?.ciphertext);
|
||||
}
|
||||
|
||||
public async getAuthState({ instanceName }: InstanceDto) {
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
|
||||
return instance.baileysGetAuthState();
|
||||
}
|
||||
}
|
105
src/api/integrations/channel/whatsapp/baileys.router.ts
Normal file
105
src/api/integrations/channel/whatsapp/baileys.router.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { baileysController } from '@api/server.module';
|
||||
import { instanceSchema } from '@validate/instance.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
export class BaileysRouter extends RouterBroker {
|
||||
constructor(...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('onWhatsapp'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.onWhatsapp(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('profilePictureUrl'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.profilePictureUrl(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('assertSessions'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.assertSessions(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('createParticipantNodes'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.createParticipantNodes(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('getUSyncDevices'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.getUSyncDevices(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('generateMessageTag'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.generateMessageTag(instance),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('sendNode'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.sendNode(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('signalRepositoryDecryptMessage'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.signalRepositoryDecryptMessage(instance, req.body),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('getAuthState'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => baileysController.getAuthState(instance),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
import { BinaryNode, Contact, JidWithDevice, proto, WAConnectionState } from 'baileys';
|
||||
|
||||
export interface ServerToClientEvents {
|
||||
withAck: (d: string, callback: (e: number) => void) => void;
|
||||
onWhatsApp: onWhatsAppType;
|
||||
profilePictureUrl: ProfilePictureUrlType;
|
||||
assertSessions: AssertSessionsType;
|
||||
createParticipantNodes: CreateParticipantNodesType;
|
||||
getUSyncDevices: GetUSyncDevicesType;
|
||||
generateMessageTag: GenerateMessageTagType;
|
||||
sendNode: SendNodeType;
|
||||
'signalRepository:decryptMessage': SignalRepositoryDecryptMessageType;
|
||||
}
|
||||
|
||||
export interface ClientToServerEvents {
|
||||
init: (
|
||||
me: Contact | undefined,
|
||||
account: proto.IADVSignedDeviceIdentity | undefined,
|
||||
status: WAConnectionState,
|
||||
) => void;
|
||||
'CB:call': (packet: any) => void;
|
||||
'CB:ack,class:call': (packet: any) => void;
|
||||
'connection.update:status': (
|
||||
me: Contact | undefined,
|
||||
account: proto.IADVSignedDeviceIdentity | undefined,
|
||||
status: WAConnectionState,
|
||||
) => void;
|
||||
'connection.update:qr': (qr: string) => void;
|
||||
}
|
||||
|
||||
export type onWhatsAppType = (jid: string, callback: onWhatsAppCallback) => void;
|
||||
export type onWhatsAppCallback = (
|
||||
response: {
|
||||
exists: boolean;
|
||||
jid: string;
|
||||
}[],
|
||||
) => void;
|
||||
|
||||
export type ProfilePictureUrlType = (
|
||||
jid: string,
|
||||
type: 'image' | 'preview',
|
||||
timeoutMs: number | undefined,
|
||||
callback: ProfilePictureUrlCallback,
|
||||
) => void;
|
||||
export type ProfilePictureUrlCallback = (response: string | undefined) => void;
|
||||
|
||||
export type AssertSessionsType = (jids: string[], force: boolean, callback: AssertSessionsCallback) => void;
|
||||
export type AssertSessionsCallback = (response: boolean) => void;
|
||||
|
||||
export type CreateParticipantNodesType = (
|
||||
jids: string[],
|
||||
message: any,
|
||||
extraAttrs: any,
|
||||
callback: CreateParticipantNodesCallback,
|
||||
) => void;
|
||||
export type CreateParticipantNodesCallback = (nodes: any, shouldIncludeDeviceIdentity: boolean) => void;
|
||||
|
||||
export type GetUSyncDevicesType = (
|
||||
jids: string[],
|
||||
useCache: boolean,
|
||||
ignoreZeroDevices: boolean,
|
||||
callback: GetUSyncDevicesTypeCallback,
|
||||
) => void;
|
||||
export type GetUSyncDevicesTypeCallback = (jids: JidWithDevice[]) => void;
|
||||
|
||||
export type GenerateMessageTagType = (callback: GenerateMessageTagTypeCallback) => void;
|
||||
export type GenerateMessageTagTypeCallback = (response: string) => void;
|
||||
|
||||
export type SendNodeType = (stanza: BinaryNode, callback: SendNodeTypeCallback) => void;
|
||||
export type SendNodeTypeCallback = (response: boolean) => void;
|
||||
|
||||
export type SignalRepositoryDecryptMessageType = (
|
||||
jid: string,
|
||||
type: 'pkmsg' | 'msg',
|
||||
ciphertext: Buffer,
|
||||
callback: SignalRepositoryDecryptMessageCallback,
|
||||
) => void;
|
||||
export type SignalRepositoryDecryptMessageCallback = (response: any) => void;
|
@ -0,0 +1,181 @@
|
||||
import { ConnectionState, WAConnectionState, WASocket } from 'baileys';
|
||||
import { io, Socket } from 'socket.io-client';
|
||||
|
||||
import { ClientToServerEvents, ServerToClientEvents } from './transport.type';
|
||||
|
||||
let baileys_connection_state: WAConnectionState = 'close';
|
||||
|
||||
export const useVoiceCallsBaileys = async (
|
||||
wavoip_token: string,
|
||||
baileys_sock: WASocket,
|
||||
status?: WAConnectionState,
|
||||
logger?: boolean,
|
||||
) => {
|
||||
baileys_connection_state = status ?? 'close';
|
||||
|
||||
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io('https://devices.wavoip.com/baileys', {
|
||||
transports: ['websocket'],
|
||||
path: `/${wavoip_token}/websocket`,
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
if (logger) console.log('[*] - Wavoip connected', socket.id);
|
||||
|
||||
socket.emit(
|
||||
'init',
|
||||
baileys_sock.authState.creds.me,
|
||||
baileys_sock.authState.creds.account,
|
||||
baileys_connection_state,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
if (logger) console.log('[*] - Wavoip disconnect');
|
||||
});
|
||||
|
||||
socket.on('connect_error', (error) => {
|
||||
if (socket.active) {
|
||||
if (logger)
|
||||
console.log(
|
||||
'[*] - Wavoip connection error temporary failure, the socket will automatically try to reconnect',
|
||||
error,
|
||||
);
|
||||
} else {
|
||||
if (logger) console.log('[*] - Wavoip connection error', error.message);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('onWhatsApp', async (jid, callback) => {
|
||||
try {
|
||||
const response: any = await baileys_sock.onWhatsApp(jid);
|
||||
|
||||
callback(response);
|
||||
|
||||
if (logger) console.log('[*] Success on call onWhatsApp function', response, jid);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call onWhatsApp function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('profilePictureUrl', async (jid, type, timeoutMs, callback) => {
|
||||
try {
|
||||
const response = await baileys_sock.profilePictureUrl(jid, type, timeoutMs);
|
||||
|
||||
callback(response);
|
||||
|
||||
if (logger) console.log('[*] Success on call profilePictureUrl function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call profilePictureUrl function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('assertSessions', async (jids, force, callback) => {
|
||||
try {
|
||||
const response = await baileys_sock.assertSessions(jids, force);
|
||||
|
||||
callback(response);
|
||||
|
||||
if (logger) console.log('[*] Success on call assertSessions function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call assertSessions function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('createParticipantNodes', async (jids, message, extraAttrs, callback) => {
|
||||
try {
|
||||
const response = await baileys_sock.createParticipantNodes(jids, message, extraAttrs);
|
||||
|
||||
callback(response, true);
|
||||
|
||||
if (logger) console.log('[*] Success on call createParticipantNodes function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call createParticipantNodes function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('getUSyncDevices', async (jids, useCache, ignoreZeroDevices, callback) => {
|
||||
try {
|
||||
const response = await baileys_sock.getUSyncDevices(jids, useCache, ignoreZeroDevices);
|
||||
|
||||
callback(response);
|
||||
|
||||
if (logger) console.log('[*] Success on call getUSyncDevices function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call getUSyncDevices function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('generateMessageTag', async (callback) => {
|
||||
try {
|
||||
const response = await baileys_sock.generateMessageTag();
|
||||
|
||||
callback(response);
|
||||
|
||||
if (logger) console.log('[*] Success on call generateMessageTag function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call generateMessageTag function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('sendNode', async (stanza, callback) => {
|
||||
try {
|
||||
console.log('sendNode', JSON.stringify(stanza));
|
||||
const response = await baileys_sock.sendNode(stanza);
|
||||
|
||||
callback(true);
|
||||
|
||||
if (logger) console.log('[*] Success on call sendNode function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call sendNode function', error);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('signalRepository:decryptMessage', async (jid, type, ciphertext, callback) => {
|
||||
try {
|
||||
const response = await baileys_sock.signalRepository.decryptMessage({
|
||||
jid: jid,
|
||||
type: type,
|
||||
ciphertext: ciphertext,
|
||||
});
|
||||
|
||||
callback(response);
|
||||
|
||||
if (logger) console.log('[*] Success on call signalRepository:decryptMessage function', response);
|
||||
} catch (error) {
|
||||
if (logger) console.error('[*] Error on call signalRepository:decryptMessage function', error);
|
||||
}
|
||||
});
|
||||
|
||||
// we only use this connection data to inform the webphone that the device is connected and creeds account to generate e2e whatsapp key for make call packets
|
||||
baileys_sock.ev.on('connection.update', (update: Partial<ConnectionState>) => {
|
||||
const { connection } = update;
|
||||
|
||||
if (connection) {
|
||||
baileys_connection_state = connection;
|
||||
socket
|
||||
.timeout(1000)
|
||||
.emit(
|
||||
'connection.update:status',
|
||||
baileys_sock.authState.creds.me,
|
||||
baileys_sock.authState.creds.account,
|
||||
connection,
|
||||
);
|
||||
}
|
||||
|
||||
if (update.qr) {
|
||||
socket.timeout(1000).emit('connection.update:qr', update.qr);
|
||||
}
|
||||
});
|
||||
|
||||
baileys_sock.ws.on('CB:call', (packet) => {
|
||||
if (logger) console.log('[*] Signling received');
|
||||
socket.volatile.timeout(1000).emit('CB:call', packet);
|
||||
});
|
||||
|
||||
baileys_sock.ws.on('CB:ack,class:call', (packet) => {
|
||||
if (logger) console.log('[*] Signling ack received');
|
||||
socket.volatile.timeout(1000).emit('CB:ack,class:call', packet);
|
||||
});
|
||||
|
||||
return socket;
|
||||
};
|
@ -131,7 +131,7 @@ import ffmpeg from 'fluent-ffmpeg';
|
||||
import FormData from 'form-data';
|
||||
import { readFileSync } from 'fs';
|
||||
import Long from 'long';
|
||||
import mime from 'mime';
|
||||
import mimeTypes from 'mime-types';
|
||||
import NodeCache from 'node-cache';
|
||||
import cron from 'node-cron';
|
||||
import { release } from 'os';
|
||||
@ -143,6 +143,8 @@ import sharp from 'sharp';
|
||||
import { PassThrough, Readable } from 'stream';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useVoiceCallsBaileys } from './voiceCalls/useVoiceCallsBaileys';
|
||||
|
||||
const groupMetadataCache = new CacheService(new CacheEngine(configService, 'groups').getEngine());
|
||||
|
||||
// Adicione a função getVideoDuration no início do arquivo
|
||||
@ -673,8 +675,30 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
this.client = makeWASocket(socketConfig);
|
||||
|
||||
if (this.localSettings.wavoipToken && this.localSettings.wavoipToken.length > 0) {
|
||||
useVoiceCallsBaileys(this.localSettings.wavoipToken, this.client, this.connectionStatus.state as any, true);
|
||||
}
|
||||
|
||||
this.eventHandler();
|
||||
|
||||
this.client.ws.on('CB:call', (packet) => {
|
||||
console.log('CB:call', packet);
|
||||
const payload = {
|
||||
event: 'CB:call',
|
||||
packet: packet,
|
||||
};
|
||||
this.sendDataWebhook(Events.CALL, payload, true, ['websocket']);
|
||||
});
|
||||
|
||||
this.client.ws.on('CB:ack,class:call', (packet) => {
|
||||
console.log('CB:ack,class:call', packet);
|
||||
const payload = {
|
||||
event: 'CB:ack,class:call',
|
||||
packet: packet,
|
||||
};
|
||||
this.sendDataWebhook(Events.CALL, payload, true, ['websocket']);
|
||||
});
|
||||
|
||||
this.phoneNumber = number;
|
||||
|
||||
return this.client;
|
||||
@ -1248,7 +1272,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
);
|
||||
|
||||
const { buffer, mediaType, fileName, size } = media;
|
||||
const mimetype = mime.getType(fileName).toString();
|
||||
const mimetype = mimeTypes.lookup(fileName).toString();
|
||||
const fullName = join(`${this.instance.id}`, received.key.remoteJid, mediaType, fileName);
|
||||
await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, {
|
||||
'Content-Type': mimetype,
|
||||
@ -2210,7 +2234,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
const { buffer, mediaType, fileName, size } = media;
|
||||
|
||||
const mimetype = mime.getType(fileName).toString();
|
||||
const mimetype = mimeTypes.lookup(fileName).toString();
|
||||
|
||||
const fullName = join(
|
||||
`${this.instance.id}`,
|
||||
@ -2532,12 +2556,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
mediaMessage.fileName = 'video.mp4';
|
||||
}
|
||||
|
||||
let mimetype: string;
|
||||
let mimetype: string | false;
|
||||
|
||||
if (mediaMessage.mimetype) {
|
||||
mimetype = mediaMessage.mimetype;
|
||||
} else {
|
||||
mimetype = mime.getType(mediaMessage.fileName);
|
||||
mimetype = mimeTypes.lookup(mediaMessage.fileName);
|
||||
|
||||
if (!mimetype && isURL(mediaMessage.media)) {
|
||||
let config: any = {
|
||||
@ -3590,7 +3614,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
);
|
||||
const typeMessage = getContentType(msg.message);
|
||||
|
||||
const ext = mime.getExtension(mediaMessage?.['mimetype']);
|
||||
const ext = mimeTypes.extension(mediaMessage?.['mimetype']);
|
||||
const fileName = mediaMessage?.['fileName'] || `${msg.key.id}.${ext}` || `${v4()}.${ext}`;
|
||||
|
||||
if (convertToMp4 && typeMessage === 'audioMessage') {
|
||||
@ -4395,4 +4419,85 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
public async baileysOnWhatsapp(jid: string) {
|
||||
const response = await this.client.onWhatsApp(jid);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async baileysProfilePictureUrl(jid: string, type: 'image' | 'preview', timeoutMs: number) {
|
||||
const response = await this.client.profilePictureUrl(jid, type, timeoutMs);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async baileysAssertSessions(jids: string[], force: boolean) {
|
||||
const response = await this.client.assertSessions(jids, force);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async baileysCreateParticipantNodes(jids: string[], message: proto.IMessage, extraAttrs: any) {
|
||||
const response = await this.client.createParticipantNodes(jids, message, extraAttrs);
|
||||
|
||||
const convertedResponse = {
|
||||
...response,
|
||||
nodes: response.nodes.map((node: any) => ({
|
||||
...node,
|
||||
content: node.content?.map((c: any) => ({
|
||||
...c,
|
||||
content: c.content instanceof Uint8Array ? Buffer.from(c.content).toString('base64') : c.content,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
|
||||
return convertedResponse;
|
||||
}
|
||||
|
||||
public async baileysSendNode(stanza: any) {
|
||||
console.log('stanza', JSON.stringify(stanza));
|
||||
const response = await this.client.sendNode(stanza);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async baileysGetUSyncDevices(jids: string[], useCache: boolean, ignoreZeroDevices: boolean) {
|
||||
const response = await this.client.getUSyncDevices(jids, useCache, ignoreZeroDevices);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async baileysGenerateMessageTag() {
|
||||
const response = await this.client.generateMessageTag();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async baileysSignalRepositoryDecryptMessage(jid: string, type: 'pkmsg' | 'msg', ciphertext: string) {
|
||||
try {
|
||||
const ciphertextBuffer = Buffer.from(ciphertext, 'base64');
|
||||
|
||||
const response = await this.client.signalRepository.decryptMessage({
|
||||
jid,
|
||||
type,
|
||||
ciphertext: ciphertextBuffer,
|
||||
});
|
||||
|
||||
return response instanceof Uint8Array ? Buffer.from(response).toString('base64') : response;
|
||||
} catch (error) {
|
||||
this.logger.error('Error decrypting message:');
|
||||
this.logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async baileysGetAuthState() {
|
||||
const response = {
|
||||
me: this.client.authState.creds.me,
|
||||
account: this.client.authState.creds.account,
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import dayjs from 'dayjs';
|
||||
import FormData from 'form-data';
|
||||
import Jimp from 'jimp';
|
||||
import Long from 'long';
|
||||
import mime from 'mime';
|
||||
import mimeTypes from 'mime-types';
|
||||
import path from 'path';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
@ -1066,7 +1066,7 @@ export class ChatwootService {
|
||||
public async sendAttachment(waInstance: any, number: string, media: any, caption?: string, options?: Options) {
|
||||
try {
|
||||
const parsedMedia = path.parse(decodeURIComponent(media));
|
||||
let mimeType = mime.getType(parsedMedia?.ext) || '';
|
||||
let mimeType = mimeTypes.lookup(parsedMedia?.ext) || '';
|
||||
let fileName = parsedMedia?.name + parsedMedia?.ext;
|
||||
|
||||
if (!mimeType) {
|
||||
@ -1958,7 +1958,7 @@ export class ChatwootService {
|
||||
}
|
||||
|
||||
if (!nameFile) {
|
||||
nameFile = `${Math.random().toString(36).substring(7)}.${mime.getExtension(downloadBase64.mimetype) || ''}`;
|
||||
nameFile = `${Math.random().toString(36).substring(7)}.${mimeTypes.extension(downloadBase64.mimetype) || ''}`;
|
||||
}
|
||||
|
||||
const fileData = Buffer.from(downloadBase64.base64, 'base64');
|
||||
@ -2057,8 +2057,8 @@ export class ChatwootService {
|
||||
if (isAdsMessage) {
|
||||
const imgBuffer = await axios.get(adsMessage.thumbnailUrl, { responseType: 'arraybuffer' });
|
||||
|
||||
const extension = mime.getExtension(imgBuffer.headers['content-type']);
|
||||
const mimeType = extension && mime.getType(extension);
|
||||
const extension = mimeTypes.extension(imgBuffer.headers['content-type']);
|
||||
const mimeType = extension && mimeTypes.lookup(extension);
|
||||
|
||||
if (!mimeType) {
|
||||
this.logger.warn('mimetype of Ads message not found');
|
||||
@ -2066,7 +2066,7 @@ export class ChatwootService {
|
||||
}
|
||||
|
||||
const random = Math.random().toString(36).substring(7);
|
||||
const nameFile = `${random}.${mime.getExtension(mimeType)}`;
|
||||
const nameFile = `${random}.${mimeTypes.extension(mimeType)}`;
|
||||
const fileData = Buffer.from(imgBuffer.data, 'binary');
|
||||
|
||||
const img = await Jimp.read(fileData);
|
||||
|
@ -13,6 +13,7 @@ export type EmitData = {
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
local?: boolean;
|
||||
integration?: string[];
|
||||
};
|
||||
|
||||
export interface EventControllerInterface {
|
||||
@ -23,7 +24,7 @@ export interface EventControllerInterface {
|
||||
|
||||
export class EventController {
|
||||
public prismaRepository: PrismaRepository;
|
||||
private waMonitor: WAMonitoringService;
|
||||
protected waMonitor: WAMonitoringService;
|
||||
private integrationStatus: boolean;
|
||||
private integrationName: string;
|
||||
|
||||
|
@ -99,6 +99,7 @@ export class EventManager {
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
local?: boolean;
|
||||
integration?: string[];
|
||||
}): Promise<void> {
|
||||
await this.websocket.emit(eventData);
|
||||
await this.rabbitmq.emit(eventData);
|
||||
|
@ -120,7 +120,11 @@ export class PusherController extends EventController implements EventController
|
||||
sender,
|
||||
apiKey,
|
||||
local,
|
||||
integration,
|
||||
}: EmitData): Promise<void> {
|
||||
if (integration && !integration.includes('pusher')) {
|
||||
return;
|
||||
}
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
@ -73,7 +73,12 @@ export class RabbitmqController extends EventController implements EventControll
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
integration,
|
||||
}: EmitData): Promise<void> {
|
||||
if (integration && !integration.includes('rabbitmq')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
@ -54,7 +54,12 @@ export class SqsController extends EventController implements EventControllerInt
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
integration,
|
||||
}: EmitData): Promise<void> {
|
||||
if (integration && !integration.includes('sqs')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
@ -64,7 +64,12 @@ export class WebhookController extends EventController implements EventControlle
|
||||
sender,
|
||||
apiKey,
|
||||
local,
|
||||
integration,
|
||||
}: EmitData): Promise<void> {
|
||||
if (integration && !integration.includes('webhook')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = (await this.get(instanceName)) as wa.LocalWebHook;
|
||||
|
||||
const webhookConfig = configService.get<Webhook>('WEBHOOK');
|
||||
@ -85,7 +90,7 @@ export class WebhookController extends EventController implements EventControlle
|
||||
apikey: apiKey,
|
||||
};
|
||||
|
||||
if ((local && !instance) || !instance?.enabled) {
|
||||
if (local && instance?.enabled) {
|
||||
if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) {
|
||||
let baseURL: string;
|
||||
|
||||
|
@ -35,6 +35,16 @@ export class WebsocketController extends EventController implements EventControl
|
||||
socket.on('disconnect', () => {
|
||||
this.logger.info('User disconnected');
|
||||
});
|
||||
|
||||
socket.on('sendNode', async (data) => {
|
||||
try {
|
||||
await this.waMonitor.waInstances[data.instanceId].baileysSendNode(data.stanza);
|
||||
this.logger.info('Node sent successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Error sending node:');
|
||||
this.logger.error(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.logger.info('Socket.io initialized');
|
||||
@ -65,7 +75,12 @@ export class WebsocketController extends EventController implements EventControl
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
integration,
|
||||
}: EmitData): Promise<void> {
|
||||
if (integration && !integration.includes('websocket')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { StorageRouter } from '@api/integrations/storage/storage.router';
|
||||
import { configService } from '@config/env.config';
|
||||
import { Router } from 'express';
|
||||
import fs from 'fs';
|
||||
import mime from 'mime';
|
||||
import mimeTypes from 'mime-types';
|
||||
import path from 'path';
|
||||
|
||||
import { CallRouter } from './call.router';
|
||||
@ -49,7 +49,7 @@ router.get('/assets/*', (req, res) => {
|
||||
const filePath = path.join(basePath, 'assets/', fileName);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
res.set('Content-Type', mime.getType(filePath) || 'text/css');
|
||||
res.set('Content-Type', mimeTypes.lookup(filePath) || 'text/css');
|
||||
res.send(fs.readFileSync(filePath));
|
||||
} else {
|
||||
res.status(404).send('File not found');
|
||||
@ -87,7 +87,7 @@ router
|
||||
.use('/settings', new SettingsRouter(...guards).router)
|
||||
.use('/proxy', new ProxyRouter(...guards).router)
|
||||
.use('/label', new LabelRouter(...guards).router)
|
||||
.use('', new ChannelRouter(configService).router)
|
||||
.use('', new ChannelRouter(configService, ...guards).router)
|
||||
.use('', new EventRouter(configService, ...guards).router)
|
||||
.use('', new ChatbotRouter(...guards).router)
|
||||
.use('', new StorageRouter(...guards).router);
|
||||
|
@ -15,6 +15,7 @@ import { TemplateController } from './controllers/template.controller';
|
||||
import { ChannelController } from './integrations/channel/channel.controller';
|
||||
import { EvolutionController } from './integrations/channel/evolution/evolution.controller';
|
||||
import { MetaController } from './integrations/channel/meta/meta.controller';
|
||||
import { BaileysController } from './integrations/channel/whatsapp/baileys.controller';
|
||||
import { ChatbotController } from './integrations/chatbot/chatbot.controller';
|
||||
import { ChatwootController } from './integrations/chatbot/chatwoot/controllers/chatwoot.controller';
|
||||
import { ChatwootService } from './integrations/chatbot/chatwoot/services/chatwoot.service';
|
||||
@ -107,7 +108,7 @@ export const channelController = new ChannelController(prismaRepository, waMonit
|
||||
// channels
|
||||
export const evolutionController = new EvolutionController(prismaRepository, waMonitor);
|
||||
export const metaController = new MetaController(prismaRepository, waMonitor);
|
||||
|
||||
export const baileysController = new BaileysController(waMonitor);
|
||||
// chatbots
|
||||
const typebotService = new TypebotService(waMonitor, configService, prismaRepository);
|
||||
export const typebotController = new TypebotController(typebotService, prismaRepository, waMonitor);
|
||||
|
@ -151,6 +151,7 @@ export class ChannelStartupService {
|
||||
this.localSettings.readMessages = data?.readMessages;
|
||||
this.localSettings.readStatus = data?.readStatus;
|
||||
this.localSettings.syncFullHistory = data?.syncFullHistory;
|
||||
this.localSettings.wavoipToken = data?.wavoipToken;
|
||||
}
|
||||
|
||||
public async setSettings(data: SettingsDto) {
|
||||
@ -166,6 +167,7 @@ export class ChannelStartupService {
|
||||
readMessages: data.readMessages,
|
||||
readStatus: data.readStatus,
|
||||
syncFullHistory: data.syncFullHistory,
|
||||
wavoipToken: data.wavoipToken,
|
||||
},
|
||||
create: {
|
||||
rejectCall: data.rejectCall,
|
||||
@ -175,6 +177,7 @@ export class ChannelStartupService {
|
||||
readMessages: data.readMessages,
|
||||
readStatus: data.readStatus,
|
||||
syncFullHistory: data.syncFullHistory,
|
||||
wavoipToken: data.wavoipToken,
|
||||
instanceId: this.instanceId,
|
||||
},
|
||||
});
|
||||
@ -186,6 +189,12 @@ export class ChannelStartupService {
|
||||
this.localSettings.readMessages = data?.readMessages;
|
||||
this.localSettings.readStatus = data?.readStatus;
|
||||
this.localSettings.syncFullHistory = data?.syncFullHistory;
|
||||
this.localSettings.wavoipToken = data?.wavoipToken;
|
||||
|
||||
if (this.localSettings.wavoipToken && this.localSettings.wavoipToken.length > 0) {
|
||||
this.client.ws.close();
|
||||
this.client.ws.connect();
|
||||
}
|
||||
}
|
||||
|
||||
public async findSettings() {
|
||||
@ -207,6 +216,7 @@ export class ChannelStartupService {
|
||||
readMessages: data.readMessages,
|
||||
readStatus: data.readStatus,
|
||||
syncFullHistory: data.syncFullHistory,
|
||||
wavoipToken: data.wavoipToken,
|
||||
};
|
||||
}
|
||||
|
||||
@ -419,7 +429,7 @@ export class ChannelStartupService {
|
||||
return data;
|
||||
}
|
||||
|
||||
public async sendDataWebhook<T = any>(event: Events, data: T, local = true) {
|
||||
public async sendDataWebhook<T = any>(event: Events, data: T, local = true, integration?: string[]) {
|
||||
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
||||
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
|
||||
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
|
||||
@ -439,6 +449,7 @@ export class ChannelStartupService {
|
||||
sender: this.wuid,
|
||||
apiKey: expose && instanceApikey ? instanceApikey : null,
|
||||
local,
|
||||
integration,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ export declare namespace wa {
|
||||
readMessages?: boolean;
|
||||
readStatus?: boolean;
|
||||
syncFullHistory?: boolean;
|
||||
wavoipToken?: string;
|
||||
};
|
||||
|
||||
export type LocalEvent = {
|
||||
|
@ -43,6 +43,7 @@ export const instanceSchema: JSONSchema7 = {
|
||||
readMessages: { type: 'boolean' },
|
||||
readStatus: { type: 'boolean' },
|
||||
syncFullHistory: { type: 'boolean' },
|
||||
wavoipToken: { type: 'string' },
|
||||
// Proxy
|
||||
proxyHost: { type: 'string' },
|
||||
proxyPort: { type: 'string' },
|
||||
|
@ -31,7 +31,24 @@ export const settingsSchema: JSONSchema7 = {
|
||||
readMessages: { type: 'boolean' },
|
||||
readStatus: { type: 'boolean' },
|
||||
syncFullHistory: { type: 'boolean' },
|
||||
wavoipToken: { type: 'string' },
|
||||
},
|
||||
required: ['rejectCall', 'groupsIgnore', 'alwaysOnline', 'readMessages', 'readStatus', 'syncFullHistory'],
|
||||
...isNotEmpty('rejectCall', 'groupsIgnore', 'alwaysOnline', 'readMessages', 'readStatus', 'syncFullHistory'),
|
||||
required: [
|
||||
'rejectCall',
|
||||
'groupsIgnore',
|
||||
'alwaysOnline',
|
||||
'readMessages',
|
||||
'readStatus',
|
||||
'syncFullHistory',
|
||||
'wavoipToken',
|
||||
],
|
||||
...isNotEmpty(
|
||||
'rejectCall',
|
||||
'groupsIgnore',
|
||||
'alwaysOnline',
|
||||
'readMessages',
|
||||
'readStatus',
|
||||
'syncFullHistory',
|
||||
'wavoipToken',
|
||||
),
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user