Merge remote-tracking branch 'upstream/main' into main

This commit is contained in:
microprocessgit 2024-04-22 08:31:20 -03:00
commit 283e0e4bea
139 changed files with 1337 additions and 857 deletions

View File

@ -1,3 +1,39 @@
# 1.7.4 (develop)
### Fixed
* Adjusts in proxy on fetchAgent
* Recovering messages lost with redis cache
* Log when init redis cache service
# 1.7.3 (2024-04-18 12:07)
### Fixed
* Revert fix audio encoding
* Recovering messages lost with redis cache
* Adjusts in redis for save instances
* Adjusts in proxy
* Revert pull request #523
* Added instance name on logs
* Added support for Spanish
* Fix error: invalid operator. The allowed operators for identifier are equal_to,not_equal_to in chatwoot
# 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) # 1.7.1 (2024-04-03 10:19)
### Fixed ### Fixed

View File

@ -33,7 +33,10 @@ CLEAN_STORE_CHATS=true
# Permanent data storage # Permanent data storage
DATABASE_ENABLED=false DATABASE_ENABLED=false
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin &
readPreference=primary &
ssl=false &
directConnection=true
DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker
# Choose the data you want to save in the application's database or store # Choose the data you want to save in the application's database or store
@ -43,16 +46,13 @@ DATABASE_SAVE_MESSAGE_UPDATE=false
DATABASE_SAVE_DATA_CONTACTS=false DATABASE_SAVE_DATA_CONTACTS=false
DATABASE_SAVE_DATA_CHATS=false DATABASE_SAVE_DATA_CHATS=false
REDIS_ENABLED=false
REDIS_URI=redis://redis:6379
REDIS_PREFIX_KEY=evdocker
RABBITMQ_ENABLED=false RABBITMQ_ENABLED=false
RABBITMQ_RABBITMQ_MODE=global RABBITMQ_RABBITMQ_MODE=global
RABBITMQ_EXCHANGE_NAME=evolution_exchange RABBITMQ_EXCHANGE_NAME=evolution_exchange
RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672 RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
WEBSOCKET_ENABLED=false WEBSOCKET_ENABLED=false
WEBSOCKET_GLOBAL_EVENTS=false
WA_BUSINESS_TOKEN_WEBHOOK=evolution WA_BUSINESS_TOKEN_WEBHOOK=evolution
WA_BUSINESS_URL=https://graph.facebook.com WA_BUSINESS_URL=https://graph.facebook.com
@ -128,6 +128,14 @@ CHATWOOT_MESSAGE_READ=false # false | true
CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgres://user:password@hostname:port/dbname CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgres://user:password@hostname:port/dbname
CHATWOOT_IMPORT_DATABASE_PLACEHOLDER_MEDIA_MESSAGE=true CHATWOOT_IMPORT_DATABASE_PLACEHOLDER_MEDIA_MESSAGE=true
CACHE_REDIS_ENABLED=false
CACHE_REDIS_URI=redis://redis:6379
CACHE_REDIS_PREFIX_KEY=evolution
CACHE_REDIS_TTL=604800
CACHE_REDIS_SAVE_INSTANCES=false
CACHE_LOCAL_ENABLED=false
CACHE_LOCAL_TTL=604800
# Defines an authentication type for the api # Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token, # We recommend using the apikey because it will allow you to use a custom token,
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token # if you use jwt, a random token will be generated and may be expired and you will have to generate a new token

View File

@ -33,7 +33,10 @@ CLEAN_STORE_CHATS=true
# Permanent data storage # Permanent data storage
DATABASE_ENABLED=true DATABASE_ENABLED=true
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin &
readPreference=primary &
ssl=false &
directConnection=true
DATABASE_CONNECTION_DB_PREFIX_NAME=evolution DATABASE_CONNECTION_DB_PREFIX_NAME=evolution
# Choose the data you want to save in the application's database or store # Choose the data you want to save in the application's database or store
@ -43,10 +46,6 @@ DATABASE_SAVE_MESSAGE_UPDATE=false
DATABASE_SAVE_DATA_CONTACTS=false DATABASE_SAVE_DATA_CONTACTS=false
DATABASE_SAVE_DATA_CHATS=false DATABASE_SAVE_DATA_CHATS=false
REDIS_ENABLED=true
REDIS_URI=redis://redis:6379
REDIS_PREFIX_KEY=evolution
# Global Webhook Settings # Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created # Each instance's Webhook URL and events will be requested at the time it is created
## Define a global webhook that will listen for enabled events from all instances ## Define a global webhook that will listen for enabled events from all instances
@ -87,6 +86,14 @@ CONFIG_SESSION_PHONE_NAME=chrome
# Set qrcode display limit # Set qrcode display limit
QRCODE_LIMIT=30 QRCODE_LIMIT=30
CACHE_REDIS_ENABLED=false
CACHE_REDIS_URI=redis://redis:6379
CACHE_REDIS_PREFIX_KEY=evolution
CACHE_REDIS_TTL=604800
CACHE_REDIS_SAVE_INSTANCES=false
CACHE_LOCAL_ENABLED=false
CACHE_LOCAL_TTL=604800
# Defines an authentication type for the api # Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token, # We recommend using the apikey because it will allow you to use a custom token,
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token # if you use jwt, a random token will be generated and may be expired and you will have to generate a new token

