Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e60d972bff | ||
|
|
3c4a139f52 | ||
|
|
bf615efa96 | ||
|
|
2af3940be7 | ||
|
|
64e145909e | ||
|
|
e90f34cc96 | ||
|
|
a23cf2dd26 | ||
|
|
7b623d0faa | ||
|
|
14788b1d53 | ||
|
|
e1bf8cf735 | ||
|
|
2117731b46 | ||
|
|
74f8fe1caf | ||
|
|
9388a3b277 | ||
|
|
13e7a05e1b | ||
|
|
885fa25c0c | ||
|
|
966b35094f | ||
|
|
65607bf3b3 | ||
|
|
5fcba7a7db | ||
|
|
f9ec3cb753 | ||
|
|
bf6ffd65a9 | ||
|
|
d680b6b43a | ||
|
|
3ead3a0ba3 | ||
|
|
4c5c977310 |
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
Dockerfile
|
||||
.sessions
|
||||
.*sessions
|
||||
files
|
||||
venv
|
||||
.wwebjs_auth
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
.sessions
|
||||
.*sessions
|
||||
tokens
|
||||
files
|
||||
venv
|
||||
|
||||
+15
-6
@@ -1,4 +1,13 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: 'v2.7.1'
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [javascript, ts]
|
||||
files: |
|
||||
(?x)^(
|
||||
src/.*|
|
||||
)$
|
||||
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
|
||||
rev: v1.1.2
|
||||
hooks:
|
||||
@@ -12,9 +21,9 @@ repos:
|
||||
language: pygrep
|
||||
entry: 'plus'
|
||||
files: |
|
||||
(?x)^(
|
||||
src/api/.*|
|
||||
src/core/.*|
|
||||
src/structures/.*|
|
||||
src/config.service.ts|
|
||||
)$
|
||||
(?x)^(
|
||||
src/api/.*|
|
||||
src/core/.*|
|
||||
src/structures/.*|
|
||||
src/config.service.ts|
|
||||
)$
|
||||
@@ -4,6 +4,15 @@
|
||||
FROM node:18 as build
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
|
||||
|
||||
# Install https://github.com/WhiskeySockets/Baileys
|
||||
WORKDIR /
|
||||
RUN git clone https://github.com/WhiskeySockets/Baileys WhiskeySockets-Baileys && \
|
||||
cd WhiskeySockets-Baileys && \
|
||||
git checkout e3ac5f596f16b706420ed016a349fa75bd3a157f && \
|
||||
yarn install && \
|
||||
yarn build:all
|
||||
|
||||
# npm packages
|
||||
WORKDIR /src
|
||||
COPY package.json .
|
||||
|
||||
@@ -1,26 +1,8 @@
|
||||
build:
|
||||
docker build . -t devlikeapro/whatsapp-http-api
|
||||
|
||||
run-webjs:
|
||||
docker run \
|
||||
--rm -d \
|
||||
-p 127.0.0.1:3000:3000/tcp \
|
||||
-v `pwd`/.sessions:/app/.sessions \
|
||||
-e WHATSAPP_HOOK_URL=https://httpbin.org/post \
|
||||
-e WHATSAPP_HOOK_EVENTS=* \
|
||||
-e WHATSAPP_API_KEY=321 \
|
||||
--name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
|
||||
run-venom:
|
||||
docker run \
|
||||
--rm -d \
|
||||
-p 127.0.0.1:3000:3000/tcp \
|
||||
-v `pwd`/.sessions:/app/.sessions \
|
||||
-e WHATSAPP_HOOK_URL=https://httpbin.org/post \
|
||||
-e WHATSAPP_HOOK_EVENTS=* \
|
||||
-e WHATSAPP_DEFAULT_ENGINE=VENOM \
|
||||
-e WHATSAPP_API_KEY=321 \
|
||||
--name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
build-plus:
|
||||
docker build . -t devlikeapro/whatsapp-http-api-plus
|
||||
|
||||
stop:
|
||||
docker stop whatsapp-http-api
|
||||
|
||||
@@ -61,8 +61,6 @@
|
||||
|
||||
## Updates
|
||||
- Bug fixes and updates come as soon as they are implemented.
|
||||
- One month delay in bug fixes and updates.
|
||||
|
||||
|
||||
## Groups
|
||||
- Create a group
|
||||
@@ -123,7 +121,7 @@ Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-ve
|
||||
Run WhatsApp HTTP API:
|
||||
|
||||
```bash
|
||||
docker run -it --rm -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
docker run -it --rm -p 3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
|
||||
# It prints logs and the last line must be
|
||||
# WhatsApp HTTP API is running on: http://[::1]:3000
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
version: '3'
|
||||
services:
|
||||
whatsapp-http-api:
|
||||
image: devlikeapro/whatsapp-http-api
|
||||
container_name: whatsapp-http-api
|
||||
ports:
|
||||
- "3000:3000/tcp"
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
- WHATSAPP_HOOK_URL=https://httpbin.org/post
|
||||
- WHATSAPP_HOOK_EVENTS=message
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
restart: always
|
||||
|
||||
whatsapp-http-api-plus:
|
||||
image: devlikeapro/whatsapp-http-api-plus
|
||||
container_name: whatsapp-http-api-plus
|
||||
ports:
|
||||
- "3000:3000/tcp"
|
||||
volumes:
|
||||
- "./.sessions:/app/.sessions"
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
- WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
- WHATSAPP_HOOK_URL=https://httpbin.org/post
|
||||
- WHATSAPP_HOOK_EVENTS=message
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
- WHATSAPP_API_KEY=321
|
||||
- WHATSAPP_SWAGGER_USERNAME=admin
|
||||
- WHATSAPP_SWAGGER_PASSWORD=123
|
||||
- WHATSAPP_API_HOSTNAME=localhost
|
||||
restart: always
|
||||
@@ -74,7 +74,7 @@ copyRight = "Copyright (c) 2021-2023 Dev Like A Pro"
|
||||
alert = true
|
||||
alertDismissable = false
|
||||
# alertText = "Introducing the Doks child theme, several DX + UX updates, and more! <a class=\"alert-link stretched-link\" href=\"https://getdoks.org/blog/doks-v0.2/\" target=\"_blank\" rel=\"noopener\">Check out Doks v0.2</a>"
|
||||
alertText = "Enjoy Core version or want to get WAHA Plus verison? You can <a class=\"alert-link stretched-link\" href=\"https://waha.devlike.pro/docs/how-to/plus-version/\">support the project -></a>"
|
||||
alertText = "Enjoy Core version or want to get WAHA Plus verison? <a class=\"alert-link stretched-link\" href=\"https://waha.devlike.pro/docs/how-to/plus-version/\">Support the project -></a>"
|
||||
|
||||
# Edit Page
|
||||
# repoHost [Github | Gitea | GitLab | Bitbucket | BitbucketServer ] is used for building the edit link based on git hoster
|
||||
|
||||
@@ -5,18 +5,64 @@ lead: "Changelog"
|
||||
date: 2020-10-06T08:49:31+00:00
|
||||
lastmod: 2020-10-06T08:49:31+00:00
|
||||
draft: false
|
||||
images: []
|
||||
images: [ ]
|
||||
menu:
|
||||
docs:
|
||||
parent: "help"
|
||||
weight: 600
|
||||
toc: true
|
||||
---
|
||||
## 2021.3
|
||||
|
||||
## 2021.6 - June 2023
|
||||
|
||||
Improvements on session management, restarting sessions and more:
|
||||
|
||||
- Added `WHATSAPP_RESTART_ALL_SESSIONS=True`: Set this variable to `True` to start all **STOPPED** sessions after
|
||||
container restarts. By default, this variable is set to `False`.
|
||||
- Please note that this will start all **STOPPED** sessions, not just the sessions that were working before the
|
||||
restart. You can maintain the session list by
|
||||
using `POST /api/session/stop` with the `logout: True` parameter or by calling `POST /api/session/logout` to remove
|
||||
**STOPPED** sessions. You can see all sessions, including **STOPPED** sessions, in the `GET /api/sessions/all=True`
|
||||
response.
|
||||
- `WHATSAPP_START_SESSION` now support more than one session! Separate session names by command, and it'll start them
|
||||
ALWAYS after container restart `WHATSAPP_START_SESSION=session1,session2`
|
||||
- `WHATSAPP_SWAGGER_CONFIG_ADVANCED=true` enables advanced configuration options for Swagger documentation - you can customize host, port and base URL for the requests.
|
||||
Disabled by default.
|
||||
- Added `?all=true` parameter to `GET /api/session?all=True` endpoint - it'll show you ALL session, included **STOPPED
|
||||
**, so you can know which one will be restarted if you set `WHATSAPP_RESTART_ALL_SESSIONS=True` environment variable.
|
||||
- Added `POST /api/sessions/logout` that allow you to logout from session - remove saved credentials.
|
||||
- Added `logout` boolean parameter to `POST /api/sessions/stop` request that allow you to stop the session AND logout at
|
||||
the same time.
|
||||
- Added [How to deploy page ->]({{< relref "/docs/how-to/deploy" >}}) with
|
||||
[docker-compose.yaml](https://github.com/devlikeapro/whatsapp-http-api/blob/core/docker-compose.yaml) example
|
||||
- Added `engine` field in webhook payload
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"engine": "WEBJS",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
## 2021.5 - May 2023
|
||||
|
||||
- Added new [NOWEB engine]({{< relref "/docs/how-to/engines" >}}). **NOWEB** engine does not require a browser to work
|
||||
with
|
||||
WhatsApp Web, it does so directly using a WebSocket.
|
||||
- Less CPU and RAM usage!
|
||||
- Send Locations API works!
|
||||
- Send Link Preview API works!
|
||||
- ⚠ Read the article before using it [How to avoid blocking ->]({{< relref "/docs/help/how-to-avoid-blocking" >}}).
|
||||
|
||||
## 2021.4 - March 2023
|
||||
|
||||
- Add [Groups API]({{< relref "/docs/how-to/groups" >}})
|
||||
- Use Chromium by default instead of Chrome
|
||||
|
||||
## 2021.1
|
||||
## 2021.1 - January 2023
|
||||
|
||||
- Added [Contacts API]({{< relref "/docs/how-to/contacts" >}})
|
||||
- Get all contacts
|
||||
- Get a contact
|
||||
@@ -25,33 +71,42 @@ toc: true
|
||||
- Check number exists (is registered in WhatsApp) - works even if the number is not in the contact list
|
||||
- Block and unblock contact
|
||||
|
||||
## 2022.12
|
||||
## 2022.12 - December 2023
|
||||
|
||||
- Added `GET /messages/` endpoint to get chat messages [#31](https://github.com/devlikeapro/whatsapp-http-api/issues/31)
|
||||
|
||||
### Security 
|
||||
|
||||
- Added `WHATSAPP_SWAGGER_USERNAME` and `WHATSAPP_SWAGGER_PASSWORD` to hide and protect swagger panel.
|
||||
|
||||
## 2022.11
|
||||
|
||||
**Please test changes in test environment before update production!!**
|
||||
|
||||
### Engine 
|
||||
1. WAHA has changed its underlying engine from Venom to Whatsapp Web.JS. It might change the response and webhook's payloads.
|
||||
|
||||
1. WAHA has changed its underlying engine from Venom to Whatsapp Web.JS. It might change the response and webhook's
|
||||
payloads.
|
||||
2. Optimize CPU and memory consumption.
|
||||
|
||||
|
||||
### Requests 
|
||||
|
||||
- For all `/api/session/` requests use `name` field instead of `sessionName`.
|
||||
- For all "chatting" requests use `session` field instead of `sessionName`.
|
||||
|
||||
### Sessions 
|
||||
|
||||
Now you don't have to scan QR code each time you run WAHA, WAHA saves it for you! Available only in Plus version.
|
||||
|
||||
### Authentication 
|
||||
|
||||
Now you can authenticate all requests for WAHA - use `WHATSAPP_API_KEY=secret` environment variable to set "secret key".
|
||||
|
||||
If `WHATSAPP_API_KEY` is set - requests must have `X-Api-Key` header with `secret` value, where `secret` - any random secret key.
|
||||
If `WHATSAPP_API_KEY` is set - requests must have `X-Api-Key` header with `secret` value, where `secret` - any random
|
||||
secret key.
|
||||
|
||||
### Webhooks 
|
||||
|
||||
#### Configuration
|
||||
|
||||
Instead of setting each webhook via environment variables - we use two environments variables:
|
||||
|
||||
@@ -5,7 +5,7 @@ lead: "Answers to frequently asked questions."
|
||||
date: 2020-10-06T08:49:31+00:00
|
||||
lastmod: 2020-10-06T08:49:31+00:00
|
||||
draft: false
|
||||
images: []
|
||||
images: [ ]
|
||||
menu:
|
||||
docs:
|
||||
parent: "help"
|
||||
@@ -13,7 +13,28 @@ weight: 630
|
||||
toc: true
|
||||
---
|
||||
|
||||
## How to update?
|
||||
|
||||
{{< alert icon="💡" text="Please test all new versions in the development environment before updating production!" />}}
|
||||
|
||||
 For **Core** version the command is
|
||||
|
||||
```bash
|
||||
docker pull devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
 For **Plus** version, we use login to get the image before:
|
||||
|
||||
```bash
|
||||
docker login -u devlikeapro -p {PASSWORD}
|
||||
docker pull devlikeapro/whatsapp-http-api-plus
|
||||
docker logout
|
||||
```
|
||||
|
||||
Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-version" >}})
|
||||
|
||||
## How much CPU and Memory does WhatsApp HTTP API consume?
|
||||
|
||||
The benchmark has been made on **Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz**.
|
||||
It may differ from case to case, it depends on usage pattern - how many messages you get, how many send, etc.
|
||||
|
||||
@@ -22,6 +43,7 @@ It may differ from case to case, it depends on usage pattern - how many messages
|
||||
| 1 | 30% | 400MB |
|
||||
| 10 | 270% | 1.5GB |
|
||||
|
||||
|
||||
The nature of WhatsApp HTTP API and its underlying engines - is to run **real** WhatsApp Web version in Chrome and
|
||||
communicate with it to prevent blocking from WhatsApp. It's the reason why it's so demanding on resources.
|
||||
|
||||
If you're looking for less resource demanded engine - [have a look at **NOWEB** engine ->]({{< relref "/docs/how-to/engines" >}})
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "How to Avoid Blocking"
|
||||
description: "How to Avoid Blocking From Whatsapp"
|
||||
date: 2020-11-12T13:26:54+01:00
|
||||
lastmod: 2020-11-12T13:26:54+01:00
|
||||
draft: false
|
||||
images: [ ]
|
||||
menu:
|
||||
docs:
|
||||
parent: "help"
|
||||
weight: 610
|
||||
toc: true
|
||||
---
|
||||
|
||||
How to Avoid Blocking in WhatsApp when developing bots.
|
||||
|
||||
It's important to keep in mind that WhatsApp has strict policies in place to prevent spamming and abuse of their
|
||||
platform.
|
||||
|
||||
If you're developing a bot for WhatsApp, it's crucial to follow these guidelines to avoid getting blocked.
|
||||
|
||||
## Guidelines to Follow
|
||||
|
||||
### 1. Only Reply to Messages
|
||||
|
||||
When developing a bot for WhatsApp, you **should never initiate a conversation**.
|
||||
Instead, your bot **should only reply** to messages that it receives.
|
||||
This will prevent your bot from being flagged as spam by WhatsApp's users and algorithms.
|
||||
|
||||
### 2. Avoid Spamming and Sending Unnecessary Content
|
||||
|
||||
Sending too many messages or sending content that the user did not request can also lead to your bot being blocked. Make
|
||||
sure to only send relevant and useful information to the user.
|
||||
Additionally, do not send too many messages at once, as this can also trigger spam filters.
|
||||
|
||||
### 3. Other Considerations
|
||||
|
||||
There are other guidelines to follow when developing a bot for WhatsApp, such as avoiding the use of banned words and
|
||||
not sharing sensitive or inappropriate content.
|
||||
Make sure to read WhatsApp's policies thoroughly to ensure that your bot complies with all of their rules.
|
||||
|
||||
## How to Process Messages
|
||||
|
||||
When processing messages in your bot, it's important to follow certain steps to avoid being flagged as spam.
|
||||
Here's a recommended process to follow:
|
||||
|
||||
1. **Send seen** before processing the message. This can be done by sending a `POST /api/sendSeen/` request to
|
||||
the WAHA API.
|
||||
|
||||
2. **Start typing** before sending a message and wait for a random interval depending on the size of the message. This can
|
||||
be done by sending a `POST /api/startTyping/` request.
|
||||
|
||||
3. **Stop typing** before sending the message. This can be done by sending a `POST /api/stopTyping/` request.
|
||||
|
||||
4. **Send the text message** using the `POST /api/sendText` request.
|
||||
|
||||
By following these steps, you can ensure that your bot processes messages in a way that's compliant with WhatsApp's
|
||||
guidelines and reduces the risk of being blocked.
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
title: "How to Update"
|
||||
description: "Regularly update the installed WAHA to keep your bot stable, usable, and secure."
|
||||
lead: "Regularly update the installed WAHA to keep your bot stable, usable, and secure."
|
||||
date: 2020-11-12T13:26:54+01:00
|
||||
lastmod: 2020-11-12T13:26:54+01:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
docs:
|
||||
parent: "help"
|
||||
weight: 610
|
||||
toc: true
|
||||
---
|
||||
|
||||
{{< alert icon="💡" text="Please test all new versions in the development environment before updating production!" />}}
|
||||
|
||||
## Core
|
||||
 For Core version the command is
