diff --git a/CHANGELOG.md b/CHANGELOG.md index 960f2f3b..c4c4363a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# 1.7.2 (2024-04-12 17:31) + +### Feature + +* Mobile connection via sms (test) + +### Fixed + +* Adjusts in redis +* Send global event in websocket +* Adjusts in proxy +* Fix audio encoding +* Fix conversation read on chatwoot version 3.7 +* Fix when receiving/sending messages from whatsapp desktop with ephemeral messages enabled +* Changed returned sessions on typebot status change +* Reorganization of files and folders + # 1.7.1 (2024-04-03 10:19) ### Fixed diff --git a/Docker/.env.example b/Docker/.env.example index f55e3d65..865ef877 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -53,6 +53,7 @@ RABBITMQ_EXCHANGE_NAME=evolution_exchange RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672 WEBSOCKET_ENABLED=false +WEBSOCKET_GLOBAL_EVENTS=false WA_BUSINESS_TOKEN_WEBHOOK=evolution WA_BUSINESS_URL=https://graph.facebook.com diff --git a/Dockerfile b/Dockerfile index 3f24af28..b4f20c80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:20.7.0-alpine AS builder -LABEL version="1.7.1" description="Api to control whatsapp features through http requests." +LABEL version="1.7.2" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL contact="contato@agenciadgcode.com" @@ -68,6 +68,7 @@ ENV RABBITMQ_EXCHANGE_NAME=evolution_exchange ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672 ENV WEBSOCKET_ENABLED=false +ENV WEBSOCKET_GLOBAL_EVENTS=false ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution ENV WA_BUSINESS_URL=https://graph.facebook.com diff --git a/package.json b/package.json index 69ea3b64..8b31f902 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "1.7.1", + "version": "1.7.2", "description": "Rest api for communication with WhatsApp", "main": "./dist/src/main.js", "scripts": { @@ -46,7 +46,7 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@sentry/node": "^7.59.2", - "@whiskeysockets/baileys": "6.6.0", + "@whiskeysockets/baileys": "github:AtendAI/Baileys", "amqplib": "^0.10.3", "aws-sdk": "^2.1499.0", "axios": "^1.6.5", @@ -60,6 +60,7 @@ "exiftool-vendored": "^22.0.0", "express": "^4.18.2", "express-async-errors": "^3.1.1", + "fluent-ffmpeg": "^2.1.2", "form-data": "^4.0.0", "hbs": "^4.2.0", "https-proxy-agent": "^7.0.2", diff --git a/src/whatsapp/abstract/abstract.cache.ts b/src/api/abstract/abstract.cache.ts similarity index 100% rename from src/whatsapp/abstract/abstract.cache.ts rename to src/api/abstract/abstract.cache.ts diff --git a/src/whatsapp/abstract/abstract.repository.ts b/src/api/abstract/abstract.repository.ts similarity index 100% rename from src/whatsapp/abstract/abstract.repository.ts rename to src/api/abstract/abstract.repository.ts diff --git a/src/whatsapp/abstract/abstract.router.ts b/src/api/abstract/abstract.router.ts similarity index 100% rename from src/whatsapp/abstract/abstract.router.ts rename to src/api/abstract/abstract.router.ts diff --git a/src/whatsapp/controllers/chat.controller.ts b/src/api/controllers/chat.controller.ts similarity index 100% rename from src/whatsapp/controllers/chat.controller.ts rename to src/api/controllers/chat.controller.ts diff --git a/src/whatsapp/controllers/group.controller.ts b/src/api/controllers/group.controller.ts similarity index 100% rename from src/whatsapp/controllers/group.controller.ts rename to src/api/controllers/group.controller.ts diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts similarity index 92% rename from src/whatsapp/controllers/instance.controller.ts rename to src/api/controllers/instance.controller.ts index 91103e33..563bc5fd 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -8,21 +8,22 @@ import { Logger } from '../../config/logger.config'; import { BadRequestException, InternalServerErrorException } from '../../exceptions'; import { RedisCache } from '../../libs/redis.client'; import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; +import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service'; +import { RabbitmqService } from '../integrations/rabbitmq/services/rabbitmq.service'; +import { SqsService } from '../integrations/sqs/services/sqs.service'; +import { TypebotService } from '../integrations/typebot/services/typebot.service'; +import { WebsocketService } from '../integrations/websocket/services/websocket.service'; import { RepositoryBroker } from '../repository/repository.manager'; import { AuthService, OldToken } from '../services/auth.service'; import { CacheService } from '../services/cache.service'; -import { ChatwootService } from '../services/chatwoot.service'; import { IntegrationService } from '../services/integration.service'; import { WAMonitoringService } from '../services/monitor.service'; -import { RabbitmqService } from '../services/rabbitmq.service'; import { SettingsService } from '../services/settings.service'; -import { SqsService } from '../services/sqs.service'; -import { TypebotService } from '../services/typebot.service'; import { WebhookService } from '../services/webhook.service'; -import { WebsocketService } from '../services/websocket.service'; -import { BaileysStartupService } from '../services/whatsapp.baileys.service'; -import { BusinessStartupService } from '../services/whatsapp.business.service'; +import { BaileysStartupService } from '../services/whatsapp/whatsapp.baileys.service'; +import { BusinessStartupService } from '../services/whatsapp/whatsapp.business.service'; import { Events, Integration, wa } from '../types/wa.types'; +import { ProxyController } from './proxy.controller'; export class InstanceController { constructor( @@ -39,6 +40,7 @@ export class InstanceController { private readonly sqsService: SqsService, private readonly typebotService: TypebotService, private readonly integrationService: IntegrationService, + private readonly proxyService: ProxyController, private readonly cache: RedisCache, private readonly chatwootCache: CacheService, ) {} @@ -53,6 +55,7 @@ export class InstanceController { events, qrcode, number, + mobile, integration, token, chatwoot_account_id, @@ -84,6 +87,7 @@ export class InstanceController { typebot_delay_message, typebot_unknown_message, typebot_listening_from_me, + proxy, }: InstanceDto) { try { this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); @@ -115,7 +119,7 @@ export class InstanceController { ); } - await this.waMonitor.saveInstance({ integration, instanceName, token, number }); + await this.waMonitor.saveInstance({ integration, instanceName, token, number, mobile }); instance.instanceName = instanceName; @@ -345,6 +349,18 @@ export class InstanceController { } } + if (proxy) { + const testProxy = await this.proxyService.testProxy(proxy); + if (!testProxy) { + throw new BadRequestException('Invalid proxy'); + } + + await this.proxyService.createProxy(instance, { + enabled: true, + proxy, + }); + } + if (typebot_url) { try { if (!isURL(typebot_url, { require_tld: false })) { @@ -405,7 +421,7 @@ export class InstanceController { if (qrcode) { this.logger.verbose('creating qrcode'); - await instance.connectToWhatsapp(number); + await instance.connectToWhatsapp(number, mobile); await delay(5000); getQrcode = instance.qrCode; } @@ -569,7 +585,7 @@ export class InstanceController { } } - public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) { + public async connectToWhatsapp({ instanceName, number = null, mobile = null }: InstanceDto) { try { this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance'); @@ -592,7 +608,7 @@ export class InstanceController { if (state == 'close') { this.logger.verbose('connecting'); - await instance.connectToWhatsapp(number); + await instance.connectToWhatsapp(number, mobile); await delay(5000); return instance.qrCode; @@ -633,6 +649,20 @@ export class InstanceController { } } + public async registerMobileCode({ instanceName }: InstanceDto, { mobileCode }: any) { + try { + this.logger.verbose('requested registerMobileCode from ' + instanceName + ' instance'); + + const instance = this.waMonitor.waInstances[instanceName]; + + console.log('mobileCode', mobileCode); + await instance.receiveMobileCode(mobileCode); + return { status: 'SUCCESS', error: false, response: { message: 'Mobile code registered' } }; + } catch (error) { + this.logger.error(error); + } + } + public async connectionState({ instanceName }: InstanceDto) { this.logger.verbose('requested connectionState from ' + instanceName + ' instance'); return { diff --git a/src/whatsapp/controllers/label.controller.ts b/src/api/controllers/label.controller.ts similarity index 100% rename from src/whatsapp/controllers/label.controller.ts rename to src/api/controllers/label.controller.ts diff --git a/src/whatsapp/controllers/proxy.controller.ts b/src/api/controllers/proxy.controller.ts similarity index 97% rename from src/whatsapp/controllers/proxy.controller.ts rename to src/api/controllers/proxy.controller.ts index d826d8c4..7b34a9d9 100644 --- a/src/whatsapp/controllers/proxy.controller.ts +++ b/src/api/controllers/proxy.controller.ts @@ -46,7 +46,7 @@ export class ProxyController { return this.proxyService.find(instance); } - private async testProxy(proxy: ProxyDto['proxy']) { + public async testProxy(proxy: ProxyDto['proxy']) { logger.verbose('requested testProxy'); try { const serverIp = await axios.get('https://icanhazip.com/'); diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/api/controllers/sendMessage.controller.ts similarity index 100% rename from src/whatsapp/controllers/sendMessage.controller.ts rename to src/api/controllers/sendMessage.controller.ts diff --git a/src/whatsapp/controllers/settings.controller.ts b/src/api/controllers/settings.controller.ts similarity index 100% rename from src/whatsapp/controllers/settings.controller.ts rename to src/api/controllers/settings.controller.ts diff --git a/src/whatsapp/controllers/webhook.controller.ts b/src/api/controllers/webhook.controller.ts similarity index 100% rename from src/whatsapp/controllers/webhook.controller.ts rename to src/api/controllers/webhook.controller.ts diff --git a/src/whatsapp/dto/chat.dto.ts b/src/api/dto/chat.dto.ts similarity index 100% rename from src/whatsapp/dto/chat.dto.ts rename to src/api/dto/chat.dto.ts diff --git a/src/whatsapp/dto/group.dto.ts b/src/api/dto/group.dto.ts similarity index 100% rename from src/whatsapp/dto/group.dto.ts rename to src/api/dto/group.dto.ts diff --git a/src/whatsapp/dto/instance.dto.ts b/src/api/dto/instance.dto.ts similarity index 89% rename from src/whatsapp/dto/instance.dto.ts rename to src/api/dto/instance.dto.ts index eaf21aab..b703b9da 100644 --- a/src/whatsapp/dto/instance.dto.ts +++ b/src/api/dto/instance.dto.ts @@ -1,10 +1,13 @@ -import { WAPresence } from "@whiskeysockets/baileys"; +import { WAPresence } from '@whiskeysockets/baileys'; + +import { ProxyDto } from './proxy.dto'; export class InstanceDto { instanceName: string; instanceId?: string; qrcode?: boolean; number?: string; + mobile?: boolean; integration?: string; token?: string; webhook?: string; @@ -40,7 +43,7 @@ export class InstanceDto { typebot_delay_message?: number; typebot_unknown_message?: string; typebot_listening_from_me?: boolean; - proxy?: string; + proxy?: ProxyDto['proxy']; } export class SetPresenceDto { diff --git a/src/whatsapp/dto/integration.dto.ts b/src/api/dto/integration.dto.ts similarity index 100% rename from src/whatsapp/dto/integration.dto.ts rename to src/api/dto/integration.dto.ts diff --git a/src/whatsapp/dto/label.dto.ts b/src/api/dto/label.dto.ts similarity index 100% rename from src/whatsapp/dto/label.dto.ts rename to src/api/dto/label.dto.ts diff --git a/src/whatsapp/dto/proxy.dto.ts b/src/api/dto/proxy.dto.ts similarity index 100% rename from src/whatsapp/dto/proxy.dto.ts rename to src/api/dto/proxy.dto.ts diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/api/dto/sendMessage.dto.ts similarity index 100% rename from src/whatsapp/dto/sendMessage.dto.ts rename to src/api/dto/sendMessage.dto.ts diff --git a/src/whatsapp/dto/settings.dto.ts b/src/api/dto/settings.dto.ts similarity index 100% rename from src/whatsapp/dto/settings.dto.ts rename to src/api/dto/settings.dto.ts diff --git a/src/whatsapp/dto/webhook.dto.ts b/src/api/dto/webhook.dto.ts similarity index 100% rename from src/whatsapp/dto/webhook.dto.ts rename to src/api/dto/webhook.dto.ts diff --git a/src/whatsapp/guards/auth.guard.ts b/src/api/guards/auth.guard.ts similarity index 98% rename from src/whatsapp/guards/auth.guard.ts rename to src/api/guards/auth.guard.ts index a72ebfff..ccc73a58 100644 --- a/src/whatsapp/guards/auth.guard.ts +++ b/src/api/guards/auth.guard.ts @@ -7,8 +7,8 @@ import { Auth, configService } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { ForbiddenException, UnauthorizedException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; +import { repository } from '../server.module'; import { JwtPayload } from '../services/auth.service'; -import { repository } from '../whatsapp.module'; const logger = new Logger('GUARD'); diff --git a/src/whatsapp/guards/instance.guard.ts b/src/api/guards/instance.guard.ts similarity index 97% rename from src/whatsapp/guards/instance.guard.ts rename to src/api/guards/instance.guard.ts index 6b193411..2214bd43 100644 --- a/src/whatsapp/guards/instance.guard.ts +++ b/src/api/guards/instance.guard.ts @@ -12,7 +12,7 @@ import { } from '../../exceptions'; import { dbserver } from '../../libs/db.connect'; import { InstanceDto } from '../dto/instance.dto'; -import { cache, waMonitor } from '../whatsapp.module'; +import { cache, waMonitor } from '../server.module'; async function getInstance(instanceName: string) { try { diff --git a/src/whatsapp/controllers/chamaai.controller.ts b/src/api/integrations/chamaai/controllers/chamaai.controller.ts similarity index 88% rename from src/whatsapp/controllers/chamaai.controller.ts rename to src/api/integrations/chamaai/controllers/chamaai.controller.ts index e9cafb50..91b33cd1 100644 --- a/src/whatsapp/controllers/chamaai.controller.ts +++ b/src/api/integrations/chamaai/controllers/chamaai.controller.ts @@ -1,6 +1,6 @@ -import { Logger } from '../../config/logger.config'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; import { ChamaaiDto } from '../dto/chamaai.dto'; -import { InstanceDto } from '../dto/instance.dto'; import { ChamaaiService } from '../services/chamaai.service'; const logger = new Logger('ChamaaiController'); diff --git a/src/whatsapp/dto/chamaai.dto.ts b/src/api/integrations/chamaai/dto/chamaai.dto.ts similarity index 100% rename from src/whatsapp/dto/chamaai.dto.ts rename to src/api/integrations/chamaai/dto/chamaai.dto.ts diff --git a/src/whatsapp/models/chamaai.model.ts b/src/api/integrations/chamaai/models/chamaai.model.ts similarity index 91% rename from src/whatsapp/models/chamaai.model.ts rename to src/api/integrations/chamaai/models/chamaai.model.ts index d3d10aff..ef4f252b 100644 --- a/src/whatsapp/models/chamaai.model.ts +++ b/src/api/integrations/chamaai/models/chamaai.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { dbserver } from '../../../../libs/db.connect'; export class ChamaaiRaw { _id?: string; diff --git a/src/whatsapp/repository/chamaai.repository.ts b/src/api/integrations/chamaai/repository/chamaai.repository.ts similarity index 87% rename from src/whatsapp/repository/chamaai.repository.ts rename to src/api/integrations/chamaai/repository/chamaai.repository.ts index a2009f41..17ff8dcd 100644 --- a/src/whatsapp/repository/chamaai.repository.ts +++ b/src/api/integrations/chamaai/repository/chamaai.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ChamaaiRaw, IChamaaiModel } from '../models'; +import { ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { IInsert, Repository } from '../../../abstract/abstract.repository'; +import { ChamaaiRaw, IChamaaiModel } from '../../../models'; export class ChamaaiRepository extends Repository { constructor(private readonly chamaaiModel: IChamaaiModel, private readonly configService: ConfigService) { diff --git a/src/whatsapp/routers/chamaai.router.ts b/src/api/integrations/chamaai/routes/chamaai.router.ts similarity index 79% rename from src/whatsapp/routers/chamaai.router.ts rename to src/api/integrations/chamaai/routes/chamaai.router.ts index e8021306..3ade4b43 100644 --- a/src/whatsapp/routers/chamaai.router.ts +++ b/src/api/integrations/chamaai/routes/chamaai.router.ts @@ -1,12 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { Logger } from '../../config/logger.config'; -import { chamaaiSchema, instanceNameSchema } from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; +import { Logger } from '../../../../config/logger.config'; +import { chamaaiSchema, instanceNameSchema } from '../../../../validate/validate.schema'; +import { RouterBroker } from '../../../abstract/abstract.router'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { HttpStatus } from '../../../routes/index.router'; +import { chamaaiController } from '../../../server.module'; import { ChamaaiDto } from '../dto/chamaai.dto'; -import { InstanceDto } from '../dto/instance.dto'; -import { chamaaiController } from '../whatsapp.module'; -import { HttpStatus } from './index.router'; const logger = new Logger('ChamaaiRouter'); diff --git a/src/whatsapp/services/chamaai.service.ts b/src/api/integrations/chamaai/services/chamaai.service.ts similarity index 94% rename from src/whatsapp/services/chamaai.service.ts rename to src/api/integrations/chamaai/services/chamaai.service.ts index ad2a42ad..911732a8 100644 --- a/src/whatsapp/services/chamaai.service.ts +++ b/src/api/integrations/chamaai/services/chamaai.service.ts @@ -2,13 +2,13 @@ import axios from 'axios'; import { writeFileSync } from 'fs'; import path from 'path'; -import { ConfigService, HttpServer } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; +import { ConfigService, HttpServer } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { ChamaaiRaw } from '../../../models'; +import { WAMonitoringService } from '../../../services/monitor.service'; +import { Events } from '../../../types/wa.types'; import { ChamaaiDto } from '../dto/chamaai.dto'; -import { InstanceDto } from '../dto/instance.dto'; -import { ChamaaiRaw } from '../models'; -import { Events } from '../types/wa.types'; -import { WAMonitoringService } from './monitor.service'; export class ChamaaiService { constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {} diff --git a/src/api/integrations/chamaai/validate/chamaai.schema.ts b/src/api/integrations/chamaai/validate/chamaai.schema.ts new file mode 100644 index 00000000..0b80104f --- /dev/null +++ b/src/api/integrations/chamaai/validate/chamaai.schema.ts @@ -0,0 +1,35 @@ +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 chamaaiSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + enabled: { type: 'boolean', enum: [true, false] }, + url: { type: 'string' }, + token: { type: 'string' }, + waNumber: { type: 'string' }, + answerByAudio: { type: 'boolean', enum: [true, false] }, + }, + required: ['enabled', 'url', 'token', 'waNumber', 'answerByAudio'], + ...isNotEmpty('enabled', 'url', 'token', 'waNumber', 'answerByAudio'), +}; diff --git a/src/libs/cacheengine.ts b/src/api/integrations/chatwoot/cache/cacheengine.ts similarity index 81% rename from src/libs/cacheengine.ts rename to src/api/integrations/chatwoot/cache/cacheengine.ts index a22d7e68..a05e3dae 100644 --- a/src/libs/cacheengine.ts +++ b/src/api/integrations/chatwoot/cache/cacheengine.ts @@ -1,5 +1,5 @@ -import { CacheConf, ConfigService } from '../config/env.config'; -import { ICache } from '../whatsapp/abstract/abstract.cache'; +import { CacheConf, ConfigService } from '../../../../config/env.config'; +import { ICache } from '../../../abstract/abstract.cache'; import { LocalCache } from './localcache'; import { RedisCache } from './rediscache'; diff --git a/src/libs/localcache.ts b/src/api/integrations/chatwoot/cache/localcache.ts similarity index 89% rename from src/libs/localcache.ts rename to src/api/integrations/chatwoot/cache/localcache.ts index fe1f295f..7bf53e71 100644 --- a/src/libs/localcache.ts +++ b/src/api/integrations/chatwoot/cache/localcache.ts @@ -1,7 +1,7 @@ import NodeCache from 'node-cache'; -import { CacheConf, CacheConfLocal, ConfigService } from '../config/env.config'; -import { ICache } from '../whatsapp/abstract/abstract.cache'; +import { CacheConf, CacheConfLocal, ConfigService } from '../../../../config/env.config'; +import { ICache } from '../../../abstract/abstract.cache'; export class LocalCache implements ICache { private conf: CacheConfLocal; diff --git a/src/libs/rediscache.client.ts b/src/api/integrations/chatwoot/cache/rediscache.client.ts similarity index 90% rename from src/libs/rediscache.client.ts rename to src/api/integrations/chatwoot/cache/rediscache.client.ts index b3f8dead..0a3ef4fc 100644 --- a/src/libs/rediscache.client.ts +++ b/src/api/integrations/chatwoot/cache/rediscache.client.ts @@ -1,7 +1,7 @@ import { createClient, RedisClientType } from 'redis'; -import { CacheConf, CacheConfRedis, configService } from '../config/env.config'; -import { Logger } from '../config/logger.config'; +import { CacheConf, CacheConfRedis, configService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; class Redis { private logger = new Logger(Redis.name); diff --git a/src/libs/rediscache.ts b/src/api/integrations/chatwoot/cache/rediscache.ts similarity index 90% rename from src/libs/rediscache.ts rename to src/api/integrations/chatwoot/cache/rediscache.ts index cd0b1283..2a08fb3a 100644 --- a/src/libs/rediscache.ts +++ b/src/api/integrations/chatwoot/cache/rediscache.ts @@ -1,8 +1,8 @@ import { RedisClientType } from 'redis'; -import { CacheConf, CacheConfRedis, ConfigService } from '../config/env.config'; -import { Logger } from '../config/logger.config'; -import { ICache } from '../whatsapp/abstract/abstract.cache'; +import { CacheConf, CacheConfRedis, ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { ICache } from '../../../abstract/abstract.cache'; import { redisClient } from './rediscache.client'; export class RedisCache implements ICache { diff --git a/src/whatsapp/controllers/chatwoot.controller.ts b/src/api/integrations/chatwoot/controllers/chatwoot.controller.ts similarity index 86% rename from src/whatsapp/controllers/chatwoot.controller.ts rename to src/api/integrations/chatwoot/controllers/chatwoot.controller.ts index 2cccf280..03282ce5 100644 --- a/src/whatsapp/controllers/chatwoot.controller.ts +++ b/src/api/integrations/chatwoot/controllers/chatwoot.controller.ts @@ -1,15 +1,15 @@ import { isURL } from 'class-validator'; -import { ConfigService, HttpServer } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { BadRequestException } from '../../exceptions'; -import { CacheEngine } from '../../libs/cacheengine'; +import { ConfigService, HttpServer } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { BadRequestException } from '../../../../exceptions'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { RepositoryBroker } from '../../../repository/repository.manager'; +import { waMonitor } from '../../../server.module'; +import { CacheService } from '../../../services/cache.service'; +import { CacheEngine } from '../cache/cacheengine'; import { ChatwootDto } from '../dto/chatwoot.dto'; -import { InstanceDto } from '../dto/instance.dto'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { CacheService } from '../services/cache.service'; import { ChatwootService } from '../services/chatwoot.service'; -import { waMonitor } from '../whatsapp.module'; const logger = new Logger('ChatwootController'); diff --git a/src/whatsapp/dto/chatwoot.dto.ts b/src/api/integrations/chatwoot/dto/chatwoot.dto.ts similarity index 100% rename from src/whatsapp/dto/chatwoot.dto.ts rename to src/api/integrations/chatwoot/dto/chatwoot.dto.ts diff --git a/src/libs/postgres.client.ts b/src/api/integrations/chatwoot/libs/postgres.client.ts similarity index 88% rename from src/libs/postgres.client.ts rename to src/api/integrations/chatwoot/libs/postgres.client.ts index d1a68cdf..1211b075 100644 --- a/src/libs/postgres.client.ts +++ b/src/api/integrations/chatwoot/libs/postgres.client.ts @@ -1,7 +1,7 @@ import postgresql from 'pg'; -import { Chatwoot, configService } from '../config/env.config'; -import { Logger } from '../config/logger.config'; +import { Chatwoot, configService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; const { Pool } = postgresql; diff --git a/src/whatsapp/models/chatwoot.model.ts b/src/api/integrations/chatwoot/models/chatwoot.model.ts similarity index 95% rename from src/whatsapp/models/chatwoot.model.ts rename to src/api/integrations/chatwoot/models/chatwoot.model.ts index e16352e1..659b847c 100644 --- a/src/whatsapp/models/chatwoot.model.ts +++ b/src/api/integrations/chatwoot/models/chatwoot.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { dbserver } from '../../../../libs/db.connect'; export class ChatwootRaw { _id?: string; diff --git a/src/whatsapp/repository/chatwoot.repository.ts b/src/api/integrations/chatwoot/repository/chatwoot.repository.ts similarity index 87% rename from src/whatsapp/repository/chatwoot.repository.ts rename to src/api/integrations/chatwoot/repository/chatwoot.repository.ts index 47398d68..820f54af 100644 --- a/src/whatsapp/repository/chatwoot.repository.ts +++ b/src/api/integrations/chatwoot/repository/chatwoot.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ChatwootRaw, IChatwootModel } from '../models'; +import { ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { IInsert, Repository } from '../../../abstract/abstract.repository'; +import { ChatwootRaw, IChatwootModel } from '../../../models'; export class ChatwootRepository extends Repository { constructor(private readonly chatwootModel: IChatwootModel, private readonly configService: ConfigService) { diff --git a/src/whatsapp/routers/chatwoot.router.ts b/src/api/integrations/chatwoot/routes/chatwoot.router.ts similarity index 84% rename from src/whatsapp/routers/chatwoot.router.ts rename to src/api/integrations/chatwoot/routes/chatwoot.router.ts index c232e007..cc227163 100644 --- a/src/whatsapp/routers/chatwoot.router.ts +++ b/src/api/integrations/chatwoot/routes/chatwoot.router.ts @@ -1,12 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { Logger } from '../../config/logger.config'; -import { chatwootSchema, instanceNameSchema } from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; +import { Logger } from '../../../../config/logger.config'; +import { chatwootSchema, instanceNameSchema } from '../../../../validate/validate.schema'; +import { RouterBroker } from '../../../abstract/abstract.router'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { HttpStatus } from '../../../routes/index.router'; +import { chatwootController } from '../../../server.module'; import { ChatwootDto } from '../dto/chatwoot.dto'; -import { InstanceDto } from '../dto/instance.dto'; -import { chatwootController } from '../whatsapp.module'; -import { HttpStatus } from './index.router'; const logger = new Logger('ChatwootRouter'); diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/api/integrations/chatwoot/services/chatwoot.service.ts similarity index 97% rename from src/whatsapp/services/chatwoot.service.ts rename to src/api/integrations/chatwoot/services/chatwoot.service.ts index 43d8f5be..35fbb7af 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/api/integrations/chatwoot/services/chatwoot.service.ts @@ -1,4 +1,12 @@ -import ChatwootClient, { ChatwootAPIConfig, contact, conversation, generic_id, inbox } from '@figuro/chatwoot-sdk'; +import ChatwootClient, { + ChatwootAPIConfig, + contact, + contact_inboxes, + conversation, + conversation_show, + generic_id, + inbox, +} from '@figuro/chatwoot-sdk'; import { request as chatwootRequest } from '@figuro/chatwoot-sdk/dist/core/request'; import axios from 'axios'; import FormData from 'form-data'; @@ -7,18 +15,18 @@ import Jimp from 'jimp'; import mimeTypes from 'mime-types'; import path from 'path'; -import { Chatwoot, ConfigService, HttpServer } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { chatwootImport } from '../../utils/chatwoot-import-helper'; -import i18next from '../../utils/i18n'; -import { ICache } from '../abstract/abstract.cache'; +import { Chatwoot, ConfigService, HttpServer } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import i18next from '../../../../utils/i18n'; +import { ICache } from '../../../abstract/abstract.cache'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '../../../dto/sendMessage.dto'; +import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../models'; +import { RepositoryBroker } from '../../../repository/repository.manager'; +import { WAMonitoringService } from '../../../services/monitor.service'; +import { Events } from '../../../types/wa.types'; import { ChatwootDto } from '../dto/chatwoot.dto'; -import { InstanceDto } from '../dto/instance.dto'; -import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '../dto/sendMessage.dto'; -import { ChatwootRaw, ContactRaw, MessageRaw } from '../models'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { Events } from '../types/wa.types'; -import { WAMonitoringService } from './monitor.service'; +import { chatwootImport } from '../utils/chatwoot-import-helper'; export class ChatwootService { private readonly logger = new Logger(ChatwootService.name); @@ -1774,6 +1782,13 @@ export class ChatwootService { return; } + // fix when receiving/sending messages from whatsapp desktop with ephemeral messages enabled + if (body.message?.ephemeralMessage?.message) { + body.message = { + ...body.message?.ephemeralMessage?.message, + }; + } + this.logger.verbose('get conversation message'); // Whatsapp to Chatwoot @@ -2123,12 +2138,13 @@ export class ChatwootService { }; if (!sourceId && inbox) { - const contact = (await this.findContact( - instance, - this.getNumberFromRemoteJid(body.key.remoteJid), - )) as contact; - const contactInbox = contact?.contact_inboxes?.find((contactInbox) => contactInbox?.inbox?.id === inbox.id); - sourceId = contactInbox?.source_id; + const conversation = (await client.conversations.get({ + accountId: this.provider.account_id, + conversationId: conversationId, + })) as conversation_show & { + last_non_activity_message: { conversation: { contact_inbox: contact_inboxes } }; + }; + sourceId = conversation.last_non_activity_message?.conversation?.contact_inbox?.source_id; } if (sourceId && inbox?.inbox_identifier) { diff --git a/src/utils/chatwoot-import-helper.ts b/src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts similarity index 98% rename from src/utils/chatwoot-import-helper.ts rename to src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts index 3283683f..dd0bb23a 100644 --- a/src/utils/chatwoot-import-helper.ts +++ b/src/api/integrations/chatwoot/utils/chatwoot-import-helper.ts @@ -1,12 +1,12 @@ import { inbox } from '@figuro/chatwoot-sdk'; import { proto } from '@whiskeysockets/baileys'; -import { Chatwoot, configService } from '../config/env.config'; -import { Logger } from '../config/logger.config'; +import { InstanceDto } from '../../../../api/dto/instance.dto'; +import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../../api/models'; +import { Chatwoot, configService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; import { postgresClient } from '../libs/postgres.client'; -import { InstanceDto } from '../whatsapp/dto/instance.dto'; -import { ChatwootRaw, ContactRaw, MessageRaw } from '../whatsapp/models'; -import { ChatwootService } from '../whatsapp/services/chatwoot.service'; +import { ChatwootService } from '../services/chatwoot.service'; type ChatwootUser = { user_type: string; diff --git a/src/api/integrations/chatwoot/validate/chatwoot.schema.ts b/src/api/integrations/chatwoot/validate/chatwoot.schema.ts new file mode 100644 index 00000000..d2784daf --- /dev/null +++ b/src/api/integrations/chatwoot/validate/chatwoot.schema.ts @@ -0,0 +1,42 @@ +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 chatwootSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + enabled: { type: 'boolean', enum: [true, false] }, + account_id: { type: 'string' }, + token: { type: 'string' }, + url: { type: 'string' }, + sign_msg: { type: 'boolean', enum: [true, false] }, + sign_delimiter: { type: ['string', 'null'] }, + reopen_conversation: { type: 'boolean', enum: [true, false] }, + conversation_pending: { type: 'boolean', enum: [true, false] }, + auto_create: { type: 'boolean', enum: [true, false] }, + import_contacts: { type: 'boolean', enum: [true, false] }, + import_messages: { type: 'boolean', enum: [true, false] }, + days_limit_import_messages: { type: 'number' }, + }, + required: ['enabled', 'account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'], + ...isNotEmpty('account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'), +}; diff --git a/src/whatsapp/controllers/rabbitmq.controller.ts b/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts similarity index 93% rename from src/whatsapp/controllers/rabbitmq.controller.ts rename to src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts index 527c5006..0b10e954 100644 --- a/src/whatsapp/controllers/rabbitmq.controller.ts +++ b/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts @@ -1,5 +1,5 @@ -import { Logger } from '../../config/logger.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; import { RabbitmqDto } from '../dto/rabbitmq.dto'; import { RabbitmqService } from '../services/rabbitmq.service'; diff --git a/src/whatsapp/dto/rabbitmq.dto.ts b/src/api/integrations/rabbitmq/dto/rabbitmq.dto.ts similarity index 100% rename from src/whatsapp/dto/rabbitmq.dto.ts rename to src/api/integrations/rabbitmq/dto/rabbitmq.dto.ts diff --git a/src/libs/amqp.server.ts b/src/api/integrations/rabbitmq/libs/amqp.server.ts similarity index 94% rename from src/libs/amqp.server.ts rename to src/api/integrations/rabbitmq/libs/amqp.server.ts index fc95b33c..5628fac9 100644 --- a/src/libs/amqp.server.ts +++ b/src/api/integrations/rabbitmq/libs/amqp.server.ts @@ -1,7 +1,7 @@ import * as amqp from 'amqplib/callback_api'; -import { configService, Rabbitmq } from '../config/env.config'; -import { Logger } from '../config/logger.config'; +import { configService, Rabbitmq } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; const logger = new Logger('AMQP'); diff --git a/src/whatsapp/models/rabbitmq.model.ts b/src/api/integrations/rabbitmq/models/rabbitmq.model.ts similarity index 88% rename from src/whatsapp/models/rabbitmq.model.ts rename to src/api/integrations/rabbitmq/models/rabbitmq.model.ts index e89ee3b1..ba0ac1af 100644 --- a/src/whatsapp/models/rabbitmq.model.ts +++ b/src/api/integrations/rabbitmq/models/rabbitmq.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { dbserver } from '../../../../libs/db.connect'; export class RabbitmqRaw { _id?: string; diff --git a/src/whatsapp/repository/rabbitmq.repository.ts b/src/api/integrations/rabbitmq/repository/rabbitmq.repository.ts similarity index 87% rename from src/whatsapp/repository/rabbitmq.repository.ts rename to src/api/integrations/rabbitmq/repository/rabbitmq.repository.ts index 3dfb5ec2..cb30c7c3 100644 --- a/src/whatsapp/repository/rabbitmq.repository.ts +++ b/src/api/integrations/rabbitmq/repository/rabbitmq.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IRabbitmqModel, RabbitmqRaw } from '../models'; +import { ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { IInsert, Repository } from '../../../abstract/abstract.repository'; +import { IRabbitmqModel, RabbitmqRaw } from '../../../models'; export class RabbitmqRepository extends Repository { constructor(private readonly rabbitmqModel: IRabbitmqModel, private readonly configService: ConfigService) { diff --git a/src/whatsapp/routers/rabbitmq.router.ts b/src/api/integrations/rabbitmq/routes/rabbitmq.router.ts similarity index 79% rename from src/whatsapp/routers/rabbitmq.router.ts rename to src/api/integrations/rabbitmq/routes/rabbitmq.router.ts index e3f65283..a477a5ba 100644 --- a/src/whatsapp/routers/rabbitmq.router.ts +++ b/src/api/integrations/rabbitmq/routes/rabbitmq.router.ts @@ -1,12 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { Logger } from '../../config/logger.config'; -import { instanceNameSchema, rabbitmqSchema } from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { instanceNameSchema, rabbitmqSchema } from '../../../../validate/validate.schema'; +import { RouterBroker } from '../../../abstract/abstract.router'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { HttpStatus } from '../../../routes/index.router'; +import { rabbitmqController } from '../../../server.module'; import { RabbitmqDto } from '../dto/rabbitmq.dto'; -import { rabbitmqController } from '../whatsapp.module'; -import { HttpStatus } from './index.router'; const logger = new Logger('RabbitmqRouter'); diff --git a/src/whatsapp/services/rabbitmq.service.ts b/src/api/integrations/rabbitmq/services/rabbitmq.service.ts similarity index 77% rename from src/whatsapp/services/rabbitmq.service.ts rename to src/api/integrations/rabbitmq/services/rabbitmq.service.ts index a377595b..efb822fd 100644 --- a/src/whatsapp/services/rabbitmq.service.ts +++ b/src/api/integrations/rabbitmq/services/rabbitmq.service.ts @@ -1,9 +1,9 @@ -import { Logger } from '../../config/logger.config'; -import { initQueues } from '../../libs/amqp.server'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { RabbitmqRaw } from '../../../models'; +import { WAMonitoringService } from '../../../services/monitor.service'; import { RabbitmqDto } from '../dto/rabbitmq.dto'; -import { RabbitmqRaw } from '../models'; -import { WAMonitoringService } from './monitor.service'; +import { initQueues } from '../libs/amqp.server'; export class RabbitmqService { constructor(private readonly waMonitor: WAMonitoringService) {} diff --git a/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts b/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts new file mode 100644 index 00000000..7a786bb4 --- /dev/null +++ b/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts @@ -0,0 +1,66 @@ +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_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', + 'NEW_JWT_TOKEN', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + 'CHAMA_AI_ACTION', + ], + }, + }, + }, + required: ['enabled'], + ...isNotEmpty('enabled'), +}; diff --git a/src/whatsapp/controllers/sqs.controller.ts b/src/api/integrations/sqs/controllers/sqs.controller.ts similarity index 92% rename from src/whatsapp/controllers/sqs.controller.ts rename to src/api/integrations/sqs/controllers/sqs.controller.ts index 8dfebc6c..d6dd346b 100644 --- a/src/whatsapp/controllers/sqs.controller.ts +++ b/src/api/integrations/sqs/controllers/sqs.controller.ts @@ -1,5 +1,5 @@ -import { Logger } from '../../config/logger.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; import { SqsDto } from '../dto/sqs.dto'; import { SqsService } from '../services/sqs.service'; diff --git a/src/whatsapp/dto/sqs.dto.ts b/src/api/integrations/sqs/dto/sqs.dto.ts similarity index 100% rename from src/whatsapp/dto/sqs.dto.ts rename to src/api/integrations/sqs/dto/sqs.dto.ts diff --git a/src/libs/sqs.server.ts b/src/api/integrations/sqs/libs/sqs.server.ts similarity index 94% rename from src/libs/sqs.server.ts rename to src/api/integrations/sqs/libs/sqs.server.ts index 04184542..e1c328cb 100644 --- a/src/libs/sqs.server.ts +++ b/src/api/integrations/sqs/libs/sqs.server.ts @@ -1,7 +1,7 @@ import { SQS } from 'aws-sdk'; -import { configService, Sqs } from '../config/env.config'; -import { Logger } from '../config/logger.config'; +import { configService, Sqs } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; const logger = new Logger('SQS'); diff --git a/src/whatsapp/models/sqs.model.ts b/src/api/integrations/sqs/models/sqs.model.ts similarity index 87% rename from src/whatsapp/models/sqs.model.ts rename to src/api/integrations/sqs/models/sqs.model.ts index 2d5f432f..c3ac4fb1 100644 --- a/src/whatsapp/models/sqs.model.ts +++ b/src/api/integrations/sqs/models/sqs.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { dbserver } from '../../../../libs/db.connect'; export class SqsRaw { _id?: string; diff --git a/src/whatsapp/repository/sqs.repository.ts b/src/api/integrations/sqs/repository/sqs.repository.ts similarity index 87% rename from src/whatsapp/repository/sqs.repository.ts rename to src/api/integrations/sqs/repository/sqs.repository.ts index 50ea1cd3..7e149daa 100644 --- a/src/whatsapp/repository/sqs.repository.ts +++ b/src/api/integrations/sqs/repository/sqs.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ISqsModel, SqsRaw } from '../models'; +import { ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { IInsert, Repository } from '../../../abstract/abstract.repository'; +import { ISqsModel, SqsRaw } from '../../../models'; export class SqsRepository extends Repository { constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) { diff --git a/src/whatsapp/routers/sqs.router.ts b/src/api/integrations/sqs/routes/sqs.router.ts similarity index 79% rename from src/whatsapp/routers/sqs.router.ts rename to src/api/integrations/sqs/routes/sqs.router.ts index e1bf8e93..80d14838 100644 --- a/src/whatsapp/routers/sqs.router.ts +++ b/src/api/integrations/sqs/routes/sqs.router.ts @@ -1,12 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { Logger } from '../../config/logger.config'; -import { instanceNameSchema, sqsSchema } from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { instanceNameSchema, sqsSchema } from '../../../../validate/validate.schema'; +import { RouterBroker } from '../../../abstract/abstract.router'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { HttpStatus } from '../../../routes/index.router'; +import { sqsController } from '../../../server.module'; import { SqsDto } from '../dto/sqs.dto'; -import { sqsController } from '../whatsapp.module'; -import { HttpStatus } from './index.router'; const logger = new Logger('SqsRouter'); diff --git a/src/whatsapp/services/sqs.service.ts b/src/api/integrations/sqs/services/sqs.service.ts similarity index 76% rename from src/whatsapp/services/sqs.service.ts rename to src/api/integrations/sqs/services/sqs.service.ts index 236d4ceb..52780104 100644 --- a/src/whatsapp/services/sqs.service.ts +++ b/src/api/integrations/sqs/services/sqs.service.ts @@ -1,9 +1,9 @@ -import { Logger } from '../../config/logger.config'; -import { initQueues } from '../../libs/sqs.server'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { SqsRaw } from '../../../models'; +import { WAMonitoringService } from '../../../services/monitor.service'; import { SqsDto } from '../dto/sqs.dto'; -import { SqsRaw } from '../models'; -import { WAMonitoringService } from './monitor.service'; +import { initQueues } from '../libs/sqs.server'; export class SqsService { constructor(private readonly waMonitor: WAMonitoringService) {} diff --git a/src/api/integrations/sqs/validate/sqs.schema.ts b/src/api/integrations/sqs/validate/sqs.schema.ts new file mode 100644 index 00000000..54379c29 --- /dev/null +++ b/src/api/integrations/sqs/validate/sqs.schema.ts @@ -0,0 +1,66 @@ +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_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', + 'NEW_JWT_TOKEN', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + 'CHAMA_AI_ACTION', + ], + }, + }, + }, + required: ['enabled'], + ...isNotEmpty('enabled'), +}; diff --git a/src/whatsapp/controllers/typebot.controller.ts b/src/api/integrations/typebot/controllers/typebot.controller.ts similarity index 92% rename from src/whatsapp/controllers/typebot.controller.ts rename to src/api/integrations/typebot/controllers/typebot.controller.ts index 53dc967f..bb52a370 100644 --- a/src/whatsapp/controllers/typebot.controller.ts +++ b/src/api/integrations/typebot/controllers/typebot.controller.ts @@ -1,5 +1,5 @@ -import { Logger } from '../../config/logger.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; import { TypebotDto } from '../dto/typebot.dto'; import { TypebotService } from '../services/typebot.service'; diff --git a/src/whatsapp/dto/typebot.dto.ts b/src/api/integrations/typebot/dto/typebot.dto.ts similarity index 100% rename from src/whatsapp/dto/typebot.dto.ts rename to src/api/integrations/typebot/dto/typebot.dto.ts diff --git a/src/whatsapp/models/typebot.model.ts b/src/api/integrations/typebot/models/typebot.model.ts similarity index 96% rename from src/whatsapp/models/typebot.model.ts rename to src/api/integrations/typebot/models/typebot.model.ts index c8ae7105..9060b6e0 100644 --- a/src/whatsapp/models/typebot.model.ts +++ b/src/api/integrations/typebot/models/typebot.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { dbserver } from '../../../../libs/db.connect'; class Session { remoteJid?: string; diff --git a/src/whatsapp/repository/typebot.repository.ts b/src/api/integrations/typebot/repository/typebot.repository.ts similarity index 88% rename from src/whatsapp/repository/typebot.repository.ts rename to src/api/integrations/typebot/repository/typebot.repository.ts index 8653e9c9..45453c63 100644 --- a/src/whatsapp/repository/typebot.repository.ts +++ b/src/api/integrations/typebot/repository/typebot.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ITypebotModel, TypebotRaw } from '../models'; +import { ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { IInsert, Repository } from '../../../abstract/abstract.repository'; +import { ITypebotModel, TypebotRaw } from '../../../models'; export class TypebotRepository extends Repository { constructor(private readonly typebotModel: ITypebotModel, private readonly configService: ConfigService) { diff --git a/src/whatsapp/routers/typebot.router.ts b/src/api/integrations/typebot/routes/typebot.router.ts similarity index 89% rename from src/whatsapp/routers/typebot.router.ts rename to src/api/integrations/typebot/routes/typebot.router.ts index 0f785de3..2eed2b39 100644 --- a/src/whatsapp/routers/typebot.router.ts +++ b/src/api/integrations/typebot/routes/typebot.router.ts @@ -1,17 +1,17 @@ import { RequestHandler, Router } from 'express'; -import { Logger } from '../../config/logger.config'; +import { Logger } from '../../../../config/logger.config'; import { instanceNameSchema, typebotSchema, typebotStartSchema, typebotStatusSchema, -} from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; -import { InstanceDto } from '../dto/instance.dto'; +} from '../../../../validate/validate.schema'; +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 } from '../dto/typebot.dto'; -import { typebotController } from '../whatsapp.module'; -import { HttpStatus } from './index.router'; const logger = new Logger('TypebotRouter'); diff --git a/src/whatsapp/services/typebot.service.ts b/src/api/integrations/typebot/services/typebot.service.ts similarity index 94% rename from src/whatsapp/services/typebot.service.ts rename to src/api/integrations/typebot/services/typebot.service.ts index 4d79b660..6c322552 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/api/integrations/typebot/services/typebot.service.ts @@ -1,13 +1,13 @@ import axios from 'axios'; import EventEmitter2 from 'eventemitter2'; -import { ConfigService, Typebot } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { ConfigService, Typebot } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { MessageRaw } from '../../../models'; +import { WAMonitoringService } from '../../../services/monitor.service'; +import { Events } from '../../../types/wa.types'; import { Session, TypebotDto } from '../dto/typebot.dto'; -import { MessageRaw } from '../models'; -import { Events } from '../types/wa.types'; -import { WAMonitoringService } from './monitor.service'; export class TypebotService { constructor( @@ -57,7 +57,7 @@ export class TypebotService { if (session) { if (status === 'closed') { - findData.sessions.splice(findData.sessions.indexOf(session), 1); + const found_session: Session[] = findData.sessions.splice(findData.sessions.indexOf(session), 1); const typebotData = { enabled: findData.enabled, @@ -68,7 +68,7 @@ export class TypebotService { delay_message: findData.delay_message, unknown_message: findData.unknown_message, listening_from_me: findData.listening_from_me, - sessions: findData.sessions, + sessions: found_session, }; this.create(instance, typebotData); @@ -106,7 +106,7 @@ export class TypebotService { delay_message: findData.delay_message, unknown_message: findData.unknown_message, listening_from_me: findData.listening_from_me, - sessions: findData.sessions, + sessions: findData.sessions.splice(findData.sessions.indexOf(session), 1), }; this.create(instance, typebotData); @@ -269,27 +269,30 @@ export class TypebotService { } private getTypeMessage(msg: any) { - this.logger.verbose('get type message'); - const types = { - conversation: msg.conversation, - extendedTextMessage: msg.extendedTextMessage?.text, - audioMessage: msg.audioMessage?.url, - imageMessage: msg.imageMessage?.url, - videoMessage: msg.videoMessage?.url, - documentMessage: msg.documentMessage?.fileName, - contactMessage: msg.contactMessage?.displayName, - locationMessage: msg.locationMessage?.degreesLatitude, - viewOnceMessageV2: msg.viewOnceMessageV2?.message?.imageMessage?.url || msg.viewOnceMessageV2?.message?.videoMessage?.url || msg.viewOnceMessageV2?.message?.audioMessage?.url, - listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId, - responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId, - }; + this.logger.verbose('get type message'); + const types = { + conversation: msg.conversation, + extendedTextMessage: msg.extendedTextMessage?.text, + audioMessage: msg.audioMessage?.url, + imageMessage: msg.imageMessage?.url, + videoMessage: msg.videoMessage?.url, + documentMessage: msg.documentMessage?.fileName, + contactMessage: msg.contactMessage?.displayName, + locationMessage: msg.locationMessage?.degreesLatitude, + viewOnceMessageV2: + msg.viewOnceMessageV2?.message?.imageMessage?.url || + msg.viewOnceMessageV2?.message?.videoMessage?.url || + msg.viewOnceMessageV2?.message?.audioMessage?.url, + listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId, + responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId, + }; + + const messageType = Object.keys(types).find((key) => types[key] !== undefined) || 'unknown'; - const messageType = Object.keys(types).find(key => types[key] !== undefined) || 'unknown'; - - this.logger.verbose('Type message: ' + JSON.stringify(types)); - return { ...types, messageType }; + this.logger.verbose('Type message: ' + JSON.stringify(types)); + return { ...types, messageType }; } - + private getMessageContent(types: any) { this.logger.verbose('get message content'); const typeKey = Object.keys(types).find((key) => types[key] !== undefined); @@ -317,7 +320,7 @@ export class TypebotService { this.logger.verbose('get audio message content'); const types = this.getTypeMessage(msg); - + const audioContent = types.audioMessage; this.logger.verbose('audio message URL: ' + audioContent); @@ -327,85 +330,85 @@ export class TypebotService { private getImageMessageContent(msg: any) { this.logger.verbose('get image message content'); - + const types = this.getTypeMessage(msg); - + const imageContent = types.imageMessage; - + this.logger.verbose('image message URL: ' + imageContent); - + return imageContent; } - + private getVideoMessageContent(msg: any) { this.logger.verbose('get video message content'); - + const types = this.getTypeMessage(msg); - + const videoContent = types.videoMessage; - + this.logger.verbose('video message URL: ' + videoContent); - + return videoContent; } - + private getDocumentMessageContent(msg: any) { this.logger.verbose('get document message content'); - + const types = this.getTypeMessage(msg); - + const documentContent = types.documentMessage; - + this.logger.verbose('document message fileName: ' + documentContent); - + return documentContent; } private getContactMessageContent(msg: any) { this.logger.verbose('get contact message content'); - + const types = this.getTypeMessage(msg); - + const contactContent = types.contactMessage; - + this.logger.verbose('contact message displayName: ' + contactContent); - + return contactContent; } private getLocationMessageContent(msg: any) { this.logger.verbose('get location message content'); - + const types = this.getTypeMessage(msg); - + const locationContent = types.locationMessage; - + this.logger.verbose('location message degreesLatitude: ' + locationContent); - + return locationContent; } private getViewOnceMessageV2Content(msg: any) { this.logger.verbose('get viewOnceMessageV2 content'); - + const types = this.getTypeMessage(msg); - + const viewOnceContent = types.viewOnceMessageV2; - + this.logger.verbose('viewOnceMessageV2 URL: ' + viewOnceContent); - + return viewOnceContent; } private getListResponseMessageContent(msg: any) { this.logger.verbose('get listResponseMessage content'); - + const types = this.getTypeMessage(msg); - + const listResponseContent = types.listResponseMessage || types.responseRowId; - + this.logger.verbose('listResponseMessage selectedRowId: ' + listResponseContent); - + return listResponseContent; } public async createNewSession(instance: InstanceDto, data: any) { @@ -791,7 +794,7 @@ export class TypebotService { sessions: sessions, remoteJid: remoteJid, pushName: msg.pushName, - prefilledVariables: { + prefilledVariables: { messageType: messageType, }, }); diff --git a/src/api/integrations/typebot/validate/typebot.schema.ts b/src/api/integrations/typebot/validate/typebot.schema.ts new file mode 100644 index 00000000..fde9d973 --- /dev/null +++ b/src/api/integrations/typebot/validate/typebot.schema.ts @@ -0,0 +1,60 @@ +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 typebotSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + enabled: { type: 'boolean', enum: [true, false] }, + url: { type: 'string' }, + typebot: { type: 'string' }, + expire: { type: 'integer' }, + delay_message: { type: 'integer' }, + unknown_message: { type: 'string' }, + listening_from_me: { type: 'boolean', enum: [true, false] }, + }, + required: ['enabled', 'url', 'typebot', 'expire', 'delay_message', 'unknown_message', 'listening_from_me'], + ...isNotEmpty('enabled', 'url', 'typebot', 'expire', 'delay_message', 'unknown_message', 'listening_from_me'), +}; + +export const typebotStatusSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + remoteJid: { type: 'string' }, + status: { type: 'string', enum: ['opened', 'closed', 'paused'] }, + }, + required: ['remoteJid', 'status'], + ...isNotEmpty('remoteJid', 'status'), +}; + +export const typebotStartSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + remoteJid: { type: 'string' }, + url: { type: 'string' }, + typebot: { type: 'string' }, + }, + required: ['remoteJid', 'url', 'typebot'], + ...isNotEmpty('remoteJid', 'url', 'typebot'), +}; diff --git a/src/whatsapp/controllers/websocket.controller.ts b/src/api/integrations/websocket/controllers/websocket.controller.ts similarity index 93% rename from src/whatsapp/controllers/websocket.controller.ts rename to src/api/integrations/websocket/controllers/websocket.controller.ts index 15abde70..2d207b74 100644 --- a/src/whatsapp/controllers/websocket.controller.ts +++ b/src/api/integrations/websocket/controllers/websocket.controller.ts @@ -1,5 +1,5 @@ -import { Logger } from '../../config/logger.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; import { WebsocketDto } from '../dto/websocket.dto'; import { WebsocketService } from '../services/websocket.service'; diff --git a/src/whatsapp/dto/websocket.dto.ts b/src/api/integrations/websocket/dto/websocket.dto.ts similarity index 100% rename from src/whatsapp/dto/websocket.dto.ts rename to src/api/integrations/websocket/dto/websocket.dto.ts diff --git a/src/libs/socket.server.ts b/src/api/integrations/websocket/libs/socket.server.ts similarity index 86% rename from src/libs/socket.server.ts rename to src/api/integrations/websocket/libs/socket.server.ts index ecf01ab1..81f97847 100644 --- a/src/libs/socket.server.ts +++ b/src/api/integrations/websocket/libs/socket.server.ts @@ -1,8 +1,8 @@ import { Server } from 'http'; import { Server as SocketIO } from 'socket.io'; -import { configService, Cors, Websocket } from '../config/env.config'; -import { Logger } from '../config/logger.config'; +import { configService, Cors, Websocket } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; const logger = new Logger('Socket'); diff --git a/src/whatsapp/models/websocket.model.ts b/src/api/integrations/websocket/models/websocket.model.ts similarity index 89% rename from src/whatsapp/models/websocket.model.ts rename to src/api/integrations/websocket/models/websocket.model.ts index e96332b1..9e824597 100644 --- a/src/whatsapp/models/websocket.model.ts +++ b/src/api/integrations/websocket/models/websocket.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { dbserver } from '../../../../libs/db.connect'; export class WebsocketRaw { _id?: string; diff --git a/src/whatsapp/repository/websocket.repository.ts b/src/api/integrations/websocket/repository/websocket.repository.ts similarity index 87% rename from src/whatsapp/repository/websocket.repository.ts rename to src/api/integrations/websocket/repository/websocket.repository.ts index 19823194..48d5b7d3 100644 --- a/src/whatsapp/repository/websocket.repository.ts +++ b/src/api/integrations/websocket/repository/websocket.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IWebsocketModel, WebsocketRaw } from '../models'; +import { ConfigService } from '../../../../config/env.config'; +import { Logger } from '../../../../config/logger.config'; +import { IInsert, Repository } from '../../../abstract/abstract.repository'; +import { IWebsocketModel, WebsocketRaw } from '../../../models'; export class WebsocketRepository extends Repository { constructor(private readonly websocketModel: IWebsocketModel, private readonly configService: ConfigService) { diff --git a/src/whatsapp/routers/websocket.router.ts b/src/api/integrations/websocket/routes/websocket.router.ts similarity index 79% rename from src/whatsapp/routers/websocket.router.ts rename to src/api/integrations/websocket/routes/websocket.router.ts index f04cad0d..0c39d53c 100644 --- a/src/whatsapp/routers/websocket.router.ts +++ b/src/api/integrations/websocket/routes/websocket.router.ts @@ -1,12 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { Logger } from '../../config/logger.config'; -import { instanceNameSchema, websocketSchema } from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { instanceNameSchema, websocketSchema } from '../../../../validate/validate.schema'; +import { RouterBroker } from '../../../abstract/abstract.router'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { HttpStatus } from '../../../routes/index.router'; +import { websocketController } from '../../../server.module'; import { WebsocketDto } from '../dto/websocket.dto'; -import { websocketController } from '../whatsapp.module'; -import { HttpStatus } from './index.router'; const logger = new Logger('WebsocketRouter'); diff --git a/src/whatsapp/services/websocket.service.ts b/src/api/integrations/websocket/services/websocket.service.ts similarity index 79% rename from src/whatsapp/services/websocket.service.ts rename to src/api/integrations/websocket/services/websocket.service.ts index 20663bbf..fcbb4353 100644 --- a/src/whatsapp/services/websocket.service.ts +++ b/src/api/integrations/websocket/services/websocket.service.ts @@ -1,8 +1,8 @@ -import { Logger } from '../../config/logger.config'; -import { InstanceDto } from '../dto/instance.dto'; +import { Logger } from '../../../../config/logger.config'; +import { InstanceDto } from '../../../dto/instance.dto'; +import { WebsocketRaw } from '../../../models'; +import { WAMonitoringService } from '../../../services/monitor.service'; import { WebsocketDto } from '../dto/websocket.dto'; -import { WebsocketRaw } from '../models'; -import { WAMonitoringService } from './monitor.service'; export class WebsocketService { constructor(private readonly waMonitor: WAMonitoringService) {} diff --git a/src/api/integrations/websocket/validate/websocket.schema.ts b/src/api/integrations/websocket/validate/websocket.schema.ts new file mode 100644 index 00000000..ddddeee2 --- /dev/null +++ b/src/api/integrations/websocket/validate/websocket.schema.ts @@ -0,0 +1,66 @@ +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_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', + 'NEW_JWT_TOKEN', + 'TYPEBOT_START', + 'TYPEBOT_CHANGE_STATUS', + 'CHAMA_AI_ACTION', + ], + }, + }, + }, + required: ['enabled'], + ...isNotEmpty('enabled'), +}; diff --git a/src/whatsapp/models/auth.model.ts b/src/api/models/auth.model.ts similarity index 100% rename from src/whatsapp/models/auth.model.ts rename to src/api/models/auth.model.ts diff --git a/src/whatsapp/models/chat.model.ts b/src/api/models/chat.model.ts similarity index 100% rename from src/whatsapp/models/chat.model.ts rename to src/api/models/chat.model.ts diff --git a/src/whatsapp/models/contact.model.ts b/src/api/models/contact.model.ts similarity index 100% rename from src/whatsapp/models/contact.model.ts rename to src/api/models/contact.model.ts diff --git a/src/api/models/index.ts b/src/api/models/index.ts new file mode 100644 index 00000000..42399bc5 --- /dev/null +++ b/src/api/models/index.ts @@ -0,0 +1,15 @@ +export * from '../integrations/chamaai/models/chamaai.model'; +export * from '../integrations/chatwoot/models/chatwoot.model'; +export * from '../integrations/rabbitmq/models/rabbitmq.model'; +export * from '../integrations/sqs/models/sqs.model'; +export * from '../integrations/typebot/models/typebot.model'; +export * from '../integrations/websocket/models/websocket.model'; +export * from './auth.model'; +export * from './chat.model'; +export * from './contact.model'; +export * from './integration.model'; +export * from './label.model'; +export * from './message.model'; +export * from './proxy.model'; +export * from './settings.model'; +export * from './webhook.model'; diff --git a/src/whatsapp/models/integration.model.ts b/src/api/models/integration.model.ts similarity index 100% rename from src/whatsapp/models/integration.model.ts rename to src/api/models/integration.model.ts diff --git a/src/whatsapp/models/label.model.ts b/src/api/models/label.model.ts similarity index 100% rename from src/whatsapp/models/label.model.ts rename to src/api/models/label.model.ts diff --git a/src/whatsapp/models/message.model.ts b/src/api/models/message.model.ts similarity index 100% rename from src/whatsapp/models/message.model.ts rename to src/api/models/message.model.ts diff --git a/src/whatsapp/models/proxy.model.ts b/src/api/models/proxy.model.ts similarity index 100% rename from src/whatsapp/models/proxy.model.ts rename to src/api/models/proxy.model.ts diff --git a/src/whatsapp/models/settings.model.ts b/src/api/models/settings.model.ts similarity index 100% rename from src/whatsapp/models/settings.model.ts rename to src/api/models/settings.model.ts diff --git a/src/whatsapp/models/webhook.model.ts b/src/api/models/webhook.model.ts similarity index 100% rename from src/whatsapp/models/webhook.model.ts rename to src/api/models/webhook.model.ts diff --git a/src/whatsapp/repository/auth.repository.ts b/src/api/repository/auth.repository.ts similarity index 100% rename from src/whatsapp/repository/auth.repository.ts rename to src/api/repository/auth.repository.ts diff --git a/src/whatsapp/repository/chat.repository.ts b/src/api/repository/chat.repository.ts similarity index 100% rename from src/whatsapp/repository/chat.repository.ts rename to src/api/repository/chat.repository.ts diff --git a/src/whatsapp/repository/contact.repository.ts b/src/api/repository/contact.repository.ts similarity index 100% rename from src/whatsapp/repository/contact.repository.ts rename to src/api/repository/contact.repository.ts diff --git a/src/whatsapp/repository/integration.repository.ts b/src/api/repository/integration.repository.ts similarity index 100% rename from src/whatsapp/repository/integration.repository.ts rename to src/api/repository/integration.repository.ts diff --git a/src/whatsapp/repository/label.repository.ts b/src/api/repository/label.repository.ts similarity index 100% rename from src/whatsapp/repository/label.repository.ts rename to src/api/repository/label.repository.ts diff --git a/src/whatsapp/repository/message.repository.ts b/src/api/repository/message.repository.ts similarity index 100% rename from src/whatsapp/repository/message.repository.ts rename to src/api/repository/message.repository.ts diff --git a/src/whatsapp/repository/messageUp.repository.ts b/src/api/repository/messageUp.repository.ts similarity index 100% rename from src/whatsapp/repository/messageUp.repository.ts rename to src/api/repository/messageUp.repository.ts diff --git a/src/whatsapp/repository/proxy.repository.ts b/src/api/repository/proxy.repository.ts similarity index 100% rename from src/whatsapp/repository/proxy.repository.ts rename to src/api/repository/proxy.repository.ts diff --git a/src/whatsapp/repository/repository.manager.ts b/src/api/repository/repository.manager.ts similarity index 92% rename from src/whatsapp/repository/repository.manager.ts rename to src/api/repository/repository.manager.ts index 10207eb3..5fd9e55e 100644 --- a/src/whatsapp/repository/repository.manager.ts +++ b/src/api/repository/repository.manager.ts @@ -4,22 +4,22 @@ import { join } from 'path'; import { Auth, ConfigService, Database } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; +import { ChamaaiRepository } from '../integrations/chamaai/repository/chamaai.repository'; +import { ChatwootRepository } from '../integrations/chatwoot/repository/chatwoot.repository'; +import { RabbitmqRepository } from '../integrations/rabbitmq/repository/rabbitmq.repository'; +import { SqsRepository } from '../integrations/sqs/repository/sqs.repository'; +import { TypebotRepository } from '../integrations/typebot/repository/typebot.repository'; +import { WebsocketRepository } from '../integrations/websocket/repository/websocket.repository'; import { AuthRepository } from './auth.repository'; -import { ChamaaiRepository } from './chamaai.repository'; import { ChatRepository } from './chat.repository'; -import { ChatwootRepository } from './chatwoot.repository'; import { ContactRepository } from './contact.repository'; import { IntegrationRepository } from './integration.repository'; import { LabelRepository } from './label.repository'; import { MessageRepository } from './message.repository'; import { MessageUpRepository } from './messageUp.repository'; import { ProxyRepository } from './proxy.repository'; -import { RabbitmqRepository } from './rabbitmq.repository'; import { SettingsRepository } from './settings.repository'; -import { SqsRepository } from './sqs.repository'; -import { TypebotRepository } from './typebot.repository'; import { WebhookRepository } from './webhook.repository'; -import { WebsocketRepository } from './websocket.repository'; export class RepositoryBroker { constructor( public readonly message: MessageRepository, diff --git a/src/whatsapp/repository/settings.repository.ts b/src/api/repository/settings.repository.ts similarity index 100% rename from src/whatsapp/repository/settings.repository.ts rename to src/api/repository/settings.repository.ts diff --git a/src/whatsapp/repository/webhook.repository.ts b/src/api/repository/webhook.repository.ts similarity index 100% rename from src/whatsapp/repository/webhook.repository.ts rename to src/api/repository/webhook.repository.ts diff --git a/src/whatsapp/routers/chat.router.ts b/src/api/routes/chat.router.ts similarity index 99% rename from src/whatsapp/routers/chat.router.ts rename to src/api/routes/chat.router.ts index d8096c79..e8c82a16 100644 --- a/src/whatsapp/routers/chat.router.ts +++ b/src/api/routes/chat.router.ts @@ -9,7 +9,6 @@ import { messageUpSchema, messageValidateSchema, presenceSchema, - presenceOnlySchema, privacySettingsSchema, profileNameSchema, profilePictureSchema, @@ -39,7 +38,7 @@ import { InstanceDto } from '../dto/instance.dto'; import { ContactQuery } from '../repository/contact.repository'; import { MessageQuery } from '../repository/message.repository'; import { MessageUpQuery } from '../repository/messageUp.repository'; -import { chatController } from '../whatsapp.module'; +import { chatController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('ChatRouter'); diff --git a/src/whatsapp/routers/group.router.ts b/src/api/routes/group.router.ts similarity index 99% rename from src/whatsapp/routers/group.router.ts rename to src/api/routes/group.router.ts index bf088129..244dfee2 100644 --- a/src/whatsapp/routers/group.router.ts +++ b/src/api/routes/group.router.ts @@ -30,7 +30,7 @@ import { GroupUpdateParticipantDto, GroupUpdateSettingDto, } from '../dto/group.dto'; -import { groupController } from '../whatsapp.module'; +import { groupController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('GroupRouter'); diff --git a/src/whatsapp/routers/index.router.ts b/src/api/routes/index.router.ts similarity index 84% rename from src/whatsapp/routers/index.router.ts rename to src/api/routes/index.router.ts index 51460339..3b26671f 100644 --- a/src/whatsapp/routers/index.router.ts +++ b/src/api/routes/index.router.ts @@ -4,21 +4,21 @@ import fs from 'fs'; import { Auth, configService } from '../../config/env.config'; import { authGuard } from '../guards/auth.guard'; import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard'; -import { ChamaaiRouter } from './chamaai.router'; +import { ChamaaiRouter } from '../integrations/chamaai/routes/chamaai.router'; +import { ChatwootRouter } from '../integrations/chatwoot/routes/chatwoot.router'; +import { RabbitmqRouter } from '../integrations/rabbitmq/routes/rabbitmq.router'; +import { SqsRouter } from '../integrations/sqs/routes/sqs.router'; +import { TypebotRouter } from '../integrations/typebot/routes/typebot.router'; +import { WebsocketRouter } from '../integrations/websocket/routes/websocket.router'; import { ChatRouter } from './chat.router'; -import { ChatwootRouter } from './chatwoot.router'; import { GroupRouter } from './group.router'; import { InstanceRouter } from './instance.router'; import { LabelRouter } from './label.router'; import { ProxyRouter } from './proxy.router'; -import { RabbitmqRouter } from './rabbitmq.router'; import { MessageRouter } from './sendMessage.router'; import { SettingsRouter } from './settings.router'; -import { SqsRouter } from './sqs.router'; -import { TypebotRouter } from './typebot.router'; import { ViewsRouter } from './view.router'; import { WebhookRouter } from './webhook.router'; -import { WebsocketRouter } from './websocket.router'; enum HttpStatus { OK = 200, diff --git a/src/whatsapp/routers/instance.router.ts b/src/api/routes/instance.router.ts similarity index 89% rename from src/whatsapp/routers/instance.router.ts rename to src/api/routes/instance.router.ts index 6d4727e2..a56271a4 100644 --- a/src/whatsapp/routers/instance.router.ts +++ b/src/api/routes/instance.router.ts @@ -3,11 +3,11 @@ import { RequestHandler, Router } from 'express'; import { Auth, ConfigService, Database } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { dbserver } from '../../libs/db.connect'; -import {instanceNameSchema, oldTokenSchema, presenceOnlySchema} from '../../validate/validate.schema'; +import { instanceNameSchema, oldTokenSchema, presenceOnlySchema } from '../../validate/validate.schema'; import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; +import { instanceController } from '../server.module'; import { OldToken } from '../services/auth.service'; -import { instanceController } from '../whatsapp.module'; import { HttpStatus } from './index.router'; const logger = new Logger('InstanceRouter'); @@ -50,6 +50,22 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) + .post(this.routerPath('registerMobileCode'), ...guards, async (req, res) => { + logger.verbose('request received in registerMobileCode'); + logger.verbose('request body: '); + logger.verbose(req.body); + + logger.verbose('request query: '); + logger.verbose(req.query); + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: SetPresenceDto, + execute: (instance, data) => instanceController.registerMobileCode(instance, data), + }); + + return res.status(HttpStatus.OK).json(response); + }) .get(this.routerPath('connect'), ...guards, async (req, res) => { logger.verbose('request received in connectInstance'); logger.verbose('request body: '); diff --git a/src/whatsapp/routers/label.router.ts b/src/api/routes/label.router.ts similarity index 97% rename from src/whatsapp/routers/label.router.ts rename to src/api/routes/label.router.ts index a856ff6d..a6002bb9 100644 --- a/src/whatsapp/routers/label.router.ts +++ b/src/api/routes/label.router.ts @@ -4,7 +4,7 @@ import { Logger } from '../../config/logger.config'; import { handleLabelSchema } from '../../validate/validate.schema'; import { RouterBroker } from '../abstract/abstract.router'; import { HandleLabelDto, LabelDto } from '../dto/label.dto'; -import { labelController } from '../whatsapp.module'; +import { labelController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('LabelRouter'); diff --git a/src/whatsapp/routers/proxy.router.ts b/src/api/routes/proxy.router.ts similarity index 97% rename from src/whatsapp/routers/proxy.router.ts rename to src/api/routes/proxy.router.ts index 2ae0141b..284fe368 100644 --- a/src/whatsapp/routers/proxy.router.ts +++ b/src/api/routes/proxy.router.ts @@ -5,7 +5,7 @@ import { instanceNameSchema, proxySchema } from '../../validate/validate.schema' import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto } from '../dto/instance.dto'; import { ProxyDto } from '../dto/proxy.dto'; -import { proxyController } from '../whatsapp.module'; +import { proxyController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('ProxyRouter'); diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/api/routes/sendMessage.router.ts similarity index 99% rename from src/whatsapp/routers/sendMessage.router.ts rename to src/api/routes/sendMessage.router.ts index 51b60593..908a1a58 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/api/routes/sendMessage.router.ts @@ -30,7 +30,7 @@ import { SendTemplateDto, SendTextDto, } from '../dto/sendMessage.dto'; -import { sendMessageController } from '../whatsapp.module'; +import { sendMessageController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('MessageRouter'); diff --git a/src/whatsapp/routers/settings.router.ts b/src/api/routes/settings.router.ts similarity index 96% rename from src/whatsapp/routers/settings.router.ts rename to src/api/routes/settings.router.ts index 57e56b0d..c2d3bad9 100644 --- a/src/whatsapp/routers/settings.router.ts +++ b/src/api/routes/settings.router.ts @@ -5,7 +5,7 @@ import { instanceNameSchema, settingsSchema } from '../../validate/validate.sche import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto } from '../dto/instance.dto'; import { SettingsDto } from '../dto/settings.dto'; -import { settingsController } from '../whatsapp.module'; +import { settingsController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('SettingsRouter'); diff --git a/src/whatsapp/routers/view.router.ts b/src/api/routes/view.router.ts similarity index 100% rename from src/whatsapp/routers/view.router.ts rename to src/api/routes/view.router.ts diff --git a/src/whatsapp/routers/webhook.router.ts b/src/api/routes/webhook.router.ts similarity index 98% rename from src/whatsapp/routers/webhook.router.ts rename to src/api/routes/webhook.router.ts index 255b6ac5..1e4ddcac 100644 --- a/src/whatsapp/routers/webhook.router.ts +++ b/src/api/routes/webhook.router.ts @@ -6,7 +6,7 @@ import { instanceNameSchema, webhookSchema } from '../../validate/validate.schem import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto } from '../dto/instance.dto'; import { WebhookDto } from '../dto/webhook.dto'; -import { webhookController } from '../whatsapp.module'; +import { webhookController } from '../server.module'; import { HttpStatus } from './index.router'; const logger = new Logger('WebhookRouter'); diff --git a/src/whatsapp/whatsapp.module.ts b/src/api/server.module.ts similarity index 79% rename from src/whatsapp/whatsapp.module.ts rename to src/api/server.module.ts index c3fe2665..5c78e1c3 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/api/server.module.ts @@ -1,23 +1,35 @@ import { configService } from '../config/env.config'; import { eventEmitter } from '../config/event.config'; import { Logger } from '../config/logger.config'; -import { CacheEngine } from '../libs/cacheengine'; import { dbserver } from '../libs/db.connect'; import { RedisCache } from '../libs/redis.client'; -import { ChamaaiController } from './controllers/chamaai.controller'; import { ChatController } from './controllers/chat.controller'; -import { ChatwootController } from './controllers/chatwoot.controller'; import { GroupController } from './controllers/group.controller'; import { InstanceController } from './controllers/instance.controller'; import { LabelController } from './controllers/label.controller'; import { ProxyController } from './controllers/proxy.controller'; -import { RabbitmqController } from './controllers/rabbitmq.controller'; import { SendMessageController } from './controllers/sendMessage.controller'; import { SettingsController } from './controllers/settings.controller'; -import { SqsController } from './controllers/sqs.controller'; -import { TypebotController } from './controllers/typebot.controller'; import { WebhookController } from './controllers/webhook.controller'; -import { WebsocketController } from './controllers/websocket.controller'; +import { ChamaaiController } from './integrations/chamaai/controllers/chamaai.controller'; +import { ChamaaiRepository } from './integrations/chamaai/repository/chamaai.repository'; +import { ChamaaiService } from './integrations/chamaai/services/chamaai.service'; +import { CacheEngine } from './integrations/chatwoot/cache/cacheengine'; +import { ChatwootController } from './integrations/chatwoot/controllers/chatwoot.controller'; +import { ChatwootRepository } from './integrations/chatwoot/repository/chatwoot.repository'; +import { ChatwootService } from './integrations/chatwoot/services/chatwoot.service'; +import { RabbitmqController } from './integrations/rabbitmq/controllers/rabbitmq.controller'; +import { RabbitmqRepository } from './integrations/rabbitmq/repository/rabbitmq.repository'; +import { RabbitmqService } from './integrations/rabbitmq/services/rabbitmq.service'; +import { SqsController } from './integrations/sqs/controllers/sqs.controller'; +import { SqsRepository } from './integrations/sqs/repository/sqs.repository'; +import { SqsService } from './integrations/sqs/services/sqs.service'; +import { TypebotController } from './integrations/typebot/controllers/typebot.controller'; +import { TypebotRepository } from './integrations/typebot/repository/typebot.repository'; +import { TypebotService } from './integrations/typebot/services/typebot.service'; +import { WebsocketController } from './integrations/websocket/controllers/websocket.controller'; +import { WebsocketRepository } from './integrations/websocket/repository/websocket.repository'; +import { WebsocketService } from './integrations/websocket/services/websocket.service'; import { AuthModel, ChamaaiModel, @@ -37,35 +49,23 @@ import { } from './models'; import { LabelModel } from './models/label.model'; import { AuthRepository } from './repository/auth.repository'; -import { ChamaaiRepository } from './repository/chamaai.repository'; import { ChatRepository } from './repository/chat.repository'; -import { ChatwootRepository } from './repository/chatwoot.repository'; import { ContactRepository } from './repository/contact.repository'; import { IntegrationRepository } from './repository/integration.repository'; import { LabelRepository } from './repository/label.repository'; import { MessageRepository } from './repository/message.repository'; import { MessageUpRepository } from './repository/messageUp.repository'; import { ProxyRepository } from './repository/proxy.repository'; -import { RabbitmqRepository } from './repository/rabbitmq.repository'; import { RepositoryBroker } from './repository/repository.manager'; import { SettingsRepository } from './repository/settings.repository'; -import { SqsRepository } from './repository/sqs.repository'; -import { TypebotRepository } from './repository/typebot.repository'; import { WebhookRepository } from './repository/webhook.repository'; -import { WebsocketRepository } from './repository/websocket.repository'; import { AuthService } from './services/auth.service'; import { CacheService } from './services/cache.service'; -import { ChamaaiService } from './services/chamaai.service'; -import { ChatwootService } from './services/chatwoot.service'; import { IntegrationService } from './services/integration.service'; import { WAMonitoringService } from './services/monitor.service'; import { ProxyService } from './services/proxy.service'; -import { RabbitmqService } from './services/rabbitmq.service'; import { SettingsService } from './services/settings.service'; -import { SqsService } from './services/sqs.service'; -import { TypebotService } from './services/typebot.service'; import { WebhookService } from './services/webhook.service'; -import { WebsocketService } from './services/websocket.service'; const logger = new Logger('WA MODULE'); @@ -108,7 +108,6 @@ export const repository = new RepositoryBroker( ); export const cache = new RedisCache(); - const chatwootCache = new CacheService(new CacheEngine(configService, ChatwootService.name).getEngine()); export const waMonitor = new WAMonitoringService(eventEmitter, configService, repository, cache, chatwootCache); @@ -116,41 +115,32 @@ export const waMonitor = new WAMonitoringService(eventEmitter, configService, re const authService = new AuthService(configService, waMonitor, repository); const typebotService = new TypebotService(waMonitor, configService, eventEmitter); - export const typebotController = new TypebotController(typebotService); const webhookService = new WebhookService(waMonitor); - export const webhookController = new WebhookController(webhookService, waMonitor); const websocketService = new WebsocketService(waMonitor); - export const websocketController = new WebsocketController(websocketService); const proxyService = new ProxyService(waMonitor); - export const proxyController = new ProxyController(proxyService, waMonitor); const chamaaiService = new ChamaaiService(waMonitor, configService); - export const chamaaiController = new ChamaaiController(chamaaiService); const rabbitmqService = new RabbitmqService(waMonitor); - export const rabbitmqController = new RabbitmqController(rabbitmqService); const sqsService = new SqsService(waMonitor); - export const sqsController = new SqsController(sqsService); const integrationService = new IntegrationService(waMonitor); const chatwootService = new ChatwootService(waMonitor, configService, repository, chatwootCache); - export const chatwootController = new ChatwootController(chatwootService, configService, repository); const settingsService = new SettingsService(waMonitor); - export const settingsController = new SettingsController(settingsService); export const instanceController = new InstanceController( @@ -167,6 +157,7 @@ export const instanceController = new InstanceController( sqsService, typebotService, integrationService, + proxyController, cache, chatwootCache, ); diff --git a/src/whatsapp/services/auth.service.ts b/src/api/services/auth.service.ts similarity index 100% rename from src/whatsapp/services/auth.service.ts rename to src/api/services/auth.service.ts diff --git a/src/whatsapp/services/cache.service.ts b/src/api/services/cache.service.ts similarity index 100% rename from src/whatsapp/services/cache.service.ts rename to src/api/services/cache.service.ts diff --git a/src/whatsapp/services/integration.service.ts b/src/api/services/integration.service.ts similarity index 100% rename from src/whatsapp/services/integration.service.ts rename to src/api/services/integration.service.ts diff --git a/src/whatsapp/services/monitor.service.ts b/src/api/services/monitor.service.ts similarity index 98% rename from src/whatsapp/services/monitor.service.ts rename to src/api/services/monitor.service.ts index e7eb0bc1..e8644e99 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/api/services/monitor.service.ts @@ -26,8 +26,8 @@ import { import { RepositoryBroker } from '../repository/repository.manager'; import { Integration } from '../types/wa.types'; import { CacheService } from './cache.service'; -import { BaileysStartupService } from './whatsapp.baileys.service'; -import { BusinessStartupService } from './whatsapp.business.service'; +import { BaileysStartupService } from './whatsapp/whatsapp.baileys.service'; +import { BusinessStartupService } from './whatsapp/whatsapp.business.service'; export class WAMonitoringService { constructor( @@ -375,7 +375,7 @@ export class WAMonitoringService { private async loadInstancesFromRedis() { this.logger.verbose('Redis enabled'); await this.cache.connect(this.redis as Redis); - const keys = await this.cache.instanceKeys(); + const keys = await this.cache.getInstanceKeys(); if (keys?.length > 0) { this.logger.verbose('Reading instance keys and setting instances'); diff --git a/src/whatsapp/services/proxy.service.ts b/src/api/services/proxy.service.ts similarity index 100% rename from src/whatsapp/services/proxy.service.ts rename to src/api/services/proxy.service.ts diff --git a/src/whatsapp/services/settings.service.ts b/src/api/services/settings.service.ts similarity index 100% rename from src/whatsapp/services/settings.service.ts rename to src/api/services/settings.service.ts diff --git a/src/whatsapp/services/webhook.service.ts b/src/api/services/webhook.service.ts similarity index 100% rename from src/whatsapp/services/webhook.service.ts rename to src/api/services/webhook.service.ts diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/api/services/whatsapp.service.ts similarity index 95% rename from src/whatsapp/services/whatsapp.service.ts rename to src/api/services/whatsapp.service.ts index 030285b8..5c6df8d3 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/api/services/whatsapp.service.ts @@ -20,23 +20,23 @@ import { import { Logger } from '../../config/logger.config'; import { ROOT_DIR } from '../../config/path.config'; import { NotFoundException } from '../../exceptions'; -import { getAMQP, removeQueues } from '../../libs/amqp.server'; -import { getIO } from '../../libs/socket.server'; -import { getSQS, removeQueues as removeQueuesSQS } from '../../libs/sqs.server'; +import { ChamaaiService } from '../integrations/chamaai/services/chamaai.service'; +import { ChatwootRaw } from '../integrations/chatwoot/models/chatwoot.model'; +import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service'; +import { getAMQP, removeQueues } from '../integrations/rabbitmq/libs/amqp.server'; +import { getSQS, removeQueues as removeQueuesSQS } from '../integrations/sqs/libs/sqs.server'; +import { TypebotService } from '../integrations/typebot/services/typebot.service'; +import { getIO } from '../integrations/websocket/libs/socket.server'; +import { WebsocketRaw } from '../integrations/websocket/models/websocket.model'; import { ChamaaiRaw, IntegrationRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, SqsRaw, TypebotRaw } from '../models'; -import { ChatwootRaw } from '../models/chatwoot.model'; import { WebhookRaw } from '../models/webhook.model'; -import { WebsocketRaw } from '../models/websocket.model'; import { ContactQuery } from '../repository/contact.repository'; import { MessageQuery } from '../repository/message.repository'; import { MessageUpQuery } from '../repository/messageUp.repository'; import { RepositoryBroker } from '../repository/repository.manager'; +import { waMonitor } from '../server.module'; import { Events, wa } from '../types/wa.types'; -import { waMonitor } from '../whatsapp.module'; import { CacheService } from './cache.service'; -import { ChamaaiService } from './chamaai.service'; -import { ChatwootService } from './chatwoot.service'; -import { TypebotService } from './typebot.service'; export class WAStartupService { constructor( @@ -583,7 +583,7 @@ export class WAStartupService { this.logger.verbose(`Proxy enabled: ${this.localProxy.enabled}`); this.localProxy.proxy = data?.proxy; - this.logger.verbose(`Proxy proxy: ${this.localProxy.proxy}`); + this.logger.verbose(`Proxy proxy: ${this.localProxy.proxy.host}`); this.logger.verbose('Proxy loaded'); } @@ -828,28 +828,56 @@ export class WAStartupService { } } - if (this.configService.get('WEBSOCKET')?.ENABLED && this.localWebsocket.enabled) { + if (this.configService.get('WEBSOCKET')?.ENABLED) { this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name); - if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) { - this.logger.verbose('Sending data to websocket on event: ' + event); - const io = getIO(); + const io = getIO(); + + const message = { + event, + instance: this.instance.name, + data, + server_url: serverUrl, + date_time: now, + sender: this.wuid, + }; - const message = { - event, - instance: this.instance.name, - data, - server_url: serverUrl, - date_time: now, - sender: this.wuid, - }; - - if (expose && instanceApikey) { - message['apikey'] = instanceApikey; + if (expose && instanceApikey) { + message['apikey'] = instanceApikey; + } + + if (this.configService.get('WEBSOCKET')?.GLOBAL_EVENTS) { + io.emit(event, message); + + if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { + const logData = { + local: WAStartupService.name + '.sendData-WebsocketGlobal', + event, + instance: this.instance.name, + data, + server_url: serverUrl, + apikey: (expose && instanceApikey) || null, + date_time: now, + sender: this.wuid, + }; + + if (expose && instanceApikey) { + logData['apikey'] = instanceApikey; + } + + this.logger.log(logData); } + } + + if (this.localWebsocket.enabled && Array.isArray(websocketLocal) && websocketLocal.includes(we)) { + this.logger.verbose('Sending data to websocket on event: ' + event); this.logger.verbose('Sending data to socket.io in channel: ' + this.instance.name); io.of(`/${this.instance.name}`).emit(event, message); + if (this.configService.get('WEBSOCKET')?.GLOBAL_EVENTS) { + io.emit(event, message); + } + if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { local: WAStartupService.name + '.sendData-Websocket', diff --git a/src/whatsapp/services/whatsapp.baileys.service.ts b/src/api/services/whatsapp/whatsapp.baileys.service.ts similarity index 93% rename from src/whatsapp/services/whatsapp.baileys.service.ts rename to src/api/services/whatsapp/whatsapp.baileys.service.ts index f99e074d..6285250b 100644 --- a/src/whatsapp/services/whatsapp.baileys.service.ts +++ b/src/api/services/whatsapp/whatsapp.baileys.service.ts @@ -24,6 +24,7 @@ import makeWASocket, { MessageUpsertType, MiscMessageGenerationOptions, ParticipantAction, + PHONENUMBER_MCC, prepareWAMessageMedia, proto, useMultiFileAuthState, @@ -38,10 +39,11 @@ import makeWASocket, { import { Label } from '@whiskeysockets/baileys/lib/Types/Label'; import { LabelAssociation } from '@whiskeysockets/baileys/lib/Types/LabelAssociation'; import axios from 'axios'; -import { exec } from 'child_process'; import { arrayUnique, isBase64, isURL } from 'class-validator'; import EventEmitter2 from 'eventemitter2'; +import ffmpeg from 'fluent-ffmpeg'; import fs, { existsSync, readFileSync } from 'fs'; +import { parsePhoneNumber } from 'libphonenumber-js'; import Long from 'long'; import NodeCache from 'node-cache'; import { getMIMEType } from 'node-mime-types'; @@ -52,15 +54,14 @@ import qrcode, { QRCodeToDataURLOptions } from 'qrcode'; import qrcodeTerminal from 'qrcode-terminal'; import sharp from 'sharp'; -import { ConfigService, ConfigSessionPhone, Database, Log, QrCode, Redis } from '../../config/env.config'; -import { INSTANCE_DIR } from '../../config/path.config'; -import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../exceptions'; -import { dbserver } from '../../libs/db.connect'; -import { RedisCache } from '../../libs/redis.client'; -import { chatwootImport } from '../../utils/chatwoot-import-helper'; -import { makeProxyAgent } from '../../utils/makeProxyAgent'; -import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; -import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; +import { ConfigService, ConfigSessionPhone, Database, Log, QrCode, Redis } from '../../../config/env.config'; +import { INSTANCE_DIR } from '../../../config/path.config'; +import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../../exceptions'; +import { dbserver } from '../../../libs/db.connect'; +import { RedisCache } from '../../../libs/redis.client'; +import { makeProxyAgent } from '../../../utils/makeProxyAgent'; +import { useMultiFileAuthStateDb } from '../../../utils/use-multi-file-auth-state-db'; +import { useMultiFileAuthStateRedisDb } from '../../../utils/use-multi-file-auth-state-redis-db'; import { ArchiveChatDto, BlockUserDto, @@ -74,7 +75,7 @@ import { SendPresenceDto, UpdateMessageDto, WhatsAppNumberDto, -} from '../dto/chat.dto'; +} from '../../dto/chat.dto'; import { AcceptGroupInvite, CreateGroupDto, @@ -88,9 +89,9 @@ import { GroupToggleEphemeralDto, GroupUpdateParticipantDto, GroupUpdateSettingDto, -} from '../dto/group.dto'; -import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; -import { HandleLabelDto, LabelDto } from '../dto/label.dto'; +} from '../../dto/group.dto'; +import { InstanceDto, SetPresenceDto } from '../../dto/instance.dto'; +import { HandleLabelDto, LabelDto } from '../../dto/label.dto'; import { ContactMessage, MediaMessage, @@ -107,18 +108,19 @@ import { SendStickerDto, SendTextDto, StatusMessage, -} from '../dto/sendMessage.dto'; -import { SettingsRaw } from '../models'; -import { ChatRaw } from '../models/chat.model'; -import { ContactRaw } from '../models/contact.model'; -import { MessageRaw, MessageUpdateRaw } from '../models/message.model'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types'; -import { waMonitor } from '../whatsapp.module'; -import { CacheService } from './cache.service'; -import { WAStartupService } from './whatsapp.service'; - -const retryCache = {}; +} from '../../dto/sendMessage.dto'; +import { chatwootImport } from '../../integrations/chatwoot/utils/chatwoot-import-helper'; +import { SettingsRaw } from '../../models'; +import { ChatRaw } from '../../models/chat.model'; +import { ContactRaw } from '../../models/contact.model'; +import { MessageRaw, MessageUpdateRaw } from '../../models/message.model'; +import { RepositoryBroker } from '../../repository/repository.manager'; +import { waMonitor } from '../../server.module'; +import { Events, MessageSubtype, TypeMediaMessage, wa } from '../../types/wa.types'; +import { CacheService } from './../cache.service'; +import { WAStartupService } from './../whatsapp.service'; + +// const retryCache = {}; export class BaileysStartupService extends WAStartupService { constructor( @@ -132,6 +134,7 @@ export class BaileysStartupService extends WAStartupService { this.logger.verbose('BaileysStartupService initialized'); this.cleanStore(); this.instance.qrcode = { count: 0 }; + this.mobile = false; } private readonly msgRetryCounterCache: CacheStore = new NodeCache(); @@ -141,7 +144,8 @@ export class BaileysStartupService extends WAStartupService { public stateConnection: wa.StateConnection = { state: 'close' }; - private phoneNumber: string; + public phoneNumber: string; + public mobile: boolean; public get connectionStatus() { this.logger.verbose('Getting connection status'); @@ -389,6 +393,10 @@ export class BaileysStartupService extends WAStartupService { ); } } + + if (connection === 'connecting') { + if (this.mobile) this.sendMobileCode(); + } } private async getMessage(key: proto.IMessageKey, full = false) { @@ -446,7 +454,7 @@ export class BaileysStartupService extends WAStartupService { return await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name)); } - public async connectToWhatsapp(number?: string): Promise { + public async connectToWhatsapp(number?: string, mobile?: boolean): Promise { this.logger.verbose('Connecting to whatsapp'); try { this.loadWebhook(); @@ -461,7 +469,14 @@ export class BaileysStartupService extends WAStartupService { this.instance.authState = await this.defineAuthState(); + if (!mobile) { + this.mobile = false; + } else { + this.mobile = mobile; + } + const { version } = await fetchLatestBaileysVersion(); + this.logger.verbose('Baileys version: ' + version); const session = this.configService.get('CONFIG_SESSION_PHONE'); const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; @@ -470,7 +485,7 @@ export class BaileysStartupService extends WAStartupService { let options; if (this.localProxy.enabled) { - this.logger.info('Proxy enabled: ' + this.localProxy.proxy); + this.logger.info('Proxy enabled: ' + this.localProxy.proxy.host); if (this.localProxy?.proxy?.host?.includes('proxyscrape')) { try { @@ -500,6 +515,7 @@ export class BaileysStartupService extends WAStartupService { }, logger: P({ level: this.logBaileys }), printQRInTerminal: false, + mobile, browser: number ? ['Chrome (Linux)', session.NAME, release()] : browser, version, markOnlineOnConnect: this.localSettings.always_online, @@ -564,6 +580,70 @@ export class BaileysStartupService extends WAStartupService { } } + private async sendMobileCode() { + const { registration } = this.client.authState.creds || null; + + let phoneNumber = registration.phoneNumber || this.phoneNumber; + + if (!phoneNumber.startsWith('+')) { + phoneNumber = '+' + phoneNumber; + } + + if (!phoneNumber) { + this.logger.error('Phone number not found'); + return; + } + + console.log('phoneNumber', phoneNumber); + + const parsedPhoneNumber = parsePhoneNumber(phoneNumber); + + console.log('parsedPhoneNumber', parsedPhoneNumber); + + if (!parsedPhoneNumber?.isValid()) { + this.logger.error('Phone number invalid'); + return; + } + + registration.phoneNumber = parsedPhoneNumber.format('E.164'); + registration.phoneNumberCountryCode = parsedPhoneNumber.countryCallingCode; + registration.phoneNumberNationalNumber = parsedPhoneNumber.nationalNumber; + + const mcc = await PHONENUMBER_MCC[parsedPhoneNumber.countryCallingCode]; + if (!mcc) { + this.logger.error('MCC not found'); + return; + } + + registration.phoneNumberMobileCountryCode = mcc; + registration.method = 'sms'; + + try { + const response = await this.client.requestRegistrationCode(registration); + + console.log('response', response); + if (['ok', 'sent'].includes(response?.status)) { + this.logger.verbose('Registration code sent successfully'); + + return response; + } + } catch (error) { + this.logger.error(error); + } + } + + public async receiveMobileCode(code: string) { + await this.client + .register(code.replace(/["']/g, '').trim().toLowerCase()) + .then(async (response) => { + this.logger.verbose('Registration code received successfully'); + console.log(response); + }) + .catch((error) => { + this.logger.error(error); + }); + } + public async reloadConnection(): Promise { try { this.instance.authState = await this.defineAuthState(); @@ -575,11 +655,26 @@ export class BaileysStartupService extends WAStartupService { let options; if (this.localProxy.enabled) { - this.logger.verbose('Proxy enabled'); - options = { - agent: makeProxyAgent(this.localProxy.proxy), - fetchAgent: makeProxyAgent(this.localProxy.proxy), - }; + this.logger.info('Proxy enabled: ' + this.localProxy.proxy.host); + + if (this.localProxy?.proxy?.host?.includes('proxyscrape')) { + try { + const response = await axios.get(this.localProxy.proxy.host); + const text = response.data; + const proxyUrls = text.split('\r\n'); + const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); + const proxyUrl = 'http://' + proxyUrls[rand]; + options = { + agent: makeProxyAgent(proxyUrl), + }; + } catch (error) { + this.localProxy.enabled = false; + } + } else { + options = { + agent: makeProxyAgent(this.localProxy.proxy), + }; + } } const socketConfig: UserFacingSocketConfig = { @@ -734,7 +829,7 @@ export class BaileysStartupService extends WAStartupService { } this.logger.verbose('Sending data to webhook in event CONTACTS_UPSERT'); - this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); + if (contactsRaw.length > 0) this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); this.logger.verbose('Inserting contacts in database'); this.repository.contact.insert(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); @@ -756,7 +851,7 @@ export class BaileysStartupService extends WAStartupService { } this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE'); - this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); + if (contactsRaw.length > 0) this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); this.logger.verbose('Updating contacts in database'); this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); @@ -951,7 +1046,8 @@ export class BaileysStartupService extends WAStartupService { if ( (type !== 'notify' && type !== 'append') || received.message?.protocolMessage || - received.message?.pollUpdateMessage + received.message?.pollUpdateMessage || + !received?.message ) { this.logger.verbose('message rejected'); return; @@ -1376,27 +1472,27 @@ export class BaileysStartupService extends WAStartupService { if (events['messages.upsert']) { this.logger.verbose('Listening event: messages.upsert'); const payload = events['messages.upsert']; - if (payload.messages.find((a) => a?.messageStubType === 2)) { - const msg = payload.messages[0]; - retryCache[msg.key.id] = msg; - return; - } + // if (payload.messages.find((a) => a?.messageStubType === 2)) { + // const msg = payload.messages[0]; + // retryCache[msg.key.id] = msg; + // return; + // } this.messageHandle['messages.upsert'](payload, database, settings); } if (events['messages.update']) { this.logger.verbose('Listening event: messages.update'); const payload = events['messages.update']; - payload.forEach((message) => { - if (retryCache[message.key.id]) { - this.client.ev.emit('messages.upsert', { - messages: [message], - type: 'notify', - }); - delete retryCache[message.key.id]; - return; - } - }); + // payload.forEach((message) => { + // if (retryCache[message.key.id]) { + // this.client.ev.emit('messages.upsert', { + // messages: [message], + // type: 'notify', + // }); + // delete retryCache[message.key.id]; + // return; + // } + // }); this.messageHandle['messages.update'](payload, database, settings); } @@ -1676,12 +1772,10 @@ export class BaileysStartupService extends WAStartupService { const msg = m?.message ? m : ((await this.getMessage(m.key, true)) as proto.IWebMessageInfo); - if (!msg) { - throw 'Message not found'; + if (msg) { + quoted = msg; + this.logger.verbose('Quoted message'); } - - quoted = msg; - this.logger.verbose('Quoted message'); } let mentions: string[]; @@ -2065,7 +2159,18 @@ export class BaileysStartupService extends WAStartupService { mimetype = getMIMEType(mediaMessage.fileName); if (!mimetype && isURL(mediaMessage.media)) { - const response = await axios.get(mediaMessage.media, { responseType: 'arraybuffer' }); + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + const response = await axios.get(mediaMessage.media, config); mimetype = response.headers['content-type']; } @@ -2134,7 +2239,18 @@ export class BaileysStartupService extends WAStartupService { const url = `${image}?timestamp=${timestamp}`; this.logger.verbose('including timestamp in url: ' + url); - const response = await axios.get(url, { responseType: 'arraybuffer' }); + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + const response = await axios.get(url, config); this.logger.verbose('Getting image from url'); const imageBuffer = Buffer.from(response.data, 'binary'); @@ -2193,7 +2309,7 @@ export class BaileysStartupService extends WAStartupService { if (isURL(audio)) { this.logger.verbose('Audio is url'); - outputAudio = `${join(this.storePath, 'temp', `${hash}.mp4`)}`; + outputAudio = `${join(this.storePath, 'temp', `${hash}.ogg`)}`; tempAudioPath = `${join(this.storePath, 'temp', `temp-${hash}.mp3`)}`; this.logger.verbose('Output audio path: ' + outputAudio); @@ -2204,14 +2320,25 @@ export class BaileysStartupService extends WAStartupService { this.logger.verbose('Including timestamp in url: ' + url); - const response = await axios.get(url, { responseType: 'arraybuffer' }); + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + const response = await axios.get(url, config); this.logger.verbose('Getting audio from url'); fs.writeFileSync(tempAudioPath, response.data); } else { this.logger.verbose('Audio is base64'); - outputAudio = `${join(this.storePath, 'temp', `${hash}.mp4`)}`; + outputAudio = `${join(this.storePath, 'temp', `${hash}.ogg`)}`; tempAudioPath = `${join(this.storePath, 'temp', `temp-${hash}.mp3`)}`; this.logger.verbose('Output audio path: ' + outputAudio); @@ -2224,15 +2351,25 @@ export class BaileysStartupService extends WAStartupService { this.logger.verbose('Converting audio to mp4'); return new Promise((resolve, reject) => { - exec(`${ffmpegPath.path} -i ${tempAudioPath} -vn -ab 128k -ar 44100 -f ipod ${outputAudio} -y`, (error) => { - fs.unlinkSync(tempAudioPath); - this.logger.verbose('Temp audio deleted'); - - if (error) reject(error); - - this.logger.verbose('Audio converted to mp4'); - resolve(outputAudio); - }); + // This fix was suggested by @PurpShell + ffmpeg.setFfmpegPath(ffmpegPath.path); + + ffmpeg() + .input(tempAudioPath) + .outputFormat('ogg') + .noVideo() + .audioCodec('libopus') + .save(outputAudio) + .on('error', function (error) { + console.log('error', error); + fs.unlinkSync(tempAudioPath); + if (error) reject(error); + }) + .on('end', async function () { + fs.unlinkSync(tempAudioPath); + resolve(outputAudio); + }) + .run(); }); } @@ -2252,7 +2389,7 @@ export class BaileysStartupService extends WAStartupService { { audio: Buffer.from(audio, 'base64'), ptt: true, - mimetype: 'audio/mp4', + mimetype: 'audio/ogg; codecs=opus', }, { presence: 'recording', delay: data?.options?.delay }, isChatwoot, @@ -2838,7 +2975,18 @@ export class BaileysStartupService extends WAStartupService { const url = `${picture}?timestamp=${timestamp}`; this.logger.verbose('Including timestamp in url: ' + url); - pic = (await axios.get(url, { responseType: 'arraybuffer' })).data; + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + pic = (await axios.get(url, config)).data; this.logger.verbose('Getting picture from url'); } else if (isBase64(picture)) { this.logger.verbose('Picture is base64'); @@ -2998,7 +3146,18 @@ export class BaileysStartupService extends WAStartupService { const url = `${picture.image}?timestamp=${timestamp}`; this.logger.verbose('Including timestamp in url: ' + url); - pic = (await axios.get(url, { responseType: 'arraybuffer' })).data; + let config: any = { + responseType: 'arraybuffer', + }; + + if (this.localProxy.enabled) { + config = { + ...config, + httpsAgent: makeProxyAgent(this.localProxy.proxy), + }; + } + + pic = (await axios.get(url, config)).data; this.logger.verbose('Getting picture from url'); } else if (isBase64(picture.image)) { this.logger.verbose('Picture is base64'); diff --git a/src/whatsapp/services/whatsapp.business.service.ts b/src/api/services/whatsapp/whatsapp.business.service.ts similarity index 98% rename from src/whatsapp/services/whatsapp.business.service.ts rename to src/api/services/whatsapp/whatsapp.business.service.ts index 2b4a32cf..89cc2089 100644 --- a/src/whatsapp/services/whatsapp.business.service.ts +++ b/src/api/services/whatsapp/whatsapp.business.service.ts @@ -5,10 +5,10 @@ import FormData from 'form-data'; import fs from 'fs/promises'; import { getMIMEType } from 'node-mime-types'; -import { ConfigService, Database, WaBusiness } from '../../config/env.config'; -import { BadRequestException, InternalServerErrorException } from '../../exceptions'; -import { RedisCache } from '../../libs/redis.client'; -import { NumberBusiness } from '../dto/chat.dto'; +import { ConfigService, Database, WaBusiness } from '../../../config/env.config'; +import { BadRequestException, InternalServerErrorException } from '../../../exceptions'; +import { RedisCache } from '../../../libs/redis.client'; +import { NumberBusiness } from '../../dto/chat.dto'; import { ContactMessage, MediaMessage, @@ -22,12 +22,12 @@ import { SendReactionDto, SendTemplateDto, SendTextDto, -} from '../dto/sendMessage.dto'; -import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../models'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { Events, wa } from '../types/wa.types'; -import { CacheService } from './cache.service'; -import { WAStartupService } from './whatsapp.service'; +} from '../../dto/sendMessage.dto'; +import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../../models'; +import { RepositoryBroker } from '../../repository/repository.manager'; +import { Events, wa } from '../../types/wa.types'; +import { CacheService } from './../cache.service'; +import { WAStartupService } from './../whatsapp.service'; export class BusinessStartupService extends WAStartupService { constructor( @@ -44,7 +44,8 @@ export class BusinessStartupService extends WAStartupService { public stateConnection: wa.StateConnection = { state: 'open' }; - private phoneNumber: string; + public phoneNumber: string; + public mobile: boolean; public get connectionStatus() { this.logger.verbose('Getting connection status'); @@ -1347,4 +1348,7 @@ export class BusinessStartupService extends WAStartupService { public async handleLabel() { throw new BadRequestException('Method not available on WhatsApp Business API'); } + public async receiveMobileCode() { + throw new BadRequestException('Method not available on WhatsApp Business API'); + } } diff --git a/src/whatsapp/types/wa.types.ts b/src/api/types/wa.types.ts similarity index 100% rename from src/whatsapp/types/wa.types.ts rename to src/api/types/wa.types.ts diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 9ee778c1..b3991d08 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -86,6 +86,7 @@ export type Sqs = { export type Websocket = { ENABLED: boolean; + GLOBAL_EVENTS: boolean; }; export type WaBusiness = { @@ -299,6 +300,7 @@ export class ConfigService { }, WEBSOCKET: { ENABLED: process.env?.WEBSOCKET_ENABLED === 'true', + GLOBAL_EVENTS: process.env?.WEBSOCKET_GLOBAL_EVENTS === 'true', }, WA_BUSINESS: { TOKEN_WEBHOOK: process.env.WA_BUSINESS_TOKEN_WEBHOOK || '', diff --git a/src/dev-env.yml b/src/dev-env.yml index adc308e8..f791b721 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -97,6 +97,7 @@ SQS: WEBSOCKET: ENABLED: false + GLOBAL_EVENTS: false WA_BUSINESS: TOKEN_WEBHOOK: evolution diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 0a3be9d1..1b307f5f 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -25,7 +25,7 @@ info: [![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442) - version: 1.7.1 + version: 1.7.2 contact: name: DavidsonGomes email: contato@agenciadgcode.com diff --git a/src/exceptions/400.exception.ts b/src/exceptions/400.exception.ts index 833295c1..2ea3a7a4 100644 --- a/src/exceptions/400.exception.ts +++ b/src/exceptions/400.exception.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../whatsapp/routers/index.router'; +import { HttpStatus } from '../api/routes/index.router'; export class BadRequestException { constructor(...objectError: any[]) { diff --git a/src/exceptions/401.exception.ts b/src/exceptions/401.exception.ts index d5424c71..f5383e0e 100644 --- a/src/exceptions/401.exception.ts +++ b/src/exceptions/401.exception.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../whatsapp/routers/index.router'; +import { HttpStatus } from '../api/routes/index.router'; export class UnauthorizedException { constructor(...objectError: any[]) { diff --git a/src/exceptions/403.exception.ts b/src/exceptions/403.exception.ts index f53ca9a5..53d8f05c 100644 --- a/src/exceptions/403.exception.ts +++ b/src/exceptions/403.exception.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../whatsapp/routers/index.router'; +import { HttpStatus } from '../api/routes/index.router'; export class ForbiddenException { constructor(...objectError: any[]) { diff --git a/src/exceptions/404.exception.ts b/src/exceptions/404.exception.ts index 1119d1a1..f2fd5c28 100644 --- a/src/exceptions/404.exception.ts +++ b/src/exceptions/404.exception.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../whatsapp/routers/index.router'; +import { HttpStatus } from '../api/routes/index.router'; export class NotFoundException { constructor(...objectError: any[]) { diff --git a/src/exceptions/500.exception.ts b/src/exceptions/500.exception.ts index 2a41dfa5..c5111f6d 100644 --- a/src/exceptions/500.exception.ts +++ b/src/exceptions/500.exception.ts @@ -1,4 +1,4 @@ -import { HttpStatus } from '../whatsapp/routers/index.router'; +import { HttpStatus } from '../api/routes/index.router'; export class InternalServerErrorException { constructor(...objectError: any[]) { diff --git a/src/libs/redis.client.ts b/src/libs/redis.client.ts index 1d74ff15..4b3e1991 100644 --- a/src/libs/redis.client.ts +++ b/src/libs/redis.client.ts @@ -43,73 +43,81 @@ export class RedisCache { } } - public async instanceKeys(): Promise { + public async getInstanceKeys(): Promise { const keys: string[] = []; try { this.logger.verbose('Fetching instance keys'); for await (const key of this.client.scanIterator({ MATCH: `${this.redisEnv.PREFIX_KEY}:*` })) { keys.push(key); } + return keys; } catch (error) { this.logger.error('Error fetching instance keys ' + error); + throw error; } - return keys; } public async keyExists(key?: string) { - if (key) { - this.logger.verbose('keyExists: ' + key); - return !!(await this.instanceKeys()).find((i) => i === key); + try { + const keys = await this.getInstanceKeys(); + const targetKey = key || this.instanceName; + this.logger.verbose('keyExists: ' + targetKey); + return keys.includes(targetKey); + } catch (error) { + return false; } - this.logger.verbose('keyExists: ' + this.instanceName); - return !!(await this.instanceKeys()).find((i) => i === this.instanceName); } - public async writeData(field: string, data: any) { + public async setData(field: string, data: any) { try { - this.logger.verbose('writeData: ' + field); + this.logger.verbose('setData: ' + field); const json = JSON.stringify(data, BufferJSON.replacer); - - return await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json); + await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json); + return true; } catch (error) { this.logger.error(error); + return false; } } - public async readData(field: string) { + public async getData(field: string): Promise { try { - this.logger.verbose('readData: ' + field); + this.logger.verbose('getData: ' + field); const data = await this.client.hGet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field); if (data) { - this.logger.verbose('readData: ' + field + ' success'); + this.logger.verbose('getData: ' + field + ' success'); return JSON.parse(data, BufferJSON.reviver); } - this.logger.verbose('readData: ' + field + ' not found'); + this.logger.verbose('getData: ' + field + ' not found'); return null; } catch (error) { this.logger.error(error); + return null; } } - public async removeData(field: string) { + public async removeData(field: string): Promise { try { this.logger.verbose('removeData: ' + field); - return await this.client.hDel(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field); + await this.client.hDel(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field); + return true; } catch (error) { this.logger.error(error); + return false; } } - public async delAll(hash?: string) { + public async delAll(hash?: string): Promise { try { - this.logger.verbose('instance delAll: ' + hash); - const result = await this.client.del(hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName); - - return result; + const targetHash = hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName; + this.logger.verbose('instance delAll: ' + targetHash); + const result = await this.client.del(targetHash); + return !!result; } catch (error) { this.logger.error(error); + return false; } } } diff --git a/src/main.ts b/src/main.ts index 554d95e8..942c725f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,17 +6,17 @@ import cors from 'cors'; import express, { json, NextFunction, Request, Response, urlencoded } from 'express'; import { join } from 'path'; +import { initAMQP } from './api/integrations/rabbitmq/libs/amqp.server'; +import { initSQS } from './api/integrations/sqs/libs/sqs.server'; +import { initIO } from './api/integrations/websocket/libs/socket.server'; +import { HttpStatus, router } from './api/routes/index.router'; +import { waMonitor } from './api/server.module'; import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config'; import { onUnexpectedError } from './config/error.config'; import { Logger } from './config/logger.config'; import { ROOT_DIR } from './config/path.config'; import { swaggerRouter } from './docs/swagger.conf'; -import { initAMQP } from './libs/amqp.server'; -import { initIO } from './libs/socket.server'; -import { initSQS } from './libs/sqs.server'; import { ServerUP } from './utils/server-up'; -import { HttpStatus, router } from './whatsapp/routers/index.router'; -import { waMonitor } from './whatsapp/whatsapp.module'; function initWA() { waMonitor.loadInstance(); diff --git a/src/utils/makeProxyAgent.ts b/src/utils/makeProxyAgent.ts index 45486523..60d5f20c 100644 --- a/src/utils/makeProxyAgent.ts +++ b/src/utils/makeProxyAgent.ts @@ -1,6 +1,6 @@ import { HttpsProxyAgent } from 'https-proxy-agent'; -import { wa } from '../whatsapp/types/wa.types'; +import { wa } from '../api/types/wa.types'; export function makeProxyAgent(proxy: wa.Proxy | string) { if (typeof proxy === 'string') { diff --git a/src/utils/use-multi-file-auth-state-redis-db.ts b/src/utils/use-multi-file-auth-state-redis-db.ts index 8e685b54..eb88d678 100644 --- a/src/utils/use-multi-file-auth-state-redis-db.ts +++ b/src/utils/use-multi-file-auth-state-redis-db.ts @@ -17,7 +17,7 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{ const writeData = async (data: any, key: string): Promise => { try { - return await cache.writeData(key, data); + return await cache.setData(key, data); } catch (error) { return logger.error({ localError: 'writeData', error }); } @@ -25,9 +25,9 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{ const readData = async (key: string): Promise => { try { - return await cache.readData(key); + return await cache.getData(key); } catch (error) { - logger.error({ readData: 'writeData', error }); + logger.error({ localError: 'readData', error }); return; } }; diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index b723826a..9013ca39 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -1,6 +1,13 @@ import { JSONSchema7, JSONSchema7Definition } from 'json-schema'; import { v4 } from 'uuid'; +// Integrations Schema +export * from '../api/integrations/chamaai/validate/chamaai.schema'; +export * from '../api/integrations/chatwoot/validate/chatwoot.schema'; +export * from '../api/integrations/rabbitmq/validate/rabbitmq.schema'; +export * from '../api/integrations/sqs/validate/sqs.schema'; +export * from '../api/integrations/typebot/validate/typebot.schema'; + const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { const properties = {}; propertyNames.forEach( @@ -957,27 +964,6 @@ export const webhookSchema: JSONSchema7 = { ...isNotEmpty('url'), }; -export const chatwootSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - enabled: { type: 'boolean', enum: [true, false] }, - account_id: { type: 'string' }, - token: { type: 'string' }, - url: { type: 'string' }, - sign_msg: { type: 'boolean', enum: [true, false] }, - sign_delimiter: { type: ['string', 'null'] }, - reopen_conversation: { type: 'boolean', enum: [true, false] }, - conversation_pending: { type: 'boolean', enum: [true, false] }, - auto_create: { type: 'boolean', enum: [true, false] }, - import_contacts: { type: 'boolean', enum: [true, false] }, - import_messages: { type: 'boolean', enum: [true, false] }, - days_limit_import_messages: { type: 'number' }, - }, - required: ['enabled', 'account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'], - ...isNotEmpty('account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'), -}; - export const settingsSchema: JSONSchema7 = { $id: v4(), type: 'object', @@ -1039,135 +1025,6 @@ export const websocketSchema: JSONSchema7 = { ...isNotEmpty('enabled'), }; -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_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', - 'NEW_JWT_TOKEN', - 'TYPEBOT_START', - 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', - ], - }, - }, - }, - required: ['enabled'], - ...isNotEmpty('enabled'), -}; - -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_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', - 'NEW_JWT_TOKEN', - 'TYPEBOT_START', - 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', - ], - }, - }, - }, - required: ['enabled'], - ...isNotEmpty('enabled'), -}; - -export const typebotSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - enabled: { type: 'boolean', enum: [true, false] }, - url: { type: 'string' }, - typebot: { type: 'string' }, - expire: { type: 'integer' }, - delay_message: { type: 'integer' }, - unknown_message: { type: 'string' }, - listening_from_me: { type: 'boolean', enum: [true, false] }, - }, - required: ['enabled', 'url', 'typebot', 'expire', 'delay_message', 'unknown_message', 'listening_from_me'], - ...isNotEmpty('enabled', 'url', 'typebot', 'expire', 'delay_message', 'unknown_message', 'listening_from_me'), -}; - -export const typebotStatusSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - remoteJid: { type: 'string' }, - status: { type: 'string', enum: ['opened', 'closed', 'paused'] }, - }, - required: ['remoteJid', 'status'], - ...isNotEmpty('remoteJid', 'status'), -}; - -export const typebotStartSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - remoteJid: { type: 'string' }, - url: { type: 'string' }, - typebot: { type: 'string' }, - }, - required: ['remoteJid', 'url', 'typebot'], - ...isNotEmpty('remoteJid', 'url', 'typebot'), -}; - export const proxySchema: JSONSchema7 = { $id: v4(), type: 'object', @@ -1190,20 +1047,6 @@ export const proxySchema: JSONSchema7 = { ...isNotEmpty('enabled', 'proxy'), }; -export const chamaaiSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - enabled: { type: 'boolean', enum: [true, false] }, - url: { type: 'string' }, - token: { type: 'string' }, - waNumber: { type: 'string' }, - answerByAudio: { type: 'boolean', enum: [true, false] }, - }, - required: ['enabled', 'url', 'token', 'waNumber', 'answerByAudio'], - ...isNotEmpty('enabled', 'url', 'token', 'waNumber', 'answerByAudio'), -}; - export const handleLabelSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/models/index.ts b/src/whatsapp/models/index.ts deleted file mode 100644 index 743b9760..00000000 --- a/src/whatsapp/models/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export * from './auth.model'; -export * from './chamaai.model'; -export * from './chat.model'; -export * from './chatwoot.model'; -export * from './contact.model'; -export * from './integration.model'; -export * from './label.model'; -export * from './message.model'; -export * from './proxy.model'; -export * from './rabbitmq.model'; -export * from './settings.model'; -export * from './sqs.model'; -export * from './typebot.model'; -export * from './webhook.model'; -export * from './websocket.model';