View File

@ -1,6 +1,6 @@
FROM node:20.7.0-alpine AS builder 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.3" description="Api to control whatsapp features through http requests."
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
LABEL contact="contato@agenciadgcode.com" LABEL contact="contato@agenciadgcode.com"
@ -58,16 +58,13 @@ ENV DATABASE_SAVE_MESSAGE_UPDATE=false
ENV DATABASE_SAVE_DATA_CONTACTS=false ENV DATABASE_SAVE_DATA_CONTACTS=false
ENV DATABASE_SAVE_DATA_CHATS=false ENV DATABASE_SAVE_DATA_CHATS=false
ENV REDIS_ENABLED=false
ENV REDIS_URI=redis://redis:6379
ENV REDIS_PREFIX_KEY=evolution
ENV RABBITMQ_ENABLED=false ENV RABBITMQ_ENABLED=false
ENV RABBITMQ_MODE=global ENV RABBITMQ_MODE=global
ENV RABBITMQ_EXCHANGE_NAME=evolution_exchange ENV RABBITMQ_EXCHANGE_NAME=evolution_exchange
ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672 ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
ENV WEBSOCKET_ENABLED=false ENV WEBSOCKET_ENABLED=false
ENV WEBSOCKET_GLOBAL_EVENTS=false
ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution
ENV WA_BUSINESS_URL=https://graph.facebook.com ENV WA_BUSINESS_URL=https://graph.facebook.com
@ -128,6 +125,14 @@ ENV QRCODE_COLOR=#198754
ENV TYPEBOT_API_VERSION=latest ENV TYPEBOT_API_VERSION=latest
ENV CACHE_REDIS_ENABLED=false
ENV CACHE_REDIS_URI=redis://redis:6379
ENV CACHE_REDIS_PREFIX_KEY=evolution
ENV CACHE_REDIS_TTL=604800
ENV CACHE_REDIS_SAVE_INSTANCES=false
ENV CACHE_LOCAL_ENABLED=false
ENV CACHE_LOCAL_TTL=604800
ENV AUTHENTICATION_TYPE=apikey ENV AUTHENTICATION_TYPE=apikey
ENV AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976 ENV AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976

View File

