Compare commits

..
92 Commits
Author SHA1 Message Date
allburov c61da10523 [core] Pin Web version
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-04-01 09:31:08 +07:00
allburov 1f53d65ac2 [docs] fix card in article 2024-03-31 18:43:31 +07:00
allburov 844ab1287e [docs] fix links 2024-03-31 18:39:39 +07:00
allburov d6d318b23b [docs] Are you a visual 2024-03-31 16:04:26 +07:00
allburov 7504ffd934 [docs] quick start second 2024-03-31 16:00:06 +07:00
allburov e8078908c9 [docs] http api 2024-03-31 15:56:37 +07:00
allburov 431848280f [docs] Fix debug 2024-03-29 16:06:31 +07:00
allburov 2e0c575512 [core] Bump version to 2024.3.14 2024-03-29 15:57:15 +07:00
allburov ebf0d99417 [core] Add "debug" flag to session config 2024-03-29 15:56:50 +07:00
allburov 423d893fee [docs] No hover 2024-03-29 14:37:21 +07:00
allburov 56fe0d3b04 [docs] Limit 2024-03-29 14:22:36 +07:00
allburov 7172d9d203 [docs] Green icons 2024-03-29 14:21:20 +07:00
allburov bd770a6902 [docs] Fix index 2024-03-29 14:06:56 +07:00
allburov 628508faed [docs] ktor 2024-03-29 13:55:27 +07:00
allburov 826634f061 [docs] Add js, php for future 2024-03-29 13:37:23 +07:00
allburov c7ed959a71 [docs] Add kotlin 2024-03-29 13:32:15 +07:00
allburov aea6befc1e [docs] Add NOWEB feedback 2024-03-29 10:08:30 +07:00
allburov 7bb3484788 [docs] Add python example 2024-03-28 08:51:38 +07:00
allburov 4eafc67a7e [core] Bump 2024.3.13 2024-03-27 17:09:29 +07:00
allburov b5197b6f20 [core] log page logs 2024-03-27 17:09:29 +07:00
allburov d306304195 [core] One line log 2024-03-27 17:09:29 +07:00
allburov 35f47dae4f [docs] Boosty instead of Patreon 2024-03-27 13:52:00 +07:00
allburov f1be4810d5 [core] Bump to 2024.3.12 2024-03-27 13:27:39 +07:00
allburov d4f9406869 [core] Update NOWEB 2024-03-27 13:27:36 +07:00
allburov a41b81d724 [docs] Add funding 2024-03-26 16:54:09 +07:00
allburov da56b86102 [docs] Boosty first 2024-03-26 16:51:58 +07:00
allburov f12d2c9be0 [docs] Add gtag 2024-03-25 11:54:21 +07:00
allburov 322b9b4c25 [docs] Correct changelog 2024-03-24 20:52:26 +07:00
allburov fb7fe410f9 [core] Bump to 2024.3.11 2024-03-24 20:43:27 +07:00
allburov ee392a079c [core] Update swagger 2024-03-24 20:43:27 +07:00
allburov 0502579d50 [core] Edit and delete messages 2024-03-24 20:43:27 +07:00
allburov f10f3175d9 [core] add new swagger 2024-03-24 18:00:36 +07:00
allburov f155af99f1 [core] Bump 2024.3.10 2024-03-24 17:58:32 +07:00
allburov 0fbf3575a6 [core] NOWEB sendVCard 2024-03-24 17:58:30 +07:00
allburov f6b9b5f54e [core] Bump to 2024.3.9 2024-03-22 18:56:54 +07:00
allburov 2dadd9d8c9 [docs] Add caption to sendfile 2024-03-22 18:56:40 +07:00
allburov 5786b3cdfe [docs] Adjust cards style 2024-03-22 10:18:26 +07:00
allburov 769fc30c0c [docs] Adjust cards style 2024-03-22 10:09:21 +07:00
allburov fb2dad9c6f [core] bump version to 2024.3.8 2024-03-22 09:47:04 +07:00
allburov c43d308a2d [core] Add star unstar
Fix #240
2024-03-22 09:46:25 +07:00
allburov 742a12ce6a [core] Fix reactions 2024-03-22 09:25:23 +07:00
allburov d953f27865 [core] Update openapi.json 2024-03-20 12:18:53 +07:00
allburov 4719cded95 [core] get engine info 2024-03-19 14:16:45 +07:00
allburov 9d489bdb69 [core] add Patron Portal 2024-03-18 18:37:20 +07:00
allburov 537c307550 [core] No mobile engine anymore 2024-03-18 18:27:11 +07:00
allburov d5575e0323 [core] Fix index 2024-03-18 18:22:38 +07:00
allburov 592962bbc8 [core] Use vars.DOCKER_IMAGE 2024-03-18 15:25:39 +07:00
allburov 7a371c86dc [core] Images 2024-03-18 15:19:20 +07:00
allburov 0e295bebde [core] Don't push each version 2024-03-18 15:06:19 +07:00
allburov 8d98759be6 Revert "[core] Update WEBJS with 2.2300 fix"
This reverts commit 924317b01b.
2024-03-18 14:56:07 +07:00
allburov 924317b01b [core] Update WEBJS with 2.2300 fix 2024-03-18 14:55:50 +07:00
allburov a08a601dc3 [core] manual push 2024-03-18 14:54:22 +07:00
allburov e652c15d6c [core] Add manual jobs 2024-03-18 14:45:57 +07:00
allburov 4534eb60de [core] Add manual jobs 2024-03-18 14:44:43 +07:00
allburov 54c6c0c24c [core] Read more about Security 2024-03-18 14:21:49 +07:00
allburov 393694ae96 [core] Rm even 2024-03-18 14:17:57 +07:00
allburov 818bb730e4 Revert "[core] fix CI"
This reverts commit 61bd35a192.
2024-03-18 14:05:54 +07:00
allburov 61bd35a192 [core] fix CI 2024-03-18 14:02:59 +07:00
allburov 81f9df000a [core] fix CI
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-03-18 13:48:46 +07:00
allburov 0dcb31c108 [core] Bump to 2024.3.6 2024-03-18 13:46:32 +07:00
allburov cd4140fda9 [core] Add exact version (tag) 2024-03-18 13:46:30 +07:00
allburov 117cc25cdb [core] Bump version to 2024.3.5 2024-03-17 19:17:32 +07:00
allburov 832ad26151 [core] Add /messages-admin-only
fix #274
2024-03-17 19:17:14 +07:00
allburov 5a344cd8ad [core] Bump version to 2024.3.4 2024-03-15 17:45:23 +07:00
allburov 0b0a67cdad [core] Update WEBJS engine 2024-03-15 17:45:21 +07:00
allburov a326a240f0 [core] Bump version 2024.3.3 2024-03-15 15:15:13 +07:00
allburov dfdb033769 [core] Add message.reaction webhook 2024-03-15 15:14:48 +07:00
allburov a31af45d97 [core] Change code on main 2024-03-13 17:49:52 +07:00
allburov cae955c483 [core] Bump to 2024.3.2
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-03-07 19:28:12 +07:00
allburov 0bfff5259e [core] Use ubuntu in NOWEB 2024-03-07 19:27:45 +07:00
allburov 0fa83e603b [core] No more MOBILE engine 2024-03-04 23:14:28 +07:00
allburov 906b03804f [core] Update WEBJS
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-03-02 18:08:52 +07:00
allburov 1ed7a61bea [core] Use fork for WEBJS in Makefile 2024-03-02 18:08:31 +07:00
allburov ad6ae7ab05 [core] Bump version to 2024.3.1 2024-03-02 18:02:30 +07:00
allburov 5e2de1d47d [core] Bump to 2024.2.4
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-02-25 17:57:29 +07:00
allburov 591ca51104 [core] add GET /health 2024-02-25 17:56:58 +07:00
allburov b210841158 [core] Fix documentation for message.revoked 2024-02-21 10:31:54 +07:00
allburov a223a3985b [core] Self-hosted solution 2024-02-19 14:52:23 +07:00
allburov 8baa6a73cd [core] Self-hosted 2024-02-19 14:52:07 +07:00
allburov 508efac2ea [core] Add links on index 2024-02-19 14:51:59 +07:00
allburov b6eed9e228 [core] Rewrite tiers 2024-02-19 14:46:17 +07:00
allburov b62cad07f0 [core] Icons on main page 2024-02-19 14:28:18 +07:00
allburov 6d552d27ac [core] Bold 2024-02-19 14:11:24 +07:00
allburov 39d0f2a060 [core] Fix doc about WEBJS
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
2024-02-18 20:22:45 +07:00
allburov 0b03abee91 [core] Fix delete file when it doesn't exist anymore 2024-02-18 20:22:41 +07:00
allburov b4fb69d0ee [core] Use extends instead of implements for LocalStoreCore.ts 2024-02-18 19:55:54 +07:00
allburov 5b9ed029b3 [core] NOWEB - fix stop session 2024-02-18 19:42:32 +07:00
allburov c29397371b [core] Use network host in mongo example 2024-02-18 19:37:45 +07:00
allburov fe47e27788 [core] Fix yarn file 2024-02-18 19:37:33 +07:00
allburov a14c702c79 [core] Bump to 2024.2.3 2024-02-18 19:25:12 +07:00
allburov fe4246036d [core] Support MongoDB 2024-02-18 19:21:19 +07:00
allburov 074063b814 [core] Fix set presence documentation 2024-02-12 11:34:15 +07:00
84 changed files with 3551 additions and 1580 deletions

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: wa_http_api
open_collective: # doks Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom:
- https://boosty.to/wa-http-api
- https://portal.devlike.pro/
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ secrets.DOCKER_IMAGE }}
images: ${{ vars.DOCKER_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=${{ matrix.tag }}
+84
View File
@@ -0,0 +1,84 @@
name: Manual push
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to push'
required: true
default: 'dev'
push:
description: 'Push to registry'
required: true
type: boolean
default: true
jobs:
docker-build:
runs-on: ${{ matrix.runner }}
name: ${{ matrix.platform }} - ${{ matrix.browser }} - ${{ matrix.tag }}
strategy:
matrix:
include:
# Chromium
- runner: 'buildjet-4vcpu-ubuntu-2204'
tag: 'latest'
platform: 'amd64'
browser: 'chromium'
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
tag: 'arm'
platform: 'linux/arm64'
browser: 'chromium'
# Chrome
- runner: 'buildjet-4vcpu-ubuntu-2204'
tag: 'chrome'
platform: 'amd64'
browser: 'chrome'
# Chrome is not supported on arm64
# - runner: "buildjet-4vcpu-ubuntu-2204-arm"
# tag: "chrome-arm"
# platform: "linux/arm64"
# browser: "chrome"
# No browser
- runner: 'buildjet-4vcpu-ubuntu-2204'
tag: 'noweb'
platform: 'amd64'
browser: 'none'
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
tag: 'noweb-arm'
platform: 'linux/arm64'
browser: 'none'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ vars.DOCKER_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=${{ inputs.tag }}-${{ matrix.tag }}
- name: Login to image repository
if: github.ref_type == 'tag'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
USE_BROWSER=${{ matrix.browser }}
push: ${{ inputs.push }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+1 -1
View File
@@ -35,7 +35,7 @@ update-swagger:
wget http://localhost:3000/-json -O ./docs/site/static/swagger/openapi.json
update-dependencies:
. ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@https://github.com/pedroslopez/whatsapp-web.js
. ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js#main-fork
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:WhiskeySockets/Baileys
. ${NVM_DIR}/nvm.sh && nvm exec yarn up venom-bot
+12
View File
@@ -30,3 +30,15 @@ services:
- WHATSAPP_SWAGGER_PASSWORD=123
- WHATSAPP_API_HOSTNAME=localhost
restart: always
mongodb:
image: mongo
container_name: mongodb
ports:
- '27017:27017/tcp'
volumes:
- mongodb_data:/data/db
restart: always
volumes:
mongodb_data: {}
@@ -85,13 +85,13 @@
[[main]]
name = "Get on Patreon ->"
weight = 50
weight = 60
url = "https://patreon.com/wa_http_api"
parent = "waha-plus"
[[main]]
name = "Get on Boosty ->"
weight = 60
weight = 50
url = "https://boosty.to/wa-http-api"
parent = "waha-plus"
@@ -104,9 +104,14 @@
[[main]]
name = "Python ->"
weight = 21
url = "https://github.com/devlikeapro/whatsapp-http-api/tree/core/examples/python"
url = "/docs/examples/python"
parent = "examples"
[[main]]
name = "Kotlin ->"
weight = 22
url = "/docs/examples/kotlin"
parent = "examples"
[[social]]
name = "GitHub"
+1 -1
View File
@@ -1,7 +1,7 @@
---
title : "WAHA"
description: "WAHA - WhatsApp HTTP API that you can run in a click!"
lead: "WAHA - WhatsApp HTTP API that you can run in a click!"
lead: "WAHA - <b>WhatsApp HTTP API</b> that you can run in a click!"
date: 2020-10-06T08:47:36+00:00
lastmod: 2020-10-06T08:47:36+00:00
draft: false
@@ -0,0 +1,115 @@
---
title: "Kotlin"
description: "WhatsApp HTTP API + Kotlin examples."
lead: "WhatsApp HTTP API + Kotlin examples."
date: 2020-10-06T08:49:31+00:00
lastmod: 2020-10-06T08:49:31+00:00
draft: false
images: ["/images/kotlin-whatsapp.png"]
menu:
docs:
parent: "help"
weight: 630
toc: true
---
![](/images/kotlin-whatsapp.png)
We've created a few examples on how to interact with WAHA WhatsApp HTTP API with Kotlin (ktor).
You can find the examples in
[examples folder on GitHub with detailed instructions in README.md ->](https://github.com/devlikeapro/waha-kotlin)
## Installation
We assume that you have installed software:
1. Kotlin + JVM
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
![](/images/versions/core.png) 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 ktor 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/waha-kotlin.git
# Build and run the app
./gradlew build
./gradlew 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!
@@ -1,18 +0,0 @@
---
title: "Python"
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
images: []
menu:
docs:
parent: "help"
weight: 630
toc: true
---
We've created a few examples on how to interact with WAHA WhatsApp HTTP API with Python.
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)
@@ -0,0 +1,148 @@
---
title: "Python"
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
images: ["/images/python-whatsapp.png"]
menu:
docs:
parent: "help"
weight: 630
toc: true
---
![](/images/python-whatsapp.png)
We've created a few examples on how to interact with WAHA WhatsApp HTTP API with Python.
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)
## 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
![](/images/versions/core.png) 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!
@@ -26,6 +26,7 @@ The following environment variables can be used to configure the WAHA.
### Common
- `DEBUG=1`: Set this variable to any value to enable debug and verbose logs.
- You can also do it for a **specific session** by setting `config.debug` field to `true` when starting a session.
- `WHATSAPP_API_PORT=3000`: The port number that the HTTP server will listen on. The default value is `3000`.
- `WHATSAPP_API_HOSTNAME=localhost`: The hostname for the HTTP server. The default value is `localhost`.
@@ -38,6 +39,8 @@ The following environment variables can be used to configure the WAHA.
response.
- `WHATSAPP_START_SESSION=session1,session2`: This variable can be used to start sessions with the specified names right
after launching the API. Separate session names with a comma.
- Also read more about [Sessions ->]({{< relref "/docs/how-to/sessions" >}})
- Also read more about [Session Storages on the dedicated page ->]({{< relref "/docs/how-to/storages#sessions" >}})
### Swagger
- `WHATSAPP_SWAGGER_CONFIG_ADVANCED=true` - enables advanced configuration options for Swagger documentation - you can customize host, port and base URL for the requests.
@@ -66,14 +69,18 @@ Read more about it on [**Session page** ->]({{< relref "/docs/how-to/sessions#co
Keep in mind that session's proxy configuration takes precedence over proxy configuration set by environment variables!
### Security ![](/images/versions/plus.png)
### Security
<b>Security is available in [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) only.</b>
- `WHATSAPP_API_KEY=mysecret`: If you set this variable, you must include the `X-Api-Key: mysecret` header in all
requests to the API. This will protect the API with a secret code.
- `WHATSAPP_SWAGGER_USERNAME=admin` and `WHATSAPP_SWAGGER_PASSWORD=admin`: These variables can be used to protect the
Swagger panel with `admin / admin` credentials. This does not affect API access.
Read more about security settings for Swagger and API on [**Security page** ->]({{< relref "/docs/how-to/security" >}}).
### Files ![](/images/versions/plus.png)
### Files
<b>Files configuration is available in [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) only.</b>
The following environment variables can be used to configure the file storage options for the WAHA:
@@ -85,8 +92,11 @@ The following environment variables can be used to configure the file storage op
- Under the hood, it sets `WHATSAPP_FILES_MIMETYPES=mimetype/ignore-all-media` to ignore all media files.
- `WHATSAPP_FILES_LIFETIME`: This variable can be used to set the time (in seconds) after which files will be removed to
free up space. The default value is `180`.
- Set this variable to `0` to disable the file lifetime.
- `WHATSAPP_FILES_FOLDER`: This variable can be used to set the folder where files from chats (images, voice messages)
will be stored. The default value is `/tmp/whatsapp-files`.
- The folder must be mounted to the host machine to keep the files between container restarts. [ Read more about how to persist files ->]({{< relref "/docs/how-to/storages#media" >}})
- Also read more about [Media Storages on the dedicated page ->]({{< relref "/docs/how-to/storages#media" >}})
💡 Even if WAHA doesn't process the message media because of `WHATSAPP_FILES_MIMETYPES` or `WHATSAPP_DOWNLOAD_MEDIA`
you'll get a webhook event with `hasMedia: True` field, but with no `media.url`.
@@ -105,6 +115,14 @@ you'll get a webhook event with `hasMedia: True` field, but with no `media.url`.
}
```
### Health Check
<b>Health check is available in [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) only.</b>
The following environment variables can be used to configure the [Health Check ->]({{< relref "/docs/how-to/other" >}}):
- `WHATSAPP_HEALTH_MEDIA_FILES_THRESHOLD_MB` - the threshold in MB for the media files storage. The default value is `100`.
- `WHATSAPP_HEALTH_SESSIONS_FILES_THRESHOLD_MB` - the threshold in MB for the sessions files storage. The default value is `100`.
- `WHATSAPP_HEALTH_MONGODB_TIMEOUT` - the timeout in milliseconds for the MongoDB health check. The default value is `5000`.
## Examples
#### Debug Mode
@@ -47,18 +47,14 @@ Not running Chromium saves you CPU and Memory, so you can run more instances on
️Working with the engine requires more attention, because it's easy to be blocked with it!
⚠ Read the article before using **NOWEB** engine
Quotes from the users:
> The server has **2 CPU and 8GB** of memory. Today we have **85 sessions** in this instance.
> **400** sessions with **4CPU and 32RAM**. It's working fine.
[How to avoid blocking ->]({{< relref " /docs/overview/how-to-avoid-blocking" >}}).
### MOBILE (experimental)
{{< alert icon="👉" text="<b>Experimental engine!</b><br/>Please contact us on <a href='https://www.patreon.com/wa_http_api' target='_blank'>Patreon -></a> or <a href='https://boosty.to/wa-http-api' target='_blank'>Boosty -></a> if you're interested in using it.<br>Available for <a href='https://waha.devlike.pro/docs/how-to/plus-version/#tiers' target='_blank'>PRO</a> subscribers only" />}}
- `WHATSAPP_DEFAULT_ENGINE=MOBILE`
- [https://github.com/WhiskeySockets/Baileys](https://github.com/WhiskeySockets/Baileys)
**MOBILE** engine **does not require a browser and a mobile device** to work with WhatsApp, it does so directly using a WebSocket.
You don't have to use a mobile device to run this engine.
### VENOM
- `WHATSAPP_DEFAULT_ENGINE=VENOM`
@@ -69,17 +65,30 @@ It uses Puppeteer to run a real instance of Whatsapp Web to avoid getting blocke
## Docker images
WAHA provides few docker images with different setup.
{{< alert icon="👉" text="Use `devlikeapro/whatsapp-http-api` instead of `devlikeapro/whatsapp-http-api-plus` to get **WAHA Plus** version." />}}
For **x86** processors use following images:
- `devlikeapro/whatsapp-http-api:latest` - latest version of WAHA, using **Chromium** (does not support video receiving and receiving in Plus version)
- `devlikeapro/whatsapp-http-api:chrome` - latest version of WAHA, using **Chrome** (supports video receiving and receiving in Plus version)
- `devlikeapro/whatsapp-http-api:noweb`- latest version of WAHA, **no browser installed** - use it only for **NOWEB** engine
| Image | CPU | Browser |
|---------------------------------------------------------|-----|-------------------------|
| [WAHA Plus]({{< relref "/docs/how-to/plus-version" >}}) | | |
| `devlikeapro/whatsapp-http-api-plus:latest` | x86 | Chromium |
| `devlikeapro/whatsapp-http-api-plus:chrome` | x86 | Chrome (supports video) |
| `devlikeapro/whatsapp-http-api-plus:noweb` | x86 | None |
| `devlikeapro/whatsapp-http-api-plus:arm` | ARM | Chromium |
| `devlikeapro/whatsapp-http-api-plus:noweb-arm` | ARM | None |
| **WAHA Core** | | |
| `devlikeapro/whatsapp-http-api:latest` | x86 | Chromium |
| `devlikeapro/whatsapp-http-api:chrome` | x86 | Chrome |
| `devlikeapro/whatsapp-http-api:noweb` | x86 | None |
| `devlikeapro/whatsapp-http-api:arm` | ARM | Chromium |
| `devlikeapro/whatsapp-http-api:noweb-arm` | ARM | None |
For **ARM** processors use following images:
- `devlikeapro/whatsapp-http-api:arm` - **Chromium**
- `devlikeapro/whatsapp-http-api:noweb-arm`- **no browser installed**
Browsers:
- **Chromium** - is the default browser for **WAHA**. It's a good choice for most cases.
- **Chrome** - is a good choice if you need to receive videos in Plus version.
- **None** - is a good choice if you want to use **NOWEB** engine.
CPU:
- **x86** - is a good choice for most cases.
- **ARM** - is a good choice if you're using ARM processors (like Apple Silicon, Apple M1, etc.)
{{< alert icon="💡" text="Chrome version is not available in ARM" />}}
## Features
@@ -96,7 +105,7 @@ Here, you will find a list of supported endpoints and webhooks per engine.
If you don't specify `WHATSAPP_DEFAULT_ENGINE` environment variable - look at **WEBJS** engine,
it's the engine WAHA runs by default.
### Engine
### Protocols
| | WEBJS | NOWEB | VENOM |
|--------------------------------------------------------------|:-----:|:-----:|:-----:|
@@ -125,25 +134,28 @@ please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues
| **Screenshot** | | | |
| `POST /api/screenshot` | ✔️ | ➖ | ✔️ |
| **Chatting** | WEBJS | NOWEB | VENOM |
|------------------------------------------------------|:-----:|:-----:|:-----:|
| `GET /api/checkNumberStatus` | ✔️ | ✔️ | ✔️ |
| `GET /api/sendContactVcard` | ➖ | ➖ | ✔️ |
| `GET /api/sendText` | ✔️ | ✔️ | ✔️ |
| `POST /api/sendText` | ✔️ | ✔️ | ✔️ |
| `POST /api/sendPoll` | ➖ | ✔️ | ➖ |
| `POST /api/sendLocation` | ✔️ | ✔️ | ✔️ |
| `POST /api/sendLinkPreview` | ➖ | ✔️ | ✔️ |
| `POST /api/sendImage` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `POST /api/sendFile` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `POST /api/sendVoice` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `POST /api/sendVideo` ![](/images/versions/plus.png) | ✔️ | ✔️ | ➖ |
| `POST /api/reply` | ✔️ | ✔️ | ✔️ |
| `POST /api/sendSeen` | ✔️ | ✔️ | ✔️ |
| `POST /api/startTyping` | ✔️ | ✔️ | ✔️ |
| `POST /api/stopTyping` | ✔️ | ✔️ | ➖ |
| `POST /api/reaction` | ✔️ | ✔️ | ➖ |
| `GET /api/messages` | ✔️ | ➖ | ✔️ |
| **Chatting** | WEBJS | NOWEB | VENOM |
|-------------------------------------------------------------|:-----:|:-----:|:-----:|
| `GET /api/checkNumberStatus` | ✔️ | ✔️ | ✔️ |
| `GET /api/sendContactVcard` | ➖ | ✔️ | ➖ |
| `GET /api/sendText` | ✔️ | ✔️ | ✔️ |
| `POST /api/sendText` | ✔️ | ✔️ | ✔️ |
| `POST /api/reply` | ✔️ | ✔️ | ✔️ |
| `PUT /api/{session}/chats/{chatId}/messages/{messageId}` | ✔️ | ✔️ | ➖ |
| `DELETE /api/{session}/chats/{chatId}/messages/{messageId}` | ✔️ | ✔️ | ➖ |
| `POST /api/sendPoll` | ➖ | ✔️ | ➖ |
| `POST /api/sendLocation` | ✔️ | ✔️ | ✔️ |
| `POST /api/sendLinkPreview` | ➖ | ✔️ | ✔️ |
| `POST /api/sendImage` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `POST /api/sendFile` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `POST /api/sendVoice` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `POST /api/sendVideo` ![](/images/versions/plus.png) | ✔️ | ✔️ | ➖ |
| `POST /api/sendSeen` | ✔️ | ✔️ | ✔️ |
| `POST /api/startTyping` | ✔️ | ✔️ | ✔️ |
| `POST /api/stopTyping` | ✔️ | ✔️ | ➖ |
| `POST /api/reaction` | ✔️ | ✔️ | ➖ |
| `POST /api/star` | ✔️ | ✔️ | ➖ |
| `GET /api/messages` | ✔️ | ➖ | ✔️ |
| | WEBJS | NOWEB | VENOM |
|-------------------------------------------------------------------|:-----:|:-----:|:-----:|
@@ -157,6 +169,8 @@ please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues
| `DELETE /api/{session}/chats/{chatId}` | ✔️ | ➖ | ➖ |
| `GET /api/{session}/chats/{chatId}/messages` | ✔️ | ➖ | ✔️ |
| `DELETE /api/{session}/chats/{chatId}/messages` | ✔️ | ➖ | ➖ |
| `PUT /api/{session}/chats/{chatId}/messages/{messageId}` | ✔️ | ✔️ | ➖ |
| `DELETE /api/{session}/chats/{chatId}/messages/{messageId}` | ✔️ | ✔️ | ➖ |
| **Contacts** | | | |
| `GET /api/contacts` | ✔️ | ➖ | ➖ |
| `GET /api/contacts/all` | ✔️ | ➖ | ➖ |
@@ -166,39 +180,45 @@ please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues
| `POST /api/contacts/block` | ✔️ | ➖ | ➖ |
| `POST /api/contacts/unblock` | ✔️ | ➖ | ➖ |
| **Groups** | WEBJS | NOWEB | VENOM |
|--------------------------------------------------------------------|:-----:|:-----:|:-----:|
| `POST /api/{session}/groups` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups/{id}` | ✔️ | ✔️ | ➖ |
| `DELETE /api/{session}/groups/{id}` | ✔️ | ➖ | ➖ |
| `GET /api/{session}/groups/{id}/settings/security/info-admin-only` | ✔️ | ➖ | ➖ |
| `PUT /api/{session}/groups/{id}/settings/security/info-admin-only` | ✔️ | ➖ | ➖ |
| `POST /api/{session}/groups/{id}/leave` | ✔️ | ✔️ | ➖ |
| `PUT /api/{session}/groups/{id}/description` | ✔️ | ✔️ | ➖ |
| `PUT /api/{session}/groups/{id}/subject` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups/{id}/invite-code` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/invite-code/revoke` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups/{id}/participants` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/participants/add` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/participants/remove` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/admin/promote` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/admin/demote` | ✔️ | ✔️ | ➖ |
| **Groups** | WEBJS | NOWEB | VENOM |
|------------------------------------------------------------------------|:-----:|:-----:|:-----:|
| `POST /api/{session}/groups` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups/{id}` | ✔️ | ✔️ | ➖ |
| `DELETE /api/{session}/groups/{id}` | ✔️ | ➖ | ➖ |
| `GET /api/{session}/groups/{id}/settings/security/info-admin-only` | ✔️ | ➖ | ➖ |
| `PUT /api/{session}/groups/{id}/settings/security/info-admin-only` | ✔️ | ➖ | ➖ |
| `GET /api/{session}/groups/{id}/settings/security/messages-admin-only` | ✔️ | ➖ | ➖ |
| `PUT /api/{session}/groups/{id}/settings/security/messages-admin-only` | ✔️ | ➖ | ➖ |
| `POST /api/{session}/groups/{id}/leave` | ✔️ | ✔️ | ➖ |
| `PUT /api/{session}/groups/{id}/description` | ✔️ | ✔️ | ➖ |
| `PUT /api/{session}/groups/{id}/subject` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups/{id}/invite-code` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/invite-code/revoke` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/groups/{id}/participants` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/participants/add` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/participants/remove` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/admin/promote` | ✔️ | ✔️ | ➖ |
| `POST /api/{session}/groups/{id}/admin/demote` | ✔️ | ✔️ | ➖ |
| | WEBJS | NOWEB | VENOM |
|---------------------------------------------------|:-----:|:-----:|:-----:|
| **Presence** | | | |
| `POST /api/{session}/presence` | ➖ | ✔️ | ➖ |
| `POST /api/{session}/presence` | ✔️ | ✔️ | ➖ |
| `GET /api/{session}/presence` | ➖ | ✔️ | ➖ |
| `GET /api/{session}/presence/{chatId}` | ➖ | ✔️ | ➖ |
| `POST /api/{session}/presence/{chatId}/subscribe` | ➖ | ✔️ | ➖ |
| **Other** | | | |
| `POST /api/version` | ➖ | ✔️ | ➖ |
| `GET /api/version` | ➖ | ✔️ | ➖ |
| `GET /health` ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
### Webhooks
| **Webhooks** | WEBJS | NOWEB | VENOM |
|-----------------------------------------------------|:-----:|:-----:|:-----:|
| `message` | ✔️ | ✔️ | ✔️ |
| `message` with files ![](/images/versions/plus.png) | ✔️ | ✔️ | ✔️ |
| `message.reaction` | ✔️ | ✔️ | ➖ |
| `message.any` | ✔️ | ✔️ | ✔️ |
| `message.ack` | ✔️ | ✔️ | ✔️ |
| `message.revoked` | ✔️ | ➖ | ➖ |
@@ -87,22 +87,47 @@ Request:
}
```
### Settings
#### Security for group info
### Security - update group info
Updates the group settings to only allow admins to edit group info (title, description, photo).
`PUT /api/{session}/groups/{groupId}/settings/security/info-admin-only`
```json
{
"adminsOnly": true
}
```
Get the group settings to only allow admins to edit group info (title, description, photo).
`GET /api/{session}/groups/{groupId}/settings/security/info-admin-only`
```json
{
"adminsOnly": true
}
```
`PUT /api/{session}/groups/{groupId}/settings/security/info-admin-only`
The request doesn't require any request's body and
**can't be used to disable the security setting (allow ANY to edit group info)**.
If you wish to have that ability - please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues)
Returns `true` if the setting was properly updated. This can return false if the user does not have the necessary permissions.
### Security - who can send messages
Updates the group settings to only allow admins to send messages.
`PUT /api/{session}/groups/{groupId}/settings/security/messages-admin-only`
```json
{
"adminsOnly": true
}
```
Returns `true` if the setting was properly updated. This can return false if the user does not have the necessary permissions.
Get the group settings to only allow admins to send messages.
`GET /api/{session}/groups/{groupId}/settings/security/messages-admin-only`
```json
{
"adminsOnly": true
}
```
### Participants
#### Get participants
@@ -0,0 +1,192 @@
---
title: "Other"
description: "Other features and API"
lead: ""
date: 2020-10-06T08:48:45+00:00
lastmod: 2020-10-06T08:48:45+00:00
draft: false
images: [ ]
weight: 802
---
This page provides useful information about other features and API that are not covered in the other sections.
## Health Check
<b>Health check is available in [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) only.</b>
The health check endpoint is used to determine the health of the service.
```
GET /health
```
It returns a **200 OK** status code if the service is healthy.
The response format:
```json
{
"status": "ok",
"info": {
"metric1": {
"field": "value"
},
"metric2": {
"field": "value"
}
},
"error": {},
"details": {}
}
```
Where:
- `status`: `'error' | 'ok' | 'shutting_down'` - If any health indicator failed the status will be `'error'`. If the app is shutting down but still accepting HTTP requests, the health check will have the `'shutting_down'` status.
- `info`: Object containing information of each health indicator which is of status `'up'`, or in other words "healthy".
- `error`: Object containing information of each health indicator which is of status `'down'`, or in other words "unhealthy".
- `details`: Object containing detailed information of each health indicator.
### Health Check Indicators
Few things we check in the health check:
- Media files storage space - `mediaFiles.space`
- Sessions files storage space - `sessionsFiles.space`
- MongoDB connection - `mongodb`
### Configuration
The following environment variables can be used to configure the health check:
- `WHATSAPP_HEALTH_MEDIA_FILES_THRESHOLD_MB` - the threshold in MB for the media files storage. The default value is `100`.
- `WHATSAPP_HEALTH_SESSIONS_FILES_THRESHOLD_MB` - the threshold in MB for the sessions files storage. The default value is `100`.
- `WHATSAPP_HEALTH_MONGODB_TIMEOUT` - the timeout in milliseconds for the MongoDB health check. The default value is `5000`.
### Examples
**Healthy response** when you use [Local Storage]({{< relref "/docs/how-to/storages#sessions" >}}) for session authentication:
**200 OK**
```json
{
"status": "ok",
"info": {
"mediaFiles.space": {
"status": "up",
"path": "/tmp/whatsapp-files",
"diskPath": "/",
"free": 132979355648,
"threshold": 104857600
},
"sessionsFiles.space": {
"status": "up",
"path": "/app/.sessions",
"diskPath": "/",
"free": 132979355648,
"threshold": 104857600
}
},
"error": {},
"details": {
"mediaFiles.space": {
"status": "up",
"path": "/tmp/whatsapp-files",
"diskPath": "/",
"free": 132979355648,
"threshold": 104857600
},
"sessionsFiles.space": {
"status": "up",
"path": "/app/.sessions",
"diskPath": "/",
"free": 132979355648,
"threshold": 104857600
}
}
}
```
**Healthy response** when you use [MongoDB Storage]({{< relref "/docs/how-to/storages#sessions" >}}) for session authentication:
**200 OK**
```json
{
"status": "ok",
"info": {
"mediaFiles.space": {
"status": "up",
"path": "/tmp/whatsapp-files",
"diskPath": "/",
"free": 132977496064,
"threshold": 104857600
},
"mongodb": {
"status": "up",
"message": "Up and running"
}
},
"error": {},
"details": {
"mediaFiles.space": {
"status": "up",
"path": "/tmp/whatsapp-files",
"diskPath": "/",
"free": 132977496064,
"threshold": 104857600
},
"mongodb": {
"status": "up",
"message": "Up and running"
}
}
}
```
**Unhealthy response example**
**503 Service Unavailable**
```json
{
"status": "error",
"info": {
"mediaFiles.space": {
"status": "up",
"path": "/tmp/whatsapp-files",
"diskPath": "/",
"free": 132976623616,
"threshold": 104857600
}
},
"error": {
"mongodb": {
"status": "down",
"error": "Timeout"
}
},
"details": {
"mediaFiles.space": {
"status": "up",
"path": "/tmp/whatsapp-files",
"diskPath": "/",
"free": 132976623616,
"threshold": 104857600
},
"mongodb": {
"status": "down",
"error": "Timeout"
}
}
}
```
## Get version
Returns the version of the installed docker image.
```
GET /api/version
```
```json
{
"version": "2024.2.3",
"engine": "NOWEB",
"tier": "PLUS",
"browser": "/usr/bin/google-chrome-stable"
}
```
@@ -49,17 +49,34 @@ Treat **WAHA Plus** it as an additional episode, behind-the-scenes videos, our n
We donate most of the donations down to the stream to the underlying libraries and tools - JS-community, Node, other libraries authors, personal contributors.
By donating to the **WAHA** you donate to the Open Source world!
### Features
### Tiers
The Plus additional features are:
The more you support the project - the more you get!
| | Core | Plus | Advanced | Pro |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------|---------|:--------:|:-------:|
| Messages limit | **∞** | **∞** | **∞** | **∞** |
| [Send and receive text messages ->]({{< relref "/docs/how-to/send-messages" >}}) | ✔️ | ✔️ | ✔️ | ✔️ |
| No license checks on installed instances | ✔️ | ✔️ | ✔️ | ✔️ |
| [Sessions limit ->]({{< relref "/docs/how-to/sessions" >}}) | **1** | **∞** | **∞** | **∞** |
| [Send and receive files ->]({{< relref "/docs/how-to/send-messages" >}}) | ➖️ | ✔️ | ✔️ | ✔️ |
| [Built-in Security ->]({{< relref "/docs/how-to/security" >}}) | ➖️ | ✔️ | ✔️ | ✔️ |
| [**Webhook retries** →]({{< relref "/docs/how-to/receive-messages#retries" >}}) | ➖️ | ✔️ | ✔️ | ✔️ |
| [MongoDB support ->]({{< relref "/docs/how-to/storages" >}}) | ➖️ | ✔️ | ✔️ | ✔️ |
| Access to **WAHA Plus** ![](/images/versions/plus.png) docker image with a personal key. | ➖️ | ✔️ | ✔️ | ✔️ |
| Special badge ([![patron:TIER](https://img.shields.io/badge/patron-TIER-188a42)](https://waha.devlike.pro/docs/how-to/plus-version/#tiers)) and labels (`patron:TIER`) on [project's issues, PRs and Discussion](https://github.com/devlikeapro/whatsapp-http-api) to highlight your support to the project! | | ✔️ | ✔️ | ✔️ |
| An invitation to **a private Github repository** so you have the **WAHA Plus** source code. | ➖️ | ➖️ | ✔️ | ✔️️ |
| Access to a Telegram group where we provide support and assistance. | ➖️ | ➖️ | ➖ | ✔️ |
| **Donation** | **FREE** | **$19** | **$39** | **$99** |
For **$19** donation you get:
1. **Secret password** for Docker Hub to download Plus image: `devlikeapro/whatsapp-http-api-plus`.
2. **All updates for one month** for the Plus image.
3. **NO LICENSE CHECKS** on installed instances!
4. **NO LICENSE EXPIRATION** on installed instances! We repeated it twice to make sure that you've got it 😊
{{< alert icon="💡" text="If you stay subscribed - the donation amount will never change for you and always be $19 to get monthly updates!" />}}
1. [**Send images\files\voices** →]({{< relref "/docs/how-to/send-messages" >}})
2. [**Receive images\files\voices** →]({{< relref "/docs/how-to/receive-messages" >}})
3. [**Webhook retries** →]({{< relref "/docs/how-to/receive-messages#retries" >}})
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. **Priority in support** - on both bugs and features
### Why WAHA?
@@ -74,49 +91,15 @@ What is the difference between **WAHA Plus** and **other SaaS solutions** for Wh
| **Message price** | ✔️ All messages are Free! | ➖ Some solutions have a price for messages - $1/100 messages. |
| **Expenses** | ✔️ $19 **donation**! | ➖ Monthly payment starts from $50 |
{{< alert icon="💡" text="If you stay subscribed - the donation amount will never change for you and always be $19 to get monthly updates!" />}}
## Get Plus ![](/images/versions/plus.png)
The more you support the project - the more you get!
### Tiers
Here's available tiers for our supporters:
| | Plus | Advanced | Pro |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|:--------:|:-------:|
| Access to WAHA Plus ![](/images/versions/plus.png) docker image with a personal key. | ✔️ | ✔️ | ✔️ |
| Special badge ([![patron:TIER](https://img.shields.io/badge/patron-TIER-188a42)](https://waha.devlike.pro/docs/how-to/plus-version/#tiers)) and labels (`patron:TIER`) on [project's issues, PRs and Discussion](https://github.com/devlikeapro/whatsapp-http-api) to highlight your support to the project! | ✔️ | ✔️ | ✔️ |
| An invitation to a private Github repository so you have the WAHA Plus source code. | ➖️ | ✔️ | ✔️️ |
| Access to a Telegram group where we provide support and assistance. | ➖️ | ➖ | ✔️ |
| Access to an [experimental MOBILE engine]({{< relref "/docs/how-to/plus-version#tiers" >}})!. | ➖️ | ➖ | ✔️ |
| **Donation** | **$19** | **$39** | **$99** |
For **$19** donation you get:
1. **Secret password** for Docker Hub to download Plus image: `devlikeapro/whatsapp-http-api-plus`.
2. **All updates for one month** for the Plus image.
3. **NO LICENSE CHECKS** on installed instances!
4. **NO LICENSE EXPIRATION** on installed instances! We repeated it twice to make sure that you've got it 😊
{{< alert icon="💡" text="If you stay subscribed - the donation amount will never change for you and always be $19 to get monthly updates!" />}}
### Patreon
<p align="center">
<img src="patreon.png" alt="Patreon" />
</p>
Support the project and get WAHA Plus version on
<a href="https://patreon.com/wa_http_api" target="_blank">
<b>
Patreon ->
</b>
</a>
Go ahead and get your own WhatsApp HTTP API!
### Boosty
<p align="center">
<img src="boosty.svg" alt="Patreon" />
<img src="boosty.svg" alt="Patreon" style='width: 40%'/>
<br/>
<br/>
</p>
@@ -130,6 +113,42 @@ Boosty ->
Go ahead and get your own WhatsApp HTTP API!
### Patreon
<p align="center">
<img src="patreon.png" alt="Patreon" style='width: 40%'/>
</p>
{{< alert icon="🙏" text="Please consider using Boosty instead of Patreon due to its lower fees 🙏" />}}
Support the project and get WAHA Plus version on
<a href="https://patreon.com/wa_http_api" target="_blank">
<b>
Patreon ->
</b>
</a>
Go ahead and get your own WhatsApp HTTP API!
### Patron Portal
<p align="center">
<img src="patron-portal.png" alt="Patron Portal" />
<br/>
<br/>
</p>
After subscribing on Patreon or Boosty, you will get access to the [WAHA Patron Portal ->](https://portal.devlike.pro/)
where you will get the password to download the Plus image and manage your perks!
Read more about Patron Portal in
**<a href="https://www.patreon.com/posts/waha-patron-97637416" target="_blank">Patreon -> </a>**
or
**<a href="https://boosty.to/wa-http-api/posts/1011" target="_blank">Boosty -></a>**
posts.
## Install Plus
After you get the password, get your login to Docker Hub in [Patron Portal ->](https://portal.devlike.pro/)
and run the commands:
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@@ -27,7 +27,8 @@ See the list of engines [**that support the feature ->**]({{< relref "/docs/how-
You can set your global or chat-related presence with `POST /api/{session}/presence` endpoint
Start typing to a chat (you can use `POST /startTyping` instead)
```json
```
POST /api/{session}/presence
{
"chatId": "111111111@c.us",
"presence": "typing"
@@ -35,7 +36,8 @@ Start typing to a chat (you can use `POST /startTyping` instead)
```
Clear "typing" state (you can use `POST /stopTyping` instead)
```json
```
POST /api/{session}/presence
{
"chatId": "111111111@c.us",
"presence": "paused"
@@ -43,7 +45,8 @@ Clear "typing" state (you can use `POST /stopTyping` instead)
```
Set global "online", all contacts will see it
```json
```
POST /api/{session}/presence
{
"presence": "online"
}
@@ -53,7 +56,8 @@ Set global "online", all contacts will see it
to the device.
If you would like to receive said notifications - you need to mark a session's presence as `offline`.
```json
```
POST /api/{session}/presence
{
"presence": "offline"
}
@@ -57,6 +57,7 @@ To send text message - use `POST /api/sendText` with example payload.
"text": "Hi there!"
}
```
#### Mention contact
If you send a message in a group and want to mention a participant in the message -
use `mentions` field for that in `POST /api/sendText` request.
@@ -75,11 +76,6 @@ also mention it in `mentions` in format `2132132130@c.us`
}
```
### Send poll
We have a dedicated page [how to send polls and receive votes]({{< relref "/docs/how-to/polls" >}})!
![](poll-example.jpg)
### Reply on message
To reply on a message - use `POST /api/reply` with example payload.
```json
@@ -94,6 +90,46 @@ To reply on a message - use `POST /api/reply` with example payload.
#### Reply files ![](/images/versions/plus-soon.png)
WAHA does not support reply with files (images, voice, etc.). If you're interested in it - please create an issue in GitHub.
### Edit message
You can edit **text** messages or **"caption"** in media messages.
```
PUT /api/{session}/chats/{chatId}/messages/{messageId}
```
👉 Remember to escape `@` in `chatId` and `messageId` with `%40`.
So if you want to edit `true_123@c.us_AAA` message in `123@c.us` chat you need to send request to:
```
PUT /api/{session}/chats/123%40c.us/messages/true_123%40c.us_AAA
```
**Payload:**
```json
{
"text": "Hello, world!"
}
```
### Delete message
You can delete messages from the chat.
```
DELETE /api/{session}/chats/{chatId}/messages/{messageId}
```
👉 Remember to escape `@` in `chatId` and `messageId` with `%40`.
So if you want to delete `true_123@c.us_AAA` message in `123@c.us` chat you need to send request to:
```
DELETE /api/{session}/chats/123%40c.us/messages/true_123%40c.us_AAA
```
### Send poll
We have a dedicated page [how to send polls and receive votes]({{< relref "/docs/how-to/polls" >}})!
![](poll-example.jpg)
### Add a reaction
Use `PUT /api/reaction` method to set reaction to a message.
@@ -116,6 +152,85 @@ To remove reaction from a message - send empty string in the reaction request.
}
```
### Star and unstar message
Use `PUT /api/star` method to star or unstar a message.
{{< alert icon="👉" text="Star API uses PUT, not POST request! Please make sure you send right request." />}}
**Star:**
```json
{
"messageId": "false_71111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"chatId": "71111111111@c.us",
"star": true,
"session": "default"
}
```
**Unstar:**
```json
{
"messageId": "false_71111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"chatId": "71111111111@c.us",
"star": false,
"session": "default"
}
```
### Send contact (vcard)
Use `POST /api/sendContactVcard` to send contact (vcard).
You can use fields this way:
```json
{
"session": "default",
"chatId": "79111111@c.us",
"contacts": [
{
"fullName": "John Doe",
"organization": "Company Name",
"phoneNumber": "+91 11111 11111",
"whatsappId": "911111111111"
}
]
}
```
Or provide a vCard file content:
```json
{
"session": "default",
"chatId": "79111111@c.us",
"contacts": [
{
"vcard": "BEGIN:VCARD\nVERSION:3.0\nFN:Jane Doe\nORG:Company Name;\nTEL;type=CELL;type=VOICE;waid=911111111111:+91 11111 11111\nEND:VCARD"
}
]
}
```
Or even combine it:
```json
{
"chatId": "79111111@c.us",
"contacts": [
{
"vcard": "BEGIN:VCARD\nVERSION:3.0\nFN:Jane Doe\nORG:Company Name;\nTEL;type=CELL;type=VOICE;waid=911111111111:+91 11111 11111\nEND:VCARD"
},
{
"fullName": "John Doe",
"organization": "Company Name",
"phoneNumber": "+91 11111 11111",
"whatsappId": "911111111111"
}
],
"session": "default"
}
```
### Send image ![](/images/versions/plus.png)
Use `POST /api/sendImage` to send images.
@@ -84,6 +84,20 @@ Read more about it in [Autostart section](#autostart).
You can configure proxy when for all sessions by set up environment variables.
Read more about it on [**Configuration page** ->]({{< relref "/docs/how-to/config#proxy" >}}).
#### Enable debug
You can enable debug mode for a session by setting `config.debug` field to `true`.
It'll show you more logs in the console.
Can be useful for debugging purposes when you're experiencing some issues.
```json
{
"name": "default",
"config": {
"debug": true
}
}
```
### List
To get session list - call `GET /api/sessions`.
@@ -298,28 +312,8 @@ configure autostart options so when the docker container restarts - it restores
### Session persistent
#### File storage
If you want to save your session and do not scan QR code everytime when you launch WAHA - connect a local file storage
to the container. WAHA stores authentication information in the directory and reuses it after restart.
[Attach volume](https://docs.docker.com/storage/volumes/) part to the command:
```bash
-v `pwd`/.sessions:/app/.sessions
```
The full command would be:
```bash
docker run -d -v `pwd`/.sessions:/app/.sessions -p 3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api-plus
```
#### Remote storage ![](/images/versions/soon.png)
If you're interested in using some "remote" storage (like Redis or other Databases) to save sessions - please create an
issue on GitHub.
For instances, it may be useful if you run WAHA in a cluster of servers and do not have shared file storage
If you want to save your session and do not scan QR code everytime when you launch WAHA -
[connect the session storage to the container ->]({{< relref "/docs/how-to/storages#sessions" >}})
### Autostart
If you don't want to call `POST /api/sessions/start` for every session each time when the container restart -
@@ -0,0 +1,149 @@
---
title: "Storages"
description: "Storages"
lead: ""
date: 2020-10-06T08:48:45+00:00
lastmod: 2020-10-06T08:48:45+00:00
draft: false
images: [ ]
weight: 700
---
There are several storages that are used by the WAHA, below you can find the list of them and the way to configure them.
1. [Sessions](#sessions) - used to store the session data, such as authentication information, configuration, and other data that is required to keep the session alive and connected to WhatsApp.
2. [Media](#media) - used to store the media files, such as images, videos, and other files that are received from the WhatsApp instance.
<b>Storages are available in [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) only.</b>
## Sessions
The "Sessions" storage is used to store the session data, such as authentication information, configuration,
and other data that is required to keep the session alive and connected to WhatsApp.
If you want to save your session and do not scan QR code everytime when you launch WAHA -
connect the session storage to the container.
For the session storage, you can use the following options:
1. [Local (files)](#local-files) - the default option, stores the session data in the local storage using files.
2. [MongoDB](#mongodb) - stores the session data in the MongoDB database.
The following table shows the compatibility of the session storage with the engines:
| | WEBJS | NOWEB | VENOM |
|---------------|:-----:|:-----:|:-----:|
| Local (files) | ✔️ | ✔️ | ✔️ |
| MongoDB | ✔️ | ✔️ | ➖ |
## Sessions - Local (files)
By default, the WAHA uses the **local storage (files)** to store the session data.
{{< alert icon="💡" text="It's <b>well tested solution</b> even for production" />}}
In order to use the local storage and save the session data between the container restarts,
you need to mount the volume to the `/app/.sessions` directory using the `-v` option in `docker run` command:
```bash
-v /path/to/on/host/.sessions:/app/.sessions
```
The full command to run the WAHA with the local storage and save the session data
in the current directory and `.sessions` directory:
```bash
docker run -v `pwd`/.sessions:/app/.sessions -p 3000:3000/tcp devlikeapro/whatsapp-http-api-plus
```
This is the only action you need to do to use the local storage - all session data will be available between the container restarts.
### How it works
In the host machine, the session data will be stored in the current directory in the `.sessions` directory.
Under the hood, the WAHA stores the session data in the following directory structure:
```
.sessions/{engine}/{sessionName}/...
```
when you "logout" the session using [POST /api/sessions/logout]({{< relref "/docs/how-to/sessions#logout">}}) or providing `logout: True` in [POST /api/sessions/stop]({{< relref "/docs/how-to/sessions#stop">}})
it removes the directory with the session data.
### Health Check
The [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) provides [the health check endpoint]({{< relref "/docs/how-to/other" >}}) that checks the local storage.
## Sessions - MongoDB
If you want to use the MongoDB to store the session data, you need to:
1. Start the MongoDB server (using docker or any other way). You can either start your own MongoDB server or use the one of cloud providers, like [MongoDB Atlas](https://www.mongodb.com/atlas/database).
2. Set `WHATSAPP_SESSIONS_MONGO_URL=mongodb://user:password@host:port/` environment variable to connect to the MongoDB server.
**We recommend using your own MongoDB server as close as possible to WAHA server** for the best performance and security reasons.
{{< alert icon="💡" text="For <b>WEBJS</b> engine it can take up to 1 minute to save credentials in Mongo Database" />}}
### Example
First, you need to start MongoDB server:
```bash
docker run -d -p 27017:27017 --name mongodb mongo
```
Then, you need to run the WAHA with the `WHATSAPP_SESSIONS_MONGO_URL` environment variable (please note using `--network host` option as well)
```bash
docker run -e WHATSAPP_SESSIONS_MONGO_URL=mongodb://localhost:27017/ --network host devlikeapro/whatsapp-http-api-plus
```
This is the only action you need to do to use the MongoDB storage -
all session authentication data will be stored in the MongoDB database.
### How it works
When you start a session, it stores the session data in the MongoDB database in two databases:
1. `waha_{engine}` - it saves the session configuration in `sessions` collection with `name: {sessionName}` field.
2. `waha_{engine}_{sessionName}` - it saves the WhatsApp authentication data and other session data. Each engine saves different data in this database.
When you "logout" the session using [POST /api/sessions/logout]({{< relref "/docs/how-to/sessions#logout">}}) or providing `logout: True` in [POST /api/sessions/stop]({{< relref "/docs/how-to/sessions#stop">}}) it removes those databases or elements from them.
For dealing and troubleshooting with the MongoDB, we recommend using [MongoDB Compass](https://www.mongodb.com/products/tools/compass).
![](waha-mongodb.png)
### MongoDB Atlas
If you use the [MongoDB Atlas](https://www.mongodb.com/atlas/database) you must grant
`Atlas Admin` role in **Security => Database Access** before you connect to the database.
For production please consider running the MongoDB server close to the WAHA server for the best performance and security reasons.
### Health Check
The [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) provides [the health check endpoint]({{< relref "/docs/how-to/other" >}}) that checks the MongoDB connection.
## Media
When your WhatsApp instance receives media files, it stores them in the media storage.
You can use the following options to store the media files:
1. [Local](#local) - the default option, stores the media files in the local storage using files.
2. [S3](#s3) - stores the media files in the S3 storage.
Read more about [available configuration options ->]({{<relref "/docs/how-to/config#files">}}).
The following table shows the compatibility of the storage with the engines:
| | WEBJS | NOWEB | VENOM |
|-------|:-----:|:-----:|:-----:|
| Local | ✔️ | ✔️ | ✔️ |
| S3 | 🕒 | 🕒 | ➖ |
## Media - Local
By default, the WAHA uses the **local file storage** to store the media files and those files has a short lifetime (180 seconds).
So it's your app responsibility to download and store them in a safe persistent place during this time.
### Save media files between the container restarts
If you want to use the local storage and **save the media files between the container restarts for a long time** - you need to:
1. Specify a dedicated folder to store the media files using `WHATSAPP_FILES_FOLDER=/app/.media` environment variable
2. Disable automatic media files cleanup using `WHATSAPP_FILES_LIFETIME=0` environment variable
3. Connect the volume to the specified folder using the `-v /path/to/files/on/host:/app/.media` option in `docker run` command to the folder specified in `WHATSAPP_FILES_FOLDER` environment variable.
Read more about [available configuration options ->]({{<relref "/docs/how-to/config#files">}}).
Here's all the steps in one command:
```bash
docker run -v /path/to/on/host/.media:/app/.media -e WHATSAPP_FILES_FOLDER=/app/.media -e WHATSAPP_FILES_LIFETIME=0 -p 3000:3000/tcp devlikeapro/whatsapp-http-api-plus
```
### Health Check
The [WAHA Plus ![](/images/versions/plus.png)]({{< relref "/docs/how-to/plus-version" >}}) provides [the health check endpoint]({{< relref "/docs/how-to/other" >}}) that checks the local storage.
## Media - S3
If you're interested in using the S3 storage or any other cloud storage (like [self-hosted S3 - Minio](https://min.io/)),
please create an issue or vote for the S3 issue in [the GitHub repository](https://github.com/devlikeapro/whatsapp-http-api/issues?q=is%3Aissue+is%3Aopen+S3).
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

@@ -226,6 +226,43 @@ Fired on all message creations, including your own. The payload is the same as f
}
```
### message.reaction
Receive events when a message is reacted to by a user (or **yourself** reacting to a message).
- `payload.reaction.text` - emoji that was used to react to the message. It'll be an empty string if the reaction was removed.
- `payload.reaction.messageId` - id of the message that was reacted to.
```json
{
"event": "message.reaction",
"session": "default",
"me": {
"id": "79222222222@c.us",
"pushName": "WAHA"
},
"payload": {
"id": "false_79111111@c.us_11111111111111111111111111111111",
"from": "79111111@c.us",
"fromMe": false,
"participant": "79111111@c.us",
"to": "79111111@c.us",
"timestamp": 1710481111.853,
"reaction": {
"text": "🙏",
"messageId": "true_79111111@c.us_11111111111111111111111111111111"
}
},
"engine": "WEBJS",
"environment": {
"version": "2024.3.3",
"engine": "WEBJS",
"tier": "PLUS",
"browser": "/usr/bin/google-chrome-stable"
}
}
```
{{< alert icon="👉" text="NOWEB engine note - reactions were sent in 'message' and 'message.any' events, not it's available only in 'message.reaction'!" />}}
### message.ack
Receive events when server or recipient gets the message, read or played it.
@@ -263,7 +300,7 @@ revokes a previously sent message.
```json
{
"event": "message.ack",
"event": "message.revoked",
"session": "default",
"payload": {
"before": {
@@ -19,6 +19,80 @@ If you wish to get and read Release Notes - read them on one of our platforms, *
You even can **subscribe to get new updates** there!
## 2024.3
### 🎉 New
----
Add `config.debug` field to `POST /api/session/start` to enable debug and verbose logs for the session.
----
Add `PUT /api/{session}/chats/{chatId}/messages/{messageId}` endpoint to **edit** the message.
- Read more about [Edit message]({{< relref "/docs/how-to/send-messages#edit-message" >}})
- Issue: [#241](https://github.com/devlikeapro/whatsapp-http-api/issues/241)
----
Add `DELETE /api/{session}/chats/{chatId}/messages/{messageId}` endpoint to **delete** the message.
- Read more about [Delete message]({{< relref "/docs/how-to/send-messages#delete-message" >}})
- Issue: [#209](https://github.com/devlikeapro/whatsapp-http-api/issues/209)
----
Add `POST /api/sendContactVcard` support for **NOWEB** engine.
- Read more about [Send Contact vCard]({{< relref "/docs/how-to/send-messages#send-contact-vcard" >}})
- Issue: [#276](https://github.com/devlikeapro/whatsapp-http-api/issues/256)
----
Handles `caption` and `filename` right in `POST /api/sendFile`
- Issue: [#94](https://github.com/devlikeapro/whatsapp-http-api/issues/94)
- Issue: [#133](https://github.com/devlikeapro/whatsapp-http-api/issues/133)
----
Add `message.reaction` webhook in **WEBJS** and **NOWEB** engines
- Read more about [message.reaction]({{< relref "/docs/how-to/webhooks#messagereaction" >}}) event
- Issue: [#275](https://github.com/devlikeapro/whatsapp-http-api/issues/275)
👉 **NOWEB** engine note - reactions were sent in `'message'` and `'message.any'` events, not it's available **only** in `'message.reaction'`!
----
Add security settings for who can send messages `PUT /api/{session}/groups/{groupId}/settings/security/messages-admin-only`
- Read [Security - who can send messages]({{< relref "/docs/how-to/groups##security---who-can-send-messages" >}})
- Issue: [#274](https://github.com/devlikeapro/whatsapp-http-api/issues/274)
----
Add star and unstar message endpoint `PUT /api/star`
- Read [Star and unstar message]({{< relref "/docs/how-to/send-messages#star-and-unstar-message" >}}) documentation
- Issue: [#273](https://github.com/devlikeapro/whatsapp-http-api/issues/240)
----
### 👉 Breaking changes
- **NOWEB** - reactions were sent in `'message'` and `'message.any'` events, not it's available **only** in `'message.reaction'`!
- Add **required body** in `PUT /api/{session}/groups/{groupId}/settings/security/info-admin-only` endpoint.
```json
{
"adminsOnly": false
}
```
- `POST /api/sendContactVcard` doesn't work in **VENOM** anymore (use **NOWEB** engine).
## 2024.2
- Add support for [MongoDB as storage for Session data]({{< relref "/docs/how-to/storages" >}})
- Support persistent file storage for media files - [now you can save media files between container restarts]({{< relref "/docs/how-to/storages#media" >}})
- If you set `WHATSAPP_FILES_LIFETIME=0` environment variable - media files will be never deleted.
- Add `GET /api/health` endpoint to [check the health of the service](https://waha.devlike.pro/docs/how-to/other/)
## 2024.1
- Implement [Patron Portal](https://portal.devlike.pro/) where you can get your personal API key and manage your perks.
- Read more on [Patreon ->](https://www.patreon.com/posts/waha-patron-97637416)
- Read more on [Boosty ->](https://boosty.to/wa-http-api/posts/8319079f-dac1-4179-b954-fcc559097c76)
## 2024.2
- Listen for browser disconnected and page close events in **WEBJS** engine [#262](https://github.com/devlikeapro/whatsapp-http-api/issues/262)
+5 -3
View File
@@ -30,7 +30,9 @@ communicate with it to prevent blocking from WhatsApp. It's the reason why it's
### NOWEB
If you're looking for less resource demanded engine - [have a look at **NOWEB** engine ->]({{< relref "/docs/how-to/engines" >}})
The quote from the user:
> The server has 2 CPU and 8GB of memory.
> Today we have 85 session in this instance.
Quotes from the users:
> The server has **2 CPU and 8GB** of memory. Today we have **85 sessions** in this instance.
> **400** sessions with **4CPU and 32RAM**. It's working fine.
@@ -9,7 +9,7 @@ images: []
menu:
docs:
parent: "overview"
weight: 110
weight: 101
toc: true
---
@@ -36,18 +36,21 @@ Also, Docker makes installation and update processes so simple, just one command
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, we've got you covered!
A 3-minute video tutorial is available to help you navigate WhatsApp HTTP API smoothly.
<div class='article-card'>
<b class='h4'>Are you a visual learner?</b>
<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>
We've got you covered! Watch the video below to see how to send your first message using <b>WAHA</b>.
<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>
### 1. Download image
@@ -107,6 +110,9 @@ Open the link in your browser [http://localhost:3000/](http://localhost:3000/) a
(Swagger).
{{< alert icon="👉" text="We don't recommend expose the API in the internet without authentication!" />}}
[Read more about Security ->]({{< relref "/docs/how-to/security" >}})
<br/>
![](swagger.png)
+78 -51
View File
@@ -1,79 +1,106 @@
{{ define "main" }}
<section class="section container-fluid mt-n3 pb-3">
<div class="row justify-content-center">
<div class="col-lg-12 text-center">
<h1 class="mt-0">{{ .Title }}</h1>
<section class='section container-fluid mt-n3 pb-3'>
<div class='row justify-content-center'>
<div class='col-lg-12 text-center'>
<h1 class='mt-0'>{{ .Title }}</h1>
</div>
<div class="col-lg-9 col-xl-9 text-center">
<div class="mb-4">
<img src="/images/logo.png" title="WhatsApp HTTP API" alt="logo" width="100"/>
<div class='col-lg-9 col-xl-9 text-center'>
<div class='mb-4'>
<img src='/images/logo.png' title='WhatsApp HTTP API' alt='logo' width='100' />
</div>
<p class="lead">{{ .Params.lead | safeHTML }}</p>
<code>
docker run -it -p 3000:3000/tcp devlikeapro/whatsapp-http-api
</code>
<br/>
<br/>
<a class="btn btn-primary btn-lg px-4 mb-2"
href="/docs/{{ if .Site.Params.options.docsVersioning }}{{ .Site.Params.docsVersion }}/{{ end }}overview/introduction/"
role="button">Get Started</a>
<p class="meta"><a href="https://github.com/devlikeapro/whatsapp-http-api">Source code in GitHub.</a></p>
<p class='lead'>{{ .Params.lead | safeHTML }}</p>
<div class='code-block highlight'>
<pre tabindex='0' class='chroma'><code class='language-bash' data-lang='bash' style='padding: 0'>docker run -it -p 3000:3000/tcp devlikeapro/whatsapp-http-api</code></pre>
</div>
<a class='btn btn-primary btn-lg px-4 mb-2'
href='/docs/{{ if .Site.Params.options.docsVersioning }}{{ .Site.Params.docsVersion }}/{{ end }}overview/introduction/'
role='button'>Get Started</a>
<p class='meta'><a href='https://github.com/devlikeapro/whatsapp-http-api'>Source code in GitHub.</a></p>
</div>
</div>
</section>
{{ end }}
{{ define "sidebar-prefooter" }}
<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>
<section class='section section-sm'>
<div class='container hr'>
<div class='row justify-content-center text-center'>
<div class='col-12'>
<h2>
Why WAHA?
</h2>
</div>
<div class='col-12 col-lg-4 pros-card'>
<img src='/images/icons/free-green.png' alt='Free' style='width: 5rem'>
<h2 class='h4 color-accent'>It's free!</h2>
<p>
<b>WAHA Core</b> version is always free!
<a href='https://waha.devlike.pro/docs/how-to/plus-version/#tiers' target='_blank'>
<b>WAHA Core</b>
</a>
version is always free, <b>no limits in messages</b> or time!
<br>
Even <b>WAHA Plus</b> version have no licence expiration!
<a href='https://waha.devlike.pro/docs/how-to/plus-version/#tiers' target='_blank'>
<b>WAHA Plus</b>
</a>
version have no licence expiration!
</p>
</div>
<div class="col-lg-5">
<h2 class="h4">Language agnostic</h2>
<div class='col-12 col-lg-4 pros-card'>
<img src='/images/icons/servers-green.png' alt='Servers' style='width: 5rem'>
<h2 class='h4'>Self-hosted solution</h2>
<p>
<a href='https://waha.devlike.pro/docs/how-to/plus-version/#why-waha' target='_blank'>
No strings to suspicious SaaS WhatsApp HTTP API solutions.
</a>
Install it on your own server!
</p>
</div>
<div class='col-12 col-lg-4 pros-card'>
<img src='/images/icons/api-green.png' alt='API' style='width: 5rem'>
<h2 class='h4'>HTTP API</h2>
<p>
Use your own favorite language - Python, JavaScript, PHP, C#, Clojure, or PowerShell.
<br/>
<small class="text-muted">
<br />
<small class='text-muted'>
PowerShell, really? Are you insane?! Choose another language, dude.
</small>
</p>
</div>
<div class="col-lg-5">
<h2 class="h4">Well documented</h2>
<p>Documentation has a lof examples and how-to guides.</p>
</div>
</div>
<div class="row justify-content-center text-center">
<div class="col-lg-5">
<h2 class="h4">Self hosted</h2>
<div class='row justify-content-center text-center'>
<div class='col-12 col-lg-4 pros-card'>
<img src='/images/icons/document-green.png' alt='Document' style='width: 5rem'>
<h2 class='h4'>Well documented</h2>
<p>
No strings to suspicious SaaS WhatsApp HTTP API solutions. Install it on your own server!
</p>
<a href='https://waha.devlike.pro/docs/overview/introduction/' target='_blank'>
Documentation
</a>
has a lof examples and how-to guides.</p>
</div>
<div class="col-lg-5">
<h2 class="h4">Easy installation</h2>
<div class='col-12 col-lg-4 pros-card'>
<img src='/images/icons/easy-green.png' alt='Easy' style='width: 5rem'>
<h2 class='h4'>Quick installation</h2>
<p>
You need to install <a href="https://docs.docker.com/engine/install/">Docker</a> and can run the API in a
click!
<br/>
<small class="text-muted">
You need to install Docker and
<a href='https://waha.devlike.pro/docs/how-to/install/' target='_blank'>
run the API in a click!
</a>
<br />
<small class='text-muted'>
It's actually "a command", not a click.
</small>
</p>
</div>
<div class="col-lg-5">
<h2 class="h4">No blocking</h2>
<div class='col-12 col-lg-4 pros-card'>
<img src='/images/icons/checked-green.png' alt='Checked' style='width: 5rem'>
<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">
<a href='https://waha.devlike.pro/docs/how-to/engines/' target='_blank'>
Under the hood
</a>
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>
@@ -85,9 +112,9 @@
{{ end }}
{{ define "sidebar-footer" }}
<section class="section section-sm container-fluid">
<div class="row justify-content-center text-center">
<div class="col-lg-9">
<section class='section section-sm container-fluid'>
<div class='row justify-content-center text-center'>
<div class='col-lg-9'>
{{- .Content -}}
</div>
</div>
@@ -1,17 +1,63 @@
<!-- Yandex.Metrika counter -->
<script type="text/javascript" >
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
<script type='text/javascript'>
(function(m, e, t, r, i, k, a) {
m[i] = m[i] || function() {
(m[i].a = m[i].a || []).push(arguments);
};
m[i].l = 1 * new Date();
for (var j = 0; j < document.scripts.length; j++) {
if (document.scripts[j].src === r) {
return;
}
}
k = e.createElement(t), a = e.getElementsByTagName(t)[0], k.async = 1, k.src = r, a.parentNode.insertBefore(k, a);
})
(window, document, 'script', 'https://mc.yandex.ru/metrika/tag.js', 'ym');
ym(91185978, "init", {
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});
ym(91185978, 'init', {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true,
});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/91185978" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<noscript>
<div><img src='https://mc.yandex.ru/watch/91185978' style='position:absolute; left:-9999px;' alt='' /></div>
</noscript>
<!-- /Yandex.Metrika counter -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-2XLLXRJ1KF"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-2XLLXRJ1KF');
</script>
<!-- /Google tag (gtag.js) -->
<!-- Main page cards -->
<style>
.pros-card {
border: 1px solid #e9ecef;
border-radius: 0.5rem;
padding: 1.5rem;
margin: 1rem;
background-color: #fff;
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.1);
}
.article-card {
border: 1px solid #e9ecef;
border-radius: 0.5rem;
padding: 1.5rem;
background-color: #fff;
box-shadow: 0 0 1rem 0 #4aae2050;
}
.hr {
border-top: 1px solid #e9ecef;
}
</style>
<!-- /Main page cards -->
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

+1 -1
View File
@@ -1 +1 @@
{"openapi":"3.1.0","paths":{"/api/{session}/auth/qr":{"get":{"operationId":"AuthController_getQR","summary":"Get QR code for pairing WhatsApp Web.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"},{"name":"format","required":true,"in":"query","schema":{"default":"image","enum":["image","raw"],"type":"string"}}],"responses":{"200":{"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Base64File"},{"$ref":"#/components/schemas/QRCodeValue"}]}}},"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/request-code":{"post":{"operationId":"AuthController_requestCode","summary":"Request authentication code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestCodeRequest"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/authorize-code":{"post":{"operationId":"AuthController_authorizeCode","summary":"Send OTP authentication code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OTPRequest"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/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","summary":"Stop session","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStopRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/logout":{"post":{"operationId":"SessionsController_clean","summary":"Logout from session.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLogoutRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions":{"get":{"operationId":"SessionsController_list","parameters":[{"name":"all","required":false,"in":"query","example":false,"description":"Return all sessions, including those that are in the STOPPED state.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SessionInfo"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/{session}/me":{"get":{"operationId":"SessionController_getMe","summary":"Get information about the authenticated account","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeInfo"}}}}},"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":""}},"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,Line truncated
{"openapi":"3.1.0","paths":{"/api/{session}/auth/qr":{"get":{"operationId":"AuthController_getQR","summary":"Get QR code for pairing WhatsApp Web.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"},{"name":"format","required":true,"in":"query","schema":{"default":"image","enum":["image","raw"],"type":"string"}}],"responses":{"200":{"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Base64File"},{"$ref":"#/components/schemas/QRCodeValue"}]}}},"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/request-code":{"post":{"operationId":"AuthController_requestCode","summary":"Request authentication code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestCodeRequest"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/authorize-code":{"post":{"operationId":"AuthController_authorizeCode","summary":"Send OTP authentication code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OTPRequest"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/captcha":{"get":{"operationId":"AuthController_getCaptcha","summary":"Get captcha image.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"responses":{"200":{"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Base64File"}]}}},"description":""}},"tags":["auth"],"security":[{"api_key":[]}]},"post":{"operationId":"AuthController_saveCaptcha","summary":"Enter captcha code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptchaBody"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/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","summary":"Stop session","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStopRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/logout":{"post":{"operationId":"SessionsController_clean","summary":"Logout from session.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLogoutRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions":{"get":{"operationId":"SessionsController_list","parameters":[{"name":"all","required":false,"in":"query","example":false,"description":"Return all sessions, including those that are in the STOPPED state.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SessionInfo"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/{session}/me":{"get":{"operationId":"SessionController_getMe","summary":"Get information about the authenticated account","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeInfo"}}}}},"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":"ChattingContrLine truncated
+5 -1
View File
@@ -24,6 +24,7 @@
"dependencies": {
"@adiwajshing/baileys": "github:WhiskeySockets/Baileys",
"@adiwajshing/keyed-db": "^0.2.4",
"@nestjs/axios": "^3.0.2",
"@nestjs/common": "^9.0.9",
"@nestjs/config": "^0.5.0",
"@nestjs/core": "^9.0.9",
@@ -31,8 +32,10 @@
"@nestjs/platform-express": "^9.0.9",
"@nestjs/serve-static": "^2.1.3",
"@nestjs/swagger": "^7.1.11",
"@nestjs/terminus": "^10.2.3",
"@types/lodash": "^4.14.194",
"@types/ws": "^8.5.4",
"check-disk-space": "^3.4.0",
"class-validator": "^0.12.2",
"del": "^6.0.0",
"express-basic-auth": "^1.2.1",
@@ -42,6 +45,7 @@
"link-preview-js": "^3.0.4",
"lodash": "^4.17.21",
"mime-types": "^2.1.27",
"mongodb": "^6.3.0",
"passport": "^0.6.0",
"passport-headerapikey": "^1.2.2",
"qrcode": "^1.5.1",
@@ -53,7 +57,7 @@
"rxjs": "^7.1.0",
"swagger-ui-express": "^4.1.4",
"venom-bot": "5.0.1",
"whatsapp-web.js": "https://github.com/pedroslopez/whatsapp-web.js"
"whatsapp-web.js": "https://github.com/devlikeapro/whatsapp-web.js#main-fork"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
+44 -2
View File
@@ -1,11 +1,26 @@
import { Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Query,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { SessionManager } from '../core/abc/manager.abc';
import { WhatsappSession } from '../core/abc/session.abc';
import { parseBool } from '../helpers';
import { GetChatMessagesQuery } from '../structures/chats.dto';
import { SessionApiParam, SessionParam } from './helpers';
import { EditMessageRequest } from '../structures/chatting.dto';
import {
ChatIdApiParam,
MessageIdApiParam,
SessionApiParam,
SessionParam,
} from './helpers';
@ApiSecurity('api_key')
@Controller('api/:session/chats')
@@ -51,6 +66,33 @@ class ChatsController {
) {
return session.clearMessages(chatId);
}
@Delete(':chatId/messages/:messageId')
@SessionApiParam
@ChatIdApiParam
@MessageIdApiParam
@ApiOperation({ summary: 'Deletes a message from the chat' })
deleteMessage(
@SessionParam session: WhatsappSession,
@Param('chatId') chatId: string,
@Param('messageId') messageId: string,
) {
return session.deleteMessage(chatId, messageId);
}
@Put(':chatId/messages/:messageId')
@SessionApiParam
@ChatIdApiParam
@MessageIdApiParam
@ApiOperation({ summary: 'Edits a message in the chat' })
editMessage(
@SessionParam session: WhatsappSession,
@Param('chatId') chatId: string,
@Param('messageId') messageId: string,
@Body() body: EditMessageRequest,
) {
return session.editMessage(chatId, messageId, body);
}
}
export { ChatsController };
+9
View File
@@ -14,6 +14,7 @@ import {
MessagePollRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageTextQuery,
MessageTextRequest,
MessageVideoRequest,
@@ -157,6 +158,14 @@ export class ChattingController {
return whatsapp.setReaction(request);
}
@Put('/star')
@ApiOperation({ summary: 'Star or unstar a message' })
async setStar(@Body() request: MessageStarRequest) {
const whatsapp = this.manager.getSession(request.session);
await whatsapp.setStar(request);
return;
}
@Get('/messages')
@ApiOperation({ summary: 'Get messages in a chat' })
getMessages(@Query() query: GetMessageQuery) {
+29 -2
View File
@@ -56,11 +56,12 @@ export class GroupsController {
summary:
'Updates the group settings to only allow admins to edit group info (title, description, photo).',
})
setGroupAdminOnly(
setInfoAdminOnly(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
@Body() request: SettingsSecurityChangeInfo,
) {
return session.setInfoAdminsOnly(id, true);
return session.setInfoAdminsOnly(id, request.adminsOnly);
}
@Get(':id/settings/security/info-admin-only')
@@ -76,6 +77,32 @@ export class GroupsController {
return session.getInfoAdminsOnly(id);
}
@Put(':id/settings/security/messages-admin-only')
@SessionApiParam
@ApiOperation({
summary:
'Updates the group settings to only allow admins to send messages.',
})
setMessagesAdminOnly(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
@Body() request: SettingsSecurityChangeInfo,
) {
return session.setMessagesAdminsOnly(id, request.adminsOnly);
}
@Get(':id/settings/security/messages-admin-only')
@SessionApiParam
@ApiOperation({
summary: 'Gets the group settings to only allow admins to send messages.',
})
getMessagesAdminOnly(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<SettingsSecurityChangeInfo> {
return session.getMessagesAdminsOnly(id);
}
@Delete(':id')
@SessionApiParam
@ApiOperation({ summary: 'Delete the group.' })
+17
View File
@@ -0,0 +1,17 @@
import { Controller, Get } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { HealthCheck } from '@nestjs/terminus';
import { WAHAHealthCheckService } from '../core/abc/WAHAHealthCheckService';
@Controller('health')
@ApiTags('other')
export class HealthController {
constructor(private wahaHealth: WAHAHealthCheckService) {}
@Get()
@HealthCheck()
async check() {
return this.wahaHealth.check();
}
}
+16
View File
@@ -48,6 +48,22 @@ export const SessionApiParam = ApiParam({
*/
export const SessionParam = Param('session', SessionPipe);
export const ChatIdApiParam = ApiParam({
name: 'chatId',
required: true,
type: 'string',
description: 'Chat ID',
example: '123456789@c.us',
});
export const MessageIdApiParam = ApiParam({
name: 'messageId',
required: true,
type: 'string',
description: 'Message ID',
example: 'true_123456789@c.us_BAE6A33293978B16',
});
function getRefSchemaPaths(models) {
return models.map((model) => {
return { $ref: getSchemaPath(model) };
+1 -1
View File
@@ -29,7 +29,7 @@ class SessionsController {
@Post('/start/')
async start(@Body() request: SessionStartRequest): Promise<SessionDTO> {
const result = await this.manager.start(request);
await this.manager.sessionStorage.configRepository.save(
await this.manager.sessionConfigRepository.save(
request.name,
request.config || null,
);
+25
View File
@@ -108,6 +108,10 @@ export class WhatsappConfigService {
return value ? value.split(',') : [];
}
getSessionMongoUrl(): string | undefined {
return this.configService.get('WHATSAPP_SESSIONS_MONGO_URL', undefined);
}
getDefaultEngineName(): WAHAEngine {
const value = getEngineName();
if (value in WAHAEngine) {
@@ -151,4 +155,25 @@ export class WhatsappConfigService {
getSwaggerAdvancedConfigEnabled(): boolean {
return this.configService.get('WHATSAPP_SWAGGER_CONFIG_ADVANCED', false);
}
getHealthMediaFilesThreshold(): number {
return this.configService.get<number>(
'WHATSAPP_HEALTH_MEDIA_FILES_THRESHOLD_MB',
100,
);
}
getHealthSessionFilesThreshold(): number {
return this.configService.get<number>(
'WHATSAPP_HEALTH_SESSION_FILES_THRESHOLD_MB',
100,
);
}
getHealthMongoTimeout(): number {
return this.configService.get<number>(
'WHATSAPP_HEALTH_MONGO_TIMEOUT_MS',
3000,
);
}
}
+1
View File
@@ -0,0 +1 @@
export abstract class DataStore {}
+18
View File
@@ -0,0 +1,18 @@
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DiskHealthIndicator, HealthCheckService } from '@nestjs/terminus';
import type { HealthCheckResult } from '@nestjs/terminus/dist/health-check/health-check-result.interface';
import { WhatsappConfigService } from '../../config.service';
import { SessionManager } from './manager.abc';
@Injectable()
export abstract class WAHAHealthCheckService {
constructor(
protected sessionManager: SessionManager,
protected health: HealthCheckService,
protected log: ConsoleLogger,
protected config: WhatsappConfigService,
) {}
abstract check(): Promise<HealthCheckResult>;
}
+5 -2
View File
@@ -8,12 +8,15 @@ import {
SessionStartRequest,
SessionStopRequest,
} from '../../structures/sessions.dto';
import { ISessionAuthRepository } from '../storage/ISessionAuthRepository';
import { ISessionConfigRepository } from '../storage/ISessionConfigRepository';
import { WhatsappSession } from './session.abc';
import { LocalSessionStorage } from './storage.abc';
import { WebhookConductor } from './webhooks.abc';
export abstract class SessionManager implements OnApplicationShutdown {
public sessionStorage: LocalSessionStorage;
public store: any;
public sessionAuthRepository: ISessionAuthRepository;
public sessionConfigRepository: ISessionConfigRepository;
protected abstract getEngine(engine: WAHAEngine): typeof WhatsappSession;
+35 -6
View File
@@ -3,10 +3,10 @@ import { EventEmitter } from 'events';
import * as fs from 'fs';
import { MessageId } from 'whatsapp-web.js';
import { OTPRequest, RequestCodeRequest } from '../../structures/auth.dto';
import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
GetMessageQuery,
MessageContactVcardRequest,
MessageFileRequest,
@@ -16,6 +16,7 @@ import {
MessagePollRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageTextRequest,
MessageVideoRequest,
MessageVoiceRequest,
@@ -47,8 +48,8 @@ import {
import { WASessionStatusBody } from '../../structures/webhooks.dto';
import { NotImplementedByEngineError } from '../exceptions';
import { QR } from '../QR';
import { DataStore } from './DataStore';
import { MediaManager } from './media.abc';
import { LocalSessionStorage } from './storage.abc';
const CHROME_PATH = '/usr/bin/google-chrome-stable';
const CHROMIUM_PATH = '/usr/bin/chromium';
@@ -77,7 +78,7 @@ export interface SessionParams {
name: string;
mediaManager: MediaManager;
log: ConsoleLogger;
sessionStorage: LocalSessionStorage;
sessionStore: DataStore;
proxyConfig?: ProxyConfig;
sessionConfig?: SessionConfig;
}
@@ -89,7 +90,7 @@ export abstract class WhatsappSession {
public name: string;
protected mediaManager: MediaManager;
protected log: ConsoleLogger;
protected sessionStorage: LocalSessionStorage;
protected sessionStore: DataStore;
protected proxyConfig?: ProxyConfig;
public sessionConfig?: SessionConfig;
@@ -98,7 +99,7 @@ export abstract class WhatsappSession {
public constructor({
name,
log,
sessionStorage,
sessionStore,
proxyConfig,
mediaManager,
sessionConfig,
@@ -107,7 +108,7 @@ export abstract class WhatsappSession {
this.name = name;
this.proxyConfig = proxyConfig;
this.log = log;
this.sessionStorage = sessionStorage;
this.sessionStore = sessionStore;
this.mediaManager = mediaManager;
this.sessionConfig = sessionConfig;
}
@@ -266,6 +267,10 @@ export abstract class WhatsappSession {
abstract setReaction(request: MessageReactionRequest);
setStar(request: MessageStarRequest): Promise<void> {
throw new NotImplementedByEngineError();
}
/**
* Chats methods
*/
@@ -285,6 +290,18 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public deleteMessage(chatId: string, messageId: string) {
throw new NotImplementedByEngineError();
}
public editMessage(
chatId: string,
messageId: string,
request: EditMessageRequest,
) {
throw new NotImplementedByEngineError();
}
public clearMessages(chatId) {
throw new NotImplementedByEngineError();
}
@@ -339,6 +356,14 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public getMessagesAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
throw new NotImplementedByEngineError();
}
public setMessagesAdminsOnly(id, value) {
throw new NotImplementedByEngineError();
}
public deleteGroup(id) {
throw new NotImplementedByEngineError();
}
@@ -418,6 +443,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public async getEngineInfo(): Promise<any> {
return {};
}
/**
* END - Methods for API
*/
-35
View File
@@ -1,35 +0,0 @@
import { SessionConfig } from '../../structures/sessions.dto';
abstract class SessionConfigRepository {
abstract save(
sessionName: string,
config: SessionConfig | null,
): Promise<void>;
abstract get(sessionName: string): Promise<SessionConfig | null>;
}
abstract class SessionStorage {
protected constructor(public engine: string) {
this.engine = engine;
}
abstract init(sessionName?: string): Promise<void>;
abstract clean(sessionName: string): Promise<void>;
abstract getAll(): Promise<string[]>;
//
// Repositories
//
abstract get configRepository(): SessionConfigRepository;
}
abstract class LocalSessionStorage extends SessionStorage {
public sessionsFolder: string;
abstract getFolderPath(sessionName: string): string;
}
export { LocalSessionStorage, SessionConfigRepository };
+10
View File
@@ -2,12 +2,14 @@ import { ConsoleLogger, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { PassportModule } from '@nestjs/passport';
import { ServeStaticModule } from '@nestjs/serve-static';
import { TerminusModule } from '@nestjs/terminus';
import { AuthController } from '../api/auth.controller';
import { ChatsController } from '../api/chats.controller';
import { ChattingController } from '../api/chatting.controller';
import { ContactsController } from '../api/contacts.controller';
import { GroupsController } from '../api/groups.controller';
import { HealthController } from '../api/health.controller';
import { PresenceController } from '../api/presence.controller';
import { ScreenshotController } from '../api/screenshot.controller';
import {
@@ -18,6 +20,8 @@ import { StatusController } from '../api/status.controller';
import { VersionController } from '../api/version.controller';
import { WhatsappConfigService } from '../config.service';
import { SessionManager } from './abc/manager.abc';
import { WAHAHealthCheckService } from './abc/WAHAHealthCheckService';
import { WAHAHealthCheckServiceCore } from './health/WAHAHealthCheckServiceCore';
import { SessionManagerCore } from './manager.core';
export const IMPORTS = [
@@ -38,6 +42,7 @@ export const IMPORTS = [
},
}),
PassportModule,
TerminusModule,
];
export const CONTROLLERS = [
AuthController,
@@ -51,12 +56,17 @@ export const CONTROLLERS = [
PresenceController,
ScreenshotController,
VersionController,
HealthController,
];
const PROVIDERS = [
{
provide: SessionManager,
useClass: SessionManagerCore,
},
{
provide: WAHAHealthCheckService,
useClass: WAHAHealthCheckServiceCore,
},
WhatsappConfigService,
ConsoleLogger,
];
@@ -0,0 +1,19 @@
import { useMultiFileAuthState } from '@adiwajshing/baileys';
import { Store } from 'whatsapp-web.js';
import { DataStore } from '../../abc/DataStore';
import { LocalStore } from '../../storage/LocalStore';
export class NowebAuthFactoryCore {
buildAuth(store: DataStore, name: string) {
if (store instanceof LocalStore) return this.buildLocalAuth(store, name);
throw new Error(`Unsupported store type '${store.constructor.name}'`);
}
protected async buildLocalAuth(store: LocalStore, name: string) {
await store.init(name);
const authFolder = store.getSessionDirectory(name);
const { state, saveCreds } = await useMultiFileAuthState(authFolder);
return { state, saveCreds };
}
}
@@ -5,6 +5,7 @@ import makeWASocket, {
getKeyAuthor,
isJidGroup,
jidNormalizedUser,
makeCacheableSignalKeyStore,
makeInMemoryStore,
PresenceData,
proto,
@@ -14,15 +15,18 @@ import makeWASocket, {
} from '@adiwajshing/baileys';
import { UnprocessableEntityException } from '@nestjs/common';
import * as Buffer from 'buffer';
import { request } from 'express';
import * as fs from 'fs/promises';
import { Agent } from 'https';
import * as lodash from 'lodash';
import { PairingCodeResponse } from 'src/structures/auth.dto';
import { flipObject, splitAt } from '../helpers';
import { flipObject, splitAt } from '../../../helpers';
import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
MessageContactVcardRequest,
MessageDestination,
MessageFileRequest,
MessageImageRequest,
@@ -31,12 +35,13 @@ import {
MessagePollRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageTextRequest,
MessageVoiceRequest,
SendSeenRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
} from '../../../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../../../structures/contacts.dto';
import {
ACK_UNKNOWN,
SECOND,
@@ -45,35 +50,40 @@ import {
WAHAPresenceStatus,
WAHASessionStatus,
WAMessageAck,
} from '../structures/enums.dto';
} from '../../../structures/enums.dto';
import {
CreateGroupRequest,
ParticipantsRequest,
} from '../structures/groups.dto';
} from '../../../structures/groups.dto';
import {
WAHAChatPresences,
WAHAPresenceData,
} from '../structures/presence.dto';
import { WAMessage } from '../structures/responses.dto';
import { MeInfo } from '../structures/sessions.dto';
import { BROADCAST_ID, TextStatus } from '../structures/status.dto';
} from '../../../structures/presence.dto';
import {
WAMessage,
WAMessageReaction,
} from '../../../structures/responses.dto';
import { MeInfo } from '../../../structures/sessions.dto';
import { BROADCAST_ID, TextStatus } from '../../../structures/status.dto';
import {
PollVote,
PollVotePayload,
WAMessageAckBody,
} from '../structures/webhooks.dto';
import { IEngineMediaProcessor } from './abc/media.abc';
} from '../../../structures/webhooks.dto';
import { IEngineMediaProcessor } from '../../abc/media.abc';
import {
ensureSuffix,
WAHAInternalEvent,
WhatsappSession,
} from './abc/session.abc';
} from '../../abc/session.abc';
import {
AvailableInPlusVersion,
NotImplementedByEngineError,
} from './exceptions';
import { createAgentProxy } from './helpers.proxy';
import { QR } from './QR';
} from '../../exceptions';
import { toVcard } from '../../helpers';
import { createAgentProxy } from '../../helpers.proxy';
import { QR } from '../../QR';
import { NowebAuthFactoryCore } from './NowebAuthFactoryCore';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
@@ -101,6 +111,8 @@ const ToEnginePresenceStatus = flipObject(PresenceStatuses);
export class WhatsappSessionNoWebCore extends WhatsappSession {
engine = WAHAEngine.NOWEB;
authFactory = new NowebAuthFactoryCore();
get listenConnectionEventsFromTheStart() {
return true;
}
@@ -123,9 +135,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return {
agent: agent,
fetchAgent: agent,
auth: state,
auth: {
creds: state.creds,
/** caching makes the store faster to send/recv messages */
keys: makeCacheableSignalKeyStore(state.keys, logger),
},
printQRInTerminal: true,
browser: Browsers.macOS('Chrome'),
browser: Browsers.ubuntu('Chrome'),
logger: logger,
mobile: false,
defaultQueryTimeoutMs: undefined,
@@ -134,9 +150,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
async makeSocket() {
const authFolder = this.sessionStorage.getFolderPath(this.name);
await fs.mkdir(authFolder, { recursive: true });
const { state, saveCreds } = await useMultiFileAuthState(authFolder);
const { state, saveCreds } = await this.authFactory.buildAuth(
this.sessionStore,
this.name,
);
const agent = this.makeAgent();
const socketConfig = this.getSocketConfig(agent, state);
const sock: any = makeWASocket(socketConfig);
@@ -191,7 +208,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
protected listenEngineEventsInDebugMode() {
this.sock.ev.process((events) => {
this.log.debug(`Received NOWEB events`, events);
this.log.debug(`NOWEB events: ${JSON.stringify(events)}`);
});
}
@@ -234,9 +251,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
async stop() {
this.sock.ev.removeAllListeners();
this.sock.ws.removeAllListeners();
this.sock.ws.close();
this.sock?.ev?.removeAllListeners();
this.sock?.ws?.removeAllListeners();
this.sock?.ws?.close();
this.status = WAHASessionStatus.STOPPED;
return;
}
@@ -336,6 +353,37 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.sock.sendMessage(chatId, message);
}
public deleteMessage(chatId: string, messageId: string) {
const jid = toJID(this.ensureSuffix(chatId));
const key = parseMessageId(messageId);
return this.sock.sendMessage(jid, { delete: key });
}
public editMessage(
chatId: string,
messageId: string,
request: EditMessageRequest,
) {
const jid = toJID(this.ensureSuffix(chatId));
const key = parseMessageId(messageId);
const message = {
text: request.text,
mentions: request.mentions?.map(toJID),
edit: key,
};
return this.sock.sendMessage(jid, message);
}
async sendContactVCard(request: MessageContactVcardRequest) {
const chatId = this.ensureSuffix(request.chatId);
const contacts = request.contacts.map((el) => ({ vcard: toVcard(el) }));
await this.sock.sendMessage(chatId, {
contacts: {
contacts: contacts,
},
});
}
async sendPoll(request: MessagePollRequest) {
const requestPoll = request.poll;
const poll = {
@@ -422,6 +470,19 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.sock.sendMessage(key.remoteJid, reactionMessage);
}
async setStar(request: MessageStarRequest) {
const key = parseMessageId(request.messageId);
await this.sock.chatModify(
{
star: {
messages: [{ id: key.id, fromMe: key.fromMe }],
star: request.star,
},
},
toJID(request.chatId),
);
}
/**
* Contacts methods
*/
@@ -576,6 +637,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.handleIncomingMessages(messages, handler, false);
});
return true;
case WAHAEvents.MESSAGE_REACTION:
this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) => {
const reactions = this.processMessageReaction(messages);
reactions.map(handler);
});
return true;
case WAHAEvents.MESSAGE_ANY:
this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) =>
this.handleIncomingMessages(messages, handler, true),
@@ -632,6 +699,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
// if there is no text or media message
if (!message) return;
if (!message.message) return;
// Ignore reactions, we have dedicated handler for that
if (message.message.reactionMessage) return;
// Ignore poll votes, we have dedicated handler for that
if (message.message.pollUpdateMessage) return;
// Do not include my messages
@@ -642,6 +711,34 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
}
private processMessageReaction(messages: any[]): WAMessageReaction[] {
const reactions = [];
for (const message of messages) {
if (!message) return [];
if (!message.message) return [];
if (!message.message.reactionMessage) return [];
const id = buildMessageId(message.key);
const fromToParticipant = getFromToParticipant(message);
const reactionMessage = message.message.reactionMessage;
const messageId = buildMessageId(reactionMessage.key);
const reaction: WAMessageReaction = {
id: id,
timestamp: message.messageTimestamp,
from: toCusFormat(fromToParticipant.from),
fromMe: message.key.fromMe,
to: toCusFormat(fromToParticipant.to),
participant: toCusFormat(fromToParticipant.participant),
reaction: {
text: reactionMessage.text,
messageId: messageId,
},
};
reactions.push(reaction);
}
return reactions;
}
private processIncomingMessage(message) {
return this.downloadMedia(message)
.then(this.toWAMessage)
@@ -6,7 +6,6 @@ import {
ChatRequest,
CheckNumberStatusQuery,
GetMessageQuery,
MessageContactVcardRequest,
MessageFileRequest,
MessageImageRequest,
MessageLinkPreviewRequest,
@@ -15,17 +14,17 @@ import {
MessageReplyRequest,
MessageTextRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
} from '../../../structures/chatting.dto';
import {
WAHAEngine,
WAHAEvents,
WAHASessionStatus,
} from '../structures/enums.dto';
import { WAMessage } from '../structures/responses.dto';
import { IEngineMediaProcessor } from './abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
import { NotImplementedByEngineError } from './exceptions';
import { QR } from './QR';
} from '../../../structures/enums.dto';
import { WAMessage } from '../../../structures/responses.dto';
import { IEngineMediaProcessor } from '../../abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from '../../abc/session.abc';
import { NotImplementedByEngineError } from '../../exceptions';
import { QR } from '../../QR';
export class WhatsappSessionVenomCore extends WhatsappSession {
engine = WAHAEngine.VENOM;
@@ -164,14 +163,6 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
}
}
sendContactVCard(request: MessageContactVcardRequest) {
return this.whatsapp.sendContactVcard(
request.chatId,
request.contactsId,
request.name,
);
}
sendText(request: MessageTextRequest) {
return this.whatsapp.sendText(
this.ensureSuffix(request.chatId),
@@ -8,24 +8,27 @@ import {
GroupChat,
Location,
Message,
Reaction,
} from 'whatsapp-web.js';
import { Message as MessageInstance } from 'whatsapp-web.js/src/structures';
import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
GetMessageQuery,
MessageFileRequest,
MessageImageRequest,
MessageLocationRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageTextRequest,
MessageVoiceRequest,
SendSeenRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
} from '../../../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../../../structures/contacts.dto';
import {
ACK_UNKNOWN,
WAHAEngine,
@@ -33,22 +36,25 @@ import {
WAHAPresenceStatus,
WAHASessionStatus,
WAMessageAck,
} from '../structures/enums.dto';
} from '../../../structures/enums.dto';
import {
CreateGroupRequest,
ParticipantsRequest,
SettingsSecurityChangeInfo,
} from '../structures/groups.dto';
import { WAMessage } from '../structures/responses.dto';
import { MeInfo } from '../structures/sessions.dto';
import { WAMessageRevokedBody } from '../structures/webhooks.dto';
import { IEngineMediaProcessor } from './abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
} from '../../../structures/groups.dto';
import {
WAMessage,
WAMessageReaction,
} from '../../../structures/responses.dto';
import { MeInfo } from '../../../structures/sessions.dto';
import { WAMessageRevokedBody } from '../../../structures/webhooks.dto';
import { IEngineMediaProcessor } from '../../abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from '../../abc/session.abc';
import {
AvailableInPlusVersion,
NotImplementedByEngineError,
} from './exceptions';
import { QR } from './QR';
} from '../../exceptions';
import { QR } from '../../QR';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
@@ -67,14 +73,24 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
this.qr = new QR();
}
protected buildClient() {
const clientOptions: ClientOptions = {
protected getClientOptions(): ClientOptions {
return {
puppeteer: {
headless: true,
executablePath: this.getBrowserExecutablePath(),
args: this.getBrowserArgsForPuppeteer(),
},
webVersion: '2.2411.2',
webVersionCache: {
type: 'remote',
remotePath:
'https://raw.githubusercontent.com/wppconnect-team/wa-version/main/html/{version}.html',
},
};
}
protected async buildClient() {
const clientOptions = this.getClientOptions();
this.addProxyConfig(clientOptions);
return new Client(clientOptions);
}
@@ -98,7 +114,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
async start() {
this.status = WAHASessionStatus.STARTING;
this.whatsapp = this.buildClient();
this.whatsapp = await this.buildClient();
this.whatsapp
.initialize()
.then(() => {
@@ -112,6 +128,15 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
this.status = WAHASessionStatus.FAILED;
this.log.error('The WhatsApp Web page has been closed');
});
if (this.isDebugEnabled()) {
this.log.debug("Logging 'console' event for web page");
this.whatsapp.pupPage.on('console', (msg) =>
this.log.debug(`WEBJS page log: ${msg.text()}`),
);
this.whatsapp.pupPage.evaluate(() =>
console.log(`url is ${location.href}`),
);
}
})
.catch((error) => {
this.status = WAHASessionStatus.FAILED;
@@ -150,7 +175,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
for (const key in Events) {
const event = Events[key];
this.whatsapp.on(event, (...data: any[]) => {
this.log.debug(`Received WEBJS event`, { event: event, data: data });
const log = { event: event, data: data };
this.log.debug(`WEBJS event: ${JSON.stringify(log)}`);
});
}
}
@@ -231,6 +257,24 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
);
}
public deleteMessage(chatId: string, messageId: string) {
const message = this.recreateMessage(messageId);
return message.delete(true);
}
public editMessage(
chatId: string,
messageId: string,
request: EditMessageRequest,
) {
const message = this.recreateMessage(messageId);
const options = {
// It's fine to sent just ids instead of Contact object
mentions: request.mentions as unknown as string[],
};
return message.edit(request.text, options);
}
reply(request: MessageReplyRequest) {
const options = {
quotedMessageId: request.reply_to,
@@ -279,15 +323,30 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
}
async setReaction(request: MessageReactionRequest) {
const messageId = this.deserializeId(request.messageId);
// Recreate instance to react on it
const message = new MessageInstance(this.whatsapp);
message.id = messageId;
message._data = { id: messageId };
const message = this.recreateMessage(request.messageId);
return message.react(request.reaction);
}
/**
* Recreate message instance from id
*/
private recreateMessage(msgId: string): MessageInstance {
const messageId = this.deserializeId(msgId);
const data = {
id: messageId,
};
return new MessageInstance(this.whatsapp, data);
}
async setStar(request: MessageStarRequest) {
const message = this.recreateMessage(request.messageId);
if (request.star) {
await message.star();
} else {
await message.unstar();
}
}
/**
* Chats methods
*/
@@ -377,6 +436,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
public async getInfoAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
return {
// Undocumented property, can be changed in the future
// @ts-ignore
adminsOnly: groupChat.groupMetadata.restrict,
};
@@ -387,6 +447,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return groupChat.setInfoAdminsOnly(value);
}
public async getMessagesAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
// @ts-ignore
return {
// Undocumented property, can be changed in the future
// @ts-ignore
adminsOnly: groupChat.groupMetadata.announce,
};
}
public async setMessagesAdminsOnly(id, value) {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
return groupChat.setMessagesAdminsOnly(value);
}
public getGroups() {
return this.whatsapp
.getChats()
@@ -520,6 +595,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
},
);
return true;
case WAHAEvents.MESSAGE_REACTION:
this.whatsapp.on('message_reaction', (message) =>
handler(this.processMessageReaction(message)),
);
return true;
case WAHAEvents.MESSAGE_ANY:
this.whatsapp.on(Events.MESSAGE_CREATE, (message) =>
this.processIncomingMessage(message).then(handler),
@@ -557,6 +637,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return await this.toWAMessage(message);
}
private processMessageReaction(reaction: Reaction): WAMessageReaction {
return {
id: reaction.id._serialized,
from: reaction.senderId,
fromMe: reaction.id.fromMe,
participant: reaction.senderId,
to: reaction.id.remote,
timestamp: reaction.timestamp,
reaction: {
text: reaction.reaction,
messageId: reaction.msgId._serialized,
},
};
}
protected toWAMessage(message: Message): Promise<WAMessage> {
// @ts-ignore
return Promise.resolve({
@@ -582,6 +677,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
});
}
public async getEngineInfo() {
return {
WWebVersion: await this.whatsapp?.getWWebVersion(),
state: await this.whatsapp?.getState(),
};
}
protected toWAContact(contact: Contact) {
// @ts-ignore
contact.id = contact.id._serialized;
@@ -0,0 +1,12 @@
import { Injectable } from '@nestjs/common';
import { HealthCheckResult } from '@nestjs/terminus';
import { WAHAHealthCheckService } from '../abc/WAHAHealthCheckService';
import { AvailableInPlusVersion } from '../exceptions';
@Injectable()
export class WAHAHealthCheckServiceCore extends WAHAHealthCheckService {
check(): Promise<HealthCheckResult> {
throw new AvailableInPlusVersion();
}
}
+21
View File
@@ -0,0 +1,21 @@
export function toVcard(data): string {
if (data.vcard) {
return data.vcard;
}
const parts = [];
parts.push('BEGIN:VCARD');
parts.push('VERSION:3.0');
parts.push(`FN:${data.fullName}`);
if (data.organization) {
parts.push(`ORG:${data.organization};`);
}
if (data.whatsappId) {
parts.push(
`TEL;type=CELL;type=VOICE;waid=${data.whatsappId}:${data.phoneNumber}`,
);
} else {
parts.push(`TEL;type=CELL;type=VOICE:${data.phoneNumber}`);
}
parts.push('END:VCARD');
return parts.join('\n');
}
+23 -13
View File
@@ -1,6 +1,7 @@
import {
ConsoleLogger,
Injectable,
LogLevel,
NotFoundException,
UnprocessableEntityException,
} from '@nestjs/common';
@@ -19,13 +20,15 @@ import {
import { WebhookConfig } from '../structures/webhooks.config.dto';
import { SessionManager } from './abc/manager.abc';
import { SessionParams, WhatsappSession } from './abc/session.abc';
import { WhatsappSessionNoWebCore } from './engines/noweb/session.noweb.core';
import { WhatsappSessionVenomCore } from './engines/venom/session.venom.core';
import { WhatsappSessionWebJSCore } from './engines/webjs/session.webjs.core';
import { DOCS_URL } from './exceptions';
import { getProxyConfig } from './helpers.proxy';
import { CoreMediaManager, MediaStorageCore } from './media.core';
import { WhatsappSessionNoWebCore } from './session.noweb.core';
import { WhatsappSessionVenomCore } from './session.venom.core';
import { WhatsappSessionWebJSCore } from './session.webjs.core';
import { SessionStorageCore } from './storage.core';
import { LocalSessionAuthRepository } from './storage/LocalSessionAuthRepository';
import { LocalSessionConfigRepository } from './storage/LocalSessionConfigRepository';
import { LocalStoreCore } from './storage/LocalStoreCore';
import { WebhookConductorCore } from './webhooks.core';
export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
@@ -36,8 +39,8 @@ export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
}
}
export function buildLogger(name) {
return new ConsoleLogger(name, { logLevels: getLogLevels() });
export function buildLogger(name: string, levels: LogLevel[]) {
return new ConsoleLogger(name, { logLevels: levels });
}
@Injectable()
@@ -59,8 +62,9 @@ export class SessionManagerCore extends SessionManager {
this.session = undefined;
const engineName = this.config.getDefaultEngineName();
this.EngineClass = this.getEngine(engineName);
this.sessionStorage = new SessionStorageCore(engineName.toLowerCase());
this.store = new LocalStoreCore(engineName.toLowerCase());
this.sessionAuthRepository = new LocalSessionAuthRepository(this.store);
this.sessionConfigRepository = new LocalSessionConfigRepository(this.store);
this.startPredefinedSessions();
}
@@ -104,23 +108,24 @@ export class SessionManagerCore extends SessionManager {
const name = request.name;
this.log.log(`'${name}' - starting session...`);
const log = buildLogger(`WhatsappSession - ${name}`);
const levels = getLogLevels(request.config?.debug);
const log = buildLogger(`WhatsappSession - ${name}`, levels);
const mediaManager = new CoreMediaManager(
new MediaStorageCore(),
this.config.mimetypes,
);
const webhookLog = buildLogger(`Webhook - ${name}`);
const webhookLog = buildLogger(`Webhook - ${name}`, levels);
const webhook = new this.WebhookConductorClass(webhookLog);
const proxyConfig = this.getProxyConfig(request);
const sessionConfig: SessionParams = {
name,
mediaManager,
log,
sessionStorage: this.sessionStorage,
sessionStore: this.store,
proxyConfig: proxyConfig,
sessionConfig: request.config,
};
await this.sessionStorage.init(name);
await this.sessionAuthRepository.init(name);
// @ts-ignore
const session = new this.EngineClass(sessionConfig);
this.session = session;
@@ -181,7 +186,7 @@ export class SessionManagerCore extends SessionManager {
}
async logout(request: SessionLogoutRequest) {
await this.sessionStorage.clean(request.name);
await this.sessionAuthRepository.clean(request.name);
}
getSession(name: string): WhatsappSession {
@@ -210,12 +215,17 @@ export class SessionManagerCore extends SessionManager {
];
}
const me = await this.session.getSessionMeInfo().catch((err) => null);
const engine = {
engine: this.session?.engine,
...(await this.session?.getEngineInfo().catch((err) => ({}))),
};
return [
{
name: this.session.name,
status: this.session.status,
config: this.session.sessionConfig,
me: me,
engine: engine,
},
];
}
-97
View File
@@ -1,97 +0,0 @@
import * as crypto from 'crypto';
import * as fs from 'fs/promises';
import * as os from 'os';
import * as path from 'path';
import { SessionConfig } from '../structures/sessions.dto';
import {
LocalSessionStorage,
SessionConfigRepository,
} from './abc/storage.abc';
class LocalSessionConfigRepository extends SessionConfigRepository {
FILENAME = '.waha.session.config.json';
constructor(private storage: LocalSessionStorage) {
super();
}
async get(sessionName: string): Promise<SessionConfig | null> {
const filepath = this.getFilePath(sessionName);
// Check file exists
try {
await fs.access(filepath, fs.constants.F_OK);
} catch (error) {
return null;
}
// Try to load config
let content;
try {
content = await fs.readFile(filepath, 'utf-8');
} catch (error) {
return null;
}
return JSON.parse(content);
}
async save(sessionName: string, config: SessionConfig | null) {
// Create folder if not exist
const folder = this.storage.getFolderPath(sessionName);
await fs.mkdir(folder, { recursive: true });
// Save config
const filepath = this.getFilePath(sessionName);
const content = JSON.stringify(config || null);
await fs.writeFile(filepath, content);
}
private getFilePath(sessionName): string {
const folder = this.storage.getFolderPath(sessionName);
return path.join(folder, this.FILENAME);
}
}
export class SessionStorageCore extends LocalSessionStorage {
public sessionsFolder: string;
constructor(engine: string) {
super(engine);
this.sessionsFolder = path.join(os.tmpdir(), 'waha-sessions');
}
get engineFolder() {
return path.join(this.sessionsFolder, this.engine);
}
async init(sessionName?: string) {
const folder = sessionName
? this.getFolderPath(sessionName)
: this.engineFolder;
await fs.mkdir(folder, { recursive: true });
}
getFolderPath(name: string): string {
const suffix = crypto.createHash('md5').update(name).digest('hex');
return path.join(this.engineFolder, `${name}-${suffix}`);
}
async clean(sessionName: string) {
const folder = this.getFolderPath(sessionName);
await fs.rm(folder, { recursive: true });
}
async getAll(): Promise<string[]> {
await this.init();
const content = await fs.readdir(this.engineFolder, {
withFileTypes: true,
});
return content
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
}
get configRepository(): LocalSessionConfigRepository {
return new LocalSessionConfigRepository(this);
}
}
@@ -0,0 +1,7 @@
export abstract class ISessionAuthRepository {
abstract init(sessionName?: string): Promise<void>;
abstract clean(sessionName: string): Promise<void>;
abstract getAll(): Promise<string[]>;
}
@@ -0,0 +1,12 @@
import { SessionConfig } from '../../structures/sessions.dto';
export abstract class ISessionConfigRepository {
abstract save(
sessionName: string,
config: SessionConfig | null,
): Promise<void>;
abstract get(sessionName: string): Promise<SessionConfig | null>;
abstract delete(sessionName: string): Promise<void>;
}
@@ -0,0 +1,32 @@
import * as fs from 'fs/promises';
import { ISessionAuthRepository } from './ISessionAuthRepository';
import { LocalStore } from './LocalStore';
export class LocalSessionAuthRepository extends ISessionAuthRepository {
private store: LocalStore;
constructor(store: LocalStore) {
super();
this.store = store;
}
async init(sessionName?: string) {
await this.store.init(sessionName);
}
async clean(sessionName: string) {
const folder = this.store.getSessionDirectory(sessionName);
await fs.rm(folder, { recursive: true });
}
async getAll(): Promise<string[]> {
await this.init();
const content = await fs.readdir(this.store.getEngineDirectory(), {
withFileTypes: true,
});
return content
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
}
}
@@ -0,0 +1,66 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import { SessionConfig } from '../../structures/sessions.dto';
import { ISessionConfigRepository } from './ISessionConfigRepository';
import { LocalStore } from './LocalStore';
export class LocalSessionConfigRepository extends ISessionConfigRepository {
FILENAME = '.waha.session.config.json';
private store: LocalStore;
constructor(store: LocalStore) {
super();
this.store = store;
}
private async fileExists(filepath: string) {
try {
await fs.access(filepath, fs.constants.F_OK);
} catch (error) {
return false;
}
return true;
}
async get(sessionName: string): Promise<SessionConfig | null> {
const filepath = this.getFilePath(sessionName);
// Check file exists
if (!(await this.fileExists(filepath))) {
return null;
}
// Try to load config
let content;
try {
content = await fs.readFile(filepath, 'utf-8');
} catch (error) {
return null;
}
return JSON.parse(content);
}
async save(sessionName: string, config: SessionConfig | null) {
// Create folder if not exist
const folder = this.store.getSessionDirectory(sessionName);
await fs.mkdir(folder, { recursive: true });
// Save config
const filepath = this.getFilePath(sessionName);
const content = JSON.stringify(config || null);
await fs.writeFile(filepath, content);
}
private getFilePath(sessionName): string {
const folder = this.store.getSessionDirectory(sessionName);
return path.join(folder, this.FILENAME);
}
async delete(sessionName: string): Promise<void> {
const filepath = this.getFilePath(sessionName);
if (!(await this.fileExists(filepath))) {
return;
}
await fs.unlink(filepath);
}
}
+20
View File
@@ -0,0 +1,20 @@
import { DataStore } from '../abc/DataStore';
export abstract class LocalStore extends DataStore {
abstract init(sessionName?: string): Promise<void>;
/**
* Get the directory where all the engines and sessions are stored
*/
abstract getBaseDirectory(): string;
/**
* Get the directory where the engine sessions are stored
*/
abstract getEngineDirectory(): string;
/**
* Get the directory where the session data is stored
*/
abstract getSessionDirectory(name: string): string;
}
+52
View File
@@ -0,0 +1,52 @@
import * as crypto from 'crypto';
import * as fs from 'fs/promises';
import * as os from 'os';
import * as path from 'path';
import { LocalStore } from './LocalStore';
export class LocalStoreCore extends LocalStore {
protected readonly baseDirectory: string = path.join(
os.tmpdir(),
'waha-sessions',
);
private readonly engine: string;
constructor(engine: string) {
super();
this.engine = engine;
}
async init(sessionName?: string) {
await fs.mkdir(this.getEngineDirectory(), { recursive: true });
if (sessionName) {
await fs.mkdir(this.getSessionDirectory(sessionName), {
recursive: true,
});
}
}
/**
* Get the directory where all the engines and sessions are stored
*/
getBaseDirectory() {
return path.join(this.baseDirectory);
}
/**
* Get the directory where the engine sessions are stored
*/
getEngineDirectory() {
return path.join(this.baseDirectory, this.engine);
}
getSessionDirectory(name: string): string {
return this.getDirectoryPath(name);
}
protected getDirectoryPath(name: string): string {
const suffix = crypto.createHash('md5').update(name).digest('hex');
return path.join(this.getEngineDirectory(), `${name}-${suffix}`);
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ export class WebhookSenderCore extends WebhookSender {
return;
}
this.log.log(`POST request was sent with status code: ${res.statusCode}`);
this.log.verbose(`Response: ${JSON.stringify(body)}`);
this.log.debug(`Response: ${body}`);
});
}
}
+5 -4
View File
@@ -33,10 +33,11 @@ function splitAt(str: string, index) {
return [fst.join(''), snd.join('')];
}
function getLogLevels(): LogLevel[] {
return process.env.DEBUG != undefined
? ['log', 'debug', 'error', 'verbose', 'warn']
: ['log', 'error', 'warn', 'verbose'];
function getLogLevels(debug: boolean): LogLevel[] {
const enableDebug = process.env.DEBUG != undefined || debug;
return enableDebug
? ['log', 'error', 'warn', 'debug', 'verbose']
: ['log', 'error', 'warn'];
}
export { flipObject, getLogLevels, parseBool, splitAt };
+1 -1
View File
@@ -31,7 +31,7 @@ async function loadModules(): Promise<
async function bootstrap() {
const [AppModule, SwaggerModule] = await loadModules();
const app = await NestFactory.create(AppModule, {
logger: getLogLevels(),
logger: getLogLevels(false),
});
app.enableShutdownHooks();
+66 -2
View File
@@ -85,9 +85,59 @@ export class MessageRequest extends SessionBaseRequest {
messageId: string;
}
export class VCardContact {
@ApiProperty({
example:
'BEGIN:VCARD\nVERSION:3.0\nFN:Jane Doe\nORG:Company Name;\nTEL;type=CELL;type=VOICE;waid=911111111111:+91 11111 11111\nEND:VCARD',
description: 'The vcard string',
})
vcard: string;
}
export class Contact {
@ApiProperty({
example: 'John Doe',
description: 'The full name of the contact',
})
fullName: string;
@ApiProperty({
example: 'Company Name',
description: 'The organization of the contact',
required: false,
})
organization: string;
@ApiProperty({
example: '+91 11111 11111',
description: 'The phone number of the contact',
})
phoneNumber: string;
@ApiProperty({
example: '911111111111',
description: 'The whatsapp id of the contact. DO NOT add + or @c.us',
required: false,
})
whatsappId: string;
vcard: string = null;
}
@ApiExtraModels(Contact, VCardContact)
export class MessageContactVcardRequest extends ChatRequest {
contactsId: string;
name: string;
@ApiProperty({
type: 'array',
oneOf: [
{
$ref: getSchemaPath(VCardContact),
},
{
$ref: getSchemaPath(Contact),
},
],
})
contacts: (VCardContact | Contact)[];
}
export class MessageTextRequest extends ChatRequest {
@@ -96,6 +146,13 @@ export class MessageTextRequest extends ChatRequest {
mentions?: string[];
}
export class EditMessageRequest {
text = 'Hello, world!';
@ApiHideProperty()
mentions?: string[];
}
export class MessageReplyRequest extends MessageTextRequest {
text = 'Reply text';
@ApiProperty({
@@ -167,6 +224,13 @@ export class MessageReactionRequest extends MessageRequest {
reaction: string;
}
export class MessageStarRequest extends MessageRequest {
@ChatIdProperty()
chatId: string;
star: boolean;
}
export class WANumberExistResult {
numberExists: boolean;
@ApiProperty({
+1
View File
@@ -3,6 +3,7 @@ export const SECOND = 1000;
export enum WAHAEvents {
SESSION_STATUS = 'session.status',
MESSAGE = 'message',
MESSAGE_REACTION = 'message.reaction',
MESSAGE_ANY = 'message.any',
MESSAGE_ACK = 'message.ack',
MESSAGE_REVOKED = 'message.revoked',
+1 -1
View File
@@ -9,7 +9,7 @@ export class Participant {
}
export class SettingsSecurityChangeInfo {
adminsOnly: boolean;
adminsOnly: boolean = true;
}
/**
+25 -1
View File
@@ -30,7 +30,7 @@ export class WAMedia {
filename?: string;
}
export class WAMessage {
class WAMessageBase {
@MessageIdProperty()
id: string;
@@ -65,7 +65,9 @@ export class WAMessage {
description: 'For groups - participant who sent the message',
})
participant: string;
}
export class WAMessage extends WAMessageBase {
@ApiProperty({
description: 'Message content',
})
@@ -121,3 +123,25 @@ export class WAMessage {
})
_data?: any;
}
export class WAReaction {
@ApiProperty({
description:
'Reaction to the message. Either the reaction (emoji) or empty string to remove the reaction',
})
text: string;
@ApiProperty({
description: 'Message ID for the message to react to',
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
})
messageId: string;
}
export class WAMessageReaction extends WAMessageBase {
@ApiProperty({
description:
'Reaction to the message. Either the reaction (emoji) or empty string to remove the reaction',
})
reaction: WAReaction;
}
+4
View File
@@ -39,10 +39,13 @@ export class ProxyConfig {
export class SessionConfig {
webhooks?: WebhookConfig[];
@ApiProperty({
example: null,
})
proxy?: ProxyConfig;
debug: boolean = false;
}
export class SessionStartRequest {
@@ -79,4 +82,5 @@ export class MeInfo {
export class SessionInfo extends SessionDTO {
me?: MeInfo;
engine?: any;
}
+12 -1
View File
@@ -10,7 +10,7 @@ import {
import { WAHAEnvironment } from './environment.dto';
import { WAHAChatPresences } from './presence.dto';
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
import { WAMessage } from './responses.dto';
import { WAMessage, WAMessageReaction } from './responses.dto';
import { MeInfo } from './sessions.dto';
export class WAMessageAckBody {
@@ -134,6 +134,16 @@ class WAHAWebhookMessage extends WAHAWebhook {
payload: WAMessage;
}
class WAHAWebhookMessageReaction extends WAHAWebhook {
@ApiProperty({
description:
'The event is triggered when a user reacts or removes a reaction.',
})
event = WAHAEvents.MESSAGE_REACTION;
payload: WAMessageReaction;
}
class WAHAWebhookMessageAny extends WAHAWebhook {
@ApiProperty({
description: 'Fired on all message creations, including your own.',
@@ -223,6 +233,7 @@ class WAHAWebhookPollVoteFailed extends WAHAWebhook {
const WAHA_WEBHOOKS = [
WAHAWebhookSessionStatus,
WAHAWebhookMessage,
WAHAWebhookMessageReaction,
WAHAWebhookMessageAny,
WAHAWebhookMessageAck,
WAHAWebhookMessageRevoked,
+34
View File
@@ -0,0 +1,34 @@
/**
* An errors which gets raised when the timeout
* exceeded
*
* @internal
*/
export class TimeoutError extends Error {}
/**
* Executes a promise in the given timeout. If the promise
* does not finish in the given timeout, it will
* raise a TimeoutError
*
* @param {number} ms The timeout in milliseconds
* @param {Promise<any>} promise The promise which should get executed
*
* @internal
*/
export const promiseTimeout = function (
ms: number,
promise: Promise<any>,
): Promise<any> {
let timer: NodeJS.Timeout;
return Promise.race([
promise,
new Promise(
(_, reject) =>
(timer = setTimeout(
() => reject(new TimeoutError(`Timed out in ${ms}ms.`)),
ms,
)),
),
]).finally(() => clearTimeout(timer));
};
+1 -1
View File
@@ -33,7 +33,7 @@ export function getEngineName(): string {
}
export const VERSION: WAHAEnvironment = {
version: '2023.12.3',
version: '2024.3.14',
engine: getEngineName(),
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
+1335 -1101
View File
File diff suppressed because it is too large. Load diff