diff --git a/src/api/integrations/typebot/controllers/typebot.controller.ts b/src/api/integrations/typebot/controllers/typebot.controller.ts index 707545ac..a73771c1 100644 --- a/src/api/integrations/typebot/controllers/typebot.controller.ts +++ b/src/api/integrations/typebot/controllers/typebot.controller.ts @@ -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').ENABLED) throw new BadRequestException('Typebot is disabled'); + + return this.typebotService.ignoreJid(instance, data); + } } diff --git a/src/api/integrations/typebot/dto/typebot.dto.ts b/src/api/integrations/typebot/dto/typebot.dto.ts index c37a1e24..1682e90b 100644 --- a/src/api/integrations/typebot/dto/typebot.dto.ts +++ b/src/api/integrations/typebot/dto/typebot.dto.ts @@ -46,3 +46,8 @@ export class TypebotSettingDto { typebotIdFallback?: string; ignoreJids?: any; } + +export class TypebotIgnoreJidDto { + remoteJid?: string; + action?: string; +} diff --git a/src/api/integrations/typebot/routes/typebot.router.ts b/src/api/integrations/typebot/routes/typebot.router.ts index 9d9042f6..842d76dd 100644 --- a/src/api/integrations/typebot/routes/typebot.router.ts +++ b/src/api/integrations/typebot/routes/typebot.router.ts @@ -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({ + request: req, + schema: typebotIgnoreJidSchema, + ClassRef: TypebotIgnoreJidDto, + execute: (instance, data) => typebotController.ignoreJid(instance, data), + }); + res.status(HttpStatus.OK).json(response); }); } diff --git a/src/api/integrations/typebot/services/typebot.service.ts b/src/api/integrations/typebot/services/typebot.service.ts index caf48d8a..955264a5 100644 --- a/src/api/integrations/typebot/services/typebot.service.ts +++ b/src/api/integrations/typebot/services/typebot.service.ts @@ -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; diff --git a/src/api/integrations/typebot/validate/typebot.schema.ts b/src/api/integrations/typebot/validate/typebot.schema.ts index 5131dbcc..838d2da5 100644 --- a/src/api/integrations/typebot/validate/typebot.schema.ts +++ b/src/api/integrations/typebot/validate/typebot.schema.ts @@ -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'), +}; diff --git a/src/api/services/channels/whatsapp.business.service.ts b/src/api/services/channels/whatsapp.business.service.ts index da61b72a..d835361b 100644 --- a/src/api/services/channels/whatsapp.business.service.ts +++ b/src/api/services/channels/whatsapp.business.service.ts @@ -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', diff --git a/src/config/event.config.ts b/src/config/event.config.ts index 8451ffdf..ff9b92f3 100644 --- a/src/config/event.config.ts +++ b/src/config/event.config.ts @@ -4,4 +4,5 @@ export const eventEmitter = new EventEmitter2({ delimiter: '.', newListener: false, ignoreErrors: false, + maxListeners: 50, });