@ -1,6 +1,6 @@
{ {
"name": "evolution-api", "name": "evolution-api",
"version": "1.7.1", "version": "1.7.3",
"description": "Rest api for communication with WhatsApp", "description": "Rest api for communication with WhatsApp",
"main": "./dist/src/main.js", "main": "./dist/src/main.js",
"scripts": { "scripts": {
@ -46,7 +46,7 @@
"@figuro/chatwoot-sdk": "^1.1.16", "@figuro/chatwoot-sdk": "^1.1.16",
"@hapi/boom": "^10.0.1", "@hapi/boom": "^10.0.1",
"@sentry/node": "^7.59.2", "@sentry/node": "^7.59.2",
"@whiskeysockets/baileys": "6.6.0", "@whiskeysockets/baileys": "github:AtendAI/Baileys",
"amqplib": "^0.10.3", "amqplib": "^0.10.3",
"aws-sdk": "^2.1499.0", "aws-sdk": "^2.1499.0",
"axios": "^1.6.5", "axios": "^1.6.5",
@ -60,6 +60,7 @@
"exiftool-vendored": "^22.0.0", "exiftool-vendored": "^22.0.0",
"express": "^4.18.2", "express": "^4.18.2",
"express-async-errors": "^3.1.1", "express-async-errors": "^3.1.1",
"fluent-ffmpeg": "^2.1.2",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"hbs": "^4.2.0", "hbs": "^4.2.0",
"https-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.2",

View File

@ -1,13 +1,19 @@
export interface ICache { export interface ICache {
get(key: string): Promise<any>; get(key: string): Promise<any>;
hGet(key: string, field: string): Promise<any>;
set(key: string, value: any, ttl?: number): void; set(key: string, value: any, ttl?: number): void;
hSet(key: string, field: string, value: any): Promise<void>;
has(key: string): Promise<boolean>; has(key: string): Promise<boolean>;
keys(appendCriteria?: string): Promise<string[]>; keys(appendCriteria?: string): Promise<string[]>;
delete(key: string | string[]): Promise<number>; delete(key: string | string[]): Promise<number>;
hDelete(key: string, field: string): Promise<any>;
deleteAll(appendCriteria?: string): Promise<number>; deleteAll(appendCriteria?: string): Promise<number>;
} }

View File

@ -6,23 +6,23 @@ import { v4 } from 'uuid';
import { ConfigService, HttpServer, WaBusiness } from '../../config/env.config'; import { ConfigService, HttpServer, WaBusiness } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { BadRequestException, InternalServerErrorException } from '../../exceptions'; import { BadRequestException, InternalServerErrorException } from '../../exceptions';
import { RedisCache } from '../../libs/redis.client';
import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; 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 { RepositoryBroker } from '../repository/repository.manager';
import { AuthService, OldToken } from '../services/auth.service'; import { AuthService, OldToken } from '../services/auth.service';
import { CacheService } from '../services/cache.service'; import { CacheService } from '../services/cache.service';
import { ChatwootService } from '../services/chatwoot.service';
import { IntegrationService } from '../services/integration.service'; import { IntegrationService } from '../services/integration.service';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
import { RabbitmqService } from '../services/rabbitmq.service';
import { SettingsService } from '../services/settings.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 { WebhookService } from '../services/webhook.service';
import { WebsocketService } from '../services/websocket.service'; import { BaileysStartupService } from '../services/whatsapp/whatsapp.baileys.service';
import { BaileysStartupService } from '../services/whatsapp.baileys.service'; import { BusinessStartupService } from '../services/whatsapp/whatsapp.business.service';
import { BusinessStartupService } from '../services/whatsapp.business.service';
import { Events, Integration, wa } from '../types/wa.types'; import { Events, Integration, wa } from '../types/wa.types';
import { ProxyController } from './proxy.controller';
export class InstanceController { export class InstanceController {
constructor( constructor(
@ -39,8 +39,10 @@ export class InstanceController {
private readonly sqsService: SqsService, private readonly sqsService: SqsService,
private readonly typebotService: TypebotService, private readonly typebotService: TypebotService,
private readonly integrationService: IntegrationService, private readonly integrationService: IntegrationService,
private readonly cache: RedisCache, private readonly proxyService: ProxyController,
private readonly cache: CacheService,
private readonly chatwootCache: CacheService, private readonly chatwootCache: CacheService,
private readonly messagesLostCache: CacheService,
) {} ) {}
private readonly logger = new Logger(InstanceController.name); private readonly logger = new Logger(InstanceController.name);
@ -53,6 +55,7 @@ export class InstanceController {
events, events,
qrcode, qrcode,
number, number,
mobile,
integration, integration,
token, token,
chatwoot_account_id, chatwoot_account_id,
@ -84,6 +87,7 @@ export class InstanceController {
typebot_delay_message, typebot_delay_message,
typebot_unknown_message, typebot_unknown_message,
typebot_listening_from_me, typebot_listening_from_me,
proxy,
}: InstanceDto) { }: InstanceDto) {
try { try {
this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
@ -104,6 +108,7 @@ export class InstanceController {
this.repository, this.repository,
this.cache, this.cache,
this.chatwootCache, this.chatwootCache,
this.messagesLostCache,
); );
} else { } else {
instance = new BaileysStartupService( instance = new BaileysStartupService(
@ -112,10 +117,11 @@ export class InstanceController {
this.repository, this.repository,
this.cache, this.cache,
this.chatwootCache, this.chatwootCache,
this.messagesLostCache,
); );
} }
await this.waMonitor.saveInstance({ integration, instanceName, token, number }); await this.waMonitor.saveInstance({ integration, instanceName, token, number, mobile });
instance.instanceName = instanceName; instance.instanceName = instanceName;
@ -345,6 +351,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) { if (typebot_url) {
try { try {
if (!isURL(typebot_url, { require_tld: false })) { if (!isURL(typebot_url, { require_tld: false })) {
@ -405,7 +423,7 @@ export class InstanceController {
if (qrcode) { if (qrcode) {
this.logger.verbose('creating qrcode'); this.logger.verbose('creating qrcode');
await instance.connectToWhatsapp(number); await instance.connectToWhatsapp(number, mobile);
await delay(5000); await delay(5000);
getQrcode = instance.qrCode; getQrcode = instance.qrCode;
} }
@ -569,7 +587,7 @@ export class InstanceController {
} }
} }
public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) { public async connectToWhatsapp({ instanceName, number = null, mobile = null }: InstanceDto) {
try { try {
this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance'); this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance');
@ -592,7 +610,7 @@ export class InstanceController {
if (state == 'close') { if (state == 'close') {
this.logger.verbose('connecting'); this.logger.verbose('connecting');
await instance.connectToWhatsapp(number); await instance.connectToWhatsapp(number, mobile);
await delay(5000); await delay(5000);
return instance.qrCode; return instance.qrCode;
@ -633,6 +651,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) { public async connectionState({ instanceName }: InstanceDto) {
this.logger.verbose('requested connectionState from ' + instanceName + ' instance'); this.logger.verbose('requested connectionState from ' + instanceName + ' instance');
return { return {

View File

@ -46,7 +46,7 @@ export class ProxyController {
return this.proxyService.find(instance); return this.proxyService.find(instance);
} }
private async testProxy(proxy: ProxyDto['proxy']) { public async testProxy(proxy: ProxyDto['proxy']) {
logger.verbose('requested testProxy'); logger.verbose('requested testProxy');
try { try {
const serverIp = await axios.get('https://icanhazip.com/'); const serverIp = await axios.get('https://icanhazip.com/');

View File

@ -1,10 +1,13 @@
import { WAPresence } from "@whiskeysockets/baileys"; import { WAPresence } from '@whiskeysockets/baileys';
import { ProxyDto } from './proxy.dto';
export class InstanceDto { export class InstanceDto {
instanceName: string; instanceName: string;
instanceId?: string; instanceId?: string;
qrcode?: boolean; qrcode?: boolean;
number?: string; number?: string;
mobile?: boolean;
integration?: string; integration?: string;
token?: string; token?: string;
webhook?: string; webhook?: string;
@ -40,7 +43,7 @@ export class InstanceDto {
typebot_delay_message?: number; typebot_delay_message?: number;
typebot_unknown_message?: string; typebot_unknown_message?: string;
typebot_listening_from_me?: boolean; typebot_listening_from_me?: boolean;
proxy?: string; proxy?: ProxyDto['proxy'];
} }
export class SetPresenceDto { export class SetPresenceDto {

View File

@ -7,8 +7,8 @@ import { Auth, configService } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { ForbiddenException, UnauthorizedException } from '../../exceptions'; import { ForbiddenException, UnauthorizedException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { repository } from '../server.module';
import { JwtPayload } from '../services/auth.service'; import { JwtPayload } from '../services/auth.service';
import { repository } from '../whatsapp.module';
const logger = new Logger('GUARD'); const logger = new Logger('GUARD');

View File

@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { configService, Database, Redis } from '../../config/env.config'; import { CacheConf, configService, Database } from '../../config/env.config';
import { INSTANCE_DIR } from '../../config/path.config'; import { INSTANCE_DIR } from '../../config/path.config';
import { import {
BadRequestException, BadRequestException,
@ -12,17 +12,18 @@ import {
} from '../../exceptions'; } from '../../exceptions';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../libs/db.connect';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { cache, waMonitor } from '../whatsapp.module'; import { cache, waMonitor } from '../server.module';
async function getInstance(instanceName: string) { async function getInstance(instanceName: string) {
try { try {
const db = configService.get<Database>('DATABASE'); const db = configService.get<Database>('DATABASE');
const redisConf = configService.get<Redis>('REDIS'); const cacheConf = configService.get<CacheConf>('CACHE');
const exists = !!waMonitor.waInstances[instanceName]; const exists = !!waMonitor.waInstances[instanceName];
if (redisConf.ENABLED) { if (cacheConf.REDIS.ENABLED && cacheConf.REDIS.SAVE_INSTANCES) {
const keyExists = await cache.keyExists(); const keyExists = await cache.has(instanceName);
return exists || keyExists; return exists || keyExists;
} }

View File

@ -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 { ChamaaiDto } from '../dto/chamaai.dto';
import { InstanceDto } from '../dto/instance.dto';
import { ChamaaiService } from '../services/chamaai.service'; import { ChamaaiService } from '../services/chamaai.service';
const logger = new Logger('ChamaaiController'); const logger = new Logger('ChamaaiController');

View File

@ -1,6 +1,6 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../../../libs/db.connect';
export class ChamaaiRaw { export class ChamaaiRaw {
_id?: string; _id?: string;

View File

@ -1,10 +1,10 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ChamaaiRaw, IChamaaiModel } from '../models'; import { ChamaaiRaw, IChamaaiModel } from '../../../models';
export class ChamaaiRepository extends Repository { export class ChamaaiRepository extends Repository {
constructor(private readonly chamaaiModel: IChamaaiModel, private readonly configService: ConfigService) { constructor(private readonly chamaaiModel: IChamaaiModel, private readonly configService: ConfigService) {

View File

@ -1,12 +1,12 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { chamaaiSchema, instanceNameSchema } from '../../validate/validate.schema'; import { chamaaiSchema, instanceNameSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; 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 { 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'); const logger = new Logger('ChamaaiRouter');

View File

@ -2,13 +2,13 @@ import axios from 'axios';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'fs';
import path from 'path'; import path from 'path';
import { ConfigService, HttpServer } from '../../config/env.config'; import { ConfigService, HttpServer } from '../../../../config/env.config';
import { Logger } from '../../config/logger.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 { 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 { export class ChamaaiService {
constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {} constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {}

View File

@ -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'),
};

View File

@ -1,15 +1,15 @@
import { isURL } from 'class-validator'; import { isURL } from 'class-validator';
import { ConfigService, HttpServer } from '../../config/env.config'; import { CacheEngine } from '../../../../cache/cacheengine';
import { Logger } from '../../config/logger.config'; import { ConfigService, HttpServer } from '../../../../config/env.config';
import { BadRequestException } from '../../exceptions'; import { Logger } from '../../../../config/logger.config';
import { CacheEngine } from '../../libs/cacheengine'; 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 { ChatwootDto } from '../dto/chatwoot.dto'; 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 { ChatwootService } from '../services/chatwoot.service';
import { waMonitor } from '../whatsapp.module';
const logger = new Logger('ChatwootController'); const logger = new Logger('ChatwootController');

View File

@ -1,7 +1,7 @@
import postgresql from 'pg'; import postgresql from 'pg';
import { Chatwoot, configService } from '../config/env.config'; import { Chatwoot, configService } from '../../../../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../../../../config/logger.config';
const { Pool } = postgresql; const { Pool } = postgresql;

View File

@ -1,6 +1,6 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../../../libs/db.connect';
export class ChatwootRaw { export class ChatwootRaw {
_id?: string; _id?: string;

View File

@ -1,10 +1,10 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ChatwootRaw, IChatwootModel } from '../models'; import { ChatwootRaw, IChatwootModel } from '../../../models';
export class ChatwootRepository extends Repository { export class ChatwootRepository extends Repository {
constructor(private readonly chatwootModel: IChatwootModel, private readonly configService: ConfigService) { constructor(private readonly chatwootModel: IChatwootModel, private readonly configService: ConfigService) {

View File

@ -1,12 +1,12 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { chatwootSchema, instanceNameSchema } from '../../validate/validate.schema'; import { chatwootSchema, instanceNameSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; 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 { 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'); const logger = new Logger('ChatwootRouter');

View File

@ -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 { request as chatwootRequest } from '@figuro/chatwoot-sdk/dist/core/request';
import axios from 'axios'; import axios from 'axios';
import FormData from 'form-data'; import FormData from 'form-data';
@ -7,18 +15,18 @@ import Jimp from 'jimp';
import mimeTypes from 'mime-types'; import mimeTypes from 'mime-types';
import path from 'path'; import path from 'path';
import { Chatwoot, ConfigService, HttpServer } from '../../config/env.config'; import { Chatwoot, ConfigService, HttpServer } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { chatwootImport } from '../../utils/chatwoot-import-helper'; import i18next from '../../../../utils/i18n';
import i18next from '../../utils/i18n'; import { ICache } from '../../../abstract/abstract.cache';
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 { ChatwootDto } from '../dto/chatwoot.dto';
import { InstanceDto } from '../dto/instance.dto'; import { chatwootImport } from '../utils/chatwoot-import-helper';
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';
export class ChatwootService { export class ChatwootService {
private readonly logger = new Logger(ChatwootService.name); private readonly logger = new Logger(ChatwootService.name);
@ -453,7 +461,7 @@ export class ChatwootService {
const queryOperator = fieldsToSearch.length - 1 === index1 && numbers.length - 1 === index2 ? null : 'OR'; const queryOperator = fieldsToSearch.length - 1 === index1 && numbers.length - 1 === index2 ? null : 'OR';
filterPayload.push({ filterPayload.push({
attribute_key: field, attribute_key: field,
filter_operator: field == 'phone_number' ? 'equal_to' : 'contains', filter_operator: ['phone_number', 'identifier'].includes(field) ? 'equal_to' : 'contains',
values: [number.replace('+', '')], values: [number.replace('+', '')],
query_operator: queryOperator, query_operator: queryOperator,
}); });
@ -1774,6 +1782,13 @@ export class ChatwootService {
return; 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'); this.logger.verbose('get conversation message');
// Whatsapp to Chatwoot // Whatsapp to Chatwoot
@ -2123,12 +2138,13 @@ export class ChatwootService {
}; };
if (!sourceId && inbox) { if (!sourceId && inbox) {
const contact = (await this.findContact( const conversation = (await client.conversations.get({
instance, accountId: this.provider.account_id,
this.getNumberFromRemoteJid(body.key.remoteJid), conversationId: conversationId,
)) as contact; })) as conversation_show & {
const contactInbox = contact?.contact_inboxes?.find((contactInbox) => contactInbox?.inbox?.id === inbox.id); last_non_activity_message: { conversation: { contact_inbox: contact_inboxes } };
sourceId = contactInbox?.source_id; };
sourceId = conversation.last_non_activity_message?.conversation?.contact_inbox?.source_id;
} }
if (sourceId && inbox?.inbox_identifier) { if (sourceId && inbox?.inbox_identifier) {

View File

@ -1,12 +1,12 @@
import { inbox } from '@figuro/chatwoot-sdk'; import { inbox } from '@figuro/chatwoot-sdk';
import { proto } from '@whiskeysockets/baileys'; import { proto } from '@whiskeysockets/baileys';
import { Chatwoot, configService } from '../config/env.config'; import { InstanceDto } from '../../../../api/dto/instance.dto';
import { Logger } from '../config/logger.config'; 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 { postgresClient } from '../libs/postgres.client';
import { InstanceDto } from '../whatsapp/dto/instance.dto'; import { ChatwootService } from '../services/chatwoot.service';
import { ChatwootRaw, ContactRaw, MessageRaw } from '../whatsapp/models';
import { ChatwootService } from '../whatsapp/services/chatwoot.service';
type ChatwootUser = { type ChatwootUser = {
user_type: string; user_type: string;

View File

@ -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'),
};

View File

@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { RabbitmqDto } from '../dto/rabbitmq.dto'; import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { RabbitmqService } from '../services/rabbitmq.service'; import { RabbitmqService } from '../services/rabbitmq.service';

View File

@ -1,7 +1,7 @@
import * as amqp from 'amqplib/callback_api'; import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq } from '../config/env.config'; import { configService, Rabbitmq } from '../../../../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../../../../config/logger.config';
const logger = new Logger('AMQP'); const logger = new Logger('AMQP');

View File

@ -1,6 +1,6 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../../../libs/db.connect';
export class RabbitmqRaw { export class RabbitmqRaw {
_id?: string; _id?: string;

View File

@ -1,10 +1,10 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { IRabbitmqModel, RabbitmqRaw } from '../models'; import { IRabbitmqModel, RabbitmqRaw } from '../../../models';
export class RabbitmqRepository extends Repository { export class RabbitmqRepository extends Repository {
constructor(private readonly rabbitmqModel: IRabbitmqModel, private readonly configService: ConfigService) { constructor(private readonly rabbitmqModel: IRabbitmqModel, private readonly configService: ConfigService) {

View File

@ -1,12 +1,12 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { instanceNameSchema, rabbitmqSchema } from '../../validate/validate.schema'; import { instanceNameSchema, rabbitmqSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { rabbitmqController } from '../../../server.module';
import { RabbitmqDto } from '../dto/rabbitmq.dto'; import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { rabbitmqController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('RabbitmqRouter'); const logger = new Logger('RabbitmqRouter');

View File

@ -1,9 +1,9 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { initQueues } from '../../libs/amqp.server'; import { InstanceDto } from '../../../dto/instance.dto';
import { InstanceDto } from '../dto/instance.dto'; import { RabbitmqRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { RabbitmqDto } from '../dto/rabbitmq.dto'; import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { RabbitmqRaw } from '../models'; import { initQueues } from '../libs/amqp.server';
import { WAMonitoringService } from './monitor.service';
export class RabbitmqService { export class RabbitmqService {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}

View File

@ -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'),
};

View File

@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto'; import { SqsDto } from '../dto/sqs.dto';
import { SqsService } from '../services/sqs.service'; import { SqsService } from '../services/sqs.service';

View File

@ -1,7 +1,7 @@
import { SQS } from 'aws-sdk'; import { SQS } from 'aws-sdk';
import { configService, Sqs } from '../config/env.config'; import { configService, Sqs } from '../../../../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../../../../config/logger.config';
const logger = new Logger('SQS'); const logger = new Logger('SQS');

View File

@ -1,6 +1,6 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../../../libs/db.connect';
export class SqsRaw { export class SqsRaw {
_id?: string; _id?: string;

View File

@ -1,10 +1,10 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ISqsModel, SqsRaw } from '../models'; import { ISqsModel, SqsRaw } from '../../../models';
export class SqsRepository extends Repository { export class SqsRepository extends Repository {
constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) { constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) {

View File

@ -1,12 +1,12 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { instanceNameSchema, sqsSchema } from '../../validate/validate.schema'; import { instanceNameSchema, sqsSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { sqsController } from '../../../server.module';
import { SqsDto } from '../dto/sqs.dto'; import { SqsDto } from '../dto/sqs.dto';
import { sqsController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('SqsRouter'); const logger = new Logger('SqsRouter');

View File

@ -1,9 +1,9 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { initQueues } from '../../libs/sqs.server'; import { InstanceDto } from '../../../dto/instance.dto';
import { InstanceDto } from '../dto/instance.dto'; import { SqsRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { SqsDto } from '../dto/sqs.dto'; import { SqsDto } from '../dto/sqs.dto';
import { SqsRaw } from '../models'; import { initQueues } from '../libs/sqs.server';
import { WAMonitoringService } from './monitor.service';
export class SqsService { export class SqsService {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}

View File

@ -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'),
};

View File

@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { TypebotDto } from '../dto/typebot.dto'; import { TypebotDto } from '../dto/typebot.dto';
import { TypebotService } from '../services/typebot.service'; import { TypebotService } from '../services/typebot.service';

View File

@ -1,6 +1,6 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../../../libs/db.connect';
class Session { class Session {
remoteJid?: string; remoteJid?: string;

View File

@ -1,10 +1,10 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ITypebotModel, TypebotRaw } from '../models'; import { ITypebotModel, TypebotRaw } from '../../../models';
export class TypebotRepository extends Repository { export class TypebotRepository extends Repository {
constructor(private readonly typebotModel: ITypebotModel, private readonly configService: ConfigService) { constructor(private readonly typebotModel: ITypebotModel, private readonly configService: ConfigService) {

View File

@ -1,17 +1,17 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { import {
instanceNameSchema, instanceNameSchema,
typebotSchema, typebotSchema,
typebotStartSchema, typebotStartSchema,
typebotStatusSchema, typebotStatusSchema,
} from '../../validate/validate.schema'; } from '../../../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { typebotController } from '../../../server.module';
import { TypebotDto } from '../dto/typebot.dto'; import { TypebotDto } from '../dto/typebot.dto';
import { typebotController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('TypebotRouter'); const logger = new Logger('TypebotRouter');

View File

@ -1,13 +1,13 @@
import axios from 'axios'; import axios from 'axios';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
import { ConfigService, Typebot } from '../../config/env.config'; import { ConfigService, Typebot } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; 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 { Session, TypebotDto } from '../dto/typebot.dto';
import { MessageRaw } from '../models';
import { Events } from '../types/wa.types';
import { WAMonitoringService } from './monitor.service';
export class TypebotService { export class TypebotService {
constructor( constructor(
@ -279,12 +279,15 @@ export class TypebotService {
documentMessage: msg.documentMessage?.fileName, documentMessage: msg.documentMessage?.fileName,
contactMessage: msg.contactMessage?.displayName, contactMessage: msg.contactMessage?.displayName,
locationMessage: msg.locationMessage?.degreesLatitude, locationMessage: msg.locationMessage?.degreesLatitude,
viewOnceMessageV2: msg.viewOnceMessageV2?.message?.imageMessage?.url || msg.viewOnceMessageV2?.message?.videoMessage?.url || msg.viewOnceMessageV2?.message?.audioMessage?.url, viewOnceMessageV2:
msg.viewOnceMessageV2?.message?.imageMessage?.url ||
msg.viewOnceMessageV2?.message?.videoMessage?.url ||
msg.viewOnceMessageV2?.message?.audioMessage?.url,
listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId, listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId,
responseRowId: 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)); this.logger.verbose('Type message: ' + JSON.stringify(types));
return { ...types, messageType }; return { ...types, messageType };

View File

@ -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'),
};

View File

@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { WebsocketDto } from '../dto/websocket.dto'; import { WebsocketDto } from '../dto/websocket.dto';
import { WebsocketService } from '../services/websocket.service'; import { WebsocketService } from '../services/websocket.service';

View File

@ -1,8 +1,8 @@
import { Server } from 'http'; import { Server } from 'http';
import { Server as SocketIO } from 'socket.io'; import { Server as SocketIO } from 'socket.io';
import { configService, Cors, Websocket } from '../config/env.config'; import { configService, Cors, Websocket } from '../../../../config/env.config';
import { Logger } from '../config/logger.config'; import { Logger } from '../../../../config/logger.config';
const logger = new Logger('Socket'); const logger = new Logger('Socket');

View File

@ -1,6 +1,6 @@
import { Schema } from 'mongoose'; import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../../../libs/db.connect';
export class WebsocketRaw { export class WebsocketRaw {
_id?: string; _id?: string;

View File

@ -1,10 +1,10 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { ConfigService } from '../../config/env.config'; import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../abstract/abstract.repository'; import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { IWebsocketModel, WebsocketRaw } from '../models'; import { IWebsocketModel, WebsocketRaw } from '../../../models';
export class WebsocketRepository extends Repository { export class WebsocketRepository extends Repository {
constructor(private readonly websocketModel: IWebsocketModel, private readonly configService: ConfigService) { constructor(private readonly websocketModel: IWebsocketModel, private readonly configService: ConfigService) {

View File

@ -1,12 +1,12 @@
import { RequestHandler, Router } from 'express'; import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { instanceNameSchema, websocketSchema } from '../../validate/validate.schema'; import { instanceNameSchema, websocketSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { websocketController } from '../../../server.module';
import { WebsocketDto } from '../dto/websocket.dto'; import { WebsocketDto } from '../dto/websocket.dto';
import { websocketController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('WebsocketRouter'); const logger = new Logger('WebsocketRouter');

View File

@ -1,8 +1,8 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../../../dto/instance.dto';
import { WebsocketRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { WebsocketDto } from '../dto/websocket.dto'; import { WebsocketDto } from '../dto/websocket.dto';
import { WebsocketRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class WebsocketService { export class WebsocketService {
constructor(private readonly waMonitor: WAMonitoringService) {} constructor(private readonly waMonitor: WAMonitoringService) {}

View File

@ -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'),
};

15
src/api/models/index.ts Normal file
View File

@ -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';

View File

@ -32,6 +32,7 @@ export class MessageRaw {
source_reply_id?: string; source_reply_id?: string;
chatwoot?: ChatwootMessage; chatwoot?: ChatwootMessage;
contextInfo?: any; contextInfo?: any;
status?: wa.StatusMessage | any;
} }
type MessageRawBoolean<T> = { type MessageRawBoolean<T> = {

View File

@ -4,22 +4,22 @@ import { join } from 'path';
import { Auth, ConfigService, Database } from '../../config/env.config'; import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.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 { AuthRepository } from './auth.repository';
import { ChamaaiRepository } from './chamaai.repository';
import { ChatRepository } from './chat.repository'; import { ChatRepository } from './chat.repository';
import { ChatwootRepository } from './chatwoot.repository';
import { ContactRepository } from './contact.repository'; import { ContactRepository } from './contact.repository';
import { IntegrationRepository } from './integration.repository'; import { IntegrationRepository } from './integration.repository';
import { LabelRepository } from './label.repository'; import { LabelRepository } from './label.repository';
import { MessageRepository } from './message.repository'; import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository'; import { MessageUpRepository } from './messageUp.repository';
import { ProxyRepository } from './proxy.repository'; import { ProxyRepository } from './proxy.repository';
import { RabbitmqRepository } from './rabbitmq.repository';
import { SettingsRepository } from './settings.repository'; import { SettingsRepository } from './settings.repository';
import { SqsRepository } from './sqs.repository';
import { TypebotRepository } from './typebot.repository';
import { WebhookRepository } from './webhook.repository'; import { WebhookRepository } from './webhook.repository';
import { WebsocketRepository } from './websocket.repository';
export class RepositoryBroker { export class RepositoryBroker {
constructor( constructor(
public readonly message: MessageRepository, public readonly message: MessageRepository,

View File

@ -9,7 +9,6 @@ import {
messageUpSchema, messageUpSchema,
messageValidateSchema, messageValidateSchema,
presenceSchema, presenceSchema,
presenceOnlySchema,
privacySettingsSchema, privacySettingsSchema,
profileNameSchema, profileNameSchema,
profilePictureSchema, profilePictureSchema,
@ -40,7 +39,7 @@ import { InstanceDto } from '../dto/instance.dto';
import { ContactQuery } from '../repository/contact.repository'; import { ContactQuery } from '../repository/contact.repository';
import { MessageQuery } from '../repository/message.repository'; import { MessageQuery } from '../repository/message.repository';
import { MessageUpQuery } from '../repository/messageUp.repository'; import { MessageUpQuery } from '../repository/messageUp.repository';
import { chatController } from '../whatsapp.module'; import { chatController } from '../server.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
const logger = new Logger('ChatRouter'); const logger = new Logger('ChatRouter');

View File

@ -30,7 +30,7 @@ import {
GroupUpdateParticipantDto, GroupUpdateParticipantDto,
GroupUpdateSettingDto, GroupUpdateSettingDto,
} from '../dto/group.dto'; } from '../dto/group.dto';
import { groupController } from '../whatsapp.module'; import { groupController } from '../server.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
const logger = new Logger('GroupRouter'); const logger = new Logger('GroupRouter');

View File

@ -4,21 +4,21 @@ import fs from 'fs';
import { Auth, configService } from '../../config/env.config'; import { Auth, configService } from '../../config/env.config';
import { authGuard } from '../guards/auth.guard'; import { authGuard } from '../guards/auth.guard';
import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.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 { ChatRouter } from './chat.router';
import { ChatwootRouter } from './chatwoot.router';
import { GroupRouter } from './group.router'; import { GroupRouter } from './group.router';
import { InstanceRouter } from './instance.router'; import { InstanceRouter } from './instance.router';
import { LabelRouter } from './label.router'; import { LabelRouter } from './label.router';
import { ProxyRouter } from './proxy.router'; import { ProxyRouter } from './proxy.router';
import { RabbitmqRouter } from './rabbitmq.router';
import { MessageRouter } from './sendMessage.router'; import { MessageRouter } from './sendMessage.router';
import { SettingsRouter } from './settings.router'; import { SettingsRouter } from './settings.router';
import { SqsRouter } from './sqs.router';
import { TypebotRouter } from './typebot.router';
import { ViewsRouter } from './view.router'; import { ViewsRouter } from './view.router';
import { WebhookRouter } from './webhook.router'; import { WebhookRouter } from './webhook.router';
import { WebsocketRouter } from './websocket.router';
enum HttpStatus { enum HttpStatus {
OK = 200, OK = 200,

View File

@ -3,11 +3,11 @@ import { RequestHandler, Router } from 'express';
import { Auth, ConfigService, Database } from '../../config/env.config'; import { Auth, ConfigService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { dbserver } from '../../libs/db.connect'; 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 { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; import { InstanceDto, SetPresenceDto } from '../dto/instance.dto';
import { instanceController } from '../server.module';
import { OldToken } from '../services/auth.service'; import { OldToken } from '../services/auth.service';
import { instanceController } from '../whatsapp.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
const logger = new Logger('InstanceRouter'); const logger = new Logger('InstanceRouter');
@ -50,6 +50,22 @@ export class InstanceRouter extends RouterBroker {
return res.status(HttpStatus.OK).json(response); 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<null>({
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) => { .get(this.routerPath('connect'), ...guards, async (req, res) => {
logger.verbose('request received in connectInstance'); logger.verbose('request received in connectInstance');
logger.verbose('request body: '); logger.verbose('request body: ');

View File

@ -4,7 +4,7 @@ import { Logger } from '../../config/logger.config';
import { handleLabelSchema } from '../../validate/validate.schema'; import { handleLabelSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { HandleLabelDto, LabelDto } from '../dto/label.dto'; import { HandleLabelDto, LabelDto } from '../dto/label.dto';
import { labelController } from '../whatsapp.module'; import { labelController } from '../server.module';
import { HttpStatus } from './index.router'; import { HttpStatus } from './index.router';
const logger = new Logger('LabelRouter'); const logger = new Logger('LabelRouter');

Some files were not shown because too many files have changed in this diff Show More