chore: Add ignoreJid feature to typebot and improve comparison

Adds a new feature to the typebot integration that allows ignoring specific remote JIDs. Also improves the comparison functionality. The main changes are in the typebot controller, DTO, route, and service. Additionally, the WhatsApp Business Service and event configuration files have been updated.

The main files modified are:
- typebot.controller.ts
- typebot.dto.ts
- typebot.router.ts
- typebot.service.ts
- typebot.schema.ts
- whatsapp.business.service.ts
- event.config.ts
This commit is contained in:
Davidson Gomes 2024-07-13 13:55:42 -03:00
parent e3a97d0071
commit f7a731a193
7 changed files with 154 additions and 14 deletions

View File

@ -1,7 +1,7 @@
import { configService, Typebot } from '../../../../config/env.config';
import { BadRequestException } from '../../../../exceptions';
import { InstanceDto } from '../../../dto/instance.dto';
import { TypebotDto } from '../dto/typebot.dto';
import { TypebotDto, TypebotIgnoreJidDto } from '../dto/typebot.dto';
import { TypebotService } from '../services/typebot.service';
export class TypebotController {
@ -66,4 +66,10 @@ export class TypebotController {
return this.typebotService.fetchSessions(instance, typebotId);
}
public async ignoreJid(instance: InstanceDto, data: TypebotIgnoreJidDto) {
if (!configService.get<Typebot>('TYPEBOT').ENABLED) throw new BadRequestException('Typebot is disabled');
return this.typebotService.ignoreJid(instance, data);
}
}

View File

@ -46,3 +46,8 @@ export class TypebotSettingDto {
typebotIdFallback?: string;
ignoreJids?: any;
}
export class TypebotIgnoreJidDto {
remoteJid?: string;
action?: string;
}

View File

@ -2,6 +2,7 @@ import { RequestHandler, Router } from 'express';
import {
instanceSchema,
typebotIgnoreJidSchema,
typebotSchema,
typebotSettingSchema,
typebotStartSchema,
@ -11,7 +12,7 @@ import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { typebotController } from '../../../server.module';
import { TypebotDto, TypebotSettingDto } from '../dto/typebot.dto';
import { TypebotDto, TypebotIgnoreJidDto, TypebotSettingDto } from '../dto/typebot.dto';
export class TypebotRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) {
@ -115,6 +116,16 @@ export class TypebotRouter extends RouterBroker {
execute: (instance) => typebotController.fetchSessions(instance, req.params.typebotId),
});
res.status(HttpStatus.OK).json(response);
})
.post(this.routerPath('ignoreJid'), ...guards, async (req, res) => {
const response = await this.dataValidate<TypebotIgnoreJidDto>({
request: req,
schema: typebotIgnoreJidSchema,
ClassRef: TypebotIgnoreJidDto,
execute: (instance, data) => typebotController.ignoreJid(instance, data),
});
res.status(HttpStatus.OK).json(response);
});
}

View File