|
||||
|
||||
```bash
|
||||
docker pull devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
## Plus
|
||||
 For Plus version, we use login to get the image before:
|
||||
|
||||
```bash
|
||||
docker login -u devlikeapro -p {PASSWORD}
|
||||
docker pull devlikeapro/whatsapp-http-api-plus
|
||||
docker logout
|
||||
```
|
||||
|
||||
Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-version" >}})
|
||||
@@ -1,13 +1,14 @@
|
||||
---
|
||||
title : "Configuration"
|
||||
title: "Configuration"
|
||||
description: "Configuration"
|
||||
lead: ""
|
||||
date: 2020-10-06T08:48:45+00:00
|
||||
lastmod: 2020-10-06T08:48:45+00:00
|
||||
draft: false
|
||||
images: []
|
||||
images: [ ]
|
||||
weight: 800
|
||||
---
|
||||
|
||||
You can configure WhatsApp HTTP API behaviour via environment variables, by adding `-e WHATSAPP_VARNAME=value` at the
|
||||
begging of the command line or by using [other options](https://docs.docker.com/engine/reference/commandline/run/)
|
||||
|
||||
@@ -15,44 +16,118 @@ begging of the command line or by using [other options](https://docs.docker.com/
|
||||
docker run -it -e "WHATSAPP_HOOK_EVENTS=*" -e WHATSAPP_HOOK_URL=https://httpbin.org/post devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
It's not necessary to always run such a long command - you can save all data in
|
||||
[docker-compose.yaml](https://github.com/devlikeapro/whatsapp-http-api/blob/core/docker-compose.yaml)
|
||||
file as described on [How to deploy page ->]({{< relref "/docs/how-to/deploy" >}}).
|
||||
|
||||
## Environment variables
|
||||
|
||||
- `DEBUG=1` - show debug and verbose logs, set in any value
|
||||
- `WHATSAPP_API_PORT=3000` - listen port for HTTP server (default: `3000`)
|
||||
- `WHATSAPP_API_HOSTNAME=localhost` - Hostname for HTTP server (default: `localhost`)
|
||||
- `WHATSAPP_API_KEY=mysecret` - protect the api with a secret code. If you set it - add `X-Api-Key: mysecret` to all
|
||||
requests.
|
||||
- `WHATSAPP_SWAGGER_USERNAME=admin` + `WHATSAPP_SWAGGER_PASSWORD=admin` - protect the Swagger panel with `admin / admin`
|
||||
credentials. It doesn't affect api access!
|
||||
- `WHATSAPP_START_SESSION=default` - start session with the name right after launching the app
|
||||
The following environment variables can be used to configure the WAHA:
|
||||
|
||||
## File storage 
|
||||
- `DEBUG=1`: Set this variable to any value to enable debug and verbose logs.
|
||||
- `WHATSAPP_API_PORT=3000`: The port number that the HTTP server will listen on. The default value is `3000`.
|
||||
- `WHATSAPP_API_HOSTNAME=localhost`: The hostname for the HTTP server. The default value is `localhost`.
|
||||
- `WHATSAPP_API_KEY=mysecret`: If you set this variable, you must include the `X-Api-Key: mysecret` header in all
|
||||
requests to the API. This will protect the API with a secret code.
|
||||
- `WHATSAPP_SWAGGER_USERNAME=admin` and `WHATSAPP_SWAGGER_PASSWORD=admin`: These variables can be used to protect the
|
||||
Swagger panel with `admin / admin` credentials. This does not affect API access.
|
||||
- `WHATSAPP_SWAGGER_CONFIG_ADVANCED=true` - enables advanced configuration options for Swagger documentation - you can customize host, port and base URL for the requests.
|
||||
Disabled by default.
|
||||
- `WHATSAPP_RESTART_ALL_SESSIONS=True`: Set this variable to `True` to start all **STOPPED** sessions after container
|
||||
restarts. By default, this variable is set to `False`.
|
||||
- Please note that this will start all **STOPPED** sessions, not just the sessions that were working before the restart. You can maintain the session list by
|
||||
using `POST /api/session/stop` with the `logout: True` parameter or by calling `POST /api/session/logout` to remove
|
||||
**STOPPED** sessions. You can see all sessions, including **STOPPED** sessions, in the `GET /api/sessions/all=True`
|
||||
response.
|
||||
- `WHATSAPP_START_SESSION=session1,session2`: This variable can be used to start sessions with the specified names right
|
||||
after launching the API. Separate session names with a comma.
|
||||
|
||||
- `WHATSAPP_FILES_MIMETYPES` - download only these mimetypes from messages (download all files be default). Mimetypes
|
||||
must be separated by a comma, without spaces: `audio,image/png,image/gif`. In order to choose type use prefix (
|
||||
like `audio,image`).
|
||||
- `WHATSAPP_FILES_LIFETIME`- to keep free space files will be removed after this time (default: `180`, in seconds)
|
||||
- `WHATSAPP_FILES_FOLDER` - folder where will be stored files from chats (images, voice messages) (
|
||||
default: `/tmp/whatsapp-files`)
|
||||
### Examples
|
||||
|
||||
## Engines
|
||||
#### Debug Mode
|
||||
|
||||
Under the hood **WAHA** allows you to use two different engines. You can control what you want to run by settings
|
||||
`WHATSAPP_DEFAULT_ENGINE` environment variables.
|
||||
To enable debug mode, set the `DEBUG` environment variable to any value:
|
||||
|
||||
If you have any problems with one engine - try another!
|
||||
```
|
||||
DEBUG=1
|
||||
```
|
||||
|
||||
{{< alert icon="👉" text="API responses and webhook payloads may differ significantly! We do our best to keep requests body stable between engines." />}}
|
||||
#### Protecting the API with a Secret Code
|
||||
|
||||
### WhatsApp WebJS 
|
||||
To protect the API with a secret code, set the `WHATSAPP_API_KEY` environment variable to your secret code:
|
||||
|
||||
**WAHA** uses **WhatsApp WebJS** engine by default
|
||||
```
|
||||
WHATSAPP_API_KEY=mysecret
|
||||
```
|
||||
|
||||
- https://github.com/pedroslopez/whatsapp-web.js
|
||||
- `WHATSAPP_DEFAULT_ENGINE=WEBJS`
|
||||
You must include the `X-Api-Key: mysecret` header in all requests to the API.
|
||||
|
||||
### Venom 
|
||||
#### Starting Sessions Automatically
|
||||
|
||||
- https://github.com/orkestral/venom
|
||||
- `WHATSAPP_DEFAULT_ENGINE=VENOM`
|
||||
To start sessions automatically when the API is launched, set the `WHATSAPP_START_SESSION` environment variable to a
|
||||
comma-separated list of session names:
|
||||
|
||||
```
|
||||
WHATSAPP_START_SESSION=session1,session2
|
||||
```
|
||||
|
||||
#### Restarting All Sessions
|
||||
|
||||
To start all **STOPPED** sessions after container restarts, set the `WHATSAPP_RESTART_ALL_SESSIONS` environment variable
|
||||
to `True`:
|
||||
|
||||
```
|
||||
WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
```
|
||||
|
||||
#### Protecting the Swagger Panel
|
||||
|
||||
To protect the Swagger panel with `admin / admin` credentials, set the `WHATSAPP_SWAGGER_USERNAME`
|
||||
and `WHATSAPP_SWAGGER_PASSWORD` environment variables:
|
||||
|
||||
```
|
||||
WHATSAPP_SWAGGER_USERNAME=admin
|
||||
WHATSAPP_SWAGGER_PASSWORD=admin
|
||||
```
|
||||
|
||||
## File storage variables 
|
||||
|
||||
|
||||
The following environment variables can be used to configure the file storage options for the WAHA:
|
||||
|
||||
- `WHATSAPP_FILES_MIMETYPES`: This variable can be used to download only specific mimetypes from messages. By default,
|
||||
all files are downloaded. The mimetypes must be separated by a comma, without spaces. For
|
||||
example: `audio,image/png,image/gif`. To choose a specific type, use a prefix (like `audio,image`).
|
||||
- `WHATSAPP_FILES_LIFETIME`: This variable can be used to set the time (in seconds) after which files will be removed to
|
||||
free up space. The default value is `180`.
|
||||
- `WHATSAPP_FILES_FOLDER`: This variable can be used to set the folder where files from chats (images, voice messages)
|
||||
will be stored. The default value is `/tmp/whatsapp-files`.
|
||||
|
||||
### Examples
|
||||
|
||||
#### Downloading Specific Mimetypes
|
||||
|
||||
To download only specific mimetypes from messages, set the `WHATSAPP_FILES_MIMETYPES` environment variable to a
|
||||
comma-separated list of mimetypes:
|
||||
|
||||
```
|
||||
WHATSAPP_FILES_MIMETYPES=audio,image/png,image/gif
|
||||
```
|
||||
|
||||
#### Setting the File Lifetime
|
||||
|
||||
To set the time (in seconds) after which files will be removed to free up space, set the `WHATSAPP_FILES_LIFETIME`
|
||||
environment variable:
|
||||
|
||||
```
|
||||
WHATSAPP_FILES_LIFETIME=300
|
||||
```
|
||||
|
||||
#### Setting the File Storage Folder
|
||||
|
||||
To set the folder where files from chats (images, voice messages) will be stored, set the `WHATSAPP_FILES_FOLDER`
|
||||
environment variable:
|
||||
|
||||
```
|
||||
WHATSAPP_FILES_FOLDER=/home/user/whatsapp-files
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: "Deploy"
|
||||
description: "How to deploy WAHA"
|
||||
lead: ""
|
||||
date: 2020-10-06T08:48:45+00:00
|
||||
lastmod: 2020-10-06T08:48:45+00:00
|
||||
draft: false
|
||||
images: [ ]
|
||||
weight: 801
|
||||
---
|
||||
|
||||
This page provides useful information to assist you in deploying the project.
|
||||
It includes tips on how to avoid running long command lines, how to view logs, and other helpful advice.
|
||||
|
||||
## How to Use Docker Compose
|
||||
|
||||
Docker Compose is a tool that allows you to define and run multi-container Docker applications. It simplifies the
|
||||
process of managing multiple containers and their dependencies. In this topic, we will walk you through how to use
|
||||
Docker Compose with the WhatsApp HTTP API.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you start, you need to have Docker and Docker Compose installed on your machine. You can download Docker from the
|
||||
official website [here](https://www.docker.com/products/docker-desktop). Docker Compose is included with Docker Desktop
|
||||
for Windows and macOS, but for Linux, you need to install it separately. You can find the installation
|
||||
instructions [here](https://docs.docker.com/compose/install/).
|
||||
|
||||
### Getting Started
|
||||
|
||||
To get started, you need to clone the docker-compose file to your local machine.
|
||||
Open your terminal and run the following command:
|
||||
|
||||
```
|
||||
wget https://raw.githubusercontent.com/devlikeapro/whatsapp-http-api/core/docker-compose.yaml
|
||||
```
|
||||
|
||||
### Running the WhatsApp HTTP API Service
|
||||
|
||||
To start the `whatsapp-http-api` service, run the following command:
|
||||
|
||||
```
|
||||
docker-compose up -d whatsapp-http-api
|
||||
```
|
||||
|
||||
This will start the container in detached mode and map port 3000 to 3000 on the host machine.
|
||||
|
||||
To stop the service, run the command:
|
||||
|
||||
```
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
This will stop and remove the container.
|
||||
|
||||
To restart the service, run the command:
|
||||
|
||||
```
|
||||
docker-compose restart whtasapp-http-api
|
||||
```
|
||||
|
||||
This will restart the container.
|
||||
|
||||
### Viewing the Logs
|
||||
|
||||
To view the logs for the `whatsapp-http-api` service, run the command:
|
||||
|
||||
```
|
||||
docker-compose logs -f whatsapp-http-api
|
||||
```
|
||||
|
||||
This will show the logs in real-time. To view the logs for the `whatsapp-http-api-plus` service,
|
||||
replace `whatsapp-http-api` with `whatsapp-http-api-plus`.
|
||||
|
||||
To view the logs for a specific period, run the command:
|
||||
|
||||
```
|
||||
docker-compose logs --since <time>
|
||||
```
|
||||
|
||||
Replace `<time>` with the time period you want to view the logs for. For example, `docker-compose logs --since 1h` will
|
||||
show the logs for the past hour.
|
||||
|
||||
### Using the WhatsApp HTTP API Services
|
||||
|
||||
There are two services provided in the `docker-compose.yml` file - `whatsapp-http-api` and `whatsapp-http-api-plus`.
|
||||
It is important to note that these services must not be run at the same time.
|
||||
By default, the `whatsapp-http-api` service will be used.
|
||||
|
||||
To use the `whatsapp-http-api-plus` service, you need to specify it in the command.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "Engines"
|
||||
description: "Engines"
|
||||
lead: ""
|
||||
date: 2020-10-06T08:48:45+00:00
|
||||
lastmod: 2020-10-06T08:48:45+00:00
|
||||
draft: false
|
||||
images: [ ]
|
||||
weight: 800
|
||||
---
|
||||
|
||||
Under the hood **WAHA** allows you to use two different engines. You can control what you want to run by settings
|
||||
`WHATSAPP_DEFAULT_ENGINE` environment variables.
|
||||
|
||||
```bash
|
||||
docker run -it -e "WHATSAPP_DEFAULT_ENGINE=WEBJS" devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
If you have any problems with one engine - try another!
|
||||
|
||||
{{< alert icon="👉" text="API responses and webhook payloads may differ significantly! We do our best to keep requests body stable between engines." />}}
|
||||
|
||||
All engines are available in Core version 
|
||||
|
||||
## WEBJS
|
||||
|
||||
A WhatsApp API client that connects through the WhatsApp Web browser app.
|
||||
It uses Puppeteer to run a real instance of Whatsapp Web to avoid getting blocked.
|
||||
|
||||
**WAHA** uses **WhatsApp WebJS** engine by default.
|
||||
|
||||
- `WHATSAPP_DEFAULT_ENGINE=WEBJS`
|
||||
- https://github.com/pedroslopez/whatsapp-web.js
|
||||
|
||||
## NOWEB
|
||||
|
||||
**NOWEB** engine does not require a browser to work with WhatsApp Web, it does so directly using a WebSocket.
|
||||
Not running Chromium saves you CPU and Memory, so you can run more instances on a single server!
|
||||
|
||||
️Working with the engine requires more attention, because it's easy to be blocked with it!
|
||||
|
||||
⚠ Read the article before using **NOWEB** engine [How to avoid blocking ->]({{< relref "/docs/help/how-to-avoid-blocking" >}}).
|
||||
|
||||
- `WHATSAPP_DEFAULT_ENGINE=NOWEB`
|
||||
- https://github.com/WhiskeySockets/Baileys
|
||||
|
||||
## VENOM
|
||||
|
||||
It's a high-performance system developed with JavaScript to create a bot for WhatsApp
|
||||
|
||||
- `WHATSAPP_DEFAULT_ENGINE=VENOM`
|
||||
- https://github.com/orkestral/venom
|
||||
@@ -32,8 +32,7 @@ The Plus additional features are:
|
||||
4. [**Security** →]({{< relref "/docs/how-to/security" >}})
|
||||
5. [**Saving sessions** →]({{< relref "/docs/how-to/sessions" >}})
|
||||
6. [**Support multiple sessions** →]({{< relref "/docs/how-to/sessions" >}})
|
||||
7. **Instant updates** - no one month delay in getting bugfixes or new features
|
||||
8. **Priority in support** - on both bugs and features
|
||||
7. **Priority in support** - on both bugs and features
|
||||
|
||||
## Differences
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ On the URL that you set via `WHATSAPP_HOOK_URL` you receive JSON-data with follo
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"engine": "WEBJS",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -50,7 +51,7 @@ Below the list of all events that WhatsApp HTTP API sends to your.
|
||||
Run the bellow command and see look at the logs - it prints body request for all events that happen in your WhatsApp!
|
||||
|
||||
```bash
|
||||
docker run -it -e "WHATSAPP_HOOK_EVENTS=*" -e WHATSAPP_HOOK_URL=https://httpbin.org/post devlikeapro/whatsapp-http-api
|
||||
docker run -it -e "WHATSAPP_HOOK_EVENTS=*" -e WHATSAPP_HOOK_URL=https://httpbin.org/post -p 3000:3000 devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -26,7 +26,7 @@ to the container. WAHA stores authentication information in the directory and re
|
||||
|
||||
The full command would be:
|
||||
```bash
|
||||
docker run --rm -d -v `pwd`/.sessions:/app/.sessions -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
docker run --rm -d -v `pwd`/.sessions:/app/.sessions -p 3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
### Remote storage 
|
||||
@@ -69,11 +69,26 @@ The response:
|
||||
|
||||
In order to stop a new session - call `POST /api/sessions/stop`
|
||||
|
||||
{{< alert icon="👉" text="The stop request does not log out the account. Manually remove the session storage." />}}
|
||||
{{< alert icon="👉" text="The stop request does not log out the account by default. Set 'logout' field to 'true'." />}}
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "default"
|
||||
"name": "default",
|
||||
"logout": true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Logout
|
||||
|
||||
In order to log out the session - call `POST /api/sessions/logout`
|
||||
|
||||
{{< alert icon="👉" text="You must stop session first." />}}
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "default",
|
||||
"logout": true
|
||||
}
|
||||
```
|
||||
|
||||
@@ -72,9 +72,7 @@ Here's the available features:
|
||||
- Swagger panel authentication 
|
||||
|
||||
### Updates
|
||||
- Bug fixes and updates come as soon as they are implemented. 
|
||||
- One month delay in bug fixes and updates. 
|
||||
|
||||
- Bug fixes and updates come as soon as they are implemented. 
|
||||
|
||||
### Groups 
|
||||
- Create a group
|
||||
|
||||
@@ -95,7 +95,7 @@ docker tag devlikeapro/whatsapp-http-api-plus:arm devlikeapro/whatsapp-http-api
|
||||
Run WhatsApp HTTP API:
|
||||
|
||||
```bash
|
||||
docker run -it --rm -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
docker run -it --rm -p 3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
|
||||
|
||||
# It prints logs and the last line must be
|
||||
# WhatsApp HTTP API is running on: http://[::1]:3000
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"openapi":"3.0.0","paths":{"/api/sessions/start":{"post":{"operationId":"SessionsController_start","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStartRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDTO"}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/stop":{"post":{"operationId":"SessionsController_stop","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStopRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions":{"get":{"operationId":"SessionsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SessionDTO"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/checkNumberStatus":{"get":{"operationId":"ChattingController_checkNumberStatus","summary":"Check number status","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WANumberExistResult"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcardRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendText":{"get":{"operationId":"ChattingController_sendTextGet","summary":"Send a text message","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"text","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]},"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WAMessage"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendTextButtons":{"post":{"operationId":"ChattingController_sendTextButtons","summary":"Send a text message with buttons","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextButtonsRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendLocation":{"post":{"operationId":"ChattingController_sendLocation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLocationRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendLinkPreview":{"post":{"operationId":"ChattingController_sendLinkPreview","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLinkPreviewRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendImage":{"post":{"operationId":"ChattingController_sendImage","summary":"Send an image. Either from an URL or base64 data - look at the request schemas for details.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageImageRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendFile":{"post":{"operationId":"ChattingController_sendFile","summary":"Send a file. Either from an URL or base64 data - look at the request schemas for details.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFileRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendVoice":{"post":{"operationId":"ChattingController_sendVoice","summary":"Send an voice message. Either from an URL or basLine truncated
|
||||
{"openapi":"3.0.0","paths":{"/api/sessions/start":{"post":{"operationId":"SessionsController_start","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStartRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDTO"}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/stop":{"post":{"operationId":"SessionsController_stop","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStopRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions":{"get":{"operationId":"SessionsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SessionDTO"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/checkNumberStatus":{"get":{"operationId":"ChattingController_checkNumberStatus","summary":"Check number status","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WANumberExistResult"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcardRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendText":{"get":{"operationId":"ChattingController_sendTextGet","summary":"Send a text message","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"text","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]},"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WAMessage"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendTextButtons":{"post":{"operationId":"ChattingController_sendTextButtons","summary":"Send a text message with buttons","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextButtonsRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendLocation":{"post":{"operationId":"ChattingController_sendLocation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLocationRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendLinkPreview":{"post":{"operationId":"ChattingController_sendLinkPreview","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLinkPreviewRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendImage":{"post":{"operationId":"ChattingController_sendImage","summary":"Send an image. Either from an URL or base64 data - look at the request schemas for details.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageImageRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendFile":{"post":{"operationId":"ChattingController_sendFile","summary":"Send a file. Either from an URL or base64 data - look at the request schemas for details.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFileRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendVoice":{"post":{"operationId":"ChattingController_sendVoice","summary":"Send an voice message. Either from an URL or basLine truncated
|
||||
@@ -1,2 +1,3 @@
|
||||
flask~=1.0
|
||||
requests~=2.0
|
||||
markupsafe==2.0.1
|
||||
@@ -25,6 +25,17 @@ def send_message(chat_id, text):
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def send_seen(chat_id, message_id, participant):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendSeen",
|
||||
json={
|
||||
"session": "default",
|
||||
"chatId": chat_id,
|
||||
"messageId": message_id,
|
||||
"participant": participant,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
@app.route("/")
|
||||
def whatsapp_echo():
|
||||
@@ -48,7 +59,13 @@ def whatsapp_webhook():
|
||||
return "No files in the message"
|
||||
|
||||
# Number in format 791111111@c.us
|
||||
from_ = payload["from"]
|
||||
chat_id = payload["from"]
|
||||
# Message ID - false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
message_id = payload['id']
|
||||
# For groups - who sent the message
|
||||
participant = payload.get('participant')
|
||||
# IMPORTANT - Always send seen before sending new message
|
||||
send_seen(chat_id=chat_id, message_id=message_id, participant=participant)
|
||||
|
||||
# Download the file and download it to the current folder
|
||||
client_url = payload["mediaUrl"]
|
||||
@@ -61,7 +78,7 @@ def whatsapp_webhook():
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
text = f"We have downloaded file here: {path}"
|
||||
print(text)
|
||||
send_message(chat_id=from_, text=text)
|
||||
send_message(chat_id=chat_id, text=text)
|
||||
|
||||
# Send OK back
|
||||
return "OK"
|
||||
@@ -24,6 +24,31 @@ def send_message(chat_id, text):
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def reply(chat_id, message_id, text):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/reply",
|
||||
json={
|
||||
"chatId": chat_id,
|
||||
"text": text,
|
||||
"reply_to": message_id,
|
||||
"session": "default",
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def send_seen(chat_id, message_id, participant):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendSeen",
|
||||
json={
|
||||
"session": "default",
|
||||
"chatId": chat_id,
|
||||
"messageId": message_id,
|
||||
"participant": participant,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def whatsapp_echo():
|
||||
@@ -45,9 +70,19 @@ def whatsapp_webhook():
|
||||
payload = data["payload"]
|
||||
# The text
|
||||
text = payload["body"]
|
||||
# Number in format 1231231231@c.us
|
||||
from_ = payload["from"]
|
||||
# Number in format 1231231231@c.us or @g.us for group
|
||||
chat_id = payload["from"]
|
||||
# Message ID - false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
message_id = payload['id']
|
||||
# For groups - who sent the message
|
||||
participant = payload.get('participant')
|
||||
# IMPORTANT - Always send seen before sending new message
|
||||
send_seen(chat_id=chat_id, message_id=message_id, participant=participant)
|
||||
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
send_message(chat_id=from_, text=text)
|
||||
send_message(chat_id=chat_id, text=text)
|
||||
# OR reply on the message
|
||||
# reply(chat_id=chat_id, message_id=message_id, text=text)
|
||||
|
||||
# Send OK back
|
||||
return "OK"
|
||||
+9
-1
@@ -22,6 +22,8 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adiwajshing/baileys": "file:../WhiskeySockets-Baileys",
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@nestjs/common": "^9.0.9",
|
||||
"@nestjs/config": "^0.5.0",
|
||||
"@nestjs/core": "^9.0.9",
|
||||
@@ -29,12 +31,18 @@
|
||||
"@nestjs/platform-express": "^9.0.9",
|
||||
"@nestjs/serve-static": "^2.1.3",
|
||||
"@nestjs/swagger": "^6.0.5",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/ws": "^8.5.4",
|
||||
"class-validator": "^0.12.2",
|
||||
"del": "^6.0.0",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"file-type": "16.5.4",
|
||||
"link-preview-js": "^3.0.4",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.27",
|
||||
"passport": "^0.6.0",
|
||||
"passport-headerapikey": "^1.2.2",
|
||||
"qrcode": "^1.5.1",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "^2.88.2",
|
||||
@@ -42,7 +50,7 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.1.0",
|
||||
"swagger-ui-express": "^4.1.4",
|
||||
"venom-bot": "^4.3.7",
|
||||
"venom-bot": "^5.0.1",
|
||||
"whatsapp-web.js": "https://github.com/pedroslopez/whatsapp-web.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+129
-112
@@ -1,134 +1,151 @@
|
||||
import {Body, Controller, Get, Post, Put, Query} from '@nestjs/common';
|
||||
import {ApiOperation, ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import { Body, Controller, Get, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextQuery,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest
|
||||
} from "../structures/chatting.dto";
|
||||
import {WAMessage, WANumberExistResult} from "../structures/responses.dto";
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextQuery,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest,
|
||||
SendSeenRequest,
|
||||
} from '../structures/chatting.dto';
|
||||
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api')
|
||||
@ApiTags('chatting')
|
||||
export class ChattingController {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/checkNumberStatus')
|
||||
@ApiOperation({summary: 'Check number status'})
|
||||
async checkNumberStatus(@Query() request: CheckNumberStatusQuery): Promise<WANumberExistResult> {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.checkNumberStatus(request)
|
||||
}
|
||||
@Get('/checkNumberStatus')
|
||||
@ApiOperation({ summary: 'Check number status' })
|
||||
async checkNumberStatus(
|
||||
@Query() request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.checkNumberStatus(request);
|
||||
}
|
||||
|
||||
@Post('/sendContactVcard')
|
||||
sendContactVcard(@Body() request: MessageContactVcardRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendContactVCard(request)
|
||||
}
|
||||
@Post('/sendContactVcard')
|
||||
sendContactVcard(@Body() request: MessageContactVcardRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendContactVCard(request);
|
||||
}
|
||||
|
||||
@Get('/sendText')
|
||||
@ApiOperation({summary: 'Send a text message'})
|
||||
sendTextGet(
|
||||
@Query() query: MessageTextQuery,
|
||||
) {
|
||||
const whatsapp = this.manager.getSession(query.session)
|
||||
const msg = new MessageTextRequest()
|
||||
msg.chatId = query.phone
|
||||
msg.text = query.text
|
||||
return whatsapp.sendText(msg)
|
||||
}
|
||||
@Get('/sendText')
|
||||
@ApiOperation({ summary: 'Send a text message' })
|
||||
sendTextGet(@Query() query: MessageTextQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
const msg = new MessageTextRequest();
|
||||
msg.chatId = query.phone;
|
||||
msg.text = query.text;
|
||||
return whatsapp.sendText(msg);
|
||||
}
|
||||
|
||||
@Post('/sendText')
|
||||
@ApiOperation({summary: 'Send a text message'})
|
||||
sendText(@Body() request: MessageTextRequest): Promise<WAMessage> {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendText(request)
|
||||
}
|
||||
@Post('/sendText')
|
||||
@ApiOperation({ summary: 'Send a text message' })
|
||||
sendText(@Body() request: MessageTextRequest): Promise<WAMessage> {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendText(request);
|
||||
}
|
||||
|
||||
@Post('/sendTextButtons')
|
||||
@ApiOperation({summary: 'Send a text message with buttons'})
|
||||
sendTextButtons(@Body() request: MessageTextButtonsRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendTextButtons(request)
|
||||
}
|
||||
@Post('/sendTextButtons')
|
||||
@ApiOperation({ summary: 'Send a text message with buttons' })
|
||||
sendTextButtons(@Body() request: MessageTextButtonsRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendTextButtons(request);
|
||||
}
|
||||
|
||||
@Post('/sendLocation')
|
||||
sendLocation(@Body() request: MessageLocationRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendLocation(request)
|
||||
}
|
||||
@Post('/sendLocation')
|
||||
sendLocation(@Body() request: MessageLocationRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendLocation(request);
|
||||
}
|
||||
|
||||
@Post('/sendLinkPreview')
|
||||
sendLinkPreview(@Body() request: MessageLinkPreviewRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendLinkPreview(request)
|
||||
}
|
||||
@Post('/sendLinkPreview')
|
||||
sendLinkPreview(@Body() request: MessageLinkPreviewRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendLinkPreview(request);
|
||||
}
|
||||
|
||||
@Post('/sendImage')
|
||||
@ApiOperation({summary: "Send an image. Either from an URL or base64 data - look at the request schemas for details."})
|
||||
sendImage(@Body() request: MessageImageRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendImage(request)
|
||||
}
|
||||
@Post('/sendImage')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Send an image. Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendImage(@Body() request: MessageImageRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendImage(request);
|
||||
}
|
||||
|
||||
@Post('/sendFile')
|
||||
@ApiOperation({summary: "Send a file. Either from an URL or base64 data - look at the request schemas for details."})
|
||||
sendFile(@Body() request: MessageFileRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendFile(request)
|
||||
}
|
||||
@Post('/sendFile')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Send a file. Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendFile(@Body() request: MessageFileRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendFile(request);
|
||||
}
|
||||
|
||||
@Post('/sendVoice')
|
||||
@ApiOperation({summary: "Send an voice message. Either from an URL or base64 data - look at the request schemas for details."})
|
||||
sendVoice(@Body() request: MessageVoiceRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.sendVoice(request)
|
||||
}
|
||||
@Post('/sendVoice')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Send an voice message. Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendVoice(@Body() request: MessageVoiceRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendVoice(request);
|
||||
}
|
||||
|
||||
@Post('/reply')
|
||||
@ApiOperation({summary: 'Reply to a text message'})
|
||||
reply(@Body() request: MessageReplyRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.reply(request)
|
||||
}
|
||||
@Post('/reply')
|
||||
@ApiOperation({ summary: 'Reply to a text message' })
|
||||
reply(@Body() request: MessageReplyRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.reply(request);
|
||||
}
|
||||
|
||||
@Post('/sendSeen')
|
||||
sendSeen(@Body() chat: ChatRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session)
|
||||
return whatsapp.sendSeen(chat)
|
||||
}
|
||||
@Post('/sendSeen')
|
||||
sendSeen(@Body() chat: SendSeenRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session);
|
||||
return whatsapp.sendSeen(chat);
|
||||
}
|
||||
|
||||
@Post('/startTyping')
|
||||
async startTyping(@Body() chat: ChatRequest) {
|
||||
// It's infinitive action
|
||||
const whatsapp = this.manager.getSession(chat.session)
|
||||
await whatsapp.startTyping(chat)
|
||||
return {result: true}
|
||||
}
|
||||
@Post('/startTyping')
|
||||
async startTyping(@Body() chat: ChatRequest) {
|
||||
// It's infinitive action
|
||||
const whatsapp = this.manager.getSession(chat.session);
|
||||
await whatsapp.startTyping(chat);
|
||||
return { result: true };
|
||||
}
|
||||
|
||||
@Post('/stopTyping')
|
||||
async stopTyping(@Body() chat: ChatRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session)
|
||||
await whatsapp.stopTyping(chat)
|
||||
return {result: true}
|
||||
}
|
||||
@Post('/stopTyping')
|
||||
async stopTyping(@Body() chat: ChatRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session);
|
||||
await whatsapp.stopTyping(chat);
|
||||
return { result: true };
|
||||
}
|
||||
|
||||
@Put('/reaction')
|
||||
@ApiOperation({summary: 'React to a message with an emoji'})
|
||||
setReaction(@Body() request: MessageReactionRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.setReaction(request)
|
||||
}
|
||||
@Put('/reaction')
|
||||
@ApiOperation({ summary: 'React to a message with an emoji' })
|
||||
setReaction(@Body() request: MessageReactionRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.setReaction(request);
|
||||
}
|
||||
|
||||
@Get('/messages')
|
||||
@ApiOperation({ summary: 'Get messages in a chat' })
|
||||
getMessages(@Query() query: GetMessageQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getMessages(query);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,73 @@
|
||||
import {Body, Controller, Get, Post, Query} from '@nestjs/common';
|
||||
import {ApiOperation, ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
import {ContactQuery, ContactRequest} from "../structures/contacts.dto";
|
||||
import {SessionQuery} from "../structures/base.dto";
|
||||
import {CheckNumberStatusQuery} from "../structures/chatting.dto";
|
||||
import {WANumberExistResult} from "../structures/responses.dto";
|
||||
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
import { SessionQuery } from '../structures/base.dto';
|
||||
import { CheckNumberStatusQuery } from '../structures/chatting.dto';
|
||||
import { WANumberExistResult } from '../structures/responses.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/contacts')
|
||||
@ApiTags('contacts')
|
||||
export class ContactsController {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/')
|
||||
@ApiOperation({summary: 'Get contact basic info. The method always return result, even if the phone number is not registered in WhatsApp. For that - use /check-exists endpoint below.'})
|
||||
get(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session)
|
||||
return whatsapp.getContact(query)
|
||||
}
|
||||
@Get('/')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Get contact basic info. The method always return result, even if the phone number is not registered in WhatsApp. For that - use /check-exists endpoint below.',
|
||||
})
|
||||
get(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContact(query);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
@ApiOperation({summary: 'Get all contacts'})
|
||||
getAll(@Query() query: SessionQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session)
|
||||
return whatsapp.getContacts()
|
||||
}
|
||||
@Get('/all')
|
||||
@ApiOperation({ summary: 'Get all contacts' })
|
||||
getAll(@Query() query: SessionQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContacts();
|
||||
}
|
||||
|
||||
@Get('/check-exists')
|
||||
@ApiOperation({summary: 'Check phone number is registered in WhatsApp.'})
|
||||
async checkExists(@Query() request: CheckNumberStatusQuery): Promise<WANumberExistResult> {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.checkNumberStatus(request)
|
||||
}
|
||||
@Get('/check-exists')
|
||||
@ApiOperation({ summary: 'Check phone number is registered in WhatsApp.' })
|
||||
async checkExists(
|
||||
@Query() request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.checkNumberStatus(request);
|
||||
}
|
||||
|
||||
@Get('/about')
|
||||
@ApiOperation({summary: 'Gets the Contact\'s current "about" info. Returns null if you don\'t have permission to read their status.'})
|
||||
getAbout(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session)
|
||||
return whatsapp.getContactAbout(query)
|
||||
}
|
||||
@Get('/about')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Gets the Contact\'s current "about" info. Returns null if you don\'t have permission to read their status.',
|
||||
})
|
||||
getAbout(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContactAbout(query);
|
||||
}
|
||||
|
||||
@Get('/profile-picture')
|
||||
@ApiOperation({summary: 'Returns the contact\'s profile picture URL, if privacy settings allow it.'})
|
||||
getProfilePicture(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session)
|
||||
return whatsapp.getContactProfilePicture(query)
|
||||
}
|
||||
@Get('/profile-picture')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
"Returns the contact's profile picture URL, if privacy settings allow it.",
|
||||
})
|
||||
getProfilePicture(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContactProfilePicture(query);
|
||||
}
|
||||
|
||||
@Post('/block')
|
||||
@ApiOperation({summary:"Block contact"})
|
||||
block(@Body() request: ContactRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.blockContact(request)
|
||||
}
|
||||
@Post('/unblock')
|
||||
@ApiOperation({summary:"Unblock contact"})
|
||||
unblock(@Body() request: ContactRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session)
|
||||
return whatsapp.unblockContact(request)
|
||||
}
|
||||
@Post('/block')
|
||||
@ApiOperation({ summary: 'Block contact' })
|
||||
block(@Body() request: ContactRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.blockContact(request);
|
||||
}
|
||||
@Post('/unblock')
|
||||
@ApiOperation({ summary: 'Unblock contact' })
|
||||
unblock(@Body() request: ContactRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.unblockContact(request);
|
||||
}
|
||||
}
|
||||
+35
-35
@@ -1,6 +1,12 @@
|
||||
import {ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus,} from '@nestjs/common';
|
||||
import {Request, Response} from 'express';
|
||||
import {VERSION} from "../version";
|
||||
import {
|
||||
ArgumentsHost,
|
||||
Catch,
|
||||
ExceptionFilter,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { VERSION } from '../version';
|
||||
|
||||
/**
|
||||
* Serializer error to JSON.
|
||||
@@ -8,42 +14,36 @@ import {VERSION} from "../version";
|
||||
* Credits: https://stackoverflow.com/a/72707578/6753144
|
||||
*/
|
||||
export function serializeError(err: unknown) {
|
||||
const properties = Object.getOwnPropertyNames(err)
|
||||
// getOwnPropertyNames does not get 'name', like "ReferenceError"
|
||||
properties.push('name')
|
||||
return JSON.parse(JSON.stringify(err, properties))
|
||||
const properties = Object.getOwnPropertyNames(err);
|
||||
// getOwnPropertyNames does not get 'name', like "ReferenceError"
|
||||
properties.push('name');
|
||||
return JSON.parse(JSON.stringify(err, properties));
|
||||
}
|
||||
|
||||
@Catch()
|
||||
export class AllExceptionsFilter implements ExceptionFilter {
|
||||
catch(exception: HttpException | Error, host: ArgumentsHost): void {
|
||||
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
const request = ctx.getRequest<Request>();
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
response
|
||||
.status(exception.getStatus())
|
||||
.json(exception.getResponse());
|
||||
return
|
||||
}
|
||||
|
||||
const httpStatus = HttpStatus.INTERNAL_SERVER_ERROR
|
||||
response
|
||||
.status(httpStatus)
|
||||
.json({
|
||||
statusCode: httpStatus,
|
||||
timestamp: new Date().toISOString(),
|
||||
exception: serializeError(exception),
|
||||
request: {
|
||||
path: request.url,
|
||||
method: request.method,
|
||||
body: request.body,
|
||||
query: request.query,
|
||||
},
|
||||
version: VERSION,
|
||||
});
|
||||
catch(exception: HttpException | Error, host: ArgumentsHost): void {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
const request = ctx.getRequest<Request>();
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
response.status(exception.getStatus()).json(exception.getResponse());
|
||||
return;
|
||||
}
|
||||
|
||||
const httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
response.status(httpStatus).json({
|
||||
statusCode: httpStatus,
|
||||
timestamp: new Date().toISOString(),
|
||||
exception: serializeError(exception),
|
||||
request: {
|
||||
path: request.url,
|
||||
method: request.method,
|
||||
body: request.body,
|
||||
query: request.query,
|
||||
},
|
||||
version: VERSION,
|
||||
});
|
||||
}
|
||||
}
|
||||
+153
-145
@@ -1,165 +1,173 @@
|
||||
import {Body, Controller, Delete, Get, Param, Post, Put} from '@nestjs/common';
|
||||
import {ApiOperation, ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
import {CreateGroupRequest, DescriptionRequest, ParticipantsRequest, SubjectRequest} from "../structures/groups.dto";
|
||||
import {WhatsappSession} from "../core/abc/session.abc";
|
||||
import {SessionApiParam, SessionParam} from "./helpers";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
DescriptionRequest,
|
||||
ParticipantsRequest,
|
||||
SubjectRequest,
|
||||
} from '../structures/groups.dto';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/groups')
|
||||
@ApiTags('groups')
|
||||
export class GroupsController {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Post('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Create a new group."})
|
||||
createGroup(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: CreateGroupRequest,
|
||||
) {
|
||||
return session.createGroup(request)
|
||||
}
|
||||
@Post('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Create a new group.' })
|
||||
createGroup(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: CreateGroupRequest,
|
||||
) {
|
||||
return session.createGroup(request);
|
||||
}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Get all groups."})
|
||||
getGroups(
|
||||
@SessionParam session: WhatsappSession,
|
||||
) {
|
||||
return session.getGroups()
|
||||
}
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get all groups.' })
|
||||
getGroups(@SessionParam session: WhatsappSession) {
|
||||
return session.getGroups();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Get the group."})
|
||||
getGroup(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return session.getGroup(id)
|
||||
}
|
||||
@Get(':id')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get the group.' })
|
||||
getGroup(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.getGroup(id);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Delete the group."})
|
||||
deleteGroup(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return session.deleteGroup(id)
|
||||
}
|
||||
@Delete(':id')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Delete the group.' })
|
||||
deleteGroup(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.deleteGroup(id);
|
||||
}
|
||||
|
||||
@Post(':id/leave')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Leave the group."})
|
||||
leaveGroup(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return session.leaveGroup(id)
|
||||
}
|
||||
@Post(':id/leave')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Leave the group.' })
|
||||
leaveGroup(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.leaveGroup(id);
|
||||
}
|
||||
|
||||
@Put(':id/description')
|
||||
@ApiOperation({
|
||||
summary: "Updates the group description.\n" +
|
||||
"Returns true if the subject was properly updated. This can return false if the user does not have the necessary permissions.\n"
|
||||
})
|
||||
@SessionApiParam
|
||||
setDescription(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: DescriptionRequest
|
||||
) {
|
||||
return session.setDescription(id, request.description)
|
||||
}
|
||||
@Put(':id/description')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Updates the group description.\n' +
|
||||
'Returns true if the subject was properly updated. This can return false if the user does not have the necessary permissions.\n',
|
||||
})
|
||||
@SessionApiParam
|
||||
setDescription(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: DescriptionRequest,
|
||||
) {
|
||||
return session.setDescription(id, request.description);
|
||||
}
|
||||
|
||||
@Put(':id/subject')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: "Updates the group subject.\n" +
|
||||
"Returns true if the subject was properly updated. This can return false if the user does not have the necessary permissions.\n"
|
||||
})
|
||||
setSubject(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: SubjectRequest
|
||||
) {
|
||||
return session.setSubject(id, request.subject)
|
||||
}
|
||||
@Put(':id/subject')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Updates the group subject.\n' +
|
||||
'Returns true if the subject was properly updated. This can return false if the user does not have the necessary permissions.\n',
|
||||
})
|
||||
setSubject(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: SubjectRequest,
|
||||
) {
|
||||
return session.setSubject(id, request.subject);
|
||||
}
|
||||
|
||||
@Get(':id/invite-code')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Gets the invite code for a specific group."})
|
||||
getInviteCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<string> {
|
||||
return session.getInviteCode(id)
|
||||
}
|
||||
@Get(':id/invite-code')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Gets the invite code for a specific group.' })
|
||||
getInviteCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<string> {
|
||||
return session.getInviteCode(id);
|
||||
}
|
||||
|
||||
@Post(':id/invite-code/revoke')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: "Invalidates the current group invite code and generates a new one."})
|
||||
revokeInviteCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<string> {
|
||||
return session.revokeInviteCode(id)
|
||||
}
|
||||
@Post(':id/invite-code/revoke')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Invalidates the current group invite code and generates a new one.',
|
||||
})
|
||||
revokeInviteCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<string> {
|
||||
return session.revokeInviteCode(id);
|
||||
}
|
||||
|
||||
@Get(':id/participants/')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: 'Get a list of participants by in the group.'})
|
||||
getParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return session.getParticipants(id)
|
||||
}
|
||||
@Get(':id/participants/')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get a list of participants by in the group.' })
|
||||
getParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return session.getParticipants(id);
|
||||
}
|
||||
|
||||
@Post(':id/participants/add')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: 'Adds a list of participants by ID to the group.'})
|
||||
addParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest
|
||||
) {
|
||||
return session.addParticipants(id, request)
|
||||
}
|
||||
@Post(':id/participants/add')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Adds a list of participants by ID to the group.' })
|
||||
addParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.addParticipants(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/participants/remove')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: 'Removes a list of participants by ID to the group.'})
|
||||
removeParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest
|
||||
) {
|
||||
return session.removeParticipants(id, request)
|
||||
}
|
||||
@Post(':id/participants/remove')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Removes a list of participants by ID to the group.',
|
||||
})
|
||||
removeParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.removeParticipants(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/admin/promote')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: 'Promote participants to admin users.'})
|
||||
promoteToAdmin(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest
|
||||
) {
|
||||
return session.promoteParticipantsToAdmin(id, request)
|
||||
}
|
||||
@Post(':id/admin/promote')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Promote participants to admin users.' })
|
||||
promoteToAdmin(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.promoteParticipantsToAdmin(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/admin/demote')
|
||||
@SessionApiParam
|
||||
@ApiOperation({summary: 'Demotes participants by to regular users.'})
|
||||
demoteToAdmin(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest
|
||||
) {
|
||||
return session.demoteParticipantsToUser(id, request)
|
||||
}
|
||||
@Post(':id/admin/demote')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Demotes participants by to regular users.' })
|
||||
demoteToAdmin(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.demoteParticipantsToUser(id, request);
|
||||
}
|
||||
}
|
||||
+14
-15
@@ -1,7 +1,7 @@
|
||||
import {ApiParam} from "@nestjs/swagger";
|
||||
import {Injectable, Param, PipeTransform} from "@nestjs/common";
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
import {WhatsappSession} from "../core/abc/session.abc";
|
||||
import { ApiParam } from '@nestjs/swagger';
|
||||
import { Injectable, Param, PipeTransform } from '@nestjs/common';
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
|
||||
/**
|
||||
* Get session name and return Whatsapp session back (if exists)
|
||||
@@ -10,26 +10,25 @@ import {WhatsappSession} from "../core/abc/session.abc";
|
||||
*/
|
||||
@Injectable()
|
||||
export class SessionPipe implements PipeTransform<WhatsappSession> {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
async transform(value: any) {
|
||||
return this.manager.getSession(value)
|
||||
}
|
||||
async transform(value: any) {
|
||||
return this.manager.getSession(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator for a method that uses SessionPipe above
|
||||
*/
|
||||
export const SessionApiParam = ApiParam({
|
||||
name: 'session',
|
||||
required: true,
|
||||
type: "string",
|
||||
description: "WhatsApp session name",
|
||||
})
|
||||
name: 'session',
|
||||
required: true,
|
||||
type: 'string',
|
||||
description: 'WhatsApp session name',
|
||||
});
|
||||
|
||||
/**
|
||||
* Session param
|
||||
@SessionParam session: WhatsappSession,
|
||||
*/
|
||||
export const SessionParam = Param('session', SessionPipe)
|
||||
export const SessionParam = Param('session', SessionPipe);
|
||||
@@ -1,34 +1,28 @@
|
||||
import {Controller, Get, Query, Res} from '@nestjs/common';
|
||||
import {ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import {Readable} from "stream";
|
||||
import {Response} from 'express';
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
import {SessionQuery} from "../structures/base.dto";
|
||||
|
||||
import { Controller, Get, Query, Res } from '@nestjs/common';
|
||||
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { Readable } from 'stream';
|
||||
import { Response } from 'express';
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { SessionQuery } from '../structures/base.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api')
|
||||
@ApiTags('screenshot')
|
||||
export class ScreenshotController {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/screenshot')
|
||||
async screenshot(
|
||||
@Res() res: Response,
|
||||
@Query() sessionQuery: SessionQuery,
|
||||
) {
|
||||
const whatsappService = this.manager.getSession(sessionQuery.session)
|
||||
const buffer = await whatsappService.getScreenshot();
|
||||
const stream = new Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
@Get('/screenshot')
|
||||
async screenshot(@Res() res: Response, @Query() sessionQuery: SessionQuery) {
|
||||
const whatsappService = this.manager.getSession(sessionQuery.session);
|
||||
const buffer = await whatsappService.getScreenshot();
|
||||
const stream = new Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
stream.pipe(res)
|
||||
}
|
||||
res.set({
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
stream.pipe(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
import {Body, Controller, Get, Post} from '@nestjs/common';
|
||||
import {ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import {SessionDTO, SessionStartRequest, SessionStopRequest} from "../structures/sessions.dto";
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
|
||||
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
SessionLogoutRequest,
|
||||
SessionDTO,
|
||||
SessionStartRequest,
|
||||
SessionStopRequest,
|
||||
ListSessionsQuery,
|
||||
} from '../structures/sessions.dto';
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import { parseBool } from '../helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/sessions')
|
||||
@ApiTags('sessions')
|
||||
export class SessionsController {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Post('/start/')
|
||||
start(@Body() request: SessionStartRequest): SessionDTO {
|
||||
return this.manager.start(request);
|
||||
}
|
||||
|
||||
@Post('/start/')
|
||||
start(@Body() request: SessionStartRequest): SessionDTO {
|
||||
return this.manager.start(request)
|
||||
@Post('/stop/')
|
||||
@ApiOperation({ summary: 'Stop session' })
|
||||
async stop(@Body() request: SessionStopRequest): Promise<void> {
|
||||
await this.manager.stop(request);
|
||||
if (request.logout) {
|
||||
await this.manager.logout(request);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Post('/stop/')
|
||||
stop(@Body() request: SessionStopRequest): Promise<void> {
|
||||
return this.manager.stop(request)
|
||||
@Post('/logout/')
|
||||
@ApiOperation({ summary: 'Logout from session.' })
|
||||
clean(@Body() request: SessionLogoutRequest): Promise<void> {
|
||||
const session = this.manager.getSession(request.name, false);
|
||||
if (session) {
|
||||
throw new UnprocessableEntityException(
|
||||
`Can not clean running session, please stop it first. You can set 'clean' field to 'True' in stop request so it stops and cleans at the same time.`,
|
||||
);
|
||||
}
|
||||
return this.manager.logout(request);
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
list(): SessionDTO[] {
|
||||
return this.manager.getSessions()
|
||||
}
|
||||
@Get('/')
|
||||
list(@Query() query: ListSessionsQuery): SessionDTO[] {
|
||||
const all = parseBool(query.all);
|
||||
return this.manager.getSessions(all);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import {Controller, Get} from '@nestjs/common';
|
||||
import {ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import {VERSION} from "../version";
|
||||
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { VERSION } from '../version';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/version')
|
||||
@ApiTags('other')
|
||||
export class VersionController {
|
||||
@Get('')
|
||||
async get() {
|
||||
return VERSION
|
||||
}
|
||||
@Get('')
|
||||
async get() {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
+92
-79
@@ -1,90 +1,103 @@
|
||||
import {Injectable} from "@nestjs/common";
|
||||
import {ConfigService} from "@nestjs/config";
|
||||
import {WhatsappEngine} from "./structures/enums.dto";
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { WhatsappEngine } from './structures/enums.dto';
|
||||
import { parseBool } from './helpers';
|
||||
|
||||
@Injectable()
|
||||
export class WhatsappConfigService {
|
||||
public files_uri = '/api/files'
|
||||
public schema = "http"
|
||||
public files_uri = '/api/files';
|
||||
public schema = 'http';
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
get filesURL(): string {
|
||||
return `${this.schema}://${this.hostname}:${this.port}${this.files_uri}/`;
|
||||
}
|
||||
|
||||
get hostname(): string {
|
||||
return this.configService.get('WHATSAPP_API_HOSTNAME', 'localhost');
|
||||
}
|
||||
|
||||
get port(): string {
|
||||
return this.configService.get('WHATSAPP_API_PORT', '3000');
|
||||
}
|
||||
|
||||
get filesFolder(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_FILES_FOLDER',
|
||||
'/tmp/whatsapp-files',
|
||||
);
|
||||
}
|
||||
|
||||
get filesLifetime(): number {
|
||||
return this.configService.get<number>('WHATSAPP_FILES_LIFETIME', 180);
|
||||
}
|
||||
|
||||
get mimetypes(): string[] | null {
|
||||
const types = this.configService.get('WHATSAPP_FILES_MIMETYPES', '');
|
||||
return types ? types.split(',') : null;
|
||||
}
|
||||
|
||||
get startSessions(): string[] {
|
||||
const value: string = this.configService.get('WHATSAPP_START_SESSION', '');
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
return value.split(',');
|
||||
}
|
||||
|
||||
get filesURL(): string {
|
||||
return `${this.schema}://${this.hostname}:${this.port}${this.files_uri}/`
|
||||
get shouldRestartAllSessions(): boolean {
|
||||
const value: string = this.configService.get(
|
||||
'WHATSAPP_RESTART_ALL_SESSIONS',
|
||||
'false',
|
||||
);
|
||||
return parseBool(value);
|
||||
}
|
||||
|
||||
getWebhookUrl(): string | undefined {
|
||||
return this.get('WHATSAPP_HOOK_URL');
|
||||
}
|
||||
|
||||
getWebhookEvents(): string[] {
|
||||
const value = this.get('WHATSAPP_HOOK_EVENTS', '');
|
||||
return value ? value.split(',') : [];
|
||||
}
|
||||
|
||||
getDefaultEngineName(): WhatsappEngine {
|
||||
const value = this.get('WHATSAPP_DEFAULT_ENGINE', WhatsappEngine.WEBJS);
|
||||
if (value in WhatsappEngine) {
|
||||
return WhatsappEngine[value];
|
||||
}
|
||||
console.log(
|
||||
`Unknown WhatsApp default engine, using WEBJS. WHATSAPP_DEFAULT_ENGINE=${value}`,
|
||||
);
|
||||
}
|
||||
|
||||
get hostname(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_API_HOSTNAME',
|
||||
'localhost',
|
||||
)
|
||||
get(name: string, defaultValue = undefined): any {
|
||||
return this.configService.get(name, defaultValue);
|
||||
}
|
||||
|
||||
getApiKey(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_API_KEY', '');
|
||||
}
|
||||
|
||||
getSwaggerUsernamePassword(): [string, string] | undefined {
|
||||
const user = this.configService.get('WHATSAPP_SWAGGER_USERNAME', undefined);
|
||||
const password = this.configService.get(
|
||||
'WHATSAPP_SWAGGER_PASSWORD',
|
||||
undefined,
|
||||
);
|
||||
if (!user && !password) {
|
||||
console.log(
|
||||
'Please set up both WHATSAPP_SWAGGER_USERNAME and WHATSAPP_SWAGGER_PASSWORD ' +
|
||||
'to enable swagger authentication.',
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
return [user, password];
|
||||
}
|
||||
|
||||
get port(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_API_PORT',
|
||||
'3000',
|
||||
)
|
||||
}
|
||||
|
||||
get filesFolder(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_FILES_FOLDER',
|
||||
'/tmp/whatsapp-files',
|
||||
)
|
||||
}
|
||||
|
||||
get filesLifetime(): number {
|
||||
return this.configService.get<number>(
|
||||
'WHATSAPP_FILES_LIFETIME',
|
||||
180,
|
||||
)
|
||||
}
|
||||
|
||||
get mimetypes(): string[] | null {
|
||||
const types = this.configService.get('WHATSAPP_FILES_MIMETYPES', "")
|
||||
return types ? types.split(',') : null
|
||||
}
|
||||
|
||||
get startSession(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_START_SESSION', undefined)
|
||||
}
|
||||
|
||||
getWebhookUrl(): string | undefined {
|
||||
return this.get('WHATSAPP_HOOK_URL')
|
||||
}
|
||||
|
||||
getWebhookEvents(): string[] {
|
||||
const value = this.get('WHATSAPP_HOOK_EVENTS', "")
|
||||
return value ? value.split(',') : []
|
||||
}
|
||||
|
||||
getDefaultEngineName(): WhatsappEngine {
|
||||
const value = this.get("WHATSAPP_DEFAULT_ENGINE", WhatsappEngine.WEBJS)
|
||||
if (value in WhatsappEngine) {
|
||||
return WhatsappEngine[value]
|
||||
}
|
||||
console.log(`Unknown WhatsApp default engine, using WEBJS. WHATSAPP_DEFAULT_ENGINE=${value}`)
|
||||
|
||||
}
|
||||
|
||||
get(name: string, defaultValue = undefined): any {
|
||||
return this.configService.get(name, defaultValue)
|
||||
}
|
||||
|
||||
getApiKey(): string | undefined {
|
||||
return this.configService.get("WHATSAPP_API_KEY", "")
|
||||
}
|
||||
|
||||
getSwaggerUsernamePassword(): [string, string] | undefined {
|
||||
const user = this.configService.get("WHATSAPP_SWAGGER_USERNAME", undefined)
|
||||
const password = this.configService.get("WHATSAPP_SWAGGER_PASSWORD", undefined)
|
||||
if (!user && !password) {
|
||||
console.log("Please set up both WHATSAPP_SWAGGER_USERNAME and WHATSAPP_SWAGGER_PASSWORD " +
|
||||
"to enable swagger authentication.")
|
||||
return undefined
|
||||
}
|
||||
return [user, password]
|
||||
}
|
||||
getSwaggerAdvancedConfigEnabled(): boolean {
|
||||
return this.configService.get('WHATSAPP_SWAGGER_CONFIG_ADVANCED', false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export class QR {
|
||||
private base64: string;
|
||||
|
||||
save(base64) {
|
||||
this.base64 = base64.replace(/^data:image\/png;base64,/, '');
|
||||
}
|
||||
|
||||
get(): Buffer {
|
||||
return Buffer.from(this.base64, 'base64');
|
||||
}
|
||||
}
|
||||
+24
-16
@@ -1,28 +1,36 @@
|
||||
import {OnApplicationShutdown} from "@nestjs/common";
|
||||
import {SessionDTO, SessionStartRequest, SessionStopRequest} from "../../structures/sessions.dto";
|
||||
import {WhatsappSession} from "./session.abc";
|
||||
import {WhatsappEngine} from "../../structures/enums.dto";
|
||||
import {MediaStorage} from "./storage.abc";
|
||||
import {WebhookConductor} from "./webhooks.abc";
|
||||
import { OnApplicationShutdown } from '@nestjs/common';
|
||||
import {
|
||||
SessionLogoutRequest,
|
||||
SessionDTO,
|
||||
SessionStartRequest,
|
||||
SessionStopRequest,
|
||||
} from '../../structures/sessions.dto';
|
||||
import { WhatsappSession } from './session.abc';
|
||||
import { WhatsappEngine } from '../../structures/enums.dto';
|
||||
import { MediaStorage } from './storage.abc';
|
||||
import { WebhookConductor } from './webhooks.abc';
|
||||
|
||||
export abstract class SessionManager implements OnApplicationShutdown {
|
||||
protected abstract getEngine(engine: WhatsappEngine): typeof WhatsappSession;
|
||||
|
||||
abstract start(request: SessionStartRequest): SessionDTO
|
||||
protected abstract get EngineClass(): typeof WhatsappSession;
|
||||
|
||||
abstract stop(request: SessionStopRequest): Promise<void>
|
||||
protected abstract get WebhookConductorClass(): typeof WebhookConductor;
|
||||
|
||||
abstract getSession(name: string): WhatsappSession
|
||||
protected abstract get MediaStorageClass(): typeof MediaStorage;
|
||||
|
||||
abstract getSessions(): SessionDTO[]
|
||||
abstract onApplicationShutdown(signal?: string);
|
||||
|
||||
abstract onApplicationShutdown(signal ?: string)
|
||||
//
|
||||
// API Methods
|
||||
//
|
||||
abstract start(request: SessionStartRequest): SessionDTO;
|
||||
|
||||
protected abstract getEngine(engine: WhatsappEngine): typeof WhatsappSession
|
||||
abstract stop(request: SessionStopRequest): Promise<void>;
|
||||
|
||||
protected abstract get EngineClass(): typeof WhatsappSession
|
||||
abstract logout(request: SessionLogoutRequest): Promise<void>;
|
||||
|
||||
protected abstract get WebhookConductorClass(): typeof WebhookConductor
|
||||
abstract getSession(name: string, error?: boolean): WhatsappSession;
|
||||
|
||||
protected abstract get MediaStorageClass(): typeof MediaStorage
|
||||
abstract getSessions(all: boolean): SessionDTO[];
|
||||
}
|
||||
|
||||
+210
-181
@@ -1,235 +1,264 @@
|
||||
import {WAEvents, WhatsappStatus} from "../../structures/enums.dto";
|
||||
import {ConsoleLogger} from "@nestjs/common";
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest
|
||||
} from "../../structures/chatting.dto";
|
||||
import {MediaStorage} from "./storage.abc";
|
||||
import {MessageId} from "whatsapp-web.js";
|
||||
import {ContactQuery, ContactRequest} from "../../structures/contacts.dto";
|
||||
import {NotImplementedByEngineError} from "../exceptions";
|
||||
import {CreateGroupRequest, ParticipantsRequest} from "../../structures/groups.dto";
|
||||
import * as fs from "fs";
|
||||
WAEvents,
|
||||
WhatsappEngine,
|
||||
WhatsappStatus,
|
||||
} from '../../structures/enums.dto';
|
||||
import { ConsoleLogger } from '@nestjs/common';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest,
|
||||
} from '../../structures/chatting.dto';
|
||||
import { LocalSessionStorage, MediaStorage } from './storage.abc';
|
||||
import { MessageId } from 'whatsapp-web.js';
|
||||
import { ContactQuery, ContactRequest } from '../../structures/contacts.dto';
|
||||
import { NotImplementedByEngineError } from '../exceptions';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
ParticipantsRequest,
|
||||
} from '../../structures/groups.dto';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const CHROME_PATH = "/usr/bin/google-chrome-stable"
|
||||
const CHROMIUM_PATH = "/usr/bin/chromium"
|
||||
const CHROME_PATH = '/usr/bin/google-chrome-stable';
|
||||
const CHROMIUM_PATH = '/usr/bin/chromium';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export function getBrowserExecutablePath() {
|
||||
if (fs.existsSync(CHROME_PATH)) {
|
||||
return CHROME_PATH
|
||||
}
|
||||
return CHROMIUM_PATH
|
||||
if (fs.existsSync(CHROME_PATH)) {
|
||||
return CHROME_PATH;
|
||||
}
|
||||
return CHROMIUM_PATH;
|
||||
}
|
||||
export function ensureSuffix(phone) {
|
||||
const suffix = '@c.us';
|
||||
if (phone.includes('@')) {
|
||||
return phone;
|
||||
}
|
||||
return phone + suffix;
|
||||
}
|
||||
|
||||
export enum WAHAInternalEvent {
|
||||
engine_start = 'engine.start',
|
||||
}
|
||||
|
||||
export interface WhatsAppSessionConfig {
|
||||
name: string;
|
||||
storage: MediaStorage;
|
||||
log: ConsoleLogger;
|
||||
sessionStorage: LocalSessionStorage;
|
||||
}
|
||||
|
||||
export abstract class WhatsappSession {
|
||||
public status: WhatsappStatus;
|
||||
public status: WhatsappStatus;
|
||||
public events: EventEmitter;
|
||||
public engine: WhatsappEngine;
|
||||
|
||||
public constructor(public name: string, protected storage: MediaStorage, protected log: ConsoleLogger) {
|
||||
this.name = name
|
||||
this.status = WhatsappStatus.STARTING
|
||||
this.log = log
|
||||
}
|
||||
public name: string;
|
||||
protected storage: MediaStorage;
|
||||
protected log: ConsoleLogger;
|
||||
protected sessionStorage: LocalSessionStorage;
|
||||
|
||||
getBrowserExecutablePath() {
|
||||
return getBrowserExecutablePath()
|
||||
}
|
||||
public constructor({ name, log, sessionStorage }: WhatsAppSessionConfig) {
|
||||
this.name = name;
|
||||
this.status = WhatsappStatus.STARTING;
|
||||
this.log = log;
|
||||
this.sessionStorage = sessionStorage;
|
||||
this.events = new EventEmitter();
|
||||
}
|
||||
|
||||
getBrowserArgsForPuppeteer() {
|
||||
// Run optimized version of Chrome
|
||||
// References:
|
||||
// https://github.com/pedroslopez/whatsapp-web.js/issues/1420
|
||||
// https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/
|
||||
return [
|
||||
'--no-sandbox',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-component-update',
|
||||
'--disable-default-apps',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-speech-api',
|
||||
'--hide-scrollbars',
|
||||
'--mute-audio',
|
||||
'--disable-extensions',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-accelerated-2d-canvas',
|
||||
'--no-first-run',
|
||||
'--no-default-browser-check',
|
||||
'--no-pings',
|
||||
'--password-store=basic',
|
||||
'--use-mock-keychain',
|
||||
'--no-zygote',
|
||||
'--single-process',
|
||||
'--disable-gpu'
|
||||
]
|
||||
}
|
||||
getBrowserExecutablePath() {
|
||||
return getBrowserExecutablePath();
|
||||
}
|
||||
|
||||
getBrowserArgsForPuppeteer() {
|
||||
// Run optimized version of Chrome
|
||||
// References:
|
||||
// https://github.com/pedroslopez/whatsapp-web.js/issues/1420
|
||||
// https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/
|
||||
return [
|
||||
'--no-sandbox',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-component-update',
|
||||
'--disable-default-apps',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-speech-api',
|
||||
'--hide-scrollbars',
|
||||
'--mute-audio',
|
||||
'--disable-extensions',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-accelerated-2d-canvas',
|
||||
'--no-first-run',
|
||||
'--no-default-browser-check',
|
||||
'--no-pings',
|
||||
'--password-store=basic',
|
||||
'--use-mock-keychain',
|
||||
'--no-zygote',
|
||||
'--single-process',
|
||||
'--disable-gpu',
|
||||
];
|
||||
}
|
||||
|
||||
/** Start the session */
|
||||
abstract start()
|
||||
/** Start the session */
|
||||
abstract start();
|
||||
|
||||
/** Stop the session */
|
||||
abstract stop(): void
|
||||
/** Stop the session */
|
||||
abstract stop(): void;
|
||||
|
||||
/** Subscribe the handler to specific hook */
|
||||
abstract subscribe(hook: WAEvents | string, handler: (message) => void)
|
||||
/** Subscribe the handler to specific hook */
|
||||
abstract subscribe(hook: WAEvents | string, handler: (message) => void);
|
||||
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
abstract getScreenshot(): Promise<Buffer | string>
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
abstract getScreenshot(): Promise<Buffer | string>;
|
||||
|
||||
abstract checkNumberStatus(request: CheckNumberStatusQuery)
|
||||
abstract checkNumberStatus(request: CheckNumberStatusQuery);
|
||||
|
||||
abstract sendText(request: MessageTextRequest)
|
||||
abstract sendText(request: MessageTextRequest);
|
||||
|
||||
abstract sendContactVCard(request: MessageContactVcardRequest)
|
||||
abstract sendContactVCard(request: MessageContactVcardRequest);
|
||||
|
||||
abstract sendTextButtons(request: MessageTextButtonsRequest)
|
||||
abstract sendTextButtons(request: MessageTextButtonsRequest);
|
||||
|
||||
abstract sendLocation(request: MessageLocationRequest)
|
||||
abstract sendLocation(request: MessageLocationRequest);
|
||||
|
||||
abstract sendLinkPreview(request: MessageLinkPreviewRequest)
|
||||
abstract sendLinkPreview(request: MessageLinkPreviewRequest);
|
||||
|
||||
abstract sendImage(request: MessageImageRequest)
|
||||
abstract sendImage(request: MessageImageRequest);
|
||||
|
||||
abstract sendFile(request: MessageFileRequest)
|
||||
abstract sendFile(request: MessageFileRequest);
|
||||
|
||||
abstract sendVoice(request: MessageVoiceRequest)
|
||||
abstract sendVoice(request: MessageVoiceRequest);
|
||||
|
||||
abstract reply(request: MessageReplyRequest)
|
||||
abstract reply(request: MessageReplyRequest);
|
||||
|
||||
abstract sendSeen(chat: ChatRequest)
|
||||
abstract sendSeen(chat: ChatRequest);
|
||||
|
||||
abstract startTyping(chat: ChatRequest)
|
||||
abstract startTyping(chat: ChatRequest);
|
||||
|
||||
abstract stopTyping(chat: ChatRequest)
|
||||
abstract stopTyping(chat: ChatRequest);
|
||||
|
||||
abstract setReaction(request: MessageReactionRequest)
|
||||
abstract getMessages(query: GetMessageQuery);
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
public getContact(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
abstract setReaction(request: MessageReactionRequest);
|
||||
|
||||
public getContacts() {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
public getContact(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getContactAbout(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getContacts() {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getContactProfilePicture(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getContactAbout(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public blockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getContactProfilePicture(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public unblockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public blockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public unblockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getGroups() {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getGroup(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getGroups() {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public leaveGroup(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setDescription(id, description) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public leaveGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setSubject(id, description) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public setDescription(id, description) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public setSubject(id, description) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public revokeInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getParticipants(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public revokeInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public addParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public getParticipants(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public removeParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public addParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public removeParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
public promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add WhatsApp suffix (@c.us) to the phone number if it doesn't have it yet
|
||||
* @param phone
|
||||
*/
|
||||
protected ensureSuffix(phone) {
|
||||
const suffix = "@c.us"
|
||||
if (phone.includes("@")) {
|
||||
return phone
|
||||
}
|
||||
return phone + suffix
|
||||
}
|
||||
|
||||
protected deserializeId(messageId: string): MessageId {
|
||||
const parts = messageId.split("_")
|
||||
return {
|
||||
"fromMe": parts[0] === "true",
|
||||
"remote": parts[1],
|
||||
"id": parts[2],
|
||||
"_serialized": messageId
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add WhatsApp suffix (@c.us) to the phone number if it doesn't have it yet
|
||||
* @param phone
|
||||
*/
|
||||
protected ensureSuffix(phone) {
|
||||
return ensureSuffix(phone);
|
||||
}
|
||||
|
||||
protected deserializeId(messageId: string): MessageId {
|
||||
const parts = messageId.split('_');
|
||||
return {
|
||||
fromMe: parts[0] === 'true',
|
||||
remote: parts[1],
|
||||
id: parts[2],
|
||||
_serialized: messageId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
export abstract class MediaStorage {
|
||||
abstract save(messageId: string, mimetype: string, buffer: Buffer): Promise<string>
|
||||
abstract save(
|
||||
messageId: string,
|
||||
mimetype: string,
|
||||
buffer: Buffer,
|
||||
): Promise<string>;
|
||||
}
|
||||
|
||||
export abstract class LocalSessionStorage {
|
||||
public sessionsFolder: string;
|
||||
|
||||
protected constructor(public engine: string) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
abstract init();
|
||||
|
||||
abstract getFolderPath(sessionName: string): string;
|
||||
|
||||
abstract clean(sessionName: string);
|
||||
|
||||
abstract getAll(): string[];
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {WhatsappSession} from "./session.abc";
|
||||
import { WhatsappSession } from './session.abc';
|
||||
|
||||
export abstract class WebhookConductor {
|
||||
abstract configure(session: WhatsappSession)
|
||||
abstract configure(session: WhatsappSession);
|
||||
}
|
||||
+49
-50
@@ -1,57 +1,56 @@
|
||||
import {ConsoleLogger, Module} from '@nestjs/common';
|
||||
import {ScreenshotController} from "../api/screenshot.controller";
|
||||
import {ConfigModule} from "@nestjs/config";
|
||||
import {WhatsappConfigService} from "../config.service";
|
||||
import {ServeStaticModule} from "@nestjs/serve-static";
|
||||
import {SessionsController} from "../api/sessions.controller";
|
||||
import {ChattingController} from "../api/chatting.controller";
|
||||
import {ContactsController} from "../api/contacts.controller";
|
||||
import {VersionController} from "../api/version.controller";
|
||||
import {PassportModule} from "@nestjs/passport";
|
||||
import {SessionManager} from "./abc/manager.abc";
|
||||
import {SessionManagerCore} from "./manager.core";
|
||||
import {GroupsController} from "../api/groups.controller";
|
||||
import { ConsoleLogger, Module } from '@nestjs/common';
|
||||
import { ScreenshotController } from '../api/screenshot.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import { SessionsController } from '../api/sessions.controller';
|
||||
import { ChattingController } from '../api/chatting.controller';
|
||||
import { ContactsController } from '../api/contacts.controller';
|
||||
import { VersionController } from '../api/version.controller';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { SessionManager } from './abc/manager.abc';
|
||||
import { SessionManagerCore } from './manager.core';
|
||||
import { GroupsController } from '../api/groups.controller';
|
||||
|
||||
export const IMPORTS = [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
ServeStaticModule.forRootAsync({
|
||||
imports: [],
|
||||
extraProviders: [WhatsappConfigService],
|
||||
inject: [WhatsappConfigService],
|
||||
useFactory: (config: WhatsappConfigService) => {
|
||||
return [{
|
||||
rootPath: config.filesFolder,
|
||||
serveRoot: config.files_uri,
|
||||
}]
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
ServeStaticModule.forRootAsync({
|
||||
imports: [],
|
||||
extraProviders: [WhatsappConfigService],
|
||||
inject: [WhatsappConfigService],
|
||||
useFactory: (config: WhatsappConfigService) => {
|
||||
return [
|
||||
{
|
||||
rootPath: config.filesFolder,
|
||||
serveRoot: config.files_uri,
|
||||
},
|
||||
}),
|
||||
PassportModule,
|
||||
]
|
||||
export const CONTROLLERS = [
|
||||
SessionsController,
|
||||
ChattingController,
|
||||
ContactsController,
|
||||
GroupsController,
|
||||
ScreenshotController,
|
||||
VersionController,
|
||||
]
|
||||
const PROVIDERS = [
|
||||
{
|
||||
provide: SessionManager,
|
||||
useClass: SessionManagerCore,
|
||||
];
|
||||
},
|
||||
WhatsappConfigService,
|
||||
ConsoleLogger,
|
||||
]
|
||||
|
||||
}),
|
||||
PassportModule,
|
||||
];
|
||||
export const CONTROLLERS = [
|
||||
SessionsController,
|
||||
ChattingController,
|
||||
ContactsController,
|
||||
GroupsController,
|
||||
ScreenshotController,
|
||||
VersionController,
|
||||
];
|
||||
const PROVIDERS = [
|
||||
{
|
||||
provide: SessionManager,
|
||||
useClass: SessionManagerCore,
|
||||
},
|
||||
WhatsappConfigService,
|
||||
ConsoleLogger,
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: IMPORTS,
|
||||
controllers: CONTROLLERS,
|
||||
providers: PROVIDERS,
|
||||
imports: IMPORTS,
|
||||
controllers: CONTROLLERS,
|
||||
providers: PROVIDERS,
|
||||
})
|
||||
export class AppModuleCore {
|
||||
|
||||
}
|
||||
export class AppModuleCore {}
|
||||
+13
-12
@@ -1,20 +1,21 @@
|
||||
import {UnprocessableEntityException} from "@nestjs/common";
|
||||
import { UnprocessableEntityException } from '@nestjs/common';
|
||||
|
||||
export const DOCS_URL = "https://waha.devlike.pro/"
|
||||
export const DOCS_URL = 'https://waha.devlike.pro/';
|
||||
|
||||
export class NotImplementedByEngineError extends UnprocessableEntityException {
|
||||
constructor(msg = "") {
|
||||
let error = "The method is not implemented by the engine."
|
||||
if (msg) {
|
||||
error += ` ${msg}`
|
||||
}
|
||||
super(error);
|
||||
constructor(msg = '') {
|
||||
let error = 'The method is not implemented by the engine.';
|
||||
if (msg) {
|
||||
error += ` ${msg}`;
|
||||
}
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class AvailableInPlusVersion extends UnprocessableEntityException {
|
||||
constructor() {
|
||||
super(`The feature is available only in Plus version. Check this out: ${DOCS_URL}`);
|
||||
}
|
||||
constructor() {
|
||||
super(
|
||||
`The feature is available only in Plus version. Check this out: ${DOCS_URL}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+152
-100
@@ -1,116 +1,168 @@
|
||||
import {ConsoleLogger, Injectable, NotFoundException, UnprocessableEntityException} from "@nestjs/common";
|
||||
import {SessionManager} from "./abc/manager.abc";
|
||||
import {WhatsappSession} from "./abc/session.abc";
|
||||
import {WhatsappEngine} from "../structures/enums.dto";
|
||||
import {SessionDTO, SessionStartRequest, SessionStopRequest} from "../structures/sessions.dto";
|
||||
import {WhatsappConfigService} from "../config.service";
|
||||
import {WhatsappSessionVenomCore} from "./session.venom.core";
|
||||
import {WhatsappSessionWebJSCore} from "./session.webjs.core";
|
||||
import {DOCS_URL} from "./exceptions";
|
||||
import {WebhookConductorCore} from "./webhooks.core";
|
||||
import {MediaStorageCore} from "./storage.core";
|
||||
import {
|
||||
ConsoleLogger,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
UnprocessableEntityException,
|
||||
} from '@nestjs/common';
|
||||
import { SessionManager } from './abc/manager.abc';
|
||||
import {
|
||||
WAHAInternalEvent,
|
||||
WhatsappSession,
|
||||
WhatsAppSessionConfig,
|
||||
} from './abc/session.abc';
|
||||
import { WhatsappEngine, WhatsappStatus } from '../structures/enums.dto';
|
||||
import {
|
||||
SessionLogoutRequest,
|
||||
SessionDTO,
|
||||
SessionStartRequest,
|
||||
SessionStopRequest,
|
||||
} from '../structures/sessions.dto';
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
import { WhatsappSessionVenomCore } from './session.venom.core';
|
||||
import { WhatsappSessionWebJSCore } from './session.webjs.core';
|
||||
import { DOCS_URL } from './exceptions';
|
||||
import { WebhookConductorCore } from './webhooks.core';
|
||||
import { MediaStorageCore, SessionStorageCore } from './storage.core';
|
||||
import { WhatsappSessionNoWebCore } from './session.noweb.core';
|
||||
import { LocalSessionStorage } from './abc/storage.abc';
|
||||
|
||||
export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
|
||||
constructor() {
|
||||
super(`WAHA Core support only 'default' session. If you want to run more then one WhatsApp account - please get WAHA PLUS version. Check this out: ${DOCS_URL}`);
|
||||
}
|
||||
constructor() {
|
||||
super(
|
||||
`WAHA Core support only 'default' session. If you want to run more then one WhatsApp account - please get WAHA PLUS version. Check this out: ${DOCS_URL}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SessionManagerCore extends SessionManager {
|
||||
private session: WhatsappSession;
|
||||
DEFAULT = "default"
|
||||
private session: WhatsappSession;
|
||||
DEFAULT = 'default';
|
||||
|
||||
// @ts-ignore
|
||||
protected MediaStorageClass = MediaStorageCore;
|
||||
// @ts-ignore
|
||||
protected WebhookConductorClass = WebhookConductorCore;
|
||||
protected readonly EngineClass: typeof WhatsappSession;
|
||||
protected sessionStorage: LocalSessionStorage;
|
||||
|
||||
constructor(
|
||||
private config: WhatsappConfigService,
|
||||
private log: ConsoleLogger,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.log.setContext('SessionManager');
|
||||
this.session = undefined;
|
||||
const engineName = this.config.getDefaultEngineName();
|
||||
this.EngineClass = this.getEngine(engineName);
|
||||
this.sessionStorage = new SessionStorageCore(engineName.toLowerCase());
|
||||
this.sessionStorage.init();
|
||||
|
||||
this.startPredefinedSessions();
|
||||
}
|
||||
|
||||
protected startPredefinedSessions() {
|
||||
const startSessions = this.config.startSessions;
|
||||
startSessions.forEach((sessionName) => {
|
||||
this.start({ name: sessionName });
|
||||
});
|
||||
}
|
||||
|
||||
protected getEngine(engine: WhatsappEngine): typeof WhatsappSession {
|
||||
if (engine === WhatsappEngine.WEBJS) {
|
||||
return WhatsappSessionWebJSCore;
|
||||
} else if (engine === WhatsappEngine.VENOM) {
|
||||
return WhatsappSessionVenomCore;
|
||||
} else if (engine === WhatsappEngine.NOWEB) {
|
||||
return WhatsappSessionNoWebCore;
|
||||
} else {
|
||||
throw new NotFoundException(`Unknown whatsapp engine '${engine}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
private onlyDefault(name: string) {
|
||||
if (name !== this.DEFAULT) {
|
||||
throw new OnlyDefaultSessionIsAllowed();
|
||||
}
|
||||
}
|
||||
|
||||
async onApplicationShutdown(signal?: string) {
|
||||
if (!this.session) {
|
||||
return;
|
||||
}
|
||||
await this.stop({ name: this.DEFAULT, logout: false });
|
||||
}
|
||||
|
||||
//
|
||||
// API Methods
|
||||
//
|
||||
start(request: SessionStartRequest): SessionDTO {
|
||||
this.onlyDefault(request.name);
|
||||
|
||||
const name = request.name;
|
||||
this.log.log(`'${name}' - starting session...`);
|
||||
const log = new ConsoleLogger(`WhatsappSession - ${name}`);
|
||||
const storage = new this.MediaStorageClass();
|
||||
const webhookLog = new ConsoleLogger(`Webhook - ${name}`);
|
||||
const webhook = new this.WebhookConductorClass(
|
||||
webhookLog,
|
||||
this.config.getWebhookUrl(),
|
||||
this.config.getWebhookEvents(),
|
||||
);
|
||||
|
||||
const sessionConfig: WhatsAppSessionConfig = {
|
||||
name,
|
||||
storage,
|
||||
log,
|
||||
sessionStorage: this.sessionStorage,
|
||||
};
|
||||
// @ts-ignore
|
||||
protected MediaStorageClass = MediaStorageCore
|
||||
// @ts-ignore
|
||||
protected WebhookConductorClass = WebhookConductorCore
|
||||
protected readonly EngineClass: typeof WhatsappSession;
|
||||
const session = new this.EngineClass(sessionConfig);
|
||||
this.session = session;
|
||||
|
||||
session.events.on(WAHAInternalEvent.engine_start, () =>
|
||||
webhook.configure(session),
|
||||
);
|
||||
session.start();
|
||||
return { name: session.name, status: session.status };
|
||||
}
|
||||
|
||||
constructor(
|
||||
private config: WhatsappConfigService,
|
||||
private log: ConsoleLogger,
|
||||
) {
|
||||
super()
|
||||
async stop(request: SessionStopRequest): Promise<void> {
|
||||
this.onlyDefault(request.name);
|
||||
|
||||
this.log.setContext('SessionManager')
|
||||
this.session = undefined
|
||||
this.EngineClass = this.getEngine(this.config.getDefaultEngineName())
|
||||
const name = request.name;
|
||||
this.log.log(`Stopping ${name} session...`);
|
||||
const session = this.getSession(name);
|
||||
await session.stop();
|
||||
this.log.log(`"${name}" has been stopped.`);
|
||||
this.session = undefined;
|
||||
}
|
||||
|
||||
// Start session from the start
|
||||
if (config.startSession) {
|
||||
this.start({name: config.startSession})
|
||||
}
|
||||
async logout(request: SessionLogoutRequest) {
|
||||
this.sessionStorage.clean(request.name);
|
||||
}
|
||||
|
||||
getSession(name: string, error = true): WhatsappSession {
|
||||
this.onlyDefault(name);
|
||||
const session = this.session;
|
||||
if (!session) {
|
||||
if (error) {
|
||||
throw new NotFoundException(
|
||||
`We didn't find a session with name '${name}'. Please start it first by using POST /sessions/start request`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
protected getEngine(engine: WhatsappEngine): typeof WhatsappSession {
|
||||
if (engine === WhatsappEngine.WEBJS) {
|
||||
return WhatsappSessionWebJSCore
|
||||
} else if (engine === WhatsappEngine.VENOM) {
|
||||
return WhatsappSessionVenomCore
|
||||
} else {
|
||||
throw new NotFoundException(`Unknown whatsapp engine '${engine}'.`)
|
||||
}
|
||||
|
||||
getSessions(all: boolean): SessionDTO[] {
|
||||
if (!this.session) {
|
||||
return all
|
||||
? [{ name: this.DEFAULT, status: WhatsappStatus.STOPPED }]
|
||||
: [];
|
||||
}
|
||||
|
||||
private onlyDefault(name: string) {
|
||||
if (name !== this.DEFAULT) {
|
||||
throw new OnlyDefaultSessionIsAllowed()
|
||||
}
|
||||
}
|
||||
|
||||
getSession(name: string): WhatsappSession {
|
||||
this.onlyDefault(name)
|
||||
if (!this.session) {
|
||||
throw new NotFoundException(
|
||||
`We didn't find a session with name '${name}'. Please start it first by using POST /sessions/start request`,
|
||||
);
|
||||
}
|
||||
return this.session
|
||||
}
|
||||
|
||||
getSessions(): SessionDTO[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
async onApplicationShutdown(signal?: string) {
|
||||
if (!this.session){
|
||||
return
|
||||
}
|
||||
await this.stop({name: this.DEFAULT})
|
||||
}
|
||||
|
||||
start(request: SessionStartRequest): SessionDTO {
|
||||
this.onlyDefault(request.name)
|
||||
|
||||
const name = request.name
|
||||
this.log.log(`'${name}' - starting session...`)
|
||||
const log = new ConsoleLogger(`WhatsappSession - ${name}`)
|
||||
const storage = new this.MediaStorageClass()
|
||||
const webhookLog = new ConsoleLogger(`Webhook - ${name}`)
|
||||
const webhook = new this.WebhookConductorClass(
|
||||
webhookLog,
|
||||
this.config.getWebhookUrl(),
|
||||
this.config.getWebhookEvents()
|
||||
)
|
||||
|
||||
// @ts-ignore
|
||||
const session = new this.EngineClass(name, storage, log)
|
||||
this.session = session
|
||||
|
||||
session.start().then(() => webhook.configure(session))
|
||||
return {name: session.name, status: session.status}
|
||||
}
|
||||
|
||||
async stop(request: SessionStopRequest): Promise<void> {
|
||||
const name = request.name
|
||||
this.log.log(`Stopping ${name} session...`)
|
||||
const session = this.getSession(name)
|
||||
await session.stop()
|
||||
this.log.log(`"${name}" has been stopped.`)
|
||||
this.session = undefined
|
||||
}
|
||||
|
||||
return [{ name: this.session.name, status: this.session.status }];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
import {
|
||||
SECOND,
|
||||
WAEvents,
|
||||
WhatsappEngine,
|
||||
WhatsappStatus,
|
||||
} from '../structures/enums.dto';
|
||||
import makeWASocket, {
|
||||
DisconnectReason,
|
||||
isJidGroup,
|
||||
makeInMemoryStore,
|
||||
useMultiFileAuthState,
|
||||
} from '@adiwajshing/baileys';
|
||||
|
||||
import {
|
||||
ensureSuffix,
|
||||
WAHAInternalEvent,
|
||||
WhatsappSession,
|
||||
} from './abc/session.abc';
|
||||
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
|
||||
import {
|
||||
Button,
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest,
|
||||
SendSeenRequest,
|
||||
} from '../structures/chatting.dto';
|
||||
import {
|
||||
AvailableInPlusVersion,
|
||||
NotImplementedByEngineError,
|
||||
} from './exceptions';
|
||||
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
ParticipantsRequest,
|
||||
} from '../structures/groups.dto';
|
||||
import { QR } from './QR';
|
||||
import { MediaStorage } from './abc/storage.abc';
|
||||
import { ConsoleLogger, UnprocessableEntityException } from '@nestjs/common';
|
||||
import { Message } from 'whatsapp-web.js';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const QRCode = require('qrcode');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const logger = require('pino')();
|
||||
|
||||
export const BaileysEvents = {
|
||||
CONNECTION_UPDATE: 'connection.update',
|
||||
CREDS_UPDATE: 'creds.update',
|
||||
MESSAGES_UPSERT: 'messages.upsert',
|
||||
GROUPS_UPSERT: 'groups.upsert',
|
||||
};
|
||||
|
||||
export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
engine = WhatsappEngine.NOWEB;
|
||||
|
||||
sock: any;
|
||||
store: any;
|
||||
private qr: QR;
|
||||
authFolder: string;
|
||||
|
||||
public constructor(config) {
|
||||
super(config);
|
||||
this.qr = new QR();
|
||||
this.authFolder = this.getAuthFolder();
|
||||
}
|
||||
|
||||
start() {
|
||||
return this.buildClient();
|
||||
}
|
||||
|
||||
protected getAuthFolder() {
|
||||
const folder = this.sessionStorage.getFolderPath(this.name);
|
||||
return fs.mkdtempSync(folder);
|
||||
}
|
||||
|
||||
async makeSocket() {
|
||||
const { state, saveCreds } = await useMultiFileAuthState(this.authFolder);
|
||||
const sock: any = makeWASocket({
|
||||
auth: state,
|
||||
printQRInTerminal: true,
|
||||
browser: ['Linux', 'Chrome', '111.0.5563.64'],
|
||||
logger: logger,
|
||||
});
|
||||
sock.ev.on(BaileysEvents.CREDS_UPDATE, saveCreds);
|
||||
return sock;
|
||||
}
|
||||
|
||||
connectStore() {
|
||||
if (!this.store) {
|
||||
this.store = makeInMemoryStore({});
|
||||
}
|
||||
this.store.bind(this.sock.ev);
|
||||
}
|
||||
|
||||
async buildClient() {
|
||||
this.sock = await this.makeSocket();
|
||||
this.connectStore();
|
||||
this.sock.ev.on(BaileysEvents.CONNECTION_UPDATE, async (update) => {
|
||||
const { connection, lastDisconnect, qr } = update;
|
||||
if (connection === 'connecting') {
|
||||
this.qr.save('');
|
||||
this.status = WhatsappStatus.WORKING;
|
||||
return;
|
||||
} else if (connection === 'close') {
|
||||
const shouldReconnect =
|
||||
lastDisconnect.error?.output?.statusCode !==
|
||||
DisconnectReason.loggedOut;
|
||||
this.log.error(
|
||||
'connection closed due to ',
|
||||
lastDisconnect.error,
|
||||
', reconnecting ',
|
||||
shouldReconnect,
|
||||
);
|
||||
this.qr.save('');
|
||||
this.status = WhatsappStatus.FAILED;
|
||||
// reconnect if not logged out
|
||||
if (shouldReconnect) {
|
||||
setTimeout(() => this.buildClient(), 2 * SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
// Save QR
|
||||
if (qr) {
|
||||
this.status = WhatsappStatus.SCAN_QR_CODE;
|
||||
QRCode.toDataURL(qr).then((url) => {
|
||||
this.qr.save(url);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.events.emit(WAHAInternalEvent.engine_start);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.sock.ws.removeAllListeners();
|
||||
this.log.log('socket connection terminated');
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
async getScreenshot(): Promise<Buffer | string> {
|
||||
if (this.status === WhatsappStatus.STARTING) {
|
||||
throw new UnprocessableEntityException(
|
||||
`The session is starting, please try again after few seconds`,
|
||||
);
|
||||
} else if (this.status === WhatsappStatus.SCAN_QR_CODE) {
|
||||
return Promise.resolve(this.qr.get());
|
||||
} else if (this.status === WhatsappStatus.WORKING) {
|
||||
throw new UnprocessableEntityException(
|
||||
`Can not get screenshot for non chrome based engine.`,
|
||||
);
|
||||
} else {
|
||||
throw new UnprocessableEntityException(`Unknown status - ${this.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
async checkNumberStatus(
|
||||
request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
const phone = request.phone.split('@')[0];
|
||||
const [result] = await this.sock.onWhatsApp(phone);
|
||||
if (!result) {
|
||||
return { numberExists: false };
|
||||
}
|
||||
return { numberExists: result.exists };
|
||||
}
|
||||
|
||||
sendText(request: MessageTextRequest) {
|
||||
const chatId = this.ensureSuffix(request.chatId);
|
||||
return this.sock.sendMessage(chatId, { text: request.text });
|
||||
}
|
||||
|
||||
sendTextButtons(request: MessageTextButtonsRequest) {
|
||||
const buttons = request.buttons.map((button: Button) => {
|
||||
return {
|
||||
buttonId: button.id,
|
||||
buttonText: { displayText: button.text },
|
||||
type: 1,
|
||||
};
|
||||
});
|
||||
|
||||
const buttonMessage = {
|
||||
text: request.title,
|
||||
buttons: buttons,
|
||||
headerType: 1,
|
||||
};
|
||||
|
||||
return this.sock.sendMessage(request.chatId, buttonMessage);
|
||||
}
|
||||
|
||||
sendContactVCard(request: MessageContactVcardRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
async reply(request: MessageReplyRequest) {
|
||||
const { id } = parseMessageId(request.reply_to);
|
||||
const message = await this.store.loadMessage(toJID(request.chatId), id);
|
||||
return await this.sock.sendMessage(
|
||||
request.chatId,
|
||||
{ text: request.text },
|
||||
{ quoted: message },
|
||||
);
|
||||
}
|
||||
|
||||
sendImage(request: MessageImageRequest) {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendFile(request: MessageFileRequest) {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendVoice(request: MessageVoiceRequest) {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendLocation(request: MessageLocationRequest) {
|
||||
return this.sock.sendMessage(request.chatId, {
|
||||
location: {
|
||||
degreesLatitude: request.latitude,
|
||||
degreesLongitude: request.longitude,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
const text = `${request.title}\n${request.url}`;
|
||||
const chatId = this.ensureSuffix(request.chatId);
|
||||
return this.sock.sendMessage(chatId, { text: text });
|
||||
}
|
||||
|
||||
async sendSeen(request: SendSeenRequest) {
|
||||
const key = parseMessageId(request.messageId);
|
||||
const data = {
|
||||
remoteJid: key.remoteJid,
|
||||
id: key.id,
|
||||
participant: request.participant,
|
||||
};
|
||||
return this.sock.readMessages([data]);
|
||||
}
|
||||
|
||||
async startTyping(request: ChatRequest) {
|
||||
return this.sock.sendPresenceUpdate('composing', request.chatId);
|
||||
}
|
||||
|
||||
async stopTyping(request: ChatRequest) {
|
||||
return this.sock.sendPresenceUpdate('paused', request.chatId);
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const key = parseMessageId(request.messageId);
|
||||
const reactionMessage = {
|
||||
react: {
|
||||
text: request.reaction,
|
||||
key: key,
|
||||
},
|
||||
};
|
||||
return this.sock.sendMessage(key.remoteJid, reactionMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
getContact(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
getContacts() {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public async getContactAbout(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public async getContactProfilePicture(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public async blockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public async unblockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupCreate(request.name, participants);
|
||||
}
|
||||
|
||||
public async getGroups() {
|
||||
return await this.sock.groupFetchAllParticipating();
|
||||
}
|
||||
|
||||
public async getGroup(id) {
|
||||
const groups = await this.sock.groupFetchAllParticipating();
|
||||
return groups[id];
|
||||
}
|
||||
|
||||
public async deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public async leaveGroup(id) {
|
||||
return this.sock.groupLeave(id);
|
||||
}
|
||||
|
||||
public async setDescription(id, description) {
|
||||
return this.sock.groupUpdateDescription(id, description);
|
||||
}
|
||||
|
||||
public async setSubject(id, subject) {
|
||||
return this.sock.groupUpdateSubject(id, subject);
|
||||
}
|
||||
|
||||
public async getInviteCode(id): Promise<string> {
|
||||
return this.sock.groupInviteCode(id);
|
||||
}
|
||||
|
||||
public async revokeInviteCode(id): Promise<string> {
|
||||
await this.sock.groupRevokeInvite(id);
|
||||
return this.sock.groupInviteCode(id);
|
||||
}
|
||||
|
||||
public async getParticipants(id) {
|
||||
const groups = await this.sock.groupFetchAllParticipating();
|
||||
return groups[id].participants;
|
||||
}
|
||||
|
||||
public async addParticipants(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'add');
|
||||
}
|
||||
|
||||
public async removeParticipants(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'remove');
|
||||
}
|
||||
|
||||
public async promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'promote');
|
||||
}
|
||||
|
||||
public async demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'demote');
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
subscribe(event, handler) {
|
||||
if (event === WAEvents.MESSAGE) {
|
||||
return this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) =>
|
||||
this.handleIncomingMessages(messages, handler, false),
|
||||
);
|
||||
} else if (event === WAEvents.MESSAGE_ANY) {
|
||||
return this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) =>
|
||||
this.handleIncomingMessages(messages, handler, true),
|
||||
);
|
||||
} else if (event === WAEvents.STATE_CHANGE) {
|
||||
return this.sock.ev.on(BaileysEvents.CONNECTION_UPDATE, handler);
|
||||
} else if (event === WAEvents.GROUP_JOIN) {
|
||||
return this.sock.ev.on(BaileysEvents.GROUPS_UPSERT, handler);
|
||||
} else {
|
||||
throw new NotImplementedByEngineError(
|
||||
`Engine does not support webhook event: ${event}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private handleIncomingMessages(messages, handler, includeFromMe) {
|
||||
for (const message of messages) {
|
||||
// if there is no text or media message
|
||||
if (!message) return;
|
||||
if (!message.message) return;
|
||||
// Do not include my messages
|
||||
if (!includeFromMe && message.key.fromMe) {
|
||||
continue;
|
||||
}
|
||||
this.processIncomingMessage(message).then(handler);
|
||||
}
|
||||
}
|
||||
|
||||
private processIncomingMessage(message) {
|
||||
return this.downloadMedia(message).then(this.toWAMessage);
|
||||
}
|
||||
|
||||
protected toWAMessage(message): Promise<WAMessage> {
|
||||
const isGroupMessage = Boolean(message.key.participant);
|
||||
let participant: string;
|
||||
let to: string;
|
||||
if (isGroupMessage) {
|
||||
participant = message.key.participant;
|
||||
to = message.key.remoteJid;
|
||||
}
|
||||
const from = message.key.remoteJid;
|
||||
const id = buildMessageId(message.key);
|
||||
let body = message.message.conversation;
|
||||
if (!body) {
|
||||
// Some of the messages have no conversation, but instead have text in extendedTextMessage
|
||||
// https://github.com/devlikeapro/whatsapp-http-api/issues/90
|
||||
body = message.message.extendedTextMessage?.text;
|
||||
}
|
||||
return Promise.resolve({
|
||||
id: id,
|
||||
timestamp: message.messageTimestamp,
|
||||
from: toCusFormat(from),
|
||||
fromMe: message.key.fromMe,
|
||||
body: body,
|
||||
to: toCusFormat(to),
|
||||
participant: toCusFormat(participant),
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
location: message.location,
|
||||
vCards: message.vCards,
|
||||
_data: message,
|
||||
});
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: Message) {
|
||||
if (!message.hasMedia) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(message.key.id, '', undefined);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from 11111111111@s.whatsapp.net to 11111111111@c.us
|
||||
*/
|
||||
function toCusFormat(remoteJid) {
|
||||
if (isJidGroup(remoteJid)) {
|
||||
return remoteJid;
|
||||
}
|
||||
if (!remoteJid) {
|
||||
return;
|
||||
}
|
||||
const number = remoteJid.split('@')[0];
|
||||
return ensureSuffix(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from 11111111111@c.us to 11111111111@s.whatsapp.net
|
||||
* @param chatId
|
||||
*/
|
||||
function toJID(chatId) {
|
||||
if (isJidGroup(chatId)) {
|
||||
return chatId;
|
||||
}
|
||||
const number = chatId.split('@')[0];
|
||||
return number + '@s.whatsapp.net';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build WAHA message id from engine one
|
||||
* {id: "AAA", remoteJid: "11111111111@s.whatsapp.net", "fromMe": false}
|
||||
* false_11111111111@c.us_AA
|
||||
*/
|
||||
function buildMessageId({ id, remoteJid, fromMe }) {
|
||||
const chatId = toCusFormat(remoteJid);
|
||||
return `${fromMe || false}_${chatId}_${id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse message id from WAHA to engine
|
||||
* false_11111111111@c.us_AAA
|
||||
* {id: "AAA", remoteJid: "11111111111@s.whatsapp.net", "fromMe": false}
|
||||
*/
|
||||
function parseMessageId(messageId) {
|
||||
const parts = messageId.split('_');
|
||||
if (parts.length != 3) {
|
||||
throw new Error(
|
||||
'Message id be in format false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
);
|
||||
}
|
||||
const fromMe = parts[0] == 'true';
|
||||
const chatId = parts[1];
|
||||
const remoteJid = toJID(chatId);
|
||||
const id = parts[2];
|
||||
return { fromMe: fromMe, id: id, remoteJid: remoteJid };
|
||||
}
|
||||
|
||||
function getId(object) {
|
||||
return object.id;
|
||||
}
|
||||
+255
-217
@@ -1,236 +1,274 @@
|
||||
import {WhatsappSession} from "./abc/session.abc";
|
||||
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
|
||||
import {
|
||||
Button,
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest
|
||||
} from "../structures/chatting.dto";
|
||||
import {WAMessage, WANumberExistResult} from "../structures/responses.dto";
|
||||
import {create, Message, Whatsapp} from "venom-bot";
|
||||
import {WAEvents, WhatsappStatus} from "../structures/enums.dto";
|
||||
import {NotImplementedByEngineError} from "./exceptions";
|
||||
import {MediaStorage} from "./abc/storage.abc";
|
||||
import {UnprocessableEntityException} from "@nestjs/common/exceptions/unprocessable-entity.exception";
|
||||
import {ConsoleLogger} from "@nestjs/common";
|
||||
import {ContactQuery} from "../structures/contacts.dto";
|
||||
|
||||
class QR {
|
||||
private base64: string;
|
||||
|
||||
save(base64) {
|
||||
this.base64 = base64.replace(/^data:image\/png;base64,/, '');
|
||||
}
|
||||
|
||||
get(): Buffer {
|
||||
return Buffer.from(this.base64, "base64")
|
||||
}
|
||||
|
||||
}
|
||||
Button,
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
} from '../structures/chatting.dto';
|
||||
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
|
||||
import { create, Message, Whatsapp } from 'venom-bot';
|
||||
import {
|
||||
WAEvents,
|
||||
WhatsappEngine,
|
||||
WhatsappStatus,
|
||||
} from '../structures/enums.dto';
|
||||
import { NotImplementedByEngineError } from './exceptions';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import { QR } from './QR';
|
||||
|
||||
export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
whatsapp: Whatsapp;
|
||||
private qr: QR
|
||||
engine = WhatsappEngine.NOWEB;
|
||||
|
||||
public constructor(public name: string, protected storage: MediaStorage, protected log: ConsoleLogger) {
|
||||
super(name, storage, log);
|
||||
this.qr = new QR()
|
||||
whatsapp: Whatsapp;
|
||||
private qr: QR;
|
||||
|
||||
public constructor(config) {
|
||||
super(config);
|
||||
this.qr = new QR();
|
||||
}
|
||||
|
||||
protected buildClient() {
|
||||
return create(this.name, this.getCatchQR(), undefined, {
|
||||
headless: true,
|
||||
devtools: false,
|
||||
debug: false,
|
||||
logQR: true,
|
||||
browserArgs: this.getBrowserArgsForPuppeteer(),
|
||||
autoClose: 60000,
|
||||
puppeteerOptions: {},
|
||||
});
|
||||
}
|
||||
|
||||
protected getCatchQR() {
|
||||
return (base64Qrimg, asciiQR, attempts, urlCode) => {
|
||||
this.qr.save(base64Qrimg);
|
||||
this.status = WhatsappStatus.SCAN_QR_CODE;
|
||||
this.log.debug('Number of attempts to read the qrcode: ', attempts);
|
||||
this.log.log('Terminal qrcode:');
|
||||
// Log QR image in console without this.log to make it pretty
|
||||
console.log(asciiQR);
|
||||
};
|
||||
}
|
||||
|
||||
async start() {
|
||||
try {
|
||||
this.whatsapp = await this.buildClient();
|
||||
} catch (error) {
|
||||
this.status = WhatsappStatus.FAILED;
|
||||
this.log.error(error);
|
||||
this.qr.save('');
|
||||
return;
|
||||
}
|
||||
|
||||
protected buildClient() {
|
||||
return create(this.name,
|
||||
this.getCatchQR(),
|
||||
undefined,
|
||||
{
|
||||
headless: true,
|
||||
devtools: false,
|
||||
useChrome: true,
|
||||
debug: false,
|
||||
logQR: true,
|
||||
browserArgs: this.getBrowserArgsForPuppeteer(),
|
||||
autoClose: 60000,
|
||||
puppeteerOptions: {},
|
||||
multidevice: true,
|
||||
}
|
||||
)
|
||||
this.status = WhatsappStatus.WORKING;
|
||||
this.events.emit(WAHAInternalEvent.engine_start);
|
||||
return this;
|
||||
}
|
||||
|
||||
stop() {
|
||||
return this.whatsapp.close();
|
||||
}
|
||||
|
||||
subscribe(event: WAEvents | string, handler: (message) => void) {
|
||||
if (event === WAEvents.MESSAGE) {
|
||||
return this.whatsapp.onMessage((message: Message) =>
|
||||
this.processIncomingMessage(message).then(handler),
|
||||
);
|
||||
} else if (event === WAEvents.MESSAGE_ANY) {
|
||||
return this.whatsapp.onAnyMessage((message: Message) =>
|
||||
this.processIncomingMessage(message).then(handler),
|
||||
);
|
||||
} else if (event === WAEvents.STATE_CHANGE) {
|
||||
return this.whatsapp.onStateChange(handler);
|
||||
} else if (event === WAEvents.MESSAGE_ACK) {
|
||||
return this.whatsapp.onAck(handler);
|
||||
} else if (event === WAEvents.GROUP_JOIN) {
|
||||
return this.whatsapp.onAddedToGroup(handler);
|
||||
} else {
|
||||
throw new NotImplementedByEngineError(
|
||||
`Engine does not support webhook event: ${event}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected getCatchQR() {
|
||||
return (base64Qrimg, asciiQR, attempts, urlCode) => {
|
||||
this.qr.save(base64Qrimg)
|
||||
this.status = WhatsappStatus.SCAN_QR_CODE
|
||||
this.log.debug('Number of attempts to read the qrcode: ', attempts);
|
||||
this.log.log('Terminal qrcode:');
|
||||
// Log QR image in console without this.log to make it pretty
|
||||
console.log(asciiQR);
|
||||
};
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
getScreenshot(): Promise<Buffer | string> {
|
||||
if (this.status === WhatsappStatus.STARTING) {
|
||||
throw new UnprocessableEntityException(
|
||||
`The session is starting, please try again after few seconds`,
|
||||
);
|
||||
} else if (this.status === WhatsappStatus.SCAN_QR_CODE) {
|
||||
return Promise.resolve(this.qr.get());
|
||||
} else if (this.status === WhatsappStatus.WORKING) {
|
||||
return this.whatsapp.page.screenshot();
|
||||
} else {
|
||||
throw new UnprocessableEntityException(`Unknown status - ${this.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
async start() {
|
||||
try {
|
||||
this.whatsapp = await this.buildClient()
|
||||
} catch (error) {
|
||||
this.status = WhatsappStatus.FAILED
|
||||
this.log.error(error)
|
||||
this.qr.save("")
|
||||
return
|
||||
}
|
||||
|
||||
this.status = WhatsappStatus.WORKING
|
||||
return this
|
||||
async checkNumberStatus(
|
||||
request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
try {
|
||||
return await this.whatsapp.checkNumberStatus(
|
||||
this.ensureSuffix(request.phone),
|
||||
);
|
||||
} catch (error) {
|
||||
// Catch number doesn't exist error and return it as is
|
||||
if (error.status === 404 && !error.numberExists) {
|
||||
return error;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
sendContactVCard(request: MessageContactVcardRequest) {
|
||||
return this.whatsapp.sendContactVcard(
|
||||
request.chatId,
|
||||
request.contactsId,
|
||||
request.name,
|
||||
);
|
||||
}
|
||||
|
||||
stop() {
|
||||
return this.whatsapp.close()
|
||||
sendText(request: MessageTextRequest) {
|
||||
return this.whatsapp.sendText(
|
||||
this.ensureSuffix(request.chatId),
|
||||
request.text,
|
||||
);
|
||||
}
|
||||
|
||||
reply(request: MessageReplyRequest) {
|
||||
return this.whatsapp.reply(request.chatId, request.text, request.reply_to);
|
||||
}
|
||||
|
||||
sendFile(request: MessageFileRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
sendImage(request: MessageImageRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
async sendVoice(request) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
return this.whatsapp.sendLinkPreview(
|
||||
request.chatId,
|
||||
request.url,
|
||||
request.title,
|
||||
);
|
||||
}
|
||||
|
||||
sendLocation(request: MessageLocationRequest) {
|
||||
return this.whatsapp.sendLocation(
|
||||
request.chatId,
|
||||
String(request.latitude),
|
||||
String(request.longitude),
|
||||
request.title,
|
||||
);
|
||||
}
|
||||
|
||||
sendSeen(chat: ChatRequest) {
|
||||
return this.whatsapp.markMarkSeenMessage(chat.chatId);
|
||||
}
|
||||
|
||||
sendTextButtons(request: MessageTextButtonsRequest) {
|
||||
const buttons = request.buttons.map((button: Button) => {
|
||||
return {
|
||||
buttonId: button.id,
|
||||
buttonText: {
|
||||
displayText: button.text,
|
||||
},
|
||||
};
|
||||
});
|
||||
return this.whatsapp.sendButtons(
|
||||
this.ensureSuffix(request.chatId),
|
||||
request.title,
|
||||
request.footer,
|
||||
buttons,
|
||||
);
|
||||
}
|
||||
|
||||
startTyping(chat: ChatRequest) {
|
||||
return this.whatsapp.startTyping(chat.chatId, false);
|
||||
}
|
||||
|
||||
stopTyping(chat: ChatRequest) {
|
||||
return;
|
||||
// The method is not available yet in new venom-bot
|
||||
// return this.whatsapp.stopTyping(chat.chatId);
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
const messages = await this.whatsapp.getAllMessagesInChat(
|
||||
query.chatId,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
// Go over messages, download media, and convert to right format.
|
||||
const result = [];
|
||||
for (const [count, message] of messages.entries()) {
|
||||
if (count > query.limit) {
|
||||
// Have enough in the list, stop processing
|
||||
break;
|
||||
}
|
||||
result.push(await this.processIncomingMessage(message));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
subscribe(event: WAEvents | string, handler: (message) => void) {
|
||||
if (event === WAEvents.MESSAGE) {
|
||||
return this.whatsapp.onMessage((message: Message) => this.processIncomingMessage(message).then(handler))
|
||||
} else if (event === WAEvents.MESSAGE_ANY) {
|
||||
return this.whatsapp.onAnyMessage((message: Message) => this.processIncomingMessage(message).then(handler))
|
||||
} else if (event === WAEvents.STATE_CHANGE) {
|
||||
return this.whatsapp.onStateChange(handler)
|
||||
} else if (event === WAEvents.MESSAGE_ACK) {
|
||||
return this.whatsapp.onAck(handler)
|
||||
} else if (event === WAEvents.GROUP_JOIN) {
|
||||
return this.whatsapp.onAddedToGroup(handler)
|
||||
} else {
|
||||
throw new NotImplementedByEngineError(`Engine does not support webhook event: ${event}`)
|
||||
}
|
||||
setReaction(request: MessageReactionRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
protected async downloadAndDecryptMedia(message: Message) {
|
||||
if (!message.isMMS || !message.isMedia) {
|
||||
return message;
|
||||
}
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(message.id, '', undefined);
|
||||
return message;
|
||||
}
|
||||
|
||||
private processIncomingMessage(message: Message) {
|
||||
return this.downloadAndDecryptMedia(message).then(this.toWAMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
getScreenshot(): Promise<Buffer | string> {
|
||||
if (this.status === WhatsappStatus.STARTING) {
|
||||
throw new UnprocessableEntityException(`The session is starting, please try again after few seconds`);
|
||||
} else if (this.status === WhatsappStatus.SCAN_QR_CODE) {
|
||||
return Promise.resolve(this.qr.get())
|
||||
} else if (this.status === WhatsappStatus.WORKING) {
|
||||
return this.whatsapp.page.screenshot()
|
||||
} else {
|
||||
throw new UnprocessableEntityException(`Unknown status - ${this.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
async checkNumberStatus(request: CheckNumberStatusQuery): Promise<WANumberExistResult> {
|
||||
try {
|
||||
return await this.whatsapp.checkNumberStatus(this.ensureSuffix(request.phone))
|
||||
} catch (error) {
|
||||
// Catch number doesn't exist error and return it as is
|
||||
if (error.status === 404 && !error.numberExists){
|
||||
return error
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
sendContactVCard(request: MessageContactVcardRequest) {
|
||||
return this.whatsapp.sendContactVcard(request.chatId, request.contactsId, request.name)
|
||||
}
|
||||
|
||||
sendText(request: MessageTextRequest) {
|
||||
return this.whatsapp.sendText(this.ensureSuffix(request.chatId), request.text)
|
||||
}
|
||||
|
||||
reply(request: MessageReplyRequest) {
|
||||
return this.whatsapp.reply(request.chatId, request.text, request.reply_to)
|
||||
}
|
||||
|
||||
sendFile(request: MessageFileRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
sendImage(request: MessageImageRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
async sendVoice(request) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
return this.whatsapp.sendLinkPreview(request.chatId, request.url, request.title)
|
||||
}
|
||||
|
||||
sendLocation(request: MessageLocationRequest) {
|
||||
return this.whatsapp.sendLocation(request.chatId, String(request.latitude), String(request.longitude), request.title)
|
||||
}
|
||||
|
||||
sendSeen(chat: ChatRequest) {
|
||||
return this.whatsapp.sendSeen(chat.chatId)
|
||||
}
|
||||
|
||||
sendTextButtons(request: MessageTextButtonsRequest) {
|
||||
const buttons = request.buttons.map((button: Button) => {
|
||||
return {
|
||||
buttonId: button.id,
|
||||
buttonText: {
|
||||
displayText: button.body
|
||||
}
|
||||
}
|
||||
})
|
||||
return this.whatsapp.sendButtons(this.ensureSuffix(request.chatId), request.title, buttons, request.text)
|
||||
}
|
||||
|
||||
startTyping(chat: ChatRequest) {
|
||||
return this.whatsapp.startTyping(chat.chatId)
|
||||
}
|
||||
|
||||
stopTyping(chat: ChatRequest) {
|
||||
return this.whatsapp.stopTyping(chat.chatId)
|
||||
}
|
||||
|
||||
setReaction(request: MessageReactionRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
protected async downloadAndDecryptMedia(message: Message) {
|
||||
if (!message.isMMS || !message.isMedia) {
|
||||
return message
|
||||
}
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(message.id, "", undefined)
|
||||
return message
|
||||
}
|
||||
|
||||
private processIncomingMessage(message: Message) {
|
||||
return this.downloadAndDecryptMedia(message).then(this.toWAMessage)
|
||||
}
|
||||
|
||||
protected toWAMessage(message: Message): Promise<WAMessage> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve({
|
||||
id: message.id,
|
||||
timestamp: message.timestamp,
|
||||
from: message.from,
|
||||
fromMe: message.fromMe,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
location: undefined,
|
||||
vCards: undefined,
|
||||
_data: message,
|
||||
})
|
||||
}
|
||||
protected toWAMessage(message: Message): Promise<WAMessage> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve({
|
||||
id: message.id,
|
||||
timestamp: message.timestamp,
|
||||
from: message.from,
|
||||
fromMe: message.fromMe,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
location: undefined,
|
||||
vCards: undefined,
|
||||
_data: message,
|
||||
});
|
||||
}
|
||||
}
|
||||
+406
-319
@@ -1,329 +1,416 @@
|
||||
import {UnprocessableEntityException} from "@nestjs/common/exceptions/unprocessable-entity.exception";
|
||||
import {Buttons, Chat, Client, Contact, Events, GroupChat, Location, Message} from "whatsapp-web.js";
|
||||
import {Message as MessageInstance} from "whatsapp-web.js/src/structures"
|
||||
import {WAEvents, WhatsappStatus} from "../structures/enums.dto";
|
||||
import {WhatsappSession} from "./abc/session.abc";
|
||||
import {WAMessage, WANumberExistResult} from "../structures/responses.dto";
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest
|
||||
} from "../structures/chatting.dto";
|
||||
import {AvailableInPlusVersion, NotImplementedByEngineError} from "./exceptions";
|
||||
import {ContactQuery, ContactRequest} from "../structures/contacts.dto";
|
||||
import {CreateGroupRequest, ParticipantsRequest} from "../structures/groups.dto";
|
||||
Buttons,
|
||||
Chat,
|
||||
Client,
|
||||
Contact,
|
||||
Events,
|
||||
GroupChat,
|
||||
Location,
|
||||
Message,
|
||||
} from 'whatsapp-web.js';
|
||||
import { Message as MessageInstance } from 'whatsapp-web.js/src/structures';
|
||||
import {
|
||||
WAEvents,
|
||||
WhatsappEngine,
|
||||
WhatsappStatus,
|
||||
} from '../structures/enums.dto';
|
||||
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
|
||||
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
|
||||
import {
|
||||
Button,
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest,
|
||||
SendSeenRequest,
|
||||
} from '../structures/chatting.dto';
|
||||
import {
|
||||
AvailableInPlusVersion,
|
||||
NotImplementedByEngineError,
|
||||
} from './exceptions';
|
||||
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
ParticipantsRequest,
|
||||
} from '../structures/groups.dto';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const qrcode = require('qrcode-terminal');
|
||||
|
||||
|
||||
export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
whatsapp: Client;
|
||||
engine = WhatsappEngine.NOWEB;
|
||||
|
||||
protected buildClient() {
|
||||
return new Client({
|
||||
puppeteer: {
|
||||
headless: true,
|
||||
executablePath: this.getBrowserExecutablePath(),
|
||||
args: this.getBrowserArgsForPuppeteer(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.whatsapp = this.buildClient()
|
||||
|
||||
this.whatsapp.initialize().catch(error => {
|
||||
this.status = WhatsappStatus.FAILED
|
||||
this.log.error(error)
|
||||
return
|
||||
});
|
||||
|
||||
// Connect events
|
||||
this.whatsapp.on(Events.QR_RECEIVED, (qr) => {
|
||||
qrcode.generate(qr, {small: true});
|
||||
this.status = WhatsappStatus.SCAN_QR_CODE
|
||||
});
|
||||
|
||||
this.whatsapp.on(Events.AUTHENTICATED, () => {
|
||||
this.status = WhatsappStatus.WORKING
|
||||
this.log.log(`Session '${this.name}' has been authenticated!`)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
stop() {
|
||||
return this.whatsapp.destroy()
|
||||
}
|
||||
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
async getScreenshot(): Promise<Buffer | string> {
|
||||
if (this.status === WhatsappStatus.FAILED) {
|
||||
throw new UnprocessableEntityException(`The session under FAILED status. Please try to restart it.`);
|
||||
}
|
||||
return await this.whatsapp.pupPage.screenshot()
|
||||
}
|
||||
|
||||
async checkNumberStatus(request: CheckNumberStatusQuery): Promise<WANumberExistResult> {
|
||||
const exist = await this.whatsapp.isRegisteredUser(this.ensureSuffix(request.phone))
|
||||
return {numberExists: exist}
|
||||
}
|
||||
|
||||
sendText(request: MessageTextRequest) {
|
||||
return this.whatsapp.sendMessage(this.ensureSuffix(request.chatId), request.text)
|
||||
}
|
||||
|
||||
sendTextButtons(request: MessageTextButtonsRequest) {
|
||||
const message = new Buttons("", request.buttons, request.title, request.text)
|
||||
return this.whatsapp.sendMessage(this.ensureSuffix(request.chatId), message)
|
||||
}
|
||||
|
||||
sendContactVCard(request: MessageContactVcardRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
reply(request: MessageReplyRequest) {
|
||||
const options = {
|
||||
quotedMessageId: request.reply_to,
|
||||
};
|
||||
return this.whatsapp.sendMessage(request.chatId, request.text, options)
|
||||
}
|
||||
|
||||
sendImage(request: MessageImageRequest) {
|
||||
throw new AvailableInPlusVersion()
|
||||
}
|
||||
|
||||
sendFile(request: MessageFileRequest) {
|
||||
throw new AvailableInPlusVersion()
|
||||
}
|
||||
|
||||
sendVoice(request: MessageVoiceRequest) {
|
||||
throw new AvailableInPlusVersion()
|
||||
}
|
||||
|
||||
async sendLocation(request: MessageLocationRequest) {
|
||||
const location = new Location(
|
||||
request.latitude,
|
||||
request.longitude,
|
||||
request.title,
|
||||
)
|
||||
return this.whatsapp.sendMessage(request.chatId, location)
|
||||
}
|
||||
|
||||
sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
|
||||
async sendSeen(request: ChatRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId)
|
||||
await chat.sendSeen()
|
||||
}
|
||||
|
||||
async startTyping(request: ChatRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId)
|
||||
await chat.sendStateTyping()
|
||||
}
|
||||
|
||||
async stopTyping(request: ChatRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId)
|
||||
await chat.clearState()
|
||||
}
|
||||
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const messageId = this.deserializeId(request.messageId)
|
||||
console.log(messageId)
|
||||
|
||||
// Recreate instance to react on it
|
||||
const message = new MessageInstance(this.whatsapp)
|
||||
message.id = messageId
|
||||
message._data = {id: messageId}
|
||||
|
||||
return message.react(request.reaction)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
getContact(query: ContactQuery) {
|
||||
return this.whatsapp.getContactById(this.ensureSuffix(query.contactId)).then(this.toWAContact)
|
||||
}
|
||||
|
||||
getContacts() {
|
||||
return this.whatsapp.getContacts().then(contacts => contacts.map(this.toWAContact))
|
||||
}
|
||||
|
||||
public async getContactAbout(query: ContactQuery) {
|
||||
const contact = await this.whatsapp.getContactById(this.ensureSuffix(query.contactId))
|
||||
return {"about": await contact.getAbout()}
|
||||
}
|
||||
|
||||
public async getContactProfilePicture(query: ContactQuery) {
|
||||
const contact = await this.whatsapp.getContactById(this.ensureSuffix(query.contactId))
|
||||
return {"profilePictureURL": await contact.getProfilePicUrl()}
|
||||
}
|
||||
|
||||
public async blockContact(request: ContactRequest) {
|
||||
const contact = await this.whatsapp.getContactById(this.ensureSuffix(request.contactId))
|
||||
await contact.block()
|
||||
}
|
||||
|
||||
public async unblockContact(request: ContactRequest) {
|
||||
const contact = await this.whatsapp.getContactById(this.ensureSuffix(request.contactId))
|
||||
await contact.unblock()
|
||||
}
|
||||
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
const participantIds = request.participants.map(participant => participant.id)
|
||||
return this.whatsapp.createGroup(request.name, participantIds)
|
||||
}
|
||||
|
||||
public getGroups() {
|
||||
return this.whatsapp.getChats().then(chats => chats.filter(chat => chat.isGroup))
|
||||
}
|
||||
|
||||
public getGroup(id) {
|
||||
return this.whatsapp.getChatById(id)
|
||||
}
|
||||
|
||||
public async deleteGroup(id) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
return groupChat.delete()
|
||||
}
|
||||
|
||||
public async leaveGroup(id) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
return groupChat.leave()
|
||||
}
|
||||
|
||||
public async setDescription(id, description) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
return groupChat.setDescription(description)
|
||||
}
|
||||
|
||||
public async setSubject(id, subject) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
return groupChat.setSubject(subject)
|
||||
}
|
||||
|
||||
public async getInviteCode(id): Promise<string> {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
return groupChat.getInviteCode()
|
||||
}
|
||||
|
||||
public async revokeInviteCode(id): Promise<string> {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
await groupChat.revokeInvite()
|
||||
return groupChat.getInviteCode()
|
||||
}
|
||||
|
||||
public async getParticipants(id) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
return groupChat.participants
|
||||
}
|
||||
|
||||
public async addParticipants(id, request: ParticipantsRequest) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
const participantIds = request.participants.map(participant => participant.id)
|
||||
return groupChat.addParticipants(participantIds)
|
||||
}
|
||||
|
||||
public async removeParticipants(id, request: ParticipantsRequest) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
const participantIds = request.participants.map(participant => participant.id)
|
||||
return groupChat.removeParticipants(participantIds)
|
||||
}
|
||||
|
||||
public async promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
const participantIds = request.participants.map(participant => participant.id)
|
||||
return groupChat.promoteParticipants(participantIds)
|
||||
}
|
||||
|
||||
public async demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
const groupChat = await this.whatsapp.getChatById(id) as GroupChat
|
||||
const participantIds = request.participants.map(participant => participant.id)
|
||||
return groupChat.demoteParticipants(participantIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
subscribe(event, handler) {
|
||||
if (event === WAEvents.MESSAGE) {
|
||||
this.whatsapp.on(Events.MESSAGE_RECEIVED, (message) => this.processIncomingMessage(message).then(handler))
|
||||
} else if (event === WAEvents.MESSAGE_ANY) {
|
||||
this.whatsapp.on(Events.MESSAGE_CREATE, (message) => this.processIncomingMessage(message).then(handler))
|
||||
} else if (event === WAEvents.STATE_CHANGE) {
|
||||
this.whatsapp.on(Events.STATE_CHANGED, handler)
|
||||
} else if (event === WAEvents.MESSAGE_ACK) {
|
||||
// We do not download media here
|
||||
this.whatsapp.on(Events.MESSAGE_ACK, (message) => this.toWAMessage(message).then(handler))
|
||||
} else if (event === WAEvents.GROUP_JOIN) {
|
||||
this.whatsapp.on(Events.GROUP_JOIN, handler)
|
||||
} else if (event === WAEvents.GROUP_LEAVE) {
|
||||
this.whatsapp.on(Events.GROUP_LEAVE, handler)
|
||||
} else {
|
||||
throw new NotImplementedByEngineError(`Engine does not support webhook event: ${event}`)
|
||||
}
|
||||
}
|
||||
|
||||
private processIncomingMessage(message: Message) {
|
||||
return this.downloadMedia(message).then(this.toWAMessage)
|
||||
}
|
||||
|
||||
protected toWAMessage(message: Message): Promise<WAMessage> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve({
|
||||
id: message.id._serialized,
|
||||
timestamp: message.timestamp,
|
||||
from: message.from,
|
||||
fromMe: message.fromMe,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
location: message.location,
|
||||
vCards: message.vCards,
|
||||
_data: message.rawData,
|
||||
})
|
||||
}
|
||||
|
||||
protected toWAContact(contact: Contact) {
|
||||
// @ts-ignore
|
||||
contact.id = contact.id._serialized
|
||||
return contact
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: Message) {
|
||||
if (!message.hasMedia) {
|
||||
return message
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(message.id._serialized, "", undefined)
|
||||
return message
|
||||
whatsapp: Client;
|
||||
|
||||
protected buildClient() {
|
||||
return new Client({
|
||||
puppeteer: {
|
||||
headless: true,
|
||||
executablePath: this.getBrowserExecutablePath(),
|
||||
args: this.getBrowserArgsForPuppeteer(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.whatsapp = this.buildClient();
|
||||
|
||||
this.whatsapp.initialize().catch((error) => {
|
||||
this.status = WhatsappStatus.FAILED;
|
||||
this.log.error(error);
|
||||
return;
|
||||
});
|
||||
|
||||
// Connect events
|
||||
this.whatsapp.on(Events.QR_RECEIVED, (qr) => {
|
||||
qrcode.generate(qr, { small: true });
|
||||
this.status = WhatsappStatus.SCAN_QR_CODE;
|
||||
});
|
||||
|
||||
this.whatsapp.on(Events.AUTHENTICATED, () => {
|
||||
this.status = WhatsappStatus.WORKING;
|
||||
this.log.log(`Session '${this.name}' has been authenticated!`);
|
||||
});
|
||||
this.events.emit(WAHAInternalEvent.engine_start);
|
||||
return this;
|
||||
}
|
||||
|
||||
stop() {
|
||||
return this.whatsapp.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
async getScreenshot(): Promise<Buffer | string> {
|
||||
if (this.status === WhatsappStatus.FAILED) {
|
||||
throw new UnprocessableEntityException(
|
||||
`The session under FAILED status. Please try to restart it.`,
|
||||
);
|
||||
}
|
||||
return await this.whatsapp.pupPage.screenshot();
|
||||
}
|
||||
|
||||
async checkNumberStatus(
|
||||
request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
const exist = await this.whatsapp.isRegisteredUser(
|
||||
this.ensureSuffix(request.phone),
|
||||
);
|
||||
return { numberExists: exist };
|
||||
}
|
||||
|
||||
sendText(request: MessageTextRequest) {
|
||||
return this.whatsapp.sendMessage(
|
||||
this.ensureSuffix(request.chatId),
|
||||
request.text,
|
||||
);
|
||||
}
|
||||
|
||||
sendTextButtons(request: MessageTextButtonsRequest) {
|
||||
const buttons = request.buttons.map((button: Button) => {
|
||||
return {
|
||||
id: button.id,
|
||||
body: button.text,
|
||||
};
|
||||
});
|
||||
const message = new Buttons('', buttons, request.title, request.footer);
|
||||
return this.whatsapp.sendMessage(
|
||||
this.ensureSuffix(request.chatId),
|
||||
message,
|
||||
);
|
||||
}
|
||||
|
||||
sendContactVCard(request: MessageContactVcardRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
reply(request: MessageReplyRequest) {
|
||||
const options = {
|
||||
quotedMessageId: request.reply_to,
|
||||
};
|
||||
return this.whatsapp.sendMessage(request.chatId, request.text, options);
|
||||
}
|
||||
|
||||
sendImage(request: MessageImageRequest) {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendFile(request: MessageFileRequest) {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendVoice(request: MessageVoiceRequest) {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
async sendLocation(request: MessageLocationRequest) {
|
||||
const location = new Location(
|
||||
request.latitude,
|
||||
request.longitude,
|
||||
request.title,
|
||||
);
|
||||
return this.whatsapp.sendMessage(request.chatId, location);
|
||||
}
|
||||
|
||||
sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
async sendSeen(request: SendSeenRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId);
|
||||
await chat.sendSeen();
|
||||
}
|
||||
|
||||
async startTyping(request: ChatRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId);
|
||||
await chat.sendStateTyping();
|
||||
}
|
||||
|
||||
async stopTyping(request: ChatRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId);
|
||||
await chat.clearState();
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(
|
||||
this.ensureSuffix(query.chatId),
|
||||
);
|
||||
const messages = await chat.fetchMessages({
|
||||
limit: query.limit,
|
||||
});
|
||||
// Go over messages, download media, and convert to right format.
|
||||
const result = [];
|
||||
for (const message of messages) {
|
||||
result.push(await this.processIncomingMessage(message));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const messageId = this.deserializeId(request.messageId);
|
||||
// Recreate instance to react on it
|
||||
const message = new MessageInstance(this.whatsapp);
|
||||
message.id = messageId;
|
||||
message._data = { id: messageId };
|
||||
|
||||
return message.react(request.reaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
getContact(query: ContactQuery) {
|
||||
return this.whatsapp
|
||||
.getContactById(this.ensureSuffix(query.contactId))
|
||||
.then(this.toWAContact);
|
||||
}
|
||||
|
||||
getContacts() {
|
||||
return this.whatsapp
|
||||
.getContacts()
|
||||
.then((contacts) => contacts.map(this.toWAContact));
|
||||
}
|
||||
|
||||
public async getContactAbout(query: ContactQuery) {
|
||||
const contact = await this.whatsapp.getContactById(
|
||||
this.ensureSuffix(query.contactId),
|
||||
);
|
||||
return { about: await contact.getAbout() };
|
||||
}
|
||||
|
||||
public async getContactProfilePicture(query: ContactQuery) {
|
||||
const contact = await this.whatsapp.getContactById(
|
||||
this.ensureSuffix(query.contactId),
|
||||
);
|
||||
return { profilePictureURL: await contact.getProfilePicUrl() };
|
||||
}
|
||||
|
||||
public async blockContact(request: ContactRequest) {
|
||||
const contact = await this.whatsapp.getContactById(
|
||||
this.ensureSuffix(request.contactId),
|
||||
);
|
||||
await contact.block();
|
||||
}
|
||||
|
||||
public async unblockContact(request: ContactRequest) {
|
||||
const contact = await this.whatsapp.getContactById(
|
||||
this.ensureSuffix(request.contactId),
|
||||
);
|
||||
await contact.unblock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
const participantIds = request.participants.map(
|
||||
(participant) => participant.id,
|
||||
);
|
||||
return this.whatsapp.createGroup(request.name, participantIds);
|
||||
}
|
||||
|
||||
public getGroups() {
|
||||
return this.whatsapp
|
||||
.getChats()
|
||||
.then((chats) => chats.filter((chat) => chat.isGroup));
|
||||
}
|
||||
|
||||
public getGroup(id) {
|
||||
return this.whatsapp.getChatById(id);
|
||||
}
|
||||
|
||||
public async deleteGroup(id) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.delete();
|
||||
}
|
||||
|
||||
public async leaveGroup(id) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.leave();
|
||||
}
|
||||
|
||||
public async setDescription(id, description) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setDescription(description);
|
||||
}
|
||||
|
||||
public async setSubject(id, subject) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setSubject(subject);
|
||||
}
|
||||
|
||||
public async getInviteCode(id): Promise<string> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.getInviteCode();
|
||||
}
|
||||
|
||||
public async revokeInviteCode(id): Promise<string> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
await groupChat.revokeInvite();
|
||||
return groupChat.getInviteCode();
|
||||
}
|
||||
|
||||
public async getParticipants(id) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.participants;
|
||||
}
|
||||
|
||||
public async addParticipants(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
(participant) => participant.id,
|
||||
);
|
||||
return groupChat.addParticipants(participantIds);
|
||||
}
|
||||
|
||||
public async removeParticipants(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
(participant) => participant.id,
|
||||
);
|
||||
return groupChat.removeParticipants(participantIds);
|
||||
}
|
||||
|
||||
public async promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
(participant) => participant.id,
|
||||
);
|
||||
return groupChat.promoteParticipants(participantIds);
|
||||
}
|
||||
|
||||
public async demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
(participant) => participant.id,
|
||||
);
|
||||
return groupChat.demoteParticipants(participantIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
subscribe(event, handler) {
|
||||
if (event === WAEvents.MESSAGE) {
|
||||
this.whatsapp.on(Events.MESSAGE_RECEIVED, (message) =>
|
||||
this.processIncomingMessage(message).then(handler),
|
||||
);
|
||||
} else if (event === WAEvents.MESSAGE_ANY) {
|
||||
this.whatsapp.on(Events.MESSAGE_CREATE, (message) =>
|
||||
this.processIncomingMessage(message).then(handler),
|
||||
);
|
||||
} else if (event === WAEvents.STATE_CHANGE) {
|
||||
this.whatsapp.on(Events.STATE_CHANGED, handler);
|
||||
} else if (event === WAEvents.MESSAGE_ACK) {
|
||||
// We do not download media here
|
||||
this.whatsapp.on(Events.MESSAGE_ACK, (message) =>
|
||||
this.toWAMessage(message).then(handler),
|
||||
);
|
||||
} else if (event === WAEvents.GROUP_JOIN) {
|
||||
this.whatsapp.on(Events.GROUP_JOIN, handler);
|
||||
} else if (event === WAEvents.GROUP_LEAVE) {
|
||||
this.whatsapp.on(Events.GROUP_LEAVE, handler);
|
||||
} else {
|
||||
throw new NotImplementedByEngineError(
|
||||
`Engine does not support webhook event: ${event}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private processIncomingMessage(message: Message) {
|
||||
return this.downloadMedia(message).then(this.toWAMessage);
|
||||
}
|
||||
|
||||
protected toWAMessage(message: Message): Promise<WAMessage> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve({
|
||||
id: message.id._serialized,
|
||||
timestamp: message.timestamp,
|
||||
from: message.from,
|
||||
fromMe: message.fromMe,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
location: message.location,
|
||||
vCards: message.vCards,
|
||||
_data: message.rawData,
|
||||
});
|
||||
}
|
||||
|
||||
protected toWAContact(contact: Contact) {
|
||||
// @ts-ignore
|
||||
contact.id = contact.id._serialized;
|
||||
return contact;
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: Message) {
|
||||
if (!message.hasMedia) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(
|
||||
message.id._serialized,
|
||||
'',
|
||||
undefined,
|
||||
);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,50 @@
|
||||
import {DOCS_URL} from "./exceptions";
|
||||
import {MediaStorage} from "./abc/storage.abc";
|
||||
import { DOCS_URL } from './exceptions';
|
||||
import { LocalSessionStorage, MediaStorage } from './abc/storage.abc';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
export class MediaStorageCore implements MediaStorage {
|
||||
async save(messageId: string, mimetype: string, buffer: Buffer): Promise<string>{
|
||||
return Promise.resolve(`Media attachment's available only in WAHA Plus version. ${DOCS_URL}`)
|
||||
}
|
||||
async save(
|
||||
messageId: string,
|
||||
mimetype: string,
|
||||
buffer: Buffer,
|
||||
): Promise<string> {
|
||||
return Promise.resolve(
|
||||
`Media attachment's available only in WAHA Plus version. ${DOCS_URL}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class SessionStorageCore extends LocalSessionStorage {
|
||||
public sessionsFolder: string;
|
||||
|
||||
constructor(engine: string) {
|
||||
super(engine);
|
||||
this.sessionsFolder = path.join(os.tmpdir(), 'waha-');
|
||||
}
|
||||
|
||||
get engineFolder() {
|
||||
return path.join(this.sessionsFolder, this.engine);
|
||||
}
|
||||
|
||||
init() {
|
||||
fs.mkdirSync(this.engineFolder, { recursive: true });
|
||||
}
|
||||
|
||||
getFolderPath(name: string): string {
|
||||
return path.join(this.engineFolder, name);
|
||||
}
|
||||
|
||||
clean(sessionName: string) {
|
||||
const folder = this.getFolderPath(sessionName);
|
||||
fs.rmSync(folder, { recursive: true });
|
||||
}
|
||||
|
||||
getAll(): string[] {
|
||||
return fs
|
||||
.readdirSync(this.engineFolder, { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,66 @@
|
||||
import {DocumentBuilder, SwaggerModule} from "@nestjs/swagger";
|
||||
import {INestApplication} from "@nestjs/common";
|
||||
import {VERSION} from "../version";
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { VERSION } from '../version';
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
|
||||
export class SwaggerModuleCore {
|
||||
configure(app: INestApplication) {
|
||||
this.setUpAuth(app)
|
||||
configure(app: INestApplication) {
|
||||
this.setUpAuth(app);
|
||||
const builder = new DocumentBuilder();
|
||||
|
||||
const options = new DocumentBuilder()
|
||||
.setTitle('WAHA - WhatsApp HTTP API')
|
||||
.setDescription('WhatsApp HTTP API that you can configure in a click!')
|
||||
.setExternalDoc("Documentation", "https://waha.devlike.pro/")
|
||||
.setVersion(VERSION.version)
|
||||
.addTag('sessions', 'Control WhatsApp sessions')
|
||||
.addTag('screenshot', 'Get screenshot of WhatsApp and show QR code')
|
||||
.addTag('chatting', 'Chat methods')
|
||||
.addTag(
|
||||
'contacts',
|
||||
`Contacts methods.<br>
|
||||
builder
|
||||
.setTitle('WAHA - WhatsApp HTTP API')
|
||||
.setDescription('WhatsApp HTTP API that you can configure in a click!')
|
||||
.setExternalDoc('Documentation', 'https://waha.devlike.pro/')
|
||||
.setVersion(VERSION.version)
|
||||
.addTag('sessions', 'Control WhatsApp sessions')
|
||||
.addTag('screenshot', 'Get screenshot of WhatsApp and show QR code')
|
||||
.addTag('chatting', 'Chat methods')
|
||||
.addTag(
|
||||
'contacts',
|
||||
`Contacts methods.<br>
|
||||
Use phone number (without +) or phone number and \`@c.us\` at the end as \`contactId\`.<br>
|
||||
'E.g: \`12312312310\` OR \`12312312310@c.us\`<br>`
|
||||
)
|
||||
.addTag(
|
||||
'groups',
|
||||
`Groups methods.<br>`
|
||||
)
|
||||
.addTag('other', 'Other methods')
|
||||
.addApiKey({
|
||||
type: 'apiKey',
|
||||
description: 'Your secret api key',
|
||||
name: 'X-Api-Key'
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, options);
|
||||
SwaggerModule.setup('', app, document);
|
||||
'E.g: \`12312312310\` OR \`12312312310@c.us\`<br>`,
|
||||
)
|
||||
.addTag('groups', `Groups methods.<br>`)
|
||||
.addTag('other', 'Other methods')
|
||||
.addApiKey({
|
||||
type: 'apiKey',
|
||||
description: 'Your secret api key',
|
||||
name: 'X-Api-Key',
|
||||
});
|
||||
|
||||
const config = app.get(WhatsappConfigService);
|
||||
if (config.getSwaggerAdvancedConfigEnabled()) {
|
||||
builder.addServer('{protocol}://{host}:{port}/{baseUrl}', '', {
|
||||
protocol: {
|
||||
default: 'http',
|
||||
enum: ['http', 'https'],
|
||||
description: 'The protocol used to access the server.',
|
||||
},
|
||||
host: {
|
||||
default: config.hostname,
|
||||
description: 'The hostname or IP address of the server.',
|
||||
},
|
||||
port: {
|
||||
default: config.port,
|
||||
description:
|
||||
'The port number on which the server is listening for requests',
|
||||
},
|
||||
baseUrl: {
|
||||
default: '',
|
||||
description:
|
||||
'The base URL path for all API endpoints. This can be used to group related endpoints together under a common path.',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected setUpAuth(app: INestApplication) {
|
||||
return undefined
|
||||
}
|
||||
const options = builder.build();
|
||||
const document = SwaggerModule.createDocument(app, options);
|
||||
SwaggerModule.setup('', app, document);
|
||||
}
|
||||
|
||||
protected setUpAuth(app: INestApplication) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
+72
-65
@@ -1,76 +1,83 @@
|
||||
import {WhatsappSession} from "./abc/session.abc";
|
||||
import {ConsoleLogger} from "@nestjs/common";
|
||||
import {WAEvents} from "../structures/enums.dto";
|
||||
import {WAWebhook} from "../structures/responses.dto";
|
||||
import {NotImplementedByEngineError} from "./exceptions";
|
||||
import {WebhookConductor} from "./abc/webhooks.abc";
|
||||
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
|
||||
import { ConsoleLogger } from '@nestjs/common';
|
||||
import { WAEvents } from '../structures/enums.dto';
|
||||
import { WAWebhook } from '../structures/responses.dto';
|
||||
import { NotImplementedByEngineError } from './exceptions';
|
||||
import { WebhookConductor } from './abc/webhooks.abc';
|
||||
import request = require('request');
|
||||
|
||||
|
||||
export class WebhookConductorCore implements WebhookConductor {
|
||||
constructor(protected log: ConsoleLogger, private readonly url, private readonly events: WAEvents[] | string[]) {
|
||||
this.url = url
|
||||
this.events = this.getSuitableEvents(events)
|
||||
this.log = log
|
||||
constructor(
|
||||
protected log: ConsoleLogger,
|
||||
private readonly url,
|
||||
private readonly events: WAEvents[] | string[],
|
||||
) {
|
||||
this.url = url;
|
||||
this.events = this.getSuitableEvents(events);
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
private getSuitableEvents(events: WAEvents[] | string[]) {
|
||||
const allEvents = Object.values(WAEvents);
|
||||
|
||||
// Enable all events if * in the events
|
||||
// @ts-ignore
|
||||
if (events.includes('*')) {
|
||||
return allEvents;
|
||||
}
|
||||
|
||||
private getSuitableEvents(events: WAEvents[] | string[]) {
|
||||
const allEvents = Object.values(WAEvents)
|
||||
// Get only known events, log and ignore others
|
||||
const rightEvents = [];
|
||||
for (const event of events) {
|
||||
// @ts-ignore
|
||||
if (!allEvents.includes(event)) {
|
||||
this.log.error(`Unknown event for webhook: '${event}'`);
|
||||
continue;
|
||||
}
|
||||
rightEvents.push(event);
|
||||
}
|
||||
return rightEvents;
|
||||
}
|
||||
|
||||
// Enable all events if * in the events
|
||||
// @ts-ignore
|
||||
if (events.includes("*")) {
|
||||
return allEvents
|
||||
public configure(session: WhatsappSession) {
|
||||
this.log.log('Configuring webhooks...');
|
||||
for (const event of this.events) {
|
||||
try {
|
||||
session.subscribe(event, (data: any) =>
|
||||
this.callWebhook(event, session, data, this.url),
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof NotImplementedByEngineError) {
|
||||
this.log.error(error);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Get only known events, log and ignore others
|
||||
const rightEvents = []
|
||||
for (const event of events) {
|
||||
// @ts-ignore
|
||||
if (!allEvents.includes(event)) {
|
||||
this.log.error(`Unknown event for webhook: '${event}'`)
|
||||
continue
|
||||
}
|
||||
rightEvents.push(event)
|
||||
}
|
||||
return rightEvents
|
||||
}
|
||||
this.log.log(`Event '${event}' is enabled for url: ${this.url}`);
|
||||
}
|
||||
this.log.log('Webhooks were configured.');
|
||||
}
|
||||
|
||||
public configure(session: WhatsappSession) {
|
||||
this.log.log('Configuring webhooks...')
|
||||
for (const event of this.events) {
|
||||
try {
|
||||
session.subscribe(event, (data: any) => this.callWebhook(event, session, data, this.url))
|
||||
} catch (error) {
|
||||
if (error instanceof NotImplementedByEngineError) {
|
||||
this.log.error(error)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
this.log.log(`Event '${event}' is enabled for url: ${this.url}`)
|
||||
}
|
||||
this.log.log('Webhooks were configured.')
|
||||
}
|
||||
public callWebhook(event, session: WhatsappSession, data: any, url) {
|
||||
const json: WAWebhook = {
|
||||
event: event,
|
||||
session: session.name,
|
||||
payload: data,
|
||||
engine: session.engine,
|
||||
};
|
||||
this.log.log(`Sending POST to ${url}...`);
|
||||
this.log.debug(`POST DATA: ${JSON.stringify(json)}`);
|
||||
this.post(json, url);
|
||||
}
|
||||
|
||||
public callWebhook(event, session: WhatsappSession, data: any, url) {
|
||||
const json: WAWebhook = {event: event, session: session.name, payload: data}
|
||||
this.log.log(`Sending POST to ${url}...`)
|
||||
this.log.debug(`POST DATA: ${JSON.stringify(json)}`)
|
||||
this.post(json, url)
|
||||
}
|
||||
|
||||
protected post(json, url) {
|
||||
request.post(
|
||||
url,
|
||||
{json: json},
|
||||
(error, res, body) => {
|
||||
if (error) {
|
||||
this.log.error(error)
|
||||
return
|
||||
}
|
||||
this.log.log(`POST request was sent with status code: ${res.statusCode}`)
|
||||
this.log.verbose(`Response: ${JSON.stringify(body)}`)
|
||||
})
|
||||
}
|
||||
protected post(json, url) {
|
||||
request.post(url, { json: json }, (error, res, body) => {
|
||||
if (error) {
|
||||
this.log.error(error);
|
||||
return;
|
||||
}
|
||||
this.log.log(`POST request was sent with status code: ${res.statusCode}`);
|
||||
this.log.verbose(`Response: ${JSON.stringify(body)}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export function parseBool(value: any): boolean {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
if (value.toLowerCase() === 'true' || value.toLowerCase() === '1') {
|
||||
return true;
|
||||
}
|
||||
if (value.toLowerCase() === 'false' || value.toLowerCase() === '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.error(
|
||||
'parseBool got an unexpected value:',
|
||||
value,
|
||||
'(accepted values : "true", "false")',
|
||||
);
|
||||
throw new Error(
|
||||
'Error: parseBool got unexpected value - use "true" or "false" values',
|
||||
);
|
||||
}
|
||||
+41
-37
@@ -1,49 +1,53 @@
|
||||
import {NestFactory} from '@nestjs/core';
|
||||
import {WhatsappConfigService} from "./config.service";
|
||||
import {AllExceptionsFilter} from "./api/exception.filter";
|
||||
import {getWAHAVersion, VERSION, WAHAVersion} from "./version";
|
||||
import {AppModuleCore} from "./core/app.module.core";
|
||||
import {SwaggerModuleCore} from "./core/swagger.module.core";
|
||||
import {json, urlencoded} from "express";
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { WhatsappConfigService } from './config.service';
|
||||
import { AllExceptionsFilter } from './api/exception.filter';
|
||||
import { getWAHAVersion, VERSION, WAHAVersion } from './version';
|
||||
import { AppModuleCore } from './core/app.module.core';
|
||||
import { SwaggerModuleCore } from './core/swagger.module.core';
|
||||
import { json, urlencoded } from 'express';
|
||||
|
||||
async function loadModules(): Promise<[typeof AppModuleCore, typeof SwaggerModuleCore]> {
|
||||
const version = getWAHAVersion()
|
||||
console.log(`WAHA (WhatsApp HTTP API) - Running ${version} version...`)
|
||||
async function loadModules(): Promise<
|
||||
[typeof AppModuleCore, typeof SwaggerModuleCore]
|
||||
> {
|
||||
const version = getWAHAVersion();
|
||||
console.log(`WAHA (WhatsApp HTTP API) - Running ${version} version...`);
|
||||
|
||||
if (version === WAHAVersion.CORE) {
|
||||
const {AppModuleCore} = await import("./core/app.module.core")
|
||||
const {SwaggerModuleCore} = await import("./core/swagger.module.core")
|
||||
return [AppModuleCore, SwaggerModuleCore]
|
||||
}
|
||||
// Ignore if it's core version - there's no plus module
|
||||
// @ts-ignore
|
||||
const {AppModulePlus} = await import("./plus/app.module.plus")
|
||||
// @ts-ignore
|
||||
const {SwaggerModulePlus} = await import("./plus/swagger.module.plus")
|
||||
return [AppModulePlus, SwaggerModulePlus]
|
||||
if (version === WAHAVersion.CORE) {
|
||||
const { AppModuleCore } = await import('./core/app.module.core');
|
||||
const { SwaggerModuleCore } = await import('./core/swagger.module.core');
|
||||
return [AppModuleCore, SwaggerModuleCore];
|
||||
}
|
||||
// Ignore if it's core version - there's no plus module
|
||||
// @ts-ignore
|
||||
const { AppModulePlus } = await import('./plus/app.module.plus');
|
||||
// @ts-ignore
|
||||
const { SwaggerModulePlus } = await import('./plus/swagger.module.plus');
|
||||
return [AppModulePlus, SwaggerModulePlus];
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
const [AppModule, SwaggerModule] = await loadModules()
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
logger: process.env.DEBUG != undefined ? ['log', 'debug', 'error', 'verbose', 'warn'] :
|
||||
['log', 'error', 'warn'],
|
||||
});
|
||||
const [AppModule, SwaggerModule] = await loadModules();
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
logger:
|
||||
process.env.DEBUG != undefined
|
||||
? ['log', 'debug', 'error', 'verbose', 'warn']
|
||||
: ['log', 'error', 'warn'],
|
||||
});
|
||||
|
||||
app.enableShutdownHooks();
|
||||
app.useGlobalFilters(new AllExceptionsFilter());
|
||||
app.enableShutdownHooks();
|
||||
app.useGlobalFilters(new AllExceptionsFilter());
|
||||
|
||||
// Allow to send big body - for images and attachments
|
||||
app.use(json({limit: '50mb'}));
|
||||
app.use(urlencoded({limit: '50mb'}));
|
||||
// Allow to send big body - for images and attachments
|
||||
app.use(json({ limit: '50mb' }));
|
||||
app.use(urlencoded({ limit: '50mb', extended: false }));
|
||||
|
||||
// Configure swagger
|
||||
const swagger = new SwaggerModule()
|
||||
swagger.configure(app)
|
||||
// Configure swagger
|
||||
const swagger = new SwaggerModule();
|
||||
swagger.configure(app);
|
||||
|
||||
const config = app.get(WhatsappConfigService);
|
||||
await app.listen(config.port);
|
||||
console.log(`WhatsApp HTTP API is running on: ${await app.getUrl()}`);
|
||||
const config = app.get(WhatsappConfigService);
|
||||
await app.listen(config.port);
|
||||
console.log(`WhatsApp HTTP API is running on: ${await app.getUrl()}`);
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
@@ -1,13 +1,13 @@
|
||||
import {IsNotEmpty} from "class-validator";
|
||||
import { IsNotEmpty } from 'class-validator';
|
||||
|
||||
export const WHATSAPP_DEFAULT_SESSION_NAME = "default"
|
||||
export const WHATSAPP_DEFAULT_SESSION_NAME = 'default';
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
export class SessionQuery {
|
||||
@IsNotEmpty()
|
||||
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
|
||||
@IsNotEmpty()
|
||||
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15,5 +15,5 @@ export class SessionQuery {
|
||||
*/
|
||||
|
||||
export class SessionBaseRequest {
|
||||
session = WHATSAPP_DEFAULT_SESSION_NAME
|
||||
session = WHATSAPP_DEFAULT_SESSION_NAME;
|
||||
}
|
||||
+145
-112
@@ -1,188 +1,221 @@
|
||||
import {IsNotEmpty, IsString} from "class-validator";
|
||||
import {ApiExtraModels, ApiProperty, getSchemaPath} from "@nestjs/swagger";
|
||||
import {SessionBaseRequest, SessionQuery} from "./base.dto";
|
||||
|
||||
import { IsNotEmpty, IsString, IsOptional, IsNumber } from 'class-validator';
|
||||
import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
|
||||
import { SessionBaseRequest, SessionQuery } from './base.dto';
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
export class CheckNumberStatusQuery extends SessionQuery {
|
||||
@IsString()
|
||||
phone: string
|
||||
@IsString()
|
||||
phone: string;
|
||||
}
|
||||
|
||||
export class MessageTextQuery extends SessionQuery {
|
||||
@IsString()
|
||||
phone: string
|
||||
@IsString()
|
||||
text: string
|
||||
@IsString()
|
||||
phone: string;
|
||||
@IsString()
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class ChatQuery extends SessionQuery {
|
||||
@ApiProperty({
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
chatId: string;
|
||||
}
|
||||
|
||||
export class GetMessageQuery extends ChatQuery {
|
||||
@IsNumber()
|
||||
limit: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
export class ChatRequest extends SessionBaseRequest {
|
||||
@ApiProperty({
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
chatId: string
|
||||
@ApiProperty({
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
chatId: string;
|
||||
}
|
||||
|
||||
export class MessageRequest extends SessionBaseRequest{
|
||||
@ApiProperty({
|
||||
example: "false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA",
|
||||
})
|
||||
messageId: string
|
||||
export class SendSeenRequest extends ChatRequest {
|
||||
@ApiProperty({
|
||||
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
required: false,
|
||||
description:
|
||||
"NOWEB engine only - it's important to mark ALL messages as seen",
|
||||
})
|
||||
messageId?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '11111111111@c.us',
|
||||
required: false,
|
||||
description:
|
||||
'NOWEB engine only - the ID of the user that sent the message (undefined for individual chats)',
|
||||
})
|
||||
participant?: string;
|
||||
}
|
||||
|
||||
export class MessageRequest extends SessionBaseRequest {
|
||||
@ApiProperty({
|
||||
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
})
|
||||
messageId: string;
|
||||
}
|
||||
|
||||
export class MessageContactVcardRequest extends ChatRequest {
|
||||
contactsId: string;
|
||||
name: string
|
||||
contactsId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class MessageTextRequest extends ChatRequest {
|
||||
text = "Hi there!"
|
||||
text = 'Hi there!';
|
||||
}
|
||||
|
||||
export class Button {
|
||||
id: string
|
||||
body: string
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class MessageTextButtonsRequest extends ChatRequest {
|
||||
title: string;
|
||||
text: string;
|
||||
buttons?: Array<Button>;
|
||||
title: string;
|
||||
footer?: string;
|
||||
buttons?: Array<Button>;
|
||||
}
|
||||
|
||||
export class MessageReplyRequest extends ChatRequest {
|
||||
text = "Reply text"
|
||||
@ApiProperty({
|
||||
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
})
|
||||
reply_to: string;
|
||||
text = 'Reply text';
|
||||
@ApiProperty({
|
||||
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
})
|
||||
reply_to: string;
|
||||
}
|
||||
|
||||
export class MessageLocationRequest extends ChatRequest {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
title: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export class RemoteFile {
|
||||
@ApiProperty({
|
||||
description: "MIME type of the attachment.",
|
||||
example: 'image/jpeg',
|
||||
})
|
||||
mimetype: string
|
||||
@ApiProperty({
|
||||
description: 'MIME type of the attachment.',
|
||||
example: 'image/jpeg',
|
||||
})
|
||||
mimetype: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Document file name. Value can be null",
|
||||
example: 'filename.jpg',
|
||||
})
|
||||
filename?: string | null
|
||||
@ApiProperty({
|
||||
description: 'Document file name. Value can be null',
|
||||
example: 'filename.jpg',
|
||||
})
|
||||
filename?: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.jpg',
|
||||
})
|
||||
url: string
|
||||
@ApiProperty({
|
||||
example:
|
||||
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.jpg',
|
||||
})
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class BinaryFile {
|
||||
@ApiProperty({
|
||||
description: "MIME type of the attachment.",
|
||||
example: 'image/jpeg',
|
||||
})
|
||||
mimetype: string
|
||||
@ApiProperty({
|
||||
description: 'MIME type of the attachment.',
|
||||
example: 'image/jpeg',
|
||||
})
|
||||
mimetype: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Document file name. Optional",
|
||||
example: 'filename.jpeg',
|
||||
})
|
||||
filename?: string | null
|
||||
@ApiProperty({
|
||||
description: 'Document file name. Optional',
|
||||
example: 'filename.jpeg',
|
||||
})
|
||||
filename?: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Base64-encoded data of the file",
|
||||
example: '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',
|
||||
})
|
||||
data: string
|
||||
@ApiProperty({
|
||||
description: 'Base64-encoded data of the file',
|
||||
example:
|
||||
'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',
|
||||
})
|
||||
data: string;
|
||||
}
|
||||
|
||||
@ApiExtraModels(BinaryFile, RemoteFile)
|
||||
class FileRequest extends ChatRequest {
|
||||
@ApiProperty({
|
||||
oneOf: [
|
||||
{$ref: getSchemaPath(BinaryFile)},
|
||||
{$ref: getSchemaPath(RemoteFile)},
|
||||
],
|
||||
})
|
||||
file: BinaryFile | RemoteFile
|
||||
@ApiProperty({
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(BinaryFile) },
|
||||
{ $ref: getSchemaPath(RemoteFile) },
|
||||
],
|
||||
})
|
||||
file: BinaryFile | RemoteFile;
|
||||
}
|
||||
|
||||
export class MessageImageRequest extends FileRequest {
|
||||
caption: string;
|
||||
caption: string;
|
||||
}
|
||||
|
||||
export class MessageFileRequest extends FileRequest {
|
||||
caption: string;
|
||||
}
|
||||
|
||||
export class VoiceBinaryFile {
|
||||
@ApiProperty({
|
||||
description: "MIME type of the attachment.",
|
||||
})
|
||||
mimetype = "audio/ogg; codecs=opus"
|
||||
@ApiProperty({
|
||||
description: 'MIME type of the attachment.',
|
||||
})
|
||||
mimetype = 'audio/ogg; codecs=opus';
|
||||
|
||||
@ApiProperty({
|
||||
description: "Document file name. Optional",
|
||||
})
|
||||
filename = "voice-message.mp3"
|
||||
@ApiProperty({
|
||||
description: 'Document file name. Optional',
|
||||
})
|
||||
filename = 'voice-message.mp3';
|
||||
|
||||
@ApiProperty({
|
||||
description: "Base64-encoded data of the file",
|
||||
example: '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
Line truncated
|
||||
})
|
||||
data: string
|
||||
@ApiProperty({
|
||||
description: 'Base64-encoded data of the file',
|
||||
example:
|
||||
'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
Line truncated
|
||||
})
|
||||
data: string;
|
||||
}
|
||||
|
||||
export class VoiceRemoteFile {
|
||||
@ApiProperty({
|
||||
description: "MIME type of the attachment.",
|
||||
})
|
||||
mimetype = "audio/ogg; codecs=opus"
|
||||
@ApiProperty({
|
||||
description: 'MIME type of the attachment.',
|
||||
})
|
||||
mimetype = 'audio/ogg; codecs=opus';
|
||||
|
||||
@ApiProperty({
|
||||
description: "Document file name. Optional",
|
||||
})
|
||||
filename = "voice-message.mp3"
|
||||
|
||||
@ApiProperty({
|
||||
example: 'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.mp3',
|
||||
})
|
||||
url: string
|
||||
@ApiProperty({
|
||||
description: 'Document file name. Optional',
|
||||
})
|
||||
filename = 'voice-message.mp3';
|
||||
|
||||
@ApiProperty({
|
||||
example:
|
||||
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.mp3',
|
||||
})
|
||||
url: string;
|
||||
}
|
||||
|
||||
@ApiExtraModels(VoiceBinaryFile, VoiceRemoteFile)
|
||||
export class MessageVoiceRequest extends ChatRequest {
|
||||
@ApiProperty({
|
||||
oneOf: [
|
||||
{$ref: getSchemaPath(VoiceBinaryFile)},
|
||||
{$ref: getSchemaPath(VoiceRemoteFile)},
|
||||
],
|
||||
})
|
||||
file: VoiceBinaryFile | VoiceRemoteFile
|
||||
@ApiProperty({
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(VoiceBinaryFile) },
|
||||
{ $ref: getSchemaPath(VoiceRemoteFile) },
|
||||
],
|
||||
})
|
||||
file: VoiceBinaryFile | VoiceRemoteFile;
|
||||
}
|
||||
|
||||
export class MessageLinkPreviewRequest extends ChatRequest {
|
||||
url: string;
|
||||
title: string;
|
||||
url: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export class MessageReactionRequest extends MessageRequest {
|
||||
@ApiProperty({
|
||||
description: "Emoji to react with. Send an empty string to remove the reaction",
|
||||
example: '👍',
|
||||
})
|
||||
reaction: string
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Emoji to react with. Send an empty string to remove the reaction',
|
||||
example: '👍',
|
||||
})
|
||||
reaction: string;
|
||||
}
|
||||
@@ -1,22 +1,20 @@
|
||||
import {IsString} from "class-validator";
|
||||
import {SessionBaseRequest, SessionQuery} from "./base.dto";
|
||||
|
||||
import { IsString } from 'class-validator';
|
||||
import { SessionBaseRequest, SessionQuery } from './base.dto';
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
export class ContactQuery extends SessionQuery {
|
||||
@IsString()
|
||||
contactId: string
|
||||
@IsString()
|
||||
contactId: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
|
||||
export class ContactRequest extends SessionBaseRequest {
|
||||
@IsString()
|
||||
contactId: string
|
||||
@IsString()
|
||||
contactId: string;
|
||||
}
|
||||
+14
-12
@@ -1,22 +1,24 @@
|
||||
export const SECOND = 1000;
|
||||
|
||||
export enum WAEvents {
|
||||
MESSAGE = "message",
|
||||
MESSAGE_ANY = "message.any",
|
||||
MESSAGE_ACK = "message.ack",
|
||||
STATE_CHANGE = "state.change",
|
||||
GROUP_JOIN = "group.join",
|
||||
GROUP_LEAVE = "group.leave",
|
||||
MESSAGE = 'message',
|
||||
MESSAGE_ANY = 'message.any',
|
||||
MESSAGE_ACK = 'message.ack',
|
||||
STATE_CHANGE = 'state.change',
|
||||
GROUP_JOIN = 'group.join',
|
||||
GROUP_LEAVE = 'group.leave',
|
||||
}
|
||||
|
||||
export enum WhatsappStatus {
|
||||
STARTING = "STARTING",
|
||||
SCAN_QR_CODE = "SCAN_QR_CODE",
|
||||
WORKING = "WORKING",
|
||||
FAILED = "FAILED",
|
||||
STOPPED = 'STOPPED',
|
||||
STARTING = 'STARTING',
|
||||
SCAN_QR_CODE = 'SCAN_QR_CODE',
|
||||
WORKING = 'WORKING',
|
||||
FAILED = 'FAILED',
|
||||
}
|
||||
|
||||
export enum WhatsappEngine {
|
||||
VENOM = "VENOM",
|
||||
WEBJS = "WEBJS"
|
||||
VENOM = 'VENOM',
|
||||
WEBJS = 'WEBJS',
|
||||
NOWEB = 'NOWEB',
|
||||
}
|
||||
@@ -1,40 +1,39 @@
|
||||
import {IsArray, IsString} from "class-validator";
|
||||
import { IsArray, IsString } from 'class-validator';
|
||||
|
||||
/**
|
||||
* Structures
|
||||
*/
|
||||
export class Participant {
|
||||
@IsString()
|
||||
id: string
|
||||
@IsString()
|
||||
id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
|
||||
export class ParticipantsRequest {
|
||||
@IsArray()
|
||||
participants: Array<Participant>
|
||||
@IsArray()
|
||||
participants: Array<Participant>;
|
||||
}
|
||||
|
||||
export class DescriptionRequest {
|
||||
@IsString()
|
||||
description: string
|
||||
@IsString()
|
||||
description: string;
|
||||
}
|
||||
export class SubjectRequest {
|
||||
@IsString()
|
||||
subject: string
|
||||
@IsString()
|
||||
subject: string;
|
||||
}
|
||||
|
||||
export class CreateGroupRequest {
|
||||
@IsString()
|
||||
name: string
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@IsArray()
|
||||
participants: Array<Participant>
|
||||
@IsArray()
|
||||
participants: Array<Participant>;
|
||||
}
|
||||
+110
-101
@@ -1,139 +1,148 @@
|
||||
import {ApiProperty} from "@nestjs/swagger";
|
||||
import {WAEvents} from "./enums.dto";
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { WAEvents, WhatsappEngine } from './enums.dto';
|
||||
|
||||
export class WANumberExistResult {
|
||||
numberExists: boolean
|
||||
numberExists: boolean;
|
||||
}
|
||||
|
||||
|
||||
export class WAGroupNotification {
|
||||
@ApiProperty({
|
||||
description: "ID that represents the groupNotification",
|
||||
})
|
||||
id: any
|
||||
@ApiProperty({
|
||||
description: 'ID that represents the groupNotification',
|
||||
})
|
||||
id: any;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Unix timestamp for when the groupNotification was created",
|
||||
})
|
||||
timestamp: number
|
||||
@ApiProperty({
|
||||
description: 'Unix timestamp for when the groupNotification was created',
|
||||
})
|
||||
timestamp: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: "ID for the Chat that this groupNotification was sent for",
|
||||
})
|
||||
chatId: string
|
||||
@ApiProperty({
|
||||
description: 'ID for the Chat that this groupNotification was sent for',
|
||||
})
|
||||
chatId: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'ContactId for the user that produced the GroupNotification',
|
||||
})
|
||||
author: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "ContactId for the user that produced the GroupNotification",
|
||||
})
|
||||
author: string
|
||||
@ApiProperty({
|
||||
description: 'Extra content',
|
||||
})
|
||||
body: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Extra content",
|
||||
})
|
||||
body: string
|
||||
|
||||
@ApiProperty({
|
||||
description: "Contact IDs for the users that were affected by this GroupNotification",
|
||||
})
|
||||
recipientIds: string[]
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Contact IDs for the users that were affected by this GroupNotification',
|
||||
})
|
||||
recipientIds: string[];
|
||||
}
|
||||
|
||||
export enum WAMessageAck {
|
||||
ACK_ERROR = -1,
|
||||
ACK_PENDING = 0,
|
||||
ACK_SERVER = 1,
|
||||
ACK_DEVICE = 2,
|
||||
ACK_READ = 3,
|
||||
ACK_PLAYED = 4,
|
||||
ACK_ERROR = -1,
|
||||
ACK_PENDING = 0,
|
||||
ACK_SERVER = 1,
|
||||
ACK_DEVICE = 2,
|
||||
ACK_READ = 3,
|
||||
ACK_PLAYED = 4,
|
||||
}
|
||||
|
||||
export class WALocation {
|
||||
description?: string | null
|
||||
latitude: string
|
||||
longitude: string
|
||||
description?: string | null;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
}
|
||||
|
||||
|
||||
export class WAMessage {
|
||||
@ApiProperty({
|
||||
description: "Message ID",
|
||||
example: "false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA"
|
||||
})
|
||||
id: string
|
||||
@ApiProperty({
|
||||
description: 'Message ID',
|
||||
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
})
|
||||
id: string;
|
||||
|
||||
/** */
|
||||
@ApiProperty({
|
||||
description: "Unix timestamp for when the message was created",
|
||||
example: 1666943582
|
||||
})
|
||||
timestamp: number
|
||||
/** */
|
||||
@ApiProperty({
|
||||
description: 'Unix timestamp for when the message was created',
|
||||
example: 1666943582,
|
||||
})
|
||||
timestamp: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: "ID for the Chat that this message was sent to, except if the message was sent by the current user "
|
||||
})
|
||||
from: string
|
||||
@ApiProperty({
|
||||
description:
|
||||
'ID for the Chat that this message was sent to, except if the message was sent by the current user ',
|
||||
})
|
||||
from: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Indicates if the message was sent by the current user"
|
||||
})
|
||||
fromMe: boolean
|
||||
@ApiProperty({
|
||||
description: 'Indicates if the message was sent by the current user',
|
||||
})
|
||||
fromMe: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: `
|
||||
@ApiProperty({
|
||||
description: `
|
||||
* ID for who this message is for.
|
||||
* If the message is sent by the current user, it will be the Chat to which the message is being sent.
|
||||
* If the message is sent by another user, it will be the ID for the current user.
|
||||
`
|
||||
})
|
||||
to: string
|
||||
`,
|
||||
})
|
||||
to: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Message content"
|
||||
})
|
||||
body: string
|
||||
@ApiProperty({
|
||||
description: 'For groups - participant who sent the message',
|
||||
})
|
||||
participant: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Indicates if the message has media available for download"
|
||||
})
|
||||
hasMedia: boolean
|
||||
@ApiProperty({
|
||||
description: 'Message content',
|
||||
})
|
||||
body: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "The URL for the media in the message if any",
|
||||
example: "http://localhost:3000/api/files/false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA.oga"
|
||||
})
|
||||
mediaUrl: string
|
||||
@ApiProperty({
|
||||
description: 'Indicates if the message has media available for download',
|
||||
})
|
||||
hasMedia: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: "ACK status for the message"
|
||||
})
|
||||
ack: WAMessageAck
|
||||
@ApiProperty({
|
||||
description: 'The URL for the media in the message if any',
|
||||
example:
|
||||
'http://localhost:3000/api/files/false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA.oga',
|
||||
})
|
||||
mediaUrl: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "If the message was sent to a group, this field will contain the user that sent the message."
|
||||
})
|
||||
author?: string;
|
||||
@ApiProperty({
|
||||
description: 'ACK status for the message',
|
||||
})
|
||||
ack: WAMessageAck;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Location information contained in the message, if the message is type \"location\""
|
||||
})
|
||||
location?: WALocation
|
||||
@ApiProperty({
|
||||
description:
|
||||
'If the message was sent to a group, this field will contain the user that sent the message.',
|
||||
})
|
||||
author?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "List of vCards contained in the message."
|
||||
})
|
||||
vCards?: string[]
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Location information contained in the message, if the message is type "location"',
|
||||
})
|
||||
location?: WALocation;
|
||||
|
||||
/** Returns message in a raw format */
|
||||
@ApiProperty({
|
||||
description: "Message in a raw format that we get from WhatsApp. May be changed anytime, use it with caution! It depends a lot on the underlying backend."
|
||||
})
|
||||
_data?: any
|
||||
@ApiProperty({
|
||||
description: 'List of vCards contained in the message.',
|
||||
})
|
||||
vCards?: string[];
|
||||
|
||||
/** Returns message in a raw format */
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Message in a raw format that we get from WhatsApp. May be changed anytime, use it with caution! It depends a lot on the underlying backend.',
|
||||
})
|
||||
_data?: any;
|
||||
}
|
||||
|
||||
export class WAWebhook {
|
||||
event: WAEvents
|
||||
session: string
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
payload: WAMessage | WAGroupNotification | object
|
||||
event: WAEvents;
|
||||
session: string;
|
||||
engine: WhatsappEngine;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
payload: WAMessage | WAGroupNotification | object;
|
||||
}
|
||||
@@ -1,14 +1,40 @@
|
||||
import {WhatsappStatus} from "./enums.dto";
|
||||
import { WhatsappStatus } from './enums.dto';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
export class ListSessionsQuery {
|
||||
@ApiProperty({
|
||||
example: false,
|
||||
required: false,
|
||||
description:
|
||||
'Return all sessions, including those that are in the STOPPED state.',
|
||||
})
|
||||
all: boolean;
|
||||
}
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
export class SessionStartRequest {
|
||||
name = "default"
|
||||
name = 'default';
|
||||
}
|
||||
|
||||
export class SessionStopRequest {
|
||||
name = "default"
|
||||
name = 'default';
|
||||
@ApiProperty({
|
||||
example: false,
|
||||
required: false,
|
||||
description: 'Stop and logout from the session.',
|
||||
})
|
||||
logout = false;
|
||||
}
|
||||
|
||||
export class SessionLogoutRequest {
|
||||
name = 'default';
|
||||
}
|
||||
|
||||
export class SessionDTO {
|
||||
name = "default"
|
||||
status: WhatsappStatus
|
||||
name = 'default';
|
||||
status: WhatsappStatus;
|
||||
}
|
||||
+19
-19
@@ -1,34 +1,34 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
import {getBrowserExecutablePath} from "./core/abc/session.abc";
|
||||
import { getBrowserExecutablePath } from './core/abc/session.abc';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const fs = require('fs')
|
||||
const fs = require('fs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('path');
|
||||
|
||||
export enum WAHAVersion {
|
||||
PLUS = "PLUS",
|
||||
CORE = "CORE",
|
||||
PLUS = 'PLUS',
|
||||
CORE = 'CORE',
|
||||
}
|
||||
|
||||
export function getWAHAVersion(): WAHAVersion {
|
||||
// force core version if env variables set
|
||||
const waha_version = process.env.WAHA_VERSION;
|
||||
if (waha_version && waha_version === WAHAVersion.CORE) {
|
||||
return WAHAVersion.CORE
|
||||
}
|
||||
// force core version if env variables set
|
||||
const waha_version = process.env.WAHA_VERSION;
|
||||
if (waha_version && waha_version === WAHAVersion.CORE) {
|
||||
return WAHAVersion.CORE;
|
||||
}
|
||||
|
||||
// Check the plus directory exists
|
||||
const plusExists = fs.existsSync(`${__dirname}/plus`)
|
||||
if (plusExists) {
|
||||
return WAHAVersion.PLUS
|
||||
}
|
||||
// Check the plus directory exists
|
||||
const plusExists = fs.existsSync(`${__dirname}/plus`);
|
||||
if (plusExists) {
|
||||
return WAHAVersion.PLUS;
|
||||
}
|
||||
|
||||
return WAHAVersion.CORE
|
||||
return WAHAVersion.CORE;
|
||||
}
|
||||
|
||||
export const VERSION = {
|
||||
version: "2023.4.14",
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowserExecutablePath()
|
||||
}
|
||||
version: '2023.6.1',
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowserExecutablePath(),
|
||||
};
|
||||
Reference in new issue
Block a user