mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-26 18:38:39 -06:00
website-historico
This commit is contained in:
parent
1665654676
commit
1ebcde65d0
0
.env:Zone.Identifier
Normal file
0
.env:Zone.Identifier
Normal file
10
Dockerfile
10
Dockerfile
@ -1,7 +1,6 @@
|
|||||||
FROM node:20-alpine AS builder
|
FROM node:20-slim AS builder
|
||||||
|
|
||||||
RUN apk update && \
|
RUN apt-get update && apt-get install -y python3 make g++ libvips-dev git pkg-config dos2unix
|
||||||
apk add git ffmpeg wget curl bash
|
|
||||||
|
|
||||||
LABEL version="2.2.0" description="Api to control whatsapp features through http requests."
|
LABEL version="2.2.0" 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"
|
||||||
@ -29,10 +28,9 @@ RUN ./Docker/scripts/generate_database.sh
|
|||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM node:20-alpine AS final
|
FROM node:20-slim AS final
|
||||||
|
|
||||||
RUN apk update && \
|
RUN apt-get update && apt-get install -y python3 make g++ libvips-dev git pkg-config dos2unix
|
||||||
apk add tzdata ffmpeg bash
|
|
||||||
|
|
||||||
ENV TZ=America/Sao_Paulo
|
ENV TZ=America/Sao_Paulo
|
||||||
|
|
||||||
|
16
Dockerfile.dev
Normal file
16
Dockerfile.dev
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
FROM node:20-slim
|
||||||
|
|
||||||
|
WORKDIR /evolution
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y python3 make g++ libvips-dev git pkg-config
|
||||||
|
RUN npm install --platform=linux --arch=x64 sharp
|
||||||
|
COPY package*.json /evolution/
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . /evolution
|
||||||
|
|
||||||
|
# Generate your Prisma Client
|
||||||
|
# RUN npm run db:deploy
|
||||||
|
# RUN npx prisma generate --schema=/evolution/prisma/postgresql-schema.prisma
|
||||||
|
|
||||||
|
CMD ["npm", "run", "dev:server"]
|
@ -1,25 +1,57 @@
|
|||||||
services:
|
services:
|
||||||
api:
|
evolution-api-dev:
|
||||||
container_name: evolution_api
|
container_name: evolution-api-dev
|
||||||
image: evolution/api:local
|
build:
|
||||||
build: .
|
context: .
|
||||||
restart: always
|
dockerfile: Dockerfile.dev
|
||||||
|
depends_on:
|
||||||
|
- evolution-redis
|
||||||
|
- evolution-postgres
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: .
|
||||||
|
target: /evolution
|
||||||
|
- type: volume
|
||||||
|
target: /evolution/node_modules
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- '8080:8080'
|
||||||
volumes:
|
environment:
|
||||||
- evolution_instances:/evolution/instances
|
- NODE_ENV=development
|
||||||
networks:
|
networks:
|
||||||
- evolution-net
|
- chatwoot-evolution-network
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- ./.env
|
||||||
expose:
|
|
||||||
- 8080
|
evolution-redis:
|
||||||
|
image: redis:latest
|
||||||
|
container_name: evolution-redis
|
||||||
|
command: >
|
||||||
|
redis-server --port 6380 --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- evolution-redis:/data
|
||||||
|
networks:
|
||||||
|
- chatwoot-evolution-network
|
||||||
|
|
||||||
|
evolution-postgres:
|
||||||
|
container_name: evolution-postgres
|
||||||
|
image: postgres:15
|
||||||
|
command: [ "postgres", "-c", "max_connections=1000" ]
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- evolution-postgres:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=${POSTGRES_USER}
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- POSTGRES_DB=${POSTGRES_DB}
|
||||||
|
networks:
|
||||||
|
- chatwoot-evolution-network
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
evolution_instances:
|
evolution-instances:
|
||||||
|
evolution-redis:
|
||||||
|
evolution-postgres:
|
||||||
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
evolution-net:
|
chatwoot-evolution-network:
|
||||||
name: evolution-net
|
external: true
|
||||||
driver: bridge
|
|
||||||
|
@ -1,57 +1,61 @@
|
|||||||
services:
|
services:
|
||||||
api:
|
evolution-api:
|
||||||
container_name: evolution_api
|
container_name: evolution-api
|
||||||
image: atendai/evolution-api:homolog
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- evolution-redis
|
||||||
- postgres
|
- evolution-postgres
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- evolution_instances:/evolution/instances
|
- evolution-instances:/evolution/instances
|
||||||
networks:
|
networks:
|
||||||
- evolution-net
|
- chatwoot-evolution-network
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
expose:
|
expose:
|
||||||
- 8080
|
- 8080
|
||||||
|
|
||||||
redis:
|
evolution-redis:
|
||||||
image: redis:latest
|
image: redis:latest
|
||||||
|
container_name: evolution-redis
|
||||||
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
- evolution-net
|
- chatwoot-evolution-network
|
||||||
container_name: redis
|
|
||||||
command: >
|
command: >
|
||||||
redis-server --port 6379 --appendonly yes
|
redis-server --port 6379 --appendonly yes
|
||||||
volumes:
|
volumes:
|
||||||
- evolution_redis:/data
|
- evolution-redis:/data
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
postgres:
|
evolution-postgres:
|
||||||
container_name: postgres
|
container_name: evolution-postgres
|
||||||
image: postgres:15
|
image: postgres:15
|
||||||
networks:
|
networks:
|
||||||
- evolution-net
|
- chatwoot-evolution-network
|
||||||
command: [ "postgres", "-c", "max_connections=1000" ]
|
command: [ "postgres", "-c", "max_connections=1000" ]
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=PASSWORD
|
- POSTGRES_USER=${POSTGRES_USER}
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- POSTGRES_DB=${POSTGRES_DB}
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- evolution-postgres:/var/lib/postgresql/data
|
||||||
expose:
|
expose:
|
||||||
- 5432
|
- 5432
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
evolution_instances:
|
evolution-instances:
|
||||||
evolution_redis:
|
evolution-redis:
|
||||||
postgres_data:
|
evolution-postgres:
|
||||||
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
evolution-net:
|
chatwoot-evolution-network:
|
||||||
name: evolution-net
|
external: true
|
||||||
driver: bridge
|
|
||||||
|
@ -17,81 +17,200 @@ import {
|
|||||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
import { BadRequestException } from '@exceptions';
|
import { BadRequestException } from '@exceptions';
|
||||||
import { isBase64, isURL } from 'class-validator';
|
import { isBase64, isURL } from 'class-validator';
|
||||||
|
import { Logger } from '@config/logger.config';
|
||||||
|
|
||||||
export class SendMessageController {
|
export class SendMessageController {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
|
private readonly logger = new Logger('SendMessageController');
|
||||||
|
|
||||||
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
|
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].templateMessage(data);
|
this.logger.log(`[sendTemplate] [${instanceName}] - Iniciando envio de template...`);
|
||||||
|
this.logger.debug(`[sendTemplate] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].templateMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendTemplate] [${instanceName}] - Envio concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendTemplate] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
|
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].textMessage(data);
|
this.logger.log(`[sendText] [${instanceName}] - Iniciando envio de texto...`);
|
||||||
|
this.logger.debug(`[sendText] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].textMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendText] [${instanceName}] - Envio concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendText] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto, file?: any) {
|
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto, file?: any) {
|
||||||
|
this.logger.log(`[sendMedia] [${instanceName}] - Iniciando envio de mídia...`);
|
||||||
|
this.logger.debug(`[sendMedia] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (isBase64(data?.media) && !data?.fileName && data?.mediatype === 'document') {
|
if (isBase64(data?.media) && !data?.fileName && data?.mediatype === 'document') {
|
||||||
|
this.logger.error(
|
||||||
|
`[sendMedia] [${instanceName}] - Falha: Para base64, é necessário informar o nome do arquivo.`
|
||||||
|
);
|
||||||
throw new BadRequestException('For base64 the file name must be informed.');
|
throw new BadRequestException('For base64 the file name must be informed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file || isURL(data?.media) || isBase64(data?.media)) {
|
if (file || isURL(data?.media) || isBase64(data?.media)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].mediaMessage(data, file);
|
const result = await this.waMonitor.waInstances[instanceName].mediaMessage(data, file);
|
||||||
|
this.logger.log(`[sendMedia] [${instanceName}] - Envio de mídia concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendMedia] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.error(
|
||||||
|
`[sendMedia] [${instanceName}] - Falha: Mídia deve ser uma URL ou base64.`
|
||||||
|
);
|
||||||
throw new BadRequestException('Owned media must be a url or base64');
|
throw new BadRequestException('Owned media must be a url or base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendPtv({ instanceName }: InstanceDto, data: SendPtvDto, file?: any) {
|
public async sendPtv({ instanceName }: InstanceDto, data: SendPtvDto, file?: any) {
|
||||||
|
this.logger.log(`[sendPtv] [${instanceName}] - Iniciando envio de vídeo (PTV)...`);
|
||||||
|
this.logger.debug(`[sendPtv] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (file || isURL(data?.video) || isBase64(data?.video)) {
|
if (file || isURL(data?.video) || isBase64(data?.video)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].ptvMessage(data, file);
|
const result = await this.waMonitor.waInstances[instanceName].ptvMessage(data, file);
|
||||||
|
this.logger.log(`[sendPtv] [${instanceName}] - Envio de vídeo (PTV) concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendPtv] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.error(
|
||||||
|
`[sendPtv] [${instanceName}] - Falha: Vídeo deve ser uma URL ou base64.`
|
||||||
|
);
|
||||||
throw new BadRequestException('Owned media must be a url or base64');
|
throw new BadRequestException('Owned media must be a url or base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto, file?: any) {
|
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto, file?: any) {
|
||||||
|
this.logger.log(`[sendSticker] [${instanceName}] - Iniciando envio de sticker...`);
|
||||||
|
this.logger.debug(`[sendSticker] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (file || isURL(data.sticker) || isBase64(data.sticker)) {
|
if (file || isURL(data.sticker) || isBase64(data.sticker)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].mediaSticker(data, file);
|
const result = await this.waMonitor.waInstances[instanceName].mediaSticker(data, file);
|
||||||
|
this.logger.log(`[sendSticker] [${instanceName}] - Envio de sticker concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendSticker] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.error(
|
||||||
|
`[sendSticker] [${instanceName}] - Falha: Sticker deve ser uma URL ou base64.`
|
||||||
|
);
|
||||||
throw new BadRequestException('Owned media must be a url or base64');
|
throw new BadRequestException('Owned media must be a url or base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto, file?: any) {
|
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto, file?: any) {
|
||||||
|
this.logger.log(`[sendWhatsAppAudio] [${instanceName}] - Iniciando envio de áudio...`);
|
||||||
|
this.logger.debug(`[sendWhatsAppAudio] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (file?.buffer || isURL(data.audio) || isBase64(data.audio)) {
|
if (file?.buffer || isURL(data.audio) || isBase64(data.audio)) {
|
||||||
// Si file existe y tiene buffer, o si es una URL o Base64, continúa
|
const result = await this.waMonitor.waInstances[instanceName].audioWhatsapp(data, file);
|
||||||
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data, file);
|
this.logger.log(`[sendWhatsAppAudio] [${instanceName}] - Envio de áudio concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendWhatsAppAudio] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
return result;
|
||||||
} else {
|
} else {
|
||||||
console.error('El archivo no tiene buffer o el audio no es una URL o Base64 válida');
|
this.logger.error(
|
||||||
throw new BadRequestException('Owned media must be a url, base64, or valid file with buffer');
|
`[sendWhatsAppAudio] [${instanceName}] - Falha: O arquivo não possui buffer, ou o áudio não é uma URL/base64 válida.`
|
||||||
|
);
|
||||||
|
throw new BadRequestException(
|
||||||
|
'Owned media must be a url, base64, or valid file with buffer'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonsDto) {
|
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonsDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].buttonMessage(data);
|
this.logger.log(`[sendButtons] [${instanceName}] - Iniciando envio de botões...`);
|
||||||
|
this.logger.debug(`[sendButtons] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].buttonMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendButtons] [${instanceName}] - Envio de botões concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendButtons] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
|
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].locationMessage(data);
|
this.logger.log(`[sendLocation] [${instanceName}] - Iniciando envio de localização...`);
|
||||||
|
this.logger.debug(`[sendLocation] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].locationMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendLocation] [${instanceName}] - Envio de localização concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendLocation] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
|
public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].listMessage(data);
|
this.logger.log(`[sendList] [${instanceName}] - Iniciando envio de lista...`);
|
||||||
|
this.logger.debug(`[sendList] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].listMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendList] [${instanceName}] - Envio de lista concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendList] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
|
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].contactMessage(data);
|
this.logger.log(`[sendContact] [${instanceName}] - Iniciando envio de contato...`);
|
||||||
|
this.logger.debug(`[sendContact] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].contactMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendContact] [${instanceName}] - Envio de contato concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendContact] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
||||||
|
this.logger.log(`[sendReaction] [${instanceName}] - Iniciando envio de reação...`);
|
||||||
|
this.logger.debug(`[sendReaction] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
if (!data.reaction.match(/[^()\w\sà-ú"-+]+/)) {
|
if (!data.reaction.match(/[^()\w\sà-ú"-+]+/)) {
|
||||||
|
this.logger.error(`[sendReaction] [${instanceName}] - Falha: "reaction" deve ser um emoji.`);
|
||||||
throw new BadRequestException('"reaction" must be an emoji');
|
throw new BadRequestException('"reaction" must be an emoji');
|
||||||
}
|
}
|
||||||
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendReaction] [${instanceName}] - Envio de reação concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendReaction] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
|
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
|
||||||
return await this.waMonitor.waInstances[instanceName].pollMessage(data);
|
this.logger.log(`[sendPoll] [${instanceName}] - Iniciando envio de enquete (poll)...`);
|
||||||
|
this.logger.debug(`[sendPoll] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].pollMessage(data);
|
||||||
|
|
||||||
|
this.logger.log(`[sendPoll] [${instanceName}] - Envio de enquete concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendPoll] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto, file?: any) {
|
public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto, file?: any) {
|
||||||
return await this.waMonitor.waInstances[instanceName].statusMessage(data, file);
|
this.logger.log(`[sendStatus] [${instanceName}] - Iniciando envio de Status...`);
|
||||||
|
this.logger.debug(`[sendStatus] [${instanceName}] - Dados de envio: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const result = await this.waMonitor.waInstances[instanceName].statusMessage(data, file);
|
||||||
|
|
||||||
|
this.logger.log(`[sendStatus] [${instanceName}] - Envio de Status concluído com sucesso.`);
|
||||||
|
this.logger.debug(`[sendStatus] [${instanceName}] - Resultado: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,14 @@ export class Metadata {
|
|||||||
|
|
||||||
export class SendTextDto extends Metadata {
|
export class SendTextDto extends Metadata {
|
||||||
text: string;
|
text: string;
|
||||||
|
// number: string; // WhatsApp ou 'webwidget:...'
|
||||||
|
// delay?: number;
|
||||||
|
// quoted?: any;
|
||||||
|
// linkPreview?: boolean;
|
||||||
|
// mentionsEveryOne?: boolean;
|
||||||
|
// mentioned?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SendPresence extends Metadata {
|
export class SendPresence extends Metadata {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
import { MediaMessage, Options, SendAudioDto, SendMediaDto, SendTextDto } from '@api/dto/sendMessage.dto';
|
import {
|
||||||
|
MediaMessage,
|
||||||
|
Options,
|
||||||
|
SendAudioDto,
|
||||||
|
SendMediaDto,
|
||||||
|
SendTextDto,
|
||||||
|
} from '@api/dto/sendMessage.dto';
|
||||||
import { ProviderFiles } from '@api/provider/sessions';
|
import { ProviderFiles } from '@api/provider/sessions';
|
||||||
import { PrismaRepository } from '@api/repository/repository.service';
|
import { PrismaRepository } from '@api/repository/repository.service';
|
||||||
import { chatbotController } from '@api/server.module';
|
import { chatbotController } from '@api/server.module';
|
||||||
@ -6,7 +12,10 @@ import { CacheService } from '@api/services/cache.service';
|
|||||||
import { ChannelStartupService } from '@api/services/channel.service';
|
import { ChannelStartupService } from '@api/services/channel.service';
|
||||||
import { Events, wa } from '@api/types/wa.types';
|
import { Events, wa } from '@api/types/wa.types';
|
||||||
import { Chatwoot, ConfigService, Openai } from '@config/env.config';
|
import { Chatwoot, ConfigService, Openai } from '@config/env.config';
|
||||||
import { BadRequestException, InternalServerErrorException } from '@exceptions';
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
InternalServerErrorException,
|
||||||
|
} from '@exceptions';
|
||||||
import { status } from '@utils/renderStatus';
|
import { status } from '@utils/renderStatus';
|
||||||
import { isURL } from 'class-validator';
|
import { isURL } from 'class-validator';
|
||||||
import EventEmitter2 from 'eventemitter2';
|
import EventEmitter2 from 'eventemitter2';
|
||||||
@ -36,14 +45,25 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
public mobile: boolean;
|
public mobile: boolean;
|
||||||
|
|
||||||
public get connectionStatus() {
|
public get connectionStatus() {
|
||||||
|
this.logger.log('[connectionStatus] Retornando estado da conexão');
|
||||||
return this.stateConnection;
|
return this.stateConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async closeClient() {
|
public async closeClient() {
|
||||||
|
this.logger.log('[closeClient] Encerrando cliente...');
|
||||||
|
try {
|
||||||
this.stateConnection = { state: 'close' };
|
this.stateConnection = { state: 'close' };
|
||||||
|
this.logger.debug('[closeClient] stateConnection atualizado para "close"');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(
|
||||||
|
`[closeClient] Erro ao tentar fechar o cliente: ${error?.toString()}`,
|
||||||
|
);
|
||||||
|
throw new InternalServerErrorException(error?.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get qrCode(): wa.QrCode {
|
public get qrCode(): wa.QrCode {
|
||||||
|
this.logger.log('[qrCode] Obtendo informações do QR Code...');
|
||||||
return {
|
return {
|
||||||
pairingCode: this.instance.qrcode?.pairingCode,
|
pairingCode: this.instance.qrcode?.pairingCode,
|
||||||
code: this.instance.qrcode?.code,
|
code: this.instance.qrcode?.code,
|
||||||
@ -53,10 +73,14 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async logoutInstance() {
|
public async logoutInstance() {
|
||||||
|
this.logger.log('[logoutInstance] Realizando logout da instância...');
|
||||||
await this.closeClient();
|
await this.closeClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async profilePicture(number: string) {
|
public async profilePicture(number: string) {
|
||||||
|
this.logger.log(
|
||||||
|
`[profilePicture] Obtendo foto de perfil para o número: ${number}`,
|
||||||
|
);
|
||||||
const jid = this.createJid(number);
|
const jid = this.createJid(number);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -66,35 +90,50 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getProfileName() {
|
public async getProfileName() {
|
||||||
|
this.logger.log('[getProfileName] Método não implementado...');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async profilePictureUrl() {
|
public async profilePictureUrl() {
|
||||||
|
this.logger.log('[profilePictureUrl] Método não implementado...');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getProfileStatus() {
|
public async getProfileStatus() {
|
||||||
|
this.logger.log('[getProfileStatus] Método não implementado...');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async connectToWhatsapp(data?: any): Promise<any> {
|
public async connectToWhatsapp(data?: any): Promise<any> {
|
||||||
if (!data) return;
|
this.logger.log('[connectToWhatsapp] Iniciando conexão com o Whatsapp...');
|
||||||
|
if (!data) {
|
||||||
|
this.logger.warn('[connectToWhatsapp] Nenhum dado recebido. Encerrando...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.debug('[connectToWhatsapp] Carregando Chatwoot...');
|
||||||
this.loadChatwoot();
|
this.loadChatwoot();
|
||||||
|
|
||||||
|
this.logger.debug('[connectToWhatsapp] Chamando eventHandler...');
|
||||||
this.eventHandler(data);
|
this.eventHandler(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(
|
||||||
|
`[connectToWhatsapp] Erro ao conectar ao Whatsapp: ${error?.toString()}`,
|
||||||
|
);
|
||||||
throw new InternalServerErrorException(error?.toString());
|
throw new InternalServerErrorException(error?.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async eventHandler(received: any) {
|
protected async eventHandler(received: any) {
|
||||||
|
this.logger.log('[eventHandler] Iniciando tratamento de evento...');
|
||||||
try {
|
try {
|
||||||
let messageRaw: any;
|
let messageRaw: any;
|
||||||
|
|
||||||
if (received.message) {
|
if (received.message) {
|
||||||
|
this.logger.debug(
|
||||||
|
`[eventHandler] Mensagem recebida: ${JSON.stringify(received)}`,
|
||||||
|
);
|
||||||
const key = {
|
const key = {
|
||||||
id: received.key.id || v4(),
|
id: received.key.id || v4(),
|
||||||
remoteJid: received.key.remoteJid,
|
remoteJid: received.key.remoteJid,
|
||||||
@ -110,8 +149,19 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`[eventHandler] Montando objeto messageRaw: ${JSON.stringify(
|
||||||
|
messageRaw,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verifica OpenAI
|
||||||
if (this.configService.get<Openai>('OPENAI').ENABLED) {
|
if (this.configService.get<Openai>('OPENAI').ENABLED) {
|
||||||
const openAiDefaultSettings = await this.prismaRepository.openaiSetting.findFirst({
|
this.logger.debug(
|
||||||
|
'[eventHandler] Verificando configurações do OpenAI...',
|
||||||
|
);
|
||||||
|
const openAiDefaultSettings =
|
||||||
|
await this.prismaRepository.openaiSetting.findFirst({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
},
|
},
|
||||||
@ -126,6 +176,9 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
openAiDefaultSettings.speechToText &&
|
openAiDefaultSettings.speechToText &&
|
||||||
received?.message?.audioMessage
|
received?.message?.audioMessage
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug(
|
||||||
|
'[eventHandler] Realizando speech-to-text no áudio...',
|
||||||
|
);
|
||||||
messageRaw.message.speechToText = await this.openaiService.speechToText(
|
messageRaw.message.speechToText = await this.openaiService.speechToText(
|
||||||
openAiDefaultSettings.OpenaiCreds,
|
openAiDefaultSettings.OpenaiCreds,
|
||||||
received,
|
received,
|
||||||
@ -134,52 +187,83 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log(messageRaw);
|
this.logger.log(`[eventHandler] messageRaw final: ${JSON.stringify(messageRaw)}`);
|
||||||
|
|
||||||
this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
|
this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
|
||||||
|
|
||||||
|
this.logger.debug('[eventHandler] Emitindo chatbotController...');
|
||||||
await chatbotController.emit({
|
await chatbotController.emit({
|
||||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
instance: {
|
||||||
|
instanceName: this.instance.name,
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
},
|
||||||
remoteJid: messageRaw.key.remoteJid,
|
remoteJid: messageRaw.key.remoteJid,
|
||||||
msg: messageRaw,
|
msg: messageRaw,
|
||||||
pushName: messageRaw.pushName,
|
pushName: messageRaw.pushName,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
if (
|
||||||
|
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||||
|
this.localChatwoot?.enabled
|
||||||
|
) {
|
||||||
|
this.logger.debug('[eventHandler] Enviando evento para Chatwoot...');
|
||||||
const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
|
const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
|
||||||
Events.MESSAGES_UPSERT,
|
Events.MESSAGES_UPSERT,
|
||||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
{
|
||||||
|
instanceName: this.instance.name,
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
},
|
||||||
messageRaw,
|
messageRaw,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (chatwootSentMessage?.id) {
|
if (chatwootSentMessage?.id) {
|
||||||
|
this.logger.debug(
|
||||||
|
`[eventHandler] chatwootSentMessage criado com ID: ${chatwootSentMessage.id}`,
|
||||||
|
);
|
||||||
messageRaw.chatwootMessageId = chatwootSentMessage.id;
|
messageRaw.chatwootMessageId = chatwootSentMessage.id;
|
||||||
messageRaw.chatwootInboxId = chatwootSentMessage.id;
|
messageRaw.chatwootInboxId = chatwootSentMessage.id;
|
||||||
messageRaw.chatwootConversationId = chatwootSentMessage.id;
|
messageRaw.chatwootConversationId = chatwootSentMessage.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[eventHandler] Salvando mensagem no Prisma...');
|
||||||
await this.prismaRepository.message.create({
|
await this.prismaRepository.message.create({
|
||||||
data: messageRaw,
|
data: messageRaw,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug('[eventHandler] Atualizando contato...');
|
||||||
await this.updateContact({
|
await this.updateContact({
|
||||||
remoteJid: messageRaw.key.remoteJid,
|
remoteJid: messageRaw.key.remoteJid,
|
||||||
pushName: messageRaw.key.fromMe ? '' : messageRaw.key.fromMe == null ? '' : received.pushName,
|
pushName: messageRaw.key.fromMe
|
||||||
|
? ''
|
||||||
|
: messageRaw.key.fromMe == null
|
||||||
|
? ''
|
||||||
|
: received.pushName,
|
||||||
profilePicUrl: received.profilePicUrl,
|
profilePicUrl: received.profilePicUrl,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(`[eventHandler] Erro: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateContact(data: { remoteJid: string; pushName?: string; profilePicUrl?: string }) {
|
private async updateContact(data: {
|
||||||
|
remoteJid: string;
|
||||||
|
pushName?: string;
|
||||||
|
profilePicUrl?: string;
|
||||||
|
}) {
|
||||||
|
this.logger.log(
|
||||||
|
`[updateContact] Atualizando ou criando contato para: ${data.remoteJid}`,
|
||||||
|
);
|
||||||
|
try {
|
||||||
const contact = await this.prismaRepository.contact.findFirst({
|
const contact = await this.prismaRepository.contact.findFirst({
|
||||||
where: { instanceId: this.instanceId, remoteJid: data.remoteJid },
|
where: { instanceId: this.instanceId, remoteJid: data.remoteJid },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (contact) {
|
if (contact) {
|
||||||
|
this.logger.debug(
|
||||||
|
`[updateContact] Contato já existe. Atualizando...: ${contact.remoteJid}`,
|
||||||
|
);
|
||||||
const contactRaw: any = {
|
const contactRaw: any = {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
pushName: data?.pushName,
|
pushName: data?.pushName,
|
||||||
@ -189,7 +273,11 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw);
|
this.sendDataWebhook(Events.CONTACTS_UPDATE, contactRaw);
|
||||||
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
if (
|
||||||
|
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||||
|
this.localChatwoot?.enabled
|
||||||
|
) {
|
||||||
|
this.logger.debug('[updateContact] Atualizando contato no Chatwoot...');
|
||||||
await this.chatwootService.eventWhatsapp(
|
await this.chatwootService.eventWhatsapp(
|
||||||
Events.CONTACTS_UPDATE,
|
Events.CONTACTS_UPDATE,
|
||||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||||
@ -197,6 +285,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[updateContact] Atualizando contato no Prisma...');
|
||||||
await this.prismaRepository.contact.updateMany({
|
await this.prismaRepository.contact.updateMany({
|
||||||
where: { remoteJid: contact.remoteJid, instanceId: this.instanceId },
|
where: { remoteJid: contact.remoteJid, instanceId: this.instanceId },
|
||||||
data: contactRaw,
|
data: contactRaw,
|
||||||
@ -204,6 +293,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[updateContact] Contato não encontrado. Criando novo...');
|
||||||
const contactRaw: any = {
|
const contactRaw: any = {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
pushName: data?.pushName,
|
pushName: data?.pushName,
|
||||||
@ -222,6 +312,9 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (chat) {
|
if (chat) {
|
||||||
|
this.logger.debug(
|
||||||
|
`[updateContact] Chat já existe para este contato. Atualizando...: ${chat.remoteJid}`,
|
||||||
|
);
|
||||||
const chatRaw: any = {
|
const chatRaw: any = {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -233,8 +326,10 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
where: { remoteJid: chat.remoteJid },
|
where: { remoteJid: chat.remoteJid },
|
||||||
data: chatRaw,
|
data: chatRaw,
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
|
this.logger.debug(
|
||||||
|
'[updateContact] Nenhum chat encontrado para este contato. Criando novo...',
|
||||||
|
);
|
||||||
const chatRaw: any = {
|
const chatRaw: any = {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -246,36 +341,68 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
data: chatRaw,
|
data: chatRaw,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`[updateContact] Erro ao atualizar/criar contato: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async sendMessageWithTyping(number: string, message: any, options?: Options, isIntegration = false) {
|
protected async sendMessageWithTyping(
|
||||||
|
number: string, // remoteJid
|
||||||
|
message: any,
|
||||||
|
options?: Options,
|
||||||
|
isIntegration = false,
|
||||||
|
) {
|
||||||
|
this.logger.log(`[sendMessageWithTyping] Enviando mensagem para: ${number}`);
|
||||||
|
this.logger.debug(
|
||||||
|
`[sendMessageWithTyping] Mensagem: ${JSON.stringify(message)}, Options: ${JSON.stringify(
|
||||||
|
options,
|
||||||
|
)}, isIntegration: ${isIntegration}`,
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
let quoted: any;
|
let quoted: any;
|
||||||
let webhookUrl: any;
|
let webhookUrl: any;
|
||||||
|
|
||||||
if (options?.quoted) {
|
if (options?.quoted) {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Opção quoted detectada...');
|
||||||
const m = options?.quoted;
|
const m = options?.quoted;
|
||||||
|
|
||||||
const msg = m?.key;
|
const msg = m?.key;
|
||||||
|
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
|
this.logger.error('[sendMessageWithTyping] Mensagem de citação não encontrada!');
|
||||||
throw 'Message not found';
|
throw 'Message not found';
|
||||||
}
|
}
|
||||||
|
|
||||||
quoted = msg;
|
quoted = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.delay) {
|
if (options?.delay) {
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] Aguardando delay de ${options.delay}ms...`);
|
||||||
await new Promise((resolve) => setTimeout(resolve, options.delay));
|
await new Promise((resolve) => setTimeout(resolve, options.delay));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options?.webhookUrl) {
|
if (options?.webhookUrl) {
|
||||||
|
this.logger.debug(
|
||||||
|
`[sendMessageWithTyping] Usando webhookUrl customizado: ${options.webhookUrl}`,
|
||||||
|
);
|
||||||
webhookUrl = options.webhookUrl;
|
webhookUrl = options.webhookUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageId = v4();
|
const messageId = v4();
|
||||||
|
this.logger.debug(
|
||||||
|
`[sendMessageWithTyping] Gerando UUID para mensagem: ${messageId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// debug message
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] Mensagem a ser enviada de numero: ${number}`);
|
||||||
|
this.logger.debug(
|
||||||
|
`[sendMessageWithTyping] Mensagem a ser enviada: ${JSON.stringify(message)}`,
|
||||||
|
);
|
||||||
let messageRaw: any = {
|
let messageRaw: any = {
|
||||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
key: {
|
||||||
|
fromMe: true,
|
||||||
|
id: messageId,
|
||||||
|
remoteJid: number,
|
||||||
|
},
|
||||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||||
webhookUrl,
|
webhookUrl,
|
||||||
source: 'unknown',
|
source: 'unknown',
|
||||||
@ -283,7 +410,22 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
status: status[1],
|
status: status[1],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Salvando chatwootConversationId para mensagens webwidget
|
||||||
|
if (number && number.startsWith('webwidget:')) {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Detectado número webwidget...');
|
||||||
|
const conversationIdStr = number.split(':')[1] || '0';
|
||||||
|
const conversation_id = parseInt(conversationIdStr, 10);
|
||||||
|
messageRaw.source = 'web';
|
||||||
|
messageRaw.chatwootConversationId = conversation_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug messageRaw
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] messageRaw a ser enviada: ${number}`);
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] messageRaw a ser enviada: ${JSON.stringify(messageRaw)}`);
|
||||||
|
|
||||||
|
// Verifica o tipo de mídia para compor a mensagem
|
||||||
if (message?.mediaType === 'image') {
|
if (message?.mediaType === 'image') {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Montando mensagem de imagem...');
|
||||||
messageRaw = {
|
messageRaw = {
|
||||||
...messageRaw,
|
...messageRaw,
|
||||||
message: {
|
message: {
|
||||||
@ -293,6 +435,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
messageType: 'imageMessage',
|
messageType: 'imageMessage',
|
||||||
};
|
};
|
||||||
} else if (message?.mediaType === 'video') {
|
} else if (message?.mediaType === 'video') {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Montando mensagem de vídeo...');
|
||||||
messageRaw = {
|
messageRaw = {
|
||||||
...messageRaw,
|
...messageRaw,
|
||||||
message: {
|
message: {
|
||||||
@ -302,6 +445,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
messageType: 'videoMessage',
|
messageType: 'videoMessage',
|
||||||
};
|
};
|
||||||
} else if (message?.mediaType === 'audio') {
|
} else if (message?.mediaType === 'audio') {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Montando mensagem de áudio...');
|
||||||
messageRaw = {
|
messageRaw = {
|
||||||
...messageRaw,
|
...messageRaw,
|
||||||
message: {
|
message: {
|
||||||
@ -311,6 +455,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
messageType: 'audioMessage',
|
messageType: 'audioMessage',
|
||||||
};
|
};
|
||||||
} else if (message?.mediaType === 'document') {
|
} else if (message?.mediaType === 'document') {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Montando mensagem de documento...');
|
||||||
messageRaw = {
|
messageRaw = {
|
||||||
...messageRaw,
|
...messageRaw,
|
||||||
message: {
|
message: {
|
||||||
@ -320,6 +465,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
messageType: 'documentMessage',
|
messageType: 'documentMessage',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Montando mensagem de texto...');
|
||||||
messageRaw = {
|
messageRaw = {
|
||||||
...messageRaw,
|
...messageRaw,
|
||||||
message: {
|
message: {
|
||||||
@ -330,11 +476,21 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log(messageRaw);
|
this.logger.log(
|
||||||
|
`[sendMessageWithTyping] messageRaw final: ${JSON.stringify(messageRaw)}`,
|
||||||
|
);
|
||||||
|
|
||||||
this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||||
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) {
|
// debug a proxima funcao
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] CHATWOOT: ${this.configService.get<Chatwoot>('CHATWOOT').ENABLED}, LOCAL: ${this.localChatwoot?.enabled}, INTEGRATION: ${isIntegration}`);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||||
|
this.localChatwoot?.enabled &&
|
||||||
|
!isIntegration
|
||||||
|
) {
|
||||||
|
this.logger.debug('[sendMessageWithTyping] Enviando evento SEND_MESSAGE ao Chatwoot...');
|
||||||
this.chatwootService.eventWhatsapp(
|
this.chatwootService.eventWhatsapp(
|
||||||
Events.SEND_MESSAGE,
|
Events.SEND_MESSAGE,
|
||||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||||
@ -342,38 +498,52 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && isIntegration)
|
if (
|
||||||
|
this.configService.get<Chatwoot>('CHATWOOT').ENABLED &&
|
||||||
|
this.localChatwoot?.enabled &&
|
||||||
|
isIntegration
|
||||||
|
) {
|
||||||
|
this.logger.debug(
|
||||||
|
'[sendMessageWithTyping] Emitindo mensagem para chatbotController em modo de integração...',
|
||||||
|
);
|
||||||
await chatbotController.emit({
|
await chatbotController.emit({
|
||||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
||||||
remoteJid: messageRaw.key.remoteJid,
|
remoteJid: messageRaw.key.remoteJid,
|
||||||
msg: messageRaw,
|
msg: messageRaw,
|
||||||
pushName: messageRaw.pushName,
|
pushName: messageRaw.pushName,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug(`[sendMessageWithTyping] Salvando mensagem no Prisma: ${JSON.stringify(messageRaw)}`);
|
||||||
await this.prismaRepository.message.create({
|
await this.prismaRepository.message.create({
|
||||||
data: messageRaw,
|
data: messageRaw,
|
||||||
});
|
});
|
||||||
|
|
||||||
return messageRaw;
|
return messageRaw;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(
|
||||||
|
`[sendMessageWithTyping] Erro ao enviar mensagem para ${number}: ${error}`,
|
||||||
|
);
|
||||||
throw new BadRequestException(error.toString());
|
throw new BadRequestException(error.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async textMessage(data: SendTextDto, isIntegration = false) {
|
public async textMessage(data2: SendTextDto, isIntegration = false) {
|
||||||
|
this.logger.log('[textMessage] Enviando mensagem de texto...');
|
||||||
|
this.logger.debug(`[textMessage] Dados recebidos: ${JSON.stringify(data2)}`);
|
||||||
|
|
||||||
const res = await this.sendMessageWithTyping(
|
const res = await this.sendMessageWithTyping(
|
||||||
data.number,
|
data2.number,
|
||||||
{
|
{
|
||||||
conversation: data.text,
|
conversation: data2.text,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
delay: data?.delay,
|
delay: data2?.delay,
|
||||||
presence: 'composing',
|
presence: 'composing',
|
||||||
quoted: data?.quoted,
|
quoted: data2?.quoted,
|
||||||
linkPreview: data?.linkPreview,
|
linkPreview: data2?.linkPreview,
|
||||||
mentionsEveryOne: data?.mentionsEveryOne,
|
mentionsEveryOne: data2?.mentionsEveryOne,
|
||||||
mentioned: data?.mentioned,
|
mentioned: data2?.mentioned,
|
||||||
},
|
},
|
||||||
isIntegration,
|
isIntegration,
|
||||||
);
|
);
|
||||||
@ -381,18 +551,31 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async prepareMediaMessage(mediaMessage: MediaMessage) {
|
protected async prepareMediaMessage(mediaMessage: MediaMessage) {
|
||||||
|
this.logger.log('[prepareMediaMessage] Preparando mensagem de mídia...');
|
||||||
|
this.logger.debug(
|
||||||
|
`[prepareMediaMessage] Dados recebidos: ${JSON.stringify(mediaMessage)}`,
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) {
|
if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) {
|
||||||
|
this.logger.debug(
|
||||||
|
'[prepareMediaMessage] Definindo filename para documento...',
|
||||||
|
);
|
||||||
const regex = new RegExp(/.*\/(.+?)\./);
|
const regex = new RegExp(/.*\/(.+?)\./);
|
||||||
const arrayMatch = regex.exec(mediaMessage.media);
|
const arrayMatch = regex.exec(mediaMessage.media);
|
||||||
mediaMessage.fileName = arrayMatch[1];
|
mediaMessage.fileName = arrayMatch[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
|
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
|
||||||
|
this.logger.debug(
|
||||||
|
'[prepareMediaMessage] Definindo filename padrão para imagem...',
|
||||||
|
);
|
||||||
mediaMessage.fileName = 'image.png';
|
mediaMessage.fileName = 'image.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
|
if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
|
||||||
|
this.logger.debug(
|
||||||
|
'[prepareMediaMessage] Definindo filename padrão para vídeo...',
|
||||||
|
);
|
||||||
mediaMessage.fileName = 'video.mp4';
|
mediaMessage.fileName = 'video.mp4';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,6 +589,7 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
gifPlayback: false,
|
gifPlayback: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.logger.debug('[prepareMediaMessage] Verificando mimetype...');
|
||||||
if (isURL(mediaMessage.media)) {
|
if (isURL(mediaMessage.media)) {
|
||||||
mimetype = mime.getType(mediaMessage.media);
|
mimetype = mime.getType(mediaMessage.media);
|
||||||
} else {
|
} else {
|
||||||
@ -414,17 +598,32 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
prepareMedia.mimetype = mimetype;
|
prepareMedia.mimetype = mimetype;
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`[prepareMediaMessage] Retornando objeto de mídia preparado: ${JSON.stringify(
|
||||||
|
prepareMedia,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
return prepareMedia;
|
return prepareMedia;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(
|
||||||
|
`[prepareMediaMessage] Erro ao preparar mensagem de mídia: ${error}`,
|
||||||
|
);
|
||||||
throw new InternalServerErrorException(error?.toString() || error);
|
throw new InternalServerErrorException(error?.toString() || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) {
|
public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) {
|
||||||
|
this.logger.log('[mediaMessage] Enviando mensagem de mídia...');
|
||||||
|
this.logger.debug(`[mediaMessage] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
try {
|
||||||
const mediaData: SendMediaDto = { ...data };
|
const mediaData: SendMediaDto = { ...data };
|
||||||
|
|
||||||
if (file) mediaData.media = file.buffer.toString('base64');
|
if (file) {
|
||||||
|
this.logger.debug(
|
||||||
|
'[mediaMessage] Convertendo arquivo em base64 para envio...',
|
||||||
|
);
|
||||||
|
mediaData.media = file.buffer.toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
const message = await this.prepareMediaMessage(mediaData);
|
const message = await this.prepareMediaMessage(mediaData);
|
||||||
|
|
||||||
@ -443,12 +642,22 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return mediaSent;
|
return mediaSent;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(
|
||||||
|
`[mediaMessage] Erro ao enviar mensagem de mídia: ${error}`,
|
||||||
|
);
|
||||||
|
throw new InternalServerErrorException(error?.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async processAudio(audio: string, number: string) {
|
public async processAudio(audio: string, number: string) {
|
||||||
|
this.logger.log('[processAudio] Processando áudio...');
|
||||||
|
this.logger.debug(`[processAudio] Áudio: ${audio}, Número: ${number}`);
|
||||||
|
try {
|
||||||
number = number.replace(/\D/g, '');
|
number = number.replace(/\D/g, '');
|
||||||
const hash = `${number}-${new Date().getTime()}`;
|
this.logger.debug(`[processAudio] Número formatado: ${number}`);
|
||||||
|
|
||||||
|
const hash = `${number}-${new Date().getTime()}`;
|
||||||
let mimetype: string;
|
let mimetype: string;
|
||||||
|
|
||||||
const prepareMedia: any = {
|
const prepareMedia: any = {
|
||||||
@ -464,17 +673,34 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prepareMedia.mimetype = mimetype;
|
prepareMedia.mimetype = mimetype;
|
||||||
|
this.logger.debug(
|
||||||
|
`[processAudio] Retornando objeto de mídia de áudio: ${JSON.stringify(
|
||||||
|
prepareMedia,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
|
||||||
return prepareMedia;
|
return prepareMedia;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(
|
||||||
|
`[processAudio] Erro ao processar áudio: ${error.toString()}`,
|
||||||
|
);
|
||||||
|
throw new InternalServerErrorException(error?.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
|
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
|
||||||
|
this.logger.log('[audioWhatsapp] Enviando áudio via Whatsapp...');
|
||||||
|
this.logger.debug(`[audioWhatsapp] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
try {
|
||||||
const mediaData: SendAudioDto = { ...data };
|
const mediaData: SendAudioDto = { ...data };
|
||||||
|
|
||||||
if (file?.buffer) {
|
if (file?.buffer) {
|
||||||
|
this.logger.debug('[audioWhatsapp] Convertendo buffer em base64...');
|
||||||
mediaData.audio = file.buffer.toString('base64');
|
mediaData.audio = file.buffer.toString('base64');
|
||||||
} else {
|
} else {
|
||||||
console.error('El archivo o buffer no est<73> definido correctamente.');
|
this.logger.error(
|
||||||
|
'[audioWhatsapp] O arquivo ou buffer não está definido corretamente.',
|
||||||
|
);
|
||||||
throw new Error('File or buffer is undefined.');
|
throw new Error('File or buffer is undefined.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,153 +721,206 @@ export class EvolutionStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return audioSent;
|
return audioSent;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`[audioWhatsapp] Erro ao enviar áudio: ${error}`);
|
||||||
|
throw new InternalServerErrorException(error?.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async buttonMessage() {
|
public async buttonMessage() {
|
||||||
|
this.logger.warn('[buttonMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async locationMessage() {
|
public async locationMessage() {
|
||||||
|
this.logger.warn('[locationMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async listMessage() {
|
public async listMessage() {
|
||||||
|
this.logger.warn('[listMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async templateMessage() {
|
public async templateMessage() {
|
||||||
|
this.logger.warn('[templateMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async contactMessage() {
|
public async contactMessage() {
|
||||||
|
this.logger.warn('[contactMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async reactionMessage() {
|
public async reactionMessage() {
|
||||||
|
this.logger.warn('[reactionMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async getBase64FromMediaMessage() {
|
public async getBase64FromMediaMessage() {
|
||||||
|
this.logger.warn('[getBase64FromMediaMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async deleteMessage() {
|
public async deleteMessage() {
|
||||||
|
this.logger.warn('[deleteMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async mediaSticker() {
|
public async mediaSticker() {
|
||||||
|
this.logger.warn('[mediaSticker] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async pollMessage() {
|
public async pollMessage() {
|
||||||
|
this.logger.warn('[pollMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async statusMessage() {
|
public async statusMessage() {
|
||||||
|
this.logger.warn('[statusMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async reloadConnection() {
|
public async reloadConnection() {
|
||||||
|
this.logger.warn('[reloadConnection] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async whatsappNumber() {
|
public async whatsappNumber() {
|
||||||
|
this.logger.warn('[whatsappNumber] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async markMessageAsRead() {
|
public async markMessageAsRead() {
|
||||||
|
this.logger.warn('[markMessageAsRead] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async archiveChat() {
|
public async archiveChat() {
|
||||||
|
this.logger.warn('[archiveChat] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async markChatUnread() {
|
public async markChatUnread() {
|
||||||
|
this.logger.warn('[markChatUnread] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async fetchProfile() {
|
public async fetchProfile() {
|
||||||
|
this.logger.warn('[fetchProfile] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async offerCall() {
|
public async offerCall() {
|
||||||
|
this.logger.warn('[offerCall] Método não disponível no WhatsApp Business API');
|
||||||
throw new BadRequestException('Method not available on WhatsApp Business API');
|
throw new BadRequestException('Method not available on WhatsApp Business API');
|
||||||
}
|
}
|
||||||
public async sendPresence() {
|
public async sendPresence() {
|
||||||
|
this.logger.warn('[sendPresence] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async setPresence() {
|
public async setPresence() {
|
||||||
|
this.logger.warn('[setPresence] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async fetchPrivacySettings() {
|
public async fetchPrivacySettings() {
|
||||||
|
this.logger.warn('[fetchPrivacySettings] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updatePrivacySettings() {
|
public async updatePrivacySettings() {
|
||||||
|
this.logger.warn('[updatePrivacySettings] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async fetchBusinessProfile() {
|
public async fetchBusinessProfile() {
|
||||||
|
this.logger.warn('[fetchBusinessProfile] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateProfileName() {
|
public async updateProfileName() {
|
||||||
|
this.logger.warn('[updateProfileName] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateProfileStatus() {
|
public async updateProfileStatus() {
|
||||||
|
this.logger.warn('[updateProfileStatus] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateProfilePicture() {
|
public async updateProfilePicture() {
|
||||||
|
this.logger.warn('[updateProfilePicture] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async removeProfilePicture() {
|
public async removeProfilePicture() {
|
||||||
|
this.logger.warn('[removeProfilePicture] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async blockUser() {
|
public async blockUser() {
|
||||||
|
this.logger.warn('[blockUser] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateMessage() {
|
public async updateMessage() {
|
||||||
|
this.logger.warn('[updateMessage] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async createGroup() {
|
public async createGroup() {
|
||||||
|
this.logger.warn('[createGroup] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateGroupPicture() {
|
public async updateGroupPicture() {
|
||||||
|
this.logger.warn('[updateGroupPicture] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateGroupSubject() {
|
public async updateGroupSubject() {
|
||||||
|
this.logger.warn('[updateGroupSubject] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateGroupDescription() {
|
public async updateGroupDescription() {
|
||||||
|
this.logger.warn('[updateGroupDescription] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async findGroup() {
|
public async findGroup() {
|
||||||
|
this.logger.warn('[findGroup] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async fetchAllGroups() {
|
public async fetchAllGroups() {
|
||||||
|
this.logger.warn('[fetchAllGroups] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async inviteCode() {
|
public async inviteCode() {
|
||||||
|
this.logger.warn('[inviteCode] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async inviteInfo() {
|
public async inviteInfo() {
|
||||||
|
this.logger.warn('[inviteInfo] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async sendInvite() {
|
public async sendInvite() {
|
||||||
|
this.logger.warn('[sendInvite] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async acceptInviteCode() {
|
public async acceptInviteCode() {
|
||||||
|
this.logger.warn('[acceptInviteCode] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async revokeInviteCode() {
|
public async revokeInviteCode() {
|
||||||
|
this.logger.warn('[revokeInviteCode] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async findParticipants() {
|
public async findParticipants() {
|
||||||
|
this.logger.warn('[findParticipants] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateGParticipant() {
|
public async updateGParticipant() {
|
||||||
|
this.logger.warn('[updateGParticipant] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async updateGSetting() {
|
public async updateGSetting() {
|
||||||
|
this.logger.warn('[updateGSetting] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async toggleEphemeral() {
|
public async toggleEphemeral() {
|
||||||
|
this.logger.warn('[toggleEphemeral] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async leaveGroup() {
|
public async leaveGroup() {
|
||||||
|
this.logger.warn('[leaveGroup] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async fetchLabels() {
|
public async fetchLabels() {
|
||||||
|
this.logger.warn('[fetchLabels] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async handleLabel() {
|
public async handleLabel() {
|
||||||
|
this.logger.warn('[handleLabel] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async receiveMobileCode() {
|
public async receiveMobileCode() {
|
||||||
|
this.logger.warn('[receiveMobileCode] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
public async fakeCall() {
|
public async fakeCall() {
|
||||||
|
this.logger.warn('[fakeCall] Método não disponível no Evolution Channel');
|
||||||
throw new BadRequestException('Method not available on Evolution Channel');
|
throw new BadRequestException('Method not available on Evolution Channel');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,39 +1,68 @@
|
|||||||
|
import { Logger } from '@config/logger.config';
|
||||||
import { PrismaRepository } from '@api/repository/repository.service';
|
import { PrismaRepository } from '@api/repository/repository.service';
|
||||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
import { Logger } from '@config/logger.config';
|
|
||||||
|
|
||||||
import { ChannelController, ChannelControllerInterface } from '../channel.controller';
|
import { ChannelController, ChannelControllerInterface } from '../channel.controller';
|
||||||
|
|
||||||
export class EvolutionController extends ChannelController implements ChannelControllerInterface {
|
export class EvolutionController extends ChannelController implements ChannelControllerInterface {
|
||||||
private readonly logger = new Logger('EvolutionController');
|
private readonly logger = new Logger('EvolutionController');
|
||||||
|
|
||||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
// Flag para indicar se a integração está habilitada
|
||||||
super(prismaRepository, waMonitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
integrationEnabled: boolean;
|
integrationEnabled: boolean;
|
||||||
|
|
||||||
public async receiveWebhook(data: any) {
|
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||||
const numberId = data.numberId;
|
super(prismaRepository, waMonitor);
|
||||||
|
this.logger.debug('EvolutionController -> constructor called');
|
||||||
|
|
||||||
|
// Exemplo de log ao definir flags ou propriedades adicionais
|
||||||
|
this.integrationEnabled = true;
|
||||||
|
this.logger.debug(`EvolutionController -> integrationEnabled set to: ${this.integrationEnabled}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async receiveWebhook(data: any) {
|
||||||
|
this.logger.debug('EvolutionController -> receiveWebhook called');
|
||||||
|
this.logger.debug(`EvolutionController -> receiveWebhook -> data: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
// Extraindo número de identificação
|
||||||
|
const numberId = data.numberId;
|
||||||
|
this.logger.debug(`EvolutionController -> receiveWebhook -> numberId: ${numberId}`);
|
||||||
|
|
||||||
|
// Validando se o numberId foi informado
|
||||||
if (!numberId) {
|
if (!numberId) {
|
||||||
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Log antes de buscar a instância
|
||||||
|
this.logger.debug(`EvolutionController -> Looking for instance with numberId: ${numberId}`);
|
||||||
const instance = await this.prismaRepository.instance.findFirst({
|
const instance = await this.prismaRepository.instance.findFirst({
|
||||||
where: { number: numberId },
|
where: { number: numberId },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Log do resultado da busca
|
||||||
|
this.logger.debug(`EvolutionController -> Prisma instance result: ${JSON.stringify(instance)}`);
|
||||||
|
|
||||||
|
// Validando se a instância foi encontrada
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log antes de tentar conectar
|
||||||
|
this.logger.debug(`EvolutionController -> Connecting to WhatsApp instance: ${instance.name}`);
|
||||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||||
|
this.logger.debug('EvolutionController -> Successfully connected to WhatsApp instance');
|
||||||
|
|
||||||
|
// Retorno de sucesso
|
||||||
|
this.logger.debug('EvolutionController -> receiveWebhook -> returning success');
|
||||||
return {
|
return {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
};
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`EvolutionController -> receiveWebhook -> Error: ${error.message}`);
|
||||||
|
this.logger.debug(`EvolutionController -> receiveWebhook -> Stack trace: ${error.stack}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,64 @@
|
|||||||
|
import { Logger } from '@config/logger.config';
|
||||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||||
import { evolutionController } from '@api/server.module';
|
import { evolutionController } from '@api/server.module';
|
||||||
import { ConfigService } from '@config/env.config';
|
import { ConfigService } from '@config/env.config';
|
||||||
import { Router } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
|
|
||||||
export class EvolutionRouter extends RouterBroker {
|
export class EvolutionRouter extends RouterBroker {
|
||||||
constructor(readonly configService: ConfigService) {
|
private readonly logger = new Logger('EvolutionRouter');
|
||||||
super();
|
|
||||||
this.router.post(this.routerPath('webhook/evolution', false), async (req, res) => {
|
|
||||||
const { body } = req;
|
|
||||||
const response = await evolutionController.receiveWebhook(body);
|
|
||||||
|
|
||||||
return res.status(200).json(response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly router: Router = Router();
|
public readonly router: Router = Router();
|
||||||
|
|
||||||
|
constructor(readonly configService: ConfigService) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// Log the initialization of the EvolutionRouter
|
||||||
|
this.logger.debug('[EvolutionRouter] Initializing router...');
|
||||||
|
|
||||||
|
this.router.post(
|
||||||
|
this.routerPath('webhook/evolution', false),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
this.logger.info('[EvolutionRouter] POST /webhook/evolution route called');
|
||||||
|
|
||||||
|
// Log the request body for debugging (cuidado com dados sensíveis)
|
||||||
|
const { body } = req;
|
||||||
|
this.logger.debug(
|
||||||
|
`[EvolutionRouter] Received request body: ${JSON.stringify(this.sanitizeBody(body))}`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug('[EvolutionRouter] Calling evolutionController.receiveWebhook...');
|
||||||
|
const response = await evolutionController.receiveWebhook(body);
|
||||||
|
|
||||||
|
// Log the response from the controller
|
||||||
|
this.logger.debug(
|
||||||
|
`[EvolutionRouter] Response from evolutionController: ${JSON.stringify(response)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug('[EvolutionRouter] Returning 200 with response');
|
||||||
|
return res.status(200).json(response);
|
||||||
|
} catch (error: any) {
|
||||||
|
// Log the error for debugging
|
||||||
|
this.logger.error(`[EvolutionRouter] Error in POST /webhook/evolution: ${error.message}`);
|
||||||
|
return res.status(500).json({
|
||||||
|
message: 'Internal server error',
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug('[EvolutionRouter] Router setup complete');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters sensitive information from the request body for safe logging.
|
||||||
|
*/
|
||||||
|
private sanitizeBody(body: any): any {
|
||||||
|
// Implement filtering logic to exclude sensitive data
|
||||||
|
const sanitizedBody = { ...body };
|
||||||
|
if (sanitizedBody.password) sanitizedBody.password = '[FILTERED]';
|
||||||
|
if (sanitizedBody.token) sanitizedBody.token = '[FILTERED]';
|
||||||
|
return sanitizedBody;
|
||||||
|
}
|
||||||
}
|
}
|
@ -270,7 +270,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
public async getProfileStatus() {
|
public async getProfileStatus() {
|
||||||
const status = await this.client.fetchStatus(this.instance.wuid);
|
const status = await this.client.fetchStatus(this.instance.wuid);
|
||||||
|
|
||||||
return status?.status;
|
return status?.[0]?.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get profilePictureUrl() {
|
public get profilePictureUrl() {
|
||||||
@ -990,7 +990,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (chatwootImport.getRepositoryMessagesCache(instance) === null) {
|
if (chatwootImport.getRepositoryMessagesCache(instance) === null) {
|
||||||
chatwootImport.setRepositoryMessagesCache(instance, messagesRepository);
|
chatwootImport.setRepositoryMessagesCache(instance, messagesRepository as Set<string>);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const m of messages) {
|
for (const m of messages) {
|
||||||
@ -1785,7 +1785,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
wuid: jid,
|
wuid: jid,
|
||||||
status: (await this.client.fetchStatus(jid))?.status,
|
status: (await this.client.fetchStatus(jid))[0]?.status,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Logger } from '@config/logger.config';
|
||||||
import { InstanceDto } from '@api/dto/instance.dto';
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
import { ChatwootDto } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto';
|
import { ChatwootDto } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto';
|
||||||
import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service';
|
import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service';
|
||||||
@ -10,6 +11,8 @@ import { BadRequestException } from '@exceptions';
|
|||||||
import { isURL } from 'class-validator';
|
import { isURL } from 'class-validator';
|
||||||
|
|
||||||
export class ChatwootController {
|
export class ChatwootController {
|
||||||
|
private readonly logger = new Logger(ChatwootController.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly chatwootService: ChatwootService,
|
private readonly chatwootService: ChatwootService,
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
@ -17,51 +20,85 @@ export class ChatwootController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async createChatwoot(instance: InstanceDto, data: ChatwootDto) {
|
public async createChatwoot(instance: InstanceDto, data: ChatwootDto) {
|
||||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) throw new BadRequestException('Chatwoot is disabled');
|
this.logger.debug(`[createChatwoot] Iniciando criação de Chatwoot para a instância: ${JSON.stringify(instance)}`);
|
||||||
|
this.logger.debug(`[createChatwoot] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const chatwootConfig = this.configService.get<Chatwoot>('CHATWOOT');
|
||||||
|
if (!chatwootConfig.ENABLED) {
|
||||||
|
this.logger.warn('[createChatwoot] Chatwoot está desabilitado. Lançando exceção...');
|
||||||
|
throw new BadRequestException('Chatwoot is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
if (data?.enabled) {
|
if (data?.enabled) {
|
||||||
|
this.logger.debug('[createChatwoot] Validação de dados habilitados...');
|
||||||
|
|
||||||
if (!isURL(data.url, { require_tld: false })) {
|
if (!isURL(data.url, { require_tld: false })) {
|
||||||
|
this.logger.error(`[createChatwoot] URL inválida: ${data.url}`);
|
||||||
throw new BadRequestException('url is not valid');
|
throw new BadRequestException('url is not valid');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.accountId) {
|
if (!data.accountId) {
|
||||||
|
this.logger.error('[createChatwoot] accountId não informado');
|
||||||
throw new BadRequestException('accountId is required');
|
throw new BadRequestException('accountId is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.token) {
|
if (!data.token) {
|
||||||
|
this.logger.error('[createChatwoot] token não informado');
|
||||||
throw new BadRequestException('token is required');
|
throw new BadRequestException('token is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.signMsg !== true && data.signMsg !== false) {
|
if (data.signMsg !== true && data.signMsg !== false) {
|
||||||
|
this.logger.error('[createChatwoot] signMsg inválido ou não informado');
|
||||||
throw new BadRequestException('signMsg is required');
|
throw new BadRequestException('signMsg is required');
|
||||||
}
|
}
|
||||||
if (data.signMsg === false) data.signDelimiter = null;
|
|
||||||
|
if (data.signMsg === false) {
|
||||||
|
this.logger.debug('[createChatwoot] signMsg definido como false, removendo signDelimiter');
|
||||||
|
data.signDelimiter = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.logger.debug('[createChatwoot] Dados informam que Chatwoot não está habilitado (enabled=false ou undefined).');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.nameInbox || data.nameInbox === '') {
|
if (!data.nameInbox || data.nameInbox === '') {
|
||||||
|
this.logger.debug(`[createChatwoot] nameInbox não informado. Usando nome da instância: "${instance.instanceName}"`);
|
||||||
data.nameInbox = instance.instanceName;
|
data.nameInbox = instance.instanceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[createChatwoot] Chamando ChatwootService.create...');
|
||||||
const result = await this.chatwootService.create(instance, data);
|
const result = await this.chatwootService.create(instance, data);
|
||||||
|
this.logger.debug(`[createChatwoot] Retorno de ChatwootService.create: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
this.logger.debug(`[createChatwoot] urlServer obtido: ${urlServer}`);
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
...result,
|
...result,
|
||||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.logger.debug(`[createChatwoot] Retornando resposta final: ${JSON.stringify(response)}`);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findChatwoot(instance: InstanceDto): Promise<ChatwootDto & { webhook_url: string }> {
|
public async findChatwoot(instance: InstanceDto): Promise<ChatwootDto & { webhook_url: string }> {
|
||||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) throw new BadRequestException('Chatwoot is disabled');
|
this.logger.debug(`[findChatwoot] Buscando configurações Chatwoot para a instância: ${JSON.stringify(instance)}`);
|
||||||
|
|
||||||
|
const chatwootConfig = this.configService.get<Chatwoot>('CHATWOOT');
|
||||||
|
if (!chatwootConfig.ENABLED) {
|
||||||
|
this.logger.warn('[findChatwoot] Chatwoot está desabilitado. Lançando exceção...');
|
||||||
|
throw new BadRequestException('Chatwoot is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[findChatwoot] Chamando ChatwootService.find...');
|
||||||
const result = await this.chatwootService.find(instance);
|
const result = await this.chatwootService.find(instance);
|
||||||
|
this.logger.debug(`[findChatwoot] Resposta de ChatwootService.find: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
this.logger.debug(`[findChatwoot] urlServer obtido: ${urlServer}`);
|
||||||
|
|
||||||
if (Object.keys(result || {}).length === 0) {
|
if (Object.keys(result || {}).length === 0) {
|
||||||
|
this.logger.debug('[findChatwoot] Nenhuma configuração encontrada. Retornando default desabilitado.');
|
||||||
return {
|
return {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
url: '',
|
url: '',
|
||||||
@ -78,15 +115,28 @@ export class ChatwootController {
|
|||||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.logger.debug(`[findChatwoot] Resposta final: ${JSON.stringify(response)}`);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async receiveWebhook(instance: InstanceDto, data: any) {
|
public async receiveWebhook(instance: InstanceDto, data: any) {
|
||||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) throw new BadRequestException('Chatwoot is disabled');
|
this.logger.debug(`[receiveWebhook] Recebendo webhook para instância: ${JSON.stringify(instance)}`);
|
||||||
|
this.logger.debug(`[receiveWebhook] Dados recebidos no webhook: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
const chatwootConfig = this.configService.get<Chatwoot>('CHATWOOT');
|
||||||
|
if (!chatwootConfig.ENABLED) {
|
||||||
|
this.logger.warn('[receiveWebhook] Chatwoot está desabilitado. Lançando exceção...');
|
||||||
|
throw new BadRequestException('Chatwoot is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[receiveWebhook] Iniciando configuração de CacheService para Chatwoot...');
|
||||||
const chatwootCache = new CacheService(new CacheEngine(this.configService, ChatwootService.name).getEngine());
|
const chatwootCache = new CacheService(new CacheEngine(this.configService, ChatwootService.name).getEngine());
|
||||||
const chatwootService = new ChatwootService(waMonitor, this.configService, this.prismaRepository, chatwootCache);
|
const chatwootService = new ChatwootService(waMonitor, this.configService, this.prismaRepository, chatwootCache);
|
||||||
|
|
||||||
return chatwootService.receiveWebhook(instance, data);
|
this.logger.debug('[receiveWebhook] Chamando chatwootService.receiveWebhook...');
|
||||||
|
const result = await chatwootService.receiveWebhook(instance, data);
|
||||||
|
this.logger.debug(`[receiveWebhook] Resposta de receiveWebhook: ${JSON.stringify(result)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@ import { WAMonitoringService } from '@api/services/monitor.service';
|
|||||||
import { Integration } from '@api/types/wa.types';
|
import { Integration } from '@api/types/wa.types';
|
||||||
import { ConfigService, Language } from '@config/env.config';
|
import { ConfigService, Language } from '@config/env.config';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
import { IntegrationSession, OpenaiBot, OpenaiCreds, OpenaiSetting } from '@prisma/client';
|
import { IntegrationSession, OpenaiBot, OpenaiCreds, OpenaiSetting, Message } from '@prisma/client';
|
||||||
import { sendTelemetry } from '@utils/sendTelemetry';
|
import { sendTelemetry } from '@utils/sendTelemetry';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { downloadMediaMessage } from 'baileys';
|
import { downloadMediaMessage } from 'baileys';
|
||||||
@ -25,7 +25,12 @@ export class OpenaiService {
|
|||||||
private readonly logger = new Logger('OpenaiService');
|
private readonly logger = new Logger('OpenaiService');
|
||||||
|
|
||||||
private async sendMessageToBot(instance: any, openaiBot: OpenaiBot, remoteJid: string, content: string) {
|
private async sendMessageToBot(instance: any, openaiBot: OpenaiBot, remoteJid: string, content: string) {
|
||||||
|
this.logger.debug('[sendMessageToBot] Enviando mensagem para o bot.');
|
||||||
|
this.logger.debug(`[sendMessageToBot] RemoteJid: ${remoteJid}, Content: ${content}`);
|
||||||
|
|
||||||
|
// System messages
|
||||||
const systemMessages: any = openaiBot.systemMessages;
|
const systemMessages: any = openaiBot.systemMessages;
|
||||||
|
this.logger.debug(`[sendMessageToBot] SystemMessages recuperadas: ${systemMessages}`);
|
||||||
|
|
||||||
const messagesSystem: any[] = systemMessages.map((message) => {
|
const messagesSystem: any[] = systemMessages.map((message) => {
|
||||||
return {
|
return {
|
||||||
@ -34,7 +39,9 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Assistant messages
|
||||||
const assistantMessages: any = openaiBot.assistantMessages;
|
const assistantMessages: any = openaiBot.assistantMessages;
|
||||||
|
this.logger.debug(`[sendMessageToBot] AssistantMessages recuperadas: ${assistantMessages}`);
|
||||||
|
|
||||||
const messagesAssistant: any[] = assistantMessages.map((message) => {
|
const messagesAssistant: any[] = assistantMessages.map((message) => {
|
||||||
return {
|
return {
|
||||||
@ -43,7 +50,9 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User messages
|
||||||
const userMessages: any = openaiBot.userMessages;
|
const userMessages: any = openaiBot.userMessages;
|
||||||
|
this.logger.debug(`[sendMessageToBot] UserMessages recuperadas: ${userMessages}`);
|
||||||
|
|
||||||
const messagesUser: any[] = userMessages.map((message) => {
|
const messagesUser: any[] = userMessages.map((message) => {
|
||||||
return {
|
return {
|
||||||
@ -52,15 +61,18 @@ export class OpenaiService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Imagem messages
|
||||||
const messageData: any = {
|
const messageData: any = {
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: [{ type: 'text', text: content }],
|
content: content,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.isImageMessage(content)) {
|
if (this.isImageMessage(content)) {
|
||||||
|
this.logger.debug('[sendMessageToBot] Identificada mensagem de imagem no texto.');
|
||||||
const contentSplit = content.split('|');
|
const contentSplit = content.split('|');
|
||||||
|
|
||||||
const url = contentSplit[1].split('?')[0];
|
const url = contentSplit[1].split('?')[0];
|
||||||
|
this.logger.debug(`[sendMessageToBot] URL da imagem extraída: ${url}`);
|
||||||
|
|
||||||
messageData.content = [
|
messageData.content = [
|
||||||
{ type: 'text', text: contentSplit[2] || content },
|
{ type: 'text', text: contentSplit[2] || content },
|
||||||
@ -73,23 +85,126 @@ export class OpenaiService {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages: any[] = [...messagesSystem, ...messagesAssistant, ...messagesUser, messageData];
|
// History Mensagens
|
||||||
|
// Define aqui o limite máximo de caracteres do histórico
|
||||||
|
const MAX_HISTORY_CHARS = 30000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrai o texto principal de um objeto de mensagem.
|
||||||
|
* @param msg Mensagem recebida do banco (tipo `Message`).
|
||||||
|
* @returns Texto extraído da mensagem ou um JSON.stringify como fallback.
|
||||||
|
*/
|
||||||
|
function extrairTextoDaMensagem(msg: Message): string {
|
||||||
|
// Se não houver conteúdo
|
||||||
|
if (!msg?.message) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso seja mensagem de texto simples
|
||||||
|
if (typeof msg.message === 'object' && 'conversation' in msg.message) {
|
||||||
|
return String(msg.message.conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso seja extendedTextMessage
|
||||||
|
if (typeof msg.message === 'object' && (msg.message as any)?.extendedTextMessage?.text) {
|
||||||
|
if (typeof msg.message === 'object' && 'extendedTextMessage' in msg.message) {
|
||||||
|
return (msg.message as any).extendedTextMessage.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso seja imagem com caption
|
||||||
|
if (typeof msg.message === 'object' && 'imageMessage' in msg.message && (msg.message as any).imageMessage?.caption) {
|
||||||
|
return (msg.message as any).imageMessage.caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: retorna o objeto como JSON
|
||||||
|
return JSON.stringify(msg.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
let historyArray: any[] = [];
|
||||||
|
|
||||||
|
if (remoteJid && remoteJid.startsWith('webwidget:')) {
|
||||||
|
// Extrai o ID da conversa a partir do remoteJid (ex: 'webwidget:12345')
|
||||||
|
const conversationId = remoteJid.split(':')[1] || '0';
|
||||||
|
this.logger.debug(`[sendMessageToBot] RemoteJid é webwidget. Buscando histórico da conversa: ${conversationId}`);
|
||||||
|
|
||||||
|
// Busca todas as mensagens, sem limite de quantidade
|
||||||
|
let conversationHistory = await this.prismaRepository.message.findMany({
|
||||||
|
where: {
|
||||||
|
chatwootConversationId: parseInt(conversationId),
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
messageTimestamp: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.logger.debug(`[sendMessageToBot] Histórico da conversa recuperado: ${conversationHistory.length} mensagens`);
|
||||||
|
|
||||||
|
if (conversationHistory.length > 0) {
|
||||||
|
// Inverte para ficar das mais antigas às mais recentes
|
||||||
|
conversationHistory = conversationHistory.reverse();
|
||||||
|
|
||||||
|
// Mapeia cada mensagem para uma linha (role + texto)
|
||||||
|
let lines = conversationHistory.map((msg) => {
|
||||||
|
const textoExtraido = extrairTextoDaMensagem(msg);
|
||||||
|
// Se a mensagem for "fromMe", consideramos como 'assistant'; senão, 'user'
|
||||||
|
const roleOpenAI = (msg.key as any)?.fromMe ? 'assistant' : 'user';
|
||||||
|
return `${roleOpenAI}: ${textoExtraido}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Monta o histórico inicial com todas as linhas
|
||||||
|
let conversationString = lines.join('\n');
|
||||||
|
|
||||||
|
// Se exceder o limite de caracteres, remover mensagens mais antigas
|
||||||
|
while (conversationString.length > MAX_HISTORY_CHARS && lines.length > 0) {
|
||||||
|
// Remove a primeira linha (mais antiga) do array
|
||||||
|
lines.shift();
|
||||||
|
conversationString = lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
historyArray = [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: `This is the conversation history so far:\n\n${conversationString}`,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// Caso não haja histórico
|
||||||
|
historyArray = [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: 'Não há histórico de conversa ainda.',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug(`[sendMessageToBot] HistoryMessages: ${JSON.stringify(historyArray)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug historyMessages
|
||||||
|
this.logger.debug(`[sendMessageToBot] HistoryMessages: ${JSON.stringify(historyArray)}`);
|
||||||
|
const messages: any[] = [...messagesSystem, ...messagesAssistant, ...messagesUser, ...historyArray, messageData];
|
||||||
|
this.logger.debug(`[sendMessageToBot] Mensagens que serão enviadas para a API da OpenAI: ${JSON.stringify(messages)}`);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageToBot] Atualizando presença para WHATSAPP_BAILEYS (composing).');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[sendMessageToBot] Chamando a API da OpenAI (chat.completions.create).');
|
||||||
const completions = await this.client.chat.completions.create({
|
const completions = await this.client.chat.completions.create({
|
||||||
model: openaiBot.model,
|
model: openaiBot.model,
|
||||||
messages: messages,
|
messages: messages,
|
||||||
max_tokens: openaiBot.maxTokens,
|
max_tokens: openaiBot.maxTokens,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS)
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageToBot] Atualizando presença para WHATSAPP_BAILEYS (paused).');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
|
}
|
||||||
|
|
||||||
const message = completions.choices[0].message.content;
|
const message = completions.choices[0].message.content;
|
||||||
|
this.logger.debug(`[sendMessageToBot] Resposta obtida da OpenAI: ${message}`);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -103,15 +218,20 @@ export class OpenaiService {
|
|||||||
content: string,
|
content: string,
|
||||||
threadId: string,
|
threadId: string,
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Enviando mensagem para o assistente.');
|
||||||
|
this.logger.debug(`[sendMessageToAssistant] RemoteJid: ${remoteJid}, ThreadId: ${threadId}, Content: ${content}`);
|
||||||
|
|
||||||
const messageData: any = {
|
const messageData: any = {
|
||||||
role: fromMe ? 'assistant' : 'user',
|
role: fromMe ? 'assistant' : 'user',
|
||||||
content: [{ type: 'text', text: content }],
|
content: [{ type: 'text', text: content }],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.isImageMessage(content)) {
|
if (this.isImageMessage(content)) {
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Identificada mensagem de imagem no texto.');
|
||||||
const contentSplit = content.split('|');
|
const contentSplit = content.split('|');
|
||||||
|
|
||||||
const url = contentSplit[1].split('?')[0];
|
const url = contentSplit[1].split('?')[0];
|
||||||
|
this.logger.debug(`[sendMessageToAssistant] URL da imagem extraída: ${url}`);
|
||||||
|
|
||||||
messageData.content = [
|
messageData.content = [
|
||||||
{ type: 'text', text: contentSplit[2] || content },
|
{ type: 'text', text: contentSplit[2] || content },
|
||||||
@ -124,28 +244,36 @@ export class OpenaiService {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Criando mensagem no thread do Assistant.');
|
||||||
await this.client.beta.threads.messages.create(threadId, messageData);
|
await this.client.beta.threads.messages.create(threadId, messageData);
|
||||||
|
|
||||||
if (fromMe) {
|
if (fromMe) {
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Mensagem enviada foi do próprio bot (fromMe). Enviando Telemetry.');
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Iniciando corrida (run) do Assistant com ID do assistant configurado.');
|
||||||
const runAssistant = await this.client.beta.threads.runs.create(threadId, {
|
const runAssistant = await this.client.beta.threads.runs.create(threadId, {
|
||||||
assistant_id: openaiBot.assistantId,
|
assistant_id: openaiBot.assistantId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Atualizando presença para WHATSAPP_BAILEYS (composing).');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Aguardando resposta do Assistant (getAIResponse).');
|
||||||
const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl, remoteJid, pushName);
|
const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl, remoteJid, pushName);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS)
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageToAssistant] Atualizando presença para WHATSAPP_BAILEYS (paused).');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
|
}
|
||||||
|
|
||||||
const message = response?.data[0].content[0].text.value;
|
const message = response?.data[0].content[0].text.value;
|
||||||
|
this.logger.debug(`[sendMessageToAssistant] Resposta obtida do Assistant: ${message}`);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -157,8 +285,10 @@ export class OpenaiService {
|
|||||||
settings: OpenaiSetting,
|
settings: OpenaiSetting,
|
||||||
message: string,
|
message: string,
|
||||||
) {
|
) {
|
||||||
const linkRegex = /(!?)\[(.*?)\]\((.*?)\)/g;
|
this.logger.debug('[sendMessageWhatsapp] Enviando mensagem para o WhatsApp.');
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] RemoteJid: ${remoteJid}, Mensagem: ${message}`);
|
||||||
|
|
||||||
|
const linkRegex = /(!?)\[(.*?)\]\((.*?)\)/g;
|
||||||
let textBuffer = '';
|
let textBuffer = '';
|
||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
|
|
||||||
@ -178,8 +308,11 @@ export class OpenaiService {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Processa links (ou mídia) dentro do texto
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Verificando se a mensagem contém mídia (links) no formato [altText](url).');
|
||||||
while ((match = linkRegex.exec(message)) !== null) {
|
while ((match = linkRegex.exec(message)) !== null) {
|
||||||
const [fullMatch, exclMark, altText, url] = match;
|
const [fullMatch, exclMark, altText, url] = match;
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Match encontrado: ${fullMatch}, url: ${url}, altText: ${altText}`);
|
||||||
const mediaType = getMediaType(url);
|
const mediaType = getMediaType(url);
|
||||||
|
|
||||||
const beforeText = message.slice(lastIndex, match.index);
|
const beforeText = message.slice(lastIndex, match.index);
|
||||||
@ -193,22 +326,24 @@ export class OpenaiService {
|
|||||||
const minDelay = 1000;
|
const minDelay = 1000;
|
||||||
const maxDelay = 20000;
|
const maxDelay = 20000;
|
||||||
|
|
||||||
|
// Envia primeiro o texto que estiver no buffer
|
||||||
if (textBuffer.trim()) {
|
if (textBuffer.trim()) {
|
||||||
if (splitMessages) {
|
if (splitMessages) {
|
||||||
const multipleMessages = textBuffer.trim().split('\n\n');
|
const multipleMessages = textBuffer.trim().split('\n\n');
|
||||||
|
|
||||||
for (let index = 0; index < multipleMessages.length; index++) {
|
for (let index = 0; index < multipleMessages.length; index++) {
|
||||||
const message = multipleMessages[index];
|
const message = multipleMessages[index];
|
||||||
|
|
||||||
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (composing) antes de enviar texto em partes.');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Enviando texto (splitMessage)`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -222,10 +357,12 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (paused) após enviar parte do texto.');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Enviando texto inteiro do buffer: ${textBuffer.trim()}`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -238,7 +375,9 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Identificado arquivo de mídia do tipo: ${mediaType}`);
|
||||||
if (mediaType === 'audio') {
|
if (mediaType === 'audio') {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Enviando arquivo de áudio para o WhatsApp.');
|
||||||
await instance.audioWhatsapp({
|
await instance.audioWhatsapp({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
delay: settings?.delayMessage || 1000,
|
delay: settings?.delayMessage || 1000,
|
||||||
@ -246,6 +385,7 @@ export class OpenaiService {
|
|||||||
caption: altText,
|
caption: altText,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Enviando arquivo de mídia (imagem, vídeo ou documento) para o WhatsApp.');
|
||||||
await instance.mediaMessage(
|
await instance.mediaMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -259,12 +399,14 @@ export class OpenaiService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Não é um tipo de mídia suportado. Adicionando link no buffer de texto.');
|
||||||
textBuffer += `[${altText}](${url})`;
|
textBuffer += `[${altText}](${url})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastIndex = linkRegex.lastIndex;
|
lastIndex = linkRegex.lastIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Processa o texto restante, caso exista
|
||||||
if (lastIndex < message.length) {
|
if (lastIndex < message.length) {
|
||||||
const remainingText = message.slice(lastIndex);
|
const remainingText = message.slice(lastIndex);
|
||||||
if (remainingText.trim()) {
|
if (remainingText.trim()) {
|
||||||
@ -277,22 +419,24 @@ export class OpenaiService {
|
|||||||
const minDelay = 1000;
|
const minDelay = 1000;
|
||||||
const maxDelay = 20000;
|
const maxDelay = 20000;
|
||||||
|
|
||||||
|
// Envia o que restou no textBuffer
|
||||||
if (textBuffer.trim()) {
|
if (textBuffer.trim()) {
|
||||||
if (splitMessages) {
|
if (splitMessages) {
|
||||||
const multipleMessages = textBuffer.trim().split('\n\n');
|
const multipleMessages = textBuffer.trim().split('\n\n');
|
||||||
|
|
||||||
for (let index = 0; index < multipleMessages.length; index++) {
|
for (let index = 0; index < multipleMessages.length; index++) {
|
||||||
const message = multipleMessages[index];
|
const message = multipleMessages[index];
|
||||||
|
|
||||||
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
const delay = Math.min(Math.max(message.length * timePerChar, minDelay), maxDelay);
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (composing) antes de enviar resto do texto em partes.');
|
||||||
await instance.client.presenceSubscribe(remoteJid);
|
await instance.client.presenceSubscribe(remoteJid);
|
||||||
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
await instance.client.sendPresenceUpdate('composing', remoteJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Enviando texto (splitMessage)`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -306,10 +450,12 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
if (instance.integration === Integration.WHATSAPP_BAILEYS) {
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Atualizando presença (paused) após enviar parte final do texto.');
|
||||||
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
await instance.client.sendPresenceUpdate('paused', remoteJid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Enviando todo o texto restante no buffer: ${textBuffer.trim()}`);
|
||||||
await instance.textMessage(
|
await instance.textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -322,8 +468,10 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[sendMessageWhatsapp] Enviando telemetria após envio de texto.');
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
|
|
||||||
|
this.logger.debug(`[sendMessageWhatsapp] Atualizando sessão (id: ${session.id}) para 'opened' e 'awaitUser: true'.`);
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -336,7 +484,13 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createAssistantNewSession(instance: InstanceDto, data: any) {
|
public async createAssistantNewSession(instance: InstanceDto, data: any) {
|
||||||
if (data.remoteJid === 'status@broadcast') return;
|
this.logger.debug('[createAssistantNewSession] Iniciando criação de nova sessão do Assistant.');
|
||||||
|
this.logger.debug(`[createAssistantNewSession] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
if (data.remoteJid === 'status@broadcast') {
|
||||||
|
this.logger.debug('[createAssistantNewSession] remoteJid é status@broadcast, abortando criação de sessão.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -344,17 +498,24 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) throw new Error('Openai Creds not found');
|
if (!creds) {
|
||||||
|
this.logger.error('[createAssistantNewSession] Openai Creds não encontrados, lançando erro.');
|
||||||
|
throw new Error('Openai Creds not found');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.debug('[createAssistantNewSession] Instanciando cliente OpenAI para Assistant.');
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const threadId = (await this.client.beta.threads.create({})).id;
|
this.logger.debug('[createAssistantNewSession] Criando thread (beta.threads.create).');
|
||||||
|
const thread = await this.client.beta.threads.create({});
|
||||||
|
const threadId = thread.id;
|
||||||
|
|
||||||
let session = null;
|
let session = null;
|
||||||
if (threadId) {
|
if (threadId) {
|
||||||
|
this.logger.debug('[createAssistantNewSession] Thread criada com sucesso. Salvando sessão no banco de dados.');
|
||||||
session = await this.prismaRepository.integrationSession.create({
|
session = await this.prismaRepository.integrationSession.create({
|
||||||
data: {
|
data: {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
@ -370,7 +531,7 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
return { session };
|
return { session };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(`[createAssistantNewSession] Erro ao criar nova sessão do Assistant: ${error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,6 +546,9 @@ export class OpenaiService {
|
|||||||
session: IntegrationSession,
|
session: IntegrationSession,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug('[initAssistantNewSession] Iniciando sessão do Assistant.');
|
||||||
|
this.logger.debug(`[initAssistantNewSession] RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}`);
|
||||||
|
|
||||||
const data = await this.createAssistantNewSession(instance, {
|
const data = await this.createAssistantNewSession(instance, {
|
||||||
remoteJid,
|
remoteJid,
|
||||||
pushName,
|
pushName,
|
||||||
@ -394,8 +558,10 @@ export class OpenaiService {
|
|||||||
|
|
||||||
if (data.session) {
|
if (data.session) {
|
||||||
session = data.session;
|
session = data.session;
|
||||||
|
this.logger.debug(`[initAssistantNewSession] Sessão criada com sucesso. ID: ${session.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[initAssistantNewSession] Enviando mensagem para Assistant para iniciar conversa.');
|
||||||
const message = await this.sendMessageToAssistant(
|
const message = await this.sendMessageToAssistant(
|
||||||
instance,
|
instance,
|
||||||
openaiBot,
|
openaiBot,
|
||||||
@ -406,7 +572,11 @@ export class OpenaiService {
|
|||||||
session.sessionId,
|
session.sessionId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.debug(`[initAssistantNewSession] Retorno do Assistant: ${message}`);
|
||||||
|
if (message) {
|
||||||
|
this.logger.debug('[initAssistantNewSession] Enviando mensagem do Assistant para WhatsApp.');
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -427,10 +597,12 @@ export class OpenaiService {
|
|||||||
remoteJid: string,
|
remoteJid: string,
|
||||||
pushName: string,
|
pushName: string,
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug(`[getAIResponse] Consultando run do Assistant. ThreadId: ${threadId}, RunId: ${runId}`);
|
||||||
const getRun = await this.client.beta.threads.runs.retrieve(threadId, runId);
|
const getRun = await this.client.beta.threads.runs.retrieve(threadId, runId);
|
||||||
let toolCalls;
|
let toolCalls;
|
||||||
switch (getRun.status) {
|
switch (getRun.status) {
|
||||||
case 'requires_action':
|
case 'requires_action':
|
||||||
|
this.logger.debug('[getAIResponse] Run requer ação. Verificando chamadas de ferramenta (tool_calls).');
|
||||||
toolCalls = getRun?.required_action?.submit_tool_outputs?.tool_calls;
|
toolCalls = getRun?.required_action?.submit_tool_outputs?.tool_calls;
|
||||||
|
|
||||||
if (toolCalls) {
|
if (toolCalls) {
|
||||||
@ -442,6 +614,7 @@ export class OpenaiService {
|
|||||||
: toolCall?.function?.arguments;
|
: toolCall?.function?.arguments;
|
||||||
|
|
||||||
let output = null;
|
let output = null;
|
||||||
|
this.logger.debug(`[getAIResponse] Chamando função externa: ${functionName} com argumentos:`, functionArgument);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(functionUrl, {
|
const { data } = await axios.post(functionUrl, {
|
||||||
@ -449,13 +622,16 @@ export class OpenaiService {
|
|||||||
arguments: { ...functionArgument, remoteJid, pushName },
|
arguments: { ...functionArgument, remoteJid, pushName },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Serializa saída para string
|
||||||
output = JSON.stringify(data)
|
output = JSON.stringify(data)
|
||||||
.replace(/\\/g, '\\\\')
|
.replace(/\\/g, '\\\\')
|
||||||
.replace(/"/g, '\\"')
|
.replace(/"/g, '\\"')
|
||||||
.replace(/\n/g, '\\n')
|
.replace(/\n/g, '\\n')
|
||||||
.replace(/\r/g, '\\r')
|
.replace(/\r/g, '\\r')
|
||||||
.replace(/\t/g, '\\t');
|
.replace(/\t/g, '\\t');
|
||||||
|
this.logger.debug(`[getAIResponse] Resposta da função externa (${functionName}):`, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.logger.error(`[getAIResponse] Erro ao chamar função externa (${functionName}):`, error);
|
||||||
output = JSON.stringify(error)
|
output = JSON.stringify(error)
|
||||||
.replace(/\\/g, '\\\\')
|
.replace(/\\/g, '\\\\')
|
||||||
.replace(/"/g, '\\"')
|
.replace(/"/g, '\\"')
|
||||||
@ -464,6 +640,7 @@ export class OpenaiService {
|
|||||||
.replace(/\t/g, '\\t');
|
.replace(/\t/g, '\\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[getAIResponse] Submetendo output para a run do Assistant (submitToolOutputs).');
|
||||||
await this.client.beta.threads.runs.submitToolOutputs(threadId, runId, {
|
await this.client.beta.threads.runs.submitToolOutputs(threadId, runId, {
|
||||||
tool_outputs: [
|
tool_outputs: [
|
||||||
{
|
{
|
||||||
@ -475,14 +652,18 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[getAIResponse] Repetindo chamada getAIResponse até status diferente de requires_action.');
|
||||||
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
||||||
case 'queued':
|
case 'queued':
|
||||||
|
this.logger.debug('[getAIResponse] Run está em fila (queued). Aguardando 1 segundo antes de tentar novamente.');
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
||||||
case 'in_progress':
|
case 'in_progress':
|
||||||
|
this.logger.debug('[getAIResponse] Run está em progresso (in_progress). Aguardando 1 segundo antes de tentar novamente.');
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
return this.getAIResponse(threadId, runId, functionUrl, remoteJid, pushName);
|
||||||
case 'completed':
|
case 'completed':
|
||||||
|
this.logger.debug('[getAIResponse] Run concluída (completed). Recuperando última mensagem.');
|
||||||
return await this.client.beta.threads.messages.list(threadId, {
|
return await this.client.beta.threads.messages.list(threadId, {
|
||||||
run_id: runId,
|
run_id: runId,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
@ -504,21 +685,27 @@ export class OpenaiService {
|
|||||||
settings: OpenaiSetting,
|
settings: OpenaiSetting,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Processando mensagem para o Assistant.');
|
||||||
|
this.logger.debug(
|
||||||
|
`[processOpenaiAssistant] RemoteJid: ${remoteJid}, pushName: ${pushName}, fromMe: ${fromMe}, content: ${content}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (session && session.status === 'closed') {
|
if (session && session.status === 'closed') {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] A sessão está fechada, não será processada.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session && settings.expire && settings.expire > 0) {
|
if (session && settings.expire && settings.expire > 0) {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Verificando tempo de expiração da sessão...');
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
||||||
|
|
||||||
const diff = now - sessionUpdatedAt;
|
const diff = now - sessionUpdatedAt;
|
||||||
|
|
||||||
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
||||||
|
|
||||||
if (diffInMinutes > settings.expire) {
|
if (diffInMinutes > settings.expire) {
|
||||||
|
this.logger.debug(`[processOpenaiAssistant] Sessão expirada há ${diffInMinutes} minutos.`);
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Atualizando status da sessão para CLOSED.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -528,6 +715,7 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Deletando sessão do banco de dados.');
|
||||||
await this.prismaRepository.integrationSession.deleteMany({
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
botId: openaiBot.id,
|
botId: openaiBot.id,
|
||||||
@ -536,6 +724,7 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Recriando nova sessão de Assistant...');
|
||||||
await this.initAssistantNewSession(
|
await this.initAssistantNewSession(
|
||||||
instance,
|
instance,
|
||||||
remoteJid,
|
remoteJid,
|
||||||
@ -551,11 +740,13 @@ export class OpenaiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Nenhuma sessão ativa encontrada, criando nova sessão de Assistant...');
|
||||||
await this.initAssistantNewSession(instance, remoteJid, pushName, fromMe, openaiBot, settings, session, content);
|
await this.initAssistantNewSession(instance, remoteJid, pushName, fromMe, openaiBot, settings, session, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.status !== 'paused')
|
if (session.status !== 'paused') {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Marcando sessão como aberta e awaitUser = false.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -565,9 +756,12 @@ export class OpenaiService {
|
|||||||
awaitUser: false,
|
awaitUser: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Não há conteúdo na mensagem. Verificando se existe unknownMessage para retorno.');
|
||||||
if (settings.unknownMessage) {
|
if (settings.unknownMessage) {
|
||||||
|
this.logger.debug(`[processOpenaiAssistant] Enviando unknownMessage para o remoteJid: ${remoteJid}`);
|
||||||
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -576,13 +770,13 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Keyword finish detectada. Encerrando sessão.');
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
@ -603,20 +797,25 @@ export class OpenaiService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Buscando OpenaiCreds no banco...');
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: openaiBot.openaiCredsId,
|
id: openaiBot.openaiCredsId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) throw new Error('Openai Creds not found');
|
if (!creds) {
|
||||||
|
this.logger.error('[processOpenaiAssistant] Openai Creds não encontrados, lançando erro.');
|
||||||
|
throw new Error('Openai Creds not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiAssistant] Instanciando cliente OpenAI para processar a mensagem no Assistant.');
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const threadId = session.sessionId;
|
const threadId = session.sessionId;
|
||||||
|
this.logger.debug(`[processOpenaiAssistant] Enviando mensagem ao Assistant (threadId: ${threadId}).`);
|
||||||
const message = await this.sendMessageToAssistant(
|
const message = await this.sendMessageToAssistant(
|
||||||
instance,
|
instance,
|
||||||
openaiBot,
|
openaiBot,
|
||||||
@ -627,15 +826,25 @@ export class OpenaiService {
|
|||||||
threadId,
|
threadId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
this.logger.debug(`[processOpenaiAssistant] Resposta do Assistant recebida. Enviando para WhatsApp: ${message}`);
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createChatCompletionNewSession(instance: InstanceDto, data: any) {
|
public async createChatCompletionNewSession(instance: InstanceDto, data: any) {
|
||||||
if (data.remoteJid === 'status@broadcast') return;
|
this.logger.debug('[createChatCompletionNewSession] Iniciando criação de nova sessão de chatCompletion.');
|
||||||
|
this.logger.debug(`[createChatCompletionNewSession] Dados recebidos: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
if (data.remoteJid === 'status@broadcast') {
|
||||||
|
this.logger.debug('[createChatCompletionNewSession] remoteJid é status@broadcast, abortando criação de sessão.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const id = Math.floor(Math.random() * 10000000000).toString();
|
const id = Math.floor(Math.random() * 10000000000).toString();
|
||||||
|
this.logger.debug(`[createChatCompletionNewSession] Gerando ID pseudo-aleatório da sessão: ${id}`);
|
||||||
|
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -643,9 +852,13 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) throw new Error('Openai Creds not found');
|
if (!creds) {
|
||||||
|
this.logger.error('[createChatCompletionNewSession] Openai Creds não encontrados, lançando erro.');
|
||||||
|
throw new Error('Openai Creds not found');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.debug('[createChatCompletionNewSession] Criando sessão no banco de dados.');
|
||||||
const session = await this.prismaRepository.integrationSession.create({
|
const session = await this.prismaRepository.integrationSession.create({
|
||||||
data: {
|
data: {
|
||||||
remoteJid: data.remoteJid,
|
remoteJid: data.remoteJid,
|
||||||
@ -661,7 +874,7 @@ export class OpenaiService {
|
|||||||
|
|
||||||
return { session, creds };
|
return { session, creds };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(`[createChatCompletionNewSession] Erro ao criar nova sessão de chatCompletion: ${error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,6 +888,9 @@ export class OpenaiService {
|
|||||||
session: IntegrationSession,
|
session: IntegrationSession,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug('[initChatCompletionNewSession] Iniciando sessão de chatCompletion.');
|
||||||
|
this.logger.debug(`[initChatCompletionNewSession] RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}`);
|
||||||
|
|
||||||
const data = await this.createChatCompletionNewSession(instance, {
|
const data = await this.createChatCompletionNewSession(instance, {
|
||||||
remoteJid,
|
remoteJid,
|
||||||
pushName,
|
pushName,
|
||||||
@ -683,16 +899,21 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
session = data.session;
|
session = data.session;
|
||||||
|
|
||||||
const creds = data.creds;
|
const creds = data.creds;
|
||||||
|
this.logger.debug(`[initChatCompletionNewSession] Sessão criada com sucesso (ID: ${session.id}). Instanciando cliente OpenAI.`);
|
||||||
|
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug('[initChatCompletionNewSession] Enviando mensagem para o Bot usando chatCompletion.');
|
||||||
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
||||||
|
|
||||||
|
this.logger.debug(`[initChatCompletionNewSession] Resposta do Bot: ${message}`);
|
||||||
|
if (message) {
|
||||||
|
this.logger.debug('[initChatCompletionNewSession] Enviando resposta para o WhatsApp.');
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -706,21 +927,27 @@ export class OpenaiService {
|
|||||||
settings: OpenaiSetting,
|
settings: OpenaiSetting,
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
|
this.logger.debug('Processando ChatCompletion (processOpenaiChatCompletion).');
|
||||||
|
this.logger.debug(
|
||||||
|
`RemoteJid: ${remoteJid}, PushName: ${pushName}, Content: ${content}, SessionId: ${session?.id}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (session && session.status !== 'opened') {
|
if (session && session.status !== 'opened') {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Sessão existente não está aberta. Não será processado.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session && settings.expire && settings.expire > 0) {
|
if (session && settings.expire && settings.expire > 0) {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Verificando tempo de expiração da sessão...');
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
const sessionUpdatedAt = new Date(session.updatedAt).getTime();
|
||||||
|
|
||||||
const diff = now - sessionUpdatedAt;
|
const diff = now - sessionUpdatedAt;
|
||||||
|
|
||||||
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
||||||
|
|
||||||
if (diffInMinutes > settings.expire) {
|
if (diffInMinutes > settings.expire) {
|
||||||
|
this.logger.debug(`[processOpenaiChatCompletion] Sessão expirada há ${diffInMinutes} minutos.`);
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Atualizando status da sessão para CLOSED.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -730,6 +957,7 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Deletando sessão do banco de dados.');
|
||||||
await this.prismaRepository.integrationSession.deleteMany({
|
await this.prismaRepository.integrationSession.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
botId: openaiBot.id,
|
botId: openaiBot.id,
|
||||||
@ -738,16 +966,19 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Recriando nova sessão de chatCompletion...');
|
||||||
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Nenhuma sessão encontrada. Criando nova sessão de chatCompletion...');
|
||||||
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
await this.initChatCompletionNewSession(instance, remoteJid, pushName, openaiBot, settings, session, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Marcando sessão como aberta e awaitUser = false.');
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
@ -759,7 +990,9 @@ export class OpenaiService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Não há conteúdo na mensagem. Verificando se existe unknownMessage para retorno.');
|
||||||
if (settings.unknownMessage) {
|
if (settings.unknownMessage) {
|
||||||
|
this.logger.debug(`[processOpenaiChatCompletion] Enviando unknownMessage para o remoteJid: ${remoteJid}`);
|
||||||
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
this.waMonitor.waInstances[instance.instanceName].textMessage(
|
||||||
{
|
{
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
@ -768,13 +1001,13 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
sendTelemetry('/message/sendText');
|
sendTelemetry('/message/sendText');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
if (settings.keywordFinish && content.toLowerCase() === settings.keywordFinish.toLowerCase()) {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Keyword finish detectada. Encerrando sessão.');
|
||||||
if (settings.keepOpen) {
|
if (settings.keepOpen) {
|
||||||
await this.prismaRepository.integrationSession.update({
|
await this.prismaRepository.integrationSession.update({
|
||||||
where: {
|
where: {
|
||||||
@ -795,33 +1028,47 @@ export class OpenaiService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Buscando OpenaiCreds no banco...');
|
||||||
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
const creds = await this.prismaRepository.openaiCreds.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: openaiBot.openaiCredsId,
|
id: openaiBot.openaiCredsId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creds) throw new Error('Openai Creds not found');
|
if (!creds) {
|
||||||
|
this.logger.error('[processOpenaiChatCompletion] Openai Creds não encontrados, lançando erro.');
|
||||||
|
throw new Error('Openai Creds not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Instanciando cliente OpenAI para processar a mensagem (ChatCompletion).');
|
||||||
this.client = new OpenAI({
|
this.client = new OpenAI({
|
||||||
apiKey: creds.apiKey,
|
apiKey: creds.apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Enviando mensagem para o Bot usando chatCompletion.');
|
||||||
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
const message = await this.sendMessageToBot(instance, openaiBot, remoteJid, content);
|
||||||
|
|
||||||
|
this.logger.debug(`[processOpenaiChatCompletion] Resposta do Bot: ${message}`);
|
||||||
|
if (message) {
|
||||||
|
this.logger.debug('[processOpenaiChatCompletion] Enviando resposta para o WhatsApp.');
|
||||||
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
await this.sendMessageWhatsapp(instance, session, remoteJid, settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async speechToText(creds: OpenaiCreds, msg: any, updateMediaMessage: any) {
|
public async speechToText(creds: OpenaiCreds, msg: any, updateMediaMessage: any) {
|
||||||
|
this.logger.debug('[speechToText] Iniciando conversão de fala em texto.');
|
||||||
|
|
||||||
let audio;
|
let audio;
|
||||||
|
|
||||||
if (msg?.message?.mediaUrl) {
|
if (msg?.message?.mediaUrl) {
|
||||||
|
this.logger.debug('[speechToText] Baixando áudio via URL (mediaUrl).');
|
||||||
audio = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' }).then((response) => {
|
audio = await axios.get(msg.message.mediaUrl, { responseType: 'arraybuffer' }).then((response) => {
|
||||||
return Buffer.from(response.data, 'binary');
|
return Buffer.from(response.data, 'binary');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.debug('[speechToText] Baixando áudio via downloadMediaMessage (baileys).');
|
||||||
audio = await downloadMediaMessage(
|
audio = await downloadMediaMessage(
|
||||||
{ key: msg.key, message: msg?.message },
|
{ key: msg.key, message: msg?.message },
|
||||||
'buffer',
|
'buffer',
|
||||||
@ -836,13 +1083,14 @@ export class OpenaiService {
|
|||||||
const lang = this.configService.get<Language>('LANGUAGE').includes('pt')
|
const lang = this.configService.get<Language>('LANGUAGE').includes('pt')
|
||||||
? 'pt'
|
? 'pt'
|
||||||
: this.configService.get<Language>('LANGUAGE');
|
: this.configService.get<Language>('LANGUAGE');
|
||||||
|
this.logger.debug(`[speechToText] Definindo idioma da transcrição como: ${lang}`);
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
formData.append('file', audio, 'audio.ogg');
|
formData.append('file', audio, 'audio.ogg');
|
||||||
formData.append('model', 'whisper-1');
|
formData.append('model', 'whisper-1');
|
||||||
formData.append('language', lang);
|
formData.append('language', lang);
|
||||||
|
|
||||||
|
this.logger.debug('[speechToText] Enviando requisição POST para a API de transcrição do OpenAI.');
|
||||||
const response = await axios.post('https://api.openai.com/v1/audio/transcriptions', formData, {
|
const response = await axios.post('https://api.openai.com/v1/audio/transcriptions', formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
@ -850,6 +1098,7 @@ export class OpenaiService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug(`[speechToText] Status da requisição: ${response.status}`);
|
||||||
return response?.data?.text;
|
return response?.data?.text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ export class ChannelStartupService {
|
|||||||
public difyService = new DifyService(waMonitor, this.configService, this.prismaRepository);
|
public difyService = new DifyService(waMonitor, this.configService, this.prismaRepository);
|
||||||
|
|
||||||
public setInstance(instance: InstanceDto) {
|
public setInstance(instance: InstanceDto) {
|
||||||
|
this.logger.debug(`[setInstance] Definindo dados da instância: ${JSON.stringify(instance)}`);
|
||||||
this.logger.setInstance(instance.instanceName);
|
this.logger.setInstance(instance.instanceName);
|
||||||
|
|
||||||
this.instance.name = instance.instanceName;
|
this.instance.name = instance.instanceName;
|
||||||
@ -61,6 +62,7 @@ export class ChannelStartupService {
|
|||||||
this.instance.businessId = instance.businessId;
|
this.instance.businessId = instance.businessId;
|
||||||
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||||
|
this.logger.debug('[setInstance] Enviando evento de STATUS_INSTANCE para Chatwoot');
|
||||||
this.chatwootService.eventWhatsapp(
|
this.chatwootService.eventWhatsapp(
|
||||||
Events.STATUS_INSTANCE,
|
Events.STATUS_INSTANCE,
|
||||||
{ instanceName: this.instance.name },
|
{ instanceName: this.instance.name },
|
||||||
@ -73,6 +75,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set instanceName(name: string) {
|
public set instanceName(name: string) {
|
||||||
|
this.logger.debug(`[setter instanceName] Atribuindo: ${name}`);
|
||||||
this.logger.setInstance(name);
|
this.logger.setInstance(name);
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
@ -87,6 +90,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set instanceId(id: string) {
|
public set instanceId(id: string) {
|
||||||
|
this.logger.debug(`[setter instanceId] Atribuindo: ${id}`);
|
||||||
if (!id) {
|
if (!id) {
|
||||||
this.instance.id = v4();
|
this.instance.id = v4();
|
||||||
return;
|
return;
|
||||||
@ -99,6 +103,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set integration(integration: string) {
|
public set integration(integration: string) {
|
||||||
|
this.logger.debug(`[setter integration] Atribuindo: ${integration}`);
|
||||||
this.instance.integration = integration;
|
this.instance.integration = integration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +112,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set number(number: string) {
|
public set number(number: string) {
|
||||||
|
this.logger.debug(`[setter number] Atribuindo número: ${number}`);
|
||||||
this.instance.number = number;
|
this.instance.number = number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +121,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set token(token: string) {
|
public set token(token: string) {
|
||||||
|
this.logger.debug(`[setter token] Atribuindo token.`);
|
||||||
this.instance.token = token;
|
this.instance.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,6 +134,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async loadWebhook() {
|
public async loadWebhook() {
|
||||||
|
this.logger.debug(`[loadWebhook] Carregando webhook para instanceId: ${this.instanceId}`);
|
||||||
const data = await this.prismaRepository.webhook.findUnique({
|
const data = await this.prismaRepository.webhook.findUnique({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -135,9 +143,12 @@ export class ChannelStartupService {
|
|||||||
|
|
||||||
this.localWebhook.enabled = data?.enabled;
|
this.localWebhook.enabled = data?.enabled;
|
||||||
this.localWebhook.webhookBase64 = data?.webhookBase64;
|
this.localWebhook.webhookBase64 = data?.webhookBase64;
|
||||||
|
|
||||||
|
this.logger.debug('[loadWebhook] Webhook carregado com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadSettings() {
|
public async loadSettings() {
|
||||||
|
this.logger.debug(`[loadSettings] Carregando configurações para instanceId: ${this.instanceId}`);
|
||||||
const data = await this.prismaRepository.setting.findUnique({
|
const data = await this.prismaRepository.setting.findUnique({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -151,9 +162,12 @@ export class ChannelStartupService {
|
|||||||
this.localSettings.readMessages = data?.readMessages;
|
this.localSettings.readMessages = data?.readMessages;
|
||||||
this.localSettings.readStatus = data?.readStatus;
|
this.localSettings.readStatus = data?.readStatus;
|
||||||
this.localSettings.syncFullHistory = data?.syncFullHistory;
|
this.localSettings.syncFullHistory = data?.syncFullHistory;
|
||||||
|
|
||||||
|
this.logger.debug('[loadSettings] Configurações carregadas com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setSettings(data: SettingsDto) {
|
public async setSettings(data: SettingsDto) {
|
||||||
|
this.logger.debug(`[setSettings] Atualizando configurações: ${JSON.stringify(data)}`);
|
||||||
await this.prismaRepository.setting.upsert({
|
await this.prismaRepository.setting.upsert({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -186,9 +200,12 @@ export class ChannelStartupService {
|
|||||||
this.localSettings.readMessages = data?.readMessages;
|
this.localSettings.readMessages = data?.readMessages;
|
||||||
this.localSettings.readStatus = data?.readStatus;
|
this.localSettings.readStatus = data?.readStatus;
|
||||||
this.localSettings.syncFullHistory = data?.syncFullHistory;
|
this.localSettings.syncFullHistory = data?.syncFullHistory;
|
||||||
|
|
||||||
|
this.logger.debug('[setSettings] Configurações atualizadas com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findSettings() {
|
public async findSettings() {
|
||||||
|
this.logger.debug(`[findSettings] Buscando configurações para instanceId: ${this.instanceId}`);
|
||||||
const data = await this.prismaRepository.setting.findUnique({
|
const data = await this.prismaRepository.setting.findUnique({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -196,9 +213,11 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
this.logger.debug('[findSettings] Nenhuma configuração encontrada.');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[findSettings] Configurações encontradas.');
|
||||||
return {
|
return {
|
||||||
rejectCall: data.rejectCall,
|
rejectCall: data.rejectCall,
|
||||||
msgCall: data.msgCall,
|
msgCall: data.msgCall,
|
||||||
@ -211,7 +230,9 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async loadChatwoot() {
|
public async loadChatwoot() {
|
||||||
|
this.logger.debug('[loadChatwoot] Carregando dados do Chatwoot...');
|
||||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
||||||
|
this.logger.debug('[loadChatwoot] Chatwoot não está habilitado nas configurações.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,10 +256,14 @@ export class ChannelStartupService {
|
|||||||
this.localChatwoot.importContacts = data?.importContacts;
|
this.localChatwoot.importContacts = data?.importContacts;
|
||||||
this.localChatwoot.importMessages = data?.importMessages;
|
this.localChatwoot.importMessages = data?.importMessages;
|
||||||
this.localChatwoot.daysLimitImportMessages = data?.daysLimitImportMessages;
|
this.localChatwoot.daysLimitImportMessages = data?.daysLimitImportMessages;
|
||||||
|
|
||||||
|
this.logger.debug('[loadChatwoot] Dados do Chatwoot carregados com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setChatwoot(data: ChatwootDto) {
|
public async setChatwoot(data: ChatwootDto) {
|
||||||
|
this.logger.debug(`[setChatwoot] Atualizando dados do Chatwoot: ${JSON.stringify(data)}`);
|
||||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
||||||
|
this.logger.debug('[setChatwoot] Chatwoot não está habilitado nas configurações.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,8 +300,8 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null });
|
Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null });
|
||||||
|
|
||||||
this.clearCacheChatwoot();
|
this.clearCacheChatwoot();
|
||||||
|
this.logger.debug('[setChatwoot] Dados do Chatwoot atualizados com sucesso.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,12 +328,14 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null });
|
Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null });
|
||||||
|
|
||||||
this.clearCacheChatwoot();
|
this.clearCacheChatwoot();
|
||||||
|
this.logger.debug('[setChatwoot] Dados do Chatwoot criados com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findChatwoot(): Promise<ChatwootDto | null> {
|
public async findChatwoot(): Promise<ChatwootDto | null> {
|
||||||
|
this.logger.debug(`[findChatwoot] Buscando dados do Chatwoot para instanceId: ${this.instanceId}`);
|
||||||
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
||||||
|
this.logger.debug('[findChatwoot] Chatwoot não está habilitado nas configurações.');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,11 +346,12 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
this.logger.debug('[findChatwoot] Nenhum dado de Chatwoot encontrado.');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ignoreJidsArray = Array.isArray(data.ignoreJids) ? data.ignoreJids.map((event) => String(event)) : [];
|
const ignoreJidsArray = Array.isArray(data.ignoreJids) ? data.ignoreJids.map((event) => String(event)) : [];
|
||||||
|
this.logger.debug('[findChatwoot] Dados de Chatwoot encontrados com sucesso.');
|
||||||
return {
|
return {
|
||||||
enabled: data?.enabled,
|
enabled: data?.enabled,
|
||||||
accountId: data.accountId,
|
accountId: data.accountId,
|
||||||
@ -345,12 +373,15 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public clearCacheChatwoot() {
|
public clearCacheChatwoot() {
|
||||||
|
this.logger.debug('[clearCacheChatwoot] Limpando cache do Chatwoot...');
|
||||||
if (this.localChatwoot?.enabled) {
|
if (this.localChatwoot?.enabled) {
|
||||||
this.chatwootService.getCache()?.deleteAll(this.instanceName);
|
this.chatwootService.getCache()?.deleteAll(this.instanceName);
|
||||||
|
this.logger.debug('[clearCacheChatwoot] Cache do Chatwoot limpo com sucesso.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadProxy() {
|
public async loadProxy() {
|
||||||
|
this.logger.debug(`[loadProxy] Carregando dados de proxy para instanceId: ${this.instanceId}`);
|
||||||
this.localProxy.enabled = false;
|
this.localProxy.enabled = false;
|
||||||
|
|
||||||
if (process.env.PROXY_HOST) {
|
if (process.env.PROXY_HOST) {
|
||||||
@ -376,9 +407,12 @@ export class ChannelStartupService {
|
|||||||
this.localProxy.username = data?.username;
|
this.localProxy.username = data?.username;
|
||||||
this.localProxy.password = data?.password;
|
this.localProxy.password = data?.password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[loadProxy] Dados de proxy carregados com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setProxy(data: ProxyDto) {
|
public async setProxy(data: ProxyDto) {
|
||||||
|
this.logger.debug(`[setProxy] Definindo dados de proxy: ${JSON.stringify(data)}`);
|
||||||
await this.prismaRepository.proxy.upsert({
|
await this.prismaRepository.proxy.upsert({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -403,9 +437,11 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(this.localProxy, data);
|
Object.assign(this.localProxy, data);
|
||||||
|
this.logger.debug('[setProxy] Dados de proxy atualizados com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findProxy() {
|
public async findProxy() {
|
||||||
|
this.logger.debug(`[findProxy] Buscando dados de proxy para instanceId: ${this.instanceId}`);
|
||||||
const data = await this.prismaRepository.proxy.findUnique({
|
const data = await this.prismaRepository.proxy.findUnique({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
@ -413,20 +449,22 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
this.logger.debug('[findProxy] Proxy não encontrado.');
|
||||||
throw new NotFoundException('Proxy not found');
|
throw new NotFoundException('Proxy not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[findProxy] Dados de proxy encontrados com sucesso.');
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendDataWebhook<T = any>(event: Events, data: T, local = true) {
|
public async sendDataWebhook<T = any>(event: Events, data: T, local = true) {
|
||||||
|
this.logger.debug(`[sendDataWebhook] Enviando dados de webhook. Evento: ${event}, local: ${local}`);
|
||||||
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
|
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
|
||||||
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
|
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
|
||||||
const now = localISOTime;
|
const now = localISOTime;
|
||||||
|
|
||||||
const expose = this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES;
|
const expose = this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES;
|
||||||
|
|
||||||
const instanceApikey = this.token || 'Apikey not found';
|
const instanceApikey = this.token || 'Apikey not found';
|
||||||
|
|
||||||
await eventManager.emit({
|
await eventManager.emit({
|
||||||
@ -440,10 +478,12 @@ export class ChannelStartupService {
|
|||||||
apiKey: expose && instanceApikey ? instanceApikey : null,
|
apiKey: expose && instanceApikey ? instanceApikey : null,
|
||||||
local,
|
local,
|
||||||
});
|
});
|
||||||
|
this.logger.debug('[sendDataWebhook] Evento de webhook enviado com sucesso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the number is MX or AR
|
// Check if the number is MX or AR
|
||||||
public formatMXOrARNumber(jid: string): string {
|
public formatMXOrARNumber(jid: string): string {
|
||||||
|
this.logger.debug(`[formatMXOrARNumber] Formatando número MX ou AR: ${jid}`);
|
||||||
const countryCode = jid.substring(0, 2);
|
const countryCode = jid.substring(0, 2);
|
||||||
|
|
||||||
if (Number(countryCode) === 52 || Number(countryCode) === 54) {
|
if (Number(countryCode) === 52 || Number(countryCode) === 54) {
|
||||||
@ -451,7 +491,6 @@ export class ChannelStartupService {
|
|||||||
const number = countryCode + jid.substring(3);
|
const number = countryCode + jid.substring(3);
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
return jid;
|
return jid;
|
||||||
}
|
}
|
||||||
return jid;
|
return jid;
|
||||||
@ -459,6 +498,7 @@ export class ChannelStartupService {
|
|||||||
|
|
||||||
// Check if the number is br
|
// Check if the number is br
|
||||||
public formatBRNumber(jid: string) {
|
public formatBRNumber(jid: string) {
|
||||||
|
this.logger.debug(`[formatBRNumber] Formatando número brasileiro: ${jid}`);
|
||||||
const regexp = new RegExp(/^(\d{2})(\d{2})\d{1}(\d{8})$/);
|
const regexp = new RegExp(/^(\d{2})(\d{2})\d{1}(\d{8})$/);
|
||||||
if (regexp.test(jid)) {
|
if (regexp.test(jid)) {
|
||||||
const match = regexp.exec(jid);
|
const match = regexp.exec(jid);
|
||||||
@ -477,11 +517,14 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createJid(number: string): string {
|
public createJid(number: string): string {
|
||||||
|
this.logger.debug(`[createJid] Criando JID para o número: ${number}`);
|
||||||
if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) {
|
if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) {
|
||||||
|
this.logger.debug('[createJid] Retornando número pois já possui sufixo de grupo ou WhatsApp.');
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (number.includes('@broadcast')) {
|
if (number.includes('@broadcast')) {
|
||||||
|
this.logger.debug('[createJid] Retornando número pois já é um broadcast.');
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,6 +538,7 @@ export class ChannelStartupService {
|
|||||||
|
|
||||||
if (number.includes('-') && number.length >= 24) {
|
if (number.includes('-') && number.length >= 24) {
|
||||||
number = number.replace(/[^\d-]/g, '');
|
number = number.replace(/[^\d-]/g, '');
|
||||||
|
this.logger.debug('[createJid] Número identificado como grupo, adicionando @g.us.');
|
||||||
return `${number}@g.us`;
|
return `${number}@g.us`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,17 +546,19 @@ export class ChannelStartupService {
|
|||||||
|
|
||||||
if (number.length >= 18) {
|
if (number.length >= 18) {
|
||||||
number = number.replace(/[^\d-]/g, '');
|
number = number.replace(/[^\d-]/g, '');
|
||||||
|
this.logger.debug('[createJid] Número extenso, provavelmente grupo, adicionando @g.us.');
|
||||||
return `${number}@g.us`;
|
return `${number}@g.us`;
|
||||||
}
|
}
|
||||||
|
|
||||||
number = this.formatMXOrARNumber(number);
|
number = this.formatMXOrARNumber(number);
|
||||||
|
|
||||||
number = this.formatBRNumber(number);
|
number = this.formatBRNumber(number);
|
||||||
|
|
||||||
|
this.logger.debug('[createJid] Adicionando sufixo @s.whatsapp.net para número individual.');
|
||||||
return `${number}@s.whatsapp.net`;
|
return `${number}@s.whatsapp.net`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchContacts(query: Query<Contact>) {
|
public async fetchContacts(query: Query<Contact>) {
|
||||||
|
this.logger.debug(`[fetchContacts] Buscando contatos. Query: ${JSON.stringify(query)}`);
|
||||||
const remoteJid = query?.where?.remoteJid
|
const remoteJid = query?.where?.remoteJid
|
||||||
? query?.where?.remoteJid.includes('@')
|
? query?.where?.remoteJid.includes('@')
|
||||||
? query.where?.remoteJid
|
? query.where?.remoteJid
|
||||||
@ -527,12 +573,15 @@ export class ChannelStartupService {
|
|||||||
where['remoteJid'] = remoteJid;
|
where['remoteJid'] = remoteJid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.prismaRepository.contact.findMany({
|
const contacts = await this.prismaRepository.contact.findMany({
|
||||||
where,
|
where,
|
||||||
});
|
});
|
||||||
|
this.logger.debug(`[fetchContacts] Retornando ${contacts.length} contato(s).`);
|
||||||
|
return contacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchMessages(query: Query<Message>) {
|
public async fetchMessages(query: Query<Message>) {
|
||||||
|
this.logger.debug(`[fetchMessages] Buscando mensagens. Query: ${JSON.stringify(query)}`);
|
||||||
const keyFilters = query?.where?.key as {
|
const keyFilters = query?.where?.key as {
|
||||||
id?: string;
|
id?: string;
|
||||||
fromMe?: boolean;
|
fromMe?: boolean;
|
||||||
@ -599,6 +648,7 @@ export class ChannelStartupService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug(`[fetchMessages] Total de mensagens encontradas: ${count}.`);
|
||||||
return {
|
return {
|
||||||
messages: {
|
messages: {
|
||||||
total: count,
|
total: count,
|
||||||
@ -610,7 +660,8 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async fetchStatusMessage(query: any) {
|
public async fetchStatusMessage(query: any) {
|
||||||
return await this.prismaRepository.messageUpdate.findMany({
|
this.logger.debug(`[fetchStatusMessage] Buscando status de mensagens. Query: ${JSON.stringify(query)}`);
|
||||||
|
const results = await this.prismaRepository.messageUpdate.findMany({
|
||||||
where: {
|
where: {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
remoteJid: query.where?.remoteJid,
|
remoteJid: query.where?.remoteJid,
|
||||||
@ -619,9 +670,12 @@ export class ChannelStartupService {
|
|||||||
skip: query.offset * (query?.page === 1 ? 0 : (query?.page as number) - 1),
|
skip: query.offset * (query?.page === 1 ? 0 : (query?.page as number) - 1),
|
||||||
take: query.offset,
|
take: query.offset,
|
||||||
});
|
});
|
||||||
|
this.logger.debug(`[fetchStatusMessage] Retornando ${results.length} atualização(ões) de status.`);
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchChats(query: any) {
|
public async fetchChats(query: any) {
|
||||||
|
this.logger.debug(`[fetchChats] Buscando chats. Query: ${JSON.stringify(query)}`);
|
||||||
const remoteJid = query?.where?.remoteJid
|
const remoteJid = query?.where?.remoteJid
|
||||||
? query?.where?.remoteJid.includes('@')
|
? query?.where?.remoteJid.includes('@')
|
||||||
? query.where?.remoteJid
|
? query.where?.remoteJid
|
||||||
@ -703,6 +757,7 @@ export class ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (results && isArray(results) && results.length > 0) {
|
if (results && isArray(results) && results.length > 0) {
|
||||||
|
this.logger.debug(`[fetchChats] Retornando ${results.length} chat(s).`);
|
||||||
return results.map((chat) => {
|
return results.map((chat) => {
|
||||||
return {
|
return {
|
||||||
id: chat.id,
|
id: chat.id,
|
||||||
@ -734,6 +789,7 @@ export class ChannelStartupService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('[fetchChats] Nenhum chat encontrado.');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ export class Logger {
|
|||||||
this.console(value, Type.WARN);
|
this.console(value, Type.WARN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public error(value: any) {
|
public error(value: any, p0?: { message: any; stack: any; }) {
|
||||||
this.console(value, Type.ERROR);
|
this.console(value, Type.ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ export class Logger {
|
|||||||
this.console(value, Type.VERBOSE);
|
this.console(value, Type.VERBOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public debug(value: any) {
|
public debug(value: any, p0?: { config: any; }) {
|
||||||
this.console(value, Type.DEBUG);
|
this.console(value, Type.DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
src/main.ts
11
src/main.ts
@ -20,18 +20,25 @@ function initWA() {
|
|||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const logger = new Logger('SERVER');
|
const logger = new Logger('SERVER');
|
||||||
const app = express();
|
|
||||||
const dsn = process.env.SENTRY_DSN;
|
const dsn = process.env.SENTRY_DSN;
|
||||||
|
|
||||||
|
// 1) Inicializa o Sentry aqui
|
||||||
if (dsn) {
|
if (dsn) {
|
||||||
logger.info('Sentry - ON');
|
logger.info('Sentry - ON');
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: dsn,
|
dsn,
|
||||||
environment: process.env.NODE_ENV || 'development',
|
environment: process.env.NODE_ENV || 'development',
|
||||||
tracesSampleRate: 1.0,
|
tracesSampleRate: 1.0,
|
||||||
profilesSampleRate: 1.0,
|
profilesSampleRate: 1.0,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Depois disso, crie o app
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// Se quiser instrumentar o Express, chame aqui
|
||||||
|
// (garantindo que app já foi declarado)
|
||||||
|
if (dsn) {
|
||||||
Sentry.setupExpressErrorHandler(app);
|
Sentry.setupExpressErrorHandler(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user