@ -7,7 +7,7 @@ import { InstanceDto } from '../../../dto/instance.dto';
import { PrismaRepository } from '../../../repository/repository.service';
import { WAMonitoringService } from '../../../services/monitor.service';
import { Events } from '../../../types/wa.types';
import { TypebotDto } from '../dto/typebot.dto';
import { TypebotDto, TypebotIgnoreJidDto } from '../dto/typebot.dto';
export class TypebotService {
constructor(
@ -472,6 +472,54 @@ export class TypebotService {
}
}
public async ignoreJid(instance: InstanceDto, data: TypebotIgnoreJidDto) {
try {
const instanceId = await this.prismaRepository.instance
.findFirst({
where: {
name: instance.instanceName,
},
})
.then((instance) => instance.id);
const settings = await this.prismaRepository.typebotSetting.findFirst({
where: {
instanceId: instanceId,
},
});
if (!settings) {
throw new Error('Settings not found');
}
let ignoreJids: any = settings?.ignoreJids || [];
if (data.action === 'add') {
if (ignoreJids.includes(data.remoteJid)) return { ignoreJids: ignoreJids };
ignoreJids.push(data.remoteJid);
} else {
ignoreJids = ignoreJids.filter((jid) => jid !== data.remoteJid);
}
const updateSettings = await this.prismaRepository.typebotSetting.update({
where: {
id: settings.id,
},
data: {
ignoreJids: ignoreJids,
},
});
return {
ignoreJids: updateSettings.ignoreJids,
};
} catch (error) {
this.logger.error(error);
throw new Error('Error setting default settings');
}
}
public async fetchSessions(instance: InstanceDto, typebotId?: string, remoteJid?: string) {
try {
const instanceId = await this.prismaRepository.instance
@ -581,6 +629,42 @@ export class TypebotService {
let stopBotFromMe = data?.typebot?.stopBotFromMe;
let keepOpen = data?.typebot?.keepOpen;
const defaultSettingCheck = await this.prismaRepository.typebotSetting.findFirst({
where: {
instanceId: instance.instanceId,
},
});
if (defaultSettingCheck?.ignoreJids) {
const ignoreJids: any = defaultSettingCheck.ignoreJids;
let ignoreGroups = false;
let ignoreContacts = false;
if (ignoreJids.includes('@g.us')) {
ignoreGroups = true;
}
if (ignoreJids.includes('@s.whatsapp.net')) {
ignoreContacts = true;
}
if (ignoreGroups && remoteJid.includes('@g.us')) {
this.logger.warn('Ignoring message from group: ' + remoteJid);
return;
}
if (ignoreContacts && remoteJid.includes('@s.whatsapp.net')) {
this.logger.warn('Ignoring message from contact: ' + remoteJid);
return;
}
if (ignoreJids.includes(remoteJid)) {
this.logger.warn('Ignoring message from jid: ' + remoteJid);
return;
}
}
const findTypebot = await this.prismaRepository.typebot.findFirst({
where: {
url: url,
@ -601,12 +685,6 @@ export class TypebotService {
!stopBotFromMe ||
!keepOpen
) {
const defaultSettingCheck = await this.prismaRepository.typebotSetting.findFirst({
where: {
instanceId: instance.instanceId,
},
});
if (!expire) expire = defaultSettingCheck?.expire || 0;
if (!keywordFinish) keywordFinish = defaultSettingCheck?.keywordFinish || '#SAIR';
if (!delayMessage) delayMessage = defaultSettingCheck?.delayMessage || 1000;
@ -1245,9 +1323,30 @@ export class TypebotService {
},
});
if (settings.ignoreJids) {
if (settings?.ignoreJids) {
const ignoreJids: any = settings.ignoreJids;
let ignoreGroups = false;
let ignoreContacts = false;
if (ignoreJids.includes('@g.us')) {
ignoreGroups = true;
}
if (ignoreJids.includes('@s.whatsapp.net')) {
ignoreContacts = true;
}
if (ignoreGroups && remoteJid.endsWith('@g.us')) {
this.logger.warn('Ignoring message from group: ' + remoteJid);
return;
}
if (ignoreContacts && remoteJid.endsWith('@s.whatsapp.net')) {
this.logger.warn('Ignoring message from contact: ' + remoteJid);
return;
}
if (ignoreJids.includes(remoteJid)) {
this.logger.warn('Ignoring message from jid: ' + remoteJid);
return;

View File

@ -83,3 +83,14 @@ export const typebotSettingSchema: JSONSchema7 = {
required: ['expire', 'keywordFinish', 'delayMessage', 'unknownMessage', 'listeningFromMe', 'stopBotFromMe'],
...isNotEmpty('expire', 'keywordFinish', 'delayMessage', 'unknownMessage', 'listeningFromMe', 'stopBotFromMe'),
};
export const typebotIgnoreJidSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
remoteJid: { type: 'string' },
action: { type: 'string', enum: ['add', 'remove'] },
},
required: ['remoteJid', 'action'],
...isNotEmpty('remoteJid', 'action'),
};

View File

@ -2,7 +2,7 @@ import axios from 'axios';
import { arrayUnique, isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2';
import FormData from 'form-data';
import fs from 'fs/promises';
import { createReadStream } from 'fs';
import { getMIMEType } from 'node-mime-types';
import { Chatwoot, ConfigService, Database, Typebot, WaBusiness } from '../../../config/env.config';
@ -885,12 +885,19 @@ export class BusinessStartupService extends ChannelStartupService {
private async getIdMedia(mediaMessage: any) {
const formData = new FormData();
const fileBuffer = await fs.readFile(mediaMessage.media);
const fileStream = createReadStream(mediaMessage.media);
const fileBlob = new Blob([fileBuffer], { type: mediaMessage.mimetype });
formData.append('file', fileBlob);
formData.append('file', fileStream, { filename: 'media', contentType: mediaMessage.mimetype });
formData.append('typeFile', mediaMessage.mimetype);
formData.append('messaging_product', 'whatsapp');
// const fileBuffer = await fs.readFile(mediaMessage.media);
// const fileBlob = new Blob([fileBuffer], { type: mediaMessage.mimetype });
// formData.append('file', fileBlob);
// formData.append('typeFile', mediaMessage.mimetype);
// formData.append('messaging_product', 'whatsapp');
const headers = { Authorization: `Bearer ${this.token}` };
const res = await axios.post(
process.env.API_URL + '/' + process.env.VERSION + '/' + this.number + '/media',

View File

@ -4,4 +4,5 @@ export const eventEmitter = new EventEmitter2({
delimiter: '.',
newListener: false,
ignoreErrors: false,
maxListeners: 50,
});