mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-14 01:41:24 -06:00
merging with develop
This commit is contained in:
commit
f695e8bdc9
@ -1,5 +1,7 @@
|
|||||||
.git
|
.git
|
||||||
*Dockerfile*
|
*Dockerfile*
|
||||||
*docker-compose*
|
*docker-compose*
|
||||||
|
package-lock.json
|
||||||
|
.env
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
2
.github/workflows/publish_docker_image.yml
vendored
2
.github/workflows/publish_docker_image.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: atendai/evolution-api
|
images: evoapicloud/evolution-api
|
||||||
tags: type=semver,pattern=v{{version}}
|
tags: type=semver,pattern=v{{version}}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
|
@ -20,7 +20,7 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: atendai/evolution-api
|
images: evoapicloud/evolution-api
|
||||||
tags: homolog
|
tags: homolog
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
|
@ -20,7 +20,7 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: atendai/evolution-api
|
images: evoapicloud/evolution-api
|
||||||
tags: latest
|
tags: latest
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,6 +2,8 @@
|
|||||||
/dist
|
/dist
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
||||||
|
.cursor*
|
||||||
|
|
||||||
/Docker/.env
|
/Docker/.env
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
@ -2,7 +2,7 @@ version: "3.7"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
evolution_v2:
|
evolution_v2:
|
||||||
image: atendai/evolution-api:v2.1.2
|
image: evoapicloud/evolution-api:latest
|
||||||
volumes:
|
volumes:
|
||||||
- evolution_instances:/evolution/instances
|
- evolution_instances:/evolution/instances
|
||||||
networks:
|
networks:
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
RUN apk update && \
|
RUN apk update && \
|
||||||
apk add git ffmpeg wget curl bash openssl
|
apk add --no-cache git ffmpeg wget curl bash openssl
|
||||||
|
|
||||||
LABEL version="2.2.3" description="Api to control whatsapp features through http requests."
|
LABEL version="2.2.3" description="Api to control whatsapp features through http requests."
|
||||||
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
|
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
|
||||||
LABEL contact="contato@atendai.com"
|
LABEL contact="contato@evolution-api.com"
|
||||||
|
|
||||||
WORKDIR /evolution
|
WORKDIR /evolution
|
||||||
|
|
||||||
|
2
LICENSE
2
LICENSE
@ -8,7 +8,7 @@ a. LOGO and copyright information: In the process of using Evolution API's front
|
|||||||
|
|
||||||
b. Usage Notification Requirement: If Evolution API is used as part of any project, including closed-source systems (e.g., proprietary software), the user is required to display a clear notification within the system that Evolution API is being utilized. This notification should be visible to system administrators and accessible from the system's documentation or settings page. Failure to comply with this requirement may result in the necessity for a commercial license, as determined by the producer.
|
b. Usage Notification Requirement: If Evolution API is used as part of any project, including closed-source systems (e.g., proprietary software), the user is required to display a clear notification within the system that Evolution API is being utilized. This notification should be visible to system administrators and accessible from the system's documentation or settings page. Failure to comply with this requirement may result in the necessity for a commercial license, as determined by the producer.
|
||||||
|
|
||||||
Please contact contato@atendai.com to inquire about licensing matters.
|
Please contact contato@evolution-api.com to inquire about licensing matters.
|
||||||
|
|
||||||
2. As a contributor, you should agree that:
|
2. As a contributor, you should agree that:
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
|
[]
|
||||||
[](https://evolution-api.com/whatsapp)
|
[](https://evolution-api.com/whatsapp)
|
||||||
[](https://evolution-api.com/discord)
|
[](https://evolution-api.com/discord)
|
||||||
[](https://evolution-api.com/postman)
|
[](https://evolution-api.com/postman)
|
||||||
@ -87,6 +88,7 @@ https://github.com/sponsors/EvolutionAPI
|
|||||||
We are proud to collaborate with the following content creators who have contributed valuable insights and tutorials about Evolution API:
|
We are proud to collaborate with the following content creators who have contributed valuable insights and tutorials about Evolution API:
|
||||||
|
|
||||||
- [Promovaweb](https://www.youtube.com/@promovaweb)
|
- [Promovaweb](https://www.youtube.com/@promovaweb)
|
||||||
|
- [Sandeco](https://www.youtube.com/@canalsandeco)
|
||||||
- [Comunidade ZDG](https://www.youtube.com/@ComunidadeZDG)
|
- [Comunidade ZDG](https://www.youtube.com/@ComunidadeZDG)
|
||||||
- [Francis MNO](https://www.youtube.com/@FrancisMNO)
|
- [Francis MNO](https://www.youtube.com/@FrancisMNO)
|
||||||
- [Pablo Cabral](https://youtube.com/@pablocabral)
|
- [Pablo Cabral](https://youtube.com/@pablocabral)
|
||||||
@ -111,7 +113,7 @@ Evolution API is licensed under the Apache License 2.0, with the following addit
|
|||||||
|
|
||||||
2. **Usage Notification Requirement**: If Evolution API is used as part of any project, including closed-source systems (e.g., proprietary software), the user is required to display a clear notification within the system that Evolution API is being utilized. This notification should be visible to system administrators and accessible from the system's documentation or settings page. Failure to comply with this requirement may result in the necessity for a commercial license, as determined by the producer.
|
2. **Usage Notification Requirement**: If Evolution API is used as part of any project, including closed-source systems (e.g., proprietary software), the user is required to display a clear notification within the system that Evolution API is being utilized. This notification should be visible to system administrators and accessible from the system's documentation or settings page. Failure to comply with this requirement may result in the necessity for a commercial license, as determined by the producer.
|
||||||
|
|
||||||
Please contact contato@atendai.com to inquire about licensing matters.
|
Please contact contato@evolution-api.com to inquire about licensing matters.
|
||||||
|
|
||||||
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
container_name: evolution_api
|
container_name: evolution_api
|
||||||
image: atendai/evolution-api:homolog
|
image: evoapicloud/evolution-api:latest
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
1684
package-lock.json
generated
1684
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -41,7 +41,7 @@
|
|||||||
],
|
],
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Davidson Gomes",
|
"name": "Davidson Gomes",
|
||||||
"email": "contato@atendai.com"
|
"email": "contato@evolution-api.com"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -82,6 +82,7 @@
|
|||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"minio": "^8.0.3",
|
"minio": "^8.0.3",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
|
"nats": "^2.29.1",
|
||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
"openai": "^4.77.3",
|
"openai": "^4.77.3",
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- A unique constraint covering the columns `[remoteJid,instanceId]` on the table `Chat` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
*/
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Setting`
|
||||||
|
ADD COLUMN IF NOT EXISTS `wavoipToken` VARCHAR(100);
|
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to alter the column `createdAt` on the `Chat` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Chat` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Chatwoot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Chatwoot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Contact` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Contact` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Dify` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Dify` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `DifySetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `DifySetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `EvolutionBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `EvolutionBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `EvolutionBotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `EvolutionBotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Flowise` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Flowise` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `FlowiseSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `FlowiseSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `disconnectionAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Instance` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `IntegrationSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `IntegrationSession` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `IsOnWhatsapp` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `IsOnWhatsapp` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Label` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Label` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Media` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `OpenaiBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `OpenaiBot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `OpenaiCreds` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `OpenaiCreds` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `OpenaiSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `OpenaiSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Proxy` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Proxy` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Pusher` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Pusher` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Rabbitmq` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Rabbitmq` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Session` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Setting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Setting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Sqs` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Sqs` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Template` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Template` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Typebot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Typebot` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `TypebotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `TypebotSetting` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Webhook` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Webhook` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `createdAt` on the `Websocket` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- You are about to alter the column `updatedAt` on the `Websocket` table. The data in that column could be lost. The data in that column will be cast from `Timestamp(0)` to `Timestamp`.
|
||||||
|
- A unique constraint covering the columns `[instanceId,remoteJid]` on the table `Chat` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Chat` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Chatwoot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Contact` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Dify` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `DifySetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `EvolutionBot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `EvolutionBotSetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Flowise` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `FlowiseSetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Instance` MODIFY `disconnectionAt` TIMESTAMP NULL,
|
||||||
|
MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `IntegrationSession` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `IsOnWhatsapp` MODIFY `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Label` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Media` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `OpenaiBot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `OpenaiCreds` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `OpenaiSetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Proxy` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Pusher` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Rabbitmq` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Session` MODIFY `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Setting` ADD COLUMN `wavoipToken` VARCHAR(100) NULL,
|
||||||
|
MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Sqs` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Template` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Typebot` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `TypebotSetting` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Webhook` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Websocket` MODIFY `createdAt` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
MODIFY `updatedAt` TIMESTAMP NOT NULL;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX `Chat_instanceId_remoteJid_key` ON `Chat`(`instanceId`, `remoteJid`);
|
@ -1,3 +1,3 @@
|
|||||||
# Please do not edit this file manually
|
# Please do not edit this file manually
|
||||||
# It should be added in your version-control system (i.e. Git)
|
# It should be added in your version-control system (e.g., Git)
|
||||||
provider = "mysql"
|
provider = "mysql"
|
@ -86,6 +86,7 @@ model Instance {
|
|||||||
Proxy Proxy?
|
Proxy Proxy?
|
||||||
Setting Setting?
|
Setting Setting?
|
||||||
Rabbitmq Rabbitmq?
|
Rabbitmq Rabbitmq?
|
||||||
|
Nats Nats?
|
||||||
Sqs Sqs?
|
Sqs Sqs?
|
||||||
Websocket Websocket?
|
Websocket Websocket?
|
||||||
Typebot Typebot[]
|
Typebot Typebot[]
|
||||||
@ -99,7 +100,7 @@ model Instance {
|
|||||||
Template Template[]
|
Template Template[]
|
||||||
Dify Dify[]
|
Dify Dify[]
|
||||||
DifySetting DifySetting?
|
DifySetting DifySetting?
|
||||||
integrationSessions IntegrationSession[]
|
IntegrationSession IntegrationSession[]
|
||||||
EvolutionBot EvolutionBot[]
|
EvolutionBot EvolutionBot[]
|
||||||
EvolutionBotSetting EvolutionBotSetting?
|
EvolutionBotSetting EvolutionBotSetting?
|
||||||
Flowise Flowise[]
|
Flowise Flowise[]
|
||||||
@ -125,9 +126,10 @@ model Chat {
|
|||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String
|
instanceId String
|
||||||
unreadMessages Int @default(0)
|
unreadMessages Int @default(0)
|
||||||
|
|
||||||
|
@@unique([instanceId, remoteJid])
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
@@index([remoteJid])
|
@@index([remoteJid])
|
||||||
@@unique([instanceId, remoteJid])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Contact {
|
model Contact {
|
||||||
@ -170,6 +172,7 @@ model Message {
|
|||||||
|
|
||||||
sessionId String?
|
sessionId String?
|
||||||
session IntegrationSession? @relation(fields: [sessionId], references: [id])
|
session IntegrationSession? @relation(fields: [sessionId], references: [id])
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +188,7 @@ model MessageUpdate {
|
|||||||
messageId String
|
messageId String
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String
|
instanceId String
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
@@index([messageId])
|
@@index([messageId])
|
||||||
}
|
}
|
||||||
@ -201,6 +205,7 @@ model Webhook {
|
|||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,6 +274,7 @@ model Setting {
|
|||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +288,16 @@ model Rabbitmq {
|
|||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Nats {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
enabled Boolean @default(false)
|
||||||
|
events Json @db.Json
|
||||||
|
createdAt DateTime? @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp
|
||||||
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
|
instanceId String @unique
|
||||||
|
}
|
||||||
|
|
||||||
model Sqs {
|
model Sqs {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
enabled Boolean @default(false)
|
enabled Boolean @default(false)
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Nats" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"events" JSONB NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP NOT NULL,
|
||||||
|
"instanceId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Nats_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Nats_instanceId_key" ON "Nats"("instanceId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Nats" ADD CONSTRAINT "Nats_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "Instance"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -86,6 +86,7 @@ model Instance {
|
|||||||
Proxy Proxy?
|
Proxy Proxy?
|
||||||
Setting Setting?
|
Setting Setting?
|
||||||
Rabbitmq Rabbitmq?
|
Rabbitmq Rabbitmq?
|
||||||
|
Nats Nats?
|
||||||
Sqs Sqs?
|
Sqs Sqs?
|
||||||
Websocket Websocket?
|
Websocket Websocket?
|
||||||
Typebot Typebot[]
|
Typebot Typebot[]
|
||||||
@ -99,7 +100,7 @@ model Instance {
|
|||||||
Template Template[]
|
Template Template[]
|
||||||
Dify Dify[]
|
Dify Dify[]
|
||||||
DifySetting DifySetting?
|
DifySetting DifySetting?
|
||||||
integrationSessions IntegrationSession[]
|
IntegrationSession IntegrationSession[]
|
||||||
EvolutionBot EvolutionBot[]
|
EvolutionBot EvolutionBot[]
|
||||||
EvolutionBotSetting EvolutionBotSetting?
|
EvolutionBotSetting EvolutionBotSetting?
|
||||||
Flowise Flowise[]
|
Flowise Flowise[]
|
||||||
@ -125,6 +126,7 @@ model Chat {
|
|||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String
|
instanceId String
|
||||||
unreadMessages Int @default(0)
|
unreadMessages Int @default(0)
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
@@index([remoteJid])
|
@@index([remoteJid])
|
||||||
}
|
}
|
||||||
@ -168,6 +170,7 @@ model Message {
|
|||||||
|
|
||||||
sessionId String?
|
sessionId String?
|
||||||
session IntegrationSession? @relation(fields: [sessionId], references: [id])
|
session IntegrationSession? @relation(fields: [sessionId], references: [id])
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +186,7 @@ model MessageUpdate {
|
|||||||
messageId String
|
messageId String
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String
|
instanceId String
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
@@index([messageId])
|
@@index([messageId])
|
||||||
}
|
}
|
||||||
@ -199,6 +203,7 @@ model Webhook {
|
|||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,6 +274,7 @@ model Setting {
|
|||||||
updatedAt DateTime @updatedAt @db.Timestamp
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
|
|
||||||
@@index([instanceId])
|
@@index([instanceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +288,16 @@ model Rabbitmq {
|
|||||||
instanceId String @unique
|
instanceId String @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Nats {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
enabled Boolean @default(false) @db.Boolean
|
||||||
|
events Json @db.JsonB
|
||||||
|
createdAt DateTime? @default(now()) @db.Timestamp
|
||||||
|
updatedAt DateTime @updatedAt @db.Timestamp
|
||||||
|
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
|
||||||
|
instanceId String @unique
|
||||||
|
}
|
||||||
|
|
||||||
model Sqs {
|
model Sqs {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
enabled Boolean @default(false) @db.Boolean
|
enabled Boolean @default(false) @db.Boolean
|
||||||
|
15
src/api/controllers/business.controller.ts
Normal file
15
src/api/controllers/business.controller.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { getCatalogDto, getCollectionsDto } from '@api/dto/business.dto';
|
||||||
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
|
|
||||||
|
export class BusinessController {
|
||||||
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
|
public async fetchCatalog({ instanceName }: InstanceDto, data: getCatalogDto) {
|
||||||
|
return await this.waMonitor.waInstances[instanceName].fetchCatalog(instanceName, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchCollections({ instanceName }: InstanceDto, data: getCollectionsDto) {
|
||||||
|
return await this.waMonitor.waInstances[instanceName].fetchCollections(instanceName, data);
|
||||||
|
}
|
||||||
|
}
|
@ -170,6 +170,9 @@ export class InstanceController {
|
|||||||
rabbitmq: {
|
rabbitmq: {
|
||||||
enabled: instanceData?.rabbitmq?.enabled,
|
enabled: instanceData?.rabbitmq?.enabled,
|
||||||
},
|
},
|
||||||
|
nats: {
|
||||||
|
enabled: instanceData?.nats?.enabled,
|
||||||
|
},
|
||||||
sqs: {
|
sqs: {
|
||||||
enabled: instanceData?.sqs?.enabled,
|
enabled: instanceData?.sqs?.enabled,
|
||||||
},
|
},
|
||||||
@ -258,6 +261,9 @@ export class InstanceController {
|
|||||||
rabbitmq: {
|
rabbitmq: {
|
||||||
enabled: instanceData?.rabbitmq?.enabled,
|
enabled: instanceData?.rabbitmq?.enabled,
|
||||||
},
|
},
|
||||||
|
nats: {
|
||||||
|
enabled: instanceData?.nats?.enabled,
|
||||||
|
},
|
||||||
sqs: {
|
sqs: {
|
||||||
enabled: instanceData?.sqs?.enabled,
|
enabled: instanceData?.sqs?.enabled,
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,14 @@ 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';
|
||||||
|
|
||||||
|
function isEmoji(str: string) {
|
||||||
|
if (str === '') return true;
|
||||||
|
|
||||||
|
const emojiRegex =
|
||||||
|
/^[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F000}-\u{1F02F}\u{1F0A0}-\u{1F0FF}\u{1F100}-\u{1F64F}\u{1F680}-\u{1F6FF}]$/u;
|
||||||
|
return emojiRegex.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
export class SendMessageController {
|
export class SendMessageController {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
@ -81,8 +89,8 @@ export class SendMessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
||||||
if (!data.reaction.match(/[^()\w\sà-ú"-+]+/)) {
|
if (!isEmoji(data.reaction)) {
|
||||||
throw new BadRequestException('"reaction" must be an emoji');
|
throw new BadRequestException('Reaction must be a single emoji or empty string');
|
||||||
}
|
}
|
||||||
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
||||||
}
|
}
|
||||||
|
14
src/api/dto/business.dto.ts
Normal file
14
src/api/dto/business.dto.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export class NumberDto {
|
||||||
|
number: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class getCatalogDto {
|
||||||
|
number?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class getCollectionsDto {
|
||||||
|
number?: string;
|
||||||
|
limit?: number;
|
||||||
|
}
|
@ -44,6 +44,7 @@ export class Metadata {
|
|||||||
mentionsEveryOne?: boolean;
|
mentionsEveryOne?: boolean;
|
||||||
mentioned?: string[];
|
mentioned?: string[];
|
||||||
encoding?: boolean;
|
encoding?: boolean;
|
||||||
|
notConvertSticker?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SendTextDto extends Metadata {
|
export class SendTextDto extends Metadata {
|
||||||
|
@ -206,6 +206,20 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private messageLocationJson(received: any) {
|
||||||
|
const message = received.messages[0];
|
||||||
|
let content: any = {
|
||||||
|
locationMessage: {
|
||||||
|
degreesLatitude: message.location.latitude,
|
||||||
|
degreesLongitude: message.location.longitude,
|
||||||
|
name: message.location?.name,
|
||||||
|
address: message.location?.address,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
message.context ? (content = { ...content, contextInfo: { stanzaId: message.context.id } }) : content;
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
private messageContactsJson(received: any) {
|
private messageContactsJson(received: any) {
|
||||||
const message = received.messages[0];
|
const message = received.messages[0];
|
||||||
let content: any = {};
|
let content: any = {};
|
||||||
@ -283,6 +297,9 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
case 'template':
|
case 'template':
|
||||||
messageType = 'conversation';
|
messageType = 'conversation';
|
||||||
break;
|
break;
|
||||||
|
case 'location':
|
||||||
|
messageType = 'locationMessage';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
messageType = 'conversation';
|
messageType = 'conversation';
|
||||||
break;
|
break;
|
||||||
@ -438,6 +455,17 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
source: 'unknown',
|
source: 'unknown',
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
|
} else if (received?.messages[0].location) {
|
||||||
|
messageRaw = {
|
||||||
|
key,
|
||||||
|
pushName,
|
||||||
|
message: this.messageLocationJson(received),
|
||||||
|
contextInfo: this.messageLocationJson(received)?.contextInfo,
|
||||||
|
messageType: this.renderMessageType(received.messages[0].type),
|
||||||
|
messageTimestamp: parseInt(received.messages[0].timestamp) as number,
|
||||||
|
source: 'unknown',
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
messageRaw = {
|
messageRaw = {
|
||||||
key,
|
key,
|
||||||
@ -800,6 +828,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
if (message['media']) {
|
if (message['media']) {
|
||||||
const isImage = message['mimetype']?.startsWith('image/');
|
const isImage = message['mimetype']?.startsWith('image/');
|
||||||
|
const isVideo = message['mimetype']?.startsWith('video/');
|
||||||
|
|
||||||
content = {
|
content = {
|
||||||
messaging_product: 'whatsapp',
|
messaging_product: 'whatsapp',
|
||||||
@ -809,7 +838,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
[message['mediaType']]: {
|
[message['mediaType']]: {
|
||||||
[message['type']]: message['id'],
|
[message['type']]: message['id'],
|
||||||
preview_url: linkPreview,
|
preview_url: linkPreview,
|
||||||
...(message['fileName'] && !isImage && { filename: message['fileName'] }),
|
...(message['fileName'] && !isImage && !isVideo && { filename: message['fileName'] }),
|
||||||
caption: message['caption'],
|
caption: message['caption'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -977,8 +1006,10 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
private async getIdMedia(mediaMessage: any) {
|
private async getIdMedia(mediaMessage: any) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
const media = mediaMessage.media || mediaMessage.audio;
|
||||||
|
if (!media) throw new Error('Media or audio not found');
|
||||||
|
|
||||||
const fileStream = createReadStream(mediaMessage.media);
|
const fileStream = createReadStream(media);
|
||||||
|
|
||||||
formData.append('file', fileStream, { filename: 'media', contentType: mediaMessage.mimetype });
|
formData.append('file', fileStream, { filename: 'media', contentType: mediaMessage.mimetype });
|
||||||
formData.append('typeFile', mediaMessage.mimetype);
|
formData.append('typeFile', mediaMessage.mimetype);
|
||||||
@ -1079,7 +1110,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
const prepareMedia: any = {
|
const prepareMedia: any = {
|
||||||
fileName: `${hash}.mp3`,
|
fileName: `${hash}.mp3`,
|
||||||
mediaType: 'audio',
|
mediaType: 'audio',
|
||||||
media: audio,
|
audio,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isURL(audio)) {
|
if (isURL(audio)) {
|
||||||
@ -1101,15 +1132,7 @@ export class BusinessStartupService extends ChannelStartupService {
|
|||||||
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
|
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
|
||||||
const mediaData: SendAudioDto = { ...data };
|
const mediaData: SendAudioDto = { ...data };
|
||||||
|
|
||||||
if (file?.buffer) {
|
if (file) mediaData.audio = file.buffer.toString('base64');
|
||||||
mediaData.audio = file.buffer.toString('base64');
|
|
||||||
} else if (isURL(mediaData.audio)) {
|
|
||||||
// DO NOTHING
|
|
||||||
// mediaData.audio = mediaData.audio;
|
|
||||||
} else {
|
|
||||||
console.error('El archivo no tiene buffer o file es undefined');
|
|
||||||
throw new Error('File or buffer is undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = await this.processAudio(mediaData.audio, data.number);
|
const message = await this.processAudio(mediaData.audio, data.number);
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { getCollectionsDto } from '@api/dto/business.dto';
|
||||||
import { OfferCallDto } from '@api/dto/call.dto';
|
import { OfferCallDto } from '@api/dto/call.dto';
|
||||||
import {
|
import {
|
||||||
ArchiveChatDto,
|
ArchiveChatDto,
|
||||||
@ -91,6 +92,7 @@ import makeWASocket, {
|
|||||||
BufferedEventData,
|
BufferedEventData,
|
||||||
BufferJSON,
|
BufferJSON,
|
||||||
CacheStore,
|
CacheStore,
|
||||||
|
CatalogCollection,
|
||||||
Chat,
|
Chat,
|
||||||
ConnectionState,
|
ConnectionState,
|
||||||
Contact,
|
Contact,
|
||||||
@ -100,6 +102,7 @@ import makeWASocket, {
|
|||||||
fetchLatestBaileysVersion,
|
fetchLatestBaileysVersion,
|
||||||
generateWAMessageFromContent,
|
generateWAMessageFromContent,
|
||||||
getAggregateVotesInPollMessage,
|
getAggregateVotesInPollMessage,
|
||||||
|
GetCatalogOptions,
|
||||||
getContentType,
|
getContentType,
|
||||||
getDevice,
|
getDevice,
|
||||||
GroupMetadata,
|
GroupMetadata,
|
||||||
@ -113,6 +116,7 @@ import makeWASocket, {
|
|||||||
MiscMessageGenerationOptions,
|
MiscMessageGenerationOptions,
|
||||||
ParticipantAction,
|
ParticipantAction,
|
||||||
prepareWAMessageMedia,
|
prepareWAMessageMedia,
|
||||||
|
Product,
|
||||||
proto,
|
proto,
|
||||||
UserFacingSocketConfig,
|
UserFacingSocketConfig,
|
||||||
WABrowserDescription,
|
WABrowserDescription,
|
||||||
@ -1128,37 +1132,74 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (received.message?.protocolMessage?.editedMessage || received.message?.editedMessage?.message) {
|
const editedMessage = received?.message?.protocolMessage || received?.message?.editedMessage?.message?.protocolMessage;
|
||||||
const editedMessage =
|
|
||||||
received.message?.protocolMessage || received.message?.editedMessage?.message?.protocolMessage;
|
if (received.message?.protocolMessage?.editedMessage && editedMessage) {
|
||||||
if (editedMessage) {
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled)
|
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled)
|
||||||
this.chatwootService.eventWhatsapp(
|
this.chatwootService.eventWhatsapp(
|
||||||
'messages.edit',
|
'messages.edit',
|
||||||
{ instanceName: this.instance.name, instanceId: this.instance.id },
|
{ instanceName: this.instance.name, instanceId: this.instance.id },
|
||||||
editedMessage,
|
editedMessage,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage);
|
await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage);
|
||||||
}
|
const oldMessage = await this.getMessage(editedMessage.key, true);
|
||||||
|
if ((oldMessage as any)?.id) {
|
||||||
|
|
||||||
|
if (Long.isLong(editedMessage?.timestampMs)) {
|
||||||
|
editedMessage.timestampMs = editedMessage.timestampMs?.toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (received.messageStubParameters && received.messageStubParameters[0] === 'Message absent from node') {
|
await this.prismaRepository.message.update({
|
||||||
this.logger.info(`Recovering message lost messageId: ${received.key.id}`);
|
where: { id: (oldMessage as any).id },
|
||||||
|
data: {
|
||||||
await this.baileysCache.set(received.key.id, {
|
message: editedMessage.editedMessage as any,
|
||||||
message: received,
|
messageTimestamp: editedMessage.timestampMs,
|
||||||
retry: 0,
|
status: 'EDITED',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
await this.prismaRepository.messageUpdate.create({
|
||||||
|
data: {
|
||||||
|
fromMe: editedMessage.key.fromMe,
|
||||||
|
keyId: editedMessage.key.id,
|
||||||
|
remoteJid: editedMessage.key.remoteJid,
|
||||||
|
status: 'EDITED',
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
messageId: (oldMessage as any).id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (received.messageStubParameters && received.messageStubParameters[0] === 'Message absent from node') {
|
||||||
|
// this.logger.info(`Recovering message lost messageId: ${received.key.id}`);
|
||||||
|
|
||||||
|
// await this.baileysCache.set(received.key.id, {
|
||||||
|
// message: received,
|
||||||
|
// retry: 0,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const retryCache = (await this.baileysCache.get(received.key.id)) || null;
|
||||||
|
|
||||||
|
// if (retryCache) {
|
||||||
|
// this.logger.info('Recovered message lost');
|
||||||
|
// await this.baileysCache.delete(received.key.id);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Cache to avoid duplicate messages
|
||||||
|
const messageKey = `${this.instance.id}_${received.key.id}`;
|
||||||
|
const cached = await this.baileysCache.get(messageKey);
|
||||||
|
|
||||||
|
if (cached && !editedMessage) {
|
||||||
|
this.logger.info(`Message duplicated ignored: ${received.key.id}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const retryCache = (await this.baileysCache.get(received.key.id)) || null;
|
await this.baileysCache.set(messageKey, true, 30 * 60);
|
||||||
|
|
||||||
if (retryCache) {
|
|
||||||
this.logger.info('Recovered message lost');
|
|
||||||
await this.baileysCache.delete(received.key.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(type !== 'notify' && type !== 'append') ||
|
(type !== 'notify' && type !== 'append') ||
|
||||||
@ -1185,7 +1226,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
existingChat &&
|
existingChat &&
|
||||||
received.pushName &&
|
received.pushName &&
|
||||||
existingChat.name !== received.pushName &&
|
existingChat.name !== received.pushName &&
|
||||||
received.pushName.trim().length > 0
|
received.pushName.trim().length > 0 &&
|
||||||
|
!received.key.remoteJid.includes('@g.us')
|
||||||
) {
|
) {
|
||||||
this.sendDataWebhook(Events.CHATS_UPSERT, [{ ...existingChat, name: received.pushName }]);
|
this.sendDataWebhook(Events.CHATS_UPSERT, [{ ...existingChat, name: received.pushName }]);
|
||||||
if (this.configService.get<Database>('DATABASE').SAVE_DATA.CHATS) {
|
if (this.configService.get<Database>('DATABASE').SAVE_DATA.CHATS) {
|
||||||
@ -1421,6 +1463,17 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateKey = `${this.instance.id}_${key.id}_${update.status}`;
|
||||||
|
|
||||||
|
const cached = await this.baileysCache.get(updateKey);
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
this.logger.info(`Message duplicated ignored: ${key.id}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.baileysCache.set(updateKey, true, 30 * 60);
|
||||||
|
|
||||||
if (status[update.status] === 'READ' && key.fromMe) {
|
if (status[update.status] === 'READ' && key.fromMe) {
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||||
this.chatwootService.eventWhatsapp(
|
this.chatwootService.eventWhatsapp(
|
||||||
@ -2716,7 +2769,9 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
if (file) mediaData.sticker = file.buffer.toString('base64');
|
if (file) mediaData.sticker = file.buffer.toString('base64');
|
||||||
|
|
||||||
const convert = await this.convertToWebP(data.sticker);
|
const convert = data?.notConvertSticker
|
||||||
|
? Buffer.from(data.sticker, 'base64')
|
||||||
|
: await this.convertToWebP(data.sticker);
|
||||||
const gifPlayback = data.sticker.includes('.gif');
|
const gifPlayback = data.sticker.includes('.gif');
|
||||||
const result = await this.sendMessageWithTyping(
|
const result = await this.sendMessageWithTyping(
|
||||||
data.number,
|
data.number,
|
||||||
@ -3572,6 +3627,18 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
status: 'DELETED',
|
status: 'DELETED',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const messageUpdate: any = {
|
||||||
|
messageId: message.id,
|
||||||
|
keyId: messageId,
|
||||||
|
remoteJid: response.key.remoteJid,
|
||||||
|
fromMe: response.key.fromMe,
|
||||||
|
participant: response.key?.remoteJid,
|
||||||
|
status: 'DELETED',
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
};
|
||||||
|
await this.prismaRepository.messageUpdate.create({
|
||||||
|
data: messageUpdate,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.prismaRepository.message.deleteMany({
|
await this.prismaRepository.message.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
@ -3898,28 +3965,75 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const oldMessage: any = await this.getMessage(data.key, true);
|
||||||
|
if (!oldMessage) throw new NotFoundException('Message not found');
|
||||||
|
if (oldMessage?.key?.remoteJid !== jid) {
|
||||||
|
throw new BadRequestException('RemoteJid does not match');
|
||||||
|
}
|
||||||
|
if (oldMessage?.messageTimestamp > Date.now() + 900000) {
|
||||||
|
// 15 minutes in milliseconds
|
||||||
|
throw new BadRequestException('Message is older than 15 minutes');
|
||||||
|
}
|
||||||
|
|
||||||
const messageSent = await this.client.sendMessage(jid, {
|
const messageSent = await this.client.sendMessage(jid, {
|
||||||
...(options as any),
|
...(options as any),
|
||||||
edit: data.key,
|
edit: data.key,
|
||||||
});
|
});
|
||||||
|
if (messageSent) {
|
||||||
|
const messageId = messageSent.message?.protocolMessage?.key?.id;
|
||||||
|
if (messageId) {
|
||||||
|
let message = await this.prismaRepository.message.findFirst({
|
||||||
|
where: {
|
||||||
|
key: {
|
||||||
|
path: ['id'],
|
||||||
|
equals: messageId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!message) throw new NotFoundException('Message not found');
|
||||||
|
|
||||||
const updatedMessage =
|
if (!(message.key.valueOf() as any).fromMe) {
|
||||||
messageSent.message?.protocolMessage || messageSent.message?.editedMessage?.message?.protocolMessage;
|
new BadRequestException('You cannot edit others messages');
|
||||||
|
}
|
||||||
|
if ((message.key.valueOf() as any)?.deleted) {
|
||||||
|
new BadRequestException('You cannot edit deleted messages');
|
||||||
|
}
|
||||||
|
if (oldMessage.messageType === 'conversation' || oldMessage.messageType === 'extendedTextMessage') {
|
||||||
|
oldMessage.message.conversation = data.text;
|
||||||
|
} else {
|
||||||
|
oldMessage.message[oldMessage.messageType].caption = data.text;
|
||||||
|
}
|
||||||
|
message = await this.prismaRepository.message.update({
|
||||||
|
where: { id: message.id },
|
||||||
|
data: {
|
||||||
|
message: oldMessage.message,
|
||||||
|
status: 'EDITED',
|
||||||
|
messageTimestamp: Math.floor(Date.now() / 1000), // Convert to int32 by dividing by 1000 to get seconds
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const messageUpdate: any = {
|
||||||
|
messageId: message.id,
|
||||||
|
keyId: messageId,
|
||||||
|
remoteJid: messageSent.key.remoteJid,
|
||||||
|
fromMe: messageSent.key.fromMe,
|
||||||
|
participant: messageSent.key?.remoteJid,
|
||||||
|
status: 'EDITED',
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
};
|
||||||
|
await this.prismaRepository.messageUpdate.create({
|
||||||
|
data: messageUpdate,
|
||||||
|
});
|
||||||
|
|
||||||
if (updatedMessage) {
|
const editedMessage = messageSent?.message?.protocolMessage || messageSent?.message?.editedMessage?.message?.protocolMessage;
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled)
|
|
||||||
this.chatwootService.eventWhatsapp(
|
this.sendDataWebhook(Events.SEND_MESSAGE_UPDATE, editedMessage);
|
||||||
'send.message.update',
|
}
|
||||||
{ instanceName: this.instance.name, instanceId: this.instance.id },
|
|
||||||
updatedMessage,
|
|
||||||
);
|
|
||||||
await this.sendDataWebhook(Events.SEND_MESSAGE_UPDATE, updatedMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageSent;
|
return messageSent;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
throw new BadRequestException(error.toString());
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4548,4 +4662,137 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Business Controller
|
||||||
|
public async fetchCatalog(instanceName: string, data: getCollectionsDto) {
|
||||||
|
const jid = data.number ? createJid(data.number) : this.client?.user?.id;
|
||||||
|
const limit = data.limit || 10;
|
||||||
|
const cursor = null;
|
||||||
|
|
||||||
|
const onWhatsapp = (await this.whatsappNumber({ numbers: [jid] }))?.shift();
|
||||||
|
|
||||||
|
if (!onWhatsapp.exists) {
|
||||||
|
throw new BadRequestException(onWhatsapp);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info = (await this.whatsappNumber({ numbers: [jid] }))?.shift();
|
||||||
|
const business = await this.fetchBusinessProfile(info?.jid);
|
||||||
|
|
||||||
|
let catalog = await this.getCatalog({ jid: info?.jid, limit, cursor });
|
||||||
|
let nextPageCursor = catalog.nextPageCursor;
|
||||||
|
let nextPageCursorJson = nextPageCursor ? JSON.parse(atob(nextPageCursor)) : null;
|
||||||
|
let pagination = nextPageCursorJson?.pagination_cursor
|
||||||
|
? JSON.parse(atob(nextPageCursorJson.pagination_cursor))
|
||||||
|
: null;
|
||||||
|
let fetcherHasMore = pagination?.fetcher_has_more === true ? true : false;
|
||||||
|
|
||||||
|
let productsCatalog = catalog.products || [];
|
||||||
|
let countLoops = 0;
|
||||||
|
while (fetcherHasMore && countLoops < 4) {
|
||||||
|
catalog = await this.getCatalog({ jid: info?.jid, limit, cursor: nextPageCursor });
|
||||||
|
nextPageCursor = catalog.nextPageCursor;
|
||||||
|
nextPageCursorJson = nextPageCursor ? JSON.parse(atob(nextPageCursor)) : null;
|
||||||
|
pagination = nextPageCursorJson?.pagination_cursor
|
||||||
|
? JSON.parse(atob(nextPageCursorJson.pagination_cursor))
|
||||||
|
: null;
|
||||||
|
fetcherHasMore = pagination?.fetcher_has_more === true ? true : false;
|
||||||
|
productsCatalog = [...productsCatalog, ...catalog.products];
|
||||||
|
countLoops++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
wuid: info?.jid || jid,
|
||||||
|
numberExists: info?.exists,
|
||||||
|
isBusiness: business.isBusiness,
|
||||||
|
catalogLength: productsCatalog.length,
|
||||||
|
catalog: productsCatalog,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
wuid: jid,
|
||||||
|
name: null,
|
||||||
|
isBusiness: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getCatalog({
|
||||||
|
jid,
|
||||||
|
limit,
|
||||||
|
cursor,
|
||||||
|
}: GetCatalogOptions): Promise<{ products: Product[]; nextPageCursor: string | undefined }> {
|
||||||
|
try {
|
||||||
|
jid = jid ? createJid(jid) : this.instance.wuid;
|
||||||
|
|
||||||
|
const catalog = await this.client.getCatalog({ jid, limit: limit, cursor: cursor });
|
||||||
|
|
||||||
|
if (!catalog) {
|
||||||
|
return {
|
||||||
|
products: undefined,
|
||||||
|
nextPageCursor: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return catalog;
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalServerErrorException('Error getCatalog', error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchCollections(instanceName: string, data: getCollectionsDto) {
|
||||||
|
const jid = data.number ? createJid(data.number) : this.client?.user?.id;
|
||||||
|
const limit = data.limit <= 20 ? data.limit : 20; //(tem esse limite, não sei porque)
|
||||||
|
|
||||||
|
const onWhatsapp = (await this.whatsappNumber({ numbers: [jid] }))?.shift();
|
||||||
|
|
||||||
|
if (!onWhatsapp.exists) {
|
||||||
|
throw new BadRequestException(onWhatsapp);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info = (await this.whatsappNumber({ numbers: [jid] }))?.shift();
|
||||||
|
const business = await this.fetchBusinessProfile(info?.jid);
|
||||||
|
const collections = await this.getCollections(info?.jid, limit);
|
||||||
|
|
||||||
|
return {
|
||||||
|
wuid: info?.jid || jid,
|
||||||
|
name: info?.name,
|
||||||
|
numberExists: info?.exists,
|
||||||
|
isBusiness: business.isBusiness,
|
||||||
|
collectionsLength: collections?.length,
|
||||||
|
collections: collections,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
wuid: jid,
|
||||||
|
name: null,
|
||||||
|
isBusiness: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getCollections(jid?: string | undefined, limit?: number): Promise<CatalogCollection[]> {
|
||||||
|
try {
|
||||||
|
jid = jid ? createJid(jid) : this.instance.wuid;
|
||||||
|
|
||||||
|
const result = await this.client.getCollections(jid, limit);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
products: [],
|
||||||
|
status: undefined,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.collections;
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalServerErrorException('Error getCatalog', error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1106,7 +1106,7 @@ export class ChatwootService {
|
|||||||
|
|
||||||
sendTelemetry('/message/sendWhatsAppAudio');
|
sendTelemetry('/message/sendWhatsAppAudio');
|
||||||
|
|
||||||
const messageSent = await waInstance?.audioWhatsapp(data, true);
|
const messageSent = await waInstance?.audioWhatsapp(data, null, true);
|
||||||
|
|
||||||
return messageSent;
|
return messageSent;
|
||||||
}
|
}
|
||||||
@ -1898,7 +1898,7 @@ export class ChatwootService {
|
|||||||
.replaceAll(/~((?!\s)([^\n~]+?)(?<!\s))~/g, '~~$1~~')
|
.replaceAll(/~((?!\s)([^\n~]+?)(?<!\s))~/g, '~~$1~~')
|
||||||
: originalMessage;
|
: originalMessage;
|
||||||
|
|
||||||
if (bodyMessage && bodyMessage.includes('Por favor, classifique esta conversa, http')) {
|
if (bodyMessage && bodyMessage.includes('/survey/responses/') && bodyMessage.includes('http')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,10 +1018,6 @@ export class TypebotController extends ChatbotController implements ChatbotContr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session && !session.awaitUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debounceTime && debounceTime > 0) {
|
if (debounceTime && debounceTime > 0) {
|
||||||
this.processDebounce(this.userMessageDebounce, content, remoteJid, debounceTime, async (debouncedContent) => {
|
this.processDebounce(this.userMessageDebounce, content, remoteJid, debounceTime, async (debouncedContent) => {
|
||||||
await this.typebotService.processTypebot(
|
await this.typebotService.processTypebot(
|
||||||
|
@ -741,6 +741,10 @@ export class TypebotService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session && !session.awaitUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (session && session.status !== 'opened') {
|
if (session && session.status !== 'opened') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -152,5 +152,8 @@ export class EventController {
|
|||||||
'TYPEBOT_CHANGE_STATUS',
|
'TYPEBOT_CHANGE_STATUS',
|
||||||
'REMOVE_INSTANCE',
|
'REMOVE_INSTANCE',
|
||||||
'LOGOUT_INSTANCE',
|
'LOGOUT_INSTANCE',
|
||||||
|
'INSTANCE_CREATE',
|
||||||
|
'INSTANCE_DELETE',
|
||||||
|
'STATUS_INSTANCE',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ export class EventDto {
|
|||||||
events?: string[];
|
events?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nats?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
events?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
pusher?: {
|
pusher?: {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
appId?: string;
|
appId?: string;
|
||||||
@ -63,6 +68,11 @@ export function EventInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
|||||||
events?: string[];
|
events?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nats?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
events?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
pusher?: {
|
pusher?: {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
appId?: string;
|
appId?: string;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { NatsController } from '@api/integrations/event/nats/nats.controller';
|
||||||
import { PusherController } from '@api/integrations/event/pusher/pusher.controller';
|
import { PusherController } from '@api/integrations/event/pusher/pusher.controller';
|
||||||
import { RabbitmqController } from '@api/integrations/event/rabbitmq/rabbitmq.controller';
|
import { RabbitmqController } from '@api/integrations/event/rabbitmq/rabbitmq.controller';
|
||||||
import { SqsController } from '@api/integrations/event/sqs/sqs.controller';
|
import { SqsController } from '@api/integrations/event/sqs/sqs.controller';
|
||||||
@ -13,6 +14,7 @@ export class EventManager {
|
|||||||
private websocketController: WebsocketController;
|
private websocketController: WebsocketController;
|
||||||
private webhookController: WebhookController;
|
private webhookController: WebhookController;
|
||||||
private rabbitmqController: RabbitmqController;
|
private rabbitmqController: RabbitmqController;
|
||||||
|
private natsController: NatsController;
|
||||||
private sqsController: SqsController;
|
private sqsController: SqsController;
|
||||||
private pusherController: PusherController;
|
private pusherController: PusherController;
|
||||||
|
|
||||||
@ -23,6 +25,7 @@ export class EventManager {
|
|||||||
this.websocket = new WebsocketController(prismaRepository, waMonitor);
|
this.websocket = new WebsocketController(prismaRepository, waMonitor);
|
||||||
this.webhook = new WebhookController(prismaRepository, waMonitor);
|
this.webhook = new WebhookController(prismaRepository, waMonitor);
|
||||||
this.rabbitmq = new RabbitmqController(prismaRepository, waMonitor);
|
this.rabbitmq = new RabbitmqController(prismaRepository, waMonitor);
|
||||||
|
this.nats = new NatsController(prismaRepository, waMonitor);
|
||||||
this.sqs = new SqsController(prismaRepository, waMonitor);
|
this.sqs = new SqsController(prismaRepository, waMonitor);
|
||||||
this.pusher = new PusherController(prismaRepository, waMonitor);
|
this.pusher = new PusherController(prismaRepository, waMonitor);
|
||||||
}
|
}
|
||||||
@ -67,6 +70,14 @@ export class EventManager {
|
|||||||
return this.rabbitmqController;
|
return this.rabbitmqController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set nats(nats: NatsController) {
|
||||||
|
this.natsController = nats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get nats() {
|
||||||
|
return this.natsController;
|
||||||
|
}
|
||||||
|
|
||||||
public set sqs(sqs: SqsController) {
|
public set sqs(sqs: SqsController) {
|
||||||
this.sqsController = sqs;
|
this.sqsController = sqs;
|
||||||
}
|
}
|
||||||
@ -85,6 +96,7 @@ export class EventManager {
|
|||||||
public init(httpServer: Server): void {
|
public init(httpServer: Server): void {
|
||||||
this.websocket.init(httpServer);
|
this.websocket.init(httpServer);
|
||||||
this.rabbitmq.init();
|
this.rabbitmq.init();
|
||||||
|
this.nats.init();
|
||||||
this.sqs.init();
|
this.sqs.init();
|
||||||
this.pusher.init();
|
this.pusher.init();
|
||||||
}
|
}
|
||||||
@ -103,6 +115,7 @@ export class EventManager {
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
await this.websocket.emit(eventData);
|
await this.websocket.emit(eventData);
|
||||||
await this.rabbitmq.emit(eventData);
|
await this.rabbitmq.emit(eventData);
|
||||||
|
await this.nats.emit(eventData);
|
||||||
await this.sqs.emit(eventData);
|
await this.sqs.emit(eventData);
|
||||||
await this.webhook.emit(eventData);
|
await this.webhook.emit(eventData);
|
||||||
await this.pusher.emit(eventData);
|
await this.pusher.emit(eventData);
|
||||||
@ -125,6 +138,14 @@ export class EventManager {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (data.nats)
|
||||||
|
await this.nats.set(instanceName, {
|
||||||
|
nats: {
|
||||||
|
enabled: true,
|
||||||
|
events: data.nats?.events,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (data.sqs)
|
if (data.sqs)
|
||||||
await this.sqs.set(instanceName, {
|
await this.sqs.set(instanceName, {
|
||||||
sqs: {
|
sqs: {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { NatsRouter } from '@api/integrations/event/nats/nats.router';
|
||||||
import { PusherRouter } from '@api/integrations/event/pusher/pusher.router';
|
import { PusherRouter } from '@api/integrations/event/pusher/pusher.router';
|
||||||
import { RabbitmqRouter } from '@api/integrations/event/rabbitmq/rabbitmq.router';
|
import { RabbitmqRouter } from '@api/integrations/event/rabbitmq/rabbitmq.router';
|
||||||
import { SqsRouter } from '@api/integrations/event/sqs/sqs.router';
|
import { SqsRouter } from '@api/integrations/event/sqs/sqs.router';
|
||||||
@ -14,6 +15,7 @@ export class EventRouter {
|
|||||||
this.router.use('/webhook', new WebhookRouter(configService, ...guards).router);
|
this.router.use('/webhook', new WebhookRouter(configService, ...guards).router);
|
||||||
this.router.use('/websocket', new WebsocketRouter(...guards).router);
|
this.router.use('/websocket', new WebsocketRouter(...guards).router);
|
||||||
this.router.use('/rabbitmq', new RabbitmqRouter(...guards).router);
|
this.router.use('/rabbitmq', new RabbitmqRouter(...guards).router);
|
||||||
|
this.router.use('/nats', new NatsRouter(...guards).router);
|
||||||
this.router.use('/pusher', new PusherRouter(...guards).router);
|
this.router.use('/pusher', new PusherRouter(...guards).router);
|
||||||
this.router.use('/sqs', new SqsRouter(...guards).router);
|
this.router.use('/sqs', new SqsRouter(...guards).router);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@ export const eventSchema: JSONSchema7 = {
|
|||||||
rabbitmq: {
|
rabbitmq: {
|
||||||
$ref: '#/$defs/event',
|
$ref: '#/$defs/event',
|
||||||
},
|
},
|
||||||
|
nats: {
|
||||||
|
$ref: '#/$defs/event',
|
||||||
|
},
|
||||||
sqs: {
|
sqs: {
|
||||||
$ref: '#/$defs/event',
|
$ref: '#/$defs/event',
|
||||||
},
|
},
|
||||||
|
161
src/api/integrations/event/nats/nats.controller.ts
Normal file
161
src/api/integrations/event/nats/nats.controller.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import { PrismaRepository } from '@api/repository/repository.service';
|
||||||
|
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||||
|
import { configService, Log, Nats } from '@config/env.config';
|
||||||
|
import { Logger } from '@config/logger.config';
|
||||||
|
import { connect, NatsConnection, StringCodec } from 'nats';
|
||||||
|
|
||||||
|
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||||
|
|
||||||
|
export class NatsController extends EventController implements EventControllerInterface {
|
||||||
|
public natsClient: NatsConnection | null = null;
|
||||||
|
private readonly logger = new Logger('NatsController');
|
||||||
|
private readonly sc = StringCodec();
|
||||||
|
|
||||||
|
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||||
|
super(prismaRepository, waMonitor, configService.get<Nats>('NATS')?.ENABLED, 'nats');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init(): Promise<void> {
|
||||||
|
if (!this.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const uri = configService.get<Nats>('NATS').URI;
|
||||||
|
|
||||||
|
this.natsClient = await connect({ servers: uri });
|
||||||
|
|
||||||
|
this.logger.info('NATS initialized');
|
||||||
|
|
||||||
|
if (configService.get<Nats>('NATS')?.GLOBAL_ENABLED) {
|
||||||
|
await this.initGlobalSubscriptions();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Failed to connect to NATS:');
|
||||||
|
this.logger.error(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async emit({
|
||||||
|
instanceName,
|
||||||
|
origin,
|
||||||
|
event,
|
||||||
|
data,
|
||||||
|
serverUrl,
|
||||||
|
dateTime,
|
||||||
|
sender,
|
||||||
|
apiKey,
|
||||||
|
integration,
|
||||||
|
}: EmitData): Promise<void> {
|
||||||
|
if (integration && !integration.includes('nats')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.status || !this.natsClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const instanceNats = await this.get(instanceName);
|
||||||
|
const natsLocal = instanceNats?.events;
|
||||||
|
const natsGlobal = configService.get<Nats>('NATS').GLOBAL_ENABLED;
|
||||||
|
const natsEvents = configService.get<Nats>('NATS').EVENTS;
|
||||||
|
const prefixKey = configService.get<Nats>('NATS').PREFIX_KEY;
|
||||||
|
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||||
|
const logEnabled = configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS');
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
event,
|
||||||
|
instance: instanceName,
|
||||||
|
data,
|
||||||
|
server_url: serverUrl,
|
||||||
|
date_time: dateTime,
|
||||||
|
sender,
|
||||||
|
apikey: apiKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Instância específica
|
||||||
|
if (instanceNats?.enabled) {
|
||||||
|
if (Array.isArray(natsLocal) && natsLocal.includes(we)) {
|
||||||
|
const subject = `${instanceName}.${event.toLowerCase()}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.natsClient.publish(subject, this.sc.encode(JSON.stringify(message)));
|
||||||
|
|
||||||
|
if (logEnabled) {
|
||||||
|
const logData = {
|
||||||
|
local: `${origin}.sendData-NATS`,
|
||||||
|
...message,
|
||||||
|
};
|
||||||
|
this.logger.log(logData);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to publish to NATS (instance): ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global
|
||||||
|
if (natsGlobal && natsEvents[we]) {
|
||||||
|
try {
|
||||||
|
const subject = prefixKey ? `${prefixKey}.${event.toLowerCase()}` : event.toLowerCase();
|
||||||
|
|
||||||
|
this.natsClient.publish(subject, this.sc.encode(JSON.stringify(message)));
|
||||||
|
|
||||||
|
if (logEnabled) {
|
||||||
|
const logData = {
|
||||||
|
local: `${origin}.sendData-NATS-Global`,
|
||||||
|
...message,
|
||||||
|
};
|
||||||
|
this.logger.log(logData);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to publish to NATS (global): ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initGlobalSubscriptions(): Promise<void> {
|
||||||
|
this.logger.info('Initializing global subscriptions');
|
||||||
|
|
||||||
|
const events = configService.get<Nats>('NATS').EVENTS;
|
||||||
|
const prefixKey = configService.get<Nats>('NATS').PREFIX_KEY;
|
||||||
|
|
||||||
|
if (!events) {
|
||||||
|
this.logger.warn('No events to initialize on NATS');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventKeys = Object.keys(events);
|
||||||
|
|
||||||
|
for (const event of eventKeys) {
|
||||||
|
if (events[event] === false) continue;
|
||||||
|
|
||||||
|
const subject = prefixKey ? `${prefixKey}.${event.toLowerCase()}` : event.toLowerCase();
|
||||||
|
|
||||||
|
// Criar uma subscription para cada evento
|
||||||
|
try {
|
||||||
|
const subscription = this.natsClient.subscribe(subject);
|
||||||
|
this.logger.info(`Subscribed to: ${subject}`);
|
||||||
|
|
||||||
|
// Processar mensagens (exemplo básico)
|
||||||
|
(async () => {
|
||||||
|
for await (const msg of subscription) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(this.sc.decode(msg.data));
|
||||||
|
// Aqui você pode adicionar a lógica de processamento
|
||||||
|
this.logger.debug(`Received message on ${subject}:`);
|
||||||
|
this.logger.debug(data);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error processing message on ${subject}:`);
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to subscribe to ${subject}:`);
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/api/integrations/event/nats/nats.router.ts
Normal file
36
src/api/integrations/event/nats/nats.router.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||||
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
|
import { EventDto } from '@api/integrations/event/event.dto';
|
||||||
|
import { HttpStatus } from '@api/routes/index.router';
|
||||||
|
import { eventManager } from '@api/server.module';
|
||||||
|
import { eventSchema, instanceSchema } from '@validate/validate.schema';
|
||||||
|
import { RequestHandler, Router } from 'express';
|
||||||
|
|
||||||
|
export class NatsRouter extends RouterBroker {
|
||||||
|
constructor(...guards: RequestHandler[]) {
|
||||||
|
super();
|
||||||
|
this.router
|
||||||
|
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<EventDto>({
|
||||||
|
request: req,
|
||||||
|
schema: eventSchema,
|
||||||
|
ClassRef: EventDto,
|
||||||
|
execute: (instance, data) => eventManager.nats.set(instance.instanceName, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
|
})
|
||||||
|
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => eventManager.nats.get(instance.instanceName),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly router: Router = Router();
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
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 { SQS } from '@aws-sdk/client-sqs';
|
import { CreateQueueCommand, DeleteQueueCommand, ListQueuesCommand, SQS } from '@aws-sdk/client-sqs';
|
||||||
import { configService, Log, Sqs } from '@config/env.config';
|
import { configService, Log, Sqs } from '@config/env.config';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
|
|
||||||
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||||
|
import { EventDto } from '../event.dto';
|
||||||
|
|
||||||
export class SqsController extends EventController implements EventControllerInterface {
|
export class SqsController extends EventController implements EventControllerInterface {
|
||||||
private sqs: SQS;
|
private sqs: SQS;
|
||||||
@ -45,6 +46,39 @@ export class SqsController extends EventController implements EventControllerInt
|
|||||||
return this.sqs;
|
return this.sqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override async set(instanceName: string, data: EventDto): Promise<any> {
|
||||||
|
if (!this.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data[this.name]?.enabled) {
|
||||||
|
data[this.name].events = [];
|
||||||
|
} else {
|
||||||
|
if (0 === data[this.name].events.length) {
|
||||||
|
data[this.name].events = EventController.events;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.saveQueues(instanceName, data[this.name].events, data[this.name]?.enabled);
|
||||||
|
|
||||||
|
const payload: any = {
|
||||||
|
where: {
|
||||||
|
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
enabled: data[this.name]?.enabled,
|
||||||
|
events: data[this.name].events,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
enabled: data[this.name]?.enabled,
|
||||||
|
events: data[this.name].events,
|
||||||
|
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log('*** payload: ', payload);
|
||||||
|
return this.prisma[this.name].upsert(payload);
|
||||||
|
}
|
||||||
|
|
||||||
public async emit({
|
public async emit({
|
||||||
instanceName,
|
instanceName,
|
||||||
origin,
|
origin,
|
||||||
@ -121,70 +155,92 @@ export class SqsController extends EventController implements EventControllerInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initQueues(instanceName: string, events: string[]) {
|
private async saveQueues(instanceName: string, events: string[], enable: boolean) {
|
||||||
if (!events || !events.length) return;
|
if (enable) {
|
||||||
|
const eventsFinded = await this.listQueuesByInstance(instanceName);
|
||||||
|
console.log('eventsFinded', eventsFinded);
|
||||||
|
|
||||||
const queues = events.map((event) => {
|
for (const event of events) {
|
||||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
const normalizedEvent = event.toLowerCase();
|
||||||
});
|
|
||||||
|
|
||||||
queues.forEach((event) => {
|
if (eventsFinded.includes(normalizedEvent)) {
|
||||||
const queueName = `${instanceName}_${event}.fifo`;
|
this.logger.info(`A queue para o evento "${normalizedEvent}" já existe. Ignorando criação.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.sqs.createQueue(
|
const queueName = `${instanceName}_${normalizedEvent}.fifo`;
|
||||||
{
|
|
||||||
|
try {
|
||||||
|
const createCommand = new CreateQueueCommand({
|
||||||
QueueName: queueName,
|
QueueName: queueName,
|
||||||
Attributes: {
|
Attributes: {
|
||||||
FifoQueue: 'true',
|
FifoQueue: 'true',
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
(err, data) => {
|
const data = await this.sqs.send(createCommand);
|
||||||
if (err) {
|
this.logger.info(`Queue ${queueName} criada: ${data.QueueUrl}`);
|
||||||
this.logger.error(`Error creating queue ${queueName}: ${err.message}`);
|
} catch (err: any) {
|
||||||
} else {
|
this.logger.error(`Erro ao criar queue ${queueName}: ${err.message}`);
|
||||||
this.logger.info(`Queue ${queueName} created: ${data.QueueUrl}`);
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async listQueuesByInstance(instanceName: string) {
|
||||||
|
let existingQueues: string[] = [];
|
||||||
|
try {
|
||||||
|
const listCommand = new ListQueuesCommand({
|
||||||
|
QueueNamePrefix: `${instanceName}_`,
|
||||||
|
});
|
||||||
|
const listData = await this.sqs.send(listCommand);
|
||||||
|
if (listData.QueueUrls && listData.QueueUrls.length > 0) {
|
||||||
|
// Extrai o nome da fila a partir da URL
|
||||||
|
existingQueues = listData.QueueUrls.map((queueUrl) => {
|
||||||
|
const parts = queueUrl.split('/');
|
||||||
|
return parts[parts.length - 1];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error(`Erro ao listar filas para a instância ${instanceName}: ${error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
public async removeQueues(instanceName: string, events: any) {
|
// Mapeia os eventos já existentes nas filas: remove o prefixo e o sufixo ".fifo"
|
||||||
const eventsArray = Array.isArray(events) ? events.map((event) => String(event)) : [];
|
return existingQueues
|
||||||
if (!events || !eventsArray.length) return;
|
.map((queueName) => {
|
||||||
|
// Espera-se que o nome seja `${instanceName}_${event}.fifo`
|
||||||
|
if (queueName.startsWith(`${instanceName}_`) && queueName.endsWith('.fifo')) {
|
||||||
|
return queueName.substring(instanceName.length + 1, queueName.length - 5).toLowerCase();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
})
|
||||||
|
.filter((event) => event !== '');
|
||||||
|
}
|
||||||
|
|
||||||
const queues = eventsArray.map((event) => {
|
// Para uma futura feature de exclusão forçada das queues
|
||||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
private async removeQueuesByInstance(instanceName: string) {
|
||||||
|
try {
|
||||||
|
const listCommand = new ListQueuesCommand({
|
||||||
|
QueueNamePrefix: `${instanceName}_`,
|
||||||
});
|
});
|
||||||
|
const listData = await this.sqs.send(listCommand);
|
||||||
|
|
||||||
queues.forEach((event) => {
|
if (!listData.QueueUrls || listData.QueueUrls.length === 0) {
|
||||||
const queueName = `${instanceName}_${event}.fifo`;
|
this.logger.info(`No queues found for instance ${instanceName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.sqs.getQueueUrl(
|
for (const queueUrl of listData.QueueUrls) {
|
||||||
{
|
try {
|
||||||
QueueName: queueName,
|
const deleteCommand = new DeleteQueueCommand({ QueueUrl: queueUrl });
|
||||||
},
|
await this.sqs.send(deleteCommand);
|
||||||
(err, data) => {
|
this.logger.info(`Queue ${queueUrl} deleted`);
|
||||||
if (err) {
|
} catch (err: any) {
|
||||||
this.logger.error(`Error getting queue URL for ${queueName}: ${err.message}`);
|
this.logger.error(`Error deleting queue ${queueUrl}: ${err.message}`);
|
||||||
} else {
|
}
|
||||||
const queueUrl = data.QueueUrl;
|
}
|
||||||
|
} catch (err: any) {
|
||||||
this.sqs.deleteQueue(
|
this.logger.error(`Error listing queues for instance ${instanceName}: ${err.message}`);
|
||||||
{
|
}
|
||||||
QueueUrl: queueUrl,
|
|
||||||
},
|
|
||||||
(deleteErr) => {
|
|
||||||
if (deleteErr) {
|
|
||||||
this.logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`);
|
|
||||||
} else {
|
|
||||||
this.logger.info(`Queue ${queueName} deleted`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { configService, Log, Webhook } from '@config/env.config';
|
|||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
import { BadRequestException } from '@exceptions';
|
import { BadRequestException } from '@exceptions';
|
||||||
import axios, { AxiosInstance } from 'axios';
|
import axios, { AxiosInstance } from 'axios';
|
||||||
import { isURL } from 'class-validator';
|
|
||||||
|
|
||||||
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
import { EmitData, EventController, EventControllerInterface } from '../event.controller';
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ export class WebhookController extends EventController implements EventControlle
|
|||||||
}
|
}
|
||||||
|
|
||||||
override async set(instanceName: string, data: EventDto): Promise<wa.LocalWebHook> {
|
override async set(instanceName: string, data: EventDto): Promise<wa.LocalWebHook> {
|
||||||
if (!isURL(data.webhook.url, { require_tld: false })) {
|
if (!/^(https?:\/\/)/.test(data.webhook.url)) {
|
||||||
throw new BadRequestException('Invalid "url" property');
|
throw new BadRequestException('Invalid "url" property');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +77,7 @@ export class WebhookController extends EventController implements EventControlle
|
|||||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||||
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
||||||
const enabledLog = configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS');
|
const enabledLog = configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS');
|
||||||
|
const regex = /^(https?:\/\/)/;
|
||||||
|
|
||||||
const webhookData = {
|
const webhookData = {
|
||||||
event,
|
event,
|
||||||
@ -111,7 +111,7 @@ export class WebhookController extends EventController implements EventControlle
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (instance?.enabled && isURL(instance.url, { require_tld: false })) {
|
if (instance?.enabled && regex.test(instance.url)) {
|
||||||
const httpService = axios.create({
|
const httpService = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
headers: webhookHeaders as Record<string, string> | undefined,
|
headers: webhookHeaders as Record<string, string> | undefined,
|
||||||
@ -155,7 +155,7 @@ export class WebhookController extends EventController implements EventControlle
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isURL(globalURL)) {
|
if (regex.test(globalURL)) {
|
||||||
const httpService = axios.create({ baseURL: globalURL });
|
const httpService = axios.create({ baseURL: globalURL });
|
||||||
|
|
||||||
await this.retryWebhookRequest(
|
await this.retryWebhookRequest(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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 { configService, Cors, Log, Websocket } from '@config/env.config';
|
import { Auth, configService, Cors, Log, Websocket } from '@config/env.config';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
import { Server } from 'http';
|
import { Server } from 'http';
|
||||||
import { Server as SocketIO } from 'socket.io';
|
import { Server as SocketIO } from 'socket.io';
|
||||||
@ -24,8 +24,40 @@ export class WebsocketController extends EventController implements EventControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.socket = new SocketIO(httpServer, {
|
this.socket = new SocketIO(httpServer, {
|
||||||
cors: {
|
cors: { origin: this.cors },
|
||||||
origin: this.cors,
|
allowRequest: async (req, callback) => {
|
||||||
|
try {
|
||||||
|
const url = new URL(req.url || '', 'http://localhost');
|
||||||
|
const params = new URLSearchParams(url.search);
|
||||||
|
|
||||||
|
// Permite conexões internas do Socket.IO (EIO=4 é o Engine.IO v4)
|
||||||
|
if (params.has('EIO')) {
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiKey = params.get('apikey') || (req.headers.apikey as string);
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
this.logger.error('Connection rejected: apiKey not provided');
|
||||||
|
return callback('apiKey is required', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = await this.prismaRepository.instance.findFirst({ where: { token: apiKey } });
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
const globalToken = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
|
||||||
|
if (apiKey !== globalToken) {
|
||||||
|
this.logger.error('Connection rejected: invalid global token');
|
||||||
|
return callback('Invalid global token', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, true);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Authentication error:');
|
||||||
|
this.logger.error(error);
|
||||||
|
callback('Authentication error', false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -101,10 +133,7 @@ export class WebsocketController extends EventController implements EventControl
|
|||||||
this.socket.emit(event, message);
|
this.socket.emit(event, message);
|
||||||
|
|
||||||
if (logEnabled) {
|
if (logEnabled) {
|
||||||
this.logger.log({
|
this.logger.log({ local: `${origin}.sendData-WebsocketGlobal`, ...message });
|
||||||
local: `${origin}.sendData-WebsocketGlobal`,
|
|
||||||
...message,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,10 +148,7 @@ export class WebsocketController extends EventController implements EventControl
|
|||||||
this.socket.of(`/${instanceName}`).emit(event, message);
|
this.socket.of(`/${instanceName}`).emit(event, message);
|
||||||
|
|
||||||
if (logEnabled) {
|
if (logEnabled) {
|
||||||
this.logger.log({
|
this.logger.log({ local: `${origin}.sendData-Websocket`, ...message });
|
||||||
local: `${origin}.sendData-Websocket`,
|
|
||||||
...message,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
37
src/api/routes/business.router.ts
Normal file
37
src/api/routes/business.router.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||||
|
import { NumberDto } from '@api/dto/chat.dto';
|
||||||
|
import { businessController } from '@api/server.module';
|
||||||
|
import { catalogSchema, collectionsSchema } from '@validate/validate.schema';
|
||||||
|
import { RequestHandler, Router } from 'express';
|
||||||
|
|
||||||
|
import { HttpStatus } from './index.router';
|
||||||
|
|
||||||
|
export class BusinessRouter extends RouterBroker {
|
||||||
|
constructor(...guards: RequestHandler[]) {
|
||||||
|
super();
|
||||||
|
this.router
|
||||||
|
.post(this.routerPath('getCatalog'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<NumberDto>({
|
||||||
|
request: req,
|
||||||
|
schema: catalogSchema,
|
||||||
|
ClassRef: NumberDto,
|
||||||
|
execute: (instance, data) => businessController.fetchCatalog(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
|
||||||
|
.post(this.routerPath('getCollections'), ...guards, async (req, res) => {
|
||||||
|
const response = await this.dataValidate<NumberDto>({
|
||||||
|
request: req,
|
||||||
|
schema: collectionsSchema,
|
||||||
|
ClassRef: NumberDto,
|
||||||
|
execute: (instance, data) => businessController.fetchCollections(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).json(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly router: Router = Router();
|
||||||
|
}
|
@ -207,7 +207,6 @@ export class ChatRouter extends RouterBroker {
|
|||||||
|
|
||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
|
|
||||||
.post(this.routerPath('updateProfileName'), ...guards, async (req, res) => {
|
.post(this.routerPath('updateProfileName'), ...guards, async (req, res) => {
|
||||||
const response = await this.dataValidate<ProfileNameDto>({
|
const response = await this.dataValidate<ProfileNameDto>({
|
||||||
request: req,
|
request: req,
|
||||||
|
@ -11,6 +11,7 @@ import fs from 'fs';
|
|||||||
import mimeTypes from 'mime-types';
|
import mimeTypes from 'mime-types';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
import { BusinessRouter } from './business.router';
|
||||||
import { CallRouter } from './call.router';
|
import { CallRouter } from './call.router';
|
||||||
import { ChatRouter } from './chat.router';
|
import { ChatRouter } from './chat.router';
|
||||||
import { GroupRouter } from './group.router';
|
import { GroupRouter } from './group.router';
|
||||||
@ -82,6 +83,7 @@ router
|
|||||||
.use('/message', new MessageRouter(...guards).router)
|
.use('/message', new MessageRouter(...guards).router)
|
||||||
.use('/call', new CallRouter(...guards).router)
|
.use('/call', new CallRouter(...guards).router)
|
||||||
.use('/chat', new ChatRouter(...guards).router)
|
.use('/chat', new ChatRouter(...guards).router)
|
||||||
|
.use('/business', new BusinessRouter(...guards).router)
|
||||||
.use('/group', new GroupRouter(...guards).router)
|
.use('/group', new GroupRouter(...guards).router)
|
||||||
.use('/template', new TemplateRouter(configService, ...guards).router)
|
.use('/template', new TemplateRouter(configService, ...guards).router)
|
||||||
.use('/settings', new SettingsRouter(...guards).router)
|
.use('/settings', new SettingsRouter(...guards).router)
|
||||||
|
@ -15,7 +15,6 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
super();
|
super();
|
||||||
this.router
|
this.router
|
||||||
.post('/create', ...guards, async (req, res) => {
|
.post('/create', ...guards, async (req, res) => {
|
||||||
console.log('create instance', req.body);
|
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceSchema,
|
schema: instanceSchema,
|
||||||
|
@ -3,6 +3,7 @@ import { Chatwoot, configService, ProviderSession } from '@config/env.config';
|
|||||||
import { eventEmitter } from '@config/event.config';
|
import { eventEmitter } from '@config/event.config';
|
||||||
import { Logger } from '@config/logger.config';
|
import { Logger } from '@config/logger.config';
|
||||||
|
|
||||||
|
import { BusinessController } from './controllers/business.controller';
|
||||||
import { CallController } from './controllers/call.controller';
|
import { CallController } from './controllers/call.controller';
|
||||||
import { ChatController } from './controllers/chat.controller';
|
import { ChatController } from './controllers/chat.controller';
|
||||||
import { GroupController } from './controllers/group.controller';
|
import { GroupController } from './controllers/group.controller';
|
||||||
@ -98,6 +99,7 @@ export const instanceController = new InstanceController(
|
|||||||
export const sendMessageController = new SendMessageController(waMonitor);
|
export const sendMessageController = new SendMessageController(waMonitor);
|
||||||
export const callController = new CallController(waMonitor);
|
export const callController = new CallController(waMonitor);
|
||||||
export const chatController = new ChatController(waMonitor);
|
export const chatController = new ChatController(waMonitor);
|
||||||
|
export const businessController = new BusinessController(waMonitor);
|
||||||
export const groupController = new GroupController(waMonitor);
|
export const groupController = new GroupController(waMonitor);
|
||||||
export const labelController = new LabelController(waMonitor);
|
export const labelController = new LabelController(waMonitor);
|
||||||
|
|
||||||
|
@ -503,9 +503,17 @@ export class ChannelStartupService {
|
|||||||
where['remoteJid'] = remoteJid;
|
where['remoteJid'] = remoteJid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.prismaRepository.contact.findMany({
|
const contactFindManyArgs: Prisma.ContactFindManyArgs = {
|
||||||
where,
|
where,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (query.offset) contactFindManyArgs.take = query.offset;
|
||||||
|
if (query.page) {
|
||||||
|
const validPage = Math.max(query.page as number, 1);
|
||||||
|
contactFindManyArgs.skip = query.offset * (validPage - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.prismaRepository.contact.findMany(contactFindManyArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public cleanMessageData(message: any) {
|
public cleanMessageData(message: any) {
|
||||||
@ -674,6 +682,13 @@ export class ChannelStartupService {
|
|||||||
: createJid(query.where?.remoteJid)
|
: createJid(query.where?.remoteJid)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const limit =
|
||||||
|
query.offset && !query.page
|
||||||
|
? Prisma.sql` LIMIT ${query.offset}`
|
||||||
|
: query.offset && query.page
|
||||||
|
? Prisma.sql` LIMIT ${query.offset} OFFSET ${((query.page as number) - 1) * query.offset}`
|
||||||
|
: Prisma.sql``;
|
||||||
|
|
||||||
const where = {
|
const where = {
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
@ -700,6 +715,7 @@ export class ChannelStartupService {
|
|||||||
to_timestamp("Message"."messageTimestamp"::double precision),
|
to_timestamp("Message"."messageTimestamp"::double precision),
|
||||||
"Contact"."updatedAt"
|
"Contact"."updatedAt"
|
||||||
) as "updatedAt",
|
) as "updatedAt",
|
||||||
|
"Chat"."name" as "chatName",
|
||||||
"Chat"."createdAt" as "windowStart",
|
"Chat"."createdAt" as "windowStart",
|
||||||
"Chat"."createdAt" + INTERVAL '24 hours' as "windowExpires",
|
"Chat"."createdAt" + INTERVAL '24 hours' as "windowExpires",
|
||||||
CASE
|
CASE
|
||||||
@ -730,6 +746,7 @@ export class ChannelStartupService {
|
|||||||
ORDER BY
|
ORDER BY
|
||||||
"Contact"."remoteJid",
|
"Contact"."remoteJid",
|
||||||
"Message"."messageTimestamp" DESC
|
"Message"."messageTimestamp" DESC
|
||||||
|
${limit}
|
||||||
)
|
)
|
||||||
SELECT * FROM rankedMessages
|
SELECT * FROM rankedMessages
|
||||||
ORDER BY "updatedAt" DESC NULLS LAST;
|
ORDER BY "updatedAt" DESC NULLS LAST;
|
||||||
@ -758,6 +775,7 @@ export class ChannelStartupService {
|
|||||||
id: contact.id,
|
id: contact.id,
|
||||||
remoteJid: contact.remoteJid,
|
remoteJid: contact.remoteJid,
|
||||||
pushName: contact.pushName,
|
pushName: contact.pushName,
|
||||||
|
chatName: contact.chatName,
|
||||||
profilePicUrl: contact.profilePicUrl,
|
profilePicUrl: contact.profilePicUrl,
|
||||||
updatedAt: contact.updatedAt,
|
updatedAt: contact.updatedAt,
|
||||||
windowStart: contact.windowStart,
|
windowStart: contact.windowStart,
|
||||||
|
@ -91,6 +91,7 @@ export class WAMonitoringService {
|
|||||||
Chatwoot: true,
|
Chatwoot: true,
|
||||||
Proxy: true,
|
Proxy: true,
|
||||||
Rabbitmq: true,
|
Rabbitmq: true,
|
||||||
|
Nats: true,
|
||||||
Sqs: true,
|
Sqs: true,
|
||||||
Websocket: true,
|
Websocket: true,
|
||||||
Setting: true,
|
Setting: true,
|
||||||
@ -190,6 +191,7 @@ export class WAMonitoringService {
|
|||||||
await this.prismaRepository.chatwoot.deleteMany({ where: { instanceId: instance.id } });
|
await this.prismaRepository.chatwoot.deleteMany({ where: { instanceId: instance.id } });
|
||||||
await this.prismaRepository.proxy.deleteMany({ where: { instanceId: instance.id } });
|
await this.prismaRepository.proxy.deleteMany({ where: { instanceId: instance.id } });
|
||||||
await this.prismaRepository.rabbitmq.deleteMany({ where: { instanceId: instance.id } });
|
await this.prismaRepository.rabbitmq.deleteMany({ where: { instanceId: instance.id } });
|
||||||
|
await this.prismaRepository.nats.deleteMany({ where: { instanceId: instance.id } });
|
||||||
await this.prismaRepository.sqs.deleteMany({ where: { instanceId: instance.id } });
|
await this.prismaRepository.sqs.deleteMany({ where: { instanceId: instance.id } });
|
||||||
await this.prismaRepository.integrationSession.deleteMany({ where: { instanceId: instance.id } });
|
await this.prismaRepository.integrationSession.deleteMany({ where: { instanceId: instance.id } });
|
||||||
await this.prismaRepository.typebot.deleteMany({ where: { instanceId: instance.id } });
|
await this.prismaRepository.typebot.deleteMany({ where: { instanceId: instance.id } });
|
||||||
|
@ -98,7 +98,16 @@ export type Rabbitmq = {
|
|||||||
EXCHANGE_NAME: string;
|
EXCHANGE_NAME: string;
|
||||||
GLOBAL_ENABLED: boolean;
|
GLOBAL_ENABLED: boolean;
|
||||||
EVENTS: EventsRabbitmq;
|
EVENTS: EventsRabbitmq;
|
||||||
PREFIX_KEY: string;
|
PREFIX_KEY?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Nats = {
|
||||||
|
ENABLED: boolean;
|
||||||
|
URI: string;
|
||||||
|
EXCHANGE_NAME: string;
|
||||||
|
GLOBAL_ENABLED: boolean;
|
||||||
|
EVENTS: EventsRabbitmq;
|
||||||
|
PREFIX_KEY?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Sqs = {
|
export type Sqs = {
|
||||||
@ -266,6 +275,7 @@ export interface Env {
|
|||||||
PROVIDER: ProviderSession;
|
PROVIDER: ProviderSession;
|
||||||
DATABASE: Database;
|
DATABASE: Database;
|
||||||
RABBITMQ: Rabbitmq;
|
RABBITMQ: Rabbitmq;
|
||||||
|
NATS: Nats;
|
||||||
SQS: Sqs;
|
SQS: Sqs;
|
||||||
WEBSOCKET: Websocket;
|
WEBSOCKET: Websocket;
|
||||||
WA_BUSINESS: WaBusiness;
|
WA_BUSINESS: WaBusiness;
|
||||||
@ -359,7 +369,7 @@ export class ConfigService {
|
|||||||
RABBITMQ: {
|
RABBITMQ: {
|
||||||
ENABLED: process.env?.RABBITMQ_ENABLED === 'true',
|
ENABLED: process.env?.RABBITMQ_ENABLED === 'true',
|
||||||
GLOBAL_ENABLED: process.env?.RABBITMQ_GLOBAL_ENABLED === 'true',
|
GLOBAL_ENABLED: process.env?.RABBITMQ_GLOBAL_ENABLED === 'true',
|
||||||
PREFIX_KEY: process.env?.RABBITMQ_PREFIX_KEY || 'evolution',
|
PREFIX_KEY: process.env?.RABBITMQ_PREFIX_KEY,
|
||||||
EXCHANGE_NAME: process.env?.RABBITMQ_EXCHANGE_NAME || 'evolution_exchange',
|
EXCHANGE_NAME: process.env?.RABBITMQ_EXCHANGE_NAME || 'evolution_exchange',
|
||||||
URI: process.env.RABBITMQ_URI || '',
|
URI: process.env.RABBITMQ_URI || '',
|
||||||
EVENTS: {
|
EVENTS: {
|
||||||
@ -393,6 +403,43 @@ export class ConfigService {
|
|||||||
TYPEBOT_CHANGE_STATUS: process.env?.RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
|
TYPEBOT_CHANGE_STATUS: process.env?.RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
NATS: {
|
||||||
|
ENABLED: process.env?.NATS_ENABLED === 'true',
|
||||||
|
GLOBAL_ENABLED: process.env?.NATS_GLOBAL_ENABLED === 'true',
|
||||||
|
PREFIX_KEY: process.env?.NATS_PREFIX_KEY,
|
||||||
|
EXCHANGE_NAME: process.env?.NATS_EXCHANGE_NAME || 'evolution_exchange',
|
||||||
|
URI: process.env.NATS_URI || '',
|
||||||
|
EVENTS: {
|
||||||
|
APPLICATION_STARTUP: process.env?.NATS_EVENTS_APPLICATION_STARTUP === 'true',
|
||||||
|
INSTANCE_CREATE: process.env?.NATS_EVENTS_INSTANCE_CREATE === 'true',
|
||||||
|
INSTANCE_DELETE: process.env?.NATS_EVENTS_INSTANCE_DELETE === 'true',
|
||||||
|
QRCODE_UPDATED: process.env?.NATS_EVENTS_QRCODE_UPDATED === 'true',
|
||||||
|
MESSAGES_SET: process.env?.NATS_EVENTS_MESSAGES_SET === 'true',
|
||||||
|
MESSAGES_UPSERT: process.env?.NATS_EVENTS_MESSAGES_UPSERT === 'true',
|
||||||
|
MESSAGES_EDITED: process.env?.NATS_EVENTS_MESSAGES_EDITED === 'true',
|
||||||
|
MESSAGES_UPDATE: process.env?.NATS_EVENTS_MESSAGES_UPDATE === 'true',
|
||||||
|
MESSAGES_DELETE: process.env?.NATS_EVENTS_MESSAGES_DELETE === 'true',
|
||||||
|
SEND_MESSAGE: process.env?.NATS_EVENTS_SEND_MESSAGE === 'true',
|
||||||
|
SEND_MESSAGE_UPDATE: process.env?.NATS_EVENTS_SEND_MESSAGE_UPDATE === 'true',
|
||||||
|
CONTACTS_SET: process.env?.NATS_EVENTS_CONTACTS_SET === 'true',
|
||||||
|
CONTACTS_UPDATE: process.env?.NATS_EVENTS_CONTACTS_UPDATE === 'true',
|
||||||
|
CONTACTS_UPSERT: process.env?.NATS_EVENTS_CONTACTS_UPSERT === 'true',
|
||||||
|
PRESENCE_UPDATE: process.env?.NATS_EVENTS_PRESENCE_UPDATE === 'true',
|
||||||
|
CHATS_SET: process.env?.NATS_EVENTS_CHATS_SET === 'true',
|
||||||
|
CHATS_UPDATE: process.env?.NATS_EVENTS_CHATS_UPDATE === 'true',
|
||||||
|
CHATS_UPSERT: process.env?.NATS_EVENTS_CHATS_UPSERT === 'true',
|
||||||
|
CHATS_DELETE: process.env?.NATS_EVENTS_CHATS_DELETE === 'true',
|
||||||
|
CONNECTION_UPDATE: process.env?.NATS_EVENTS_CONNECTION_UPDATE === 'true',
|
||||||
|
LABELS_EDIT: process.env?.NATS_EVENTS_LABELS_EDIT === 'true',
|
||||||
|
LABELS_ASSOCIATION: process.env?.NATS_EVENTS_LABELS_ASSOCIATION === 'true',
|
||||||
|
GROUPS_UPSERT: process.env?.NATS_EVENTS_GROUPS_UPSERT === 'true',
|
||||||
|
GROUP_UPDATE: process.env?.NATS_EVENTS_GROUPS_UPDATE === 'true',
|
||||||
|
GROUP_PARTICIPANTS_UPDATE: process.env?.NATS_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
|
||||||
|
CALL: process.env?.NATS_EVENTS_CALL === 'true',
|
||||||
|
TYPEBOT_START: process.env?.NATS_EVENTS_TYPEBOT_START === 'true',
|
||||||
|
TYPEBOT_CHANGE_STATUS: process.env?.NATS_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
|
||||||
|
},
|
||||||
|
},
|
||||||
SQS: {
|
SQS: {
|
||||||
ENABLED: process.env?.SQS_ENABLED === 'true',
|
ENABLED: process.env?.SQS_ENABLED === 'true',
|
||||||
ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '',
|
ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '',
|
||||||
|
@ -3,8 +3,8 @@ import { configService, S3 } from '@config/env.config';
|
|||||||
const getTypeMessage = (msg: any) => {
|
const getTypeMessage = (msg: any) => {
|
||||||
let mediaId: string;
|
let mediaId: string;
|
||||||
|
|
||||||
if (configService.get<S3>('S3').ENABLE) mediaId = msg.message.mediaUrl;
|
if (configService.get<S3>('S3').ENABLE) mediaId = msg.message?.mediaUrl;
|
||||||
else mediaId = msg.key.id;
|
else mediaId = msg.key?.id;
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
conversation: msg?.message?.conversation,
|
conversation: msg?.message?.conversation,
|
||||||
@ -15,7 +15,7 @@ const getTypeMessage = (msg: any) => {
|
|||||||
msg?.message?.viewOnceMessageV2?.message?.imageMessage?.url ||
|
msg?.message?.viewOnceMessageV2?.message?.imageMessage?.url ||
|
||||||
msg?.message?.viewOnceMessageV2?.message?.videoMessage?.url ||
|
msg?.message?.viewOnceMessageV2?.message?.videoMessage?.url ||
|
||||||
msg?.message?.viewOnceMessageV2?.message?.audioMessage?.url,
|
msg?.message?.viewOnceMessageV2?.message?.audioMessage?.url,
|
||||||
listResponseMessage: msg?.message?.listResponseMessage?.title,
|
listResponseMessage: msg?.message?.listResponseMessage?.title || msg?.listResponseMessage?.title,
|
||||||
responseRowId: msg?.message?.listResponseMessage?.singleSelectReply?.selectedRowId,
|
responseRowId: msg?.message?.listResponseMessage?.singleSelectReply?.selectedRowId,
|
||||||
templateButtonReplyMessage:
|
templateButtonReplyMessage:
|
||||||
msg?.message?.templateButtonReplyMessage?.selectedId || msg?.message?.buttonsResponseMessage?.selectedButtonId,
|
msg?.message?.templateButtonReplyMessage?.selectedId || msg?.message?.buttonsResponseMessage?.selectedButtonId,
|
||||||
|
17
src/validate/business.schema.ts
Normal file
17
src/validate/business.schema.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { JSONSchema7 } from 'json-schema';
|
||||||
|
|
||||||
|
export const catalogSchema: JSONSchema7 = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
number: { type: 'string' },
|
||||||
|
limit: { type: 'number' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const collectionsSchema: JSONSchema7 = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
number: { type: 'string' },
|
||||||
|
limit: { type: 'number' },
|
||||||
|
},
|
||||||
|
};
|
@ -126,6 +126,43 @@ export const instanceSchema: JSONSchema7 = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// NATS
|
||||||
|
natsEnabled: { type: 'boolean' },
|
||||||
|
natsEvents: {
|
||||||
|
type: 'array',
|
||||||
|
minItems: 0,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
enum: [
|
||||||
|
'APPLICATION_STARTUP',
|
||||||
|
'QRCODE_UPDATED',
|
||||||
|
'MESSAGES_SET',
|
||||||
|
'MESSAGES_UPSERT',
|
||||||
|
'MESSAGES_EDITED',
|
||||||
|
'MESSAGES_UPDATE',
|
||||||
|
'MESSAGES_DELETE',
|
||||||
|
'SEND_MESSAGE',
|
||||||
|
'SEND_MESSAGE_UPDATE',
|
||||||
|
'CONTACTS_SET',
|
||||||
|
'CONTACTS_UPSERT',
|
||||||
|
'CONTACTS_UPDATE',
|
||||||
|
'PRESENCE_UPDATE',
|
||||||
|
'CHATS_SET',
|
||||||
|
'CHATS_UPSERT',
|
||||||
|
'CHATS_UPDATE',
|
||||||
|
'CHATS_DELETE',
|
||||||
|
'GROUPS_UPSERT',
|
||||||
|
'GROUP_UPDATE',
|
||||||
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
|
'CALL',
|
||||||
|
'TYPEBOT_START',
|
||||||
|
'TYPEBOT_CHANGE_STATUS',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
// SQS
|
// SQS
|
||||||
sqsEnabled: { type: 'boolean' },
|
sqsEnabled: { type: 'boolean' },
|
||||||
sqsEvents: {
|
sqsEvents: {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Integrations Schema
|
// Integrations Schema
|
||||||
|
export * from './business.schema';
|
||||||
export * from './chat.schema';
|
export * from './chat.schema';
|
||||||
export * from './group.schema';
|
export * from './group.schema';
|
||||||
export * from './instance.schema';
|
export * from './instance.schema';
|
||||||
|
Loading…
Reference in New Issue
Block a user