Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
966b35094f | ||
|
|
65607bf3b3 | ||
|
|
5fcba7a7db | ||
|
|
f9ec3cb753 | ||
|
|
bf6ffd65a9 | ||
|
|
d680b6b43a | ||
|
|
3ead3a0ba3 | ||
|
|
4c5c977310 | ||
|
|
89a9ce87da | ||
|
|
52e469f6ac | ||
|
|
4f275de8d4 | ||
|
|
d9de41e350 | ||
|
|
a4c279ba25 | ||
|
|
e68282a9e4 | ||
|
|
8613206c3a | ||
|
|
31b8cde8bf | ||
|
|
2f2f9520db | ||
|
|
2501ae836e | ||
|
|
2c8bf91e77 | ||
|
|
0ac063498e | ||
|
|
2bf672f0cd | ||
|
|
ca209adca0 | ||
|
|
dd10c82c5e | ||
|
|
c3ad2a0931 | ||
|
|
30aacc8946 | ||
|
|
723a644937 | ||
|
|
165eeb5bf2 | ||
|
|
f1776d4a26 | ||
|
|
2f2a1b3182 | ||
|
|
0477cf7db5 | ||
|
|
2d7c7986aa | ||
|
|
bf84ea8fca | ||
|
|
f3ef8a7f96 | ||
|
|
ec214f610a | ||
|
|
013fd46722 | ||
|
|
32d583b9ef | ||
|
|
df6e201ba5 | ||
|
|
789b613b82 | ||
|
|
b698342db4 | ||
|
|
d853f2fe99 | ||
|
|
20d66b5b31 | ||
|
|
da7264c5e8 | ||
|
|
f3299f78d9 | ||
|
|
7ef0805c91 | ||
|
|
ba1afa2569 | ||
|
|
936e2d0863 | ||
|
|
166410f66a |
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
Dockerfile
|
||||
.sessions
|
||||
.*sessions
|
||||
files
|
||||
venv
|
||||
.wwebjs_auth
|
||||
|
||||
@@ -13,6 +13,11 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: 'amd64'
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
@@ -33,10 +38,11 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: 'amd64'
|
||||
push: ${{ github.ref_type == 'tag' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
.sessions
|
||||
.*sessions
|
||||
tokens
|
||||
files
|
||||
venv
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
# Changelog
|
||||
You'll find changelog on [the changelog page](https://waha.devlike.pro/docs/help/changelog/)
|
||||
You can find changelog on [the changelog page](https://waha.devlike.pro/docs/help/changelog/)
|
||||
+37
-8
@@ -2,12 +2,22 @@
|
||||
# Build
|
||||
#
|
||||
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 b7878e531144133b48d9e5970787e7b256d21063 && \
|
||||
yarn install && \
|
||||
yarn build:all
|
||||
|
||||
# npm packages
|
||||
WORKDIR /src
|
||||
COPY package.json .
|
||||
COPY package-lock.json .
|
||||
RUN npm install
|
||||
COPY yarn.lock .
|
||||
RUN yarn install
|
||||
|
||||
# App
|
||||
WORKDIR /src
|
||||
@@ -18,17 +28,36 @@ RUN npm run build && find ./dist -name "*.d.ts" -delete
|
||||
# Final
|
||||
#
|
||||
FROM node:18 as release
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
ARG USE_BROWSER=chromium
|
||||
|
||||
# Install Chrome
|
||||
# Install fonts
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y wget gnupg \
|
||||
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
|
||||
&& apt-get install -y fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Chromium
|
||||
RUN if [ "$USE_BROWSER" = "chromium" ]; then \
|
||||
apt-get update \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y chromium \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Install Chrome
|
||||
RUN if [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
apt-get update \
|
||||
&& apt-get install -y wget gnupg \
|
||||
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y google-chrome-stable \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Attach sources, install packages
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
|
||||
@@ -35,4 +35,5 @@ update-swagger:
|
||||
wget http://localhost:3000/-json -O ./docs/site/static/swagger/openapi.json
|
||||
|
||||
update-dependencies:
|
||||
. ${NVM_DIR}/nvm.sh && nvm exec npm install venom-bot whatsapp-web.js --save
|
||||
. ${NVM_DIR}/nvm.sh && nvm exec yarn add whatsapp-web.js@https://github.com/pedroslopez/whatsapp-web.js
|
||||
. ${NVM_DIR}/nvm.sh && nvm exec yarn add venom-bot
|
||||
@@ -18,8 +18,8 @@
|
||||
* [Sessions](#sessions)
|
||||
* [Security](#security)
|
||||
* [Updates](#updates)
|
||||
* [Groups [soon]](#groups-soon)
|
||||
* [Contacts [soon]](#contacts-soon)
|
||||
* [Groups](#groups)
|
||||
* [Contacts](#contacts)
|
||||
- [Quick start](#quick-start)
|
||||
* [Requirements](#requirements)
|
||||
* [Send your first message](#send-your-first-message)
|
||||
@@ -61,25 +61,20 @@
|
||||
|
||||
## Updates
|
||||
- Bug fixes and updates come as soon as they are implemented.
|
||||
- One month delay in bug fixes and updates.
|
||||
|
||||
|
||||
## Groups [soon]
|
||||
- Join groups by invite
|
||||
## Groups
|
||||
- Create a group
|
||||
- Get invite for group
|
||||
- Modify group info (subject, description)
|
||||
- Modify group settings (send messages, edit info)
|
||||
- Add group participants
|
||||
- Kick group participants
|
||||
- Promote/demote group participants
|
||||
|
||||
## Contacts [soon]
|
||||
- Mention users
|
||||
## Contacts
|
||||
- Mute/unmute chats
|
||||
- Block/unblock contacts
|
||||
- Get contact info
|
||||
- Get profile pictures
|
||||
- Set user status message
|
||||
|
||||
# Quick start
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "Python"
|
||||
description: "WhatsApp HTTP API Python example."
|
||||
lead: "WhatsApp HTTP API Python example."
|
||||
description: "WhatsApp HTTP API Python examples."
|
||||
lead: "WhatsApp HTTP API Python examples."
|
||||
date: 2020-10-06T08:49:31+00:00
|
||||
lastmod: 2020-10-06T08:49:31+00:00
|
||||
draft: false
|
||||
@@ -12,41 +12,7 @@ parent: "help"
|
||||
weight: 630
|
||||
toc: true
|
||||
---
|
||||
We've created a few examples on how to interact with WAHA WhatsApp HTTP API with Python.
|
||||
|
||||
To show how to receive messages we've created a simple "echo" server with two functions:
|
||||
You can find the examples in [examples folder on GitHub with detailed instructions in README.md](https://github.com/devlikeapro/whatsapp-http-api/tree/core/examples/python)
|
||||
|
||||
1. When we receive a text message - just send the text back
|
||||
2. When we receive a message with a file (an image, a voice message) - download it and send the path back 
|
||||
|
||||
In order to send you messages we use **Webhooks** and configure them via environments variables. So
|
||||
what you need to
|
||||
create "echo" server is HTTP server that will receive JSON POST request and then call back WhatsApp HTTP API
|
||||
via `POST /api/sendText` endpoint with JSON body.
|
||||
|
||||
## Python echo server
|
||||
|
||||
We use Python. Feel free to create your favorite language example and contribute to the project!
|
||||
|
||||
Run "echo" server in one terminal and leave it working:
|
||||
|
||||
```bash
|
||||
# if you haven't already
|
||||
git clone https://github.com/devlikeapro/whatsapp-http-api.git
|
||||
cd whatsapp-http-api
|
||||
python -mpip install -r examples/requirements.txt
|
||||
export FLASK_APP=examples/echo.py
|
||||
flask run
|
||||
```
|
||||
|
||||
Visit [http://localhost:5000](http://localhost:5000) and check that we are good to go further.
|
||||
|
||||
Let's start WhatsApp HTTP API and configure the "on message" webhook and point it on our "http://localhost:5000/message"
|
||||
endpoint:
|
||||
|
||||
```bash
|
||||
docker run -it --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/message -e WHATSAPP_HOOK_EVENTS=* devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
Now go ahead, open the second whatsapp and send to our WhatsApp HTTP API a text message! It must reply the same text.
|
||||
|
||||
 If you try to send an image the "echo server" will send a path to the downloaded file.
|
||||
@@ -5,15 +5,32 @@ 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.1
|
||||
- Added [contacts API]({{< relref "/docs/how-to/contacts" >}})
|
||||
|
||||
## 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 - January 2023
|
||||
|
||||
- Added [Contacts API]({{< relref "/docs/how-to/contacts" >}})
|
||||
- Get all contacts
|
||||
- Get a contact
|
||||
- Get contact "about" (status)
|
||||
@@ -21,33 +38,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.
|
||||
### Engine 
|
||||
|
||||
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 
|
||||
|
||||
### 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 
|
||||
|
||||
### 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" >}})
|
||||
@@ -12,7 +12,7 @@ You can configure WhatsApp HTTP API behaviour via environment variables, by addi
|
||||
begging of the command line or by using [other options](https://docs.docker.com/engine/reference/commandline/run/)
|
||||
|
||||
```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 devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
@@ -34,25 +34,3 @@ docker run -it -e WHATSAPP_HOOK_EVENTS=* -e WHATSAPP_HOOK_URL=https://httpbin.or
|
||||
- `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`)
|
||||
|
||||
## Engines
|
||||
|
||||
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.
|
||||
|
||||
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." />}}
|
||||
|
||||
### WhatsApp WebJS 
|
||||
|
||||
**WAHA** uses **WhatsApp WebJS** engine by default
|
||||
|
||||
- https://github.com/pedroslopez/whatsapp-web.js
|
||||
- `WHATSAPP_DEFAULT_ENGINE=WEBJS`
|
||||
|
||||
### Venom 
|
||||
|
||||
- https://github.com/orkestral/venom
|
||||
- `WHATSAPP_DEFAULT_ENGINE=VENOM`
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title : "Groups"
|
||||
description: "Groups"
|
||||
lead: ""
|
||||
date: 2020-10-06T08:48:45+00:00
|
||||
lastmod: 2020-10-06T08:48:45+00:00
|
||||
draft: false
|
||||
images: []
|
||||
weight: 140
|
||||
---
|
||||
|
||||
Methods for groups. Please look at swagger for details.
|
||||
|
||||
- `{session}` - use the session name for Whatsapp instance that you created with `POST /api/session/start` endpoint
|
||||
- `{groupId}` - group id in format `123123123123@g.us`. You can get the id in a few ways:
|
||||
- By [handling incoming message webhook]({{< relref "/docs/how-to/receive-messages" >}}).
|
||||
- By getting all groups (see below).
|
||||
- By creating a new group and saving the id.
|
||||
|
||||
## Create a new group
|
||||
|
||||
`POST /api/{session}/groups`
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Group name",
|
||||
"participants": [
|
||||
{
|
||||
"id": "123123123123@c.us"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Get all groups
|
||||
|
||||
`GET /api/{session}/groups`
|
||||
|
||||
## Get the group
|
||||
|
||||
`GET /api/{session}/groups/{groupId}`
|
||||
|
||||
## Delete the group
|
||||
|
||||
`DELETE /api/{session}/groups/{groupId}`
|
||||
|
||||
## Leave the group
|
||||
|
||||
`POST /api/{session}/groups/{groupId}/leave`
|
||||
|
||||
## Set group subject
|
||||
|
||||
Updates the group subject.
|
||||
|
||||
Returns `true` if the subject was properly updated. This can return false if the user does not have the necessary
|
||||
permissions.
|
||||
|
||||
`PUT /api/{session}/groups/{groupId}/subject`
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": "Group name"
|
||||
}
|
||||
```
|
||||
|
||||
## Set group description
|
||||
|
||||
Updates the group description.
|
||||
|
||||
Returns `true` if the subject was properly updated. This can return false if the user does not have the necessary
|
||||
permissions.
|
||||
|
||||
`PUT /api/{session}/groups/{groupId}/description`
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Group description"
|
||||
}
|
||||
```
|
||||
|
||||
## Participants
|
||||
|
||||
### Get participants
|
||||
|
||||
`GET /api/{session}/groups/{groupId}/participants`
|
||||
|
||||
### Add participants
|
||||
|
||||
`POST /api/{session}/groups/{groupId}/participants/add`
|
||||
|
||||
```json
|
||||
{
|
||||
"participants": [
|
||||
{
|
||||
"id": "123123123123@c.us"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Remove participants
|
||||
|
||||
`POST /api/{session}/groups/{groupId}/participants/remove`
|
||||
|
||||
```json
|
||||
{
|
||||
"participants": [
|
||||
{
|
||||
"id": "123123123123@c.us"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Admin
|
||||
|
||||
### Promote to admin
|
||||
|
||||
Promote participants to admin users.
|
||||
|
||||
`POST /api/{session}/groups/{groupId}/admin/promote`
|
||||
|
||||
```json
|
||||
{
|
||||
"participants": [
|
||||
{
|
||||
"id": "123123123123@c.us"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Demote to regular users
|
||||
|
||||
Demote participants by to regular users.
|
||||
|
||||
`POST /api/{session}/groups/{groupId}/admin/demote`
|
||||
|
||||
```json
|
||||
{
|
||||
"participants": [
|
||||
{
|
||||
"id": "123123123123@c.us"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Invite code
|
||||
|
||||
### Get invite code
|
||||
|
||||
`GET /api/{session}/groups/{groupId}/invite-code`
|
||||
|
||||
Then you can put it in the url `https://chat.whatsapp.com/{inviteCode}` and send it to contacts.
|
||||
|
||||
### Revoke invite code
|
||||
|
||||
Invalidates the current group invite code and generates a new one.
|
||||
|
||||
`POST /api/{session}/groups/{groupId}/invite-code/revoke`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -9,17 +9,26 @@ images: []
|
||||
weight: 120
|
||||
---
|
||||
|
||||
The project uses [Webhooks](https://en.wikipedia.org/wiki/Webhook) to send the messages or events from WhatsApp to your
|
||||
The project uses [Webhooks](https://en.wikipedia.org/wiki/Webhook) to send the messages and events from WhatsApp to your
|
||||
application.
|
||||
|
||||
## Webhooks  
|
||||
Webhooks are a way for two different applications to communicate with each other in real-time.
|
||||
When a certain event happens in one application, it sends a message to another application through a webhook URL.
|
||||
The receiving application can then take action based on the information received.
|
||||
|
||||
To receive incoming messages with webhooks,
|
||||
you first need to set up a webhook URL in your application and pass it to `WHATSAPP_HOOK_URL` environment variable.
|
||||
This URL is where WhatsApp will send incoming messages and other events -
|
||||
define which events you want to receive with `WHATSAPP_HOOK_EVENTS` environment variable.
|
||||
|
||||
## Webhooks 
|
||||
|
||||
You can configure where you want to receive events in environment variables:
|
||||
|
||||
- `WHATSAPP_HOOK_URL=https://httpbin.org/post` - to set up a URL for the webhook
|
||||
- `WHATSAPP_HOOK_EVENTS=message,message.any,state.change,group.join,group.leave` - specify events. DO NOT specify all of
|
||||
them, it's too heavy payload, choose the right for you.
|
||||
- `WHATSAPP_HOOK_EVENTS=*` - subscribe to all events. It's not recommended for production, but it's fine for
|
||||
- `WHATSAPP_HOOK_EVENTS=*` - subscribe to all events. It's not recommended for production, but it's fine for
|
||||
development.
|
||||
|
||||
On the URL that you set via `WHATSAPP_HOOK_URL` you receive JSON-data with following format:
|
||||
@@ -27,6 +36,7 @@ On the URL that you set via `WHATSAPP_HOOK_URL` you receive JSON-data with follo
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -40,7 +50,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 devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -55,7 +65,7 @@ You can create a pull request with your favorite language in the [GitHub, in exa
|
||||
Just put the code and **README.md** file in the folder - we'll do the rest!
|
||||
|
||||
|
||||
## Events  
|
||||
## Events 
|
||||
|
||||
### message
|
||||
|
||||
@@ -64,6 +74,7 @@ Incoming message (text/audio/files)
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"payload": {
|
||||
"id": "true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 1667561485,
|
||||
@@ -126,6 +137,7 @@ Fired on all message creations, including your own. The payload is the same as f
|
||||
```json
|
||||
{
|
||||
"event": "message.any",
|
||||
"session": "default",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -135,6 +147,7 @@ Fired on all message creations, including your own. The payload is the same as f
|
||||
```json
|
||||
{
|
||||
"event": "message.ack",
|
||||
"session": "default",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -144,6 +157,7 @@ Fired on all message creations, including your own. The payload is the same as f
|
||||
```json
|
||||
{
|
||||
"event": "state.change",
|
||||
"session": "default",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -153,6 +167,7 @@ Fired on all message creations, including your own. The payload is the same as f
|
||||
```json
|
||||
{
|
||||
"event": "group.join",
|
||||
"session": "default",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -162,6 +177,7 @@ Fired on all message creations, including your own. The payload is the same as f
|
||||
```json
|
||||
{
|
||||
"event": "group.left",
|
||||
"session": "default",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
@@ -176,6 +192,7 @@ For example, you can get the webhook like this with `mediaUrl` value (we've skip
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"payload": {
|
||||
"id": "true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 1667561485,
|
||||
|
||||
@@ -28,7 +28,7 @@ Read more about [multiple sessions →]({{< relref "/docs/how-to/sessions" >}})
|
||||
For phone number `12132132131` the `chatId` is `12132132131@c.us`.
|
||||
- **Groups** use random number with `@g.us` at the end.
|
||||
|
||||
## Send text  
|
||||
## Send text 
|
||||
To send text message - use `POST /api/sendText` with example payload.
|
||||
```json
|
||||
{
|
||||
@@ -38,7 +38,7 @@ To send text message - use `POST /api/sendText` with example payload.
|
||||
}
|
||||
```
|
||||
|
||||
## Reply on message  
|
||||
## Reply on message 
|
||||
To reply on a message - use `POST /api/reply` with example payload.
|
||||
```json
|
||||
{
|
||||
@@ -52,7 +52,7 @@ To reply on a message - use `POST /api/reply` with example payload.
|
||||
### Reply files 
|
||||
WAHA does not support reply with files (images, voice, etc). If you're interested in it - please create an issue in GitHub.
|
||||
|
||||
## Add a reaction  
|
||||
## Add a reaction 
|
||||
Use `PUT /api/reaction` method to set reaction to a message.
|
||||
|
||||
{{< alert icon="👉" text="Reaction API uses PUT, not POST request! Please make sure you send right request." />}}
|
||||
|
||||
@@ -22,7 +22,22 @@ toc: true
|
||||
|
||||
{{< alert icon="👉" text="Run WhatsApp HTTP API in less than 5 minutes!" />}}
|
||||
|
||||
Go straight to the [Quick Start →]({{< relref "quick-start" >}})
|
||||
We will guide you through the necessary steps to successfully send your first text message using WhatsApp HTTP API in
|
||||
[Quick Start →]({{< relref "quick-start" >}}).
|
||||
|
||||
If you find it easier to follow a video tutorial rather than reading through text, we've got you covered!
|
||||
A 3-minute video tutorial is available to help you navigate WhatsApp HTTP API smoothly.
|
||||
|
||||
<div class="d-flex justify-content-center my-4">
|
||||
<iframe
|
||||
width="560" height="315"
|
||||
src="https://www.youtube.com/embed/RFerMyAUPRg"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Features
|
||||
WAHA is distributed in two versions:
|
||||
@@ -32,21 +47,21 @@ WAHA is distributed in two versions:
|
||||
Here's the available features:
|
||||
|
||||
### Messages
|
||||
- Send messages  
|
||||
- Receive messages  
|
||||
- Message replies  
|
||||
- Send location  
|
||||
- Send buttons  
|
||||
- Receive location  
|
||||
- React to messages  
|
||||
- Send messages 
|
||||
- Receive messages 
|
||||
- Message replies 
|
||||
- Send location 
|
||||
- Send buttons 
|
||||
- Receive location 
|
||||
- React to messages 
|
||||
- Send media (images/documents/files) 
|
||||
- Send voice messages 
|
||||
- Receive media (images/audio/video/documents) 
|
||||
- Receive messages - webhook retries 
|
||||
|
||||
### Sessions
|
||||
- Multi Device  
|
||||
- Get the screenshot  
|
||||
- Multi Device 
|
||||
- Get the screenshot 
|
||||
- Session saving (don't have to scan QR on every restart) 
|
||||
- Session saving on remote storage (database, redis) 
|
||||
- Single WhatsApp account running inside one container 
|
||||
@@ -54,26 +69,21 @@ Here's the available features:
|
||||
|
||||
### Security
|
||||
- API authentication 
|
||||
- Swagger panel authentication 
|
||||
- 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 
|
||||
- Join groups by invite
|
||||
### Groups 
|
||||
- Create a group
|
||||
- Get invite for group
|
||||
- Modify group info (subject, description)
|
||||
- Modify group settings (send messages, edit info)
|
||||
- Add group participants
|
||||
- Kick group participants
|
||||
- Promote/demote group participants
|
||||
|
||||
### Contacts 
|
||||
- Mention users
|
||||
### Contacts 
|
||||
- Mute/unmute chats
|
||||
- Block/unblock contacts
|
||||
- Get contact info
|
||||
- Get profile pictures
|
||||
- Set user status message
|
||||
@@ -13,7 +13,8 @@ weight: 110
|
||||
toc: true
|
||||
---
|
||||
|
||||
On this page you're going to install WAHA, run it, authenticate the client using QR code and send the first text message to WhatsApp!
|
||||
On this page you're going to install WAHA, run it, authenticate the client using QR code and send the first text message
|
||||
to WhatsApp!
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -31,7 +32,21 @@ Also Docker makes installation and update processes so simple, just one command!
|
||||
|
||||
## Send your first message
|
||||
|
||||
Let's go over steps that allow you to send your first text message via WhatsApp HTTP API!
|
||||
We will guide you through the necessary steps to successfully send your first text message using WhatsApp HTTP API.
|
||||
|
||||
If you find it easier to follow a video tutorial rather than reading through text, we've got you covered!
|
||||
A 3-minute video tutorial is available to help you navigate WhatsApp HTTP API smoothly.
|
||||
|
||||
<div class="d-flex justify-content-center my-4">
|
||||
<iframe
|
||||
width="560" height="315"
|
||||
src="https://www.youtube.com/embed/RFerMyAUPRg"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
### 1. Download image
|
||||
|
||||
@@ -53,6 +68,28 @@ docker logout
|
||||
|
||||
Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-version" >}})
|
||||
|
||||
#### 1.1 Download image on ARM
|
||||
|
||||
If you're using ARM (like Apple Silicon, Apple M1, etc) - use following commands to download the image
|
||||
|
||||
 For Core version the command is
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/whatsapp-http-api:arm
|
||||
# Rename it, so you can use devlikeapro/whatsapp-http-api image in other place
|
||||
docker tag devlikeapro/whatsapp-http-api:arm devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
 For Plus version the command is:
|
||||
```bash
|
||||
docker login -u devlikeapro -p {PASSWORD}
|
||||
docker pull devlikeapro/whatsapp-http-api-plus:arm
|
||||
docker logout
|
||||
|
||||
# Rename it, so you can use devlikeapro/whatsapp-http-api image in other place
|
||||
docker tag devlikeapro/whatsapp-http-api-plus:arm devlikeapro/whatsapp-http-api
|
||||
```
|
||||
|
||||
### 2. Run WhatsApp HTTP API
|
||||
|
||||
Run WhatsApp HTTP API:
|
||||
@@ -83,8 +120,8 @@ How to log in - the instruction on WhatsApp site
|
||||
|
||||
When your ready - find `POST /api/session/start`, click on **Try it out**, then **Execute** a bit below.
|
||||
|
||||
|
||||
The example payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "default"
|
||||
@@ -93,7 +130,8 @@ The example payload:
|
||||
|
||||

|
||||
|
||||
By using the request with `name` values you can start multiple session (WhatsApp accounts) inside the single docker container in Plus
|
||||
By using the request with `name` values you can start multiple session (WhatsApp accounts) inside the single docker
|
||||
container in Plus
|
||||
 version and only one account in Core  version.
|
||||
|
||||
Read more about [multiple sessions →]({{< relref "/docs/how-to/sessions" >}})
|
||||
@@ -123,6 +161,7 @@ way: use a phone international phone number without `+` symbol and add `@c.us` a
|
||||
For phone number `12132132131` the `chatId` is `12132132131@c.us`.
|
||||
|
||||
The example payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"chatId": "12132132130@c.us",
|
||||
@@ -142,6 +181,7 @@ curl -d "{\"chatId\": \"${PHONE}@c.us\", \"text\": \"Hello from WhatsApp HTTP AP
|
||||
```
|
||||
|
||||
## What is next?
|
||||
|
||||
- Right now you've sent simple text message, but you can send more! [Send messages →]({{< relref "/docs/how-to/send-messages" >}})
|
||||
- Read how to receive and response to messages [Receive messages →]({{< relref "/docs/how-to/receive-messages" >}})
|
||||
- Make sure your [API is properly protected ->]({{< relref "/docs/how-to/security" >}})
|
||||
|
||||
@@ -15,6 +15,18 @@
|
||||
<p class="meta"><a href="https://github.com/devlikeapro/whatsapp-http-api">Source code in GitHub.</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex justify-content-center my-4">
|
||||
<iframe
|
||||
width="560" height="315"
|
||||
src="https://www.youtube.com/embed/RFerMyAUPRg"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
@@ -22,6 +34,14 @@
|
||||
<section class="section section-sm">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center text-center">
|
||||
<div class="col-lg-5">
|
||||
<h2 class="h4">It's free!</h2>
|
||||
<p>
|
||||
<b>WAHA Core</b> version is always free!
|
||||
<br>
|
||||
Even <b>WAHA Plus</b> version have no licence expiration!
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<h2 class="h4">Language agnostic</h2>
|
||||
<p>
|
||||
@@ -36,17 +56,6 @@
|
||||
<h2 class="h4">Well documented</h2>
|
||||
<p>Documentation has a lof examples and how-to guides.</p>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<h2 class="h4">No blocking</h2>
|
||||
<p>
|
||||
Under the hood it runs a real instance of Whatsapp Web to avoid getting blocked.
|
||||
<br/>
|
||||
<small class="text-muted">
|
||||
WhatsApp does not allow bots or unofficial clients on their platform, so this shouldn't be considered
|
||||
totally safe.
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center text-center">
|
||||
<div class="col-lg-5">
|
||||
@@ -67,11 +76,14 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<h2 class="h4">It's free!</h2>
|
||||
<h2 class="h4">No blocking</h2>
|
||||
<p>
|
||||
<b>WAHA Core</b> version is always free!
|
||||
<br>
|
||||
Even <b>WAHA Plus</b> version does not require monthly donations and have no licence expiration!
|
||||
Under the hood it runs a real instance of Whatsapp Web to avoid getting blocked.
|
||||
<br/>
|
||||
<small class="text-muted">
|
||||
WhatsApp does not allow bots or unofficial clients on their platform, so this shouldn't be considered
|
||||
totally safe.
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,118 @@
|
||||
# WAHA (WhatsApp HTTP API) Python examples
|
||||
|
||||
## Installation
|
||||
|
||||
We assume that you have installed software:
|
||||
|
||||
1. Python 3
|
||||
2. Docker
|
||||
|
||||
### Download and start image
|
||||
|
||||
First of all, you must run WhatsApp HTTP API locally (which under the hood it runs real WhatsApp Web instance and expose
|
||||
HTTP API for interaction).
|
||||
|
||||
Here are the steps from [Quick Start](https://waha.devlike.pro/docs/overview/quick-start/):
|
||||
|
||||
Download and start WhatsApp HTTP API docker container
|
||||
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/whatsapp-http-api
|
||||
# Run the docker container
|
||||
docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bot -e "WHATSAPP_HOOK_EVENTS=*" --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
|
||||
```
|
||||
|
||||
#### Download image - ARM
|
||||
|
||||
If you're using ARM (like Apple Silicon, Apple M1, etc) - use following commands to download the image
|
||||
|
||||
 For Core version the command is
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/whatsapp-http-api:arm
|
||||
# Rename it, so you can use devlikeapro/whatsapp-http-api image in other place
|
||||
docker tag devlikeapro/whatsapp-http-api:arm devlikeapro/whatsapp-http-api
|
||||
# Run the docker container
|
||||
docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bot -e "WHATSAPP_HOOK_EVENTS=*" --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
|
||||
```
|
||||
|
||||
### Start session and scan QR
|
||||
|
||||
2. Open Swagger API in the browser http://localhost:3000/
|
||||
3. Start session and scan QR code in swagger
|
||||
1. Find `POST /api/session/start` request press **Try it out** and **Execute** it with `default` session name
|
||||
2. Find `GET /api/screenshot` and execute it - it must show QR code
|
||||
3. Scan QR code on your mobile WhatsApp application (that installed on your phone)
|
||||
4. Execute `GET /api/screenshot` once again - it must show the screenshot from WhatsApp Web.
|
||||
4. Send test text message - find `POST /api/sendText` and execute it with payload (change `12132132130` in the `chatId`
|
||||
to phone number that is registered in WhatsApp).
|
||||
|
||||
```json
|
||||
{
|
||||
"chatId": "12132132130@c.us",
|
||||
"text": "Hi there!",
|
||||
"session": "default"
|
||||
}
|
||||
```
|
||||
|
||||
If you see **Hi there!** message then you're ready to run bots!
|
||||
|
||||
## WhatsApp Echo Bot
|
||||
|
||||
The WhatsApp Echo Bot is a sample flask webhook server application that echoes back to you whatever you send it. It can
|
||||
serve
|
||||
as a basic reference for how to set up webhooks and reply to incoming messages.
|
||||
|
||||
```bash
|
||||
# Clone the git repository with example
|
||||
git clone https://github.com/devlikeapro/whatsapp-http-api.git
|
||||
# Open python example folder
|
||||
cd whatsapp-http-api/examples/python
|
||||
# Install requirements
|
||||
python -mpip install -r requirements.txt
|
||||
# Run the bot
|
||||
FLASK_APP=whatsapp_echo_bot.py flask run
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Echo Bot is ready!** then the bot is ready to receive message!
|
||||
|
||||
Send message to the WhatsApp (that you used to scan QR code) and it'll echo text back to you!
|
||||
|
||||
## WhatsApp Download Files Bot
|
||||
|
||||
The WhatsApp Download Image Bot downloads all files people send to your WhatsApp and log the path for the file.
|
||||
|
||||
**The bot works only with WAHA Plus version** available with donations.
|
||||
Visit [read more about difference between Core and Plus versions](https://waha.devlike.pro/docs/how-to/plus-version/).
|
||||
|
||||
Download **WAHA Plus** version:
|
||||
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/whatsapp-http-api-plus
|
||||
# Run the docker container
|
||||
docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bot -e "WHATSAPP_HOOK_EVENTS=*" --name whatsapp-http-api devlikeapro/whatsapp-http-api-plus
|
||||
```
|
||||
|
||||
Run the WhatsApp Download Files bot:
|
||||
|
||||
```bash
|
||||
# Clone the git repository with example
|
||||
git clone https://github.com/devlikeapro/whatsapp-http-api.git
|
||||
# Open python example folder
|
||||
cd whatsapp-http-api/examples/python
|
||||
# Install requirements
|
||||
python -mpip install -r requirements.txt
|
||||
# Run the bot
|
||||
FLASK_APP=whatsapp_download_files_bot.py flask run
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Download Files Bot!** then the bot is ready to receive message
|
||||
with files!
|
||||
@@ -1,44 +0,0 @@
|
||||
from pprint import pprint
|
||||
|
||||
import requests
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def whatsapp_echo():
|
||||
return "WhatsApp HTTP API Echo server is ready!"
|
||||
|
||||
|
||||
@app.route("/message", methods=["GET", "POST"])
|
||||
def whatsapp_webhook():
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return "WhatsApp HTTP API Echo server is ready!"
|
||||
pprint(data)
|
||||
if data['event'] != "message":
|
||||
return f"Unknown event {data['event']}"
|
||||
|
||||
payload = data["payload"]
|
||||
# The text
|
||||
text = payload["body"]
|
||||
# Number in format 791111111@c.us
|
||||
from_ = payload["from"]
|
||||
|
||||
if payload.get("mediaUrl", None):
|
||||
# Download file and set text to path
|
||||
client_url = payload["mediaUrl"]
|
||||
filename = client_url.split("/")[-1]
|
||||
path = "/tmp/" + filename
|
||||
r = requests.get(client_url)
|
||||
open(path, "wb").write(r.content)
|
||||
text = f"We have downloaded file here: {path}"
|
||||
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendText", json={"chatId": from_, "text": text}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return "OK"
|
||||
@@ -1,2 +1,2 @@
|
||||
flask
|
||||
requests
|
||||
flask~=1.0
|
||||
requests~=2.0
|
||||
@@ -0,0 +1,84 @@
|
||||
from os.path import abspath
|
||||
from pprint import pprint
|
||||
|
||||
import requests
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
def send_message(chat_id, text):
|
||||
"""
|
||||
Send message to chat_id.
|
||||
:param chat_id: Phone number + "@c.us" suffix - 1231231231@c.us
|
||||
:param text: Message for the recipient
|
||||
"""
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendText",
|
||||
json={
|
||||
"chatId": chat_id,
|
||||
"text": text,
|
||||
"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():
|
||||
return "WhatsApp Download Files Bot is ready!"
|
||||
|
||||
|
||||
@app.route("/bot", methods=["GET", "POST"])
|
||||
def whatsapp_webhook():
|
||||
if request.method == "GET":
|
||||
return "WhatsApp Download Files Bot is ready!"
|
||||
|
||||
data = request.get_json()
|
||||
pprint(data)
|
||||
if data["event"] != "message":
|
||||
# We can't process other event yet
|
||||
return f"Unknown event {data['event']}"
|
||||
|
||||
payload = data["payload"]
|
||||
# Ignore messages without files
|
||||
if not payload.get("mediaUrl", None):
|
||||
return "No files in the message"
|
||||
|
||||
# Number in format 791111111@c.us
|
||||
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"]
|
||||
filename = client_url.split("/")[-1]
|
||||
path = abspath("./" + filename)
|
||||
r = requests.get(client_url)
|
||||
with open(path, "wb") as f:
|
||||
f.write(r.content)
|
||||
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
text = f"We have downloaded file here: {path}"
|
||||
print(text)
|
||||
send_message(chat_id=chat_id, text=text)
|
||||
|
||||
# Send OK back
|
||||
return "OK"
|
||||
@@ -0,0 +1,88 @@
|
||||
from pprint import pprint
|
||||
|
||||
import requests
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
def send_message(chat_id, text):
|
||||
"""
|
||||
Send message to chat_id.
|
||||
:param chat_id: Phone number + "@c.us" suffix - 1231231231@c.us
|
||||
:param text: Message for the recipient
|
||||
"""
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendText",
|
||||
json={
|
||||
"chatId": chat_id,
|
||||
"text": text,
|
||||
"session": "default",
|
||||
},
|
||||
)
|
||||
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():
|
||||
return "WhatsApp Echo Bot is ready!"
|
||||
|
||||
|
||||
@app.route("/bot", methods=["GET", "POST"])
|
||||
def whatsapp_webhook():
|
||||
if request.method == "GET":
|
||||
return "WhatsApp Echo Bot is ready!"
|
||||
|
||||
data = request.get_json()
|
||||
pprint(data)
|
||||
if data["event"] != "message":
|
||||
# We can't process other event yet
|
||||
return f"Unknown event {data['event']}"
|
||||
|
||||
# Payload that we've got
|
||||
payload = data["payload"]
|
||||
# The text
|
||||
text = payload["body"]
|
||||
# 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=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"
|
||||
Generated
-32623
File diff suppressed because it is too large.
Load diff
+8
-2
@@ -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,16 @@
|
||||
"@nestjs/platform-express": "^9.0.9",
|
||||
"@nestjs/serve-static": "^2.1.3",
|
||||
"@nestjs/swagger": "^6.0.5",
|
||||
"@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",
|
||||
"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",
|
||||
@@ -43,7 +49,7 @@
|
||||
"rxjs": "^7.1.0",
|
||||
"swagger-ui-express": "^4.1.4",
|
||||
"venom-bot": "^4.3.7",
|
||||
"whatsapp-web.js": "^1.19.3"
|
||||
"whatsapp-web.js": "https://github.com/pedroslopez/whatsapp-web.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
@@ -51,7 +57,7 @@
|
||||
"@nestjs/testing": "^9.0.9",
|
||||
"@types/express": "^4.17.3",
|
||||
"@types/jest": "26.0.10",
|
||||
"@types/node": "^13.9.1",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "3.9.1",
|
||||
"@typescript-eslint/parser": "3.9.1",
|
||||
|
||||
@@ -2,7 +2,7 @@ import {Body, Controller, Get, Post, Put, Query} from '@nestjs/common';
|
||||
import {ApiOperation, ApiSecurity, ApiTags} from "@nestjs/swagger";
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
CheckNumberStatusQuery, GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextQuery,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest
|
||||
MessageVoiceRequest, SendSeenRequest
|
||||
} from "../structures/chatting.dto";
|
||||
import {WAMessage, WANumberExistResult} from "../structures/responses.dto";
|
||||
import {SessionManager} from "../core/abc/manager.abc";
|
||||
@@ -105,7 +105,7 @@ export class ChattingController {
|
||||
}
|
||||
|
||||
@Post('/sendSeen')
|
||||
sendSeen(@Body() chat: ChatRequest) {
|
||||
sendSeen(@Body() chat: SendSeenRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session)
|
||||
return whatsapp.sendSeen(chat)
|
||||
}
|
||||
@@ -131,4 +131,11 @@ export class ChattingController {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
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) {
|
||||
}
|
||||
|
||||
@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(':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)
|
||||
}
|
||||
|
||||
@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/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)
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
@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/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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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)
|
||||
* use it as
|
||||
@Param('session', SessionPipe) session: WhatsappSession,
|
||||
*/
|
||||
@Injectable()
|
||||
export class SessionPipe implements PipeTransform<WhatsappSession> {
|
||||
constructor(private manager: SessionManager) {
|
||||
}
|
||||
|
||||
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",
|
||||
})
|
||||
|
||||
/**
|
||||
* Session param
|
||||
@SessionParam session: WhatsappSession,
|
||||
*/
|
||||
export const SessionParam = Param('session', SessionPipe)
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import {WAEvents, WhatsappStatus} from "../../structures/enums.dto";
|
||||
import {ConsoleLogger} from "@nestjs/common";
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
CheckNumberStatusQuery, GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
@@ -18,18 +18,45 @@ 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";
|
||||
|
||||
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
|
||||
}
|
||||
export function ensureSuffix(phone) {
|
||||
const suffix = "@c.us"
|
||||
if (phone.includes("@")) {
|
||||
return phone
|
||||
}
|
||||
return phone + suffix
|
||||
}
|
||||
|
||||
export enum WAHAInternalEvent {
|
||||
engine_start = "engine.start",
|
||||
}
|
||||
|
||||
export abstract class WhatsappSession {
|
||||
public status: WhatsappStatus;
|
||||
public events: EventEmitter
|
||||
|
||||
public constructor(public name: string, protected storage: MediaStorage, protected log: ConsoleLogger) {
|
||||
this.name = name
|
||||
this.status = WhatsappStatus.STARTING
|
||||
this.log = log
|
||||
this.events = new EventEmitter()
|
||||
}
|
||||
|
||||
getBrowserExecutablePath() {
|
||||
return "/usr/bin/google-chrome-stable"
|
||||
return getBrowserExecutablePath()
|
||||
}
|
||||
|
||||
getBrowserArgsForPuppeteer() {
|
||||
@@ -103,8 +130,11 @@ export abstract class WhatsappSession {
|
||||
|
||||
abstract stopTyping(chat: ChatRequest)
|
||||
|
||||
abstract getMessages(query: GetMessageQuery)
|
||||
|
||||
abstract setReaction(request: MessageReactionRequest)
|
||||
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
@@ -132,6 +162,65 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public getGroups() {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public getGroup(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public leaveGroup(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public setDescription(id, description) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public setSubject(id, description) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public getInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public revokeInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public getParticipants(id) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public addParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
public removeParticipants(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
|
||||
@@ -142,11 +231,7 @@ export abstract class WhatsappSession {
|
||||
* @param phone
|
||||
*/
|
||||
protected ensureSuffix(phone) {
|
||||
const suffix = "@c.us"
|
||||
if (phone.includes("@")) {
|
||||
return phone
|
||||
}
|
||||
return phone + suffix
|
||||
return ensureSuffix(phone)
|
||||
}
|
||||
|
||||
protected deserializeId(messageId: string): MessageId {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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({
|
||||
@@ -32,6 +33,7 @@ export const CONTROLLERS = [
|
||||
SessionsController,
|
||||
ChattingController,
|
||||
ContactsController,
|
||||
GroupsController,
|
||||
ScreenshotController,
|
||||
VersionController,
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {ConsoleLogger, Injectable, NotFoundException, UnprocessableEntityException} from "@nestjs/common";
|
||||
import {SessionManager} from "./abc/manager.abc";
|
||||
import {WhatsappSession} from "./abc/session.abc";
|
||||
import {WAHAInternalEvent, WhatsappSession} from "./abc/session.abc";
|
||||
import {WhatsappEngine} from "../structures/enums.dto";
|
||||
import {SessionDTO, SessionStartRequest, SessionStopRequest} from "../structures/sessions.dto";
|
||||
import {WhatsappConfigService} from "../config.service";
|
||||
@@ -9,6 +9,7 @@ import {WhatsappSessionWebJSCore} from "./session.webjs.core";
|
||||
import {DOCS_URL} from "./exceptions";
|
||||
import {WebhookConductorCore} from "./webhooks.core";
|
||||
import {MediaStorageCore} from "./storage.core";
|
||||
import {WhatsappSessionNoWebCore} from "./session.noweb.core";
|
||||
|
||||
export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
|
||||
constructor() {
|
||||
@@ -49,6 +50,8 @@ export class SessionManagerCore extends SessionManager {
|
||||
return WhatsappSessionWebJSCore
|
||||
} else if (engine === WhatsappEngine.VENOM) {
|
||||
return WhatsappSessionVenomCore
|
||||
} else if (engine === WhatsappEngine.NOWEB) {
|
||||
return WhatsappSessionNoWebCore
|
||||
} else {
|
||||
throw new NotFoundException(`Unknown whatsapp engine '${engine}'.`)
|
||||
}
|
||||
@@ -100,7 +103,8 @@ export class SessionManagerCore extends SessionManager {
|
||||
const session = new this.EngineClass(name, storage, log)
|
||||
this.session = session
|
||||
|
||||
session.start().then(() => webhook.configure(session))
|
||||
session.events.on(WAHAInternalEvent.engine_start, () => webhook.configure(session))
|
||||
session.start()
|
||||
return {name: session.name, status: session.status}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
import {SECOND, WAEvents, WhatsappStatus} from "../structures/enums.dto";
|
||||
import makeWASocket, {DisconnectReason, makeInMemoryStore, isJidGroup, 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 {
|
||||
sock: any
|
||||
store: any
|
||||
private qr: QR
|
||||
authFolder: string
|
||||
|
||||
public constructor(public name: string, protected storage: MediaStorage, protected log: ConsoleLogger) {
|
||||
super(name, storage, log);
|
||||
this.qr = new QR()
|
||||
this.authFolder = this.getAuthFolder()
|
||||
}
|
||||
|
||||
start() {
|
||||
return this.buildClient()
|
||||
}
|
||||
|
||||
protected getAuthFolder() {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), 'noweb-'))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import {WhatsappSession} from "./abc/session.abc";
|
||||
import {WAHAInternalEvent, WhatsappSession} from "./abc/session.abc";
|
||||
import {
|
||||
Button,
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
@@ -20,20 +21,7 @@ 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")
|
||||
}
|
||||
|
||||
}
|
||||
import {QR} from "./QR";
|
||||
|
||||
export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
whatsapp: Whatsapp;
|
||||
@@ -84,6 +72,7 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
this.status = WhatsappStatus.WORKING
|
||||
this.events.emit(WAHAInternalEvent.engine_start)
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -126,10 +115,10 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
|
||||
async checkNumberStatus(request: CheckNumberStatusQuery): Promise<WANumberExistResult> {
|
||||
try {
|
||||
return await this.whatsapp.checkNumberStatus(this.ensureSuffix(request.phone))
|
||||
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){
|
||||
if (error.status === 404 && !error.numberExists) {
|
||||
return error
|
||||
}
|
||||
throw error
|
||||
@@ -165,7 +154,7 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
sendLocation(request: MessageLocationRequest) {
|
||||
return this.whatsapp.sendLocation(request.chatId, request.latitude, request.longitude, request.title)
|
||||
return this.whatsapp.sendLocation(request.chatId, String(request.latitude), String(request.longitude), request.title)
|
||||
}
|
||||
|
||||
sendSeen(chat: ChatRequest) {
|
||||
@@ -177,11 +166,11 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
return {
|
||||
buttonId: button.id,
|
||||
buttonText: {
|
||||
displayText: button.body
|
||||
displayText: button.text
|
||||
}
|
||||
}
|
||||
})
|
||||
return this.whatsapp.sendButtons(this.ensureSuffix(request.chatId), request.title, buttons, request.text)
|
||||
return this.whatsapp.sendButtons(this.ensureSuffix(request.chatId), request.title, buttons, request.footer)
|
||||
}
|
||||
|
||||
startTyping(chat: ChatRequest) {
|
||||
@@ -192,6 +181,20 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
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
|
||||
}
|
||||
|
||||
setReaction(request: MessageReactionRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
+114
-11
@@ -1,12 +1,14 @@
|
||||
import {UnprocessableEntityException} from "@nestjs/common/exceptions/unprocessable-entity.exception";
|
||||
import {Buttons, Chat, Client, Contact, Events, Message} from "whatsapp-web.js";
|
||||
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 {WAHAInternalEvent, WhatsappSession} from "./abc/session.abc";
|
||||
import {WAMessage, WANumberExistResult} from "../structures/responses.dto";
|
||||
import {
|
||||
Button,
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
@@ -16,10 +18,11 @@ import {
|
||||
MessageReplyRequest,
|
||||
MessageTextButtonsRequest,
|
||||
MessageTextRequest,
|
||||
MessageVoiceRequest
|
||||
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');
|
||||
@@ -57,11 +60,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
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.pupPage.close()
|
||||
return this.whatsapp.destroy()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +88,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
sendTextButtons(request: MessageTextButtonsRequest) {
|
||||
const message = new Buttons("", request.buttons, request.title, request.text)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -111,15 +121,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
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()
|
||||
}
|
||||
|
||||
sendLocation(request: MessageLocationRequest) {
|
||||
throw new NotImplementedByEngineError()
|
||||
}
|
||||
|
||||
async sendSeen(request: ChatRequest) {
|
||||
async sendSeen(request: SendSeenRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(request.chatId)
|
||||
await chat.sendSeen()
|
||||
}
|
||||
@@ -134,10 +150,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
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)
|
||||
console.log(messageId)
|
||||
|
||||
// Recreate instance to react on it
|
||||
const message = new MessageInstance(this.whatsapp)
|
||||
message.id = messageId
|
||||
@@ -178,6 +205,82 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,10 @@ export class SwaggerModuleCore {
|
||||
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',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {WhatsappSession} from "./abc/session.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";
|
||||
@@ -40,7 +40,7 @@ export class WebhookConductorCore implements WebhookConductor {
|
||||
this.log.log('Configuring webhooks...')
|
||||
for (const event of this.events) {
|
||||
try {
|
||||
session.subscribe(event, (data: any) => this.callWebhook(event, data, this.url))
|
||||
session.subscribe(event, (data: any) => this.callWebhook(event, session, data, this.url))
|
||||
} catch (error) {
|
||||
if (error instanceof NotImplementedByEngineError) {
|
||||
this.log.error(error)
|
||||
@@ -53,8 +53,8 @@ export class WebhookConductorCore implements WebhookConductor {
|
||||
this.log.log('Webhooks were configured.')
|
||||
}
|
||||
|
||||
public callWebhook(event, data: any, url) {
|
||||
const json: WAWebhook = {event: event, payload: data}
|
||||
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)
|
||||
|
||||
@@ -4,6 +4,7 @@ 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()
|
||||
@@ -32,6 +33,10 @@ async function bootstrap() {
|
||||
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'}));
|
||||
|
||||
// Configure swagger
|
||||
const swagger = new SwaggerModule()
|
||||
swagger.configure(app)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {IsNotEmpty, IsString} from "class-validator";
|
||||
import {IsNotEmpty, IsString, IsOptional, IsNumber} from "class-validator";
|
||||
import {ApiExtraModels, ApiProperty, getSchemaPath} from "@nestjs/swagger";
|
||||
import {SessionBaseRequest, SessionQuery} from "./base.dto";
|
||||
|
||||
@@ -18,6 +18,18 @@ export class MessageTextQuery extends SessionQuery {
|
||||
text: string
|
||||
}
|
||||
|
||||
export class ChatQuery extends SessionQuery {
|
||||
@ApiProperty({
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
chatId: string
|
||||
}
|
||||
|
||||
export class GetMessageQuery extends ChatQuery {
|
||||
@IsNumber()
|
||||
limit: number
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Requests
|
||||
@@ -29,7 +41,23 @@ export class ChatRequest extends SessionBaseRequest {
|
||||
chatId: string
|
||||
}
|
||||
|
||||
export class MessageRequest extends SessionBaseRequest{
|
||||
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",
|
||||
})
|
||||
@@ -47,12 +75,12 @@ export class MessageTextRequest extends ChatRequest {
|
||||
|
||||
export class Button {
|
||||
id: string
|
||||
body: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export class MessageTextButtonsRequest extends ChatRequest {
|
||||
title: string;
|
||||
text: string;
|
||||
footer?: string
|
||||
buttons?: Array<Button>;
|
||||
}
|
||||
|
||||
@@ -65,8 +93,8 @@ export class MessageReplyRequest extends ChatRequest {
|
||||
}
|
||||
|
||||
export class MessageLocationRequest extends ChatRequest {
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
title: string;
|
||||
}
|
||||
|
||||
@@ -186,3 +214,4 @@ export class MessageReactionRequest extends MessageRequest {
|
||||
})
|
||||
reaction: string
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {IsString} from "class-validator";
|
||||
import {SessionBaseRequest, SessionQuery, WHATSAPP_DEFAULT_SESSION_NAME} from "./base.dto";
|
||||
import {SessionBaseRequest, SessionQuery} from "./base.dto";
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,5 +18,6 @@ export enum WhatsappStatus {
|
||||
|
||||
export enum WhatsappEngine {
|
||||
VENOM = "VENOM",
|
||||
WEBJS = "WEBJS"
|
||||
WEBJS = "WEBJS",
|
||||
NOWEB = "NOWEB",
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {IsArray, IsString} from "class-validator";
|
||||
|
||||
/**
|
||||
* Structures
|
||||
*/
|
||||
export class Participant {
|
||||
@IsString()
|
||||
id: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
|
||||
export class ParticipantsRequest {
|
||||
@IsArray()
|
||||
participants: Array<Participant>
|
||||
}
|
||||
|
||||
export class DescriptionRequest {
|
||||
@IsString()
|
||||
description: string
|
||||
}
|
||||
export class SubjectRequest {
|
||||
@IsString()
|
||||
subject: string
|
||||
}
|
||||
|
||||
export class CreateGroupRequest {
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsArray()
|
||||
participants: Array<Participant>
|
||||
}
|
||||
@@ -88,6 +88,11 @@ export class WAMessage {
|
||||
})
|
||||
to: string
|
||||
|
||||
@ApiProperty({
|
||||
description: "For groups - participant who sent the message"
|
||||
})
|
||||
participant: string
|
||||
|
||||
@ApiProperty({
|
||||
description: "Message content"
|
||||
})
|
||||
@@ -133,6 +138,7 @@ export class WAMessage {
|
||||
|
||||
export class WAWebhook {
|
||||
event: WAEvents
|
||||
session: string
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
payload: WAMessage | WAGroupNotification | object
|
||||
}
|
||||
+8
-1
@@ -1,3 +1,6 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
import {getBrowserExecutablePath} from "./core/abc/session.abc";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const fs = require('fs')
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
@@ -24,4 +27,8 @@ export function getWAHAVersion(): WAHAVersion {
|
||||
return WAHAVersion.CORE
|
||||
}
|
||||
|
||||
export const VERSION = {version: "2022.12.11", tier: getWAHAVersion()}
|
||||
export const VERSION = {
|
||||
version: "2023.5.3",
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowserExecutablePath()
|
||||
}
|
||||
-656
@@ -1,656 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "WhatsApp HTTP API",
|
||||
"description": "WhatsApp HTTP API that you can configure in a click",
|
||||
"version": "1.0",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "device",
|
||||
"description": "Device information"
|
||||
},
|
||||
{
|
||||
"name": "chatting",
|
||||
"description": "Chat methods"
|
||||
}
|
||||
],
|
||||
"servers": [],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-VENOM-TOKEN",
|
||||
"description": "Your secret key"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"MessageContactVcard": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"contactsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"contactsId",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"MessageText": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"MessageLocation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"latitude": {
|
||||
"type": "string"
|
||||
},
|
||||
"longitude": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"title"
|
||||
]
|
||||
},
|
||||
"MessageLinkPreview": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"url",
|
||||
"title"
|
||||
]
|
||||
},
|
||||
"MessageImage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"path",
|
||||
"filename",
|
||||
"caption"
|
||||
]
|
||||
},
|
||||
"MessageFile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"path",
|
||||
"filename",
|
||||
"caption"
|
||||
]
|
||||
},
|
||||
"MessageReply": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"reply_to": {
|
||||
"type": "string",
|
||||
"example": "message.id"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId",
|
||||
"reply_to",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"Chat": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"chatId"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/api/sendContactVcard": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendContactVcard",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageContactVcard"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendText": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendText",
|
||||
"summary": "Send a text message",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageText"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendLocation": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendLocation",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageLocation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendLinkPreview": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendLinkPreview",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageLinkPreview"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendImage": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendImage",
|
||||
"summary": "NOT IMPLEMENTED YET",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageImage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendFile": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendFile",
|
||||
"summary": "NOT IMPLEMENTED YET",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageFile"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/reply": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_reply",
|
||||
"summary": "Reply to a text message",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageReply"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendSeen": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendSeen",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Chat"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/startTyping": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_startTyping",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Chat"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/stopTyping": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_stopTyping",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Chat"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/killServiceWorker": {
|
||||
"post": {
|
||||
"operationId": "DeviceController_killServiceWorker",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/restartService": {
|
||||
"post": {
|
||||
"operationId": "DeviceController_restartService",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getHostDevice": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getHostDevice",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getConnectionState": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getConnectionState",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getBatteryLevel": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getBatteryLevel",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/isConnected": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_isConnected",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getWAVersion": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getWAVersion",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/screenshot": {
|
||||
"get": {
|
||||
"operationId": "ScreenshotController_screenshot",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"screenshot"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user