mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-19 03:42:23 -06:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c20da2481 | ||
|
|
72d2a563f3 | ||
|
|
6f3f8c944d | ||
|
|
ed60fd2e82 | ||
|
|
636fef4693 | ||
|
|
77775e2f85 | ||
|
|
b260959a6e | ||
|
|
2a04f7b378 | ||
|
|
d4766f9c81 | ||
|
|
7f4c9b5b11 | ||
|
|
70f7c8ce89 | ||
|
|
f33a6aba39 | ||
|
|
236d717f10 | ||
|
|
0fc160f57c | ||
|
|
666023d89a | ||
|
|
8c4f2991c7 | ||
|
|
4453dff36d | ||
|
|
90b043561f | ||
|
|
43a8b34673 | ||
|
|
6005d33c94 | ||
|
|
cffb4736ce | ||
|
|
be54331d05 | ||
|
|
e19b8255d3 | ||
|
|
d680900f07 | ||
|
|
78bd4fd7de | ||
|
|
01d4a95d2d | ||
|
|
bb4490307d | ||
|
|
be492a3e3f | ||
|
|
a7b05f1e85 | ||
|
|
1c5ae4eb4d | ||
|
|
16d03d20ac | ||
|
|
8f062fab7d | ||
|
|
2565b934a5 | ||
|
|
7e88a9084d | ||
|
|
ef03292e35 | ||
|
|
d309ede623 | ||
|
|
834a80c35a | ||
|
|
99b0288d1b | ||
|
|
926f58f336 | ||
|
|
b7bfe99520 | ||
|
|
fa60b68425 | ||
|
|
8622e1e4ff | ||
|
|
3e13ae9740 | ||
|
|
e5cd577fbf | ||
|
|
86985231ff | ||
|
|
e64926a429 | ||
|
|
6232190cfe | ||
|
|
eb83d89307 | ||
|
|
b4a9941452 | ||
|
|
be782ba512 | ||
|
|
db54f247a2 | ||
|
|
0fc3b47c88 | ||
|
|
3eda2a1f2a | ||
|
|
c45b2adad6 | ||
|
|
702dbebfbe | ||
|
|
052303cc93 | ||
|
|
69380146e4 | ||
|
|
514fb56209 | ||
|
|
57b61070d9 | ||
|
|
86ce00365a | ||
|
|
a8bd32bef3 | ||
|
|
ae8801dd8a | ||
|
|
0a4e52e663 | ||
|
|
95045db74e | ||
|
|
19e7c0be0b | ||
|
|
3fcbf458b6 | ||
|
|
4580146cdb | ||
|
|
ea6a7c1c87 | ||
|
|
06cde721b3 | ||
|
|
31486e5963 | ||
|
|
1b52bdf425 | ||
|
|
9b59895ad2 | ||
|
|
c7600ff059 | ||
|
|
b1769edb65 | ||
|
|
14ad09e867 | ||
|
|
26a99d3696 | ||
|
|
c973730acc | ||
|
|
048bea376d | ||
|
|
eca4285ea8 | ||
|
|
437803da07 | ||
|
|
a7be7c3e19 | ||
|
|
5bd7dd3022 | ||
|
|
27aa0add4e | ||
|
|
24712c4c2d | ||
|
|
4bf4b4a045 | ||
|
|
9604f5c317 | ||
|
|
69c1059644 | ||
|
|
26b2903995 | ||
|
|
97abb256d5 | ||
|
|
a342911dae | ||
|
|
1f43563295 | ||
|
|
f23ebf1e99 | ||
|
|
d66b751c2e | ||
|
|
6a7c76a9ba | ||
|
|
2338787dbb | ||
|
|
a216c9cc37 | ||
|
|
b7da8d2193 | ||
|
|
41f191902b | ||
|
|
37397c7a69 | ||
|
|
153695288e | ||
|
|
a5e29758a4 | ||
|
|
964427e533 | ||
|
|
0a925df2a9 | ||
|
|
db95de6731 | ||
|
|
bc2191eae6 | ||
|
|
9fed844e59 | ||
|
|
a2c125ee90 | ||
|
|
a2779612be | ||
|
|
f51c3b6519 | ||
|
|
870968a7c5 | ||
|
|
c4f39ab85c | ||
|
|
8cb431ad40 | ||
|
|
77f98c2438 | ||
|
|
d7d0e5ec82 | ||
|
|
2519efb3ea | ||
|
|
b1c527cb71 | ||
|
|
b87f687e55 | ||
|
|
a62d27ffc2 | ||
|
|
a9a1c6c49b | ||
|
|
4989d6ddfa | ||
|
|
fd01b9de36 | ||
|
|
1017010e15 | ||
|
|
e0bd06489f | ||
|
|
4a6301c2af | ||
|
|
bca830dc54 | ||
|
|
77bde5325e | ||
|
|
bdca506dd4 | ||
|
|
34faaa28ca | ||
|
|
a08d433d0a | ||
|
|
6d08162012 | ||
|
|
2481650028 | ||
|
|
5e551fee41 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -2,6 +2,8 @@
|
|||||||
/dist
|
/dist
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
||||||
|
/Docker/.env
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs/**.json
|
logs/**.json
|
||||||
*.log
|
*.log
|
||||||
@@ -11,8 +13,8 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
/store/*
|
|
||||||
/docker-compose-data
|
/docker-compose-data
|
||||||
|
/docker-data
|
||||||
|
|
||||||
# Package
|
# Package
|
||||||
/yarn.lock
|
/yarn.lock
|
||||||
@@ -33,3 +35,6 @@ lerna-debug.log*
|
|||||||
!/instances/.gitkeep
|
!/instances/.gitkeep
|
||||||
/test/
|
/test/
|
||||||
/src/env.yml
|
/src/env.yml
|
||||||
|
/store
|
||||||
|
|
||||||
|
/temp/*
|
||||||
|
|||||||
97
CHANGELOG.md
97
CHANGELOG.md
@@ -1,3 +1,99 @@
|
|||||||
|
# 1.2.1 (homolog)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Adjusts in docker files
|
||||||
|
* Save picture url groups in chatwoot
|
||||||
|
|
||||||
|
# 1.2.0 (2023-07-14 15:28)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Native integration with chatwoot
|
||||||
|
* Added returning or non-returning participants option in fetchAllGroups
|
||||||
|
* Added group integration to chatwoot
|
||||||
|
* Added automation on create instance to chatwoot
|
||||||
|
* Added verbose logs and format chatwoot service
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Adjusts in docker-compose files
|
||||||
|
* Adjusts in number validation for AR and MX numbers
|
||||||
|
* Adjusts in env files, removed save old_messages
|
||||||
|
* Fix when sending a message to a group I don't belong returns a bad request
|
||||||
|
* Fits the format on return from the fetchAllGroups endpoint
|
||||||
|
* Adjust in send document with caption from chatwoot
|
||||||
|
* Fixed message with undefind in chatwoot
|
||||||
|
* Changed message in path /
|
||||||
|
* Test duplicate message media in groups chatwoot
|
||||||
|
* Optimize send message from group with mentions
|
||||||
|
* Fixed name of the profile status in fetchInstances
|
||||||
|
* Fixed error 500 when logout in instance with status = close
|
||||||
|
|
||||||
|
# 1.1.5 (2023-07-12 07:17)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Adjusts in temp folder
|
||||||
|
* Return with event send_message
|
||||||
|
|
||||||
|
# 1.1.4 (2023-07-08 11:01)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Route to send status broadcast
|
||||||
|
* Added verbose logs
|
||||||
|
* Insert allContacts in payload of endpoint sendStatus
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Adjusted set in webhook to go empty when enabled false
|
||||||
|
* Adjust in store files
|
||||||
|
* Fixed the problem when do not save contacts when receive messages
|
||||||
|
* Changed owner of the jid for instanceName
|
||||||
|
* Create .env for installation in docker
|
||||||
|
|
||||||
|
# 1.1.3 (2023-07-06 11:43)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Added configuration for Baileys log level in env
|
||||||
|
* Added audio to mp4 converter in optionally get Base64 From MediaMessage
|
||||||
|
* Added organization name in vcard
|
||||||
|
* Added email in vcard
|
||||||
|
* Added url in vcard
|
||||||
|
* Added verbose logs
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Added timestamp internally in urls to avoid caching
|
||||||
|
* Correction in decryption of poll votes
|
||||||
|
* Change in the way the api sent and saved the sent messages, now it goes in the messages.upsert event
|
||||||
|
* Fixed cash when sending stickers via url
|
||||||
|
* Improved how Redis works for instances
|
||||||
|
* Fixed problem when disconnecting the instance it removes the instance
|
||||||
|
* Fixed problem sending ack when preview is done by me
|
||||||
|
* Adjust in store files
|
||||||
|
|
||||||
|
# 1.1.2 (2023-06-28 13:43)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed baileys version in package.json
|
||||||
|
* Fixed problem that did not validate if the token passed in create instance already existed
|
||||||
|
* Fixed problem that does not delete instance files in server mode
|
||||||
|
|
||||||
|
# 1.1.1 (2023-06-28 10:27)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Added group invitation sending
|
||||||
|
* Added webhook configuration per event in the individual instance registration
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Adjust dockerfile variables
|
||||||
|
|
||||||
# 1.1.0 (2023-06-21 11:17)
|
# 1.1.0 (2023-06-21 11:17)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
@@ -26,6 +122,7 @@
|
|||||||
* fixed the problem of not disabling the global webhook by the variable
|
* fixed the problem of not disabling the global webhook by the variable
|
||||||
* Adjustment in the recording of temporary files and periodic cleaning
|
* Adjustment in the recording of temporary files and periodic cleaning
|
||||||
* Fix for container mode also work only with files
|
* Fix for container mode also work only with files
|
||||||
|
* Remove recording of old messages on sync
|
||||||
|
|
||||||
# 1.0.9 (2023-06-10)
|
# 1.0.9 (2023-06-10)
|
||||||
|
|
||||||
|
|||||||
87
Docker/.env
87
Docker/.env
@@ -1,87 +0,0 @@
|
|||||||
CORS_ORIGIN='*' # Or separate by commas - ex.: 'yourdomain1.com, yourdomain2.com'
|
|
||||||
CORS_METHODS='POST,GET,PUT,DELETE'
|
|
||||||
CORS_CREDENTIALS=true
|
|
||||||
|
|
||||||
# Determine the logs to be displayed
|
|
||||||
LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK'
|
|
||||||
LOG_COLOR=true
|
|
||||||
|
|
||||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
|
||||||
# Default time: 5 minutes
|
|
||||||
# If you don't even want an expiration, enter the value false
|
|
||||||
DEL_INSTANCE=5
|
|
||||||
|
|
||||||
# Temporary data storage
|
|
||||||
STORE_MESSAGES=true
|
|
||||||
STORE_MESSAGE_UP=true
|
|
||||||
STORE_CONTACTS=false
|
|
||||||
STORE_CHATS=false
|
|
||||||
|
|
||||||
CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds ===2h
|
|
||||||
CLEAN_STORE_MESSAGES=true
|
|
||||||
CLEAN_STORE_MESSAGE_UP=true
|
|
||||||
CLEAN_STORE_CONTACTS=false
|
|
||||||
CLEAN_STORE_CHATS=false
|
|
||||||
|
|
||||||
# Permanent data storage
|
|
||||||
DATABASE_ENABLED=false
|
|
||||||
DATABASE_CONNECTION_URI='<uri>'
|
|
||||||
DATABASE_CONNECTION_DB_PREFIX_NAME='evolution'
|
|
||||||
DATABASE_SAVE_DATA_INSTANCE=false
|
|
||||||
DATABASE_SAVE_DATA_OLD_MESSAGE=false
|
|
||||||
DATABASE_SAVE_DATA_NEW_MESSAGE=true
|
|
||||||
DATABASE_SAVE_MESSAGE_UPDATE=true
|
|
||||||
DATABASE_SAVE_DATA_CONTACTS=true
|
|
||||||
DATABASE_SAVE_DATA_CHATS=true
|
|
||||||
|
|
||||||
REDIS_ENABLED=true
|
|
||||||
REDIS_URI='<uri>/1'
|
|
||||||
REDIS_PREFIX_KEY='evolution'
|
|
||||||
|
|
||||||
# Webhook Settings
|
|
||||||
## Define a global webhook that will listen for enabled events from all instances
|
|
||||||
WEBHOOK_GLOBAL_URL='<url>'
|
|
||||||
WEBHOOK_GLOBAL_ENABLED=false
|
|
||||||
WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
|
|
||||||
## Set the events you want to hear
|
|
||||||
WEBHOOK_EVENTS_STATUS_INSTANCE=true
|
|
||||||
WEBHOOK_EVENTS_APPLICATION_STARTUP=true
|
|
||||||
WEBHOOK_EVENTS_QRCODE_UPDATED=true
|
|
||||||
WEBHOOK_EVENTS_MESSAGES_SET=true
|
|
||||||
WEBHOOK_EVENTS_MESSAGES_UPDATE=true
|
|
||||||
WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
|
||||||
WEBHOOK_EVENTS_SEND_MESSAGE=true
|
|
||||||
WEBHOOK_EVENTS_CONTACTS_SET=true
|
|
||||||
WEBHOOK_EVENTS_CONTACTS_UPSERT=true
|
|
||||||
WEBHOOK_EVENTS_CONTACTS_UPDATE=true
|
|
||||||
WEBHOOK_EVENTS_PRESENCE_UPDATE=true
|
|
||||||
WEBHOOK_EVENTS_CHATS_SET=true
|
|
||||||
WEBHOOK_EVENTS_CHATS_UPSERT=true
|
|
||||||
WEBHOOK_EVENTS_CHATS_UPDATE=true
|
|
||||||
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
|
||||||
WEBHOOK_EVENTS_GROUPS_UPSERT=false
|
|
||||||
WEBHOOK_EVENTS_GROUPS_UPDATE=false
|
|
||||||
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=false
|
|
||||||
## This event fires every time a new token is requested via the refresh route
|
|
||||||
WEBHOOK_EVENTS_NEW_JWT_TOKEN=true
|
|
||||||
|
|
||||||
CONFIG_SESSION_PHONE_CLIENT='Evolution API'
|
|
||||||
CONFIG_SESSION_PHONE_NAME='Chrome'
|
|
||||||
|
|
||||||
# Set qrcode display limit
|
|
||||||
QRCODE_LIMIT=6
|
|
||||||
|
|
||||||
# Defines an authentication type for the api
|
|
||||||
AUTHENTICATION_TYPE='jwt' # or 'apikey'
|
|
||||||
## Define a global apikey to access all instances.
|
|
||||||
### OBS: This key must be inserted in the request header to create an instance.
|
|
||||||
AUTHENTICATION_API_KEY='t8OOEeISKzpmc3jjcMqBWYSaJsafdefer'
|
|
||||||
AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
|
|
||||||
## Set the secret key to encrypt and decrypt your token and its expiration time
|
|
||||||
AUTHENTICATION_JWT_EXPIRIN_IN=3600 # seconds - 3600s ===1h | zero (0) - never expires
|
|
||||||
AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG'
|
|
||||||
|
|
||||||
AUTHENTICATION_INSTANCE_NAME='evolution'
|
|
||||||
AUTHENTICATION_INSTANCE_WEBHOOK_URL='<url>'
|
|
||||||
AUTHENTICATION_INSTANCE_MODE='container' # or 'server'
|
|
||||||
AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS=false
|
|
||||||
100
Docker/.env.example
Normal file
100
Docker/.env.example
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
SERVER_URL='<url>' # ex.: http://localhost:3333
|
||||||
|
|
||||||
|
CORS_ORIGIN='*' # Or separate by commas - ex.: 'yourdomain1.com, yourdomain2.com'
|
||||||
|
CORS_METHODS='POST,GET,PUT,DELETE'
|
||||||
|
CORS_CREDENTIALS=true
|
||||||
|
|
||||||
|
# Determine the logs to be displayed
|
||||||
|
LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS'
|
||||||
|
LOG_COLOR=true
|
||||||
|
LOG_BAILEYS=error # "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
||||||
|
|
||||||
|
# Determine how long the instance should be deleted from memory in case of no connection.
|
||||||
|
# Default time: 5 minutes
|
||||||
|
# If you don't even want an expiration, enter the value false
|
||||||
|
DEL_INSTANCE=false
|
||||||
|
|
||||||
|
# Temporary data storage
|
||||||
|
STORE_MESSAGES=true
|
||||||
|
STORE_MESSAGE_UP=true
|
||||||
|
STORE_CONTACTS=true
|
||||||
|
STORE_CHATS=true
|
||||||
|
|
||||||
|
CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h
|
||||||
|
CLEAN_STORE_MESSAGES=true
|
||||||
|
CLEAN_STORE_MESSAGE_UP=true
|
||||||
|
CLEAN_STORE_CONTACTS=true
|
||||||
|
CLEAN_STORE_CHATS=true
|
||||||
|
|
||||||
|
# Permanent data storage
|
||||||
|
DATABASE_ENABLED=false
|
||||||
|
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
||||||
|
DATABASE_CONNECTION_DB_PREFIX_NAME=evolution
|
||||||
|
|
||||||
|
# Choose the data you want to save in the application's database or store
|
||||||
|
DATABASE_SAVE_DATA_INSTANCE=false
|
||||||
|
DATABASE_SAVE_DATA_NEW_MESSAGE=false
|
||||||
|
DATABASE_SAVE_MESSAGE_UPDATE=false
|
||||||
|
DATABASE_SAVE_DATA_CONTACTS=false
|
||||||
|
DATABASE_SAVE_DATA_CHATS=false
|
||||||
|
|
||||||
|
REDIS_ENABLED=false
|
||||||
|
REDIS_URI=redis://redis:6379
|
||||||
|
REDIS_PREFIX_KEY=evolution
|
||||||
|
|
||||||
|
# Global Webhook Settings
|
||||||
|
# Each instance's Webhook URL and events will be requested at the time it is created
|
||||||
|
## Define a global webhook that will listen for enabled events from all instances
|
||||||
|
WEBHOOK_GLOBAL_URL='<url>'
|
||||||
|
WEBHOOK_GLOBAL_ENABLED=false
|
||||||
|
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
|
||||||
|
WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
|
||||||
|
## Set the events you want to hear
|
||||||
|
WEBHOOK_EVENTS_APPLICATION_STARTUP=false
|
||||||
|
WEBHOOK_EVENTS_QRCODE_UPDATED=true
|
||||||
|
WEBHOOK_EVENTS_MESSAGES_SET=true
|
||||||
|
WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
||||||
|
WEBHOOK_EVENTS_MESSAGES_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_SEND_MESSAGE=true
|
||||||
|
WEBHOOK_EVENTS_CONTACTS_SET=true
|
||||||
|
WEBHOOK_EVENTS_CONTACTS_UPSERT=true
|
||||||
|
WEBHOOK_EVENTS_CONTACTS_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_PRESENCE_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_CHATS_SET=true
|
||||||
|
WEBHOOK_EVENTS_CHATS_UPSERT=true
|
||||||
|
WEBHOOK_EVENTS_CHATS_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_CHATS_DELETE=true
|
||||||
|
WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
||||||
|
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||||
|
# This event fires every time a new token is requested via the refresh route
|
||||||
|
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||||
|
|
||||||
|
# Name that will be displayed on smartphone connection
|
||||||
|
CONFIG_SESSION_PHONE_CLIENT='Evolution API'
|
||||||
|
CONFIG_SESSION_PHONE_NAME=chrome # chrome | firefox | edge | opera | safari
|
||||||
|
|
||||||
|
# Set qrcode display limit
|
||||||
|
QRCODE_LIMIT=30
|
||||||
|
|
||||||
|
# Defines an authentication type for the api
|
||||||
|
# We recommend using the apikey because it will allow you to use a custom token,
|
||||||
|
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
|
||||||
|
AUTHENTICATION_TYPE='apikey' # jwt or 'apikey'
|
||||||
|
## Define a global apikey to access all instances.
|
||||||
|
### OBS: This key must be inserted in the request header to create an instance.
|
||||||
|
AUTHENTICATION_API_KEY='B6D711FCDE4D4FD5936544120E713976'
|
||||||
|
AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
|
||||||
|
## Set the secret key to encrypt and decrypt your token and its expiration time
|
||||||
|
AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s ===1h | zero (0) - never expires
|
||||||
|
AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG'
|
||||||
|
# Set the instance name and webhook url to create an instance in init the application
|
||||||
|
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
||||||
|
AUTHENTICATION_INSTANCE_MODE=server # container or server
|
||||||
|
# if you are using container mode, set the container name and the webhook url to default instance
|
||||||
|
AUTHENTICATION_INSTANCE_NAME=evolution
|
||||||
|
AUTHENTICATION_INSTANCE_WEBHOOK_URL='<url>'
|
||||||
|
AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=1
|
||||||
|
AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=123456
|
||||||
|
AUTHENTICATION_INSTANCE_CHATWOOT_URL='<url>'
|
||||||
43
Docker/mongodb/docker-compose.yaml
Normal file
43
Docker/mongodb/docker-compose.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mongodb:
|
||||||
|
container_name: mongodb
|
||||||
|
image: mongo
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=root
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=root
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
volumes:
|
||||||
|
- evolution_mongodb_data:/data/db
|
||||||
|
- evolution_mongodb_configdb:/data/configdb
|
||||||
|
networks:
|
||||||
|
- evolution-net
|
||||||
|
expose:
|
||||||
|
- 27017
|
||||||
|
|
||||||
|
mongo-express:
|
||||||
|
image: mongo-express
|
||||||
|
environment:
|
||||||
|
ME_CONFIG_BASICAUTH_USERNAME: root
|
||||||
|
ME_CONFIG_BASICAUTH_PASSWORD: root
|
||||||
|
ME_CONFIG_MONGODB_SERVER: mongodb
|
||||||
|
ME_CONFIG_MONGODB_ADMINUSERNAME: root
|
||||||
|
ME_CONFIG_MONGODB_ADMINPASSWORD: root
|
||||||
|
ports:
|
||||||
|
- 8081:8081
|
||||||
|
links:
|
||||||
|
- mongodb
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
evolution_mongodb_data:
|
||||||
|
evolution_mongodb_configdb:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: evolution-net
|
||||||
|
|
||||||
28
Docker/redis/docker-compose.yaml
Normal file
28
Docker/redis/docker-compose.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
container_name: redis
|
||||||
|
command: >
|
||||||
|
redis-server
|
||||||
|
--port 6379
|
||||||
|
--appendonly yes
|
||||||
|
volumes:
|
||||||
|
- evolution_redis:/data
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
|
||||||
|
rebrow:
|
||||||
|
image: marian/rebrow
|
||||||
|
ports:
|
||||||
|
- 5001:5001
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
evolution_redis:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: evolution-net
|
||||||
20
Dockerfile
20
Dockerfile
@@ -1,5 +1,9 @@
|
|||||||
FROM node:16.18-alpine
|
FROM node:16.18-alpine
|
||||||
|
|
||||||
|
LABEL version="1.1.3" description="Api to control whatsapp features through http requests."
|
||||||
|
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
|
||||||
|
LABEL contact="contato@agenciadgcode.com"
|
||||||
|
|
||||||
RUN apk update && apk upgrade && \
|
RUN apk update && apk upgrade && \
|
||||||
apk add --no-cache git
|
apk add --no-cache git
|
||||||
|
|
||||||
@@ -11,13 +15,14 @@ ENV DOCKER_ENV=true
|
|||||||
|
|
||||||
ENV SERVER_TYPE="http"
|
ENV SERVER_TYPE="http"
|
||||||
ENV SERVER_PORT=8080
|
ENV SERVER_PORT=8080
|
||||||
|
ENV SERVER_URL=$SERVER_URL
|
||||||
|
|
||||||
ENV CORS_ORIGIN="*"
|
ENV CORS_ORIGIN="*"
|
||||||
ENV CORS_METHODS="POST,GET,PUT,DELETE"
|
ENV CORS_METHODS="POST,GET,PUT,DELETE"
|
||||||
ENV CORS_CREDENTIALS=true
|
ENV CORS_CREDENTIALS=true
|
||||||
|
|
||||||
ENV LOG_LEVEL="ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK"
|
ENV LOG_LEVEL=$LOG_LEVEL
|
||||||
ENV LOG_COLOR=true
|
ENV LOG_COLOR=$LOG_COLOR
|
||||||
|
|
||||||
ENV DEL_INSTANCE=$DEL_INSTANCE
|
ENV DEL_INSTANCE=$DEL_INSTANCE
|
||||||
|
|
||||||
@@ -36,7 +41,6 @@ ENV DATABASE_ENABLED=$DATABASE_ENABLED
|
|||||||
ENV DATABASE_CONNECTION_URI=$DATABASE_CONNECTION_URI
|
ENV DATABASE_CONNECTION_URI=$DATABASE_CONNECTION_URI
|
||||||
ENV DATABASE_CONNECTION_DB_PREFIX_NAME=$DATABASE_CONNECTION_DB_PREFIX_NAME
|
ENV DATABASE_CONNECTION_DB_PREFIX_NAME=$DATABASE_CONNECTION_DB_PREFIX_NAME
|
||||||
ENV DATABASE_SAVE_DATA_INSTANCE=$DATABASE_SAVE_DATA_INSTANCE
|
ENV DATABASE_SAVE_DATA_INSTANCE=$DATABASE_SAVE_DATA_INSTANCE
|
||||||
ENV DATABASE_SAVE_DATA_OLD_MESSAGE=$DATABASE_SAVE_DATA_OLD_MESSAGE
|
|
||||||
ENV DATABASE_SAVE_DATA_NEW_MESSAGE=$DATABASE_SAVE_DATA_NEW_MESSAGE
|
ENV DATABASE_SAVE_DATA_NEW_MESSAGE=$DATABASE_SAVE_DATA_NEW_MESSAGE
|
||||||
ENV DATABASE_SAVE_MESSAGE_UPDATE=$DATABASE_SAVE_MESSAGE_UPDATE
|
ENV DATABASE_SAVE_MESSAGE_UPDATE=$DATABASE_SAVE_MESSAGE_UPDATE
|
||||||
ENV DATABASE_SAVE_DATA_CONTACTS=$DATABASE_SAVE_DATA_CONTACTS
|
ENV DATABASE_SAVE_DATA_CONTACTS=$DATABASE_SAVE_DATA_CONTACTS
|
||||||
@@ -44,17 +48,17 @@ ENV DATABASE_SAVE_DATA_CHATS=$DATABASE_SAVE_DATA_CHATS
|
|||||||
|
|
||||||
ENV REDIS_ENABLED=$REDIS_ENABLED
|
ENV REDIS_ENABLED=$REDIS_ENABLED
|
||||||
ENV REDIS_URI=$REDIS_URI
|
ENV REDIS_URI=$REDIS_URI
|
||||||
|
ENV REDIS_PREFIX_KEY=$REDIS_PREFIX_KEY
|
||||||
|
|
||||||
ENV WEBHOOK_GLOBAL_URL=$WEBHOOK_GLOBAL_URL
|
ENV WEBHOOK_GLOBAL_URL=$WEBHOOK_GLOBAL_URL
|
||||||
ENV WEBHOOK_GLOBAL_ENABLED=$WEBHOOK_GLOBAL_ENABLED
|
ENV WEBHOOK_GLOBAL_ENABLED=$WEBHOOK_GLOBAL_ENABLED
|
||||||
ENV WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=$WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS
|
ENV WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=$WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS
|
||||||
|
|
||||||
ENV WEBHOOK_EVENTS_STATUS_INSTANCE=$WEBHOOK_EVENTS_STATUS_INSTANCE
|
|
||||||
ENV WEBHOOK_EVENTS_APPLICATION_STARTUP=$WEBHOOK_EVENTS_APPLICATION_STARTUP
|
ENV WEBHOOK_EVENTS_APPLICATION_STARTUP=$WEBHOOK_EVENTS_APPLICATION_STARTUP
|
||||||
ENV WEBHOOK_EVENTS_QRCODE_UPDATED=$WEBHOOK_EVENTS_QRCODE_UPDATED
|
ENV WEBHOOK_EVENTS_QRCODE_UPDATED=$WEBHOOK_EVENTS_QRCODE_UPDATED
|
||||||
ENV WEBHOOK_EVENTS_MESSAGES_SET=$WEBHOOK_EVENTS_MESSAGES_SET
|
ENV WEBHOOK_EVENTS_MESSAGES_SET=$WEBHOOK_EVENTS_MESSAGES_SET
|
||||||
ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=$WEBHOOK_EVENTS_MESSAGES_UPDATE
|
|
||||||
ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=$WEBHOOK_EVENTS_MESSAGES_UPSERT
|
ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=$WEBHOOK_EVENTS_MESSAGES_UPSERT
|
||||||
|
ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=$WEBHOOK_EVENTS_MESSAGES_UPDATE
|
||||||
ENV WEBHOOK_EVENTS_SEND_MESSAGE=$WEBHOOK_EVENTS_SEND_MESSAGE
|
ENV WEBHOOK_EVENTS_SEND_MESSAGE=$WEBHOOK_EVENTS_SEND_MESSAGE
|
||||||
ENV WEBHOOK_EVENTS_CONTACTS_SET=$WEBHOOK_EVENTS_CONTACTS_SET
|
ENV WEBHOOK_EVENTS_CONTACTS_SET=$WEBHOOK_EVENTS_CONTACTS_SET
|
||||||
ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=$WEBHOOK_EVENTS_CONTACTS_UPSERT
|
ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=$WEBHOOK_EVENTS_CONTACTS_UPSERT
|
||||||
@@ -71,7 +75,7 @@ ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=$WEBHOOK_EVENTS_GROUP_PARTICIPANTS_
|
|||||||
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=$WEBHOOK_EVENTS_NEW_JWT_TOKEN
|
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=$WEBHOOK_EVENTS_NEW_JWT_TOKEN
|
||||||
|
|
||||||
ENV CONFIG_SESSION_PHONE_CLIENT=$CONFIG_SESSION_PHONE_CLIENT
|
ENV CONFIG_SESSION_PHONE_CLIENT=$CONFIG_SESSION_PHONE_CLIENT
|
||||||
ENV CONFIG_SESSION_PHONE_NAME="Chrome"
|
ENV CONFIG_SESSION_PHONE_NAME=$CONFIG_SESSION_PHONE_NAME
|
||||||
|
|
||||||
ENV QRCODE_LIMIT=$QRCODE_LIMIT
|
ENV QRCODE_LIMIT=$QRCODE_LIMIT
|
||||||
|
|
||||||
@@ -85,8 +89,10 @@ ENV AUTHENTICATION_JWT_SECRET="L=0YWt]b2w[WF>#>:&E`"
|
|||||||
|
|
||||||
ENV AUTHENTICATION_INSTANCE_NAME=$AUTHENTICATION_INSTANCE_NAME
|
ENV AUTHENTICATION_INSTANCE_NAME=$AUTHENTICATION_INSTANCE_NAME
|
||||||
ENV AUTHENTICATION_INSTANCE_WEBHOOK_URL=$AUTHENTICATION_INSTANCE_WEBHOOK_URL
|
ENV AUTHENTICATION_INSTANCE_WEBHOOK_URL=$AUTHENTICATION_INSTANCE_WEBHOOK_URL
|
||||||
|
ENV AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=$AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID
|
||||||
|
ENV AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=$AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN
|
||||||
|
ENV AUTHENTICATION_INSTANCE_CHATWOOT_URL=$AUTHENTICATION_INSTANCE_CHATWOOT_URL
|
||||||
ENV AUTHENTICATION_INSTANCE_MODE=$AUTHENTICATION_INSTANCE_MODE
|
ENV AUTHENTICATION_INSTANCE_MODE=$AUTHENTICATION_INSTANCE_MODE
|
||||||
ENV AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS=$AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS
|
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
|
|||||||
256
README.md
256
README.md
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<!-- [](#)-->
|
[](https://evolution-api.com/whatsapp)
|
||||||
<!-- [](#) -->
|
[](https://evolution-api.com/discord)
|
||||||
|
[](https://evolution-api.com/postman)
|
||||||
|
[](https://doc.evolution-api.com)
|
||||||
[](./LICENSE)
|
[](./LICENSE)
|
||||||
[](https://app.picpay.com/user/davidsongomes1998)
|
[](https://app.picpay.com/user/davidsongomes1998)
|
||||||
|
|
||||||
@@ -16,256 +18,6 @@
|
|||||||
This project is based on the [CodeChat](https://github.com/code-chat-br/whatsapp-api). The original project is an implementation of [Baileys](https://github.com/WhiskeySockets/Baileys), serving as a Restful API service that controls WhatsApp functions.</br>
|
This project is based on the [CodeChat](https://github.com/code-chat-br/whatsapp-api). The original project is an implementation of [Baileys](https://github.com/WhiskeySockets/Baileys), serving as a Restful API service that controls WhatsApp functions.</br>
|
||||||
The code allows the creation of multiservice chats, service bots, or any other system that utilizes WhatsApp. The documentation provides instructions on how to set up and use the project, as well as additional information about its features and configuration options.
|
The code allows the creation of multiservice chats, service bots, or any other system that utilizes WhatsApp. The documentation provides instructions on how to set up and use the project, as well as additional information about its features and configuration options.
|
||||||
|
|
||||||
## Infrastructure
|
|
||||||
|
|
||||||
### Nvm installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
|
|
||||||
# or
|
|
||||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
|
|
||||||
```
|
|
||||||
>
|
|
||||||
> After finishing, restart the terminal to load the new information.
|
|
||||||
>
|
|
||||||
|
|
||||||
### Docker installation \[optional\]
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
|
||||||
|
|
||||||
sudo sh get-docker.sh
|
|
||||||
|
|
||||||
sudo usermod -aG docker ${USER}
|
|
||||||
```
|
|
||||||
### Nodejs installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
nvm install 16.18.1
|
|
||||||
```
|
|
||||||
|
|
||||||
### pm2 installation
|
|
||||||
```sh
|
|
||||||
npm i -g pm2
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
docker --version
|
|
||||||
|
|
||||||
node --version
|
|
||||||
```
|
|
||||||
## MongoDb [optional]
|
|
||||||
|
|
||||||
After installing docker and docker-compose, up the container.
|
|
||||||
- [compose from mongodb](./mongodb/docker-compose.yaml)
|
|
||||||
|
|
||||||
In the same directory where the file is located, run the following command:
|
|
||||||
```sh
|
|
||||||
bash docker.sh
|
|
||||||
```
|
|
||||||
Using the database is optional.
|
|
||||||
|
|
||||||
## Application startup
|
|
||||||
|
|
||||||
Cloning the Repository
|
|
||||||
```
|
|
||||||
git clone https://github.com/EvolutionAPI/evolution-api.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Go to the project directory and install all dependencies.</br>
|
|
||||||
```sh
|
|
||||||
cd whatsapp-api
|
|
||||||
|
|
||||||
npm i
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, run the command below to start the application:
|
|
||||||
```sh
|
|
||||||
# Under development
|
|
||||||
npm run start
|
|
||||||
|
|
||||||
# In production
|
|
||||||
npm run start:prod
|
|
||||||
|
|
||||||
# pm2
|
|
||||||
pm2 start 'npm run start:prod' --name ApiEvolution
|
|
||||||
```
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
You can define two authentication **types** for the routes in the **[env file](./src/dev-env.yml)**.
|
|
||||||
Authentications must be inserted in the request header.
|
|
||||||
|
|
||||||
1. **apikey**
|
|
||||||
|
|
||||||
2. **jwt:** A JWT is a standard for authentication and information exchange defined with a signature.
|
|
||||||
|
|
||||||
> Authentications are generated at instance creation time.
|
|
||||||
|
|
||||||
**Note:** There is also the possibility to define a global api key, which can access and control all instances.
|
|
||||||
|
|
||||||
### Connection
|
|
||||||
|
|
||||||
#### Create an instance
|
|
||||||
|
|
||||||
##### HTTP
|
|
||||||
|
|
||||||
> *NOTE:* This key must be inserted in the request header to create an instance.
|
|
||||||
|
|
||||||
```http
|
|
||||||
POST /instance/create HTTP/1.1
|
|
||||||
Host: localhost:8080
|
|
||||||
Content-Type: application/json
|
|
||||||
apikey: t8OOEeISKzpmc3jjcMqBWYSaJH2PIxns
|
|
||||||
|
|
||||||
```
|
|
||||||
##### cURL
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl --location --request POST 'http://localhost:8080/instance/create' \
|
|
||||||
--header 'Content-Type: application/json' \
|
|
||||||
--header 'apikey: t8OOEeISKzpmc3jjcMqBWYSaJH2PIxns' \
|
|
||||||
--data-raw '{
|
|
||||||
"instanceName": "evolution"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
### Response
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
"instance": {
|
|
||||||
"instanceName": "evolution",
|
|
||||||
"status": "created"
|
|
||||||
},
|
|
||||||
"hash": {
|
|
||||||
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. [...]"
|
|
||||||
|
|
||||||
// or
|
|
||||||
// "apikey": "88513847-1B0E-4188-8D76-4A2750C9B6C3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
#### Connection with qrcode
|
|
||||||
|
|
||||||
##### HTTP
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /instance/connect/evolution HTTP/1.1
|
|
||||||
Host: localhost:8080
|
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. [...]
|
|
||||||
```
|
|
||||||
```http
|
|
||||||
GET /instance/connect/evolution HTTP/1.1
|
|
||||||
Host: localhost:8080
|
|
||||||
apikey: 88513847-1B0E-4188-8D76-4A2750C9B6C3
|
|
||||||
```
|
|
||||||
##### cURL
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl --location --request GET 'http://localhost:8080/instance/connect/evolution' \
|
|
||||||
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. [...]'
|
|
||||||
```
|
|
||||||
```bash
|
|
||||||
curl --location --request GET 'http://localhost:8080/instance/connect/evolution' \
|
|
||||||
--header 'apikey: 88513847-1B0E-4188-8D76-4A2750C9B6C3'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Response
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
"code": "2@nXSUgRJSBY6T0XJmiFKZ0 [...] ,XsgJhJHYa+0MPpXANdPHHt6Ke/I7O2QyXT/Lsge0uSg=",
|
|
||||||
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhE [...] LkMtqAAAAABJRU5ErkJggg=="
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### App in Docker
|
|
||||||
- [docker run](./docker.sh)
|
|
||||||
- [docker-compose](./docker-compose.yml)
|
|
||||||
- [env for docker](./Docker/.env)
|
|
||||||
- [DockerHub Evolution API](https://hub.docker.com/repository/docker/davidsongomes/evolution-api)
|
|
||||||
|
|
||||||
After building the application, in the same directory as the files above, run the following command:
|
|
||||||
```sh
|
|
||||||
docker-compose up
|
|
||||||
```
|
|
||||||
## Send Messages
|
|
||||||
| | |
|
|
||||||
|-----|---|
|
|
||||||
| Send Text | ✔ |
|
|
||||||
| Send Template | ❌ |
|
|
||||||
| Send Media: audio - video - image - document - gif <br></br>base64: ```true``` | ✔ |
|
|
||||||
| Send Media File | ❌ |
|
|
||||||
| Send Audio type WhatsApp | ✔ |
|
|
||||||
| Send Location | ✔ |
|
|
||||||
| Send Link Preview | ✔ |
|
|
||||||
| Send Contact | ✔ |
|
|
||||||
| Send Reaction - emoji | ✔ |
|
|
||||||
| Send Poll Message | ✔ |
|
|
||||||
| Send Buttons (Deprecated) | ❌ |
|
|
||||||
| Send List (Deprecated) | ❌ |
|
|
||||||
|
|
||||||
## Postman collections
|
|
||||||
- [Postman Json](./postman.json)
|
|
||||||
|
|
||||||
## Webhook Events
|
|
||||||
|
|
||||||
| Name | Event | TypeData | Description |
|
|
||||||
|------|-------|-----------|------------|
|
|
||||||
| APPLICATION_STARTUP | application.startup | json | Notifies you when a application startup |
|
|
||||||
| QRCODE_UPDATED | qrcode.updated | json | Sends the base64 of the qrcode for reading |
|
|
||||||
| CONNECTION_UPDATE | connection.update | json | Informs the status of the connection with whatsapp |
|
|
||||||
| MESSAGES_SET | message.set | json | Sends a list of all your messages uploaded on whatsapp</br>This event occurs only once |
|
|
||||||
| MESSAGES_UPSERT | message.upsert | json | Notifies you when a message is received |
|
|
||||||
| MESSAGES_UPDATE | message.update | json | Tells you when a message is updated |
|
|
||||||
| SEND_MESSAGE | send.message | json | Notifies when a message is sent |
|
|
||||||
| CONTACTS_SET | contacts.set | json | Performs initial loading of all contacts</br>This event occurs only once |
|
|
||||||
| CONTACTS_UPSERT | contacts.upsert | json | Reloads all contacts with additional information</br>This event occurs only once |
|
|
||||||
| CONTACTS_UPDATE | contacts.update | json | Informs you when the chat is updated |
|
|
||||||
| PRESENCE_UPDATE | presence.update | json | Informs if the user is online, if he is performing some action like writing or recording and his last seen</br>'unavailable' | 'available' | 'composing' | 'recording' | 'paused' |
|
|
||||||
| CHATS_SET | chats.set | json | Send a list of all loaded chats |
|
|
||||||
| CHATS_UPDATE | chats.update | json | Informs you when the chat is updated |
|
|
||||||
| CHATS_UPSERT | chats.upsert | json | Sends any new chat information |
|
|
||||||
| GROUPS_UPSERT | groups.upsert | JSON | Notifies when a group is created |
|
|
||||||
| GROUPS_UPDATE | groups.update | JSON | Notifies when a group has its information updated |
|
|
||||||
| GROUP_PARTICIPANTS_UPDATE | group-participants.update | JSON | Notifies when an action occurs involving a participant</br>'add' | 'remove' | 'promote' | 'demote' |
|
|
||||||
| NEW_TOKEN | new.jwt | JSON | Notifies when the token (jwt) is updated
|
|
||||||
|
|
||||||
## Webhook Routes
|
|
||||||
When enabling the WEBHOOK_BY_EVENTS options in the global and local webhooks, the following paths will be added at the end of the webhook.
|
|
||||||
<br><br>
|
|
||||||
Example:
|
|
||||||
|
|
||||||
https://sub.domain.com/webhook-test/exclusive-webhook-code/qrcode-updated
|
|
||||||
|
|
||||||
| Name | Path |
|
|
||||||
|------|-------|
|
|
||||||
| APPLICATION_STARTUP | /application-startup |
|
|
||||||
| QRCODE_UPDATED | /qrcode-updated |
|
|
||||||
| CONNECTION_UPDATE | /connection-update |
|
|
||||||
| MESSAGES_SET | /messages-set |
|
|
||||||
| MESSAGES_UPSERT | /messages-upsert |
|
|
||||||
| MESSAGES_UPDATE | /messages-update |
|
|
||||||
| SEND_MESSAGE | /send-message |
|
|
||||||
| CONTACTS_SET | /contacts-set |
|
|
||||||
| CONTACTS_UPSERT | /contacts-upsert |
|
|
||||||
| CONTACTS_UPDATE | /contacts-update |
|
|
||||||
| PRESENCE_UPDATE | /presence-update |
|
|
||||||
| CHATS_SET | /chats-set |
|
|
||||||
| CHATS_UPDATE | /chats-update |
|
|
||||||
| CHATS_UPSERT | /chats-upsert |
|
|
||||||
| GROUPS_UPSERT | /groups-upsert |
|
|
||||||
| GROUPS_UPDATE | /groups-update |
|
|
||||||
| GROUP_PARTICIPANTS_UPDATE | /groups-participants-update |
|
|
||||||
| NEW_TOKEN | /new-token |
|
|
||||||
## Env File
|
|
||||||
|
|
||||||
See additional settings that can be applied through the **env** file by clicking **[here](./src/dev-env.yml)**.
|
|
||||||
|
|
||||||
> **⚠️Attention⚠️:** rename the **dev-env.yml** file to **env.yml**.
|
|
||||||
|
|
||||||
## SSL
|
|
||||||
|
|
||||||
To install the SSL certificate, follow the **[instructions](https://certbot.eff.org/instructions?ws=other&os=ubuntufocal)** below.
|
|
||||||
## SSL
|
## SSL
|
||||||
|
|
||||||
To install the SSL certificate, follow the **[instructions](https://certbot.eff.org/instructions?ws=other&os=ubuntufocal)** below.
|
To install the SSL certificate, follow the **[instructions](https://certbot.eff.org/instructions?ws=other&os=ubuntufocal)** below.
|
||||||
|
|||||||
72
docker-compose-full.yaml
Normal file
72
docker-compose-full.yaml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
container_name: redis
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
|
||||||
|
rebrow:
|
||||||
|
image: marian/rebrow
|
||||||
|
ports:
|
||||||
|
- 5001:5001
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
mongodb:
|
||||||
|
container_name: mongodb
|
||||||
|
image: mongo
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- evolution_mongodb_data:/data/db
|
||||||
|
- evolution_mongodb_configdb:/data/configdb
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: root
|
||||||
|
expose:
|
||||||
|
- 27017
|
||||||
|
|
||||||
|
mongo-express:
|
||||||
|
image: mongo-express
|
||||||
|
environment:
|
||||||
|
ME_CONFIG_BASICAUTH_USERNAME: root
|
||||||
|
ME_CONFIG_BASICAUTH_PASSWORD: root
|
||||||
|
ME_CONFIG_MONGODB_SERVER: mongodb
|
||||||
|
ME_CONFIG_MONGODB_ADMINUSERNAME: root
|
||||||
|
ME_CONFIG_MONGODB_ADMINPASSWORD: root
|
||||||
|
ports:
|
||||||
|
- 8081:8081
|
||||||
|
links:
|
||||||
|
- mongodb
|
||||||
|
api:
|
||||||
|
container_name: evolution_api
|
||||||
|
image: evolution/api:local
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- evolution_instances:/evolution/instances
|
||||||
|
- evolution_store:/evolution/store
|
||||||
|
env_file:
|
||||||
|
- ./Docker/.env
|
||||||
|
command: ['node', './dist/src/main.js']
|
||||||
|
expose:
|
||||||
|
- 8080
|
||||||
|
links:
|
||||||
|
- mongodb
|
||||||
|
- redis
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
evolution_instances:
|
||||||
|
evolution_store:
|
||||||
|
evolution_mongodb_data:
|
||||||
|
evolution_mongodb_configdb:
|
||||||
|
evolution_redis:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: evolution-net
|
||||||
|
|
||||||
@@ -1,143 +1,26 @@
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
|
|
||||||
networks:
|
|
||||||
evolution-net:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
container_name: evolution_api
|
container_name: evolution_api
|
||||||
image: evolution/api:local
|
image: evolution/api:local
|
||||||
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- evolution_instances:/evolution/instances
|
- evolution_instances:/evolution/instances
|
||||||
- evolution_store:/evolution/store
|
- evolution_store:/evolution/store
|
||||||
depends_on:
|
env_file:
|
||||||
- mongodb
|
- ./Docker/.env
|
||||||
- redis
|
|
||||||
environment:
|
|
||||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
|
||||||
# Default time: 5 minutes
|
|
||||||
# If you don't even want an expiration, enter the value false
|
|
||||||
- DEL_INSTANCE=5 # or false
|
|
||||||
# Temporary data storage
|
|
||||||
- STORE_MESSAGES=true
|
|
||||||
- STORE_MESSAGE_UP=true
|
|
||||||
- STORE_CONTACTS=true
|
|
||||||
- STORE_CHATS=true
|
|
||||||
- CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h
|
|
||||||
- CLEAN_STORE_MESSAGES=true
|
|
||||||
- CLEAN_STORE_MESSAGE_UP=true
|
|
||||||
- CLEAN_STORE_CONTACTS=true
|
|
||||||
- CLEAN_STORE_CHATS=true
|
|
||||||
# Permanent data storage
|
|
||||||
- DATABASE_ENABLED=true
|
|
||||||
- DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
|
||||||
- DATABASE_CONNECTION_DB_PREFIX_NAME=evolution
|
|
||||||
# Choose the data you want to save in the application's database or store
|
|
||||||
- DATABASE_SAVE_DATA_INSTANCE=true
|
|
||||||
- DATABASE_SAVE_DATA_OLD_MESSAGE=false
|
|
||||||
- DATABASE_SAVE_DATA_NEW_MESSAGE=true
|
|
||||||
- DATABASE_SAVE_MESSAGE_UPDATE=true
|
|
||||||
- DATABASE_SAVE_DATA_CONTACTS=true
|
|
||||||
- DATABASE_SAVE_DATA_CHATS=true
|
|
||||||
- REDIS_ENABLED=true
|
|
||||||
- REDIS_URI=redis://redis:6379
|
|
||||||
- REDIS_PREFIX_KEY=evolution
|
|
||||||
# Webhook Settings
|
|
||||||
# Define a global webhook that will listen for enabled events from all instances
|
|
||||||
- WEBHOOK_GLOBAL_URL=url
|
|
||||||
- WEBHOOK_GLOBAL_ENABLED=false
|
|
||||||
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
|
|
||||||
- WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
|
|
||||||
# Automatically maps webhook paths
|
|
||||||
# Set the events you want to hear
|
|
||||||
- WEBHOOK_EVENTS_STATUS_INSTANCE=true
|
|
||||||
- WEBHOOK_EVENTS_APPLICATION_STARTUP=false
|
|
||||||
- WEBHOOK_EVENTS_QRCODE_UPDATED=true
|
|
||||||
- WEBHOOK_EVENTS_MESSAGES_SET=true
|
|
||||||
- WEBHOOK_EVENTS_MESSAGES_UPDATE=true
|
|
||||||
- WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
|
||||||
- WEBHOOK_EVENTS_SEND_MESSAGE=true
|
|
||||||
- WEBHOOK_EVENTS_CONTACTS_SET=true
|
|
||||||
- WEBHOOK_EVENTS_CONTACTS_UPSERT=true
|
|
||||||
- WEBHOOK_EVENTS_CONTACTS_UPDATE=true
|
|
||||||
- WEBHOOK_EVENTS_PRESENCE_UPDATE=true
|
|
||||||
- WEBHOOK_EVENTS_CHATS_SET=true
|
|
||||||
- WEBHOOK_EVENTS_CHATS_UPSERT=true
|
|
||||||
- WEBHOOK_EVENTS_CHATS_UPDATE=true
|
|
||||||
- WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
|
||||||
- WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
|
||||||
- WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
|
||||||
- WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
|
||||||
# This event fires every time a new token is requested via the refresh route
|
|
||||||
- WEBHOOK_EVENTS_NEW_JWT_TOKEN=true
|
|
||||||
# Name that will be displayed on smartphone connection
|
|
||||||
- CONFIG_SESSION_PHONE_CLIENT="Evolution API"
|
|
||||||
# Set qrcode display limit
|
|
||||||
- QRCODE_LIMIT=30
|
|
||||||
# Defines an authentication type for the api
|
|
||||||
- AUTHENTICATION_TYPE=apikey # jwt or apikey
|
|
||||||
# Define a global apikey to access all instances
|
|
||||||
# OBS: This key must be inserted in the request header to create an instance.
|
|
||||||
- AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976
|
|
||||||
# Expose the api key on return from fetch instances
|
|
||||||
- AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
|
|
||||||
# Set the secret key to encrypt and decrypt your token and its expiration time.
|
|
||||||
- AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s === 1h | zero (0) - never expires
|
|
||||||
# Set the instance name and webhook url to create an instance in init the application
|
|
||||||
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
|
||||||
- AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS=false
|
|
||||||
- AUTHENTICATION_INSTANCE_MODE=server # container or server
|
|
||||||
# if you are using container mode, set the container name and the webhook url to default instance
|
|
||||||
- AUTHENTICATION_INSTANCE_NAME=evolution
|
|
||||||
- AUTHENTICATION_INSTANCE_WEBHOOK_URL=url
|
|
||||||
command: ['node', './dist/src/main.js']
|
command: ['node', './dist/src/main.js']
|
||||||
networks:
|
|
||||||
- evolution-net
|
|
||||||
expose:
|
expose:
|
||||||
- 8080
|
- 8080
|
||||||
|
|
||||||
mongodb:
|
|
||||||
container_name: mongodb
|
|
||||||
image: mongo
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- evolution_mongodb_data:/data/db
|
|
||||||
- evolution_mongodb_configdb:/data/configdb
|
|
||||||
ports:
|
|
||||||
- 27017:27017
|
|
||||||
environment:
|
|
||||||
MONGO_INITDB_ROOT_USERNAME: root
|
|
||||||
MONGO_INITDB_ROOT_PASSWORD: root
|
|
||||||
networks:
|
|
||||||
- evolution-net
|
|
||||||
expose:
|
|
||||||
- 27017
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:latest
|
|
||||||
command: >
|
|
||||||
redis-server
|
|
||||||
--port 6379
|
|
||||||
--appendonly yes
|
|
||||||
--save 900 1
|
|
||||||
--save 300 10
|
|
||||||
--save 60 10000
|
|
||||||
--appendfsync everysec
|
|
||||||
volumes:
|
|
||||||
- evolution_redis:/data
|
|
||||||
container_name: redis
|
|
||||||
ports:
|
|
||||||
- 6379:6379
|
|
||||||
networks:
|
|
||||||
- evolution-net
|
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
evolution_instances:
|
evolution_instances:
|
||||||
evolution_store:
|
evolution_store:
|
||||||
evolution_mongodb_data:
|
|
||||||
evolution_mongodb_configdb:
|
networks:
|
||||||
evolution_redis:
|
default:
|
||||||
|
name: evolution-net
|
||||||
|
|
||||||
@@ -8,11 +8,6 @@ then
|
|||||||
docker network create -d bridge ${NET}
|
docker network create -d bridge ${NET}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo mkdir -p ./docker-data/instances
|
|
||||||
sudo mkdir -p ./docker-data/mongodb
|
|
||||||
sudo mkdir -p ./docker-data/mongodb/data
|
|
||||||
sudo mkdir -p ./docker-data/mongodb/configdb
|
|
||||||
|
|
||||||
docker build -t ${IMAGE} .
|
docker build -t ${IMAGE} .
|
||||||
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
networks:
|
|
||||||
api-net:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
services:
|
|
||||||
mongodb:
|
|
||||||
container_name: mongodb
|
|
||||||
|
|
||||||
# This image already has a single replica set
|
|
||||||
image: mongo
|
|
||||||
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
# sudo mkdir -p /data/mongodb
|
|
||||||
- /data/mongodb:/data/db
|
|
||||||
ports:
|
|
||||||
- 26712:27017
|
|
||||||
environment:
|
|
||||||
MONGO_INITDB_ROOT_USERNAME: root
|
|
||||||
# Set a password to access the bank
|
|
||||||
MONGO_INITDB_ROOT_PASSWORD: <password>
|
|
||||||
networks:
|
|
||||||
- api-net
|
|
||||||
expose:
|
|
||||||
- 26712
|
|
||||||
14
package.json
14
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "evolution-api",
|
"name": "evolution-api",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"description": "Rest api for communication with WhatsApp",
|
"description": "Rest api for communication with WhatsApp",
|
||||||
"main": "index.js",
|
"main": "./dist/src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "ts-node --files --transpile-only ./src/main.ts",
|
"start": "ts-node --files --transpile-only ./src/main.ts",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/DavidsonGomes/evolution-api.git"
|
"url": "git+https://github.com/EvolutionAPI/evolution-api.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"chat",
|
"chat",
|
||||||
@@ -36,14 +36,15 @@
|
|||||||
},
|
},
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/DavidsonGomes/evolution-api/issues"
|
"url": "https://github.com/EvolutionAPI/evolution-api/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/DavidsonGomes/evolution-api#readme",
|
"homepage": "https://github.com/EvolutionAPI/evolution-api#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adiwajshing/keyed-db": "^0.2.4",
|
"@adiwajshing/keyed-db": "^0.2.4",
|
||||||
"@evolution/base": "github:WhiskeySockets/Baileys",
|
|
||||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||||
"@hapi/boom": "^10.0.1",
|
"@hapi/boom": "^10.0.1",
|
||||||
|
"@whiskeysockets/baileys": "github:DavidsonGomes/Baileys",
|
||||||
|
"@figuro/chatwoot-sdk": "^1.1.14",
|
||||||
"axios": "^1.3.5",
|
"axios": "^1.3.5",
|
||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
@@ -65,6 +66,7 @@
|
|||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
"node-mime-types": "^1.1.0",
|
"node-mime-types": "^1.1.0",
|
||||||
"pino": "^8.11.0",
|
"pino": "^8.11.0",
|
||||||
|
"proxy-agent": "^6.2.1",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"redis": "^4.6.5",
|
"redis": "^4.6.5",
|
||||||
|
|||||||
2034
postman.json
2034
postman.json
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@ import { join } from 'path';
|
|||||||
import { SRC_DIR } from './path.config';
|
import { SRC_DIR } from './path.config';
|
||||||
import { isBooleanString } from 'class-validator';
|
import { isBooleanString } from 'class-validator';
|
||||||
|
|
||||||
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number };
|
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string };
|
||||||
|
|
||||||
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
|
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
|
||||||
export type Cors = {
|
export type Cors = {
|
||||||
@@ -13,15 +13,26 @@ export type Cors = {
|
|||||||
CREDENTIALS: boolean;
|
CREDENTIALS: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK';
|
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
||||||
|
|
||||||
|
export type LogLevel =
|
||||||
|
| 'ERROR'
|
||||||
|
| 'WARN'
|
||||||
|
| 'DEBUG'
|
||||||
|
| 'INFO'
|
||||||
|
| 'LOG'
|
||||||
|
| 'VERBOSE'
|
||||||
|
| 'DARK'
|
||||||
|
| 'WEBHOOKS';
|
||||||
|
|
||||||
export type Log = {
|
export type Log = {
|
||||||
LEVEL: LogLevel[];
|
LEVEL: LogLevel[];
|
||||||
COLOR: boolean;
|
COLOR: boolean;
|
||||||
|
BAILEYS: LogBaileys;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SaveData = {
|
export type SaveData = {
|
||||||
INSTANCE: boolean;
|
INSTANCE: boolean;
|
||||||
OLD_MESSAGE: boolean;
|
|
||||||
NEW_MESSAGE: boolean;
|
NEW_MESSAGE: boolean;
|
||||||
MESSAGE_UPDATE: boolean;
|
MESSAGE_UPDATE: boolean;
|
||||||
CONTACTS: boolean;
|
CONTACTS: boolean;
|
||||||
@@ -87,7 +98,9 @@ export type Instance = {
|
|||||||
NAME: string;
|
NAME: string;
|
||||||
WEBHOOK_URL: string;
|
WEBHOOK_URL: string;
|
||||||
MODE: string;
|
MODE: string;
|
||||||
WEBHOOK_BY_EVENTS: boolean;
|
CHATWOOT_ACCOUNT_ID?: string;
|
||||||
|
CHATWOOT_TOKEN?: string;
|
||||||
|
CHATWOOT_URL?: string;
|
||||||
};
|
};
|
||||||
export type Auth = {
|
export type Auth = {
|
||||||
API_KEY: ApiKey;
|
API_KEY: ApiKey;
|
||||||
@@ -150,7 +163,9 @@ export class ConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private envYaml(): Env {
|
private envYaml(): Env {
|
||||||
return load(readFileSync(join(SRC_DIR, 'env.yml'), { encoding: 'utf-8' })) as Env;
|
return load(
|
||||||
|
readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' }),
|
||||||
|
) as Env;
|
||||||
}
|
}
|
||||||
|
|
||||||
private envProcess(): Env {
|
private envProcess(): Env {
|
||||||
@@ -158,6 +173,7 @@ export class ConfigService {
|
|||||||
SERVER: {
|
SERVER: {
|
||||||
TYPE: process.env.SERVER_TYPE as 'http' | 'https',
|
TYPE: process.env.SERVER_TYPE as 'http' | 'https',
|
||||||
PORT: Number.parseInt(process.env.SERVER_PORT),
|
PORT: Number.parseInt(process.env.SERVER_PORT),
|
||||||
|
URL: process.env.SERVER_URL,
|
||||||
},
|
},
|
||||||
CORS: {
|
CORS: {
|
||||||
ORIGIN: process.env.CORS_ORIGIN.split(','),
|
ORIGIN: process.env.CORS_ORIGIN.split(','),
|
||||||
@@ -191,7 +207,6 @@ export class ConfigService {
|
|||||||
ENABLED: process.env?.DATABASE_ENABLED === 'true',
|
ENABLED: process.env?.DATABASE_ENABLED === 'true',
|
||||||
SAVE_DATA: {
|
SAVE_DATA: {
|
||||||
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
|
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
|
||||||
OLD_MESSAGE: process.env?.DATABASE_SAVE_DATA_OLD_MESSAGE === 'true',
|
|
||||||
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
|
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
|
||||||
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
|
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
|
||||||
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
|
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
|
||||||
@@ -206,6 +221,7 @@ export class ConfigService {
|
|||||||
LOG: {
|
LOG: {
|
||||||
LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[],
|
LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[],
|
||||||
COLOR: process.env?.LOG_COLOR === 'true',
|
COLOR: process.env?.LOG_COLOR === 'true',
|
||||||
|
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
|
||||||
},
|
},
|
||||||
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
|
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
|
||||||
? process.env.DEL_INSTANCE === 'true'
|
? process.env.DEL_INSTANCE === 'true'
|
||||||
@@ -263,8 +279,10 @@ export class ConfigService {
|
|||||||
NAME: process.env.AUTHENTICATION_INSTANCE_NAME,
|
NAME: process.env.AUTHENTICATION_INSTANCE_NAME,
|
||||||
WEBHOOK_URL: process.env.AUTHENTICATION_INSTANCE_WEBHOOK_URL,
|
WEBHOOK_URL: process.env.AUTHENTICATION_INSTANCE_WEBHOOK_URL,
|
||||||
MODE: process.env.AUTHENTICATION_INSTANCE_MODE,
|
MODE: process.env.AUTHENTICATION_INSTANCE_MODE,
|
||||||
WEBHOOK_BY_EVENTS:
|
CHATWOOT_ACCOUNT_ID:
|
||||||
process.env.AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS === 'true',
|
process.env.AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID || '',
|
||||||
|
CHATWOOT_TOKEN: process.env.AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN || '',
|
||||||
|
CHATWOOT_URL: process.env.AUTHENTICATION_INSTANCE_CHATWOOT_URL || '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ export class Logger {
|
|||||||
this.configService.get<Log>('LOG').LEVEL.forEach((level) => types.push(Type[level]));
|
this.configService.get<Log>('LOG').LEVEL.forEach((level) => types.push(Type[level]));
|
||||||
|
|
||||||
const typeValue = typeof value;
|
const typeValue = typeof value;
|
||||||
|
|
||||||
if (types.includes(type)) {
|
if (types.includes(type)) {
|
||||||
if (configService.get<Log>('LOG').COLOR) {
|
if (configService.get<Log>('LOG').COLOR) {
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ export const ROOT_DIR = process.cwd();
|
|||||||
export const INSTANCE_DIR = join(ROOT_DIR, 'instances');
|
export const INSTANCE_DIR = join(ROOT_DIR, 'instances');
|
||||||
export const SRC_DIR = join(ROOT_DIR, 'src');
|
export const SRC_DIR = join(ROOT_DIR, 'src');
|
||||||
export const AUTH_DIR = join(ROOT_DIR, 'store', 'auth');
|
export const AUTH_DIR = join(ROOT_DIR, 'store', 'auth');
|
||||||
|
export const STORE_DIR = join(ROOT_DIR, 'store');
|
||||||
|
|||||||
@@ -2,13 +2,23 @@ import mongoose from 'mongoose';
|
|||||||
import { configService, Database } from '../config/env.config';
|
import { configService, Database } from '../config/env.config';
|
||||||
import { Logger } from '../config/logger.config';
|
import { Logger } from '../config/logger.config';
|
||||||
|
|
||||||
const logger = new Logger('Db Connection');
|
const logger = new Logger('MongoDB');
|
||||||
|
|
||||||
const db = configService.get<Database>('DATABASE');
|
const db = configService.get<Database>('DATABASE');
|
||||||
export const dbserver = db.ENABLED
|
export const dbserver = (() => {
|
||||||
? mongoose.createConnection(db.CONNECTION.URI, {
|
if (db.ENABLED) {
|
||||||
|
logger.verbose('connecting');
|
||||||
|
const dbs = mongoose.createConnection(db.CONNECTION.URI, {
|
||||||
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
|
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
|
||||||
})
|
});
|
||||||
: null;
|
logger.verbose('connected in ' + db.CONNECTION.URI);
|
||||||
|
logger.info('ON - dbName: ' + dbs['$dbName']);
|
||||||
|
|
||||||
db.ENABLED ? logger.info('ON - dbName: ' + dbserver['$dbName']) : null;
|
process.on('beforeExit', () => {
|
||||||
|
logger.verbose('instance destroyed');
|
||||||
|
dbserver.destroy(true, (error) => logger.error(error));
|
||||||
|
});
|
||||||
|
|
||||||
|
return dbs;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,24 +1,44 @@
|
|||||||
import { createClient, RedisClientType } from '@redis/client';
|
import { createClient, RedisClientType } from '@redis/client';
|
||||||
import { Logger } from '../config/logger.config';
|
import { Logger } from '../config/logger.config';
|
||||||
import { BufferJSON } from '@evolution/base';
|
import { BufferJSON } from '@whiskeysockets/baileys';
|
||||||
import { Redis } from '../config/env.config';
|
import { Redis } from '../config/env.config';
|
||||||
|
|
||||||
export class RedisCache {
|
export class RedisCache {
|
||||||
constructor(private readonly redisEnv: Partial<Redis>, private instanceName?: string) {
|
constructor() {
|
||||||
this.client = createClient({ url: this.redisEnv.URI });
|
this.logger.verbose('instance created');
|
||||||
|
process.on('beforeExit', async () => {
|
||||||
this.client.connect();
|
this.logger.verbose('instance destroyed');
|
||||||
|
if (this.statusConnection) {
|
||||||
|
this.logger.verbose('instance disconnect');
|
||||||
|
await this.client.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private statusConnection = false;
|
||||||
|
private instanceName: string;
|
||||||
|
private redisEnv: Redis;
|
||||||
|
|
||||||
public set reference(reference: string) {
|
public set reference(reference: string) {
|
||||||
|
this.logger.verbose('set reference: ' + reference);
|
||||||
this.instanceName = reference;
|
this.instanceName = reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async connect(redisEnv: Redis) {
|
||||||
|
this.logger.verbose('connecting');
|
||||||
|
this.client = createClient({ url: redisEnv.URI });
|
||||||
|
this.logger.verbose('connected in ' + redisEnv.URI);
|
||||||
|
await this.client.connect();
|
||||||
|
this.statusConnection = true;
|
||||||
|
this.redisEnv = redisEnv;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly logger = new Logger(RedisCache.name);
|
private readonly logger = new Logger(RedisCache.name);
|
||||||
private client: RedisClientType;
|
private client: RedisClientType;
|
||||||
|
|
||||||
public async instanceKeys(): Promise<string[]> {
|
public async instanceKeys(): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
|
||||||
return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']);
|
return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
@@ -27,14 +47,18 @@ export class RedisCache {
|
|||||||
|
|
||||||
public async keyExists(key?: string) {
|
public async keyExists(key?: string) {
|
||||||
if (key) {
|
if (key) {
|
||||||
|
this.logger.verbose('keyExists: ' + key);
|
||||||
return !!(await this.instanceKeys()).find((i) => i === key);
|
return !!(await this.instanceKeys()).find((i) => i === key);
|
||||||
}
|
}
|
||||||
|
this.logger.verbose('keyExists: ' + this.instanceName);
|
||||||
return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
|
return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async writeData(field: string, data: any) {
|
public async writeData(field: string, data: any) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('writeData: ' + field);
|
||||||
const json = JSON.stringify(data, BufferJSON.replacer);
|
const json = JSON.stringify(data, BufferJSON.replacer);
|
||||||
|
|
||||||
return await this.client.hSet(
|
return await this.client.hSet(
|
||||||
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||||
field,
|
field,
|
||||||
@@ -47,13 +71,19 @@ export class RedisCache {
|
|||||||
|
|
||||||
public async readData(field: string) {
|
public async readData(field: string) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('readData: ' + field);
|
||||||
const data = await this.client.hGet(
|
const data = await this.client.hGet(
|
||||||
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||||
field,
|
field,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
|
this.logger.verbose('readData: ' + field + ' success');
|
||||||
return JSON.parse(data, BufferJSON.reviver);
|
return JSON.parse(data, BufferJSON.reviver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('readData: ' + field + ' not found');
|
||||||
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
}
|
}
|
||||||
@@ -61,6 +91,7 @@ export class RedisCache {
|
|||||||
|
|
||||||
public async removeData(field: string) {
|
public async removeData(field: string) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('removeData: ' + field);
|
||||||
return await this.client.hDel(
|
return await this.client.hDel(
|
||||||
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||||
field,
|
field,
|
||||||
@@ -72,9 +103,12 @@ export class RedisCache {
|
|||||||
|
|
||||||
public async delAll(hash?: string) {
|
public async delAll(hash?: string) {
|
||||||
try {
|
try {
|
||||||
return await this.client.del(
|
this.logger.verbose('instance delAll: ' + hash);
|
||||||
|
const result = await this.client.del(
|
||||||
hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
SERVER:
|
SERVER:
|
||||||
TYPE: http # https
|
TYPE: http # https
|
||||||
PORT: 8080 # 443
|
PORT: 8080 # 443
|
||||||
|
URL: localhost
|
||||||
|
|
||||||
CORS:
|
CORS:
|
||||||
ORIGIN:
|
ORIGIN:
|
||||||
- '*'
|
- "*"
|
||||||
# - yourdomain.com
|
# - yourdomain.com
|
||||||
METHODS:
|
METHODS:
|
||||||
- POST
|
- POST
|
||||||
@@ -36,7 +37,9 @@ LOG:
|
|||||||
- LOG
|
- LOG
|
||||||
- VERBOSE
|
- VERBOSE
|
||||||
- DARK
|
- DARK
|
||||||
|
- WEBHOOKS
|
||||||
COLOR: true
|
COLOR: true
|
||||||
|
BAILEYS: error # "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
||||||
|
|
||||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
# Determine how long the instance should be deleted from memory in case of no connection.
|
||||||
# Default time: 5 minutes
|
# Default time: 5 minutes
|
||||||
@@ -59,36 +62,36 @@ CLEAN_STORE:
|
|||||||
|
|
||||||
# Permanent data storage
|
# Permanent data storage
|
||||||
DATABASE:
|
DATABASE:
|
||||||
ENABLED: true
|
ENABLED: false
|
||||||
CONNECTION:
|
CONNECTION:
|
||||||
URI: 'mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true'
|
URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true"
|
||||||
DB_PREFIX_NAME: evolution
|
DB_PREFIX_NAME: evolution
|
||||||
# Choose the data you want to save in the application's database or store
|
# Choose the data you want to save in the application's database or store
|
||||||
SAVE_DATA:
|
SAVE_DATA:
|
||||||
INSTANCE: false
|
INSTANCE: false
|
||||||
OLD_MESSAGE: false
|
NEW_MESSAGE: false
|
||||||
NEW_MESSAGE: true
|
MESSAGE_UPDATE: false
|
||||||
MESSAGE_UPDATE: true
|
CONTACTS: false
|
||||||
CONTACTS: true
|
CHATS: false
|
||||||
CHATS: true
|
|
||||||
|
|
||||||
REDIS:
|
REDIS:
|
||||||
ENABLED: true
|
ENABLED: false
|
||||||
URI: 'redis://localhost:6379/1'
|
URI: "redis://localhost:6379"
|
||||||
PREFIX_KEY: 'evolution'
|
PREFIX_KEY: "evolution"
|
||||||
|
|
||||||
# Webhook Settings
|
# Global Webhook Settings
|
||||||
|
# Each instance's Webhook URL and events will be requested at the time it is created
|
||||||
WEBHOOK:
|
WEBHOOK:
|
||||||
# Define a global webhook that will listen for enabled events from all instances
|
# Define a global webhook that will listen for enabled events from all instances
|
||||||
GLOBAL:
|
GLOBAL:
|
||||||
URL: <url>
|
URL: <url>
|
||||||
ENABLED: true
|
ENABLED: false
|
||||||
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
|
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
|
||||||
WEBHOOK_BY_EVENTS: false
|
WEBHOOK_BY_EVENTS: false
|
||||||
# Automatically maps webhook paths
|
# Automatically maps webhook paths
|
||||||
# Set the events you want to hear
|
# Set the events you want to hear
|
||||||
EVENTS:
|
EVENTS:
|
||||||
APPLICATION_STARTUP: true
|
APPLICATION_STARTUP: false
|
||||||
QRCODE_UPDATED: true
|
QRCODE_UPDATED: true
|
||||||
MESSAGES_SET: true
|
MESSAGES_SET: true
|
||||||
MESSAGES_UPSERT: true
|
MESSAGES_UPSERT: true
|
||||||
@@ -107,24 +110,26 @@ WEBHOOK:
|
|||||||
GROUP_PARTICIPANTS_UPDATE: true
|
GROUP_PARTICIPANTS_UPDATE: true
|
||||||
CONNECTION_UPDATE: true
|
CONNECTION_UPDATE: true
|
||||||
# This event fires every time a new token is requested via the refresh route
|
# This event fires every time a new token is requested via the refresh route
|
||||||
NEW_JWT_TOKEN: true
|
NEW_JWT_TOKEN: false
|
||||||
|
|
||||||
CONFIG_SESSION_PHONE:
|
CONFIG_SESSION_PHONE:
|
||||||
# Name that will be displayed on smartphone connection
|
# Name that will be displayed on smartphone connection
|
||||||
CLIENT: 'Evolution API'
|
CLIENT: "Evolution API"
|
||||||
NAME: Chrome # firefox | edge | opera | safari
|
NAME: chrome # chrome | firefox | edge | opera | safari
|
||||||
|
|
||||||
# Set qrcode display limit
|
# Set qrcode display limit
|
||||||
QRCODE:
|
QRCODE:
|
||||||
LIMIT: 30
|
LIMIT: 30
|
||||||
|
|
||||||
# Defines an authentication type for the api
|
# Defines an authentication type for the api
|
||||||
|
# We recommend using the apikey because it will allow you to use a custom token,
|
||||||
|
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
|
||||||
AUTHENTICATION:
|
AUTHENTICATION:
|
||||||
TYPE: apikey # or jwt apikey
|
TYPE: apikey # jwt or apikey
|
||||||
# Define a global apikey to access all instances
|
# Define a global apikey to access all instances
|
||||||
API_KEY:
|
API_KEY:
|
||||||
# OBS: This key must be inserted in the request header to create an instance.
|
# OBS: This key must be inserted in the request header to create an instance.
|
||||||
KEY: B6D711FC-DE4D-4FD5-9365-44120E713976
|
KEY: B6D711FCDE4D4FD5936544120E713976
|
||||||
# Expose the api key on return from fetch instances
|
# Expose the api key on return from fetch instances
|
||||||
EXPOSE_IN_FETCH_INSTANCES: true
|
EXPOSE_IN_FETCH_INSTANCES: true
|
||||||
# Set the secret key to encrypt and decrypt your token and its expiration time.
|
# Set the secret key to encrypt and decrypt your token and its expiration time.
|
||||||
@@ -133,9 +138,11 @@ AUTHENTICATION:
|
|||||||
SECRET: L=0YWt]b2w[WF>#>:&E`
|
SECRET: L=0YWt]b2w[WF>#>:&E`
|
||||||
# Set the instance name and webhook url to create an instance in init the application
|
# Set the instance name and webhook url to create an instance in init the application
|
||||||
INSTANCE:
|
INSTANCE:
|
||||||
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
||||||
WEBHOOK_BY_EVENTS: false
|
|
||||||
MODE: server # container or server
|
MODE: server # container or server
|
||||||
# if you are using container mode, set the container name and the webhook url to default instance
|
# if you are using container mode, set the container name and the webhook url to default instance
|
||||||
NAME: evolution
|
NAME: evolution
|
||||||
WEBHOOK_URL: <url>
|
WEBHOOK_URL: <url>
|
||||||
|
CHATWOOT_ACCOUNT_ID: 1
|
||||||
|
CHATWOOT_TOKEN: 123456
|
||||||
|
CHATWOOT_URL: <url>
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ function initWA() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function bootstrap() {
|
function bootstrap() {
|
||||||
initWA();
|
|
||||||
|
|
||||||
const logger = new Logger('SERVER');
|
const logger = new Logger('SERVER');
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@@ -34,8 +32,8 @@ function bootstrap() {
|
|||||||
methods: [...configService.get<Cors>('CORS').METHODS],
|
methods: [...configService.get<Cors>('CORS').METHODS],
|
||||||
credentials: configService.get<Cors>('CORS').CREDENTIALS,
|
credentials: configService.get<Cors>('CORS').CREDENTIALS,
|
||||||
}),
|
}),
|
||||||
urlencoded({ extended: true, limit: '50mb' }),
|
urlencoded({ extended: true, limit: '136mb' }),
|
||||||
json({ limit: '50mb' }),
|
json({ limit: '136mb' }),
|
||||||
compression(),
|
compression(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -73,6 +71,8 @@ function bootstrap() {
|
|||||||
logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT),
|
logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
initWA();
|
||||||
|
|
||||||
onUnexpectedError();
|
onUnexpectedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
initAuthCreds,
|
initAuthCreds,
|
||||||
proto,
|
proto,
|
||||||
SignalDataTypeMap,
|
SignalDataTypeMap,
|
||||||
} from '@evolution/base';
|
} from '@whiskeysockets/baileys';
|
||||||
import { configService, Database } from '../config/env.config';
|
import { configService, Database } from '../config/env.config';
|
||||||
import { Logger } from '../config/logger.config';
|
import { Logger } from '../config/logger.config';
|
||||||
import { dbserver } from '../db/db.connect';
|
import { dbserver } from '../db/db.connect';
|
||||||
|
|||||||
@@ -4,22 +4,17 @@ import {
|
|||||||
initAuthCreds,
|
initAuthCreds,
|
||||||
proto,
|
proto,
|
||||||
SignalDataTypeMap,
|
SignalDataTypeMap,
|
||||||
} from '@evolution/base';
|
} from '@whiskeysockets/baileys';
|
||||||
import { RedisCache } from '../db/redis.client';
|
import { RedisCache } from '../db/redis.client';
|
||||||
import { Logger } from '../config/logger.config';
|
import { Logger } from '../config/logger.config';
|
||||||
import { Redis } from '../config/env.config';
|
import { Redis } from '../config/env.config';
|
||||||
|
|
||||||
export async function useMultiFileAuthStateRedisDb(
|
export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||||
redisEnv: Partial<Redis>,
|
|
||||||
instanceName: string,
|
|
||||||
): Promise<{
|
|
||||||
state: AuthenticationState;
|
state: AuthenticationState;
|
||||||
saveCreds: () => Promise<void>;
|
saveCreds: () => Promise<void>;
|
||||||
}> {
|
}> {
|
||||||
const logger = new Logger(useMultiFileAuthStateRedisDb.name);
|
const logger = new Logger(useMultiFileAuthStateRedisDb.name);
|
||||||
|
|
||||||
const cache = new RedisCache(redisEnv, instanceName);
|
|
||||||
|
|
||||||
const writeData = async (data: any, key: string): Promise<any> => {
|
const writeData = async (data: any, key: string): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
return await cache.writeData(key, data);
|
return await cache.writeData(key, data);
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ export const instanceNameSchema: JSONSchema7 = {
|
|||||||
properties: {
|
properties: {
|
||||||
instanceName: { type: 'string' },
|
instanceName: { type: 'string' },
|
||||||
webhook: { type: 'string' },
|
webhook: { type: 'string' },
|
||||||
|
webhook_by_events: { type: 'boolean' },
|
||||||
events: {
|
events: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
minItems: 1,
|
minItems: 0,
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: [
|
enum: [
|
||||||
@@ -189,6 +190,37 @@ export const pollMessageSchema: JSONSchema7 = {
|
|||||||
required: ['pollMessage', 'number'],
|
required: ['pollMessage', 'number'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const statusMessageSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
statusMessage: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: { type: 'string', enum: ['text', 'image', 'audio', 'video'] },
|
||||||
|
content: { type: 'string' },
|
||||||
|
caption: { type: 'string' },
|
||||||
|
backgroundColor: { type: 'string' },
|
||||||
|
font: { type: 'integer', minimum: 0, maximum: 5 },
|
||||||
|
statusJidList: {
|
||||||
|
type: 'array',
|
||||||
|
minItems: 1,
|
||||||
|
uniqueItems: true,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
pattern: '^\\d+',
|
||||||
|
description: '"statusJidList" must be an array of numeric strings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
allContacts: { type: 'boolean', enum: [true, false] },
|
||||||
|
},
|
||||||
|
required: ['type', 'content'],
|
||||||
|
...isNotEmpty('type', 'content'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['statusMessage'],
|
||||||
|
};
|
||||||
|
|
||||||
export const mediaMessageSchema: JSONSchema7 = {
|
export const mediaMessageSchema: JSONSchema7 = {
|
||||||
$id: v4(),
|
$id: v4(),
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -379,6 +411,9 @@ export const contactMessageSchema: JSONSchema7 = {
|
|||||||
description: '"wuid" must be a numeric string',
|
description: '"wuid" must be a numeric string',
|
||||||
},
|
},
|
||||||
phoneNumber: { type: 'string', minLength: 10 },
|
phoneNumber: { type: 'string', minLength: 10 },
|
||||||
|
organization: { type: 'string' },
|
||||||
|
email: { type: 'string' },
|
||||||
|
url: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: ['fullName', 'wuid', 'phoneNumber'],
|
required: ['fullName', 'wuid', 'phoneNumber'],
|
||||||
...isNotEmpty('fullName'),
|
...isNotEmpty('fullName'),
|
||||||
@@ -665,6 +700,38 @@ export const groupJidSchema: JSONSchema7 = {
|
|||||||
...isNotEmpty('groupJid'),
|
...isNotEmpty('groupJid'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getParticipantsSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
getParticipants: { type: 'string', enum: ['true', 'false'] },
|
||||||
|
},
|
||||||
|
required: ['getParticipants'],
|
||||||
|
...isNotEmpty('getParticipants'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const groupSendInviteSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupJid: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
numbers: {
|
||||||
|
type: 'array',
|
||||||
|
minItems: 1,
|
||||||
|
uniqueItems: true,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
minLength: 10,
|
||||||
|
pattern: '\\d+',
|
||||||
|
description: '"numbers" must be an array of numeric strings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['groupJid', 'description', 'numbers'],
|
||||||
|
...isNotEmpty('groupJid', 'description', 'numbers'),
|
||||||
|
};
|
||||||
|
|
||||||
export const groupInviteSchema: JSONSchema7 = {
|
export const groupInviteSchema: JSONSchema7 = {
|
||||||
$id: v4(),
|
$id: v4(),
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -770,7 +837,7 @@ export const webhookSchema: JSONSchema7 = {
|
|||||||
enabled: { type: 'boolean', enum: [true, false] },
|
enabled: { type: 'boolean', enum: [true, false] },
|
||||||
events: {
|
events: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
minItems: 1,
|
minItems: 0,
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: [
|
enum: [
|
||||||
@@ -800,3 +867,17 @@ export const webhookSchema: JSONSchema7 = {
|
|||||||
required: ['url', 'enabled'],
|
required: ['url', 'enabled'],
|
||||||
...isNotEmpty('url'),
|
...isNotEmpty('url'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const chatwootSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
enabled: { type: 'boolean', enum: [true, false] },
|
||||||
|
account_id: { type: 'string' },
|
||||||
|
token: { type: 'string' },
|
||||||
|
url: { type: 'string' },
|
||||||
|
sign_msg: { type: 'boolean', enum: [true, false] },
|
||||||
|
},
|
||||||
|
required: ['enabled', 'account_id', 'token', 'url', 'sign_msg'],
|
||||||
|
...isNotEmpty('account_id', 'token', 'url', 'sign_msg'),
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { ROOT_DIR } from '../../config/path.config';
|
|||||||
export type IInsert = { insertCount: number };
|
export type IInsert = { insertCount: number };
|
||||||
|
|
||||||
export interface IRepository {
|
export interface IRepository {
|
||||||
insert(data: any, saveDb?: boolean): Promise<IInsert>;
|
insert(data: any, instanceName: string, saveDb?: boolean): Promise<IInsert>;
|
||||||
|
update(data: any, instanceName: string, saveDb?: boolean): Promise<IInsert>;
|
||||||
find(query: any): Promise<any>;
|
find(query: any): Promise<any>;
|
||||||
delete(query: any, force?: boolean): Promise<any>;
|
delete(query: any, force?: boolean): Promise<any>;
|
||||||
|
|
||||||
@@ -45,9 +46,14 @@ export abstract class Repository implements IRepository {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public insert(data: any, saveDb = false): Promise<IInsert> {
|
public insert(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public update(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
public find(query: any): Promise<any> {
|
public find(query: any): Promise<any> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { validate } from 'jsonschema';
|
|||||||
import { BadRequestException } from '../../exceptions';
|
import { BadRequestException } from '../../exceptions';
|
||||||
import 'express-async-errors';
|
import 'express-async-errors';
|
||||||
import { Logger } from '../../config/logger.config';
|
import { Logger } from '../../config/logger.config';
|
||||||
import { GroupInvite, GroupJid } from '../dto/group.dto';
|
import { GetParticipant, GroupInvite, GroupJid } from '../dto/group.dto';
|
||||||
|
|
||||||
type DataValidate<T> = {
|
type DataValidate<T> = {
|
||||||
request: Request;
|
request: Request;
|
||||||
@@ -181,4 +181,47 @@ export abstract class RouterBroker {
|
|||||||
|
|
||||||
return await execute(instance, ref);
|
return await execute(instance, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getParticipantsValidate<T>(args: DataValidate<T>) {
|
||||||
|
const { request, ClassRef, schema, execute } = args;
|
||||||
|
|
||||||
|
const getParticipants = request.query as unknown as GetParticipant;
|
||||||
|
|
||||||
|
if (!getParticipants?.getParticipants) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
'The getParticipants needs to be informed in the query',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = request.params as unknown as InstanceDto;
|
||||||
|
const body = request.body;
|
||||||
|
|
||||||
|
const ref = new ClassRef();
|
||||||
|
|
||||||
|
Object.assign(body, getParticipants);
|
||||||
|
Object.assign(ref, body);
|
||||||
|
|
||||||
|
const v = validate(ref, schema);
|
||||||
|
|
||||||
|
console.log(v, '@checkei aqui');
|
||||||
|
|
||||||
|
if (!v.valid) {
|
||||||
|
const message: any[] = v.errors.map(({ property, stack, schema }) => {
|
||||||
|
let message: string;
|
||||||
|
if (schema['description']) {
|
||||||
|
message = schema['description'];
|
||||||
|
} else {
|
||||||
|
message = stack.replace('instance.', '');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
property: property.replace('instance.', ''),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
logger.error([...message]);
|
||||||
|
throw new BadRequestException(...message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await execute(instance, ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { proto } from '@evolution/base';
|
import { proto } from '@whiskeysockets/baileys';
|
||||||
import {
|
import {
|
||||||
ArchiveChatDto,
|
ArchiveChatDto,
|
||||||
DeleteMessage,
|
DeleteMessage,
|
||||||
@@ -9,62 +9,77 @@ import {
|
|||||||
ProfileStatusDto,
|
ProfileStatusDto,
|
||||||
ReadMessageDto,
|
ReadMessageDto,
|
||||||
WhatsAppNumberDto,
|
WhatsAppNumberDto,
|
||||||
|
getBase64FromMediaMessageDto,
|
||||||
} from '../dto/chat.dto';
|
} from '../dto/chat.dto';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { ContactQuery } from '../repository/contact.repository';
|
import { ContactQuery } from '../repository/contact.repository';
|
||||||
import { MessageQuery } from '../repository/message.repository';
|
import { MessageQuery } from '../repository/message.repository';
|
||||||
import { MessageUpQuery } from '../repository/messageUp.repository';
|
import { MessageUpQuery } from '../repository/messageUp.repository';
|
||||||
import { WAMonitoringService } from '../services/monitor.service';
|
import { WAMonitoringService } from '../services/monitor.service';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('ChatController');
|
||||||
|
|
||||||
export class ChatController {
|
export class ChatController {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) {
|
public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) {
|
||||||
|
logger.verbose('requested whatsappNumber from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].whatsappNumber(data);
|
return await this.waMonitor.waInstances[instanceName].whatsappNumber(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async readMessage({ instanceName }: InstanceDto, data: ReadMessageDto) {
|
public async readMessage({ instanceName }: InstanceDto, data: ReadMessageDto) {
|
||||||
|
logger.verbose('requested readMessage from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].markMessageAsRead(data);
|
return await this.waMonitor.waInstances[instanceName].markMessageAsRead(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async archiveChat({ instanceName }: InstanceDto, data: ArchiveChatDto) {
|
public async archiveChat({ instanceName }: InstanceDto, data: ArchiveChatDto) {
|
||||||
|
logger.verbose('requested archiveChat from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].archiveChat(data);
|
return await this.waMonitor.waInstances[instanceName].archiveChat(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) {
|
public async deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) {
|
||||||
|
logger.verbose('requested deleteMessage from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].deleteMessage(data);
|
return await this.waMonitor.waInstances[instanceName].deleteMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchProfilePicture({ instanceName }: InstanceDto, data: NumberDto) {
|
public async fetchProfilePicture({ instanceName }: InstanceDto, data: NumberDto) {
|
||||||
|
logger.verbose('requested fetchProfilePicture from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].profilePicture(data.number);
|
return await this.waMonitor.waInstances[instanceName].profilePicture(data.number);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) {
|
public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) {
|
||||||
|
logger.verbose('requested fetchContacts from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].fetchContacts(query);
|
return await this.waMonitor.waInstances[instanceName].fetchContacts(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getBase64FromMediaMessage(
|
public async getBase64FromMediaMessage(
|
||||||
{ instanceName }: InstanceDto,
|
{ instanceName }: InstanceDto,
|
||||||
message: proto.IWebMessageInfo,
|
data: getBase64FromMediaMessageDto,
|
||||||
) {
|
) {
|
||||||
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(
|
logger.verbose(
|
||||||
message,
|
'requested getBase64FromMediaMessage from ' + instanceName + ' instance',
|
||||||
);
|
);
|
||||||
|
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) {
|
public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) {
|
||||||
|
logger.verbose('requested fetchMessages from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].fetchMessages(query);
|
return await this.waMonitor.waInstances[instanceName].fetchMessages(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) {
|
public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) {
|
||||||
|
logger.verbose('requested fetchStatusMessage from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query);
|
return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchChats({ instanceName }: InstanceDto) {
|
public async fetchChats({ instanceName }: InstanceDto) {
|
||||||
|
logger.verbose('requested fetchChats from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].fetchChats();
|
return await this.waMonitor.waInstances[instanceName].fetchChats();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchPrivacySettings({ instanceName }: InstanceDto) {
|
public async fetchPrivacySettings({ instanceName }: InstanceDto) {
|
||||||
|
logger.verbose('requested fetchPrivacySettings from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings();
|
return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +87,7 @@ export class ChatController {
|
|||||||
{ instanceName }: InstanceDto,
|
{ instanceName }: InstanceDto,
|
||||||
data: PrivacySettingDto,
|
data: PrivacySettingDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose('requested updatePrivacySettings from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].updatePrivacySettings(data);
|
return await this.waMonitor.waInstances[instanceName].updatePrivacySettings(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,12 +95,14 @@ export class ChatController {
|
|||||||
{ instanceName }: InstanceDto,
|
{ instanceName }: InstanceDto,
|
||||||
data: ProfilePictureDto,
|
data: ProfilePictureDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose('requested fetchBusinessProfile from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile(
|
return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile(
|
||||||
data.number,
|
data.number,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateProfileName({ instanceName }: InstanceDto, data: ProfileNameDto) {
|
public async updateProfileName({ instanceName }: InstanceDto, data: ProfileNameDto) {
|
||||||
|
logger.verbose('requested updateProfileName from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].updateProfileName(data.name);
|
return await this.waMonitor.waInstances[instanceName].updateProfileName(data.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +110,7 @@ export class ChatController {
|
|||||||
{ instanceName }: InstanceDto,
|
{ instanceName }: InstanceDto,
|
||||||
data: ProfileStatusDto,
|
data: ProfileStatusDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose('requested updateProfileStatus from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].updateProfileStatus(
|
return await this.waMonitor.waInstances[instanceName].updateProfileStatus(
|
||||||
data.status,
|
data.status,
|
||||||
);
|
);
|
||||||
@@ -101,6 +120,7 @@ export class ChatController {
|
|||||||
{ instanceName }: InstanceDto,
|
{ instanceName }: InstanceDto,
|
||||||
data: ProfilePictureDto,
|
data: ProfilePictureDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose('requested updateProfilePicture from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].updateProfilePicture(
|
return await this.waMonitor.waInstances[instanceName].updateProfilePicture(
|
||||||
data.picture,
|
data.picture,
|
||||||
);
|
);
|
||||||
@@ -110,6 +130,7 @@ export class ChatController {
|
|||||||
{ instanceName }: InstanceDto,
|
{ instanceName }: InstanceDto,
|
||||||
data: ProfilePictureDto,
|
data: ProfilePictureDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose('requested removeProfilePicture from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].removeProfilePicture();
|
return await this.waMonitor.waInstances[instanceName].removeProfilePicture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
85
src/whatsapp/controllers/chatwoot.controller.ts
Normal file
85
src/whatsapp/controllers/chatwoot.controller.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { isURL } from 'class-validator';
|
||||||
|
import { BadRequestException } from '../../exceptions';
|
||||||
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
|
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||||
|
import { ChatwootService } from '../services/chatwoot.service';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
import { waMonitor } from '../whatsapp.module';
|
||||||
|
import { ConfigService, HttpServer } from '../../config/env.config';
|
||||||
|
|
||||||
|
const logger = new Logger('ChatwootController');
|
||||||
|
|
||||||
|
export class ChatwootController {
|
||||||
|
constructor(
|
||||||
|
private readonly chatwootService: ChatwootService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async createChatwoot(instance: InstanceDto, data: ChatwootDto) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested createChatwoot from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.enabled) {
|
||||||
|
if (!isURL(data.url, { require_tld: false })) {
|
||||||
|
throw new BadRequestException('url is not valid');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.account_id) {
|
||||||
|
throw new BadRequestException('account_id is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.token) {
|
||||||
|
throw new BadRequestException('token is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.sign_msg) {
|
||||||
|
throw new BadRequestException('sign_msg is required');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.enabled) {
|
||||||
|
logger.verbose('chatwoot disabled');
|
||||||
|
data.account_id = '';
|
||||||
|
data.token = '';
|
||||||
|
data.url = '';
|
||||||
|
data.sign_msg = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.name_inbox = instance.instanceName;
|
||||||
|
|
||||||
|
const result = this.chatwootService.create(instance, data);
|
||||||
|
|
||||||
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
...result,
|
||||||
|
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findChatwoot(instance: InstanceDto) {
|
||||||
|
logger.verbose('requested findChatwoot from ' + instance.instanceName + ' instance');
|
||||||
|
const result = await this.chatwootService.find(instance);
|
||||||
|
|
||||||
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
...result,
|
||||||
|
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async receiveWebhook(instance: InstanceDto, data: any) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested receiveWebhook from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
|
const chatwootService = new ChatwootService(waMonitor);
|
||||||
|
|
||||||
|
return chatwootService.receiveWebhook(instance, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
CreateGroupDto,
|
CreateGroupDto,
|
||||||
|
GetParticipant,
|
||||||
GroupDescriptionDto,
|
GroupDescriptionDto,
|
||||||
GroupInvite,
|
GroupInvite,
|
||||||
GroupJid,
|
GroupJid,
|
||||||
GroupPictureDto,
|
GroupPictureDto,
|
||||||
|
GroupSendInvite,
|
||||||
GroupSubjectDto,
|
GroupSubjectDto,
|
||||||
GroupToggleEphemeralDto,
|
GroupToggleEphemeralDto,
|
||||||
GroupUpdateParticipantDto,
|
GroupUpdateParticipantDto,
|
||||||
@@ -11,21 +13,31 @@ import {
|
|||||||
} from '../dto/group.dto';
|
} from '../dto/group.dto';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { WAMonitoringService } from '../services/monitor.service';
|
import { WAMonitoringService } from '../services/monitor.service';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('ChatController');
|
||||||
|
|
||||||
export class GroupController {
|
export class GroupController {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
public async createGroup(instance: InstanceDto, create: CreateGroupDto) {
|
public async createGroup(instance: InstanceDto, create: CreateGroupDto) {
|
||||||
|
logger.verbose('requested createGroup from ' + instance.instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].createGroup(create);
|
return await this.waMonitor.waInstances[instance.instanceName].createGroup(create);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) {
|
public async updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested updateGroupPicture from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture(
|
return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture(
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateGroupSubject(instance: InstanceDto, update: GroupSubjectDto) {
|
public async updateGroupSubject(instance: InstanceDto, update: GroupSubjectDto) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested updateGroupSubject from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject(
|
return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject(
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
@@ -35,40 +47,56 @@ export class GroupController {
|
|||||||
instance: InstanceDto,
|
instance: InstanceDto,
|
||||||
update: GroupDescriptionDto,
|
update: GroupDescriptionDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested updateGroupDescription from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription(
|
return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription(
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) {
|
public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) {
|
||||||
|
logger.verbose('requested findGroupInfo from ' + instance.instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].findGroup(groupJid);
|
return await this.waMonitor.waInstances[instance.instanceName].findGroup(groupJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchAllGroups(instance: InstanceDto) {
|
public async fetchAllGroups(instance: InstanceDto, getPaticipants: GetParticipant) {
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups();
|
logger.verbose(
|
||||||
|
'requested fetchAllGroups from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
|
return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups(
|
||||||
|
getPaticipants,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async inviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
public async inviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
||||||
|
logger.verbose('requested inviteCode from ' + instance.instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].inviteCode(groupJid);
|
return await this.waMonitor.waInstances[instance.instanceName].inviteCode(groupJid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async inviteInfo(instance: InstanceDto, inviteCode: GroupInvite) {
|
public async inviteInfo(instance: InstanceDto, inviteCode: GroupInvite) {
|
||||||
|
logger.verbose('requested inviteInfo from ' + instance.instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].inviteInfo(inviteCode);
|
return await this.waMonitor.waInstances[instance.instanceName].inviteInfo(inviteCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async acceptInvite(instance: InstanceDto, inviteCode: GroupInvite) {
|
public async sendInvite(instance: InstanceDto, data: GroupSendInvite) {
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].acceptInvite(
|
logger.verbose('requested sendInvite from ' + instance.instanceName + ' instance');
|
||||||
inviteCode,
|
return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested revokeInviteCode from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(
|
return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(
|
||||||
groupJid,
|
groupJid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findParticipants(instance: InstanceDto, groupJid: GroupJid) {
|
public async findParticipants(instance: InstanceDto, groupJid: GroupJid) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested findParticipants from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].findParticipants(
|
return await this.waMonitor.waInstances[instance.instanceName].findParticipants(
|
||||||
groupJid,
|
groupJid,
|
||||||
);
|
);
|
||||||
@@ -78,22 +106,32 @@ export class GroupController {
|
|||||||
instance: InstanceDto,
|
instance: InstanceDto,
|
||||||
update: GroupUpdateParticipantDto,
|
update: GroupUpdateParticipantDto,
|
||||||
) {
|
) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested updateGParticipate from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant(
|
return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant(
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateGSetting(instance: InstanceDto, update: GroupUpdateSettingDto) {
|
public async updateGSetting(instance: InstanceDto, update: GroupUpdateSettingDto) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested updateGSetting from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update);
|
return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) {
|
public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) {
|
||||||
|
logger.verbose(
|
||||||
|
'requested toggleEphemeral from ' + instance.instanceName + ' instance',
|
||||||
|
);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(
|
return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(
|
||||||
update,
|
update,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) {
|
public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) {
|
||||||
|
logger.verbose('requested leaveGroup from ' + instance.instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid);
|
return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { delay } from '@evolution/base';
|
import { delay } from '@whiskeysockets/baileys';
|
||||||
import EventEmitter2 from 'eventemitter2';
|
import EventEmitter2 from 'eventemitter2';
|
||||||
import { Auth, ConfigService } from '../../config/env.config';
|
import { Auth, ConfigService, HttpServer } from '../../config/env.config';
|
||||||
import { BadRequestException, InternalServerErrorException } from '../../exceptions';
|
import { BadRequestException, InternalServerErrorException } from '../../exceptions';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { RepositoryBroker } from '../repository/repository.manager';
|
import { RepositoryBroker } from '../repository/repository.manager';
|
||||||
@@ -8,8 +8,10 @@ import { AuthService, OldToken } from '../services/auth.service';
|
|||||||
import { WAMonitoringService } from '../services/monitor.service';
|
import { WAMonitoringService } from '../services/monitor.service';
|
||||||
import { WAStartupService } from '../services/whatsapp.service';
|
import { WAStartupService } from '../services/whatsapp.service';
|
||||||
import { WebhookService } from '../services/webhook.service';
|
import { WebhookService } from '../services/webhook.service';
|
||||||
|
import { ChatwootService } from '../services/chatwoot.service';
|
||||||
import { Logger } from '../../config/logger.config';
|
import { Logger } from '../../config/logger.config';
|
||||||
import { wa } from '../types/wa.types';
|
import { wa } from '../types/wa.types';
|
||||||
|
import { RedisCache } from '../../db/redis.client';
|
||||||
|
|
||||||
export class InstanceController {
|
export class InstanceController {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -19,6 +21,8 @@ export class InstanceController {
|
|||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly webhookService: WebhookService,
|
private readonly webhookService: WebhookService,
|
||||||
|
private readonly chatwootService: ChatwootService,
|
||||||
|
private readonly cache: RedisCache,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private readonly logger = new Logger(InstanceController.name);
|
private readonly logger = new Logger(InstanceController.name);
|
||||||
@@ -26,13 +30,22 @@ export class InstanceController {
|
|||||||
public async createInstance({
|
public async createInstance({
|
||||||
instanceName,
|
instanceName,
|
||||||
webhook,
|
webhook,
|
||||||
|
webhook_by_events,
|
||||||
events,
|
events,
|
||||||
qrcode,
|
qrcode,
|
||||||
token,
|
token,
|
||||||
|
chatwoot_account_id,
|
||||||
|
chatwoot_token,
|
||||||
|
chatwoot_url,
|
||||||
|
chatwoot_sign_msg,
|
||||||
}: InstanceDto) {
|
}: InstanceDto) {
|
||||||
|
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
|
||||||
|
|
||||||
const mode = this.configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE;
|
const mode = this.configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE;
|
||||||
|
|
||||||
if (mode === 'container') {
|
if (mode === 'container') {
|
||||||
|
this.logger.verbose('container mode');
|
||||||
|
|
||||||
if (Object.keys(this.waMonitor.waInstances).length > 0) {
|
if (Object.keys(this.waMonitor.waInstances).length > 0) {
|
||||||
throw new BadRequestException([
|
throw new BadRequestException([
|
||||||
'Instance already created',
|
'Instance already created',
|
||||||
@@ -40,15 +53,23 @@ export class InstanceController {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('checking duplicate token');
|
||||||
|
await this.authService.checkDuplicateToken(token);
|
||||||
|
|
||||||
|
this.logger.verbose('creating instance');
|
||||||
const instance = new WAStartupService(
|
const instance = new WAStartupService(
|
||||||
this.configService,
|
this.configService,
|
||||||
this.eventEmitter,
|
this.eventEmitter,
|
||||||
this.repository,
|
this.repository,
|
||||||
|
this.cache,
|
||||||
);
|
);
|
||||||
instance.instanceName = instanceName;
|
instance.instanceName = instanceName;
|
||||||
|
this.logger.verbose('instance: ' + instance.instanceName + ' created');
|
||||||
|
|
||||||
this.waMonitor.waInstances[instance.instanceName] = instance;
|
this.waMonitor.waInstances[instance.instanceName] = instance;
|
||||||
this.waMonitor.delInstanceTime(instance.instanceName);
|
this.waMonitor.delInstanceTime(instance.instanceName);
|
||||||
|
|
||||||
|
this.logger.verbose('generating hash');
|
||||||
const hash = await this.authService.generateHash(
|
const hash = await this.authService.generateHash(
|
||||||
{
|
{
|
||||||
instanceName: instance.instanceName,
|
instanceName: instance.instanceName,
|
||||||
@@ -56,11 +77,19 @@ export class InstanceController {
|
|||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('hash: ' + hash + ' generated');
|
||||||
|
|
||||||
let getEvents: string[];
|
let getEvents: string[];
|
||||||
|
|
||||||
if (webhook) {
|
if (webhook) {
|
||||||
|
this.logger.verbose('creating webhook');
|
||||||
try {
|
try {
|
||||||
this.webhookService.create(instance, { enabled: true, url: webhook, events });
|
this.webhookService.create(instance, {
|
||||||
|
enabled: true,
|
||||||
|
url: webhook,
|
||||||
|
events,
|
||||||
|
webhook_by_events,
|
||||||
|
});
|
||||||
|
|
||||||
getEvents = (await this.webhookService.find(instance)).events;
|
getEvents = (await this.webhookService.find(instance)).events;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -68,25 +97,100 @@ export class InstanceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
|
||||||
|
this.logger.verbose('instance created');
|
||||||
|
this.logger.verbose({
|
||||||
|
instance: {
|
||||||
|
instanceName: instance.instanceName,
|
||||||
|
status: 'created',
|
||||||
|
},
|
||||||
|
hash,
|
||||||
|
webhook,
|
||||||
|
events: getEvents,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
instance: {
|
||||||
|
instanceName: instance.instanceName,
|
||||||
|
status: 'created',
|
||||||
|
},
|
||||||
|
hash,
|
||||||
|
webhook,
|
||||||
|
events: getEvents,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_account_id) {
|
||||||
|
throw new BadRequestException('account_id is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_token) {
|
||||||
|
throw new BadRequestException('token is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_url) {
|
||||||
|
throw new BadRequestException('url is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.chatwootService.create(instance, {
|
||||||
|
enabled: true,
|
||||||
|
account_id: chatwoot_account_id,
|
||||||
|
token: chatwoot_token,
|
||||||
|
url: chatwoot_url,
|
||||||
|
sign_msg: chatwoot_sign_msg || false,
|
||||||
|
name_inbox: instance.instanceName,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.chatwootService.initInstanceChatwoot(
|
||||||
|
instance,
|
||||||
|
instance.instanceName,
|
||||||
|
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||||
|
qrcode,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
instance: {
|
instance: {
|
||||||
instanceName: instance.instanceName,
|
instanceName: instance.instanceName,
|
||||||
status: 'created',
|
status: 'created',
|
||||||
},
|
},
|
||||||
hash,
|
hash,
|
||||||
webhook,
|
chatwoot: {
|
||||||
events: getEvents,
|
enabled: true,
|
||||||
|
account_id: chatwoot_account_id,
|
||||||
|
token: chatwoot_token,
|
||||||
|
url: chatwoot_url,
|
||||||
|
sign_msg: chatwoot_sign_msg || false,
|
||||||
|
name_inbox: instance.instanceName,
|
||||||
|
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('server mode');
|
||||||
|
|
||||||
|
this.logger.verbose('checking duplicate token');
|
||||||
|
await this.authService.checkDuplicateToken(token);
|
||||||
|
|
||||||
|
this.logger.verbose('creating instance');
|
||||||
const instance = new WAStartupService(
|
const instance = new WAStartupService(
|
||||||
this.configService,
|
this.configService,
|
||||||
this.eventEmitter,
|
this.eventEmitter,
|
||||||
this.repository,
|
this.repository,
|
||||||
|
this.cache,
|
||||||
);
|
);
|
||||||
instance.instanceName = instanceName;
|
instance.instanceName = instanceName;
|
||||||
|
|
||||||
|
this.logger.verbose('instance: ' + instance.instanceName + ' created');
|
||||||
|
|
||||||
this.waMonitor.waInstances[instance.instanceName] = instance;
|
this.waMonitor.waInstances[instance.instanceName] = instance;
|
||||||
this.waMonitor.delInstanceTime(instance.instanceName);
|
this.waMonitor.delInstanceTime(instance.instanceName);
|
||||||
|
|
||||||
|
this.logger.verbose('generating hash');
|
||||||
const hash = await this.authService.generateHash(
|
const hash = await this.authService.generateHash(
|
||||||
{
|
{
|
||||||
instanceName: instance.instanceName,
|
instanceName: instance.instanceName,
|
||||||
@@ -94,11 +198,19 @@ export class InstanceController {
|
|||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('hash: ' + hash + ' generated');
|
||||||
|
|
||||||
let getEvents: string[];
|
let getEvents: string[];
|
||||||
|
|
||||||
if (webhook) {
|
if (webhook) {
|
||||||
|
this.logger.verbose('creating webhook');
|
||||||
try {
|
try {
|
||||||
this.webhookService.create(instance, { enabled: true, url: webhook, events });
|
this.webhookService.create(instance, {
|
||||||
|
enabled: true,
|
||||||
|
url: webhook,
|
||||||
|
events,
|
||||||
|
webhook_by_events,
|
||||||
|
});
|
||||||
|
|
||||||
getEvents = (await this.webhookService.find(instance)).events;
|
getEvents = (await this.webhookService.find(instance)).events;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -106,12 +218,74 @@ export class InstanceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let getQrcode: wa.QrCode;
|
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
|
||||||
|
let getQrcode: wa.QrCode;
|
||||||
|
|
||||||
if (qrcode) {
|
if (qrcode) {
|
||||||
await instance.connectToWhatsapp();
|
this.logger.verbose('creating qrcode');
|
||||||
await delay(2000);
|
await instance.connectToWhatsapp();
|
||||||
getQrcode = instance.qrCode;
|
await delay(2000);
|
||||||
|
getQrcode = instance.qrCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('instance created');
|
||||||
|
this.logger.verbose({
|
||||||
|
instance: {
|
||||||
|
instanceName: instance.instanceName,
|
||||||
|
status: 'created',
|
||||||
|
},
|
||||||
|
hash,
|
||||||
|
webhook,
|
||||||
|
webhook_by_events,
|
||||||
|
events: getEvents,
|
||||||
|
qrcode: getQrcode,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
instance: {
|
||||||
|
instanceName: instance.instanceName,
|
||||||
|
status: 'created',
|
||||||
|
},
|
||||||
|
hash,
|
||||||
|
webhook,
|
||||||
|
webhook_by_events,
|
||||||
|
events: getEvents,
|
||||||
|
qrcode: getQrcode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_account_id) {
|
||||||
|
throw new BadRequestException('account_id is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_token) {
|
||||||
|
throw new BadRequestException('token is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chatwoot_url) {
|
||||||
|
throw new BadRequestException('url is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.chatwootService.create(instance, {
|
||||||
|
enabled: true,
|
||||||
|
account_id: chatwoot_account_id,
|
||||||
|
token: chatwoot_token,
|
||||||
|
url: chatwoot_url,
|
||||||
|
sign_msg: chatwoot_sign_msg || false,
|
||||||
|
name_inbox: instance.instanceName,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.chatwootService.initInstanceChatwoot(
|
||||||
|
instance,
|
||||||
|
instance.instanceName,
|
||||||
|
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||||
|
qrcode,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -121,19 +295,35 @@ export class InstanceController {
|
|||||||
},
|
},
|
||||||
hash,
|
hash,
|
||||||
webhook,
|
webhook,
|
||||||
|
webhook_by_events,
|
||||||
events: getEvents,
|
events: getEvents,
|
||||||
qrcode: getQrcode,
|
chatwoot: {
|
||||||
|
enabled: true,
|
||||||
|
account_id: chatwoot_account_id,
|
||||||
|
token: chatwoot_token,
|
||||||
|
url: chatwoot_url,
|
||||||
|
sign_msg: chatwoot_sign_msg || false,
|
||||||
|
name_inbox: instance.instanceName,
|
||||||
|
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async connectToWhatsapp({ instanceName }: InstanceDto) {
|
public async connectToWhatsapp({ instanceName }: InstanceDto) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose(
|
||||||
|
'requested connectToWhatsapp from ' + instanceName + ' instance',
|
||||||
|
);
|
||||||
|
|
||||||
const instance = this.waMonitor.waInstances[instanceName];
|
const instance = this.waMonitor.waInstances[instanceName];
|
||||||
const state = instance?.connectionStatus?.state;
|
const state = instance?.connectionStatus?.state;
|
||||||
|
|
||||||
|
this.logger.verbose('state: ' + state);
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'close':
|
case 'close':
|
||||||
|
this.logger.verbose('connecting');
|
||||||
await instance.connectToWhatsapp();
|
await instance.connectToWhatsapp();
|
||||||
await delay(2000);
|
await delay(2000);
|
||||||
return instance.qrCode;
|
return instance.qrCode;
|
||||||
@@ -147,12 +337,44 @@ export class InstanceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async restartInstance({ instanceName }: InstanceDto) {
|
||||||
|
try {
|
||||||
|
this.logger.verbose('requested restartInstance from ' + instanceName + ' instance');
|
||||||
|
|
||||||
|
this.logger.verbose('deleting instance: ' + instanceName);
|
||||||
|
delete this.waMonitor.waInstances[instanceName];
|
||||||
|
|
||||||
|
this.logger.verbose('creating instance: ' + instanceName);
|
||||||
|
const instance = new WAStartupService(
|
||||||
|
this.configService,
|
||||||
|
this.eventEmitter,
|
||||||
|
this.repository,
|
||||||
|
this.cache,
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.instanceName = instanceName;
|
||||||
|
|
||||||
|
this.logger.verbose('instance: ' + instance.instanceName + ' created');
|
||||||
|
|
||||||
|
this.logger.verbose('connecting instance: ' + instanceName);
|
||||||
|
await instance.connectToWhatsapp();
|
||||||
|
this.waMonitor.waInstances[instance.instanceName] = instance;
|
||||||
|
|
||||||
|
return { error: false, message: 'Instance restarted' };
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async connectionState({ instanceName }: InstanceDto) {
|
public async connectionState({ instanceName }: InstanceDto) {
|
||||||
|
this.logger.verbose('requested connectionState from ' + instanceName + ' instance');
|
||||||
return this.waMonitor.waInstances[instanceName]?.connectionStatus;
|
return this.waMonitor.waInstances[instanceName]?.connectionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchInstances({ instanceName }: InstanceDto) {
|
public async fetchInstances({ instanceName }: InstanceDto) {
|
||||||
|
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
|
||||||
if (instanceName) {
|
if (instanceName) {
|
||||||
|
this.logger.verbose('instanceName: ' + instanceName);
|
||||||
return this.waMonitor.instanceInfo(instanceName);
|
return this.waMonitor.instanceInfo(instanceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,13 +382,23 @@ export class InstanceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async logout({ instanceName }: InstanceDto) {
|
public async logout({ instanceName }: InstanceDto) {
|
||||||
|
this.logger.verbose('requested logout from ' + instanceName + ' instance');
|
||||||
|
const stateConn = await this.connectionState({ instanceName });
|
||||||
|
|
||||||
|
if (stateConn.state === 'close') {
|
||||||
|
throw new BadRequestException(
|
||||||
|
'The "' + instanceName + '" instance is not connected',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('logging out instance: ' + instanceName);
|
||||||
await this.waMonitor.waInstances[instanceName]?.client?.logout(
|
await this.waMonitor.waInstances[instanceName]?.client?.logout(
|
||||||
'Log out instance: ' + instanceName,
|
'Log out instance: ' + instanceName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('close connection instance: ' + instanceName);
|
||||||
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
|
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
|
||||||
this.waMonitor.waInstances[instanceName]?.client?.end(undefined);
|
|
||||||
|
|
||||||
return { error: false, message: 'Instance logged out' };
|
return { error: false, message: 'Instance logged out' };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -175,22 +407,35 @@ export class InstanceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async deleteInstance({ instanceName }: InstanceDto) {
|
public async deleteInstance({ instanceName }: InstanceDto) {
|
||||||
|
this.logger.verbose('requested deleteInstance from ' + instanceName + ' instance');
|
||||||
const stateConn = await this.connectionState({ instanceName });
|
const stateConn = await this.connectionState({ instanceName });
|
||||||
|
|
||||||
if (stateConn.state === 'open') {
|
if (stateConn.state === 'open') {
|
||||||
throw new BadRequestException([
|
throw new BadRequestException(
|
||||||
'Deletion failed',
|
'The "' + instanceName + '" instance needs to be disconnected',
|
||||||
'The instance needs to be disconnected',
|
);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
delete this.waMonitor.waInstances[instanceName];
|
if (stateConn.state === 'connecting') {
|
||||||
return { error: false, message: 'Instance deleted' };
|
this.logger.verbose('logging out instance: ' + instanceName);
|
||||||
|
|
||||||
|
await this.logout({ instanceName });
|
||||||
|
delete this.waMonitor.waInstances[instanceName];
|
||||||
|
return { error: false, message: 'Instance deleted' };
|
||||||
|
} else {
|
||||||
|
this.logger.verbose('deleting instance: ' + instanceName);
|
||||||
|
|
||||||
|
delete this.waMonitor.waInstances[instanceName];
|
||||||
|
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
|
||||||
|
return { error: false, message: 'Instance deleted' };
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new BadRequestException(error.toString());
|
throw new BadRequestException(error.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refreshToken(_: InstanceDto, oldToken: OldToken) {
|
public async refreshToken(_: InstanceDto, oldToken: OldToken) {
|
||||||
|
this.logger.verbose('requested refreshToken');
|
||||||
return await this.authService.refreshToken(oldToken);
|
return await this.authService.refreshToken(oldToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,22 +11,35 @@ import {
|
|||||||
SendMediaDto,
|
SendMediaDto,
|
||||||
SendPollDto,
|
SendPollDto,
|
||||||
SendReactionDto,
|
SendReactionDto,
|
||||||
|
SendStatusDto,
|
||||||
SendStickerDto,
|
SendStickerDto,
|
||||||
SendTextDto,
|
SendTextDto,
|
||||||
} from '../dto/sendMessage.dto';
|
} from '../dto/sendMessage.dto';
|
||||||
import { WAMonitoringService } from '../services/monitor.service';
|
import { WAMonitoringService } from '../services/monitor.service';
|
||||||
|
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('MessageRouter');
|
||||||
|
|
||||||
export class SendMessageController {
|
export class SendMessageController {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
|
public async sendText({ instanceName }: InstanceDto, data: SendTextDto) {
|
||||||
|
logger.verbose('requested sendText from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].textMessage(data);
|
return await this.waMonitor.waInstances[instanceName].textMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
|
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
|
||||||
|
logger.verbose('requested sendMedia from ' + instanceName + ' instance');
|
||||||
if (isBase64(data?.mediaMessage?.media) && !data?.mediaMessage?.fileName) {
|
if (isBase64(data?.mediaMessage?.media) && !data?.mediaMessage?.fileName) {
|
||||||
throw new BadRequestException('For bse64 the file name must be informed.');
|
throw new BadRequestException('For bse64 the file name must be informed.');
|
||||||
}
|
}
|
||||||
|
logger.verbose(
|
||||||
|
'isURL: ' +
|
||||||
|
isURL(data?.mediaMessage?.media) +
|
||||||
|
', isBase64: ' +
|
||||||
|
isBase64(data?.mediaMessage?.media),
|
||||||
|
);
|
||||||
if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) {
|
if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].mediaMessage(data);
|
return await this.waMonitor.waInstances[instanceName].mediaMessage(data);
|
||||||
}
|
}
|
||||||
@@ -34,6 +47,14 @@ export class SendMessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto) {
|
public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto) {
|
||||||
|
logger.verbose('requested sendSticker from ' + instanceName + ' instance');
|
||||||
|
|
||||||
|
logger.verbose(
|
||||||
|
'isURL: ' +
|
||||||
|
isURL(data?.stickerMessage?.image) +
|
||||||
|
', isBase64: ' +
|
||||||
|
isBase64(data?.stickerMessage?.image),
|
||||||
|
);
|
||||||
if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) {
|
if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].mediaSticker(data);
|
return await this.waMonitor.waInstances[instanceName].mediaSticker(data);
|
||||||
}
|
}
|
||||||
@@ -41,6 +62,14 @@ export class SendMessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) {
|
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) {
|
||||||
|
logger.verbose('requested sendWhatsAppAudio from ' + instanceName + ' instance');
|
||||||
|
|
||||||
|
logger.verbose(
|
||||||
|
'isURL: ' +
|
||||||
|
isURL(data?.audioMessage?.audio) +
|
||||||
|
', isBase64: ' +
|
||||||
|
isBase64(data?.audioMessage?.audio),
|
||||||
|
);
|
||||||
if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) {
|
if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) {
|
||||||
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data);
|
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data);
|
||||||
}
|
}
|
||||||
@@ -48,6 +77,7 @@ export class SendMessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) {
|
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) {
|
||||||
|
logger.verbose('requested sendButtons from ' + instanceName + ' instance');
|
||||||
if (
|
if (
|
||||||
isBase64(data.buttonMessage.mediaMessage?.media) &&
|
isBase64(data.buttonMessage.mediaMessage?.media) &&
|
||||||
!data.buttonMessage.mediaMessage?.fileName
|
!data.buttonMessage.mediaMessage?.fileName
|
||||||
@@ -58,18 +88,22 @@ export class SendMessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
|
public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) {
|
||||||
|
logger.verbose('requested sendLocation from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].locationMessage(data);
|
return await this.waMonitor.waInstances[instanceName].locationMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
|
public async sendList({ instanceName }: InstanceDto, data: SendListDto) {
|
||||||
|
logger.verbose('requested sendList from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].listMessage(data);
|
return await this.waMonitor.waInstances[instanceName].listMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
|
public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) {
|
||||||
|
logger.verbose('requested sendContact from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].contactMessage(data);
|
return await this.waMonitor.waInstances[instanceName].contactMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
||||||
|
logger.verbose('requested sendReaction from ' + instanceName + ' instance');
|
||||||
if (!data.reactionMessage.reaction.match(/[^\(\)\w\sà-ú"-\+]+/)) {
|
if (!data.reactionMessage.reaction.match(/[^\(\)\w\sà-ú"-\+]+/)) {
|
||||||
throw new BadRequestException('"reaction" must be an emoji');
|
throw new BadRequestException('"reaction" must be an emoji');
|
||||||
}
|
}
|
||||||
@@ -77,10 +111,17 @@ export class SendMessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
|
public async sendPoll({ instanceName }: InstanceDto, data: SendPollDto) {
|
||||||
|
logger.verbose('requested sendPoll from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].pollMessage(data);
|
return await this.waMonitor.waInstances[instanceName].pollMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async sendStatus({ instanceName }: InstanceDto, data: SendStatusDto) {
|
||||||
|
logger.verbose('requested sendStatus from ' + instanceName + ' instance');
|
||||||
|
return await this.waMonitor.waInstances[instanceName].statusMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
public async sendLinkPreview({ instanceName }: InstanceDto, data: SendLinkPreviewDto) {
|
public async sendLinkPreview({ instanceName }: InstanceDto, data: SendLinkPreviewDto) {
|
||||||
|
logger.verbose('requested sendLinkPreview from ' + instanceName + ' instance');
|
||||||
return await this.waMonitor.waInstances[instanceName].linkPreview(data);
|
return await this.waMonitor.waInstances[instanceName].linkPreview(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,31 @@ import { BadRequestException } from '../../exceptions';
|
|||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { WebhookDto } from '../dto/webhook.dto';
|
import { WebhookDto } from '../dto/webhook.dto';
|
||||||
import { WebhookService } from '../services/webhook.service';
|
import { WebhookService } from '../services/webhook.service';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('WebhookController');
|
||||||
|
|
||||||
export class WebhookController {
|
export class WebhookController {
|
||||||
constructor(private readonly webhookService: WebhookService) {}
|
constructor(private readonly webhookService: WebhookService) {}
|
||||||
|
|
||||||
public async createWebhook(instance: InstanceDto, data: WebhookDto) {
|
public async createWebhook(instance: InstanceDto, data: WebhookDto) {
|
||||||
if (!isURL(data.url, { require_tld: false })) {
|
logger.verbose('requested createWebhook from ' + instance.instanceName + ' instance');
|
||||||
|
|
||||||
|
if (data.enabled && !isURL(data.url, { require_tld: false })) {
|
||||||
throw new BadRequestException('Invalid "url" property');
|
throw new BadRequestException('Invalid "url" property');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.enabled) {
|
||||||
|
logger.verbose('webhook disabled');
|
||||||
|
data.url = '';
|
||||||
|
data.events = [];
|
||||||
|
}
|
||||||
|
|
||||||
return this.webhookService.create(instance, data);
|
return this.webhookService.create(instance, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findWebhook(instance: InstanceDto) {
|
public async findWebhook(instance: InstanceDto) {
|
||||||
|
logger.verbose('requested findWebhook from ' + instance.instanceName + ' instance');
|
||||||
return this.webhookService.find(instance);
|
return this.webhookService.find(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import {
|
|||||||
WAPrivacyOnlineValue,
|
WAPrivacyOnlineValue,
|
||||||
WAPrivacyValue,
|
WAPrivacyValue,
|
||||||
WAReadReceiptsValue,
|
WAReadReceiptsValue,
|
||||||
} from '@evolution/base';
|
proto,
|
||||||
|
} from '@whiskeysockets/baileys';
|
||||||
|
|
||||||
export class OnWhatsAppDto {
|
export class OnWhatsAppDto {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -12,6 +13,11 @@ export class OnWhatsAppDto {
|
|||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class getBase64FromMediaMessageDto {
|
||||||
|
message: proto.WebMessageInfo;
|
||||||
|
convertToMp4?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class WhatsAppNumberDto {
|
export class WhatsAppNumberDto {
|
||||||
numbers: string[];
|
numbers: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/whatsapp/dto/chatwoot.dto.ts
Normal file
8
src/whatsapp/dto/chatwoot.dto.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export class ChatwootDto {
|
||||||
|
enabled?: boolean;
|
||||||
|
account_id?: string;
|
||||||
|
token?: string;
|
||||||
|
url?: string;
|
||||||
|
name_inbox?: string;
|
||||||
|
sign_msg?: boolean;
|
||||||
|
}
|
||||||
@@ -23,10 +23,20 @@ export class GroupJid {
|
|||||||
groupJid: string;
|
groupJid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GetParticipant {
|
||||||
|
getParticipants: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class GroupInvite {
|
export class GroupInvite {
|
||||||
inviteCode: string;
|
inviteCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GroupSendInvite {
|
||||||
|
groupJid: string;
|
||||||
|
description: string;
|
||||||
|
numbers: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export class GroupUpdateParticipantDto extends GroupJid {
|
export class GroupUpdateParticipantDto extends GroupJid {
|
||||||
action: 'add' | 'remove' | 'promote' | 'demote';
|
action: 'add' | 'remove' | 'promote' | 'demote';
|
||||||
participants: string[];
|
participants: string[];
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
export class InstanceDto {
|
export class InstanceDto {
|
||||||
instanceName: string;
|
instanceName: string;
|
||||||
webhook?: string;
|
webhook?: string;
|
||||||
|
webhook_by_events?: boolean;
|
||||||
events?: string[];
|
events?: string[];
|
||||||
qrcode?: boolean;
|
qrcode?: boolean;
|
||||||
token?: string;
|
token?: string;
|
||||||
|
chatwoot_account_id?: string;
|
||||||
|
chatwoot_token?: string;
|
||||||
|
chatwoot_url?: string;
|
||||||
|
chatwoot_sign_msg?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { proto, WAPresence } from '@evolution/base';
|
import { proto, WAPresence } from '@whiskeysockets/baileys';
|
||||||
|
|
||||||
export class Quoted {
|
export class Quoted {
|
||||||
key: proto.IMessageKey;
|
key: proto.IMessageKey;
|
||||||
@@ -32,6 +32,16 @@ class linkPreviewMessage {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class StatusMessage {
|
||||||
|
type: string;
|
||||||
|
content: string;
|
||||||
|
statusJidList?: string[];
|
||||||
|
allContacts?: boolean;
|
||||||
|
caption?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
font?: number;
|
||||||
|
}
|
||||||
|
|
||||||
class PollMessage {
|
class PollMessage {
|
||||||
name: string;
|
name: string;
|
||||||
selectableCount: number;
|
selectableCount: number;
|
||||||
@@ -46,6 +56,10 @@ export class SendLinkPreviewDto extends Metadata {
|
|||||||
linkPreview: linkPreviewMessage;
|
linkPreview: linkPreviewMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SendStatusDto extends Metadata {
|
||||||
|
statusMessage: StatusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
export class SendPollDto extends Metadata {
|
export class SendPollDto extends Metadata {
|
||||||
pollMessage: PollMessage;
|
pollMessage: PollMessage;
|
||||||
}
|
}
|
||||||
@@ -125,6 +139,9 @@ export class ContactMessage {
|
|||||||
fullName: string;
|
fullName: string;
|
||||||
wuid: string;
|
wuid: string;
|
||||||
phoneNumber: string;
|
phoneNumber: string;
|
||||||
|
organization?: string;
|
||||||
|
email?: string;
|
||||||
|
url?: string;
|
||||||
}
|
}
|
||||||
export class SendContactDto extends Metadata {
|
export class SendContactDto extends Metadata {
|
||||||
contactMessage: ContactMessage[];
|
contactMessage: ContactMessage[];
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export class WebhookDto {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
url?: string;
|
url?: string;
|
||||||
events?: string[];
|
events?: string[];
|
||||||
|
webhook_by_events?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from '../../exceptions';
|
} from '../../exceptions';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { waMonitor } from '../whatsapp.module';
|
import { cache, waMonitor } from '../whatsapp.module';
|
||||||
import { Database, Redis, configService } from '../../config/env.config';
|
import { Database, Redis, configService } from '../../config/env.config';
|
||||||
import { RedisCache } from '../../db/redis.client';
|
import { RedisCache } from '../../db/redis.client';
|
||||||
|
|
||||||
@@ -20,7 +20,6 @@ async function getInstance(instanceName: string) {
|
|||||||
const exists = !!waMonitor.waInstances[instanceName];
|
const exists = !!waMonitor.waInstances[instanceName];
|
||||||
|
|
||||||
if (redisConf.ENABLED) {
|
if (redisConf.ENABLED) {
|
||||||
const cache = new RedisCache(redisConf, instanceName);
|
|
||||||
const keyExists = await cache.keyExists();
|
const keyExists = await cache.keyExists();
|
||||||
return exists || keyExists;
|
return exists || keyExists;
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/whatsapp/models/chatwoot.model.ts
Normal file
29
src/whatsapp/models/chatwoot.model.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Schema } from 'mongoose';
|
||||||
|
import { dbserver } from '../../db/db.connect';
|
||||||
|
|
||||||
|
export class ChatwootRaw {
|
||||||
|
_id?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
account_id?: string;
|
||||||
|
token?: string;
|
||||||
|
url?: string;
|
||||||
|
name_inbox?: string;
|
||||||
|
sign_msg?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatwootSchema = new Schema<ChatwootRaw>({
|
||||||
|
_id: { type: String, _id: true },
|
||||||
|
enabled: { type: Boolean, required: true },
|
||||||
|
account_id: { type: String, required: true },
|
||||||
|
token: { type: String, required: true },
|
||||||
|
url: { type: String, required: true },
|
||||||
|
name_inbox: { type: String, required: true },
|
||||||
|
sign_msg: { type: Boolean, required: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatwootModel = dbserver?.model(
|
||||||
|
ChatwootRaw.name,
|
||||||
|
chatwootSchema,
|
||||||
|
'chatwoot',
|
||||||
|
);
|
||||||
|
export type IChatwootModel = typeof ChatwootModel;
|
||||||
@@ -3,3 +3,4 @@ export * from './contact.model';
|
|||||||
export * from './message.model';
|
export * from './message.model';
|
||||||
export * from './auth.model';
|
export * from './auth.model';
|
||||||
export * from './webhook.model';
|
export * from './webhook.model';
|
||||||
|
export * from './chatwoot.model';
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export class MessageUpdateRaw {
|
|||||||
datetime?: number;
|
datetime?: number;
|
||||||
status?: wa.StatusMessage;
|
status?: wa.StatusMessage;
|
||||||
owner: string;
|
owner: string;
|
||||||
|
pollUpdates?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageUpdateSchema = new Schema<MessageUpdateRaw>({
|
const messageUpdateSchema = new Schema<MessageUpdateRaw>({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export class WebhookRaw {
|
|||||||
url?: string;
|
url?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
events?: string[];
|
events?: string[];
|
||||||
|
webhook_by_events?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const webhookSchema = new Schema<WebhookRaw>({
|
const webhookSchema = new Schema<WebhookRaw>({
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { IInsert, Repository } from '../abstract/abstract.repository';
|
|||||||
import { IAuthModel, AuthRaw } from '../models';
|
import { IAuthModel, AuthRaw } from '../models';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { AUTH_DIR } from '../../config/path.config';
|
import { AUTH_DIR } from '../../config/path.config';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class AuthRepository extends Repository {
|
export class AuthRepository extends Repository {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -15,24 +16,35 @@ export class AuthRepository extends Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly auth: Auth;
|
private readonly auth: Auth;
|
||||||
|
private readonly logger = new Logger('AuthRepository');
|
||||||
|
|
||||||
public async create(data: AuthRaw, instance: string): Promise<IInsert> {
|
public async create(data: AuthRaw, instance: string): Promise<IInsert> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('creating auth');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('saving auth to db');
|
||||||
const insert = await this.authModel.replaceOne(
|
const insert = await this.authModel.replaceOne(
|
||||||
{ _id: instance },
|
{ _id: instance },
|
||||||
{ ...data },
|
{ ...data },
|
||||||
{ upsert: true },
|
{ upsert: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth');
|
||||||
return { insertCount: insert.modifiedCount };
|
return { insertCount: insert.modifiedCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving auth to store');
|
||||||
|
|
||||||
this.writeStore<AuthRaw>({
|
this.writeStore<AuthRaw>({
|
||||||
path: join(AUTH_DIR, this.auth.TYPE),
|
path: join(AUTH_DIR, this.auth.TYPE),
|
||||||
fileName: instance,
|
fileName: instance,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
this.logger.verbose(
|
||||||
|
'auth saved to store in path: ' + join(AUTH_DIR, this.auth.TYPE) + '/' + instance,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('auth created');
|
||||||
return { insertCount: 1 };
|
return { insertCount: 1 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { error } as any;
|
return { error } as any;
|
||||||
@@ -41,10 +53,14 @@ export class AuthRepository extends Repository {
|
|||||||
|
|
||||||
public async find(instance: string): Promise<AuthRaw> {
|
public async find(instance: string): Promise<AuthRaw> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('finding auth');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding auth in db');
|
||||||
return await this.authModel.findOne({ _id: instance });
|
return await this.authModel.findOne({ _id: instance });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding auth in store');
|
||||||
|
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), {
|
readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ConfigService, StoreConf } from '../../config/env.config';
|
|||||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||||
import { opendirSync, readFileSync, rmSync } from 'fs';
|
import { opendirSync, readFileSync, rmSync } from 'fs';
|
||||||
import { ChatRaw, IChatModel } from '../models';
|
import { ChatRaw, IChatModel } from '../models';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class ChatQuery {
|
export class ChatQuery {
|
||||||
where: ChatRaw;
|
where: ChatRaw;
|
||||||
@@ -16,31 +17,54 @@ export class ChatRepository extends Repository {
|
|||||||
super(configService);
|
super(configService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async insert(data: ChatRaw[], saveDb = false): Promise<IInsert> {
|
private readonly logger = new Logger('ChatRepository');
|
||||||
|
|
||||||
|
public async insert(
|
||||||
|
data: ChatRaw[],
|
||||||
|
instanceName: string,
|
||||||
|
saveDb = false,
|
||||||
|
): Promise<IInsert> {
|
||||||
|
this.logger.verbose('inserting chats');
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
|
this.logger.verbose('no chats to insert');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('saving chats to store');
|
||||||
if (this.dbSettings.ENABLED && saveDb) {
|
if (this.dbSettings.ENABLED && saveDb) {
|
||||||
|
this.logger.verbose('saving chats to db');
|
||||||
const insert = await this.chatModel.insertMany([...data]);
|
const insert = await this.chatModel.insertMany([...data]);
|
||||||
|
|
||||||
|
this.logger.verbose('chats saved to db: ' + insert.length + ' chats');
|
||||||
return { insertCount: insert.length };
|
return { insertCount: insert.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving chats to store');
|
||||||
|
|
||||||
const store = this.configService.get<StoreConf>('STORE');
|
const store = this.configService.get<StoreConf>('STORE');
|
||||||
|
|
||||||
if (store.CHATS) {
|
if (store.CHATS) {
|
||||||
|
this.logger.verbose('saving chats to store');
|
||||||
data.forEach((chat) => {
|
data.forEach((chat) => {
|
||||||
this.writeStore<ChatRaw>({
|
this.writeStore<ChatRaw>({
|
||||||
path: join(this.storePath, 'chats', chat.owner),
|
path: join(this.storePath, 'chats', instanceName),
|
||||||
fileName: chat.id,
|
fileName: chat.id,
|
||||||
data: chat,
|
data: chat,
|
||||||
});
|
});
|
||||||
|
this.logger.verbose(
|
||||||
|
'chats saved to store in path: ' +
|
||||||
|
join(this.storePath, 'chats', instanceName) +
|
||||||
|
'/' +
|
||||||
|
chat.id,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.verbose('chats saved to store');
|
||||||
return { insertCount: data.length };
|
return { insertCount: data.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('chats not saved to store');
|
||||||
return { insertCount: 0 };
|
return { insertCount: 0 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
@@ -51,10 +75,14 @@ export class ChatRepository extends Repository {
|
|||||||
|
|
||||||
public async find(query: ChatQuery): Promise<ChatRaw[]> {
|
public async find(query: ChatQuery): Promise<ChatRaw[]> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('finding chats');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding chats in db');
|
||||||
return await this.chatModel.find({ owner: query.where.owner });
|
return await this.chatModel.find({ owner: query.where.owner });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding chats in store');
|
||||||
|
|
||||||
const chats: ChatRaw[] = [];
|
const chats: ChatRaw[] = [];
|
||||||
const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner));
|
const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner));
|
||||||
for await (const dirent of openDir) {
|
for await (const dirent of openDir) {
|
||||||
@@ -70,6 +98,7 @@ export class ChatRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('chats found in store: ' + chats.length + ' chats');
|
||||||
return chats;
|
return chats;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
@@ -78,10 +107,13 @@ export class ChatRepository extends Repository {
|
|||||||
|
|
||||||
public async delete(query: ChatQuery) {
|
public async delete(query: ChatQuery) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('deleting chats');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('deleting chats in db');
|
||||||
return await this.chatModel.deleteOne({ ...query.where });
|
return await this.chatModel.deleteOne({ ...query.where });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('deleting chats in store');
|
||||||
rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), {
|
rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), {
|
||||||
force: true,
|
force: true,
|
||||||
recursive: true,
|
recursive: true,
|
||||||
|
|||||||
75
src/whatsapp/repository/chatwoot.repository.ts
Normal file
75
src/whatsapp/repository/chatwoot.repository.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||||
|
import { ConfigService } from '../../config/env.config';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { IChatwootModel, ChatwootRaw } from '../models';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
export class ChatwootRepository extends Repository {
|
||||||
|
constructor(
|
||||||
|
private readonly chatwootModel: IChatwootModel,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
) {
|
||||||
|
super(configService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly logger = new Logger('ChatwootRepository');
|
||||||
|
|
||||||
|
public async create(data: ChatwootRaw, instance: string): Promise<IInsert> {
|
||||||
|
try {
|
||||||
|
this.logger.verbose('creating chatwoot');
|
||||||
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('saving chatwoot to db');
|
||||||
|
const insert = await this.chatwootModel.replaceOne(
|
||||||
|
{ _id: instance },
|
||||||
|
{ ...data },
|
||||||
|
{ upsert: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.verbose(
|
||||||
|
'chatwoot saved to db: ' + insert.modifiedCount + ' chatwoot',
|
||||||
|
);
|
||||||
|
return { insertCount: insert.modifiedCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving chatwoot to store');
|
||||||
|
|
||||||
|
this.writeStore<ChatwootRaw>({
|
||||||
|
path: join(this.storePath, 'chatwoot'),
|
||||||
|
fileName: instance,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.verbose(
|
||||||
|
'chatwoot saved to store in path: ' +
|
||||||
|
join(this.storePath, 'chatwoot') +
|
||||||
|
'/' +
|
||||||
|
instance,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('chatwoot created');
|
||||||
|
return { insertCount: 1 };
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async find(instance: string): Promise<ChatwootRaw> {
|
||||||
|
try {
|
||||||
|
this.logger.verbose('finding chatwoot');
|
||||||
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding chatwoot in db');
|
||||||
|
return await this.chatwootModel.findOne({ _id: instance });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding chatwoot in store');
|
||||||
|
return JSON.parse(
|
||||||
|
readFileSync(join(this.storePath, 'chatwoot', instance + '.json'), {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
}),
|
||||||
|
) as ChatwootRaw;
|
||||||
|
} catch (error) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { join } from 'path';
|
|||||||
import { ConfigService, StoreConf } from '../../config/env.config';
|
import { ConfigService, StoreConf } from '../../config/env.config';
|
||||||
import { ContactRaw, IContactModel } from '../models';
|
import { ContactRaw, IContactModel } from '../models';
|
||||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class ContactQuery {
|
export class ContactQuery {
|
||||||
where: ContactRaw;
|
where: ContactRaw;
|
||||||
@@ -16,31 +17,121 @@ export class ContactRepository extends Repository {
|
|||||||
super(configService);
|
super(configService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async insert(data: ContactRaw[], saveDb = false): Promise<IInsert> {
|
private readonly logger = new Logger('ContactRepository');
|
||||||
|
|
||||||
|
public async insert(
|
||||||
|
data: ContactRaw[],
|
||||||
|
instanceName: string,
|
||||||
|
saveDb = false,
|
||||||
|
): Promise<IInsert> {
|
||||||
|
this.logger.verbose('inserting contacts');
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
|
this.logger.verbose('no contacts to insert');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.dbSettings.ENABLED && saveDb) {
|
if (this.dbSettings.ENABLED && saveDb) {
|
||||||
|
this.logger.verbose('saving contacts to db');
|
||||||
|
|
||||||
const insert = await this.contactModel.insertMany([...data]);
|
const insert = await this.contactModel.insertMany([...data]);
|
||||||
|
|
||||||
|
this.logger.verbose('contacts saved to db: ' + insert.length + ' contacts');
|
||||||
return { insertCount: insert.length };
|
return { insertCount: insert.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving contacts to store');
|
||||||
|
|
||||||
const store = this.configService.get<StoreConf>('STORE');
|
const store = this.configService.get<StoreConf>('STORE');
|
||||||
|
|
||||||
if (store.CONTACTS) {
|
if (store.CONTACTS) {
|
||||||
|
this.logger.verbose('saving contacts to store');
|
||||||
data.forEach((contact) => {
|
data.forEach((contact) => {
|
||||||
this.writeStore({
|
this.writeStore({
|
||||||
path: join(this.storePath, 'contacts', contact.owner),
|
path: join(this.storePath, 'contacts', instanceName),
|
||||||
fileName: contact.id,
|
fileName: contact.id,
|
||||||
data: contact,
|
data: contact,
|
||||||
});
|
});
|
||||||
|
this.logger.verbose(
|
||||||
|
'contacts saved to store in path: ' +
|
||||||
|
join(this.storePath, 'contacts', instanceName) +
|
||||||
|
'/' +
|
||||||
|
contact.id,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.verbose('contacts saved to store: ' + data.length + ' contacts');
|
||||||
|
return { insertCount: data.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('contacts not saved');
|
||||||
|
return { insertCount: 0 };
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
} finally {
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async update(
|
||||||
|
data: ContactRaw[],
|
||||||
|
instanceName: string,
|
||||||
|
saveDb = false,
|
||||||
|
): Promise<IInsert> {
|
||||||
|
try {
|
||||||
|
this.logger.verbose('updating contacts');
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
this.logger.verbose('no contacts to update');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dbSettings.ENABLED && saveDb) {
|
||||||
|
this.logger.verbose('updating contacts in db');
|
||||||
|
|
||||||
|
const contacts = data.map((contact) => {
|
||||||
|
return {
|
||||||
|
updateOne: {
|
||||||
|
filter: { id: contact.id },
|
||||||
|
update: { ...contact },
|
||||||
|
upsert: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const { nModified } = await this.contactModel.bulkWrite(contacts);
|
||||||
|
|
||||||
|
this.logger.verbose('contacts updated in db: ' + nModified + ' contacts');
|
||||||
|
return { insertCount: nModified };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('updating contacts in store');
|
||||||
|
|
||||||
|
const store = this.configService.get<StoreConf>('STORE');
|
||||||
|
|
||||||
|
if (store.CONTACTS) {
|
||||||
|
this.logger.verbose('updating contacts in store');
|
||||||
|
data.forEach((contact) => {
|
||||||
|
this.writeStore({
|
||||||
|
path: join(this.storePath, 'contacts', instanceName),
|
||||||
|
fileName: contact.id,
|
||||||
|
data: contact,
|
||||||
|
});
|
||||||
|
this.logger.verbose(
|
||||||
|
'contacts updated in store in path: ' +
|
||||||
|
join(this.storePath, 'contacts', instanceName) +
|
||||||
|
'/' +
|
||||||
|
contact.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.verbose('contacts updated in store: ' + data.length + ' contacts');
|
||||||
|
|
||||||
return { insertCount: data.length };
|
return { insertCount: data.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('contacts not updated');
|
||||||
return { insertCount: 0 };
|
return { insertCount: 0 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
@@ -51,11 +142,16 @@ export class ContactRepository extends Repository {
|
|||||||
|
|
||||||
public async find(query: ContactQuery): Promise<ContactRaw[]> {
|
public async find(query: ContactQuery): Promise<ContactRaw[]> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('finding contacts');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding contacts in db');
|
||||||
return await this.contactModel.find({ ...query.where });
|
return await this.contactModel.find({ ...query.where });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding contacts in store');
|
||||||
const contacts: ContactRaw[] = [];
|
const contacts: ContactRaw[] = [];
|
||||||
if (query?.where?.id) {
|
if (query?.where?.id) {
|
||||||
|
this.logger.verbose('finding contacts in store by id');
|
||||||
contacts.push(
|
contacts.push(
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
readFileSync(
|
readFileSync(
|
||||||
@@ -70,6 +166,8 @@ export class ContactRepository extends Repository {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('finding contacts in store by owner');
|
||||||
|
|
||||||
const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), {
|
const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
@@ -86,6 +184,8 @@ export class ContactRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('contacts found in store: ' + contacts.length + ' contacts');
|
||||||
return contacts;
|
return contacts;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { join } from 'path';
|
|||||||
import { IMessageModel, MessageRaw } from '../models';
|
import { IMessageModel, MessageRaw } from '../models';
|
||||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||||
import { opendirSync, readFileSync } from 'fs';
|
import { opendirSync, readFileSync } from 'fs';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class MessageQuery {
|
export class MessageQuery {
|
||||||
where: MessageRaw;
|
where: MessageRaw;
|
||||||
@@ -17,13 +18,23 @@ export class MessageRepository extends Repository {
|
|||||||
super(configService);
|
super(configService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async insert(data: MessageRaw[], saveDb = false): Promise<IInsert> {
|
private readonly logger = new Logger('MessageRepository');
|
||||||
|
|
||||||
|
public async insert(
|
||||||
|
data: MessageRaw[],
|
||||||
|
instanceName: string,
|
||||||
|
saveDb = false,
|
||||||
|
): Promise<IInsert> {
|
||||||
|
this.logger.verbose('inserting messages');
|
||||||
|
|
||||||
if (!Array.isArray(data) || data.length === 0) {
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
this.logger.verbose('no messages to insert');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.dbSettings.ENABLED && saveDb) {
|
if (this.dbSettings.ENABLED && saveDb) {
|
||||||
|
this.logger.verbose('saving messages to db');
|
||||||
const cleanedData = data.map((obj) => {
|
const cleanedData = data.map((obj) => {
|
||||||
const cleanedObj = { ...obj };
|
const cleanedObj = { ...obj };
|
||||||
if ('extendedTextMessage' in obj.message) {
|
if ('extendedTextMessage' in obj.message) {
|
||||||
@@ -44,23 +55,37 @@ export class MessageRepository extends Repository {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const insert = await this.messageModel.insertMany([...cleanedData]);
|
const insert = await this.messageModel.insertMany([...cleanedData]);
|
||||||
|
|
||||||
|
this.logger.verbose('messages saved to db: ' + insert.length + ' messages');
|
||||||
return { insertCount: insert.length };
|
return { insertCount: insert.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving messages to store');
|
||||||
|
|
||||||
const store = this.configService.get<StoreConf>('STORE');
|
const store = this.configService.get<StoreConf>('STORE');
|
||||||
|
|
||||||
if (store.MESSAGES) {
|
if (store.MESSAGES) {
|
||||||
data.forEach((msg) =>
|
this.logger.verbose('saving messages to store');
|
||||||
this.writeStore<MessageRaw>({
|
|
||||||
path: join(this.storePath, 'messages', msg.owner),
|
|
||||||
fileName: msg.key.id,
|
|
||||||
data: msg,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
data.forEach((message) => {
|
||||||
|
this.writeStore({
|
||||||
|
path: join(this.storePath, 'messages', instanceName),
|
||||||
|
fileName: message.key.id,
|
||||||
|
data: message,
|
||||||
|
});
|
||||||
|
this.logger.verbose(
|
||||||
|
'messages saved to store in path: ' +
|
||||||
|
join(this.storePath, 'messages', instanceName) +
|
||||||
|
'/' +
|
||||||
|
message.key.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.verbose('messages saved to store: ' + data.length + ' messages');
|
||||||
return { insertCount: data.length };
|
return { insertCount: data.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('messages not saved to store');
|
||||||
return { insertCount: 0 };
|
return { insertCount: 0 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('ERROR: ', error);
|
console.log('ERROR: ', error);
|
||||||
@@ -72,21 +97,26 @@ export class MessageRepository extends Repository {
|
|||||||
|
|
||||||
public async find(query: MessageQuery) {
|
public async find(query: MessageQuery) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('finding messages');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding messages in db');
|
||||||
if (query?.where?.key) {
|
if (query?.where?.key) {
|
||||||
for (const [k, v] of Object.entries(query.where.key)) {
|
for (const [k, v] of Object.entries(query.where.key)) {
|
||||||
query.where['key.' + k] = v;
|
query.where['key.' + k] = v;
|
||||||
}
|
}
|
||||||
delete query?.where?.key;
|
delete query?.where?.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.messageModel
|
return await this.messageModel
|
||||||
.find({ ...query.where })
|
.find({ ...query.where })
|
||||||
.sort({ messageTimestamp: -1 })
|
.sort({ messageTimestamp: -1 })
|
||||||
.limit(query?.limit ?? 0);
|
.limit(query?.limit ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding messages in store');
|
||||||
const messages: MessageRaw[] = [];
|
const messages: MessageRaw[] = [];
|
||||||
if (query?.where?.key?.id) {
|
if (query?.where?.key?.id) {
|
||||||
|
this.logger.verbose('finding messages in store by id');
|
||||||
messages.push(
|
messages.push(
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
readFileSync(
|
readFileSync(
|
||||||
@@ -101,6 +131,7 @@ export class MessageRepository extends Repository {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('finding messages in store by owner');
|
||||||
const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), {
|
const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
@@ -119,6 +150,7 @@ export class MessageRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('messages found in store: ' + messages.length + ' messages');
|
||||||
return messages
|
return messages
|
||||||
.sort((x, y) => {
|
.sort((x, y) => {
|
||||||
return (y.messageTimestamp as number) - (x.messageTimestamp as number);
|
return (y.messageTimestamp as number) - (x.messageTimestamp as number);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { IMessageUpModel, MessageUpdateRaw } from '../models';
|
|||||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { opendirSync, readFileSync } from 'fs';
|
import { opendirSync, readFileSync } from 'fs';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class MessageUpQuery {
|
export class MessageUpQuery {
|
||||||
where: MessageUpdateRaw;
|
where: MessageUpdateRaw;
|
||||||
@@ -17,31 +18,54 @@ export class MessageUpRepository extends Repository {
|
|||||||
super(configService);
|
super(configService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async insert(data: MessageUpdateRaw[], saveDb?: boolean): Promise<IInsert> {
|
private readonly logger = new Logger('MessageUpRepository');
|
||||||
|
|
||||||
|
public async insert(
|
||||||
|
data: MessageUpdateRaw[],
|
||||||
|
instanceName: string,
|
||||||
|
saveDb?: boolean,
|
||||||
|
): Promise<IInsert> {
|
||||||
|
this.logger.verbose('inserting message up');
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
|
this.logger.verbose('no message up to insert');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.dbSettings.ENABLED && saveDb) {
|
if (this.dbSettings.ENABLED && saveDb) {
|
||||||
|
this.logger.verbose('saving message up to db');
|
||||||
const insert = await this.messageUpModel.insertMany([...data]);
|
const insert = await this.messageUpModel.insertMany([...data]);
|
||||||
|
|
||||||
|
this.logger.verbose('message up saved to db: ' + insert.length + ' message up');
|
||||||
return { insertCount: insert.length };
|
return { insertCount: insert.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving message up to store');
|
||||||
|
|
||||||
const store = this.configService.get<StoreConf>('STORE');
|
const store = this.configService.get<StoreConf>('STORE');
|
||||||
|
|
||||||
if (store.MESSAGE_UP) {
|
if (store.MESSAGE_UP) {
|
||||||
|
this.logger.verbose('saving message up to store');
|
||||||
data.forEach((update) => {
|
data.forEach((update) => {
|
||||||
this.writeStore<MessageUpdateRaw>({
|
this.writeStore<MessageUpdateRaw>({
|
||||||
path: join(this.storePath, 'message-up', update.owner),
|
path: join(this.storePath, 'message-up', instanceName),
|
||||||
fileName: update.id,
|
fileName: update.id,
|
||||||
data: update,
|
data: update,
|
||||||
});
|
});
|
||||||
|
this.logger.verbose(
|
||||||
|
'message up saved to store in path: ' +
|
||||||
|
join(this.storePath, 'message-up', instanceName) +
|
||||||
|
'/' +
|
||||||
|
update.id,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.verbose('message up saved to store: ' + data.length + ' message up');
|
||||||
return { insertCount: data.length };
|
return { insertCount: data.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('message up not saved to store');
|
||||||
return { insertCount: 0 };
|
return { insertCount: 0 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
@@ -50,15 +74,21 @@ export class MessageUpRepository extends Repository {
|
|||||||
|
|
||||||
public async find(query: MessageUpQuery) {
|
public async find(query: MessageUpQuery) {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('finding message up');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding message up in db');
|
||||||
return await this.messageUpModel
|
return await this.messageUpModel
|
||||||
.find({ ...query.where })
|
.find({ ...query.where })
|
||||||
.sort({ datetime: -1 })
|
.sort({ datetime: -1 })
|
||||||
.limit(query?.limit ?? 0);
|
.limit(query?.limit ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding message up in store');
|
||||||
|
|
||||||
const messageUpdate: MessageUpdateRaw[] = [];
|
const messageUpdate: MessageUpdateRaw[] = [];
|
||||||
if (query?.where?.id) {
|
if (query?.where?.id) {
|
||||||
|
this.logger.verbose('finding message up in store by id');
|
||||||
|
|
||||||
messageUpdate.push(
|
messageUpdate.push(
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
readFileSync(
|
readFileSync(
|
||||||
@@ -73,6 +103,8 @@ export class MessageUpRepository extends Repository {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('finding message up in store by owner');
|
||||||
|
|
||||||
const openDir = opendirSync(
|
const openDir = opendirSync(
|
||||||
join(this.storePath, 'message-up', query.where.owner),
|
join(this.storePath, 'message-up', query.where.owner),
|
||||||
{ encoding: 'utf-8' },
|
{ encoding: 'utf-8' },
|
||||||
@@ -92,6 +124,9 @@ export class MessageUpRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose(
|
||||||
|
'message up found in store: ' + messageUpdate.length + ' message up',
|
||||||
|
);
|
||||||
return messageUpdate
|
return messageUpdate
|
||||||
.sort((x, y) => {
|
.sort((x, y) => {
|
||||||
return y.datetime - x.datetime;
|
return y.datetime - x.datetime;
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import { ContactRepository } from './contact.repository';
|
|||||||
import { MessageUpRepository } from './messageUp.repository';
|
import { MessageUpRepository } from './messageUp.repository';
|
||||||
import { MongoClient } from 'mongodb';
|
import { MongoClient } from 'mongodb';
|
||||||
import { WebhookRepository } from './webhook.repository';
|
import { WebhookRepository } from './webhook.repository';
|
||||||
|
import { ChatwootRepository } from './chatwoot.repository';
|
||||||
import { AuthRepository } from './auth.repository';
|
import { AuthRepository } from './auth.repository';
|
||||||
|
import { Auth, ConfigService, Database } from '../../config/env.config';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class RepositoryBroker {
|
export class RepositoryBroker {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -13,15 +18,59 @@ export class RepositoryBroker {
|
|||||||
public readonly contact: ContactRepository,
|
public readonly contact: ContactRepository,
|
||||||
public readonly messageUpdate: MessageUpRepository,
|
public readonly messageUpdate: MessageUpRepository,
|
||||||
public readonly webhook: WebhookRepository,
|
public readonly webhook: WebhookRepository,
|
||||||
|
public readonly chatwoot: ChatwootRepository,
|
||||||
public readonly auth: AuthRepository,
|
public readonly auth: AuthRepository,
|
||||||
|
private configService: ConfigService,
|
||||||
dbServer?: MongoClient,
|
dbServer?: MongoClient,
|
||||||
) {
|
) {
|
||||||
|
this.logger.verbose('initializing repository broker');
|
||||||
this.dbClient = dbServer;
|
this.dbClient = dbServer;
|
||||||
|
this.__init_repo_without_db__();
|
||||||
}
|
}
|
||||||
|
|
||||||
private dbClient?: MongoClient;
|
private dbClient?: MongoClient;
|
||||||
|
private readonly logger = new Logger('RepositoryBroker');
|
||||||
|
|
||||||
public get dbServer() {
|
public get dbServer() {
|
||||||
return this.dbClient;
|
return this.dbClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private __init_repo_without_db__() {
|
||||||
|
this.logger.verbose('initializing repository without db');
|
||||||
|
if (!this.configService.get<Database>('DATABASE').ENABLED) {
|
||||||
|
this.logger.verbose('database is disabled');
|
||||||
|
|
||||||
|
const storePath = join(process.cwd(), 'store');
|
||||||
|
|
||||||
|
this.logger.verbose('creating store path: ' + storePath);
|
||||||
|
execSync(
|
||||||
|
`mkdir -p ${join(
|
||||||
|
storePath,
|
||||||
|
'auth',
|
||||||
|
this.configService.get<Auth>('AUTHENTICATION').TYPE,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('creating chats path: ' + join(storePath, 'chats'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'chats')}`);
|
||||||
|
|
||||||
|
this.logger.verbose('creating contacts path: ' + join(storePath, 'contacts'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'contacts')}`);
|
||||||
|
|
||||||
|
this.logger.verbose('creating messages path: ' + join(storePath, 'messages'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'messages')}`);
|
||||||
|
|
||||||
|
this.logger.verbose('creating message-up path: ' + join(storePath, 'message-up'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'message-up')}`);
|
||||||
|
|
||||||
|
this.logger.verbose('creating webhook path: ' + join(storePath, 'webhook'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'webhook')}`);
|
||||||
|
|
||||||
|
this.logger.verbose('creating chatwoot path: ' + join(storePath, 'chatwoot'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'chatwoot')}`);
|
||||||
|
|
||||||
|
this.logger.verbose('creating temp path: ' + join(storePath, 'temp'));
|
||||||
|
execSync(`mkdir -p ${join(storePath, 'temp')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ConfigService } from '../../config/env.config';
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { IWebhookModel, WebhookRaw } from '../models';
|
import { IWebhookModel, WebhookRaw } from '../models';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class WebhookRepository extends Repository {
|
export class WebhookRepository extends Repository {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -12,23 +13,39 @@ export class WebhookRepository extends Repository {
|
|||||||
super(configService);
|
super(configService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly logger = new Logger('WebhookRepository');
|
||||||
|
|
||||||
public async create(data: WebhookRaw, instance: string): Promise<IInsert> {
|
public async create(data: WebhookRaw, instance: string): Promise<IInsert> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('creating webhook');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('saving webhook to db');
|
||||||
const insert = await this.webhookModel.replaceOne(
|
const insert = await this.webhookModel.replaceOne(
|
||||||
{ _id: instance },
|
{ _id: instance },
|
||||||
{ ...data },
|
{ ...data },
|
||||||
{ upsert: true },
|
{ upsert: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('webhook saved to db: ' + insert.modifiedCount + ' webhook');
|
||||||
return { insertCount: insert.modifiedCount };
|
return { insertCount: insert.modifiedCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('saving webhook to store');
|
||||||
|
|
||||||
this.writeStore<WebhookRaw>({
|
this.writeStore<WebhookRaw>({
|
||||||
path: join(this.storePath, 'webhook'),
|
path: join(this.storePath, 'webhook'),
|
||||||
fileName: instance,
|
fileName: instance,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.verbose(
|
||||||
|
'webhook saved to store in path: ' +
|
||||||
|
join(this.storePath, 'webhook') +
|
||||||
|
'/' +
|
||||||
|
instance,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('webhook created');
|
||||||
return { insertCount: 1 };
|
return { insertCount: 1 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
@@ -37,10 +54,13 @@ export class WebhookRepository extends Repository {
|
|||||||
|
|
||||||
public async find(instance: string): Promise<WebhookRaw> {
|
public async find(instance: string): Promise<WebhookRaw> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('finding webhook');
|
||||||
if (this.dbSettings.ENABLED) {
|
if (this.dbSettings.ENABLED) {
|
||||||
|
this.logger.verbose('finding webhook in db');
|
||||||
return await this.webhookModel.findOne({ _id: instance });
|
return await this.webhookModel.findOne({ _id: instance });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('finding webhook in store');
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
readFileSync(join(this.storePath, 'webhook', instance + '.json'), {
|
readFileSync(join(this.storePath, 'webhook', instance + '.json'), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
ProfileStatusDto,
|
ProfileStatusDto,
|
||||||
ReadMessageDto,
|
ReadMessageDto,
|
||||||
WhatsAppNumberDto,
|
WhatsAppNumberDto,
|
||||||
|
getBase64FromMediaMessageDto,
|
||||||
} from '../dto/chat.dto';
|
} from '../dto/chat.dto';
|
||||||
import { ContactQuery } from '../repository/contact.repository';
|
import { ContactQuery } from '../repository/contact.repository';
|
||||||
import { MessageQuery } from '../repository/message.repository';
|
import { MessageQuery } from '../repository/message.repository';
|
||||||
@@ -29,14 +30,24 @@ import { chatController } from '../whatsapp.module';
|
|||||||
import { RouterBroker } from '../abstract/abstract.router';
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
import { HttpStatus } from './index.router';
|
import { HttpStatus } from './index.router';
|
||||||
import { MessageUpQuery } from '../repository/messageUp.repository';
|
import { MessageUpQuery } from '../repository/messageUp.repository';
|
||||||
import { proto } from '@evolution/base';
|
import { proto } from '@whiskeysockets/baileys';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('ChatRouter');
|
||||||
|
|
||||||
export class ChatRouter extends RouterBroker {
|
export class ChatRouter extends RouterBroker {
|
||||||
constructor(...guards: RequestHandler[]) {
|
constructor(...guards: RequestHandler[]) {
|
||||||
super();
|
super();
|
||||||
this.router
|
this.router
|
||||||
.post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => {
|
.post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in whatsappNumbers');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<WhatsAppNumberDto>({
|
const response = await this.dataValidate<WhatsAppNumberDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: whatsappNumberSchema,
|
schema: whatsappNumberSchema,
|
||||||
@@ -47,6 +58,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => {
|
.put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in markMessageAsRead');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ReadMessageDto>({
|
const response = await this.dataValidate<ReadMessageDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: readMessageSchema,
|
schema: readMessageSchema,
|
||||||
@@ -57,6 +75,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('archiveChat'), ...guards, async (req, res) => {
|
.put(this.routerPath('archiveChat'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in archiveChat');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ArchiveChatDto>({
|
const response = await this.dataValidate<ArchiveChatDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: archiveChatSchema,
|
schema: archiveChatSchema,
|
||||||
@@ -70,6 +95,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
this.routerPath('deleteMessageForEveryone'),
|
this.routerPath('deleteMessageForEveryone'),
|
||||||
...guards,
|
...guards,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
|
logger.verbose('request received in deleteMessageForEveryone');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<DeleteMessage>({
|
const response = await this.dataValidate<DeleteMessage>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: deleteMessageSchema,
|
schema: deleteMessageSchema,
|
||||||
@@ -81,6 +113,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(this.routerPath('fetchProfilePictureUrl'), ...guards, async (req, res) => {
|
.post(this.routerPath('fetchProfilePictureUrl'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in fetchProfilePictureUrl');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<NumberDto>({
|
const response = await this.dataValidate<NumberDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: profilePictureSchema,
|
schema: profilePictureSchema,
|
||||||
@@ -91,6 +130,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('findContacts'), ...guards, async (req, res) => {
|
.post(this.routerPath('findContacts'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findContacts');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ContactQuery>({
|
const response = await this.dataValidate<ContactQuery>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: contactValidateSchema,
|
schema: contactValidateSchema,
|
||||||
@@ -101,10 +147,17 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => {
|
.post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => {
|
||||||
const response = await this.dataValidate<proto.IWebMessageInfo>({
|
logger.verbose('request received in getBase64FromMediaMessage');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
|
const response = await this.dataValidate<getBase64FromMediaMessageDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: null,
|
schema: null,
|
||||||
ClassRef: Object,
|
ClassRef: getBase64FromMediaMessageDto,
|
||||||
execute: (instance, data) =>
|
execute: (instance, data) =>
|
||||||
chatController.getBase64FromMediaMessage(instance, data),
|
chatController.getBase64FromMediaMessage(instance, data),
|
||||||
});
|
});
|
||||||
@@ -112,6 +165,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('findMessages'), ...guards, async (req, res) => {
|
.post(this.routerPath('findMessages'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findMessages');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<MessageQuery>({
|
const response = await this.dataValidate<MessageQuery>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: messageValidateSchema,
|
schema: messageValidateSchema,
|
||||||
@@ -122,6 +182,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('findStatusMessage'), ...guards, async (req, res) => {
|
.post(this.routerPath('findStatusMessage'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findStatusMessage');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<MessageUpQuery>({
|
const response = await this.dataValidate<MessageUpQuery>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: messageUpSchema,
|
schema: messageUpSchema,
|
||||||
@@ -132,6 +199,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('findChats'), ...guards, async (req, res) => {
|
.get(this.routerPath('findChats'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findChats');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: null,
|
schema: null,
|
||||||
@@ -143,6 +217,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
})
|
})
|
||||||
// Profile routes
|
// Profile routes
|
||||||
.get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => {
|
.get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in fetchPrivacySettings');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: null,
|
schema: null,
|
||||||
@@ -153,6 +234,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updatePrivacySettings'), ...guards, async (req, res) => {
|
.put(this.routerPath('updatePrivacySettings'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updatePrivacySettings');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<PrivacySettingDto>({
|
const response = await this.dataValidate<PrivacySettingDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: privacySettingsSchema,
|
schema: privacySettingsSchema,
|
||||||
@@ -164,6 +252,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('fetchBusinessProfile'), ...guards, async (req, res) => {
|
.post(this.routerPath('fetchBusinessProfile'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in fetchBusinessProfile');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ProfilePictureDto>({
|
const response = await this.dataValidate<ProfilePictureDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: profilePictureSchema,
|
schema: profilePictureSchema,
|
||||||
@@ -175,6 +270,13 @@ 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) => {
|
||||||
|
logger.verbose('request received in updateProfileName');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ProfileNameDto>({
|
const response = await this.dataValidate<ProfileNameDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: profileNameSchema,
|
schema: profileNameSchema,
|
||||||
@@ -185,6 +287,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('updateProfileStatus'), ...guards, async (req, res) => {
|
.post(this.routerPath('updateProfileStatus'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateProfileStatus');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ProfileStatusDto>({
|
const response = await this.dataValidate<ProfileStatusDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: profileStatusSchema,
|
schema: profileStatusSchema,
|
||||||
@@ -195,6 +304,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updateProfilePicture'), ...guards, async (req, res) => {
|
.put(this.routerPath('updateProfilePicture'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateProfilePicture');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ProfilePictureDto>({
|
const response = await this.dataValidate<ProfilePictureDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: profilePictureSchema,
|
schema: profilePictureSchema,
|
||||||
@@ -206,6 +322,13 @@ export class ChatRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.delete(this.routerPath('removeProfilePicture'), ...guards, async (req, res) => {
|
.delete(this.routerPath('removeProfilePicture'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in removeProfilePicture');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.dataValidate<ProfilePictureDto>({
|
const response = await this.dataValidate<ProfilePictureDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: profilePictureSchema,
|
schema: profilePictureSchema,
|
||||||
|
|||||||
68
src/whatsapp/routers/chatwoot.router.ts
Normal file
68
src/whatsapp/routers/chatwoot.router.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { RequestHandler, Router } from 'express';
|
||||||
|
import { instanceNameSchema, chatwootSchema } from '../../validate/validate.schema';
|
||||||
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
|
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||||
|
import { chatwootController } from '../whatsapp.module';
|
||||||
|
import { ChatwootService } from '../services/chatwoot.service';
|
||||||
|
import { HttpStatus } from './index.router';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('ChatwootRouter');
|
||||||
|
|
||||||
|
export class ChatwootRouter extends RouterBroker {
|
||||||
|
constructor(...guards: RequestHandler[]) {
|
||||||
|
super();
|
||||||
|
this.router
|
||||||
|
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in setChatwoot');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.dataValidate<ChatwootDto>({
|
||||||
|
request: req,
|
||||||
|
schema: chatwootSchema,
|
||||||
|
ClassRef: ChatwootDto,
|
||||||
|
execute: (instance, data) => chatwootController.createChatwoot(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
|
})
|
||||||
|
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findChatwoot');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceNameSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => chatwootController.findChatwoot(instance),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.post(this.routerPath('webhook'), async (req, res) => {
|
||||||
|
logger.verbose('request received in findChatwoot');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceNameSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance, data) => chatwootController.receiveWebhook(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly router = Router();
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
updateGroupSubjectSchema,
|
updateGroupSubjectSchema,
|
||||||
updateGroupDescriptionSchema,
|
updateGroupDescriptionSchema,
|
||||||
groupInviteSchema,
|
groupInviteSchema,
|
||||||
|
groupSendInviteSchema,
|
||||||
|
getParticipantsSchema,
|
||||||
} from '../../validate/validate.schema';
|
} from '../../validate/validate.schema';
|
||||||
import { RouterBroker } from '../abstract/abstract.router';
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
import {
|
import {
|
||||||
@@ -21,15 +23,26 @@ import {
|
|||||||
GroupUpdateParticipantDto,
|
GroupUpdateParticipantDto,
|
||||||
GroupUpdateSettingDto,
|
GroupUpdateSettingDto,
|
||||||
GroupToggleEphemeralDto,
|
GroupToggleEphemeralDto,
|
||||||
|
GroupSendInvite,
|
||||||
|
GetParticipant,
|
||||||
} from '../dto/group.dto';
|
} from '../dto/group.dto';
|
||||||
import { groupController } from '../whatsapp.module';
|
import { groupController } from '../whatsapp.module';
|
||||||
import { HttpStatus } from './index.router';
|
import { HttpStatus } from './index.router';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('GroupRouter');
|
||||||
|
|
||||||
export class GroupRouter extends RouterBroker {
|
export class GroupRouter extends RouterBroker {
|
||||||
constructor(...guards: RequestHandler[]) {
|
constructor(...guards: RequestHandler[]) {
|
||||||
super();
|
super();
|
||||||
this.router
|
this.router
|
||||||
.post(this.routerPath('create'), ...guards, async (req, res) => {
|
.post(this.routerPath('create'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in createGroup');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<CreateGroupDto>({
|
const response = await this.dataValidate<CreateGroupDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: createGroupSchema,
|
schema: createGroupSchema,
|
||||||
@@ -40,6 +53,13 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updateGroupSubject'), ...guards, async (req, res) => {
|
.put(this.routerPath('updateGroupSubject'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateGroupSubject');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
const response = await this.groupValidate<GroupSubjectDto>({
|
const response = await this.groupValidate<GroupSubjectDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: updateGroupSubjectSchema,
|
schema: updateGroupSubjectSchema,
|
||||||
@@ -50,6 +70,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updateGroupPicture'), ...guards, async (req, res) => {
|
.put(this.routerPath('updateGroupPicture'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateGroupPicture');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupPictureDto>({
|
const response = await this.groupValidate<GroupPictureDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: updateGroupPictureSchema,
|
schema: updateGroupPictureSchema,
|
||||||
@@ -60,6 +86,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updateGroupDescription'), ...guards, async (req, res) => {
|
.put(this.routerPath('updateGroupDescription'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateGroupDescription');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupDescriptionDto>({
|
const response = await this.groupValidate<GroupDescriptionDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: updateGroupDescriptionSchema,
|
schema: updateGroupDescriptionSchema,
|
||||||
@@ -71,6 +103,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('findGroupInfos'), ...guards, async (req, res) => {
|
.get(this.routerPath('findGroupInfos'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findGroupInfos');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupJid>({
|
const response = await this.groupValidate<GroupJid>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: groupJidSchema,
|
schema: groupJidSchema,
|
||||||
@@ -81,16 +119,28 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('fetchAllGroups'), ...guards, async (req, res) => {
|
.get(this.routerPath('fetchAllGroups'), ...guards, async (req, res) => {
|
||||||
const response = await this.groupNoValidate<GroupJid>({
|
logger.verbose('request received in fetchAllGroups');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.getParticipantsValidate<GetParticipant>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: {},
|
schema: getParticipantsSchema,
|
||||||
ClassRef: GroupJid,
|
ClassRef: GetParticipant,
|
||||||
execute: (instance) => groupController.fetchAllGroups(instance),
|
execute: (instance, data) => groupController.fetchAllGroups(instance, data),
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('participants'), ...guards, async (req, res) => {
|
.get(this.routerPath('participants'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in participants');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupJid>({
|
const response = await this.groupValidate<GroupJid>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: groupJidSchema,
|
schema: groupJidSchema,
|
||||||
@@ -101,6 +151,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('inviteCode'), ...guards, async (req, res) => {
|
.get(this.routerPath('inviteCode'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in inviteCode');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupJid>({
|
const response = await this.groupValidate<GroupJid>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: groupJidSchema,
|
schema: groupJidSchema,
|
||||||
@@ -111,6 +167,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('inviteInfo'), ...guards, async (req, res) => {
|
.get(this.routerPath('inviteInfo'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in inviteInfo');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.inviteCodeValidate<GroupInvite>({
|
const response = await this.inviteCodeValidate<GroupInvite>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: groupInviteSchema,
|
schema: groupInviteSchema,
|
||||||
@@ -120,17 +182,29 @@ export class GroupRouter extends RouterBroker {
|
|||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('acceptInvite'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendInvite'), ...guards, async (req, res) => {
|
||||||
const response = await this.inviteCodeValidate<GroupInvite>({
|
logger.verbose('request received in sendInvite');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.groupNoValidate<GroupSendInvite>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: groupInviteSchema,
|
schema: groupSendInviteSchema,
|
||||||
ClassRef: GroupInvite,
|
ClassRef: GroupSendInvite,
|
||||||
execute: (instance, data) => groupController.acceptInvite(instance, data),
|
execute: (instance, data) => groupController.sendInvite(instance, data),
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('revokeInviteCode'), ...guards, async (req, res) => {
|
.put(this.routerPath('revokeInviteCode'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in revokeInviteCode');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupJid>({
|
const response = await this.groupValidate<GroupJid>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: groupJidSchema,
|
schema: groupJidSchema,
|
||||||
@@ -141,6 +215,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updateParticipant'), ...guards, async (req, res) => {
|
.put(this.routerPath('updateParticipant'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateParticipant');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupUpdateParticipantDto>({
|
const response = await this.groupValidate<GroupUpdateParticipantDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: updateParticipantsSchema,
|
schema: updateParticipantsSchema,
|
||||||
@@ -151,6 +231,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('updateSetting'), ...guards, async (req, res) => {
|
.put(this.routerPath('updateSetting'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in updateSetting');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupUpdateSettingDto>({
|
const response = await this.groupValidate<GroupUpdateSettingDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: updateSettingsSchema,
|
schema: updateSettingsSchema,
|
||||||
@@ -161,6 +247,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('toggleEphemeral'), ...guards, async (req, res) => {
|
.put(this.routerPath('toggleEphemeral'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in toggleEphemeral');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupToggleEphemeralDto>({
|
const response = await this.groupValidate<GroupToggleEphemeralDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: toggleEphemeralSchema,
|
schema: toggleEphemeralSchema,
|
||||||
@@ -171,6 +263,12 @@ export class GroupRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => {
|
.delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in leaveGroup');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.groupValidate<GroupJid>({
|
const response = await this.groupValidate<GroupJid>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: {},
|
schema: {},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { InstanceRouter } from './instance.router';
|
|||||||
import { MessageRouter } from './sendMessage.router';
|
import { MessageRouter } from './sendMessage.router';
|
||||||
import { ViewsRouter } from './view.router';
|
import { ViewsRouter } from './view.router';
|
||||||
import { WebhookRouter } from './webhook.router';
|
import { WebhookRouter } from './webhook.router';
|
||||||
|
import { ChatwootRouter } from './chatwoot.router';
|
||||||
|
|
||||||
enum HttpStatus {
|
enum HttpStatus {
|
||||||
OK = 200,
|
OK = 200,
|
||||||
@@ -24,6 +25,12 @@ const authType = configService.get<Auth>('AUTHENTICATION').TYPE;
|
|||||||
const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
|
const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
|
||||||
|
|
||||||
router
|
router
|
||||||
|
.get('/', (req, res) => {
|
||||||
|
res.status(HttpStatus.OK).json({
|
||||||
|
status: HttpStatus.OK,
|
||||||
|
message: 'Welcome to the Evolution API, it is working!',
|
||||||
|
});
|
||||||
|
})
|
||||||
.use(
|
.use(
|
||||||
'/instance',
|
'/instance',
|
||||||
new InstanceRouter(configService, ...guards).router,
|
new InstanceRouter(configService, ...guards).router,
|
||||||
@@ -32,6 +39,7 @@ router
|
|||||||
.use('/message', new MessageRouter(...guards).router)
|
.use('/message', new MessageRouter(...guards).router)
|
||||||
.use('/chat', new ChatRouter(...guards).router)
|
.use('/chat', new ChatRouter(...guards).router)
|
||||||
.use('/group', new GroupRouter(...guards).router)
|
.use('/group', new GroupRouter(...guards).router)
|
||||||
.use('/webhook', new WebhookRouter(...guards).router);
|
.use('/webhook', new WebhookRouter(...guards).router)
|
||||||
|
.use('/chatwoot', new ChatwootRouter(...guards).router);
|
||||||
|
|
||||||
export { router, HttpStatus };
|
export { router, HttpStatus };
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { HttpStatus } from './index.router';
|
|||||||
import { OldToken } from '../services/auth.service';
|
import { OldToken } from '../services/auth.service';
|
||||||
import { Auth, ConfigService, Database } from '../../config/env.config';
|
import { Auth, ConfigService, Database } from '../../config/env.config';
|
||||||
import { dbserver } from '../../db/db.connect';
|
import { dbserver } from '../../db/db.connect';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('InstanceRouter');
|
||||||
|
|
||||||
export class InstanceRouter extends RouterBroker {
|
export class InstanceRouter extends RouterBroker {
|
||||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||||
@@ -14,6 +17,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
const auth = configService.get<Auth>('AUTHENTICATION');
|
const auth = configService.get<Auth>('AUTHENTICATION');
|
||||||
this.router
|
this.router
|
||||||
.post('/create', ...guards, async (req, res) => {
|
.post('/create', ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in createInstance');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceNameSchema,
|
schema: instanceNameSchema,
|
||||||
@@ -23,7 +32,29 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
|
|
||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
|
.put(this.routerPath('restart'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in restartInstance');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: instanceNameSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance) => instanceController.restartInstance(instance),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
.get(this.routerPath('connect'), ...guards, async (req, res) => {
|
.get(this.routerPath('connect'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in connectInstance');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceNameSchema,
|
schema: instanceNameSchema,
|
||||||
@@ -34,6 +65,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('connectionState'), ...guards, async (req, res) => {
|
.get(this.routerPath('connectionState'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in connectionState');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceNameSchema,
|
schema: instanceNameSchema,
|
||||||
@@ -44,6 +81,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => {
|
.get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in fetchInstances');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: null,
|
schema: null,
|
||||||
@@ -54,6 +97,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.delete(this.routerPath('logout'), ...guards, async (req, res) => {
|
.delete(this.routerPath('logout'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in logoutInstances');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceNameSchema,
|
schema: instanceNameSchema,
|
||||||
@@ -64,6 +113,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.OK).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.delete(this.routerPath('delete'), ...guards, async (req, res) => {
|
.delete(this.routerPath('delete'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in deleteInstances');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceNameSchema,
|
schema: instanceNameSchema,
|
||||||
@@ -76,6 +131,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
|
|
||||||
if (auth.TYPE === 'jwt') {
|
if (auth.TYPE === 'jwt') {
|
||||||
this.router.put('/refreshToken', async (req, res) => {
|
this.router.put('/refreshToken', async (req, res) => {
|
||||||
|
logger.verbose('request received in refreshToken');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<OldToken>({
|
const response = await this.dataValidate<OldToken>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: oldTokenSchema,
|
schema: oldTokenSchema,
|
||||||
@@ -88,6 +149,12 @@ export class InstanceRouter extends RouterBroker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.router.delete('/deleteDatabase', async (req, res) => {
|
this.router.delete('/deleteDatabase', async (req, res) => {
|
||||||
|
logger.verbose('request received in deleteDatabase');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const db = this.configService.get<Database>('DATABASE');
|
const db = this.configService.get<Database>('DATABASE');
|
||||||
if (db.ENABLED) {
|
if (db.ENABLED) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
mediaMessageSchema,
|
mediaMessageSchema,
|
||||||
pollMessageSchema,
|
pollMessageSchema,
|
||||||
reactionMessageSchema,
|
reactionMessageSchema,
|
||||||
|
statusMessageSchema,
|
||||||
stickerMessageSchema,
|
stickerMessageSchema,
|
||||||
textMessageSchema,
|
textMessageSchema,
|
||||||
} from '../../validate/validate.schema';
|
} from '../../validate/validate.schema';
|
||||||
@@ -22,18 +23,28 @@ import {
|
|||||||
SendMediaDto,
|
SendMediaDto,
|
||||||
SendPollDto,
|
SendPollDto,
|
||||||
SendReactionDto,
|
SendReactionDto,
|
||||||
|
SendStatusDto,
|
||||||
SendStickerDto,
|
SendStickerDto,
|
||||||
SendTextDto,
|
SendTextDto,
|
||||||
} from '../dto/sendMessage.dto';
|
} from '../dto/sendMessage.dto';
|
||||||
import { sendMessageController } from '../whatsapp.module';
|
import { sendMessageController } from '../whatsapp.module';
|
||||||
import { RouterBroker } from '../abstract/abstract.router';
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
import { HttpStatus } from './index.router';
|
import { HttpStatus } from './index.router';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('MessageRouter');
|
||||||
|
|
||||||
export class MessageRouter extends RouterBroker {
|
export class MessageRouter extends RouterBroker {
|
||||||
constructor(...guards: RequestHandler[]) {
|
constructor(...guards: RequestHandler[]) {
|
||||||
super();
|
super();
|
||||||
this.router
|
this.router
|
||||||
.post(this.routerPath('sendText'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendText'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendText');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendTextDto>({
|
const response = await this.dataValidate<SendTextDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: textMessageSchema,
|
schema: textMessageSchema,
|
||||||
@@ -44,6 +55,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendMedia'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendMedia'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendMedia');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendMediaDto>({
|
const response = await this.dataValidate<SendMediaDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: mediaMessageSchema,
|
schema: mediaMessageSchema,
|
||||||
@@ -54,6 +71,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendWhatsAppAudio'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendWhatsAppAudio'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendWhatsAppAudio');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendAudioDto>({
|
const response = await this.dataValidate<SendAudioDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: audioMessageSchema,
|
schema: audioMessageSchema,
|
||||||
@@ -65,6 +88,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendButtons'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendButtons'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendButtons');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendButtonDto>({
|
const response = await this.dataValidate<SendButtonDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: buttonMessageSchema,
|
schema: buttonMessageSchema,
|
||||||
@@ -75,6 +104,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendLocation'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendLocation'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendLocation');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendLocationDto>({
|
const response = await this.dataValidate<SendLocationDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: locationMessageSchema,
|
schema: locationMessageSchema,
|
||||||
@@ -85,6 +120,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendList'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendList'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendList');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendListDto>({
|
const response = await this.dataValidate<SendListDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: listMessageSchema,
|
schema: listMessageSchema,
|
||||||
@@ -95,6 +136,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendContact'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendContact'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendContact');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendContactDto>({
|
const response = await this.dataValidate<SendContactDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: contactMessageSchema,
|
schema: contactMessageSchema,
|
||||||
@@ -105,6 +152,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendReaction'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendReaction'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendReaction');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendReactionDto>({
|
const response = await this.dataValidate<SendReactionDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: reactionMessageSchema,
|
schema: reactionMessageSchema,
|
||||||
@@ -115,6 +168,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendPoll'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendPoll'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendPoll');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendPollDto>({
|
const response = await this.dataValidate<SendPollDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: pollMessageSchema,
|
schema: pollMessageSchema,
|
||||||
@@ -124,7 +183,29 @@ export class MessageRouter extends RouterBroker {
|
|||||||
|
|
||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
|
.post(this.routerPath('sendStatus'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendStatus');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.dataValidate<SendStatusDto>({
|
||||||
|
request: req,
|
||||||
|
schema: statusMessageSchema,
|
||||||
|
ClassRef: SendStatusDto,
|
||||||
|
execute: (instance, data) => sendMessageController.sendStatus(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
|
})
|
||||||
.post(this.routerPath('sendLinkPreview'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendLinkPreview'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendLinkPreview');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendLinkPreviewDto>({
|
const response = await this.dataValidate<SendLinkPreviewDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: linkPreviewSchema,
|
schema: linkPreviewSchema,
|
||||||
@@ -136,6 +217,12 @@ export class MessageRouter extends RouterBroker {
|
|||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('sendSticker'), ...guards, async (req, res) => {
|
.post(this.routerPath('sendSticker'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in sendSticker');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<SendStickerDto>({
|
const response = await this.dataValidate<SendStickerDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: stickerMessageSchema,
|
schema: stickerMessageSchema,
|
||||||
|
|||||||
@@ -5,12 +5,21 @@ import { InstanceDto } from '../dto/instance.dto';
|
|||||||
import { WebhookDto } from '../dto/webhook.dto';
|
import { WebhookDto } from '../dto/webhook.dto';
|
||||||
import { webhookController } from '../whatsapp.module';
|
import { webhookController } from '../whatsapp.module';
|
||||||
import { HttpStatus } from './index.router';
|
import { HttpStatus } from './index.router';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
|
const logger = new Logger('WebhookRouter');
|
||||||
|
|
||||||
export class WebhookRouter extends RouterBroker {
|
export class WebhookRouter extends RouterBroker {
|
||||||
constructor(...guards: RequestHandler[]) {
|
constructor(...guards: RequestHandler[]) {
|
||||||
super();
|
super();
|
||||||
this.router
|
this.router
|
||||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in setWebhook');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<WebhookDto>({
|
const response = await this.dataValidate<WebhookDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: webhookSchema,
|
schema: webhookSchema,
|
||||||
@@ -21,6 +30,12 @@ export class WebhookRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.CREATED).json(response);
|
res.status(HttpStatus.CREATED).json(response);
|
||||||
})
|
})
|
||||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findWebhook');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
const response = await this.dataValidate<InstanceDto>({
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
request: req,
|
request: req,
|
||||||
schema: instanceNameSchema,
|
schema: instanceNameSchema,
|
||||||
|
|||||||
@@ -43,8 +43,12 @@ export class AuthService {
|
|||||||
{ expiresIn: jwtOpts.EXPIRIN_IN, encoding: 'utf8', subject: 'g-t' },
|
{ expiresIn: jwtOpts.EXPIRIN_IN, encoding: 'utf8', subject: 'g-t' },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.logger.verbose('JWT token created: ' + token);
|
||||||
|
|
||||||
const auth = await this.repository.auth.create({ jwt: token }, instance.instanceName);
|
const auth = await this.repository.auth.create({ jwt: token }, instance.instanceName);
|
||||||
|
|
||||||
|
this.logger.verbose('JWT token saved in database');
|
||||||
|
|
||||||
if (auth['error']) {
|
if (auth['error']) {
|
||||||
this.logger.error({
|
this.logger.error({
|
||||||
localError: AuthService.name + '.jwt',
|
localError: AuthService.name + '.jwt',
|
||||||
@@ -59,8 +63,14 @@ export class AuthService {
|
|||||||
private async apikey(instance: InstanceDto, token?: string) {
|
private async apikey(instance: InstanceDto, token?: string) {
|
||||||
const apikey = token ? token : v4().toUpperCase();
|
const apikey = token ? token : v4().toUpperCase();
|
||||||
|
|
||||||
|
this.logger.verbose(
|
||||||
|
token ? 'APIKEY defined: ' + apikey : 'APIKEY created: ' + apikey,
|
||||||
|
);
|
||||||
|
|
||||||
const auth = await this.repository.auth.create({ apikey }, instance.instanceName);
|
const auth = await this.repository.auth.create({ apikey }, instance.instanceName);
|
||||||
|
|
||||||
|
this.logger.verbose('APIKEY saved in database');
|
||||||
|
|
||||||
if (auth['error']) {
|
if (auth['error']) {
|
||||||
this.logger.error({
|
this.logger.error({
|
||||||
localError: AuthService.name + '.apikey',
|
localError: AuthService.name + '.apikey',
|
||||||
@@ -72,45 +82,80 @@ export class AuthService {
|
|||||||
return { apikey };
|
return { apikey };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async checkDuplicateToken(token: string) {
|
||||||
|
const instances = await this.waMonitor.instanceInfo();
|
||||||
|
|
||||||
|
this.logger.verbose('checking duplicate token');
|
||||||
|
|
||||||
|
const instance = instances.find((instance) => instance.instance.apikey === token);
|
||||||
|
|
||||||
|
if (instance) {
|
||||||
|
throw new BadRequestException('Token already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('available token');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public async generateHash(instance: InstanceDto, token?: string) {
|
public async generateHash(instance: InstanceDto, token?: string) {
|
||||||
const options = this.configService.get<Auth>('AUTHENTICATION');
|
const options = this.configService.get<Auth>('AUTHENTICATION');
|
||||||
|
|
||||||
|
this.logger.verbose(
|
||||||
|
'generating hash ' + options.TYPE + ' to instance: ' + instance.instanceName,
|
||||||
|
);
|
||||||
|
|
||||||
return (await this[options.TYPE](instance, token)) as
|
return (await this[options.TYPE](instance, token)) as
|
||||||
| { jwt: string }
|
| { jwt: string }
|
||||||
| { apikey: string };
|
| { apikey: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refreshToken({ oldToken }: OldToken) {
|
public async refreshToken({ oldToken }: OldToken) {
|
||||||
|
this.logger.verbose('refreshing token');
|
||||||
|
|
||||||
if (!isJWT(oldToken)) {
|
if (!isJWT(oldToken)) {
|
||||||
throw new BadRequestException('Invalid "oldToken"');
|
throw new BadRequestException('Invalid "oldToken"');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
|
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
|
||||||
|
|
||||||
|
this.logger.verbose('checking oldToken');
|
||||||
|
|
||||||
const decode = verify(oldToken, jwtOpts.SECRET, {
|
const decode = verify(oldToken, jwtOpts.SECRET, {
|
||||||
ignoreExpiration: true,
|
ignoreExpiration: true,
|
||||||
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
|
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
|
||||||
|
|
||||||
|
this.logger.verbose('checking token in database');
|
||||||
|
|
||||||
const tokenStore = await this.repository.auth.find(decode.instanceName);
|
const tokenStore = await this.repository.auth.find(decode.instanceName);
|
||||||
|
|
||||||
const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, {
|
const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, {
|
||||||
ignoreExpiration: true,
|
ignoreExpiration: true,
|
||||||
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
|
}) as Pick<JwtPayload, 'apiName' | 'instanceName' | 'tokenId'>;
|
||||||
|
|
||||||
|
this.logger.verbose('checking tokenId');
|
||||||
|
|
||||||
if (decode.tokenId !== decodeTokenStore.tokenId) {
|
if (decode.tokenId !== decodeTokenStore.tokenId) {
|
||||||
throw new BadRequestException('Invalid "oldToken"');
|
throw new BadRequestException('Invalid "oldToken"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('generating new token');
|
||||||
|
|
||||||
const token = {
|
const token = {
|
||||||
jwt: (await this.jwt({ instanceName: decode.instanceName })).jwt,
|
jwt: (await this.jwt({ instanceName: decode.instanceName })).jwt,
|
||||||
instanceName: decode.instanceName,
|
instanceName: decode.instanceName,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('checking webhook');
|
||||||
const webhook = await this.repository.webhook.find(decode.instanceName);
|
const webhook = await this.repository.webhook.find(decode.instanceName);
|
||||||
if (
|
if (
|
||||||
webhook?.enabled &&
|
webhook?.enabled &&
|
||||||
this.configService.get<Webhook>('WEBHOOK').EVENTS.NEW_JWT_TOKEN
|
this.configService.get<Webhook>('WEBHOOK').EVENTS.NEW_JWT_TOKEN
|
||||||
) {
|
) {
|
||||||
|
this.logger.verbose('sending webhook');
|
||||||
|
|
||||||
const httpService = axios.create({ baseURL: webhook.url });
|
const httpService = axios.create({ baseURL: webhook.url });
|
||||||
await httpService.post(
|
await httpService.post(
|
||||||
'',
|
'',
|
||||||
@@ -126,6 +171,8 @@ export class AuthService {
|
|||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('token refreshed');
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error({
|
this.logger.error({
|
||||||
|
|||||||
1476
src/whatsapp/services/chatwoot.service.ts
Normal file
1476
src/whatsapp/services/chatwoot.service.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
import { opendirSync, readdirSync, rmSync } from 'fs';
|
import { opendirSync, readdirSync, rmSync } from 'fs';
|
||||||
import { WAStartupService } from './whatsapp.service';
|
import { WAStartupService } from './whatsapp.service';
|
||||||
import { INSTANCE_DIR } from '../../config/path.config';
|
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
|
||||||
import EventEmitter2 from 'eventemitter2';
|
import EventEmitter2 from 'eventemitter2';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { Logger } from '../../config/logger.config';
|
import { Logger } from '../../config/logger.config';
|
||||||
@@ -9,21 +9,25 @@ import {
|
|||||||
ConfigService,
|
ConfigService,
|
||||||
Database,
|
Database,
|
||||||
DelInstance,
|
DelInstance,
|
||||||
|
HttpServer,
|
||||||
Redis,
|
Redis,
|
||||||
} from '../../config/env.config';
|
} from '../../config/env.config';
|
||||||
import { RepositoryBroker } from '../repository/repository.manager';
|
import { RepositoryBroker } from '../repository/repository.manager';
|
||||||
import { NotFoundException } from '../../exceptions';
|
import { NotFoundException } from '../../exceptions';
|
||||||
import { Db } from 'mongodb';
|
import { Db } from 'mongodb';
|
||||||
import { RedisCache } from '../../db/redis.client';
|
|
||||||
import { initInstance } from '../whatsapp.module';
|
import { initInstance } from '../whatsapp.module';
|
||||||
import { ValidationError } from 'class-validator';
|
import { RedisCache } from '../../db/redis.client';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
export class WAMonitoringService {
|
export class WAMonitoringService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
private readonly repository: RepositoryBroker,
|
private readonly repository: RepositoryBroker,
|
||||||
|
private readonly cache: RedisCache,
|
||||||
) {
|
) {
|
||||||
|
this.logger.verbose('instance created');
|
||||||
|
|
||||||
this.removeInstance();
|
this.removeInstance();
|
||||||
this.noConnection();
|
this.noConnection();
|
||||||
this.delInstanceFiles();
|
this.delInstanceFiles();
|
||||||
@@ -34,15 +38,12 @@ export class WAMonitoringService {
|
|||||||
this.dbInstance = this.db.ENABLED
|
this.dbInstance = this.db.ENABLED
|
||||||
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
|
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
this.redisCache = this.redis.ENABLED ? new RedisCache(this.redis) : undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly db: Partial<Database> = {};
|
private readonly db: Partial<Database> = {};
|
||||||
private readonly redis: Partial<Redis> = {};
|
private readonly redis: Partial<Redis> = {};
|
||||||
|
|
||||||
private dbInstance: Db;
|
private dbInstance: Db;
|
||||||
private redisCache: RedisCache;
|
|
||||||
|
|
||||||
private readonly logger = new Logger(WAMonitoringService.name);
|
private readonly logger = new Logger(WAMonitoringService.name);
|
||||||
public readonly waInstances: Record<string, WAStartupService> = {};
|
public readonly waInstances: Record<string, WAStartupService> = {};
|
||||||
@@ -50,15 +51,30 @@ export class WAMonitoringService {
|
|||||||
public delInstanceTime(instance: string) {
|
public delInstanceTime(instance: string) {
|
||||||
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
|
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
|
||||||
if (typeof time === 'number' && time > 0) {
|
if (typeof time === 'number' && time > 0) {
|
||||||
setTimeout(() => {
|
this.logger.verbose(
|
||||||
|
`Instance "${instance}" don't have connection, will be removed in ${time} minutes`,
|
||||||
|
);
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
|
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
|
||||||
delete this.waInstances[instance];
|
if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') {
|
||||||
|
await this.waInstances[instance]?.client?.logout(
|
||||||
|
'Log out instance: ' + instance,
|
||||||
|
);
|
||||||
|
this.waInstances[instance]?.client?.ws?.close();
|
||||||
|
this.waInstances[instance]?.client?.end(undefined);
|
||||||
|
delete this.waInstances[instance];
|
||||||
|
} else {
|
||||||
|
delete this.waInstances[instance];
|
||||||
|
this.eventEmitter.emit('remove.instance', instance, 'inner');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 1000 * 60 * time);
|
}, 1000 * 60 * time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async instanceInfo(instanceName?: string) {
|
public async instanceInfo(instanceName?: string) {
|
||||||
|
this.logger.verbose('get instance info');
|
||||||
if (instanceName && !this.waInstances[instanceName]) {
|
if (instanceName && !this.waInstances[instanceName]) {
|
||||||
throw new NotFoundException(`Instance "${instanceName}" not found`);
|
throw new NotFoundException(`Instance "${instanceName}" not found`);
|
||||||
}
|
}
|
||||||
@@ -67,9 +83,27 @@ export class WAMonitoringService {
|
|||||||
|
|
||||||
for await (const [key, value] of Object.entries(this.waInstances)) {
|
for await (const [key, value] of Object.entries(this.waInstances)) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
this.logger.verbose('get instance info: ' + key);
|
||||||
|
let chatwoot: any;
|
||||||
|
|
||||||
|
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
|
|
||||||
|
const findChatwoot = await this.waInstances[key].findChatwoot();
|
||||||
|
|
||||||
|
if (findChatwoot.enabled) {
|
||||||
|
chatwoot = {
|
||||||
|
...findChatwoot,
|
||||||
|
webhook_url: `${urlServer}/chatwoot/webhook/${key}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (value.connectionStatus.state === 'open') {
|
if (value.connectionStatus.state === 'open') {
|
||||||
|
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
|
||||||
let apikey: string;
|
let apikey: string;
|
||||||
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
|
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
|
||||||
|
this.logger.verbose(
|
||||||
|
'instance: ' + key + ' - hash exposed in fetch instances',
|
||||||
|
);
|
||||||
const tokenStore = await this.repository.auth.find(key);
|
const tokenStore = await this.repository.auth.find(key);
|
||||||
apikey = tokenStore.apikey || 'Apikey not found';
|
apikey = tokenStore.apikey || 'Apikey not found';
|
||||||
|
|
||||||
@@ -79,24 +113,36 @@ export class WAMonitoringService {
|
|||||||
owner: value.wuid,
|
owner: value.wuid,
|
||||||
profileName: (await value.getProfileName()) || 'not loaded',
|
profileName: (await value.getProfileName()) || 'not loaded',
|
||||||
profilePictureUrl: value.profilePictureUrl,
|
profilePictureUrl: value.profilePictureUrl,
|
||||||
status: (await value.getProfileStatus()) || '',
|
profileStatus: (await value.getProfileStatus()) || '',
|
||||||
|
status: value.connectionStatus.state,
|
||||||
apikey,
|
apikey,
|
||||||
|
chatwoot,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose(
|
||||||
|
'instance: ' + key + ' - hash not exposed in fetch instances',
|
||||||
|
);
|
||||||
instances.push({
|
instances.push({
|
||||||
instance: {
|
instance: {
|
||||||
instanceName: key,
|
instanceName: key,
|
||||||
owner: value.wuid,
|
owner: value.wuid,
|
||||||
profileName: (await value.getProfileName()) || 'not loaded',
|
profileName: (await value.getProfileName()) || 'not loaded',
|
||||||
profilePictureUrl: value.profilePictureUrl,
|
profilePictureUrl: value.profilePictureUrl,
|
||||||
status: (await value.getProfileStatus()) || '',
|
profileStatus: (await value.getProfileStatus()) || '',
|
||||||
|
status: value.connectionStatus.state,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose(
|
||||||
|
'instance: ' + key + ' - connectionStatus: ' + value.connectionStatus.state,
|
||||||
|
);
|
||||||
let apikey: string;
|
let apikey: string;
|
||||||
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
|
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
|
||||||
|
this.logger.verbose(
|
||||||
|
'instance: ' + key + ' - hash exposed in fetch instances',
|
||||||
|
);
|
||||||
const tokenStore = await this.repository.auth.find(key);
|
const tokenStore = await this.repository.auth.find(key);
|
||||||
apikey = tokenStore.apikey || 'Apikey not found';
|
apikey = tokenStore.apikey || 'Apikey not found';
|
||||||
|
|
||||||
@@ -105,9 +151,13 @@ export class WAMonitoringService {
|
|||||||
instanceName: key,
|
instanceName: key,
|
||||||
status: value.connectionStatus.state,
|
status: value.connectionStatus.state,
|
||||||
apikey,
|
apikey,
|
||||||
|
chatwoot,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose(
|
||||||
|
'instance: ' + key + ' - hash not exposed in fetch instances',
|
||||||
|
);
|
||||||
instances.push({
|
instances.push({
|
||||||
instance: {
|
instance: {
|
||||||
instanceName: key,
|
instanceName: key,
|
||||||
@@ -119,10 +169,13 @@ export class WAMonitoringService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('return instance info: ' + instances.length);
|
||||||
|
|
||||||
return instances.find((i) => i.instance.instanceName === instanceName) ?? instances;
|
return instances.find((i) => i.instance.instanceName === instanceName) ?? instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
private delInstanceFiles() {
|
private delInstanceFiles() {
|
||||||
|
this.logger.verbose('cron to delete instance files started');
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
||||||
const collections = await this.dbInstance.collections();
|
const collections = await this.dbInstance.collections();
|
||||||
@@ -134,7 +187,9 @@ export class WAMonitoringService {
|
|||||||
{ _id: { $regex: /^session-.*/ } },
|
{ _id: { $regex: /^session-.*/ } },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
this.logger.verbose('instance files deleted: ' + name);
|
||||||
});
|
});
|
||||||
|
} else if (this.redis.ENABLED) {
|
||||||
} else {
|
} else {
|
||||||
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
|
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
|
||||||
for await (const dirent of dir) {
|
for await (const dirent of dir) {
|
||||||
@@ -150,14 +205,17 @@ export class WAMonitoringService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.logger.verbose('instance files deleted: ' + dirent.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 3600 * 1000 * 2);
|
}, 3600 * 1000 * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async cleaningUp(instanceName: string) {
|
public async cleaningUp(instanceName: string) {
|
||||||
|
this.logger.verbose('cleaning up instance: ' + instanceName);
|
||||||
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
||||||
|
this.logger.verbose('cleaning up instance in database: ' + instanceName);
|
||||||
await this.repository.dbServer.connect();
|
await this.repository.dbServer.connect();
|
||||||
const collections: any[] = await this.dbInstance.collections();
|
const collections: any[] = await this.dbInstance.collections();
|
||||||
if (collections.length > 0) {
|
if (collections.length > 0) {
|
||||||
@@ -167,52 +225,89 @@ export class WAMonitoringService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.redis.ENABLED) {
|
if (this.redis.ENABLED) {
|
||||||
this.redisCache.reference = instanceName;
|
this.logger.verbose('cleaning up instance in redis: ' + instanceName);
|
||||||
await this.redisCache.delAll();
|
this.cache.reference = instanceName;
|
||||||
|
await this.cache.delAll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('cleaning up instance in files: ' + instanceName);
|
||||||
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async cleaningStoreFiles(instanceName: string) {
|
||||||
|
this.logger.verbose('cleaning store files instance: ' + instanceName);
|
||||||
|
|
||||||
|
if (!this.db.ENABLED) {
|
||||||
|
const instance = this.waInstances[instanceName];
|
||||||
|
|
||||||
|
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
||||||
|
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`);
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`);
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`);
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`);
|
||||||
|
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`);
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`);
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'chatwoot', instanceName + '*')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async loadInstance() {
|
public async loadInstance() {
|
||||||
|
this.logger.verbose('load instances');
|
||||||
const set = async (name: string) => {
|
const set = async (name: string) => {
|
||||||
const instance = new WAStartupService(
|
const instance = new WAStartupService(
|
||||||
this.configService,
|
this.configService,
|
||||||
this.eventEmitter,
|
this.eventEmitter,
|
||||||
this.repository,
|
this.repository,
|
||||||
|
this.cache,
|
||||||
);
|
);
|
||||||
instance.instanceName = name;
|
instance.instanceName = name;
|
||||||
|
this.logger.verbose('instance loaded: ' + name);
|
||||||
|
|
||||||
await instance.connectToWhatsapp();
|
await instance.connectToWhatsapp();
|
||||||
|
this.logger.verbose('connectToWhatsapp: ' + name);
|
||||||
|
|
||||||
this.waInstances[name] = instance;
|
this.waInstances[name] = instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.redis.ENABLED) {
|
if (this.redis.ENABLED) {
|
||||||
const keys = await this.redisCache.instanceKeys();
|
this.logger.verbose('redis enabled');
|
||||||
|
await this.cache.connect(this.redis as Redis);
|
||||||
|
const keys = await this.cache.instanceKeys();
|
||||||
if (keys?.length > 0) {
|
if (keys?.length > 0) {
|
||||||
|
this.logger.verbose('reading instance keys and setting instances');
|
||||||
keys.forEach(async (k) => await set(k.split(':')[1]));
|
keys.forEach(async (k) => await set(k.split(':')[1]));
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('no instance keys found');
|
||||||
initInstance();
|
initInstance();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
||||||
|
this.logger.verbose('database enabled');
|
||||||
await this.repository.dbServer.connect();
|
await this.repository.dbServer.connect();
|
||||||
const collections: any[] = await this.dbInstance.collections();
|
const collections: any[] = await this.dbInstance.collections();
|
||||||
if (collections.length > 0) {
|
if (collections.length > 0) {
|
||||||
|
this.logger.verbose('reading collections and setting instances');
|
||||||
collections.forEach(
|
collections.forEach(
|
||||||
async (coll) => await set(coll.namespace.replace(/^[\w-]+\./, '')),
|
async (coll) => await set(coll.namespace.replace(/^[\w-]+\./, '')),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('no collections found');
|
||||||
initInstance();
|
initInstance();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('store in files enabled');
|
||||||
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
|
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
|
||||||
for await (const dirent of dir) {
|
for await (const dirent of dir) {
|
||||||
if (dirent.isDirectory()) {
|
if (dirent.isDirectory()) {
|
||||||
|
this.logger.verbose('reading instance files and setting instances');
|
||||||
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
|
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
@@ -223,6 +318,7 @@ export class WAMonitoringService {
|
|||||||
|
|
||||||
await set(dirent.name);
|
await set(dirent.name);
|
||||||
} else {
|
} else {
|
||||||
|
this.logger.verbose('no instance files found');
|
||||||
initInstance();
|
initInstance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,22 +329,39 @@ export class WAMonitoringService {
|
|||||||
|
|
||||||
private removeInstance() {
|
private removeInstance() {
|
||||||
this.eventEmitter.on('remove.instance', async (instanceName: string) => {
|
this.eventEmitter.on('remove.instance', async (instanceName: string) => {
|
||||||
|
this.logger.verbose('remove instance: ' + instanceName);
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('instance: ' + instanceName + ' - removing from memory');
|
||||||
this.waInstances[instanceName] = undefined;
|
this.waInstances[instanceName] = undefined;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('request cleaning up instance: ' + instanceName);
|
||||||
this.cleaningUp(instanceName);
|
this.cleaningUp(instanceName);
|
||||||
|
this.cleaningStoreFiles(instanceName);
|
||||||
} finally {
|
} finally {
|
||||||
this.logger.warn(`Instance "${instanceName}" - REMOVED`);
|
this.logger.warn(`Instance "${instanceName}" - REMOVED`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.eventEmitter.on('logout.instance', async (instanceName: string) => {
|
||||||
|
this.logger.verbose('logout instance: ' + instanceName);
|
||||||
|
try {
|
||||||
|
this.logger.verbose('request cleaning up instance: ' + instanceName);
|
||||||
|
this.cleaningUp(instanceName);
|
||||||
|
} finally {
|
||||||
|
this.logger.warn(`Instance "${instanceName}" - LOGOUT`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private noConnection() {
|
private noConnection() {
|
||||||
|
this.logger.verbose('checking instances without connection');
|
||||||
this.eventEmitter.on('no.connection', async (instanceName) => {
|
this.eventEmitter.on('no.connection', async (instanceName) => {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('instance: ' + instanceName + ' - removing from memory');
|
||||||
this.waInstances[instanceName] = undefined;
|
this.waInstances[instanceName] = undefined;
|
||||||
|
|
||||||
|
this.logger.verbose('request cleaning up instance: ' + instanceName);
|
||||||
this.cleaningUp(instanceName);
|
this.cleaningUp(instanceName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error({
|
this.logger.error({
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { WebhookDto } from '../dto/webhook.dto';
|
import { WebhookDto } from '../dto/webhook.dto';
|
||||||
import { WAMonitoringService } from './monitor.service';
|
import { WAMonitoringService } from './monitor.service';
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
|
||||||
export class WebhookService {
|
export class WebhookService {
|
||||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
|
private readonly logger = new Logger(WebhookService.name);
|
||||||
|
|
||||||
public create(instance: InstanceDto, data: WebhookDto) {
|
public create(instance: InstanceDto, data: WebhookDto) {
|
||||||
|
this.logger.verbose('create webhook: ' + instance.instanceName);
|
||||||
this.waMonitor.waInstances[instance.instanceName].setWebhook(data);
|
this.waMonitor.waInstances[instance.instanceName].setWebhook(data);
|
||||||
|
|
||||||
return { webhook: { ...instance, webhook: data } };
|
return { webhook: { ...instance, webhook: data } };
|
||||||
@@ -13,6 +17,7 @@ export class WebhookService {
|
|||||||
|
|
||||||
public async find(instance: InstanceDto): Promise<WebhookDto> {
|
public async find(instance: InstanceDto): Promise<WebhookDto> {
|
||||||
try {
|
try {
|
||||||
|
this.logger.verbose('find webhook: ' + instance.instanceName);
|
||||||
return await this.waMonitor.waInstances[instance.instanceName].findWebhook();
|
return await this.waMonitor.waInstances[instance.instanceName].findWebhook();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { enabled: null, url: '' };
|
return { enabled: null, url: '' };
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-namespace */
|
/* eslint-disable @typescript-eslint/no-namespace */
|
||||||
import { AuthenticationState, WAConnectionState } from '@evolution/base';
|
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
|
||||||
|
|
||||||
export enum Events {
|
export enum Events {
|
||||||
APPLICATION_STARTUP = 'application.startup',
|
APPLICATION_STARTUP = 'application.startup',
|
||||||
@@ -34,7 +34,21 @@ export declare namespace wa {
|
|||||||
profilePictureUrl?: string;
|
profilePictureUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LocalWebHook = { enabled?: boolean; url?: string; events?: string[] };
|
export type LocalWebHook = {
|
||||||
|
enabled?: boolean;
|
||||||
|
url?: string;
|
||||||
|
events?: string[];
|
||||||
|
webhook_by_events?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LocalChatwoot = {
|
||||||
|
enabled?: boolean;
|
||||||
|
account_id?: string;
|
||||||
|
token?: string;
|
||||||
|
url?: string;
|
||||||
|
name_inbox?: string;
|
||||||
|
sign_msg?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type StateConnection = {
|
export type StateConnection = {
|
||||||
instance?: string;
|
instance?: string;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import { GroupController } from './controllers/group.controller';
|
|||||||
import { ViewsController } from './controllers/views.controller';
|
import { ViewsController } from './controllers/views.controller';
|
||||||
import { WebhookService } from './services/webhook.service';
|
import { WebhookService } from './services/webhook.service';
|
||||||
import { WebhookController } from './controllers/webhook.controller';
|
import { WebhookController } from './controllers/webhook.controller';
|
||||||
|
import { ChatwootService } from './services/chatwoot.service';
|
||||||
|
import { ChatwootController } from './controllers/chatwoot.controller';
|
||||||
import { RepositoryBroker } from './repository/repository.manager';
|
import { RepositoryBroker } from './repository/repository.manager';
|
||||||
import {
|
import {
|
||||||
AuthModel,
|
AuthModel,
|
||||||
@@ -21,14 +23,17 @@ import {
|
|||||||
ContactModel,
|
ContactModel,
|
||||||
MessageModel,
|
MessageModel,
|
||||||
MessageUpModel,
|
MessageUpModel,
|
||||||
|
ChatwootModel,
|
||||||
|
WebhookModel,
|
||||||
} from './models';
|
} from './models';
|
||||||
import { dbserver } from '../db/db.connect';
|
import { dbserver } from '../db/db.connect';
|
||||||
import { WebhookRepository } from './repository/webhook.repository';
|
import { WebhookRepository } from './repository/webhook.repository';
|
||||||
import { WebhookModel } from './models/webhook.model';
|
import { ChatwootRepository } from './repository/chatwoot.repository';
|
||||||
import { AuthRepository } from './repository/auth.repository';
|
import { AuthRepository } from './repository/auth.repository';
|
||||||
import { WAStartupService } from './services/whatsapp.service';
|
import { WAStartupService } from './services/whatsapp.service';
|
||||||
import { delay } from '@evolution/base';
|
import { delay } from '@whiskeysockets/baileys';
|
||||||
import { Events } from './types/wa.types';
|
import { Events } from './types/wa.types';
|
||||||
|
import { RedisCache } from '../db/redis.client';
|
||||||
|
|
||||||
const logger = new Logger('WA MODULE');
|
const logger = new Logger('WA MODULE');
|
||||||
|
|
||||||
@@ -37,6 +42,7 @@ const chatRepository = new ChatRepository(ChatModel, configService);
|
|||||||
const contactRepository = new ContactRepository(ContactModel, configService);
|
const contactRepository = new ContactRepository(ContactModel, configService);
|
||||||
const messageUpdateRepository = new MessageUpRepository(MessageUpModel, configService);
|
const messageUpdateRepository = new MessageUpRepository(MessageUpModel, configService);
|
||||||
const webhookRepository = new WebhookRepository(WebhookModel, configService);
|
const webhookRepository = new WebhookRepository(WebhookModel, configService);
|
||||||
|
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
|
||||||
const authRepository = new AuthRepository(AuthModel, configService);
|
const authRepository = new AuthRepository(AuthModel, configService);
|
||||||
|
|
||||||
export const repository = new RepositoryBroker(
|
export const repository = new RepositoryBroker(
|
||||||
@@ -45,11 +51,20 @@ export const repository = new RepositoryBroker(
|
|||||||
contactRepository,
|
contactRepository,
|
||||||
messageUpdateRepository,
|
messageUpdateRepository,
|
||||||
webhookRepository,
|
webhookRepository,
|
||||||
|
chatwootRepository,
|
||||||
authRepository,
|
authRepository,
|
||||||
|
configService,
|
||||||
dbserver?.getClient(),
|
dbserver?.getClient(),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const waMonitor = new WAMonitoringService(eventEmitter, configService, repository);
|
export const cache = new RedisCache();
|
||||||
|
|
||||||
|
export const waMonitor = new WAMonitoringService(
|
||||||
|
eventEmitter,
|
||||||
|
configService,
|
||||||
|
repository,
|
||||||
|
cache,
|
||||||
|
);
|
||||||
|
|
||||||
const authService = new AuthService(configService, waMonitor, repository);
|
const authService = new AuthService(configService, waMonitor, repository);
|
||||||
|
|
||||||
@@ -57,6 +72,10 @@ const webhookService = new WebhookService(waMonitor);
|
|||||||
|
|
||||||
export const webhookController = new WebhookController(webhookService);
|
export const webhookController = new WebhookController(webhookService);
|
||||||
|
|
||||||
|
const chatwootService = new ChatwootService(waMonitor);
|
||||||
|
|
||||||
|
export const chatwootController = new ChatwootController(chatwootService, configService);
|
||||||
|
|
||||||
export const instanceController = new InstanceController(
|
export const instanceController = new InstanceController(
|
||||||
waMonitor,
|
waMonitor,
|
||||||
configService,
|
configService,
|
||||||
@@ -64,6 +83,8 @@ export const instanceController = new InstanceController(
|
|||||||
eventEmitter,
|
eventEmitter,
|
||||||
authService,
|
authService,
|
||||||
webhookService,
|
webhookService,
|
||||||
|
chatwootService,
|
||||||
|
cache,
|
||||||
);
|
);
|
||||||
export const viewsController = new ViewsController(waMonitor, configService);
|
export const viewsController = new ViewsController(waMonitor, configService);
|
||||||
export const sendMessageController = new SendMessageController(waMonitor);
|
export const sendMessageController = new SendMessageController(waMonitor);
|
||||||
@@ -71,10 +92,11 @@ export const chatController = new ChatController(waMonitor);
|
|||||||
export const groupController = new GroupController(waMonitor);
|
export const groupController = new GroupController(waMonitor);
|
||||||
|
|
||||||
export async function initInstance() {
|
export async function initInstance() {
|
||||||
const instance = new WAStartupService(configService, eventEmitter, repository);
|
const instance = new WAStartupService(configService, eventEmitter, repository, cache);
|
||||||
|
|
||||||
const mode = configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE;
|
const mode = configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE;
|
||||||
|
|
||||||
|
logger.verbose('Sending data webhook for event: ' + Events.APPLICATION_STARTUP);
|
||||||
instance.sendDataWebhook(
|
instance.sendDataWebhook(
|
||||||
Events.APPLICATION_STARTUP,
|
Events.APPLICATION_STARTUP,
|
||||||
{
|
{
|
||||||
@@ -85,9 +107,25 @@ export async function initInstance() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (mode === 'container') {
|
if (mode === 'container') {
|
||||||
|
logger.verbose('Application startup in container mode');
|
||||||
|
|
||||||
const instanceName = configService.get<Auth>('AUTHENTICATION').INSTANCE.NAME;
|
const instanceName = configService.get<Auth>('AUTHENTICATION').INSTANCE.NAME;
|
||||||
|
logger.verbose('Instance name: ' + instanceName);
|
||||||
|
|
||||||
const instanceWebhook =
|
const instanceWebhook =
|
||||||
configService.get<Auth>('AUTHENTICATION').INSTANCE.WEBHOOK_URL;
|
configService.get<Auth>('AUTHENTICATION').INSTANCE.WEBHOOK_URL;
|
||||||
|
logger.verbose('Instance webhook: ' + instanceWebhook);
|
||||||
|
|
||||||
|
const chatwootAccountId =
|
||||||
|
configService.get<Auth>('AUTHENTICATION').INSTANCE.CHATWOOT_ACCOUNT_ID;
|
||||||
|
logger.verbose('Chatwoot account id: ' + chatwootAccountId);
|
||||||
|
|
||||||
|
const chatwootToken =
|
||||||
|
configService.get<Auth>('AUTHENTICATION').INSTANCE.CHATWOOT_TOKEN;
|
||||||
|
logger.verbose('Chatwoot token: ' + chatwootToken);
|
||||||
|
|
||||||
|
const chatwootUrl = configService.get<Auth>('AUTHENTICATION').INSTANCE.CHATWOOT_URL;
|
||||||
|
logger.verbose('Chatwoot url: ' + chatwootUrl);
|
||||||
|
|
||||||
instance.instanceName = instanceName;
|
instance.instanceName = instanceName;
|
||||||
|
|
||||||
@@ -96,13 +134,33 @@ export async function initInstance() {
|
|||||||
|
|
||||||
const hash = await authService.generateHash({
|
const hash = await authService.generateHash({
|
||||||
instanceName: instance.instanceName,
|
instanceName: instance.instanceName,
|
||||||
|
token: configService.get<Auth>('AUTHENTICATION').API_KEY.KEY,
|
||||||
});
|
});
|
||||||
|
logger.verbose('Hash generated: ' + hash);
|
||||||
|
|
||||||
if (instanceWebhook) {
|
if (instanceWebhook) {
|
||||||
|
logger.verbose('Creating webhook for instance: ' + instanceName);
|
||||||
try {
|
try {
|
||||||
webhookService.create(instance, { enabled: true, url: instanceWebhook });
|
webhookService.create(instance, { enabled: true, url: instanceWebhook });
|
||||||
|
logger.verbose('Webhook created');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.log(error);
|
logger.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatwootUrl && chatwootToken && chatwootAccountId) {
|
||||||
|
logger.verbose('Creating chatwoot for instance: ' + instanceName);
|
||||||
|
try {
|
||||||
|
chatwootService.create(instance, {
|
||||||
|
enabled: true,
|
||||||
|
url: chatwootUrl,
|
||||||
|
token: chatwootToken,
|
||||||
|
account_id: chatwootAccountId,
|
||||||
|
sign_msg: false,
|
||||||
|
});
|
||||||
|
logger.verbose('Chatwoot created');
|
||||||
|
} catch (error) {
|
||||||
|
logger.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +178,7 @@ export async function initInstance() {
|
|||||||
return await this.connectionState({ instanceName });
|
return await this.connectionState({ instanceName });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.log(error);
|
logger.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
|
|||||||
BIN
temp/audio.mp4
BIN
temp/audio.mp4
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB |
Reference in New Issue
Block a user