mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-22 20:12:02 -06:00
Merge remote-tracking branch 'upstream/v2.0.0' into v2.0.0
This commit is contained in:
commit
f6b3612c3d
@ -13,6 +13,14 @@
|
||||
|
||||
* Refactor integrations structure for modular system
|
||||
* Fixed dify agent integration
|
||||
* Update Baileys Version
|
||||
* Fixed proxy config in manager
|
||||
* Fixed send messages in groups
|
||||
* S3 saving media sent from me
|
||||
|
||||
### Break Changes
|
||||
|
||||
* Payloads for events changed (create Instance and set events). Check postman to understand
|
||||
|
||||
# 2.0.10 (2024-08-16 16:23)
|
||||
|
||||
|
@ -41,8 +41,7 @@ WORKDIR /evolution
|
||||
COPY --from=builder /evolution/package.json ./package.json
|
||||
COPY --from=builder /evolution/package-lock.json ./package-lock.json
|
||||
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY --from=builder /evolution/node_modules ./node_modules
|
||||
COPY --from=builder /evolution/dist ./dist
|
||||
COPY --from=builder /evolution/prisma ./prisma
|
||||
COPY --from=builder /evolution/manager ./manager
|
||||
|
File diff suppressed because one or more lines are too long
2
manager/dist/index.html
vendored
2
manager/dist/index.html
vendored
@ -5,7 +5,7 @@
|
||||
<link rel="icon" type="image/png" href="/assets/images/evolution-logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Evolution Manager</title>
|
||||
<script type="module" crossorigin src="/assets/index-BmAfUzu7.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BoEnBOjp.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BJ9JMAl_.css">
|
||||
</head>
|
||||
<body>
|
||||
|
@ -56,7 +56,7 @@
|
||||
"@sentry/profiling-node": "^8.26.0",
|
||||
"amqplib": "^0.10.3",
|
||||
"axios": "^1.6.5",
|
||||
"baileys": "6.7.5",
|
||||
"baileys": "6.7.7",
|
||||
"class-validator": "^0.14.1",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
|
@ -2,7 +2,7 @@ import { InstanceDto, SetPresenceDto } from '@api/dto/instance.dto';
|
||||
import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service';
|
||||
import { ProviderFiles } from '@api/provider/sessions';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { channelController, eventController } from '@api/server.module';
|
||||
import { channelController, eventManager } from '@api/server.module';
|
||||
import { CacheService } from '@api/services/cache.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { SettingsService } from '@api/services/settings.service';
|
||||
@ -77,7 +77,7 @@ export class InstanceController {
|
||||
this.waMonitor.delInstanceTime(instance.instanceName);
|
||||
|
||||
// set events
|
||||
await eventController.setInstance(instance.instanceName, instanceData);
|
||||
await eventManager.setInstance(instance.instanceName, instanceData);
|
||||
|
||||
instance.sendDataWebhook(Events.INSTANCE_CREATE, {
|
||||
instanceName: instanceData.instanceName,
|
||||
@ -150,22 +150,18 @@ export class InstanceController {
|
||||
},
|
||||
hash,
|
||||
webhook: {
|
||||
webhookUrl: instanceData.webhookUrl,
|
||||
webhookByEvents: instanceData.webhookByEvents,
|
||||
webhookBase64: instanceData.webhookBase64,
|
||||
// events: getWebhookEvents,
|
||||
webhookUrl: instanceData?.webhook?.url,
|
||||
webhookByEvents: instanceData?.webhook?.byEvents,
|
||||
webhookBase64: instanceData?.webhook?.base64,
|
||||
},
|
||||
websocket: {
|
||||
enabled: instanceData.websocketEnabled,
|
||||
// events: getWebsocketEvents,
|
||||
enabled: instanceData?.websocket?.enabled,
|
||||
},
|
||||
rabbitmq: {
|
||||
enabled: instanceData.rabbitmqEnabled,
|
||||
// events: getRabbitmqEvents,
|
||||
enabled: instanceData?.rabbitmq?.enabled,
|
||||
},
|
||||
sqs: {
|
||||
enabled: instanceData.sqsEnabled,
|
||||
// events: getSqsEvents,
|
||||
enabled: instanceData?.sqs?.enabled,
|
||||
},
|
||||
settings,
|
||||
qrcode: getQrcode,
|
||||
@ -241,22 +237,18 @@ export class InstanceController {
|
||||
},
|
||||
hash,
|
||||
webhook: {
|
||||
webhookUrl: instanceData.webhookUrl,
|
||||
webhookByEvents: instanceData.webhookByEvents,
|
||||
webhookBase64: instanceData.webhookBase64,
|
||||
// events: getWebhookEvents,
|
||||
webhookUrl: instanceData?.webhook?.url,
|
||||
webhookByEvents: instanceData?.webhook?.byEvents,
|
||||
webhookBase64: instanceData?.webhook?.base64,
|
||||
},
|
||||
websocket: {
|
||||
enabled: instanceData.websocketEnabled,
|
||||
// events: getWebsocketEvents,
|
||||
enabled: instanceData?.websocket?.enabled,
|
||||
},
|
||||
rabbitmq: {
|
||||
enabled: instanceData.rabbitmqEnabled,
|
||||
// events: getRabbitmqEvents,
|
||||
enabled: instanceData?.rabbitmq?.enabled,
|
||||
},
|
||||
sqs: {
|
||||
enabled: instanceData.sqsEnabled,
|
||||
// events: getSqsEvents,
|
||||
enabled: instanceData?.sqs?.enabled,
|
||||
},
|
||||
settings,
|
||||
chatwoot: {
|
||||
@ -277,6 +269,7 @@ export class InstanceController {
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
this.waMonitor.deleteInstance(instanceData.instanceName);
|
||||
this.logger.error(isArray(error.message) ? error.message[0] : error.message);
|
||||
throw new BadRequestException(isArray(error.message) ? error.message[0] : error.message);
|
||||
}
|
||||
@ -338,6 +331,7 @@ export class InstanceController {
|
||||
instance.client?.end(new Error('restart'));
|
||||
return await this.connectToWhatsapp({ instanceName });
|
||||
} else if (state == 'connecting') {
|
||||
instance.client?.ws?.close();
|
||||
instance.client?.end(new Error('restart'));
|
||||
return await this.connectToWhatsapp({ instanceName });
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export class ProxyController {
|
||||
throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`);
|
||||
}
|
||||
|
||||
if (!data.enabled) {
|
||||
if (!data?.enabled) {
|
||||
data.host = '';
|
||||
data.port = '';
|
||||
data.protocol = '';
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { proto, WAPresence, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from 'baileys';
|
||||
import {
|
||||
proto,
|
||||
WAPresence,
|
||||
WAPrivacyGroupAddValue,
|
||||
WAPrivacyOnlineValue,
|
||||
WAPrivacyValue,
|
||||
WAReadReceiptsValue,
|
||||
} from 'baileys';
|
||||
|
||||
export class OnWhatsAppDto {
|
||||
constructor(
|
||||
@ -84,7 +91,7 @@ export class PrivacySettingDto {
|
||||
status: WAPrivacyValue;
|
||||
online: WAPrivacyOnlineValue;
|
||||
last: WAPrivacyValue;
|
||||
groupadd: WAPrivacyValue;
|
||||
groupadd: WAPrivacyGroupAddValue;
|
||||
}
|
||||
|
||||
export class DeleteMessage {
|
||||
|
@ -2,14 +2,15 @@ import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { ProviderFiles } from '@api/provider/sessions';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { CacheService } from '@api/services/cache.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Integration } from '@api/types/wa.types';
|
||||
import { ConfigService } from '@config/env.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
|
||||
import { EvolutionStartupService } from './evolution/evolution.channel.service';
|
||||
import { BaileysStartupService } from './whatsapp/baileys/whatsapp.baileys.service';
|
||||
import { BusinessStartupService } from './whatsapp/business/whatsapp.business.service';
|
||||
import { BusinessStartupService } from './meta/whatsapp.business.service';
|
||||
import { BaileysStartupService } from './whatsapp/whatsapp.baileys.service';
|
||||
|
||||
type ChannelDataType = {
|
||||
configService: ConfigService;
|
||||
@ -21,7 +22,35 @@ type ChannelDataType = {
|
||||
providerFiles: ProviderFiles;
|
||||
};
|
||||
|
||||
export interface ChannelControllerInterface {
|
||||
receiveWebhook(data: any): Promise<any>;
|
||||
}
|
||||
|
||||
export class ChannelController {
|
||||
public prismaRepository: PrismaRepository;
|
||||
public waMonitor: WAMonitoringService;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
this.prisma = prismaRepository;
|
||||
this.monitor = waMonitor;
|
||||
}
|
||||
|
||||
public set prisma(prisma: PrismaRepository) {
|
||||
this.prismaRepository = prisma;
|
||||
}
|
||||
|
||||
public get prisma() {
|
||||
return this.prismaRepository;
|
||||
}
|
||||
|
||||
public set monitor(waMonitor: WAMonitoringService) {
|
||||
this.waMonitor = waMonitor;
|
||||
}
|
||||
|
||||
public get monitor() {
|
||||
return this.waMonitor;
|
||||
}
|
||||
|
||||
public init(instanceData: InstanceDto, data: ChannelDataType) {
|
||||
if (!instanceData.token && instanceData.integration === Integration.WHATSAPP_BUSINESS) {
|
||||
throw new BadRequestException('token is required');
|
||||
|
15
src/api/integrations/channel/channel.router.ts
Normal file
15
src/api/integrations/channel/channel.router.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
import { EvolutionRouter } from './evolution/evolution.router';
|
||||
import { MetaRouter } from './meta/meta.router';
|
||||
|
||||
export class ChannelRouter {
|
||||
public readonly router: Router;
|
||||
|
||||
constructor(configService: any) {
|
||||
this.router = Router();
|
||||
|
||||
this.router.use('/', new EvolutionRouter(configService).router);
|
||||
this.router.use('/', new MetaRouter(configService).router);
|
||||
}
|
||||
}
|
@ -141,7 +141,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
pushName: messageRaw.pushName,
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
|
||||
Events.MESSAGES_UPSERT,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -182,7 +182,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
|
||||
this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
await this.chatwootService.eventWhatsapp(
|
||||
Events.CONTACTS_UPDATE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -246,7 +246,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
|
||||
this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled && !isIntegration) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.SEND_MESSAGE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -254,7 +254,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled && isIntegration)
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && isIntegration)
|
||||
await chatbotController.emit({
|
||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
remoteJid: messageRaw.key.remoteJid,
|
||||
|
@ -0,0 +1,39 @@
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
|
||||
import { ChannelController, ChannelControllerInterface } from '../channel.controller';
|
||||
|
||||
export class EvolutionController extends ChannelController implements ChannelControllerInterface {
|
||||
private readonly logger = new Logger(EvolutionController.name);
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
integrationEnabled: boolean;
|
||||
|
||||
public async receiveWebhook(data: any) {
|
||||
const numberId = data.numberId;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
}
|
18
src/api/integrations/channel/evolution/evolution.router.ts
Normal file
18
src/api/integrations/channel/evolution/evolution.router.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { evolutionController } from '@api/server.module';
|
||||
import { ConfigService } from '@config/env.config';
|
||||
import { Router } from 'express';
|
||||
|
||||
export class EvolutionRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService) {
|
||||
super();
|
||||
this.router.post(this.routerPath('webhook/evolution', false), async (req, res) => {
|
||||
const { body } = req;
|
||||
const response = await evolutionController.receiveWebhook(body);
|
||||
|
||||
return res.status(200).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
72
src/api/integrations/channel/meta/meta.controller.ts
Normal file
72
src/api/integrations/channel/meta/meta.controller.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import axios from 'axios';
|
||||
|
||||
import { ChannelController, ChannelControllerInterface } from '../channel.controller';
|
||||
|
||||
export class MetaController extends ChannelController implements ChannelControllerInterface {
|
||||
private readonly logger = new Logger(MetaController.name);
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
integrationEnabled: boolean;
|
||||
|
||||
public async receiveWebhook(data: any) {
|
||||
if (data.object === 'whatsapp_business_account') {
|
||||
if (data.entry[0]?.changes[0]?.field === 'message_template_status_update') {
|
||||
const template = await this.prismaRepository.template.findFirst({
|
||||
where: { templateId: `${data.entry[0].changes[0].value.message_template_id}` },
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
console.log('template not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const { webhookUrl } = template;
|
||||
|
||||
await axios.post(webhookUrl, data.entry[0].changes[0].value, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
data.entry?.forEach(async (entry: any) => {
|
||||
const numberId = entry.changes[0].value.metadata.phone_number_id;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookMeta -> numberId not found');
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhookMeta -> instance not found');
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
}
|
24
src/api/integrations/channel/meta/meta.router.ts
Normal file
24
src/api/integrations/channel/meta/meta.router.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { metaController } from '@api/server.module';
|
||||
import { ConfigService, WaBusiness } from '@config/env.config';
|
||||
import { Router } from 'express';
|
||||
|
||||
export class MetaRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService) {
|
||||
super();
|
||||
this.router
|
||||
.get(this.routerPath('webhook/meta', false), async (req, res) => {
|
||||
if (req.query['hub.verify_token'] === configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK)
|
||||
res.send(req.query['hub.challenge']);
|
||||
else res.send('Error, wrong validation token');
|
||||
})
|
||||
.post(this.routerPath('webhook/meta', false), async (req, res) => {
|
||||
const { body } = req;
|
||||
const response = await metaController.receiveWebhook(body);
|
||||
|
||||
return res.status(200).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
@ -482,7 +482,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
pushName: messageRaw.pushName,
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
|
||||
Events.MESSAGES_UPSERT,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -525,7 +525,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
|
||||
this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
await this.chatwootService.eventWhatsapp(
|
||||
Events.CONTACTS_UPDATE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -588,7 +588,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
data: message,
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.MESSAGES_DELETE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -908,7 +908,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
|
||||
this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled && !isIntegration) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.SEND_MESSAGE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -916,7 +916,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled && isIntegration)
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && isIntegration)
|
||||
await chatbotController.emit({
|
||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
remoteJid: messageRaw.key.remoteJid,
|
@ -92,10 +92,9 @@ import makeWASocket, {
|
||||
getContentType,
|
||||
getDevice,
|
||||
GroupMetadata,
|
||||
GroupParticipant,
|
||||
isJidBroadcast,
|
||||
isJidGroup,
|
||||
// isJidNewsletter,
|
||||
isJidNewsletter,
|
||||
isJidUser,
|
||||
makeCacheableSignalKeyStore,
|
||||
MessageUpsertType,
|
||||
@ -144,7 +143,6 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
) {
|
||||
super(configService, eventEmitter, prismaRepository, chatwootCache);
|
||||
this.instance.qrcode = { count: 0 };
|
||||
// this.recoveringMessages();
|
||||
|
||||
this.authStateProvider = new AuthStateProvider(this.providerFiles);
|
||||
}
|
||||
@ -159,58 +157,6 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
public phoneNumber: string;
|
||||
|
||||
// private async recoveringMessages() {
|
||||
// const cacheConf = this.configService.get<CacheConf>('CACHE');
|
||||
|
||||
// if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) {
|
||||
// this.logger.info('Recovering messages lost from cache');
|
||||
// setInterval(async () => {
|
||||
// this.baileysCache.keys().then((keys) => {
|
||||
// keys.forEach(async (key) => {
|
||||
// const data = await this.baileysCache.get(key.split(':')[2]);
|
||||
|
||||
// let message: any;
|
||||
// let retry: number;
|
||||
|
||||
// if (!data?.message) {
|
||||
// message = data;
|
||||
// retry = 0;
|
||||
// } else {
|
||||
// message = data.message;
|
||||
// retry = data.retry;
|
||||
// }
|
||||
|
||||
// if (message.messageStubParameters && message.messageStubParameters[0] === 'Message absent from node') {
|
||||
// retry = retry + 1;
|
||||
// this.logger.info(`Message absent from node, retrying to send, key: ${key.split(':')[2]} retry: ${retry}`);
|
||||
// if (message.messageStubParameters[1]) {
|
||||
// await this.client.sendMessageAck(JSON.parse(message.messageStubParameters[1], BufferJSON.reviver));
|
||||
// }
|
||||
|
||||
// this.baileysCache.set(key.split(':')[2], { message, retry });
|
||||
|
||||
// if (retry >= 100) {
|
||||
// this.logger.warn(`Message absent from node, retry limit reached, key: ${key.split(':')[2]}`);
|
||||
// this.baileysCache.delete(key.split(':')[2]);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// // 15 minutes
|
||||
// }, 15 * 60 * 1000);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async forceUpdateGroupMetadataCache() {
|
||||
// this.logger.verbose('Force update group metadata cache');
|
||||
// const groups = await this.fetchAllGroups({ getParticipants: 'false' });
|
||||
|
||||
// for (const group of groups) {
|
||||
// await this.updateGroupMetadataCache(group.id);
|
||||
// }
|
||||
// }
|
||||
|
||||
public get connectionStatus() {
|
||||
return this.stateConnection;
|
||||
}
|
||||
@ -275,7 +221,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
statusCode: DisconnectReason.badSession,
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.QRCODE_UPDATED,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -333,7 +279,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
},
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.QRCODE_UPDATED,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -401,7 +347,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
},
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.STATUS_INSTANCE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -418,17 +364,6 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
}
|
||||
}
|
||||
|
||||
// if (connection === 'connecting') {
|
||||
// if (this.configService.get<Database>('DATABASE').ENABLED) {
|
||||
// await this.prismaRepository.instance.update({
|
||||
// where: { id: this.instanceId },
|
||||
// data: {
|
||||
// connectionStatus: 'connecting',
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
if (connection === 'open') {
|
||||
this.instance.wuid = this.client.user.id.replace(/:\d+/, '');
|
||||
this.instance.profilePictureUrl = (await this.profilePicture(this.instance.wuid)).profilePictureUrl;
|
||||
@ -457,7 +392,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
},
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.CONNECTION_UPDATE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -564,7 +499,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
let options;
|
||||
|
||||
if (this.localProxy.enabled) {
|
||||
if (this.localProxy?.enabled) {
|
||||
this.logger.info('Proxy enabled: ' + this.localProxy?.host);
|
||||
|
||||
if (this.localProxy?.host?.includes('proxyscrape')) {
|
||||
@ -625,8 +560,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
shouldIgnoreJid: (jid) => {
|
||||
const isGroupJid = this.localSettings.groupsIgnore && isJidGroup(jid);
|
||||
const isBroadcast = !this.localSettings.readStatus && isJidBroadcast(jid);
|
||||
// const isNewsletter = !isJidNewsletter(jid);
|
||||
const isNewsletter = jid && jid.includes('newsletter');
|
||||
const isNewsletter = isJidNewsletter(jid);
|
||||
// const isNewsletter = jid && jid.includes('newsletter');
|
||||
|
||||
return isGroupJid || isBroadcast || isNewsletter;
|
||||
},
|
||||
@ -634,7 +569,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
shouldSyncHistoryMessage: (msg: proto.Message.IHistorySyncNotification) => {
|
||||
return this.historySyncNotification(msg);
|
||||
},
|
||||
// cachedGroupMetadata: this.getGroupMetadataCache,
|
||||
cachedGroupMetadata: this.getGroupMetadataCache,
|
||||
userDevicesCache: this.userDevicesCache,
|
||||
transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 3000 },
|
||||
};
|
||||
@ -759,7 +694,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
if (
|
||||
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||
this.localChatwoot.enabled &&
|
||||
this.localChatwoot?.enabled &&
|
||||
this.localChatwoot.importContacts &&
|
||||
contactsRaw.length
|
||||
) {
|
||||
@ -798,6 +733,10 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
const findParticipant = await this.chatwootService.findContact(instance, contact.remoteJid.split('@')[0]);
|
||||
|
||||
if (!findParticipant) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chatwootService.updateContact(instance, findParticipant.id, {
|
||||
name: contact.pushName,
|
||||
avatar_url: contact.profilePicUrl,
|
||||
@ -871,7 +810,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
let timestampLimitToImport = null;
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
||||
const daysLimitToImport = this.localChatwoot.enabled ? this.localChatwoot.daysLimitImportMessages : 1000;
|
||||
const daysLimitToImport = this.localChatwoot?.enabled ? this.localChatwoot.daysLimitImportMessages : 1000;
|
||||
|
||||
const date = new Date();
|
||||
timestampLimitToImport = new Date(date.setDate(date.getDate() - daysLimitToImport)).getTime() / 1000;
|
||||
@ -979,7 +918,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
if (
|
||||
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||
this.localChatwoot.enabled &&
|
||||
this.localChatwoot?.enabled &&
|
||||
this.localChatwoot.importMessages &&
|
||||
messagesRaw.length > 0
|
||||
) {
|
||||
@ -1024,15 +963,15 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
if (received.message?.conversation || received.message?.extendedTextMessage?.text) {
|
||||
const text = received.message?.conversation || received.message?.extendedTextMessage?.text;
|
||||
if (text == 'requestPlaceholder' && !requestId) {
|
||||
// const messageId = await this.client.requestPlaceholderResend(received.key);
|
||||
// console.log('requested placeholder resync, id=', messageId);
|
||||
const messageId = await this.client.requestPlaceholderResend(received.key);
|
||||
console.log('requested placeholder resync, id=', messageId);
|
||||
} else if (requestId) {
|
||||
console.log('Message received from phone, id=', requestId, received);
|
||||
}
|
||||
|
||||
if (text == 'onDemandHistSync') {
|
||||
// const messageId = await this.client.fetchMessageHistory(50, received.key, received.messageTimestamp!);
|
||||
// console.log('requested on-demand sync, id=', messageId);
|
||||
const messageId = await this.client.fetchMessageHistory(50, received.key, received.messageTimestamp!);
|
||||
console.log('requested on-demand sync, id=', messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1040,7 +979,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
const editedMessage =
|
||||
received.message?.protocolMessage || received.message?.editedMessage?.message?.protocolMessage;
|
||||
if (editedMessage) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled)
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled)
|
||||
this.chatwootService.eventWhatsapp(
|
||||
'messages.edit',
|
||||
{ instanceName: this.instance.name, instanceId: this.instance.id },
|
||||
@ -1092,7 +1031,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
pushName: received.pushName,
|
||||
message: { ...received.message },
|
||||
contextInfo: contentMsg?.contextInfo,
|
||||
messageType: getContentType(received.message),
|
||||
messageType: getContentType(received.message) || 'unknown',
|
||||
messageTimestamp: received.messageTimestamp as number,
|
||||
instanceId: this.instanceId,
|
||||
source: getDevice(received.key.id),
|
||||
@ -1116,7 +1055,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
if (
|
||||
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||
this.localChatwoot.enabled &&
|
||||
this.localChatwoot?.enabled &&
|
||||
!received.key.id.includes('@broadcast')
|
||||
) {
|
||||
const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
|
||||
@ -1253,7 +1192,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
await this.chatwootService.eventWhatsapp(
|
||||
Events.CONTACTS_UPDATE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -1303,7 +1242,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
}
|
||||
|
||||
if (status[update.status] === 'READ' && key.fromMe) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
'messages.read',
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -1359,7 +1298,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
data: message,
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.MESSAGES_DELETE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -1615,7 +1554,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
if (
|
||||
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||
this.localChatwoot.enabled &&
|
||||
this.localChatwoot?.enabled &&
|
||||
this.localChatwoot.importMessages &&
|
||||
this.isSyncNotificationFromUsedSyncType(msg)
|
||||
) {
|
||||
@ -1737,7 +1676,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
quoted: any,
|
||||
messageId?: string,
|
||||
ephemeralExpiration?: number,
|
||||
participants?: GroupParticipant[],
|
||||
// participants?: GroupParticipant[],
|
||||
) {
|
||||
const option: any = {
|
||||
quoted,
|
||||
@ -1745,10 +1684,10 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
if (isJidGroup(sender)) {
|
||||
option.useCachedGroupMetadata = true;
|
||||
if (participants)
|
||||
option.cachedGroupMetadata = async () => {
|
||||
return { participants: participants as GroupParticipant[] };
|
||||
};
|
||||
// if (participants)
|
||||
// option.cachedGroupMetadata = async () => {
|
||||
// return { participants: participants as GroupParticipant[] };
|
||||
// };
|
||||
}
|
||||
|
||||
if (ephemeralExpiration) option.ephemeralExpiration = ephemeralExpiration;
|
||||
@ -1870,7 +1809,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
const isWA = (await this.whatsappNumber({ numbers: [number] }))?.shift();
|
||||
|
||||
if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
const body = {
|
||||
key: { remoteJid: isWA.jid },
|
||||
};
|
||||
@ -2002,7 +1941,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
|
||||
this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled && !isIntegration) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.SEND_MESSAGE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
@ -2010,7 +1949,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled && isIntegration)
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && isIntegration)
|
||||
await chatbotController.emit({
|
||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
remoteJid: messageRaw.key.remoteJid,
|
||||
@ -2019,11 +1958,61 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
isIntegration,
|
||||
});
|
||||
|
||||
if (this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE)
|
||||
await this.prismaRepository.message.create({
|
||||
if (this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE) {
|
||||
const msg = await this.prismaRepository.message.create({
|
||||
data: messageRaw,
|
||||
});
|
||||
|
||||
const isMedia =
|
||||
messageRaw?.message?.imageMessage ||
|
||||
messageRaw?.message?.videoMessage ||
|
||||
messageRaw?.message?.stickerMessage ||
|
||||
messageRaw?.message?.documentMessage ||
|
||||
messageRaw?.message?.documentWithCaptionMessage ||
|
||||
messageRaw?.message?.audioMessage;
|
||||
|
||||
if (isMedia) {
|
||||
if (this.configService.get<S3>('S3').ENABLE) {
|
||||
try {
|
||||
const message: any = messageRaw;
|
||||
const media = await this.getBase64FromMediaMessage(
|
||||
{
|
||||
message,
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
const { buffer, mediaType, fileName, size } = media;
|
||||
|
||||
const mimetype = mime.getType(fileName).toString();
|
||||
|
||||
const fullName = join(`${this.instance.id}`, messageRaw.key.remoteJid, mediaType, fileName);
|
||||
|
||||
await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, {
|
||||
'Content-Type': mimetype,
|
||||
});
|
||||
|
||||
await this.prismaRepository.media.create({
|
||||
data: {
|
||||
messageId: msg.id,
|
||||
instanceId: this.instanceId,
|
||||
type: mediaType,
|
||||
fileName: fullName,
|
||||
mimetype,
|
||||
},
|
||||
});
|
||||
|
||||
const mediaUrl = await s3Service.getObjectUrl(fullName);
|
||||
|
||||
messageRaw.message.mediaUrl = mediaUrl;
|
||||
} catch (error) {
|
||||
this.logger.error('line 1181');
|
||||
this.logger.error(['Error on upload file to minio', error?.message, error?.stack]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messageSent;
|
||||
} catch (error) {
|
||||
this.logger.error('line 2081');
|
||||
@ -2287,7 +2276,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
responseType: 'arraybuffer',
|
||||
};
|
||||
|
||||
if (this.localProxy.enabled) {
|
||||
if (this.localProxy?.enabled) {
|
||||
config = {
|
||||
...config,
|
||||
httpsAgent: makeProxyAgent({
|
||||
@ -2344,7 +2333,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
responseType: 'arraybuffer',
|
||||
};
|
||||
|
||||
if (this.localProxy.enabled) {
|
||||
if (this.localProxy?.enabled) {
|
||||
config = {
|
||||
...config,
|
||||
httpsAgent: makeProxyAgent({
|
||||
@ -3077,7 +3066,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
responseType: 'arraybuffer',
|
||||
};
|
||||
|
||||
if (this.localProxy.enabled) {
|
||||
if (this.localProxy?.enabled) {
|
||||
config = {
|
||||
...config,
|
||||
httpsAgent: makeProxyAgent({
|
||||
@ -3264,7 +3253,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
private async getGroupMetadataCache(groupJid: string) {
|
||||
if (!isJidGroup(groupJid)) return null;
|
||||
|
||||
const cacheConf = this.configService.get<CacheConf>('CACHE');
|
||||
const cacheConf = configService.get<CacheConf>('CACHE');
|
||||
|
||||
if ((cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') || cacheConf?.LOCAL?.ENABLED) {
|
||||
if (await groupMetadataCache.has(groupJid)) {
|
||||
@ -3325,7 +3314,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
||||
responseType: 'arraybuffer',
|
||||
};
|
||||
|
||||
if (this.localProxy.enabled) {
|
||||
if (this.localProxy?.enabled) {
|
||||
config = {
|
||||
...config,
|
||||
httpsAgent: makeProxyAgent({
|
@ -6,7 +6,6 @@ import {
|
||||
genericController,
|
||||
openaiController,
|
||||
typebotController,
|
||||
websocketController,
|
||||
} from '@api/server.module';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
@ -90,31 +89,17 @@ export class ChatbotController {
|
||||
pushName,
|
||||
isIntegration,
|
||||
};
|
||||
// generic
|
||||
await genericController.emit(emitData);
|
||||
|
||||
// typebot
|
||||
await typebotController.emit(emitData);
|
||||
|
||||
// openai
|
||||
await openaiController.emit(emitData);
|
||||
|
||||
// dify
|
||||
await difyController.emit(emitData);
|
||||
|
||||
// flowise
|
||||
await flowiseController.emit(emitData);
|
||||
}
|
||||
|
||||
public async setInstance(instanceName: string, data: any): Promise<any> {
|
||||
// chatwoot
|
||||
if (data.websocketEnabled)
|
||||
await websocketController.set(instanceName, {
|
||||
enabled: true,
|
||||
events: data.websocketEvents,
|
||||
});
|
||||
}
|
||||
|
||||
public processDebounce(
|
||||
userMessageDebounce: any,
|
||||
content: string,
|
||||
@ -204,7 +189,7 @@ export class ChatbotController {
|
||||
instance: InstanceDto,
|
||||
session?: IntegrationSession,
|
||||
) {
|
||||
let findBot = null;
|
||||
let findBot: null;
|
||||
|
||||
if (!session) {
|
||||
findBot = await findBotByTrigger(botRepository, settingsRepository, content, instance.instanceId);
|
||||
|
@ -19,7 +19,7 @@ export class ChatwootController {
|
||||
public async createChatwoot(instance: InstanceDto, data: ChatwootDto) {
|
||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) throw new BadRequestException('Chatwoot is disabled');
|
||||
|
||||
if (data.enabled) {
|
||||
if (data?.enabled) {
|
||||
if (!isURL(data.url, { require_tld: false })) {
|
||||
throw new BadRequestException('url is not valid');
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { configService, Dify } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import { Dify as DifyModel } from '@prisma/client';
|
||||
import { getConversationMessage } from '@utils/getConversationMessage';
|
||||
|
||||
import { ChatbotController, ChatbotControllerInterface, EmitData } from '../../chatbot.controller';
|
||||
@ -149,7 +150,7 @@ export class DifyController extends ChatbotController implements ChatbotControll
|
||||
try {
|
||||
const bot = await this.botRepository.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
description: data.description,
|
||||
botType: data.botType,
|
||||
apiUrl: data.apiUrl,
|
||||
@ -330,7 +331,7 @@ export class DifyController extends ChatbotController implements ChatbotControll
|
||||
id: botId,
|
||||
},
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
botType: data.botType,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
@ -723,49 +724,25 @@ export class DifyController extends ChatbotController implements ChatbotControll
|
||||
|
||||
const content = getConversationMessage(msg);
|
||||
|
||||
const findBot = await this.findBotTrigger(
|
||||
const findBot = (await this.findBotTrigger(
|
||||
this.botRepository,
|
||||
this.settingsRepository,
|
||||
content,
|
||||
instance,
|
||||
session,
|
||||
);
|
||||
)) as DifyModel;
|
||||
|
||||
if (!findBot) return;
|
||||
|
||||
let expire = findBot.expire;
|
||||
let keywordFinish = findBot.keywordFinish;
|
||||
let delayMessage = findBot.delayMessage;
|
||||
let unknownMessage = findBot.unknownMessage;
|
||||
let listeningFromMe = findBot.listeningFromMe;
|
||||
let stopBotFromMe = findBot.stopBotFromMe;
|
||||
let keepOpen = findBot.keepOpen;
|
||||
let debounceTime = findBot.debounceTime;
|
||||
|
||||
if (
|
||||
!expire ||
|
||||
!keywordFinish ||
|
||||
!delayMessage ||
|
||||
!unknownMessage ||
|
||||
!listeningFromMe ||
|
||||
!stopBotFromMe ||
|
||||
!keepOpen ||
|
||||
!debounceTime
|
||||
) {
|
||||
if (!expire) expire = settings.expire;
|
||||
|
||||
if (!keywordFinish) keywordFinish = settings.keywordFinish;
|
||||
|
||||
if (!delayMessage) delayMessage = settings.delayMessage;
|
||||
|
||||
if (!unknownMessage) unknownMessage = settings.unknownMessage;
|
||||
|
||||
if (!listeningFromMe || !stopBotFromMe || !debounceTime) {
|
||||
if (!listeningFromMe) listeningFromMe = settings.listeningFromMe;
|
||||
|
||||
if (!stopBotFromMe) stopBotFromMe = settings.stopBotFromMe;
|
||||
|
||||
if (!keepOpen) keepOpen = settings.keepOpen;
|
||||
|
||||
if (!debounceTime) debounceTime = settings.debounceTime;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { getConversationMessage } from '@utils/getConversationMessage';
|
||||
import { ChatbotController, ChatbotControllerInterface, EmitData } from '../../chatbot.controller';
|
||||
import { FlowiseDto } from '../dto/flowise.dto';
|
||||
import { FlowiseService } from '../services/flowise.service';
|
||||
import { Flowise } from '@prisma/client';
|
||||
|
||||
export class FlowiseController extends ChatbotController implements ChatbotControllerInterface {
|
||||
constructor(
|
||||
@ -144,7 +145,7 @@ export class FlowiseController extends ChatbotController implements ChatbotContr
|
||||
try {
|
||||
const bot = await this.botRepository.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
description: data.description,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
@ -317,7 +318,7 @@ export class FlowiseController extends ChatbotController implements ChatbotContr
|
||||
id: botId,
|
||||
},
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
expire: data.expire,
|
||||
@ -695,49 +696,25 @@ export class FlowiseController extends ChatbotController implements ChatbotContr
|
||||
|
||||
const content = getConversationMessage(msg);
|
||||
|
||||
const findBot = await this.findBotTrigger(
|
||||
const findBot = (await this.findBotTrigger(
|
||||
this.botRepository,
|
||||
this.settingsRepository,
|
||||
content,
|
||||
instance,
|
||||
session,
|
||||
);
|
||||
)) as Flowise;
|
||||
|
||||
if (!findBot) return;
|
||||
|
||||
let expire = findBot.expire;
|
||||
let keywordFinish = findBot.keywordFinish;
|
||||
let delayMessage = findBot.delayMessage;
|
||||
let unknownMessage = findBot.unknownMessage;
|
||||
let listeningFromMe = findBot.listeningFromMe;
|
||||
let stopBotFromMe = findBot.stopBotFromMe;
|
||||
let keepOpen = findBot.keepOpen;
|
||||
let debounceTime = findBot.debounceTime;
|
||||
|
||||
if (
|
||||
!expire ||
|
||||
!keywordFinish ||
|
||||
!delayMessage ||
|
||||
!unknownMessage ||
|
||||
!listeningFromMe ||
|
||||
!stopBotFromMe ||
|
||||
!keepOpen ||
|
||||
!debounceTime
|
||||
) {
|
||||
if (!expire) expire = settings.expire;
|
||||
|
||||
if (!keywordFinish) keywordFinish = settings.keywordFinish;
|
||||
|
||||
if (!delayMessage) delayMessage = settings.delayMessage;
|
||||
|
||||
if (!unknownMessage) unknownMessage = settings.unknownMessage;
|
||||
|
||||
if (!listeningFromMe || !stopBotFromMe || !debounceTime) {
|
||||
if (!listeningFromMe) listeningFromMe = settings.listeningFromMe;
|
||||
|
||||
if (!stopBotFromMe) stopBotFromMe = settings.stopBotFromMe;
|
||||
|
||||
if (!keepOpen) keepOpen = settings.keepOpen;
|
||||
|
||||
if (!debounceTime) debounceTime = settings.debounceTime;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { GenericBot } from '@prisma/client';
|
||||
import { getConversationMessage } from '@utils/getConversationMessage';
|
||||
|
||||
import { ChatbotController, ChatbotControllerInterface, EmitData } from '../../chatbot.controller';
|
||||
@ -144,7 +145,7 @@ export class GenericController extends ChatbotController implements ChatbotContr
|
||||
try {
|
||||
const bot = await this.botRepository.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
description: data.description,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
@ -317,7 +318,7 @@ export class GenericController extends ChatbotController implements ChatbotContr
|
||||
id: botId,
|
||||
},
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
expire: data.expire,
|
||||
@ -695,49 +696,25 @@ export class GenericController extends ChatbotController implements ChatbotContr
|
||||
|
||||
const content = getConversationMessage(msg);
|
||||
|
||||
const findBot = await this.findBotTrigger(
|
||||
const findBot = (await this.findBotTrigger(
|
||||
this.botRepository,
|
||||
this.settingsRepository,
|
||||
content,
|
||||
instance,
|
||||
session,
|
||||
);
|
||||
)) as GenericBot;
|
||||
|
||||
if (!findBot) return;
|
||||
|
||||
let expire = findBot.expire;
|
||||
let keywordFinish = findBot.keywordFinish;
|
||||
let delayMessage = findBot.delayMessage;
|
||||
let unknownMessage = findBot.unknownMessage;
|
||||
let listeningFromMe = findBot.listeningFromMe;
|
||||
let stopBotFromMe = findBot.stopBotFromMe;
|
||||
let keepOpen = findBot.keepOpen;
|
||||
let debounceTime = findBot.debounceTime;
|
||||
|
||||
if (
|
||||
!expire ||
|
||||
!keywordFinish ||
|
||||
!delayMessage ||
|
||||
!unknownMessage ||
|
||||
!listeningFromMe ||
|
||||
!stopBotFromMe ||
|
||||
!keepOpen ||
|
||||
!debounceTime
|
||||
) {
|
||||
if (!expire) expire = settings.expire;
|
||||
|
||||
if (!keywordFinish) keywordFinish = settings.keywordFinish;
|
||||
|
||||
if (!delayMessage) delayMessage = settings.delayMessage;
|
||||
|
||||
if (!unknownMessage) unknownMessage = settings.unknownMessage;
|
||||
|
||||
if (!listeningFromMe || !stopBotFromMe || !debounceTime) {
|
||||
if (!listeningFromMe) listeningFromMe = settings.listeningFromMe;
|
||||
|
||||
if (!stopBotFromMe) stopBotFromMe = settings.stopBotFromMe;
|
||||
|
||||
if (!keepOpen) keepOpen = settings.keepOpen;
|
||||
|
||||
if (!debounceTime) debounceTime = settings.debounceTime;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { configService, Openai } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import { OpenaiBot } from '@prisma/client';
|
||||
import { getConversationMessage } from '@utils/getConversationMessage';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
@ -313,7 +314,7 @@ export class OpenaiController extends ChatbotController implements ChatbotContro
|
||||
try {
|
||||
const bot = await this.botRepository.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
description: data.description,
|
||||
openaiCredsId: data.openaiCredsId,
|
||||
botType: data.botType,
|
||||
@ -527,7 +528,7 @@ export class OpenaiController extends ChatbotController implements ChatbotContro
|
||||
id: botId,
|
||||
},
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
openaiCredsId: data.openaiCredsId,
|
||||
botType: data.botType,
|
||||
assistantId: data.assistantId,
|
||||
@ -941,54 +942,26 @@ export class OpenaiController extends ChatbotController implements ChatbotContro
|
||||
|
||||
const content = getConversationMessage(msg);
|
||||
|
||||
const findBot = await this.findBotTrigger(
|
||||
const findBot = (await this.findBotTrigger(
|
||||
this.botRepository,
|
||||
this.settingsRepository,
|
||||
content,
|
||||
instance,
|
||||
session,
|
||||
);
|
||||
)) as OpenaiBot;
|
||||
|
||||
if (!findBot) return;
|
||||
|
||||
// verify default settings
|
||||
let openaiCredsId = findBot.openaiCredsId;
|
||||
let expire = findBot.expire;
|
||||
let keywordFinish = findBot.keywordFinish;
|
||||
let delayMessage = findBot.delayMessage;
|
||||
let unknownMessage = findBot.unknownMessage;
|
||||
let listeningFromMe = findBot.listeningFromMe;
|
||||
let stopBotFromMe = findBot.stopBotFromMe;
|
||||
let keepOpen = findBot.keepOpen;
|
||||
let debounceTime = findBot.debounceTime;
|
||||
|
||||
if (
|
||||
!openaiCredsId ||
|
||||
!expire ||
|
||||
!keywordFinish ||
|
||||
!delayMessage ||
|
||||
!unknownMessage ||
|
||||
!listeningFromMe ||
|
||||
!stopBotFromMe ||
|
||||
!keepOpen ||
|
||||
!debounceTime
|
||||
) {
|
||||
if (!openaiCredsId) openaiCredsId = settings.openaiCredsId;
|
||||
|
||||
if (!expire) expire = settings.expire;
|
||||
|
||||
if (!keywordFinish) keywordFinish = settings.keywordFinish;
|
||||
|
||||
if (!delayMessage) delayMessage = settings.delayMessage;
|
||||
|
||||
if (!unknownMessage) unknownMessage = settings.unknownMessage;
|
||||
|
||||
if (!listeningFromMe || !stopBotFromMe || !debounceTime) {
|
||||
if (!listeningFromMe) listeningFromMe = settings.listeningFromMe;
|
||||
|
||||
if (!stopBotFromMe) stopBotFromMe = settings.stopBotFromMe;
|
||||
|
||||
if (!keepOpen) keepOpen = settings.keepOpen;
|
||||
|
||||
if (!debounceTime) debounceTime = settings.debounceTime;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { Events } from '@api/types/wa.types';
|
||||
import { Auth, configService, HttpServer, Typebot } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import { Typebot as TypebotModel } from '@prisma/client';
|
||||
import { getConversationMessage } from '@utils/getConversationMessage';
|
||||
import axios from 'axios';
|
||||
|
||||
@ -150,7 +151,7 @@ export class TypebotController extends ChatbotController implements ChatbotContr
|
||||
try {
|
||||
const bot = await this.botRepository.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
description: data.description,
|
||||
url: data.url,
|
||||
typebot: data.typebot,
|
||||
@ -333,7 +334,7 @@ export class TypebotController extends ChatbotController implements ChatbotContr
|
||||
id: botId,
|
||||
},
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
url: data.url,
|
||||
typebot: data.typebot,
|
||||
expire: data.expire,
|
||||
@ -1001,13 +1002,13 @@ export class TypebotController extends ChatbotController implements ChatbotContr
|
||||
|
||||
const content = getConversationMessage(msg);
|
||||
|
||||
const findBot = await this.findBotTrigger(
|
||||
const findBot = (await this.findBotTrigger(
|
||||
this.botRepository,
|
||||
this.settingsRepository,
|
||||
content,
|
||||
instance,
|
||||
session,
|
||||
);
|
||||
)) as TypebotModel;
|
||||
|
||||
if (!findBot) return;
|
||||
|
||||
|
@ -382,7 +382,7 @@ export class TypebotService {
|
||||
}
|
||||
|
||||
const data = await this.createNewSession(instance, {
|
||||
enabled: findTypebot.enabled,
|
||||
enabled: findTypebot?.enabled,
|
||||
url: url,
|
||||
typebot: typebot,
|
||||
expire: expire,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { EventDto } from '@api/integrations/event/event.dto';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { rabbitmqController, sqsController, webhookController, websocketController } from '@api/server.module';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Server } from 'http';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
|
||||
export type EmitData = {
|
||||
instanceName: string;
|
||||
@ -16,19 +16,27 @@ export type EmitData = {
|
||||
};
|
||||
|
||||
export interface EventControllerInterface {
|
||||
integrationEnabled: boolean;
|
||||
set(instanceName: string, data: any): Promise<any>;
|
||||
get(instanceName: string): Promise<any>;
|
||||
emit({ instanceName, origin, event, data, serverUrl, dateTime, sender, apiKey, local }: EmitData): Promise<void>;
|
||||
}
|
||||
|
||||
export class EventController {
|
||||
public prismaRepository: PrismaRepository;
|
||||
public waMonitor: WAMonitoringService;
|
||||
private prismaRepository: PrismaRepository;
|
||||
private waMonitor: WAMonitoringService;
|
||||
private integrationStatus: boolean;
|
||||
private integrationName: string;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
constructor(
|
||||
prismaRepository: PrismaRepository,
|
||||
waMonitor: WAMonitoringService,
|
||||
integrationStatus: boolean,
|
||||
integrationName: string,
|
||||
) {
|
||||
this.prisma = prismaRepository;
|
||||
this.monitor = waMonitor;
|
||||
this.status = integrationStatus;
|
||||
this.name = integrationName;
|
||||
}
|
||||
|
||||
public set prisma(prisma: PrismaRepository) {
|
||||
@ -47,7 +55,74 @@ export class EventController {
|
||||
return this.waMonitor;
|
||||
}
|
||||
|
||||
public readonly events = [
|
||||
public set name(name: string) {
|
||||
this.integrationName = name;
|
||||
}
|
||||
|
||||
public get name() {
|
||||
return this.integrationName;
|
||||
}
|
||||
|
||||
public set status(status: boolean) {
|
||||
this.integrationStatus = status;
|
||||
}
|
||||
|
||||
public get status() {
|
||||
return this.integrationStatus;
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: EventDto): Promise<wa.LocalEvent> {
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data[this.name]?.enabled) {
|
||||
data[this.name].events = [];
|
||||
} else {
|
||||
if (0 === data[this.name].events.length) {
|
||||
data[this.name].events = EventController.events;
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma[this.name].upsert({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
update: {
|
||||
enabled: data[this.name]?.enabled,
|
||||
events: data[this.name].events,
|
||||
},
|
||||
create: {
|
||||
enabled: data[this.name]?.enabled,
|
||||
events: data[this.name].events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async get(instanceName: string): Promise<wa.LocalEvent> {
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await this.prisma[this.name].findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static readonly events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
@ -76,93 +151,4 @@ export class EventController {
|
||||
'REMOVE_INSTANCE',
|
||||
'LOGOUT_INSTANCE',
|
||||
];
|
||||
|
||||
public init(httpServer: Server): void {
|
||||
// websocket
|
||||
websocketController.init(httpServer);
|
||||
|
||||
// rabbitmq
|
||||
rabbitmqController.init();
|
||||
|
||||
// sqs
|
||||
sqsController.init();
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
event,
|
||||
data,
|
||||
serverUrl,
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
local,
|
||||
}: {
|
||||
instanceName: string;
|
||||
origin: string;
|
||||
event: string;
|
||||
data: Object;
|
||||
serverUrl: string;
|
||||
dateTime: string;
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
local?: boolean;
|
||||
}): Promise<void> {
|
||||
const emitData = {
|
||||
instanceName,
|
||||
origin,
|
||||
event,
|
||||
data,
|
||||
serverUrl,
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
local,
|
||||
};
|
||||
// websocket
|
||||
await websocketController.emit(emitData);
|
||||
|
||||
// rabbitmq
|
||||
await rabbitmqController.emit(emitData);
|
||||
|
||||
// sqs
|
||||
await sqsController.emit(emitData);
|
||||
|
||||
// webhook
|
||||
await webhookController.emit(emitData);
|
||||
}
|
||||
|
||||
public async setInstance(instanceName: string, data: any): Promise<any> {
|
||||
// websocket
|
||||
if (data.websocketEnabled)
|
||||
await websocketController.set(instanceName, {
|
||||
enabled: true,
|
||||
events: data.websocketEvents,
|
||||
});
|
||||
|
||||
// rabbitmq
|
||||
if (data.rabbitmqEnabled)
|
||||
await rabbitmqController.set(instanceName, {
|
||||
enabled: true,
|
||||
events: data.rabbitmqEvents,
|
||||
});
|
||||
|
||||
// sqs
|
||||
if (data.sqsEnabled)
|
||||
await sqsController.set(instanceName, {
|
||||
enabled: true,
|
||||
events: data.sqsEvents,
|
||||
});
|
||||
|
||||
// webhook
|
||||
if (data.webhookEnabled)
|
||||
await webhookController.set(instanceName, {
|
||||
enabled: true,
|
||||
events: data.webhookEvents,
|
||||
url: data.webhookUrl,
|
||||
webhookBase64: data.webhookBase64,
|
||||
webhookByEvents: data.webhookByEvents,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
53
src/api/integrations/event/event.dto.ts
Normal file
53
src/api/integrations/event/event.dto.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Constructor } from '@api/integrations/integration.dto';
|
||||
|
||||
export class EventDto {
|
||||
webhook?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
url?: string;
|
||||
byEvents?: boolean;
|
||||
base64?: boolean;
|
||||
};
|
||||
|
||||
websocket?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
|
||||
sqs?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
|
||||
rabbitmq?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export function EventInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
webhook?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
url?: string;
|
||||
byEvents?: boolean;
|
||||
base64?: boolean;
|
||||
};
|
||||
|
||||
websocket?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
|
||||
sqs?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
|
||||
rabbitmq?: {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
};
|
||||
}
|
134
src/api/integrations/event/event.manager.ts
Normal file
134
src/api/integrations/event/event.manager.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import { RabbitmqController } from '@api/integrations/event/rabbitmq/rabbitmq.controller';
|
||||
import { SqsController } from '@api/integrations/event/sqs/sqs.controller';
|
||||
import { WebhookController } from '@api/integrations/event/webhook/webhook.controller';
|
||||
import { WebsocketController } from '@api/integrations/event/websocket/websocket.controller';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Server } from 'http';
|
||||
|
||||
export class EventManager {
|
||||
private prismaRepository: PrismaRepository;
|
||||
private waMonitor: WAMonitoringService;
|
||||
private websocketController: WebsocketController;
|
||||
private webhookController: WebhookController;
|
||||
private rabbitmqController: RabbitmqController;
|
||||
private sqsController: SqsController;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
this.prisma = prismaRepository;
|
||||
this.monitor = waMonitor;
|
||||
|
||||
this.websocket = new WebsocketController(prismaRepository, waMonitor);
|
||||
this.webhook = new WebhookController(prismaRepository, waMonitor);
|
||||
this.rabbitmq = new RabbitmqController(prismaRepository, waMonitor);
|
||||
this.sqs = new SqsController(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
public set prisma(prisma: PrismaRepository) {
|
||||
this.prismaRepository = prisma;
|
||||
}
|
||||
|
||||
public get prisma() {
|
||||
return this.prismaRepository;
|
||||
}
|
||||
|
||||
public set monitor(waMonitor: WAMonitoringService) {
|
||||
this.waMonitor = waMonitor;
|
||||
}
|
||||
|
||||
public get monitor() {
|
||||
return this.waMonitor;
|
||||
}
|
||||
|
||||
public set websocket(websocket: WebsocketController) {
|
||||
this.websocketController = websocket;
|
||||
}
|
||||
|
||||
public get websocket() {
|
||||
return this.websocketController;
|
||||
}
|
||||
|
||||
public set webhook(webhook: WebhookController) {
|
||||
this.webhookController = webhook;
|
||||
}
|
||||
|
||||
public get webhook() {
|
||||
return this.webhookController;
|
||||
}
|
||||
|
||||
public set rabbitmq(rabbitmq: RabbitmqController) {
|
||||
this.rabbitmqController = rabbitmq;
|
||||
}
|
||||
|
||||
public get rabbitmq() {
|
||||
return this.rabbitmqController;
|
||||
}
|
||||
|
||||
public set sqs(sqs: SqsController) {
|
||||
this.sqsController = sqs;
|
||||
}
|
||||
|
||||
public get sqs() {
|
||||
return this.sqsController;
|
||||
}
|
||||
|
||||
public init(httpServer: Server): void {
|
||||
this.websocket.init(httpServer);
|
||||
this.rabbitmq.init();
|
||||
this.sqs.init();
|
||||
}
|
||||
|
||||
public async emit(eventData: {
|
||||
instanceName: string;
|
||||
origin: string;
|
||||
event: string;
|
||||
data: Object;
|
||||
serverUrl: string;
|
||||
dateTime: string;
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
local?: boolean;
|
||||
}): Promise<void> {
|
||||
await this.websocket.emit(eventData);
|
||||
await this.rabbitmq.emit(eventData);
|
||||
await this.sqs.emit(eventData);
|
||||
await this.webhook.emit(eventData);
|
||||
}
|
||||
|
||||
public async setInstance(instanceName: string, data: any): Promise<any> {
|
||||
if (data.websocket)
|
||||
await this.websocket.set(instanceName, {
|
||||
websocket: {
|
||||
enabled: data.websocket?.enabled,
|
||||
events: data.websocket?.events,
|
||||
},
|
||||
});
|
||||
|
||||
if (data.rabbitmq)
|
||||
await this.rabbitmq.set(instanceName, {
|
||||
rabbitmq: {
|
||||
enabled: data.rabbitmq?.enabled,
|
||||
events: data.rabbitmq?.events,
|
||||
},
|
||||
});
|
||||
|
||||
if (data.sqs)
|
||||
await this.sqs.set(instanceName, {
|
||||
sqs: {
|
||||
enabled: data.sqs?.enabled,
|
||||
events: data.sqs?.events,
|
||||
},
|
||||
});
|
||||
|
||||
if (data.webhook)
|
||||
await this.webhook.set(instanceName, {
|
||||
webhook: {
|
||||
enabled: data.webhook?.enabled,
|
||||
events: data.webhook?.events,
|
||||
url: data.webhook?.url,
|
||||
base64: data.webhook?.base64,
|
||||
byEvents: data.webhook?.byEvents,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { RabbitmqRouter } from '@api/integrations/event/rabbitmq/routes/rabbitmq.router';
|
||||
import { SqsRouter } from '@api/integrations/event/sqs/routes/sqs.router';
|
||||
import { WebhookRouter } from '@api/integrations/event/webhook/routes/webhook.router';
|
||||
import { WebsocketRouter } from '@api/integrations/event/websocket/routes/websocket.router';
|
||||
import { RabbitmqRouter } from '@api/integrations/event/rabbitmq/rabbitmq.router';
|
||||
import { SqsRouter } from '@api/integrations/event/sqs/sqs.router';
|
||||
import { WebhookRouter } from '@api/integrations/event/webhook/webhook.router';
|
||||
import { WebsocketRouter } from '@api/integrations/event/websocket/websocket.router';
|
||||
import { Router } from 'express';
|
||||
|
||||
export class EventRouter {
|
||||
|
@ -1,4 +1,39 @@
|
||||
export * from '@api/integrations/event/rabbitmq/validate/rabbitmq.schema';
|
||||
export * from '@api/integrations/event/sqs/validate/sqs.schema';
|
||||
export * from '@api/integrations/event/webhook/validate/webhook.schema';
|
||||
export * from '@api/integrations/event/websocket/validate/websocket.schema';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { EventController } from './event.controller';
|
||||
|
||||
export * from '@api/integrations/event/webhook/webhook.schema';
|
||||
|
||||
export const eventSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
websocket: {
|
||||
$ref: '#/$defs/event',
|
||||
},
|
||||
rabbitmq: {
|
||||
$ref: '#/$defs/event',
|
||||
},
|
||||
sqs: {
|
||||
$ref: '#/$defs/event',
|
||||
},
|
||||
},
|
||||
$defs: {
|
||||
event: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: EventController.events,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { Constructor } from '@api/integrations/integration.dto';
|
||||
|
||||
export class RabbitmqDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
export function RabbitMQInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
rabbitmqEnabled?: boolean;
|
||||
rabbitmqEvents?: string[];
|
||||
};
|
||||
}
|
@ -1,37 +1,38 @@
|
||||
import { RabbitmqDto } from '@api/integrations/event/rabbitmq/dto/rabbitmq.dto';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { configService, Log, Rabbitmq } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { NotFoundException } from '@exceptions';
|
||||
import * as amqp from 'amqplib/callback_api';
|
||||
|
||||
import { EmitData, EventController, EventControllerInterface } from '../../event.controller';
|
||||
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||
|
||||
export class RabbitmqController extends EventController implements EventControllerInterface {
|
||||
public amqpChannel: amqp.Channel | null = null;
|
||||
private readonly logger = new Logger(RabbitmqController.name);
|
||||
integrationEnabled = configService.get<Rabbitmq>('RABBITMQ')?.ENABLED;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
super(prismaRepository, waMonitor, configService.get<Rabbitmq>('RABBITMQ')?.ENABLED, 'rabbitmq');
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
if (!this.integrationEnabled) return;
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
|
||||
|
||||
amqp.connect(uri, (error, connection) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
connection.createChannel((channelError, channel) => {
|
||||
if (channelError) {
|
||||
reject(channelError);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -45,6 +46,7 @@ export class RabbitmqController extends EventController implements EventControll
|
||||
this.amqpChannel = channel;
|
||||
|
||||
this.logger.info('AMQP initialized');
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@ -61,57 +63,6 @@ export class RabbitmqController extends EventController implements EventControll
|
||||
return this.amqpChannel;
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: RabbitmqDto): Promise<wa.LocalRabbitmq> {
|
||||
if (!this.integrationEnabled) return;
|
||||
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.prisma.rabbitmq.update({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
return this.prisma.rabbitmq.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async get(instanceName: string): Promise<wa.LocalWebsocket> {
|
||||
if (!this.integrationEnabled) return;
|
||||
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.rabbitmq.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
@ -122,7 +73,9 @@ export class RabbitmqController extends EventController implements EventControll
|
||||
sender,
|
||||
apiKey,
|
||||
}: EmitData): Promise<void> {
|
||||
if (!this.integrationEnabled) return;
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceRabbitmq = await this.get(instanceName);
|
||||
const rabbitmqLocal = instanceRabbitmq?.events;
|
||||
@ -178,6 +131,7 @@ export class RabbitmqController extends EventController implements EventControll
|
||||
|
||||
this.logger.log(logData);
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (error) {
|
||||
retry++;
|
||||
@ -231,10 +185,12 @@ export class RabbitmqController extends EventController implements EventControll
|
||||
|
||||
private async initGlobalQueues(): Promise<void> {
|
||||
this.logger.info('Initializing global queues');
|
||||
|
||||
const events = configService.get<Rabbitmq>('RABBITMQ').EVENTS;
|
||||
|
||||
if (!events) {
|
||||
this.logger.warn('No events to initialize on AMQP');
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { RabbitmqDto } from '@api/integrations/event/rabbitmq/dto/rabbitmq.dto';
|
||||
import { rabbitmqController } from '@api/server.module';
|
||||
import { EventDto } from '@api/integrations/event/event.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { instanceSchema, rabbitmqSchema } from '@validate/validate.schema';
|
||||
import { eventManager } from '@api/server.module';
|
||||
import { eventSchema, instanceSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
export class RabbitmqRouter extends RouterBroker {
|
||||
@ -11,11 +11,11 @@ export class RabbitmqRouter extends RouterBroker {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<RabbitmqDto>({
|
||||
const response = await this.dataValidate<EventDto>({
|
||||
request: req,
|
||||
schema: rabbitmqSchema,
|
||||
ClassRef: RabbitmqDto,
|
||||
execute: (instance, data) => rabbitmqController.set(instance.instanceName, data),
|
||||
schema: eventSchema,
|
||||
ClassRef: EventDto,
|
||||
execute: (instance, data) => eventManager.rabbitmq.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
@ -25,7 +25,7 @@ export class RabbitmqRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => rabbitmqController.get(instance.instanceName),
|
||||
execute: (instance) => eventManager.rabbitmq.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
@ -1,65 +0,0 @@
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
const properties = {};
|
||||
propertyNames.forEach(
|
||||
(property) =>
|
||||
(properties[property] = {
|
||||
minLength: 1,
|
||||
description: `The "${property}" cannot be empty`,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
if: {
|
||||
propertyNames: {
|
||||
enum: [...propertyNames],
|
||||
},
|
||||
},
|
||||
then: { properties },
|
||||
};
|
||||
};
|
||||
|
||||
export const rabbitmqSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
import { Constructor } from '@api/integrations/integration.dto';
|
||||
|
||||
export class SqsDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
export function SQSInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
sqsEnabled?: boolean;
|
||||
sqsEvents?: string[];
|
||||
};
|
||||
}
|
@ -1,29 +1,27 @@
|
||||
import { SqsDto } from '@api/integrations/event/sqs/dto/sqs.dto';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { SQS } from '@aws-sdk/client-sqs';
|
||||
import { configService, Log, Sqs } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { NotFoundException } from '@exceptions';
|
||||
|
||||
import { EmitData, EventController, EventControllerInterface } from '../../event.controller';
|
||||
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||
|
||||
export class SqsController extends EventController implements EventControllerInterface {
|
||||
private sqs: SQS;
|
||||
private readonly logger = new Logger(SqsController.name);
|
||||
integrationEnabled = configService.get<Sqs>('SQS')?.ENABLED;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
super(prismaRepository, waMonitor, configService.get<Sqs>('SQS')?.ENABLED, 'sqs');
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
if (!this.integrationEnabled) return;
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
new Promise<void>((resolve, reject) => {
|
||||
new Promise<void>((resolve) => {
|
||||
const awsConfig = configService.get<Sqs>('SQS');
|
||||
|
||||
this.sqs = new SQS({
|
||||
credentials: {
|
||||
accessKeyId: awsConfig.ACCESS_KEY_ID,
|
||||
@ -34,6 +32,7 @@ export class SqsController extends EventController implements EventControllerInt
|
||||
});
|
||||
|
||||
this.logger.info('SQS initialized');
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
@ -46,57 +45,6 @@ export class SqsController extends EventController implements EventControllerInt
|
||||
return this.sqs;
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: SqsDto): Promise<wa.LocalSqs> {
|
||||
if (!this.integrationEnabled) return;
|
||||
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.prisma.sqs.update({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
return this.prisma.sqs.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async get(instanceName: string): Promise<wa.LocalSqs> {
|
||||
if (!this.integrationEnabled) return;
|
||||
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.sqs.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
@ -107,7 +55,9 @@ export class SqsController extends EventController implements EventControllerInt
|
||||
sender,
|
||||
apiKey,
|
||||
}: EmitData): Promise<void> {
|
||||
if (!this.integrationEnabled) return;
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceSqs = await this.get(instanceName);
|
||||
const sqsLocal = instanceSqs?.events;
|
||||
@ -117,11 +67,8 @@ export class SqsController extends EventController implements EventControllerInt
|
||||
if (this.sqs) {
|
||||
if (Array.isArray(sqsLocal) && sqsLocal.includes(we)) {
|
||||
const eventFormatted = `${event.replace('.', '_').toLowerCase()}`;
|
||||
|
||||
const queueName = `${instanceName}_${eventFormatted}.fifo`;
|
||||
|
||||
const sqsConfig = configService.get<Sqs>('SQS');
|
||||
|
||||
const sqsUrl = `https://sqs.${sqsConfig.REGION}.amazonaws.com/${sqsConfig.ACCOUNT_ID}/${queueName}`;
|
||||
|
||||
const message = {
|
@ -1,9 +1,9 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { SqsDto } from '@api/integrations/event/sqs/dto/sqs.dto';
|
||||
import { sqsController } from '@api/server.module';
|
||||
import { EventDto } from '@api/integrations/event/event.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { instanceSchema, sqsSchema } from '@validate/validate.schema';
|
||||
import { eventManager } from '@api/server.module';
|
||||
import { eventSchema, instanceSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
export class SqsRouter extends RouterBroker {
|
||||
@ -11,11 +11,11 @@ export class SqsRouter extends RouterBroker {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<SqsDto>({
|
||||
const response = await this.dataValidate<EventDto>({
|
||||
request: req,
|
||||
schema: sqsSchema,
|
||||
ClassRef: SqsDto,
|
||||
execute: (instance, data) => sqsController.set(instance.instanceName, data),
|
||||
schema: eventSchema,
|
||||
ClassRef: EventDto,
|
||||
execute: (instance, data) => eventManager.sqs.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
@ -25,7 +25,7 @@ export class SqsRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => sqsController.get(instance.instanceName),
|
||||
execute: (instance) => eventManager.sqs.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
@ -1,65 +0,0 @@
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
const properties = {};
|
||||
propertyNames.forEach(
|
||||
(property) =>
|
||||
(properties[property] = {
|
||||
minLength: 1,
|
||||
description: `The "${property}" cannot be empty`,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
if: {
|
||||
propertyNames: {
|
||||
enum: [...propertyNames],
|
||||
},
|
||||
},
|
||||
then: { properties },
|
||||
};
|
||||
};
|
||||
|
||||
export const sqsSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
import { Constructor } from '@api/integrations/integration.dto';
|
||||
|
||||
export class WebhookDto {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
events?: string[];
|
||||
webhookByEvents?: boolean;
|
||||
webhookBase64?: boolean;
|
||||
}
|
||||
|
||||
export function WebhookInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
webhookUrl?: string;
|
||||
webhookByEvents?: boolean;
|
||||
webhookBase64?: boolean;
|
||||
webhookEvents?: string[];
|
||||
};
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { webhookController } from '@api/server.module';
|
||||
import { ConfigService, WaBusiness } from '@config/env.config';
|
||||
import { instanceSchema, webhookSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
import { WebhookDto } from '../dto/webhook.dto';
|
||||
|
||||
export class WebhookRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<WebhookDto>({
|
||||
request: req,
|
||||
schema: webhookSchema,
|
||||
ClassRef: WebhookDto,
|
||||
execute: (instance, data) => webhookController.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => webhookController.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.get(this.routerPath('meta', false), async (req, res) => {
|
||||
if (req.query['hub.verify_token'] === configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK)
|
||||
res.send(req.query['hub.challenge']);
|
||||
else res.send('Error, wrong validation token');
|
||||
})
|
||||
.post(this.routerPath('meta', false), async (req, res) => {
|
||||
const { body } = req;
|
||||
const response = await webhookController.receiveWebhookMeta(body);
|
||||
|
||||
return res.status(200).json(response);
|
||||
})
|
||||
.post(this.routerPath('evolution', false), async (req, res) => {
|
||||
const { body } = req;
|
||||
const response = await webhookController.receiveWebhookEvolution(body);
|
||||
|
||||
return res.status(200).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
const properties = {};
|
||||
propertyNames.forEach(
|
||||
(property) =>
|
||||
(properties[property] = {
|
||||
minLength: 1,
|
||||
description: `The "${property}" cannot be empty`,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
if: {
|
||||
propertyNames: {
|
||||
enum: [...propertyNames],
|
||||
},
|
||||
},
|
||||
then: { properties },
|
||||
};
|
||||
};
|
||||
|
||||
export const webhookSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean' },
|
||||
url: { type: 'string' },
|
||||
webhookByEvents: { type: 'boolean' },
|
||||
webhookBase64: { type: 'boolean' },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled', 'url'],
|
||||
...isNotEmpty('enabled', 'url'),
|
||||
};
|
@ -1,74 +1,57 @@
|
||||
import { EventDto } from '@api/integrations/event/event.dto';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { configService, Log, Webhook } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { BadRequestException, NotFoundException } from '@exceptions';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import axios from 'axios';
|
||||
import { isURL } from 'class-validator';
|
||||
|
||||
import { EmitData, EventController, EventControllerInterface } from '../../event.controller';
|
||||
import { WebhookDto } from '../dto/webhook.dto';
|
||||
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||
|
||||
export class WebhookController extends EventController implements EventControllerInterface {
|
||||
private readonly logger = new Logger(WebhookController.name);
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
super(prismaRepository, waMonitor, true, 'webhook');
|
||||
}
|
||||
integrationEnabled: boolean;
|
||||
|
||||
public async set(instanceName: string, data: WebhookDto): Promise<wa.LocalWebHook> {
|
||||
if (!isURL(data.url, { require_tld: false })) {
|
||||
override async set(instanceName: string, data: EventDto): Promise<wa.LocalWebHook> {
|
||||
if (!isURL(data.webhook.url, { require_tld: false })) {
|
||||
throw new BadRequestException('Invalid "url" property');
|
||||
}
|
||||
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
if (!data.webhook?.enabled) {
|
||||
data.webhook.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
if (0 === data.webhook.events.length) {
|
||||
data.webhook.events = EventController.events;
|
||||
}
|
||||
}
|
||||
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.prisma.webhook.upsert({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
update: {
|
||||
...data,
|
||||
enabled: data.webhook?.enabled,
|
||||
events: data.webhook?.events,
|
||||
url: data.webhook?.url,
|
||||
webhookBase64: data.webhook.base64,
|
||||
webhookByEvents: data.webhook.byEvents,
|
||||
},
|
||||
create: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
enabled: data.webhook?.enabled,
|
||||
events: data.webhook?.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
url: data.url,
|
||||
webhookBase64: data.webhookBase64,
|
||||
webhookByEvents: data.webhookByEvents,
|
||||
url: data.webhook?.url,
|
||||
webhookBase64: data.webhook.base64,
|
||||
webhookByEvents: data.webhook.byEvents,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async get(instanceName: string): Promise<wa.LocalWebHook> {
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.webhook.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
@ -80,13 +63,14 @@ export class WebhookController extends EventController implements EventControlle
|
||||
apiKey,
|
||||
local,
|
||||
}: EmitData): Promise<void> {
|
||||
const instanceWebhook = await this.get(instanceName);
|
||||
if (!instanceWebhook || !instanceWebhook.enabled) {
|
||||
const instance = (await this.get(instanceName)) as EventDto;
|
||||
|
||||
if (!instance || !instance.webhook?.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const webhookConfig = configService.get<Webhook>('WEBHOOK');
|
||||
const webhookLocal = instanceWebhook?.events;
|
||||
const webhookLocal = instance.webhook?.events;
|
||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
||||
const enabledLog = configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS');
|
||||
@ -95,7 +79,7 @@ export class WebhookController extends EventController implements EventControlle
|
||||
event,
|
||||
instance: instanceName,
|
||||
data,
|
||||
destination: instanceWebhook?.url,
|
||||
destination: instance.webhook?.url,
|
||||
date_time: dateTime,
|
||||
sender,
|
||||
server_url: serverUrl,
|
||||
@ -106,10 +90,10 @@ export class WebhookController extends EventController implements EventControlle
|
||||
if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) {
|
||||
let baseURL: string;
|
||||
|
||||
if (instanceWebhook?.webhookByEvents) {
|
||||
baseURL = `${instanceWebhook?.url}/${transformedWe}`;
|
||||
if (instance.webhook?.byEvents) {
|
||||
baseURL = `${instance.webhook?.url}/${transformedWe}`;
|
||||
} else {
|
||||
baseURL = instanceWebhook?.url;
|
||||
baseURL = instance.webhook?.url;
|
||||
}
|
||||
|
||||
if (enabledLog) {
|
||||
@ -123,7 +107,7 @@ export class WebhookController extends EventController implements EventControlle
|
||||
}
|
||||
|
||||
try {
|
||||
if (instanceWebhook?.enabled && isURL(instanceWebhook.url, { require_tld: false })) {
|
||||
if (instance.webhook?.enabled && isURL(instance.webhook.url, { require_tld: false })) {
|
||||
const httpService = axios.create({ baseURL });
|
||||
|
||||
await httpService.post('', webhookData);
|
||||
@ -186,76 +170,4 @@ export class WebhookController extends EventController implements EventControlle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async receiveWebhookMeta(data: any) {
|
||||
if (data.object === 'whatsapp_business_account') {
|
||||
if (data.entry[0]?.changes[0]?.field === 'message_template_status_update') {
|
||||
const template = await this.prismaRepository.template.findFirst({
|
||||
where: { templateId: `${data.entry[0].changes[0].value.message_template_id}` },
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
console.log('template not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const { webhookUrl } = template;
|
||||
|
||||
await axios.post(webhookUrl, data.entry[0].changes[0].value, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
data.entry?.forEach(async (entry: any) => {
|
||||
const numberId = entry.changes[0].value.metadata.phone_number_id;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookMeta -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhookMeta -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public async receiveWebhookEvolution(data: any) {
|
||||
const numberId = data.numberId;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
}
|
37
src/api/integrations/event/webhook/webhook.router.ts
Normal file
37
src/api/integrations/event/webhook/webhook.router.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { EventDto } from '@api/integrations/event/event.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { eventManager } from '@api/server.module';
|
||||
import { ConfigService, WaBusiness } from '@config/env.config';
|
||||
import { instanceSchema, webhookSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
export class WebhookRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<EventDto>({
|
||||
request: req,
|
||||
schema: webhookSchema,
|
||||
ClassRef: EventDto,
|
||||
execute: (instance, data) => eventManager.webhook.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => eventManager.webhook.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
50
src/api/integrations/event/webhook/webhook.schema.ts
Normal file
50
src/api/integrations/event/webhook/webhook.schema.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { EventController } from '../event.controller';
|
||||
|
||||
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
const properties = {};
|
||||
propertyNames.forEach(
|
||||
(property) =>
|
||||
(properties[property] = {
|
||||
minLength: 1,
|
||||
description: `The "${property}" cannot be empty`,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
if: {
|
||||
propertyNames: {
|
||||
enum: [...propertyNames],
|
||||
},
|
||||
},
|
||||
then: { properties },
|
||||
};
|
||||
};
|
||||
|
||||
export const webhookSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
webhook: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean' },
|
||||
url: { type: 'string' },
|
||||
byEvents: { type: 'boolean' },
|
||||
base64: { type: 'boolean' },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: EventController.events,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled', 'url'],
|
||||
...isNotEmpty('enabled', 'url'),
|
||||
},
|
||||
},
|
||||
required: ['webhook'],
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
import { Constructor } from '@api/integrations/integration.dto';
|
||||
|
||||
export class WebsocketDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
export function WebsocketInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
websocketEnabled?: boolean;
|
||||
websocketEvents?: string[];
|
||||
};
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
const properties = {};
|
||||
propertyNames.forEach(
|
||||
(property) =>
|
||||
(properties[property] = {
|
||||
minLength: 1,
|
||||
description: `The "${property}" cannot be empty`,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
if: {
|
||||
propertyNames: {
|
||||
enum: [...propertyNames],
|
||||
},
|
||||
},
|
||||
then: { properties },
|
||||
};
|
||||
};
|
||||
|
||||
export const websocketSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
@ -1,28 +1,27 @@
|
||||
import { WebsocketDto } from '@api/integrations/event/websocket/dto/websocket.dto';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { configService, Cors, Log, Websocket } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { NotFoundException } from '@exceptions';
|
||||
import { Server } from 'http';
|
||||
import { Server as SocketIO } from 'socket.io';
|
||||
|
||||
import { EmitData, EventController, EventControllerInterface } from '../../event.controller';
|
||||
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||
|
||||
export class WebsocketController extends EventController implements EventControllerInterface {
|
||||
private io: SocketIO;
|
||||
private corsConfig: Array<any>;
|
||||
private readonly logger = new Logger(WebsocketController.name);
|
||||
integrationEnabled = configService.get<Websocket>('WEBSOCKET')?.ENABLED;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
super(prismaRepository, waMonitor, configService.get<Websocket>('WEBSOCKET')?.ENABLED, 'websocket');
|
||||
|
||||
this.cors = configService.get<Cors>('CORS').ORIGIN;
|
||||
}
|
||||
|
||||
public init(httpServer: Server): void {
|
||||
if (!this.integrationEnabled) return;
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.socket = new SocketIO(httpServer, {
|
||||
cors: {
|
||||
@ -57,57 +56,6 @@ export class WebsocketController extends EventController implements EventControl
|
||||
return this.io;
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: WebsocketDto): Promise<wa.LocalWebsocket> {
|
||||
if (!this.integrationEnabled) return;
|
||||
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.prisma.websocket.update({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
return this.prisma.websocket.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async get(instanceName: string): Promise<wa.LocalWebsocket> {
|
||||
if (!this.integrationEnabled) return;
|
||||
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.websocket.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
@ -118,7 +66,9 @@ export class WebsocketController extends EventController implements EventControl
|
||||
sender,
|
||||
apiKey,
|
||||
}: EmitData): Promise<void> {
|
||||
if (!this.integrationEnabled) return;
|
||||
if (!this.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
const configEv = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
const logEnabled = configService.get<Log>('LOG').LEVEL.includes('WEBSOCKET');
|
||||
@ -144,13 +94,13 @@ export class WebsocketController extends EventController implements EventControl
|
||||
}
|
||||
|
||||
try {
|
||||
const instanceSocket = await this.get(instanceName);
|
||||
const instance = await this.get(instanceName);
|
||||
|
||||
if (!instanceSocket?.enabled) {
|
||||
if (!instance?.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(instanceSocket?.events) && instanceSocket?.events.includes(configEv)) {
|
||||
if (Array.isArray(instance?.events) && instance?.events.includes(configEv)) {
|
||||
this.socket.of(`/${instanceName}`).emit(event, message);
|
||||
|
||||
if (logEnabled) {
|
@ -1,9 +1,9 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { WebsocketDto } from '@api/integrations/event/websocket/dto/websocket.dto';
|
||||
import { websocketController } from '@api/server.module';
|
||||
import { EventDto } from '@api/integrations/event/event.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { instanceSchema, websocketSchema } from '@validate/validate.schema';
|
||||
import { eventManager } from '@api/server.module';
|
||||
import { eventSchema, instanceSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
export class WebsocketRouter extends RouterBroker {
|
||||
@ -11,11 +11,11 @@ export class WebsocketRouter extends RouterBroker {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<WebsocketDto>({
|
||||
const response = await this.dataValidate<EventDto>({
|
||||
request: req,
|
||||
schema: websocketSchema,
|
||||
ClassRef: WebsocketDto,
|
||||
execute: (instance, data) => websocketController.set(instance.instanceName, data),
|
||||
schema: eventSchema,
|
||||
ClassRef: EventDto,
|
||||
execute: (instance, data) => eventManager.websocket.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
@ -25,7 +25,7 @@ export class WebsocketRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => websocketController.get(instance.instanceName),
|
||||
execute: (instance) => eventManager.websocket.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
@ -1,11 +1,6 @@
|
||||
import { ChatwootInstanceMixin } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto';
|
||||
import { RabbitMQInstanceMixin } from '@api/integrations/event/rabbitmq/dto/rabbitmq.dto';
|
||||
import { SQSInstanceMixin } from '@api/integrations/event/sqs/dto/sqs.dto';
|
||||
import { WebhookInstanceMixin } from '@api/integrations/event/webhook/dto/webhook.dto';
|
||||
import { WebsocketInstanceMixin } from '@api/integrations/event/websocket/dto/websocket.dto';
|
||||
import { EventInstanceMixin } from '@api/integrations/event/event.dto';
|
||||
|
||||
export type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export class IntegrationDto extends WebhookInstanceMixin(
|
||||
WebsocketInstanceMixin(RabbitMQInstanceMixin(SQSInstanceMixin(ChatwootInstanceMixin(class {})))),
|
||||
) {}
|
||||
export class IntegrationDto extends EventInstanceMixin(ChatwootInstanceMixin(class {})) {}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { authGuard } from '@api/guards/auth.guard';
|
||||
import { instanceExistsGuard, instanceLoggedGuard } from '@api/guards/instance.guard';
|
||||
import Telemetry from '@api/guards/telemetry.guard';
|
||||
import { ChannelRouter } from '@api/integrations/channel/channel.router';
|
||||
import { ChatbotRouter } from '@api/integrations/chatbot/chatbot.router';
|
||||
import { EventRouter } from '@api/integrations/event/event.router';
|
||||
import { StorageRouter } from '@api/integrations/storage/storage.router';
|
||||
@ -84,6 +85,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 EventRouter(configService, ...guards).router)
|
||||
.use('', new ChatbotRouter(...guards).router)
|
||||
.use('', new StorageRouter(...guards).router);
|
||||
|
@ -12,6 +12,8 @@ import { SendMessageController } from './controllers/sendMessage.controller';
|
||||
import { SettingsController } from './controllers/settings.controller';
|
||||
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 { ChatbotController } from './integrations/chatbot/chatbot.controller';
|
||||
import { ChatwootController } from './integrations/chatbot/chatwoot/controllers/chatwoot.controller';
|
||||
import { ChatwootService } from './integrations/chatbot/chatwoot/services/chatwoot.service';
|
||||
@ -25,11 +27,7 @@ import { OpenaiController } from './integrations/chatbot/openai/controllers/open
|
||||
import { OpenaiService } from './integrations/chatbot/openai/services/openai.service';
|
||||
import { TypebotController } from './integrations/chatbot/typebot/controllers/typebot.controller';
|
||||
import { TypebotService } from './integrations/chatbot/typebot/services/typebot.service';
|
||||
import { EventController } from './integrations/event/event.controller';
|
||||
import { RabbitmqController } from './integrations/event/rabbitmq/controllers/rabbitmq.controller';
|
||||
import { SqsController } from './integrations/event/sqs/controllers/sqs.controller';
|
||||
import { WebhookController } from './integrations/event/webhook/controllers/webhook.controller';
|
||||
import { WebsocketController } from './integrations/event/websocket/controllers/websocket.controller';
|
||||
import { EventManager } from './integrations/event/event.manager';
|
||||
import { S3Controller } from './integrations/storage/s3/controllers/s3.controller';
|
||||
import { S3Service } from './integrations/storage/s3/services/s3.service';
|
||||
import { ProviderFiles } from './provider/sessions';
|
||||
@ -100,15 +98,13 @@ export const chatController = new ChatController(waMonitor);
|
||||
export const groupController = new GroupController(waMonitor);
|
||||
export const labelController = new LabelController(waMonitor);
|
||||
|
||||
export const eventController = new EventController(prismaRepository, waMonitor);
|
||||
export const eventManager = new EventManager(prismaRepository, waMonitor);
|
||||
export const chatbotController = new ChatbotController(prismaRepository, waMonitor);
|
||||
export const channelController = new ChannelController();
|
||||
export const channelController = new ChannelController(prismaRepository, waMonitor);
|
||||
|
||||
// events
|
||||
export const websocketController = new WebsocketController(prismaRepository, waMonitor);
|
||||
export const rabbitmqController = new RabbitmqController(prismaRepository, waMonitor);
|
||||
export const sqsController = new SqsController(prismaRepository, waMonitor);
|
||||
export const webhookController = new WebhookController(prismaRepository, waMonitor);
|
||||
// channels
|
||||
export const evolutionController = new EvolutionController(prismaRepository, waMonitor);
|
||||
export const metaController = new MetaController(prismaRepository, waMonitor);
|
||||
|
||||
// chatbots
|
||||
const typebotService = new TypebotService(waMonitor, configService, prismaRepository);
|
||||
|
@ -7,7 +7,7 @@ import { DifyService } from '@api/integrations/chatbot/dify/services/dify.servic
|
||||
import { OpenaiService } from '@api/integrations/chatbot/openai/services/openai.service';
|
||||
import { TypebotService } from '@api/integrations/chatbot/typebot/services/typebot.service';
|
||||
import { PrismaRepository, Query } from '@api/repository/repository.service';
|
||||
import { eventController, waMonitor } from '@api/server.module';
|
||||
import { eventManager, waMonitor } from '@api/server.module';
|
||||
import { Events, wa } from '@api/types/wa.types';
|
||||
import { Auth, Chatwoot, ConfigService, HttpServer } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
@ -58,7 +58,7 @@ export class ChannelStartupService {
|
||||
this.instance.token = instance.token;
|
||||
this.instance.businessId = instance.businessId;
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot.enabled) {
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.STATUS_INSTANCE,
|
||||
{ instanceName: this.instance.name },
|
||||
@ -241,7 +241,7 @@ export class ChannelStartupService {
|
||||
instanceId: this.instanceId,
|
||||
},
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
accountId: data.accountId,
|
||||
token: data.token,
|
||||
url: data.url,
|
||||
@ -269,7 +269,7 @@ export class ChannelStartupService {
|
||||
|
||||
await this.prismaRepository.chatwoot.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
accountId: data.accountId,
|
||||
token: data.token,
|
||||
url: data.url,
|
||||
@ -312,7 +312,7 @@ export class ChannelStartupService {
|
||||
const ignoreJidsArray = Array.isArray(data.ignoreJids) ? data.ignoreJids.map((event) => String(event)) : [];
|
||||
|
||||
return {
|
||||
enabled: data.enabled,
|
||||
enabled: data?.enabled,
|
||||
accountId: data.accountId,
|
||||
token: data.token,
|
||||
url: data.url,
|
||||
@ -332,7 +332,7 @@ export class ChannelStartupService {
|
||||
}
|
||||
|
||||
public clearCacheChatwoot() {
|
||||
if (this.localChatwoot.enabled) {
|
||||
if (this.localChatwoot?.enabled) {
|
||||
this.chatwootService.getCache()?.deleteAll(this.instanceName);
|
||||
}
|
||||
}
|
||||
@ -353,9 +353,20 @@ export class ChannelStartupService {
|
||||
}
|
||||
|
||||
public async setProxy(data: ProxyDto) {
|
||||
await this.prismaRepository.proxy.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
await this.prismaRepository.proxy.upsert({
|
||||
where: {
|
||||
instanceId: this.instanceId,
|
||||
},
|
||||
update: {
|
||||
enabled: data?.enabled,
|
||||
host: data.host,
|
||||
port: data.port,
|
||||
protocol: data.protocol,
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
},
|
||||
create: {
|
||||
enabled: data?.enabled,
|
||||
host: data.host,
|
||||
port: data.port,
|
||||
protocol: data.protocol,
|
||||
@ -392,7 +403,7 @@ export class ChannelStartupService {
|
||||
|
||||
const instanceApikey = this.token || 'Apikey not found';
|
||||
|
||||
await eventController.emit({
|
||||
await eventManager.emit({
|
||||
instanceName: this.instance.name,
|
||||
origin: ChannelStartupService.name,
|
||||
event,
|
||||
|
@ -207,6 +207,14 @@ export class WAMonitoringService {
|
||||
}
|
||||
}
|
||||
|
||||
public deleteInstance(instanceName: string) {
|
||||
try {
|
||||
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async setInstance(instanceData: InstanceDto) {
|
||||
const instance = channelController.init(instanceData, {
|
||||
configService: this.configService,
|
||||
|
@ -60,14 +60,6 @@ export declare namespace wa {
|
||||
businessId?: string;
|
||||
};
|
||||
|
||||
export type LocalWebHook = {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
events?: JsonValue;
|
||||
webhookByEvents?: boolean;
|
||||
webhookBase64?: boolean;
|
||||
};
|
||||
|
||||
export type LocalChatwoot = {
|
||||
enabled?: boolean;
|
||||
accountId?: string;
|
||||
@ -95,19 +87,15 @@ export declare namespace wa {
|
||||
syncFullHistory?: boolean;
|
||||
};
|
||||
|
||||
export type LocalWebsocket = {
|
||||
export type LocalEvent = {
|
||||
enabled?: boolean;
|
||||
events?: JsonValue;
|
||||
};
|
||||
|
||||
export type LocalRabbitmq = {
|
||||
enabled?: boolean;
|
||||
events?: JsonValue;
|
||||
};
|
||||
|
||||
export type LocalSqs = {
|
||||
enabled?: boolean;
|
||||
events?: JsonValue;
|
||||
export type LocalWebHook = LocalEvent & {
|
||||
url?: string;
|
||||
webhookByEvents?: boolean;
|
||||
webhookBase64?: boolean;
|
||||
};
|
||||
|
||||
type Session = {
|
||||
|
@ -427,7 +427,7 @@ export class ConfigService {
|
||||
ENABLED: process.env?.CHATWOOT_ENABLED === 'true',
|
||||
MESSAGE_DELETE: process.env.CHATWOOT_MESSAGE_DELETE === 'true',
|
||||
MESSAGE_READ: process.env.CHATWOOT_MESSAGE_READ === 'true',
|
||||
BOT_CONTACT: process.env.CHATWOOT_BOT_CONTACT === 'true',
|
||||
BOT_CONTACT: !process.env.CHATWOOT_BOT_CONTACT || process.env.CHATWOOT_BOT_CONTACT === 'true',
|
||||
IMPORT: {
|
||||
DATABASE: {
|
||||
CONNECTION: {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ProviderFiles } from '@api/provider/sessions';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { HttpStatus, router } from '@api/routes/index.router';
|
||||
import { eventController, waMonitor } from '@api/server.module';
|
||||
import { eventManager, waMonitor } from '@api/server.module';
|
||||
import { Auth, configService, Cors, HttpServer, ProviderSession, Webhook } from '@config/env.config';
|
||||
import { onUnexpectedError } from '@config/error.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
@ -139,7 +139,7 @@ async function bootstrap() {
|
||||
ServerUP.app = app;
|
||||
const server = ServerUP[httpServer.TYPE];
|
||||
|
||||
eventController.init(server);
|
||||
eventManager.init(server);
|
||||
|
||||
server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user