Compare commits

..
47 Commits
Author SHA1 Message Date
allburov 6e427a0a34 [core] Update yarn, engines
Release / docker-amd64 (push) Waiting to run
Release / docker-arm (push) Waiting to run
2023-09-07 17:33:27 +07:00
allburov 40c5e95d93 [core] Up version 2023-09-07 17:33:18 +07:00
allburov ab70e27266 [core] fix for QR scanning 2023-09-07 16:47:17 +07:00
allburov 301b6f57d3 [core] Add changelog note 2023-09-07 13:54:06 +07:00
allburov 4c9f5ed907 [core] Add personal key to Pro 2023-09-07 13:49:22 +07:00
allburov 04d65150ed [core] Add install doc 2023-09-07 13:47:41 +07:00
allburov d29165c9a5 [core] Remove buttons API 2023-09-07 13:11:49 +07:00
allburov 30db175d52 No more semver build, only latest 2023-09-04 18:42:54 +07:00
allburov 5c666c0dd4 Use buildjet with arm
Release / docker-amd64 (push) Waiting to run
Release / docker-arm (push) Waiting to run
2023-09-04 18:39:57 +07:00
allburov 3ccc0469c0 Use buildjet 2023-09-04 18:39:55 +07:00
allburov 5ab3244d6a adjust CI 2023-09-04 18:39:52 +07:00
allburov 05bc8abbee [core] Bump version to 2023.9.3 2023-09-03 19:30:47 +07:00
allburov 8e9056a0cd [core] Add /api/sessions/{session}/me endpoint
Fix #191
2023-09-03 19:30:16 +07:00
allburov ea75b357a7 [core] Log engine events if DEBUG enabled for NOWEB 2023-09-03 16:33:08 +07:00
allburov 2b607e9b44 [core] Bump version to 2023.8.29 2023-08-29 07:49:13 +03:00
allburov 57d3528a96 [core] Apply code review suggestions 2023-08-29 07:48:51 +03:00
allburov 660a04aa52 [core] Document polls 2023-08-29 07:48:51 +03:00
allburov d0a408001d [core] poll.vote.failed 2023-08-29 07:48:51 +03:00
allburov 48391533a6 [core] poll.vote 2023-08-29 07:48:51 +03:00
allburov f2113adbbc [core] Send poll 2023-08-29 07:48:51 +03:00
allburov abfcbc7397 [core] Update WEBJS
Release / deploy-docker (push) Waiting to run
2023-08-23 16:40:46 +03:00
allburov 96f2982325 [core] Bump to 2023.8.23 2023-08-23 12:15:06 +03:00
allburov 3a521bc06c [core] Update WEBJS engine 2023-08-23 12:14:51 +03:00
allburov 6fbaec0083 [core] Bump to 2023.8.21 2023-08-21 14:25:06 +03:00
allburov cae94c7b5c [core] catch error while handling incoming message NOWEB 2023-08-21 14:24:51 +03:00
allburov bce012f692 [core] Bump version to 2023.8.19
Release / deploy-docker (push) Waiting to run
2023-08-19 12:36:08 +03:00
allburov b23b639a8b [core] Support message.ack for NOWEB, add ackName 2023-08-19 12:35:44 +03:00
allburov 5a628907ed [core] Add pairing auth method for NOWEB, add QR endpoint 2023-08-13 16:08:56 +03:00
allburov 6da479c0d7 [core] Update WEBJS engine
Release / deploy-docker (push) Waiting to run
2023-08-10 10:45:40 +03:00
allburov cbb321df6d [core] Bump to 2023.8.10 2023-08-10 07:27:36 +03:00
allburov 0a7beb6909 [core] Update webjs engine
Fix TypeError: Cannot read properties of undefined (reading 'CallCollection')
2023-08-10 07:27:33 +03:00
allburov 2105094ac3 [core] Bump version to 2023.8.8
Release / deploy-docker (push) Waiting to run
2023-08-08 08:57:31 +03:00
allburov d8fee686f8 [core] Update WEBJS and NOWEB engines 2023-08-08 08:56:23 +03:00
allburov 75fc1fd7e0 [docs] Update swagger (openapi) 2023-08-07 08:06:36 +03:00
allburov e718652491 [docs] Keep only versions as headers 2023-08-07 08:05:01 +03:00
allburov bcb49f2be7 [docs] Fix changelog for stories 2023-08-06 18:52:56 +03:00
allburov dab63e4060 [example] Send message and reply 2023-08-06 18:02:36 +03:00
allburov 3fdacbb6ac [core] set defaultQueryTimeoutMs to undefined 2023-08-01 16:42:16 +07:00
allburov f02b62e620 [plus] Show macos by default 2023-08-01 15:42:59 +07:00
allburov 51acce14a0 [plus] Add session to what's next? 2023-08-01 15:28:26 +07:00
allburov b63f6ac39e [core] Add status endpoints 2023-07-25 17:42:44 +07:00
allburov 9e107a8a76 [core] Update noweb engine 2023-07-23 16:49:21 +07:00
allburov 8c083242d3 [core] add documentation and changelog for info-admin-only 2023-07-21 16:08:04 +07:00
gqlxj1987 2a068d561f [core] Add PUT /api/groups/:id/settings/security/info-admin-only endpoint 2023-07-21 13:24:23 +07:00
allburov ef96fbf8c5 [core] Fix chatId bracket 2023-07-16 16:51:50 +07:00
allburov 2c381a0b9c [core] Add openapi json link 2023-07-16 16:41:37 +07:00
allburov fef2bcb6e2 [core] create directory before saving file
Release / deploy-docker (push) Waiting to run
2023-07-16 16:25:41 +07:00
58 changed files with 3312 additions and 1804 deletions

No files matched your search

+39 -10
View File
@@ -6,18 +6,12 @@ on:
- "*"
jobs:
deploy-docker:
runs-on: ubuntu-latest
docker-amd64:
runs-on: buildjet-4vcpu-ubuntu-2204
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: 'amd64'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
@@ -26,9 +20,9 @@ jobs:
uses: docker/metadata-action@v4
with:
images: ${{ secrets.DOCKER_IMAGE }}
flavor: latest=true
flavor: latest=false
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
- name: Login to image repository
if: github.ref_type == 'tag'
@@ -46,3 +40,38 @@ jobs:
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker-arm:
runs-on: buildjet-4vcpu-ubuntu-2204-arm
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: ${{ secrets.DOCKER_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=arm
- 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: 'linux/arm64'
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+1
View File
@@ -1,4 +1,5 @@
.yarn
example.ts
.*sessions
tokens
files
+1 -1
View File
@@ -1,3 +1,3 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.6.1.cjs
yarnPath: .yarn/releases/yarn-3.6.3.cjs
+1 -1
View File
@@ -8,7 +8,7 @@ ENV PUPPETEER_SKIP_DOWNLOAD=True
WORKDIR /src
COPY package.json .
COPY yarn.lock .
RUN yarn set version 3.6.1
RUN yarn set version 3.6.3
RUN yarn install
# App
+1 -2
View File
@@ -40,7 +40,6 @@
- Receive messages
- Message replies
- Send location
- Send buttons
- Receive location
- React to messages
- Send media (images/documents/files)
@@ -110,7 +109,7 @@ docker pull devlikeapro/whatsapp-http-api
![](./docs/site/content/en/docs/overview/quick-start//images/versions/plus.png) For Plus version, we use login to get the image before:
```bash
docker login -u devlikeapro -p {PASSWORD}
docker login -u devlikeapro -p {KEY}
docker pull devlikeapro/whatsapp-http-api-plus
docker logout
```
@@ -56,10 +56,16 @@
[[main]]
name = "Swagger ->"
weight = 99
weight = 98
url = "https://waha.devlike.pro/swagger"
parent = "docs"
[[main]]
name = "OpenAPI JSON ->"
weight = 99
url = "https://waha.devlike.pro/swagger/openapi.json"
parent = "docs"
[[main]]
name = "WAHA Plus"
weight = 30
@@ -6,7 +6,7 @@ date: 2020-10-06T08:48:45+00:00
lastmod: 2020-10-06T08:48:45+00:00
draft: false
images: []
weight: 126
weight: 127
---
Chats methods.
@@ -23,12 +23,12 @@ Get all chats
### Get messages from chat
Get 100 messages from the chat
`GET /api/{session}/chats/{chatId/messages?limit=100`
`GET /api/{session}/chats/{chatId}/messages?limit=100`
Get 100 messages from the chat, skip downloading media (images, files)
`GET /api/{session}/chats/{chatId/messages?limit=100&downloadMedia=false`
`GET /api/{session}/chats/{chatId}/messages?limit=100&downloadMedia=false`
### Delete chat
Use the method to delete chat
@@ -84,6 +84,17 @@ Request:
}
```
## Settings
### Security for 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`
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)
## Participants
### Get participants
@@ -0,0 +1,115 @@
---
title : "Install"
description: "How to install and update WAHA"
lead: ""
date: 2020-10-06T08:48:45+00:00
lastmod: 2020-10-06T08:48:45+00:00
draft: false
images: []
weight: 109
---
On the page you'll find answers on how to install WAHA.
If you wish to read a quick start guide which show you how to send you first message via HTTP API
please go to [**Quick Start ->**]({{< relref "/docs/overview/quick-start" >}}).
WAHA is distributed in two versions, that have a similar installation instructions.
- WAHA Core ![](/images/versions/core.png) - the basic version that meet almost 80% people's needs. 100% free and [open source ->](https://github.com/devlikeapro/whatsapp-http-api).
- WAHA Plus ![](/images/versions/plus.png) - the version with advanced messages, security, and reliability features. Donations, no license expiration, quick updates, read more about [Plus Version →]({{< relref "plus-version" >}})
## Requirements
Only thing that you must have - installed docker. Please follow the original
instruction <a href="https://docs.docker.com/get-docker/" target="_blank" rel="noopener">how to install docker -></a>.
{{< details "Why Docker?" >}}
Docker makes it easy to ship all-in-one solution with the runtime and dependencies. You don't have to worry about
language-specific libraries or chrome installation.
Also, Docker makes installation and update processes so simple, just one command!
{{< /details >}}
## WAHA Core
Download the image:
```bash
docker pull devlikeapro/whatsapp-http-api
```
Run the container:
```bash
docker run -it -p 3000:3000/tcp devlikeapro/whatsapp-http-api
```
Open API documentation [http://localhost:3000](http://localhost:3000).
## WAHA Plus
Before installing WAHA Plus version you need to get a key, `{KEY}` in below commands, in format `dckr_pat_1111`
We distribute the key via two platforms (Boosty and Patreon) and you'll find the active key **in the latest post**.
Keep in mind that we publish new key every month, so you must get the latest active key in order to download
the latest image.
Read more about [**Plus Version →**]({{< relref "plus-version" >}})
{{< alert icon="👉" text="Subscribe as **PRO** level and get your personal key which won't expire every month while you're supporting the project" />}}
Download the image:
```bash
docker login -u devlikeapro -p {KEY}
docker pull devlikeapro/whatsapp-http-api-plus
docker logout
```
Run the container:
```bash
docker run -it -p 3000:3000/tcp devlikeapro/whatsapp-http-api-plus
```
Open API documentation [http://localhost:3000](http://localhost:3000).
## ARM
If you’re using **ARM** processor (like Apple Silicon, Apple M1, etc.) - add `:arm` tag at the end of the image name in the above commands.
- **WAHA Core** - `devlikeapro/whatsapp-http-api:arm`
- **WAHA Plus** - `devlikeapro/whatsapp-http-api-plus:arm`
You can also rename the image after you downloaded it with `docker tag` command,
so you can use the same image names in commands:
```bash
# Rename WAHA Core ARM
docker tag devlikeapro/whatsapp-http-api:arm devlikeapro/whatsapp-http-api
# Rename WAHA Plus ARM
docker tag devlikeapro/whatsapp-http-api-plus:arm devlikeapro/whatsapp-http-api-plus
```
## Update WAHA
{{< alert icon="💡" text="Please test all new versions in the development environment before updating production!" />}}
The commands are the same as for downloading, expected you need to restart **all working containers** after you download new image.
**WAHA Core** - download new image
```bash
# Download the image
docker pull devlikeapro/whatsapp-http-api
# Restart all containers
docker stop whatsapp-http-api
docker rm whatsapp-http-api
docker run -it --rm -p 3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
```
**WAHA Plus** - download new image (please use **the latest key** from Boosty or Patreon)
```bash
# Download the image
docker login -u devlikeapro -p {KEY}
docker pull devlikeapro/whatsapp-http-api-plus
docker logout
docker stop whatsapp-http-api
# Restart all containers
docker stop whatsapp-http-api
docker rm whatsapp-http-api
docker run -it --rm -p 3000:3000/tcp --name whatsapp-http-api devlikeapro/whatsapp-http-api
```
@@ -86,6 +86,7 @@ Here's available tiers for our supporters:
| Access to WAHA Plus ![](/images/versions/plus.png) docker image. | ✔️ | ✔️ | ✔️ |
| 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. | ➖️ | ➖ | ✔️ |
| Personal key to Docker Hub. | ➖️ | ➖ | ✔️ |
| **Donation** | **$19** | **$39** | **$99** |
For **$19** donation you get:
@@ -131,7 +132,7 @@ Go ahead and get your own WhatsApp HTTP API!
### Install Plus
After you get the password, use it to login and download docker image:
```bash
docker login -u devlikeapro -p {PASSWORD}
docker login -u devlikeapro -p {KEY}
docker pull devlikeapro/whatsapp-http-api-plus
docker logout
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -0,0 +1,219 @@
---
title: "Polls"
description: "How to send polls and receive votes"
lead: ""
date: 2020-10-06T08:48:45+00:00
lastmod: 2020-10-06T08:48:45+00:00
draft: false
images: [ ]
weight: 126
---
**Available on NOWEB engine only**.
Waiting for WEBJS engine to support it, and we'll add it ASAP!
![poll-example.jpg](poll-example.jpg)
## Endpoints
### Send poll
Use the endpoint to send a poll!
```bash
POST /api/sendPoll
```
The request body is pretty simple:
```json
{
"session": "default",
"chatId": "123123123@c.us",
"poll": {
"name": "How are you?",
"options": [
"Awesome!",
"Good!",
"Not bad!"
],
"multipleAnswers": false
}
}
```
The response you get back:
```json
{
"id": "true_321321321@c.us_83ACBAAAAAAAAAAAAAAAAAAAA",
"other-fields-here": "value"
}
```
You must save the `id` field from the response in your database so that you can identify the poll for which you receive
a vote (see webhook events below).
## Events
### poll.vote
With this event, you receive new votes for the poll sent.
#### Vote from a user in direct messages.
```json
{
"event": "poll.vote",
"session": "default",
"payload": {
"vote": {
"id": "false_1111111111@c.us_83ACBE602A05C79B234B54415E95EE8A",
"to": "me",
"from": "1111111@c.us",
"fromMe": false,
"selectedOptions": [
"Awesome!"
],
"timestamp": 1692861427
},
"poll": {
"id": "true_1111111111@c.us_BAE5F2EF5C69001E",
"to": "1111111111@c.us",
"from": "me",
"fromMe": true
}
},
"engine": "NOWEB"
}
```
#### Do I receive votes only for my polls?
No, you receive all votes. Keep in mind that you'll get all votes with this event, even from other polls. To identify
that it's your poll, look at the `poll.fromMe` field.
#### How to handle multiple-answer votes
For `multipleAnswers: true`, you receive the `selectedOptions` with all the selected values at a certain moment. So if a
user has chosen 3 options from the poll, you will receive **3** `poll.vote` events:
1. `selectedOptions: ["First"]`
2. `selectedOptions: ["First", "Second"]`
3. `selectedOptions: ["First", "Second", "Third"]`
#### Timestamp
If a user clicks on the poll multiple times, you will receive multiple `poll.vote` events. This is true for
both `multipleAnswers: false` (when a user changes their mind about answers) and `multipleAnswers: true` (when a user
selects two or more options) events.
There is a little chance that you may receive votes in the wrong order (due to the nature of HTTP and Webhooks),
like `1-3-2` instead of `1-2-3`. To determine the right order, look at the `timestamp` field. The event with a
higher `timestamp` value is more recent.
👉 It's important to save the `timestamp` for each vote in your database and compare them as numbers, without converting to
internal datetime. Right now, the `timestamp` shows the timestamp in seconds, but it may be changed to milliseconds in
the future.
#### Vote from a user in a group
```json
{
"event": "poll.vote",
"session": "default",
"payload": {
"vote": {
"id": "false_3333333333333@g.us_1C18A7EAADD2A8D0324755D241C4238A",
"to": "3333333333333@g.us",
"from": "1111111111@c.us",
"fromMe": false,
"selectedOptions": [
"Awesome!"
],
"timestamp": 1692861427
},
"poll": {
"id": "true_3333333333333@g.us_BAE5304BA1ECF704",
"to": "33333333333333@g.us",
"from": "222222222@c.us",
"fromMe": true
}
},
"engine": "NOWEB"
}
```
### poll.vote.failed
There may be cases when WAHA fails to decrypt a vote from the user. In such cases, you will receive
a `poll.vote.failed` event on your webhook.
The payload for `poll.vote.failed` is the same as for `poll.vote`, but with an empty list in `selectedOptions`.
```json
{
"event": "poll.vote.failed",
"session": "default",
"payload": {
"vote": {
"id": "false_11111111111@c.us_2E8C4CDA89EDE3BC0BC7F605364B8451",
"to": "me",
"from": "111111111@c.us",
"fromMe": false,
"selectedOptions": [],
"timestamp": 1692956972
},
"poll": {
"id": "true_1111111111@c.us_BAE595F4E0A2042C",
"to": "111111111@c.us",
"from": "me",
"fromMe": true
}
},
"engine": "NOWEB"
}
```
#### How should I handle poll.vote.failed events?
When you send a poll, save the poll configuration (question and options) in your database with the `id` field from the
response you received from `POST /api/sendPoll`.
Later, when you receive a `poll.vote.failed` event, find the `id` for the poll in the database and repeat the same
question to the user, apologizing for the inconvenience.
For example, you can say:
> Sorry, we don't understand your choice 😞
>
> Please click one more time on the message below 👇
After the user clicks on the poll again, you will receive a `poll.vote` event with their choice.
![handle-poll-vote-failed.jpg](handle-poll-vote-failed.jpg)
#### How to test poll.vote.failed events?
To receive `poll.vote.failed` events, follow these steps:
1. Start a session and authorize it with a QR code.
2. Send a poll to a chat.
3. Stop the session (logout is not required).
4. Start the session again.
5. Vote on the poll.
6. You will receive a `poll.vote.failed` event.
#### Why does the poll.vote.failed event occur and when will it be fixed?
The issue occurs because WAHA does not have a proper storage system, but polls require proper storage in order to
decrypt votes later.
There will be two fixes for this:
1. In the short term, a [local file storage](https://github.com/devlikeapro/whatsapp-http-api/issues/188) will be used
to save poll keys.
2. In the long term, work is being done
on [remote storages](https://github.com/devlikeapro/whatsapp-http-api/issues/41).
Even after these fixes are implemented, it's better to handle `poll.vote.failed` events anyway, so your application is
prepared for such cases!
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@@ -129,7 +129,8 @@ You can subscribe to presence information by calling `POST /api/{session}/presen
You can get later presence information for the chat with above `GET` endpoints or by listening to `presence.update`
webhook.
## Webhook
## Events
### presence.update
You can subscribe to `presence.update` webhook event to get the most recent presence information.
@@ -116,6 +116,9 @@ Fired on all message creations, including your own. The payload is the same as f
```
### message.ack
Receive events when server or recipient gets the message, read or played it.
[See details on Webhooks page ->]({{< relref "webhooks#messageack" >}}).
```json
{
@@ -25,7 +25,13 @@ Read more about [multiple sessions →]({{< relref "/docs/how-to/sessions" >}})
For phone number `12132132131` the `chatId` is `12132132131@c.us`.
- **Groups** use random number with `@g.us` at the end.
## Send text ![](/images/versions/core.png)
When sending media (images, voice, files) you can either use:
- `file.data` field with base64 encoded file (https://base64.guru/converter/encode/file)
- `file.url` field with public available URL for that file
## Endpoints
### Send text
To send text message - use `POST /api/sendText` with example payload.
```json
{
@@ -34,7 +40,7 @@ To send text message - use `POST /api/sendText` with example payload.
"text": "Hi there!"
}
```
### Mention contact
#### 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.
@@ -52,7 +58,12 @@ also mention it in `mentions` in format `2132132130@c.us`
}
```
## Reply on message ![](/images/versions/core.png)
### 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
{
@@ -63,10 +74,10 @@ 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.
#### 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.
## Add a reaction ![](/images/versions/core.png)
### Add a reaction
Use `PUT /api/reaction` method to set reaction to a message.
{{< alert icon="👉" text="Reaction API uses PUT, not POST request! Please make sure you send right request." />}}
@@ -88,14 +99,14 @@ To remove reaction from a message - send empty string in the reaction request.
}
```
## Send image ![](/images/versions/plus.png)
### Send image ![](/images/versions/plus.png)
Use `POST /api/sendImage` to send images.
You can send images in two ways:
1. Provide a URL for the image.
2. Encode the whole file content into base64 and send it in the request body.
### URL
#### URL
```json
{
"session": "default",
@@ -109,7 +120,7 @@ You can send images in two ways:
}
```
### Base64
#### Base64
```json
{
"session": "default",
@@ -123,14 +134,14 @@ You can send images in two ways:
}
```
## Send voice ![](/images/versions/plus.png)
### Send voice ![](/images/versions/plus.png)
Use `POST /api/sendVoice` to send voice messages.
You can send voice messages in two ways:
1. Provide a URL for the voice.
2. Encode the whole file content into base64 and send it in the request body.
### URL
#### URL
```json
{
"session": "default",
@@ -144,7 +155,7 @@ You can send voice messages in two ways:
}
```
### Base64
#### Base64
```json
{
"chatId": "11111111111@c.us",
@@ -156,14 +167,15 @@ You can send voice messages in two ways:
"session": "default"
}
```
## Send file ![](/images/versions/plus.png)
### Send file ![](/images/versions/plus.png)
Use `POST /api/sendFile` to send a file as a document.
You can send files in two ways:
1. Provide a URL for the file.
2. Encode the whole file content into base64 and send it in the request body.
### URL
#### URL
```json
{
"chatId": "11111111111@c.us",
@@ -176,7 +188,7 @@ You can send files in two ways:
}
```
### Base64
#### Base64
```json
{
"session": "default",
@@ -190,3 +202,73 @@ You can send files in two ways:
}
```
### Send status (aka stories)
With [NOWEB engine]({{< relref "/docs/how-to/engines" >}}) you can send statuses (aka stories)!
It is always necessary to inform the list of `contacts` that will have access to the posted status!
#### Send text status
`POST /api/{session}/status/text`
```json
{
"contacts": [
"55xxxxxxxxxxx@c.us"
],
"text": "Have a look! https://waha.devlike.pro/",
"backgroundColor": "#38b42f",
"font": 1
}
```
#### Send image status ![](/images/versions/plus.png)
`POST /api/{session}/status/image`
```json
{
"contacts": [
"55xxxxxxxxxxx@c.us"
],
"file": {
"mimetype": "image/jpeg",
"filename": "filename.jpeg",
"data": "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"
},
"caption": "string"
}
```
#### Send voice status ![](/images/versions/plus.png)
`POST /api/{session}/status/voice`
```json
{
"contacts": [
"55xxxxxxxxxxx@c.us"
],
"file": {
"mimetype": "audio/mp4",
"filename": "voice-message.mp3",
"data": "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 truncated
},
"backgroundColor": "#38b42f"
}
```
#### Send video status ![](/images/versions/plus.png)
`POST /api/{session}/status/video`
```json
{
"contacts": [
"55xxxxxxxxxxx@c.us"
],
"file": {
"mimetype": "audio/mp4",
"filename": "video-message.mp3",
"data": "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 truncated
},
"backgroundColor": "#38b42f"
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@@ -9,7 +9,7 @@ images: [ ]
weight: 125
---
## Endpoints
## Sessions
### Start
@@ -128,6 +128,60 @@ In order to log out the session - call `POST /api/sessions/logout`
}
```
### Get me
Get information about the associated account for that session (if any).
```bash
GET /api/sessions/{session}/me
```
**Authenticated and working** session's response:
```json
{
"id": "11111111111@c.us",
"pushName": "string"
}
```
**Stopped** or **not authenticated** session returns null:
```json
null
```
## Authentication
### Get QR
The simplest way to authenticate a new session - get QR code and scan it on your device.
```bash
GET /api/{session}/auth/qr
```
You'll get QR image that you can scan and get authenticated
### Get pairing code
{{< alert icon="👉" text="Available in **NOWEB** engine only." />}}
You can [link a session with phone number](https://faq.whatsapp.com/1324084875126592) - make a request to the endpoint.
```bash
POST /api/{session}/auth/request-code
```
Body example:
```json
{
"phoneNumber": "12132132130"
}
```
You'll get code in the response that you can use on your WhatsApp app to connect the session:
```json
{
"code": "ABCD-ABCD"
}
```
## Advanced sessions ![](/images/versions/plus.png)
@@ -190,14 +190,32 @@ Fired on all message creations, including your own. The payload is the same as f
```
### message.ack
Receive events when server or recipient gets the message, read or played it.
`ackName` field contains message status (`ack` has the same meaning, but show the value in int, but we keep it for backward compatability, they much to each other)
Possible message ack statuses:
- `ackName: ERROR, ack: -1`
- `ackName: PENDING, ack: 0`
- `ackName: SERVER, ack: 1`
- `ackName: DEVICE, ack: 2`
- `ackName: READ, ack: 3`
- `ackName: PLAYED, ack: 4`
The payload may have more fields, it depends on the engine you use, but here's a minimum amount that all engines send:
```json
{
"event": "message.ack",
"session": "default",
"engine": "WEBJS",
"payload": {
...
"id":"true_11111111111@c.us_4CC5EDD64BC22EBA6D639F2AF571346C",
"from":"11111111111@c.us",
"participant": null,
"fromMe":true,
"ack":3,
"ackName":"READ"
}
}
```
@@ -266,6 +284,60 @@ It's an internal engine's state, not **session** `status`.
}
```
### poll.vote
We have a dedicated page [how to send polls and receive votes]({{< relref "/docs/how-to/polls" >}})!
```json
{
"event": "poll.vote",
"session": "default",
"payload": {
"vote": {
"id": "false_1111111111@c.us_83ACBE602A05C79B234B54415E95EE8A",
"to": "me",
"from": "1111111@c.us",
"fromMe": false,
"selectedOptions": ["Awesome!"],
"timestamp": 1692861427
},
"poll": {
"id": "true_1111111111@c.us_BAE5F2EF5C69001E",
"to": "1111111111@c.us",
"from": "me",
"fromMe": true
}
},
"engine": "NOWEB"
}
```
### poll.vote.failed
We have a dedicated page [how to send polls and receive votes]({{< relref "/docs/how-to/polls" >}})!
```json
{
"event": "poll.vote.failed",
"session": "default",
"payload": {
"vote": {
"id": "false_11111111111@c.us_2E8C4CDA89EDE3BC0BC7F605364B8451",
"to": "me",
"from": "111111111@c.us",
"fromMe": false,
"selectedOptions": [],
"timestamp": 1692956972
},
"poll": {
"id": "true_1111111111@c.us_BAE595F4E0A2042C",
"to": "111111111@c.us",
"from": "me",
"fromMe": true
}
},
"engine": "NOWEB"
}
```
## Webhooks Advanced ![](/images/versions/plus.png)
### HMAC authentication
+26 -12
View File
@@ -1,7 +1,7 @@
---
title: "Changelog"
description: "WAHA's changelog"
lead: "Changelog"
lead: "You can find here the list of changes made to WAHA."
date: 2020-10-06T08:49:31+00:00
lastmod: 2020-10-06T08:49:31+00:00
draft: false
@@ -12,8 +12,26 @@ menu:
weight: 199
toc: true
---
If you wish to get and read Release Notes - read them on one of our platforms, **these posts are FREE**.
- [**Boosty ->**](https://boosty.to/wa-http-api)
- [**Patreon ->**](patreon.com/wa_http_api)
You even can **subscribe to get new updates** there!
## 2023.9
September 2023
- Add `GET /api/sessions/{session/me` endpoint so you [can get the phone number associated with the session](https://waha.devlike.pro/docs/how-to/sessions/#get-me)
- Add [polls support in NOWEB engine](https://waha.devlike.pro/docs/how-to/polls)
- Add dedicated [Get QR](https://waha.devlike.pro/docs/how-to/sessions/#get-qr) endpoint!
- Support [pairing method (NOWEB)](https://waha.devlike.pro/docs/how-to/sessions/#get-pairing-code) - you can connect with a code instead of QR.
- Add string field `ackName: DEVICE|READ|...` in [message.ack payload](https://waha.devlike.pro/docs/how-to/webhooks/#messageack)
- Support `message.ack` event webhook in **NOWEB** engine
## 2023.8
August 2023
- Added [stories (aka status) endpoints](https://waha.devlike.pro/docs/how-to/send-messages/#send-status-aka-stories) to **NOWEB** engine!
- `POST /api/{session}/status/text|image|voice|video` - choose one that suites your!
- Added [chats endpoints](https://waha.devlike.pro/docs/how-to/chats)
- Get all chats - `GET /api/{session/chats`
- Delete chat - `DELETE /api/{session/chats/{chatId}`
@@ -23,6 +41,7 @@ August 2023
- Added [set presence endpoint](https://waha.devlike.pro/docs/how-to/presence)!
- With `online` presence you can show your status as online [#121](https://github.com/devlikeapro/whatsapp-http-api/issues/121)
- With `offline` presence you can get notifications for your device [#28](https://github.com/devlikeapro/whatsapp-http-api/issues/28)
- Added security settings for edit group info `PUT /api/{session}/groups/{groupId}/settings/security/info-admin-only` - updates the group settings to only allow admins to edit group info (title, description, photo).
## 2023.7
July 2023
@@ -105,40 +124,35 @@ January 2023
December 2023
- Added `GET /messages/` endpoint to get chat messages [#31](https://github.com/devlikeapro/whatsapp-http-api/issues/31)
### Security ![](/images/versions/plus.png)
- Added `WHATSAPP_SWAGGER_USERNAME` and `WHATSAPP_SWAGGER_PASSWORD` to hide and protect swagger panel.
## 2022.11
**Please test changes in test environment before update production!!**
### Engine ![](/images/versions/core.png)
**Engine** ![](/images/versions/core.png)
1. WAHA has changed its underlying engine from Venom to Whatsapp Web.JS. It might change the response and webhook's
payloads.
2. Optimize CPU and memory consumption.
### Requests ![](/images/versions/core.png)
**Requests** ![](/images/versions/core.png)
- For all `/api/session/` requests use `name` field instead of `sessionName`.
- For all "chatting" requests use `session` field instead of `sessionName`.
### Sessions ![](/images/versions/plus.png)
**Sessions** ![](/images/versions/plus.png)
Now you don't have to scan QR code each time you run WAHA, WAHA saves it for you! Available only in Plus version.
### Authentication ![](/images/versions/plus.png)
**Authentication** ![](/images/versions/plus.png)
Now you can authenticate all requests for WAHA - use `WHATSAPP_API_KEY=secret` environment variable to set "secret key".
If `WHATSAPP_API_KEY` is set - requests must have `X-Api-Key` header with `secret` value, where `secret` - any random
secret key.
### Webhooks ![](/images/versions/core.png)
#### Configuration
**Webhooks** ![](/images/versions/core.png)
Instead of setting each webhook via environment variables - we use two environments variables:
@@ -149,7 +163,7 @@ Instead of setting each webhook via environment variables - we use two environme
|----------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
| <pre>WHATSAPP_HOOK_ONMESSAGE=https://httpbin.org/post <br>WHATSAPP_HOOK_ONANYMESSAGE=https://httpbin.org/post </pre> | <pre>WHATSAPP_HOOK_URL=https://httpbin.org/post <br>WHATSAPP_HOOK_EVENTS=message,message.any</pre> |
#### Payload
** Webhook payload**
The data for webhooks are wrapped inside a new `WAWebhook` object with `event` and `payload` fields to help you identify
which handler you should call based on `event`.
-20
View File
@@ -13,26 +13,6 @@ weight: 130
toc: true
---
## How to update?
{{< alert icon="💡" text="Please test all new versions in the development environment before updating production!" />}}
![](/images/versions/core.png) For **Core** version the command is
```bash
docker pull devlikeapro/whatsapp-http-api
```
![](/images/versions/plus.png) For **Plus** version, we use login to get the image before:
```bash
docker login -u devlikeapro -p {PASSWORD}
docker pull devlikeapro/whatsapp-http-api-plus
docker logout
```
Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-version" >}})
## How much CPU and Memory does WhatsApp HTTP API consume?
The benchmark has been made on **Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz**.
@@ -37,7 +37,6 @@ Here's the available features:
- Receive messages
- Message replies
- Send location
- Send buttons
- Receive location
- React to messages
- Send media (images/documents/files) ![](/images/versions/plus.png)
@@ -29,7 +29,7 @@ When you are ready - come back and follows the below steps to send the first tex
Docker makes it easy to ship all-in-one solution with the runtime and dependencies. You don't have to worry about
language-specific libraries or chrome installation.
Also Docker makes installation and update processes so simple, just one command!
Also, Docker makes installation and update processes so simple, just one command!
{{< /details >}}
## Send your first message
@@ -63,7 +63,7 @@ docker pull devlikeapro/whatsapp-http-api
![](/images/versions/plus.png) For Plus version, we use login to get the image before:
```bash
docker login -u devlikeapro -p {PASSWORD}
docker login -u devlikeapro -p {KEY}
docker pull devlikeapro/whatsapp-http-api-plus
docker logout
```
@@ -72,7 +72,7 @@ Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-ve
#### 1.1 Download image on ARM
If you're using ARM (like Apple Silicon, Apple M1, etc) - use following commands to download the image
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
@@ -84,12 +84,12 @@ docker tag devlikeapro/whatsapp-http-api:arm devlikeapro/whatsapp-http-api
![](/images/versions/plus.png) For Plus version the command is:
```bash
docker login -u devlikeapro -p {PASSWORD}
docker login -u devlikeapro -p {KEY}
docker pull devlikeapro/whatsapp-http-api-plus:arm
docker logout
# Rename it, so you can use devlikeapro/whatsapp-http-api image in other place
docker tag devlikeapro/whatsapp-http-api-plus:arm devlikeapro/whatsapp-http-api
docker tag devlikeapro/whatsapp-http-api-plus:arm devlikeapro/whatsapp-http-api-plus
```
### 2. Run WhatsApp HTTP API
@@ -185,5 +185,6 @@ curl -d "{\"chatId\": \"${PHONE}@c.us\", \"text\": \"Hello from WhatsApp HTTP AP
## What is next?
- Right now you've sent simple text message, but you can send more! [Send messages →]({{< relref "/docs/how-to/send-messages" >}})
- Read how to receive and response to messages [Receive messages →]({{< relref "/docs/how-to/receive-messages" >}})
- [Save session information, so you don't have to scan QR after container restart ->]({{< relref "/docs/how-to/sessions" >}})
- Make sure your [API is properly protected ->]({{< relref "/docs/how-to/security" >}})
+1 -1
View File
@@ -1 +1 @@
{"openapi":"3.0.0","paths":{"/api/sessions/start":{"post":{"operationId":"SessionsController_start","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStartRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDTO"}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/stop":{"post":{"operationId":"SessionsController_stop","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/SessionDTO"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/checkNumberStatus":{"get":{"operationId":"ChattingController_checkNumberStatus","summary":"Check number status","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WANumberExistResult"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcardRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendText":{"get":{"operationId":"ChattingController_sendTextGet","summary":"Send a text message","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"text","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]},"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WAMessage"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendTextButtons":{"post":{"operationId":"ChattingController_sendTextButtons","summary":"Send a text message with buttons","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextButtonsRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendLocation":{"post":{"operationId":"ChattingController_sendLocation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLocationRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendLinkPreview":{"post":{"operationId":"ChattingController_sendLinkPreview","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLinkPreviewRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendImage":{"post":{"operationId":"ChattingController_sendImage","summary":"Send an image. Either from an URL or base64 data - look at the request schemas for details.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageImageRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendFile":{"post":{"operationId":"ChattingControllLine truncated
{"openapi":"3.0.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","description":"WhatsApp session name","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/request-code":{"post":{"operationId":"AuthController_requestCode","summary":"Request authentication code. NOWEB and NOWEB_MOBILE engines only","parameters":[{"name":"session","required":true,"in":"path","description":"WhatsApp session name","schema":{"type":"string"}}],"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. NOWEB_MOBILE engine only","parameters":[{"name":"session","required":true,"in":"path","description":"WhatsApp session name","schema":{"type":"string"}}],"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/SessionDTO"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/checkNumberStatus":{"get":{"operationId":"ChattingController_checkNumberStatus","summary":"Check number status","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WANumberExistResult"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcardRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendText":{"get":{"operationId":"ChattingController_sendTextGet","summary":"Send a text message","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"text","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]},"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WAMessage"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendTextButtons":{"post":{"operationId":"ChattingController_sendTextButtons","summary":"Send a text message with buttons","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTextButtonsRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"taLine truncated
+1 -1
View File
@@ -28,7 +28,7 @@ docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bo
#### Download image - ARM
If you're using ARM (like Apple Silicon, Apple M1, etc) - use following commands to download the image
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
+1 -1
View File
@@ -82,7 +82,7 @@ def whatsapp_webhook():
# Send a text back via WhatsApp HTTP API
send_message(chat_id=chat_id, text=text)
# OR reply on the message
# reply(chat_id=chat_id, message_id=message_id, text=text)
reply(chat_id=chat_id, message_id=message_id, text=text)
# Send OK back
return "OK"
+3 -2
View File
@@ -38,6 +38,7 @@
"express-basic-auth": "^1.2.1",
"file-type": "16.5.4",
"https-proxy-agent": "^7.0.0",
"libphonenumber-js": "^1.10.36",
"link-preview-js": "^3.0.4",
"lodash": "^4.17.21",
"mime-types": "^2.1.27",
@@ -52,7 +53,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/elhumbertoz/whatsapp-web.js#patch-2"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
@@ -91,5 +92,5 @@
"coverageDirectory": "../coverage",
"testEnvironment": "node"
},
"packageManager": "yarn@3.6.0"
"packageManager": "yarn@3.6.3"
}
+67
View File
@@ -0,0 +1,67 @@
import { Body, Controller, Get, Post, Res } 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 { OTPRequest, RequestCodeRequest } from '../structures/auth.dto';
import { SessionApiParam, SessionParam } from './helpers';
import { WAHASessionStatus } from '../structures/enums.dto';
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
import { Readable } from 'stream';
import { Response } from 'express';
@ApiSecurity('api_key')
@Controller('api/:session/auth')
@ApiTags('auth')
class AuthController {
constructor(private manager: SessionManager) {}
@Get('qr')
@SessionApiParam
@ApiOperation({
summary: 'Get QR code for pairing WhatsApp Web.',
})
async getQR(@Res() res: Response, @SessionParam session: WhatsappSession) {
if (session.status != WAHASessionStatus.SCAN_QR_CODE) {
const err = `Can get QR code only in SCAN_QR_CODE status. The current status is '${session.status}'`;
throw new UnprocessableEntityException(err);
}
const buffer = await session.getQR();
const stream = new Readable();
stream.push(buffer);
stream.push(null);
res.set({
'Content-Type': 'image/png',
'Content-Length': buffer.length,
});
stream.pipe(res);
}
@Post('request-code')
@SessionApiParam
@ApiOperation({
summary: 'Request authentication code. NOWEB and NOWEB_MOBILE engines only',
})
requestCode(
@SessionParam session: WhatsappSession,
@Body() request: RequestCodeRequest,
) {
return session.requestCode(request.phoneNumber, request.method);
}
@Post('authorize-code')
@SessionApiParam
@ApiOperation({
summary: 'Send OTP authentication code. NOWEB_MOBILE engine only',
})
authorizeCode(
@SessionParam session: WhatsappSession,
@Body() request: OTPRequest,
) {
return session.authorizeCode(request.code);
}
}
export { AuthController };
+9 -6
View File
@@ -11,15 +11,16 @@ import {
MessageImageRequest,
MessageLinkPreviewRequest,
MessageLocationRequest,
MessagePollRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageTextButtonsRequest,
MessageTextQuery,
MessageTextRequest,
MessageVoiceRequest,
SendSeenRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
import { WAMessage } from '../structures/responses.dto';
@ApiSecurity('api_key')
@Controller('api')
@@ -59,11 +60,13 @@ export class ChattingController {
return whatsapp.sendText(request);
}
@Post('/sendTextButtons')
@ApiOperation({ summary: 'Send a text message with buttons' })
sendTextButtons(@Body() request: MessageTextButtonsRequest) {
@Post('/sendPoll')
@ApiOperation({
summary: 'Send a poll with options. You can use it as buttons replace',
})
sendPoll(@Body() request: MessagePollRequest) {
const whatsapp = this.manager.getSession(request.session);
return whatsapp.sendTextButtons(request);
return whatsapp.sendPoll(request);
}
@Post('/sendLocation')
+4 -2
View File
@@ -3,9 +3,11 @@ import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { SessionManager } from '../core/abc/manager.abc';
import { SessionQuery } from '../structures/base.dto';
import { CheckNumberStatusQuery } from '../structures/chatting.dto';
import {
CheckNumberStatusQuery,
WANumberExistResult,
} from '../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
import { WANumberExistResult } from '../structures/responses.dto';
@ApiSecurity('api_key')
@Controller('api/contacts')
+13
View File
@@ -49,6 +49,19 @@ export class GroupsController {
return session.getGroup(id);
}
@Put(':id/settings/security/info-admin-only')
@SessionApiParam
@ApiOperation({
summary:
'Updates the group settings to only allow admins to edit group info (title, description, photo).',
})
setGroupAdminOnly(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
) {
return session.setInfoAdminsOnly(id, true);
}
@Delete(':id')
@SessionApiParam
@ApiOperation({ summary: 'Delete the group.' })
+23 -2
View File
@@ -6,16 +6,21 @@ import { SessionManager } from '../core/abc/manager.abc';
import { parseBool } from '../helpers';
import {
ListSessionsQuery,
MeInfo,
SessionDTO,
SessionInfo,
SessionLogoutRequest,
SessionStartRequest,
SessionStopRequest,
} from '../structures/sessions.dto';
import { SessionApiParam, SessionParam } from './helpers';
import { WhatsappSession } from '../core/abc/session.abc';
import { WAHAChatPresences } from '../structures/presence.dto';
@ApiSecurity('api_key')
@Controller('api/sessions')
@ApiTags('sessions')
export class SessionsController {
class SessionsController {
constructor(private manager: SessionManager) {}
@Post('/start/')
@@ -45,8 +50,24 @@ export class SessionsController {
}
@Get('/')
async list(@Query() query: ListSessionsQuery): Promise<SessionDTO[]> {
async list(@Query() query: ListSessionsQuery): Promise<SessionInfo[]> {
const all = parseBool(query.all);
return this.manager.getSessions(all);
}
}
@ApiSecurity('api_key')
@Controller('api/sessions/:session')
@ApiTags('sessions')
class SessionController {
constructor(private manager: SessionManager) {}
@Get('me')
@SessionApiParam
@ApiOperation({ summary: 'Get information about the authenticated account' })
getMe(@SessionParam session: WhatsappSession): Promise<MeInfo> {
return session.getSessionMeInfo();
}
}
export { SessionController, SessionsController };
+69
View File
@@ -0,0 +1,69 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
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 { SessionApiParam, SessionParam } from './helpers';
import {
ImageStatus,
TextStatus,
VideoStatus,
VoiceStatus,
} from '../structures/status.dto';
@ApiSecurity('api_key')
@Controller('api/:session/status')
@ApiTags('status')
class StatusController {
constructor(private manager: SessionManager) {}
@Post('text')
@SessionApiParam
@ApiOperation({ summary: 'Send text status.' })
sendTextStatus(
@SessionParam session: WhatsappSession,
@Body() status: TextStatus,
) {
return session.sendTextStatus(status);
}
@Post('image')
@SessionApiParam
@ApiOperation({ summary: 'Send image status.' })
sendImageStatus(
@SessionParam session: WhatsappSession,
@Body() status: ImageStatus,
) {
return session.sendImageStatus(status);
}
@Post('voice')
@SessionApiParam
@ApiOperation({ summary: 'Send voice status.' })
sendVoiceStatus(
@SessionParam session: WhatsappSession,
@Body() status: VoiceStatus,
) {
return session.sendVoiceStatus(status);
}
@Post('video')
@SessionApiParam
@ApiOperation({ summary: 'Send video status.' })
sendVideoStatus(
@SessionParam session: WhatsappSession,
@Body() status: VideoStatus,
) {
return session.sendVideoStatus(status);
}
}
export { StatusController };
+1 -1
View File
@@ -3,7 +3,7 @@ import { ConfigService } from '@nestjs/config';
import { parseBool } from './helpers';
import { WAHAEngine } from './structures/enums.dto';
import { WebhookConfig } from './structures/webhooks.dto';
import { WebhookConfig } from './structures/webhooks.config.dto';
@Injectable()
export class WhatsappConfigService {
+2 -1
View File
@@ -3,6 +3,7 @@ import { OnApplicationShutdown } from '@nestjs/common';
import { WAHAEngine } from '../../structures/enums.dto';
import {
SessionDTO,
SessionInfo,
SessionLogoutRequest,
SessionStartRequest,
SessionStopRequest,
@@ -35,5 +36,5 @@ export abstract class SessionManager implements OnApplicationShutdown {
abstract getSession(name: string, error?: boolean): WhatsappSession;
abstract getSessions(all: boolean): Promise<SessionDTO[]>;
abstract getSessions(all: boolean): Promise<SessionInfo[]>;
}
+63 -3
View File
@@ -12,9 +12,9 @@ import {
MessageImageRequest,
MessageLinkPreviewRequest,
MessageLocationRequest,
MessagePollRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageTextButtonsRequest,
MessageTextRequest,
MessageVoiceRequest,
} from '../../structures/chatting.dto';
@@ -30,9 +30,20 @@ import {
ParticipantsRequest,
} from '../../structures/groups.dto';
import { WAHAChatPresences } from '../../structures/presence.dto';
import { ProxyConfig, SessionConfig } from '../../structures/sessions.dto';
import {
MeInfo,
ProxyConfig,
SessionConfig,
} from '../../structures/sessions.dto';
import { NotImplementedByEngineError } from '../exceptions';
import { LocalSessionStorage, MediaStorage } from './storage.abc';
import { OTPRequest, RequestCodeRequest } from '../../structures/auth.dto';
import {
ImageStatus,
TextStatus,
VideoStatus,
VoiceStatus,
} from '../../structures/status.dto';
const CHROME_PATH = '/usr/bin/google-chrome-stable';
const CHROMIUM_PATH = '/usr/bin/chromium';
@@ -141,15 +152,41 @@ export abstract class WhatsappSession {
/**
* START - Methods for API
*/
/**
* Auth methods
*/
public getQR(): Promise<Buffer> {
throw new NotImplementedByEngineError();
}
public requestCode(phoneNumber: string, method: string) {
throw new NotImplementedByEngineError();
}
public authorizeCode(code: string) {
throw new NotImplementedByEngineError();
}
abstract getScreenshot(): Promise<Buffer | string>;
public getSessionMeInfo(): Promise<MeInfo | null> {
throw new NotImplementedByEngineError();
}
/**
* Other methods
*/
abstract checkNumberStatus(request: CheckNumberStatusQuery);
abstract sendText(request: MessageTextRequest);
abstract sendContactVCard(request: MessageContactVcardRequest);
abstract sendTextButtons(request: MessageTextButtonsRequest);
sendPoll(request: MessagePollRequest) {
throw new NotImplementedByEngineError();
}
abstract sendLocation(request: MessageLocationRequest);
@@ -240,6 +277,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public setInfoAdminsOnly(id, value) {
throw new NotImplementedByEngineError();
}
public deleteGroup(id) {
throw new NotImplementedByEngineError();
}
@@ -300,6 +341,25 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
/**
* Status methods
*/
public sendTextStatus(status: TextStatus) {
throw new NotImplementedByEngineError();
}
public sendImageStatus(status: ImageStatus) {
throw new NotImplementedByEngineError();
}
public sendVoiceStatus(status: VoiceStatus) {
throw new NotImplementedByEngineError();
}
public sendVideoStatus(status: VideoStatus) {
throw new NotImplementedByEngineError();
}
/**
* END - Methods for API
*/
+1 -1
View File
@@ -1,6 +1,6 @@
import { ConsoleLogger } from '@nestjs/common';
import { WebhookConfig } from '../../structures/webhooks.dto';
import { WebhookConfig } from '../../structures/webhooks.config.dto';
import { WhatsappSession } from './session.abc';
export abstract class WebhookSender {
+9 -1
View File
@@ -8,12 +8,17 @@ import { ContactsController } from '../api/contacts.controller';
import { GroupsController } from '../api/groups.controller';
import { PresenceController } from '../api/presence.controller';
import { ScreenshotController } from '../api/screenshot.controller';
import { SessionsController } from '../api/sessions.controller';
import {
SessionController,
SessionsController,
} from '../api/sessions.controller';
import { VersionController } from '../api/version.controller';
import { WhatsappConfigService } from '../config.service';
import { SessionManager } from './abc/manager.abc';
import { SessionManagerCore } from './manager.core';
import { AuthController } from '../api/auth.controller';
import { ChatsController } from '../api/chats.controller';
import { StatusController } from '../api/status.controller';
export const IMPORTS = [
ConfigModule.forRoot({
@@ -35,9 +40,12 @@ export const IMPORTS = [
PassportModule,
];
export const CONTROLLERS = [
AuthController,
SessionsController,
SessionController,
ChattingController,
ChatsController,
StatusController,
ContactsController,
GroupsController,
PresenceController,
+6 -2
View File
@@ -10,11 +10,12 @@ import { WAHAEngine, WAHASessionStatus } from '../structures/enums.dto';
import {
ProxyConfig,
SessionDTO,
SessionInfo,
SessionLogoutRequest,
SessionStartRequest,
SessionStopRequest,
} from '../structures/sessions.dto';
import { WebhookConfig } from '../structures/webhooks.dto';
import { WebhookConfig } from '../structures/webhooks.config.dto';
import { SessionManager } from './abc/manager.abc';
import {
SessionParams,
@@ -191,7 +192,7 @@ export class SessionManagerCore extends SessionManager {
return session;
}
async getSessions(all: boolean): Promise<SessionDTO[]> {
async getSessions(all: boolean): Promise<SessionInfo[]> {
if (!this.session) {
if (!all) {
return [];
@@ -201,14 +202,17 @@ export class SessionManagerCore extends SessionManager {
name: this.DEFAULT,
status: WAHASessionStatus.STOPPED,
config: undefined,
me: null,
},
];
}
const me = await this.session.getSessionMeInfo().catch((err) => null);
return [
{
name: this.session.name,
status: this.session.status,
config: this.session.sessionConfig,
me: me,
},
];
}
+367 -43
View File
@@ -1,42 +1,53 @@
import makeWASocket, {
Browsers,
DisconnectReason,
getAggregateVotesInPollMessage,
getKeyAuthor,
isJidGroup,
jidNormalizedUser,
makeInMemoryStore,
PresenceData,
proto,
useMultiFileAuthState,
WAMessageContent,
WAMessageKey,
} from '@adiwajshing/baileys';
import { WABrowserDescription } from '@adiwajshing/baileys';
import { UnprocessableEntityException } from '@nestjs/common';
import { request } from 'express';
import * as fs from 'fs/promises';
import { Agent } from 'https';
import * as lodash from 'lodash';
import { update } from 'lodash';
import { PairingCodeResponse } from 'src/structures/auth.dto';
import { Message } from 'whatsapp-web.js';
import { flipObject } from '../helpers';
import { flipObject, splitAt } from '../helpers';
import {
Button,
ChatRequest,
CheckNumberStatusQuery,
GetMessageQuery,
MessageContactVcardRequest,
MessageDestination,
MessageFileRequest,
MessageImageRequest,
MessageLinkPreviewRequest,
MessageLocationRequest,
MessagePollRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageTextButtonsRequest,
MessageTextRequest,
MessageVoiceRequest,
SendSeenRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
import {
ACK_UNKNOWN,
SECOND,
WAHAEngine,
WAHAEvents,
WAHAPresenceStatus,
WAHASessionStatus,
WAMessageAck,
} from '../structures/enums.dto';
import {
CreateGroupRequest,
@@ -46,7 +57,13 @@ import {
WAHAChatPresences,
WAHAPresenceData,
} from '../structures/presence.dto';
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
import { WAMessage } from '../structures/responses.dto';
import { BROADCAST_ID, TextStatus } from '../structures/status.dto';
import {
PollVote,
PollVotePayload,
WAMessageAckBody,
} from '../structures/webhooks.dto';
import {
ensureSuffix,
WAHAInternalEvent,
@@ -58,6 +75,7 @@ import {
} from './exceptions';
import { createAgentProxy } from './helpers.proxy';
import { QR } from './QR';
import { MeInfo } from '../structures/sessions.dto';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
@@ -67,7 +85,9 @@ const logger = require('pino')();
export const BaileysEvents = {
CONNECTION_UPDATE: 'connection.update',
CREDS_UPDATE: 'creds.update',
MESSAGES_UPDATE: 'messages.update',
MESSAGES_UPSERT: 'messages.upsert',
MESSAGE_RECEIPT_UPDATE: 'message-receipt.update',
GROUPS_UPSERT: 'groups.upsert',
PRESENCE_UPDATE: 'presence.update',
};
@@ -103,9 +123,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
fetchAgent: agent,
auth: state,
printQRInTerminal: true,
browser: ['Linux', 'Chrome', '111.0.5563.64'] as WABrowserDescription,
browser: Browsers.macOS('Chrome'),
logger: logger,
mobile: false,
defaultQueryTimeoutMs: undefined,
getMessage: (key) => this.getMessage(key),
};
}
@@ -117,6 +139,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
const socketConfig = this.getSocketConfig(agent, state);
const sock: any = makeWASocket(socketConfig);
sock.ev.on(BaileysEvents.CREDS_UPDATE, saveCreds);
this.debugLogEnginesEvents(sock);
return sock;
}
@@ -128,9 +151,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
connectStore() {
this.log.debug(`Connecting store...`);
if (!this.store) {
this.log.debug(`Making a new auth store...`);
this.store = makeInMemoryStore({});
}
this.log.debug(`Binding store to socket...`);
this.store.bind(this.sock.ev);
}
@@ -143,6 +169,22 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
async buildClient() {
this.sock = await this.makeSocket();
this.connectStore();
this.listenConnectionEvents();
this.events.emit(WAHAInternalEvent.engine_start);
}
protected async getMessage(
key: WAMessageKey,
): Promise<WAMessageContent | undefined> {
if (!this.store) {
return proto.Message.fromObject({});
}
const msg = await this.store.loadMessage(key.remoteJid!, key.id!);
return msg?.message || undefined;
}
protected listenConnectionEvents() {
this.log.debug(`Start listening ${BaileysEvents.CONNECTION_UPDATE}...`);
this.sock.ev.on(BaileysEvents.CONNECTION_UPDATE, async (update) => {
const { connection, lastDisconnect, qr } = update;
if (connection === 'connecting') {
@@ -176,7 +218,19 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
});
}
});
this.events.emit(WAHAInternalEvent.engine_start);
}
protected debugLogEnginesEvents(sock) {
if (!process.env.DEBUG) {
return;
}
sock.ev.process((events) => {
this.log.debug('Engine events');
this.log.debug('=============');
console.log(JSON.stringify(events));
this.log.debug('=============');
});
}
stop() {
@@ -186,9 +240,58 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return;
}
async getSessionMeInfo(): Promise<MeInfo | null> {
const me = this.sock.authState?.creds?.me;
if (!me) {
return null;
}
const meId = jidNormalizedUser(me.id);
const meInfo: MeInfo = {
id: toCusFormat(meId),
pushName: me.name,
};
return meInfo;
}
/**
* START - Methods for API
*/
/**
* Auth methods
*/
public async getQR(): Promise<Buffer> {
return Promise.resolve(this.qr.get());
}
public async requestCode(
phoneNumber: string,
method: string,
): Promise<PairingCodeResponse> {
if (method) {
const err = `NOWEB engine doesn't support any 'method', remove it and try again`;
throw new UnprocessableEntityException(err);
}
if (this.status == WAHASessionStatus.STARTING) {
this.log.debug('Waiting for connection update...');
await this.sock.waitForConnectionUpdate((update) => !!update.qr);
}
if (this.status != WAHASessionStatus.SCAN_QR_CODE) {
const err = `Can request code only in SCAN_QR_CODE status. The current status is ${this.status}`;
throw new UnprocessableEntityException(err);
}
this.log.debug('Requesting pairing code...');
const code: string = await this.sock.requestPairingCode(phoneNumber);
// show it as ABCD-ABCD
const parts = splitAt(code, 4);
const codeRepr = parts.join('-');
this.log.debug(`Your code: ${codeRepr}`);
return { code: codeRepr };
}
async getScreenshot(): Promise<Buffer | string> {
if (this.status === WAHASessionStatus.STARTING) {
throw new UnprocessableEntityException(
@@ -205,6 +308,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
}
/**
* Other methods
*/
async checkNumberStatus(
request: CheckNumberStatusQuery,
): Promise<WANumberExistResult> {
@@ -225,22 +331,19 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.sock.sendMessage(chatId, message);
}
sendTextButtons(request: MessageTextButtonsRequest) {
const buttons = request.buttons.map((button: Button) => {
return {
buttonId: button.id,
buttonText: { displayText: button.text },
type: 1,
};
});
const buttonMessage = {
text: request.title,
buttons: buttons,
headerType: 1,
async sendPoll(request: MessagePollRequest) {
const requestPoll = request.poll;
const poll = {
name: requestPoll.name,
values: requestPoll.options,
selectableCount: requestPoll.multipleAnswers
? requestPoll.options.length
: 1,
};
return this.sock.sendMessage(request.chatId, buttonMessage);
const message = { poll: poll };
const remoteJid = toJID(request.chatId);
const result = await this.sock.sendMessage(remoteJid, message);
return this.toWAMessage(result);
}
sendContactVCard(request: MessageContactVcardRequest) {
@@ -448,27 +551,68 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return;
}
/**
* Status methods
*/
public sendTextStatus(status: TextStatus) {
const message = { text: status.text };
const options = {
backgroundColor: status.backgroundColor,
font: status.font,
statusJidList: status.contacts.map(toJID),
};
return this.sock.sendMessage(BROADCAST_ID, message, options);
}
/**
* END - Methods for API
*/
subscribe(event, handler) {
if (event === WAHAEvents.MESSAGE) {
return this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) =>
this.handleIncomingMessages(messages, handler, false),
);
this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) => {
this.handleIncomingMessages(messages, handler, false);
});
} else if (event === WAHAEvents.MESSAGE_ANY) {
return this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) =>
this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) =>
this.handleIncomingMessages(messages, handler, true),
);
} else if (event === WAHAEvents.MESSAGE_ACK) {
// Direct message ack
this.sock.ev.on(BaileysEvents.MESSAGES_UPDATE, (events) => {
events
.filter(isMine)
.filter(isAckUpdateMessageEvent)
.map(this.convertMessageUpdateToMessageAck)
.forEach(handler);
});
// Group message ack
this.sock.ev.on(BaileysEvents.MESSAGE_RECEIPT_UPDATE, (events) => {
events
.filter(isMine)
.map(this.convertMessageReceiptUpdateToMessageAck)
.forEach(handler);
});
} else if (event === WAHAEvents.STATE_CHANGE) {
return this.sock.ev.on(BaileysEvents.CONNECTION_UPDATE, handler);
this.sock.ev.on(BaileysEvents.CONNECTION_UPDATE, handler);
} else if (event === WAHAEvents.GROUP_JOIN) {
return this.sock.ev.on(BaileysEvents.GROUPS_UPSERT, handler);
this.sock.ev.on(BaileysEvents.GROUPS_UPSERT, handler);
} else if (event === WAHAEvents.PRESENCE_UPDATE) {
return this.sock.ev.on(BaileysEvents.PRESENCE_UPDATE, (data) =>
this.sock.ev.on(BaileysEvents.PRESENCE_UPDATE, (data) =>
handler(this.toWahaPresences(data.id, data.presences)),
);
} else if (event === WAHAEvents.POLL_VOTE) {
this.sock.ev.on(BaileysEvents.MESSAGES_UPDATE, (events) => {
events.forEach((event) =>
this.handleMessagesUpdatePollVote(event, handler),
);
});
} else if (event === WAHAEvents.POLL_VOTE_FAILED) {
this.sock.ev.on(BaileysEvents.MESSAGES_UPSERT, ({ messages }) => {
messages.forEach((message) =>
this.handleMessageUpsertPollVoteFailed(message, handler),
);
});
} else {
throw new NotImplementedByEngineError(
`Engine does not support webhook event: ${event}`,
@@ -481,6 +625,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
// if there is no text or media message
if (!message) return;
if (!message.message) return;
// Ignore poll votes, we have dedicated handler for that
if (message.message.pollUpdateMessage) return;
// Do not include my messages
if (!includeFromMe && message.key.fromMe) {
continue;
@@ -490,18 +636,17 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
private processIncomingMessage(message) {
return this.downloadMedia(message).then(this.toWAMessage);
return this.downloadMedia(message)
.then(this.toWAMessage)
.catch((error) => {
this.log.error('Failed to process incoming message');
this.log.error(error);
console.trace(error);
});
}
protected toWAMessage(message): Promise<WAMessage> {
const isGroupMessage = Boolean(message.key.participant);
let participant: string;
let to: string;
if (isGroupMessage) {
participant = message.key.participant;
to = message.key.remoteJid;
}
const from = message.key.remoteJid;
const fromToParticipant = getFromToParticipant(message);
const id = buildMessageId(message.key);
let body = message.message.conversation;
if (!body) {
@@ -512,23 +657,138 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return Promise.resolve({
id: id,
timestamp: message.messageTimestamp,
from: toCusFormat(from),
from: toCusFormat(fromToParticipant.from),
fromMe: message.key.fromMe,
body: body,
to: toCusFormat(to),
participant: toCusFormat(participant),
to: toCusFormat(fromToParticipant.to),
participant: toCusFormat(fromToParticipant.participant),
// @ts-ignore
hasMedia: Boolean(message.mediaUrl),
// @ts-ignore
mediaUrl: message.mediaUrl,
// @ts-ignore
ack: message.ack,
// @ts-ignore
ackName: WAMessageAck[message.ack] || ACK_UNKNOWN,
location: message.location,
vCards: message.vCards,
_data: message,
});
}
protected convertMessageUpdateToMessageAck(event): WAMessageAckBody {
const message = event;
const fromToParticipant = getFromToParticipant(message);
const id = buildMessageId(message.key);
const ack = message.update.status - 1;
const body: WAMessageAckBody = {
id: id,
from: toCusFormat(fromToParticipant.from),
to: toCusFormat(fromToParticipant.to),
participant: toCusFormat(fromToParticipant.participant),
fromMe: message.key.fromMe,
ack: ack,
ackName: WAMessageAck[ack] || ACK_UNKNOWN,
};
return body;
}
protected convertMessageReceiptUpdateToMessageAck(event): WAMessageAckBody {
const fromToParticipant = getFromToParticipant(event);
const id = buildMessageId(event.key);
const receipt = event.receipt;
let ack;
if (receipt.receiptTimestamp) {
ack = WAMessageAck.SERVER;
} else if (receipt.playedTimestamp) {
ack = WAMessageAck.PLAYED;
} else if (receipt.readTimestamp) {
ack = WAMessageAck.READ;
}
const body: WAMessageAckBody = {
id: id,
from: toCusFormat(fromToParticipant.from),
to: toCusFormat(fromToParticipant.to),
participant: toCusFormat(fromToParticipant.participant),
fromMe: event.key.fromMe,
ack: ack,
ackName: WAMessageAck[ack] || ACK_UNKNOWN,
};
return body;
}
protected async handleMessagesUpdatePollVote(event, handler) {
const { key, update } = event;
const pollUpdates = update?.pollUpdates;
if (!pollUpdates) {
return;
}
const pollCreationMessageKey = key;
const pollCreationMessage = await this.getMessage(key);
// Handle updates one by one, so we can get Vote Message for the specific vote
for (const pollUpdate of pollUpdates) {
const votes = getAggregateVotesInPollMessage({
message: pollCreationMessage,
pollUpdates: [pollUpdate],
});
// Get selected options for the author
const selectedOptions = [];
for (const voteAggregation of votes) {
for (const voter of voteAggregation.voters) {
if (voter === getKeyAuthor(pollUpdate.pollUpdateMessageKey)) {
selectedOptions.push(voteAggregation.name);
}
}
}
// Build payload and call the handler
const voteDestination = getDestination(pollUpdate.pollUpdateMessageKey);
const pollVote: PollVote = {
...voteDestination,
selectedOptions: selectedOptions,
timestamp: pollUpdate.senderTimestampMs,
};
const payload: PollVotePayload = {
vote: pollVote,
poll: getDestination(pollCreationMessageKey),
};
handler(payload);
}
}
protected async handleMessageUpsertPollVoteFailed(message, handler) {
const pollUpdateMessage = message.message?.pollUpdateMessage;
if (!pollUpdateMessage) {
return;
}
const pollCreationMessageKey = pollUpdateMessage.pollCreationMessageKey;
const pollCreationMessage = await this.getMessage(pollCreationMessageKey);
if (pollCreationMessage) {
// We found message, so later the engine will issue a message.update message
return;
}
// We didn't find the creation message, so send failed one
const pollUpdateMessageKey = message.key;
const voteDestination = getDestination(pollUpdateMessageKey);
const pollVote: PollVote = {
...voteDestination,
selectedOptions: [],
// change to below line when the PR merged, so we have the same timestamps
// https://github.com/WhiskeySockets/Baileys/pull/348
// Or without toNumber() - it depends on the PR above
// timestamp: pollUpdateMessage.senderTimestampMs.toNumber()
timestamp: message.messageTimestamp,
};
const payload: PollVotePayload = {
vote: pollVote,
poll: getDestination(pollCreationMessageKey),
};
handler(payload);
}
private toWahaPresences(
remoteJid,
storedPresences: PresenceData[],
@@ -574,6 +834,9 @@ function toCusFormat(remoteJid) {
if (!remoteJid) {
return;
}
if (remoteJid == 'me') {
return remoteJid;
}
const number = remoteJid.split('@')[0];
return ensureSuffix(number);
}
@@ -582,7 +845,7 @@ function toCusFormat(remoteJid) {
* Convert from 11111111111@c.us to 11111111111@s.whatsapp.net
* @param chatId
*/
function toJID(chatId) {
export function toJID(chatId) {
if (isJidGroup(chatId)) {
return chatId;
}
@@ -622,3 +885,64 @@ function parseMessageId(messageId) {
function getId(object) {
return object.id;
}
function isMine(message) {
return message?.key?.fromMe;
}
function isNotMine(message) {
return !message?.key?.fromMe;
}
function isAckUpdateMessageEvent(event) {
return event?.update.status != null;
}
function getFromToParticipant(message) {
const isGroupMessage = Boolean(message.key.participant);
let participant: string;
let to: string;
if (isGroupMessage) {
participant = message.key.participant;
to = message.key.remoteJid;
}
const from = message.key.remoteJid;
return {
from: from,
to: to,
participant: participant,
};
}
function getTo(key, meId = undefined) {
// For group - always to group JID
const isGroupMessage = Boolean(key.participant);
if (isGroupMessage) {
return key.remoteJid;
}
if (key.fromMe) {
return key.remoteJid;
}
return meId || 'me';
}
function getFrom(key, meId) {
// For group - always from participant
const isGroupMessage = Boolean(key.participant);
if (isGroupMessage) {
return key.participant;
}
if (key.fromMe) {
return meId || 'me';
}
return key.remoteJid;
}
function getDestination(key, meId = undefined): MessageDestination {
return {
id: buildMessageId(key),
to: toCusFormat(getTo(key, meId)),
from: toCusFormat(getFrom(key, meId)),
fromMe: key.fromMe,
};
}
+2 -20
View File
@@ -2,7 +2,6 @@ import { UnprocessableEntityException } from '@nestjs/common/exceptions/unproces
import { create, CreateConfig, Message, Whatsapp } from 'venom-bot';
import {
Button,
ChatRequest,
CheckNumberStatusQuery,
GetMessageQuery,
@@ -13,15 +12,15 @@ import {
MessageLocationRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageTextButtonsRequest,
MessageTextRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
import {
WAHAEngine,
WAHAEvents,
WAHASessionStatus,
} from '../structures/enums.dto';
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
import { WAMessage } from '../structures/responses.dto';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
import { NotImplementedByEngineError } from './exceptions';
import { QR } from './QR';
@@ -201,23 +200,6 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
return this.whatsapp.markMarkSeenMessage(chat.chatId);
}
sendTextButtons(request: MessageTextButtonsRequest) {
const buttons = request.buttons.map((button: Button) => {
return {
buttonId: button.id,
buttonText: {
displayText: button.text,
},
};
});
return this.whatsapp.sendButtons(
this.ensureSuffix(request.chatId),
request.title,
request.footer,
buttons,
);
}
startTyping(chat: ChatRequest) {
return this.whatsapp.startTyping(chat.chatId, false);
}
+63 -31
View File
@@ -1,6 +1,5 @@
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
import {
Buttons,
Chat,
Client,
ClientOptions,
@@ -13,7 +12,6 @@ import {
import { Message as MessageInstance } from 'whatsapp-web.js/src/structures';
import {
Button,
ChatRequest,
CheckNumberStatusQuery,
GetMessageQuery,
@@ -24,28 +22,36 @@ import {
MessageLocationRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageTextButtonsRequest,
MessageTextRequest,
MessageVoiceRequest,
SendSeenRequest,
WANumberExistResult,
} from '../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
import {
ACK_UNKNOWN,
WAHAEngine,
WAHAEvents,
WAHAPresenceStatus,
WAHASessionStatus,
WAMessageAck,
} from '../structures/enums.dto';
import {
CreateGroupRequest,
ParticipantsRequest,
} from '../structures/groups.dto';
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
import { WAMessage } from '../structures/responses.dto';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
import {
AvailableInPlusVersion,
NotImplementedByEngineError,
} from './exceptions';
import { QR } from './QR';
import { MeInfo } from '../structures/sessions.dto';
import { jidNormalizedUser } from '@adiwajshing/baileys';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const qrcode = require('qrcode-terminal');
@@ -54,6 +60,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
engine = WAHAEngine.WEBJS;
whatsapp: Client;
protected qr: QR;
public constructor(config) {
super(config);
this.qr = new QR();
}
protected buildClient() {
const clientOptions: ClientOptions = {
@@ -86,23 +98,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
async start() {
this.whatsapp = this.buildClient();
this.whatsapp.initialize().catch((error) => {
this.status = WAHASessionStatus.FAILED;
this.log.error(error);
return;
});
// Connect events
this.whatsapp.on(Events.QR_RECEIVED, (qr) => {
qrcode.generate(qr, { small: true });
this.status = WAHASessionStatus.SCAN_QR_CODE;
});
this.whatsapp.on(Events.AUTHENTICATED, () => {
this.status = WAHASessionStatus.WORKING;
this.log.log(`Session '${this.name}' has been authenticated!`);
});
this.listenConnectionEvents();
this.events.emit(WAHAInternalEvent.engine_start);
return this;
}
@@ -111,9 +112,49 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return this.whatsapp.destroy();
}
async getSessionMeInfo(): Promise<MeInfo | null> {
const clientInfo = this.whatsapp?.info;
if (!clientInfo) {
return null;
}
const wid = clientInfo.wid;
const meInfo: MeInfo = {
id: wid._serialized,
pushName: clientInfo.pushname,
};
return meInfo;
}
protected listenConnectionEvents() {
this.whatsapp.on(Events.QR_RECEIVED, (qr) => {
this.log.debug('QR received');
// Convert to image and save
QRCode.toDataURL(qr).then((url) => {
this.qr.save(url);
});
// Print in terminal
qrcode.generate(qr, { small: true });
this.status = WAHASessionStatus.SCAN_QR_CODE;
});
this.whatsapp.on(Events.AUTHENTICATED, () => {
this.status = WAHASessionStatus.WORKING;
this.qr.save('');
this.log.log(`Session '${this.name}' has been authenticated!`);
});
}
/**
* START - Methods for API
*/
/**
* Auth methods
*/
public async getQR(): Promise<Buffer> {
return Promise.resolve(this.qr.get());
}
async getScreenshot(): Promise<Buffer | string> {
if (this.status === WAHASessionStatus.FAILED) {
throw new UnprocessableEntityException(
@@ -144,20 +185,6 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
);
}
sendTextButtons(request: MessageTextButtonsRequest) {
const buttons = request.buttons.map((button: Button) => {
return {
id: button.id,
body: button.text,
};
});
const message = new Buttons('', buttons, request.title, request.footer);
return this.whatsapp.sendMessage(
this.ensureSuffix(request.chatId),
message,
);
}
sendContactVCard(request: MessageContactVcardRequest) {
throw new NotImplementedByEngineError();
}
@@ -311,6 +338,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return this.whatsapp.createGroup(request.name, participantIds);
}
public async setInfoAdminsOnly(id, value) {
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
return groupChat.setInfoAdminsOnly(value);
}
public getGroups() {
return this.whatsapp
.getChats()
@@ -473,8 +505,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
hasMedia: Boolean(message.mediaUrl),
// @ts-ignore
mediaUrl: message.mediaUrl,
// @ts-ignore
ack: message.ack,
ackName: WAMessageAck[message.ack] || ACK_UNKNOWN,
location: message.location,
vCards: message.vCards,
_data: message.rawData,
+12 -1
View File
@@ -39,11 +39,22 @@ class LocalSessionConfigRepository extends SessionConfigRepository {
return undefined;
}
const content = await fs.readFile(filepath, 'utf-8');
// Try to load config
let content;
try {
content = await fs.readFile(filepath, 'utf-8');
} catch (error) {
return undefined;
}
return JSON.parse(content);
}
async save(sessionName: string, config: SessionConfig) {
// 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);
+5
View File
@@ -15,8 +15,13 @@ export class SwaggerModuleCore {
.setExternalDoc('Documentation', 'https://waha.devlike.pro/')
.setVersion(VERSION.version)
.addTag('sessions', 'Control WhatsApp sessions')
.addTag('auth', 'Authentication')
.addTag('screenshot', 'Get screenshot of WhatsApp and show QR code')
.addTag('chatting', 'Chatting methods')
.addTag(
'status',
'Status (aka stories) methods. <b>NOWEB</b> engine only!',
)
.addTag('chats', `Chats methods`)
.addTag(
'contacts',
+2 -2
View File
@@ -1,12 +1,12 @@
import { ConsoleLogger } from '@nestjs/common';
import { WAHAEvents } from '../structures/enums.dto';
import { WAWebhook } from '../structures/responses.dto';
import { WebhookConfig } from '../structures/webhooks.dto';
import { WebhookConfig } from '../structures/webhooks.config.dto';
import { WhatsappSession } from './abc/session.abc';
import { WebhookConductor, WebhookSender } from './abc/webhooks.abc';
import { NotImplementedByEngineError } from './exceptions';
import request = require('request');
import { WAWebhook } from '../structures/webhooks.dto';
export class WebhookSenderCore extends WebhookSender {
send(json) {
+7 -1
View File
@@ -25,4 +25,10 @@ function flipObject(object) {
);
}
export { flipObject, parseBool };
function splitAt(str: string, index) {
const fst = [...str];
const snd = fst.splice(index);
return [fst.join(''), snd.join('')];
}
export { flipObject, parseBool, splitAt };
+24
View File
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
export class RequestCodeRequest {
@ApiProperty({
description: 'Mobile phone number in international format',
example: '12132132130',
})
phoneNumber: string;
@ApiProperty({
description:
'How would you like to receive the one time code for registration? |sms|voice. Leave empty for Web pairing.',
example: null,
required: false,
})
method: string;
}
export class OTPRequest {
code: string;
}
export class PairingCodeResponse {
code: string;
}
+42 -95
View File
@@ -2,6 +2,13 @@ import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
import { IsNumber, IsString } from 'class-validator';
import { SessionBaseRequest, SessionQuery } from './base.dto';
import {
BinaryFile,
RemoteFile,
VoiceBinaryFile,
VoiceRemoteFile,
} from './files.dto';
import { ChatIdProperty } from './properties.dto';
/**
* Queries
@@ -19,9 +26,7 @@ export class MessageTextQuery extends SessionQuery {
}
export class ChatQuery extends SessionQuery {
@ApiProperty({
example: '11111111111@c.us',
})
@ChatIdProperty()
chatId: string;
}
@@ -42,9 +47,7 @@ export class GetPresenceQuery extends ChatQuery {}
* Requests
*/
export class ChatRequest extends SessionBaseRequest {
@ApiProperty({
example: '11111111111@c.us',
})
@ChatIdProperty()
chatId: string;
}
@@ -89,17 +92,6 @@ export class MessageTextRequest extends ChatRequest {
mentions?: string[];
}
export class Button {
id: string;
text: string;
}
export class MessageTextButtonsRequest extends ChatRequest {
title: string;
footer?: string;
buttons?: Array<Button>;
}
export class MessageReplyRequest extends MessageTextRequest {
text = 'Reply text';
@ApiProperty({
@@ -114,47 +106,6 @@ export class MessageLocationRequest extends ChatRequest {
title: string;
}
export class RemoteFile {
@ApiProperty({
description: 'MIME type of the attachment.',
example: 'image/jpeg',
})
mimetype: string;
@ApiProperty({
description: 'Document file name. Value can be null',
example: 'filename.jpg',
})
filename?: string | null;
@ApiProperty({
example:
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.jpg',
})
url: string;
}
export class BinaryFile {
@ApiProperty({
description: 'MIME type of the attachment.',
example: 'image/jpeg',
})
mimetype: string;
@ApiProperty({
description: 'Document file name. Optional',
example: 'filename.jpeg',
})
filename?: string | null;
@ApiProperty({
description: 'Base64-encoded data of the file',
example:
'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',
})
data: string;
}
@ApiExtraModels(BinaryFile, RemoteFile)
class FileRequest extends ChatRequest {
@ApiProperty({
@@ -174,43 +125,6 @@ export class MessageFileRequest extends FileRequest {
caption: string;
}
export class VoiceBinaryFile {
@ApiProperty({
description: 'MIME type of the attachment.',
})
mimetype = 'audio/ogg; codecs=opus';
@ApiProperty({
description: 'Document file name. Optional',
})
filename = 'voice-message.mp3';
@ApiProperty({
description: 'Base64-encoded data of the file',
example:
'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 Line truncated
})
data: string;
}
export class VoiceRemoteFile {
@ApiProperty({
description: 'MIME type of the attachment.',
})
mimetype = 'audio/ogg; codecs=opus';
@ApiProperty({
description: 'Document file name. Optional',
})
filename = 'voice-message.mp3';
@ApiProperty({
example:
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.mp3',
})
url: string;
}
@ApiExtraModels(VoiceBinaryFile, VoiceRemoteFile)
export class MessageVoiceRequest extends ChatRequest {
@ApiProperty({
@@ -235,3 +149,36 @@ export class MessageReactionRequest extends MessageRequest {
})
reaction: string;
}
export class WANumberExistResult {
numberExists: boolean;
}
export class MessagePoll {
@ApiProperty({
example: 'How are you?',
})
name: string;
@ApiProperty({
example: ['Awesome!', 'Good!', 'Not bad!'],
})
options: string[];
multipleAnswers = false;
}
export class MessagePollRequest extends ChatRequest {
poll: MessagePoll;
}
export class MessageDestination {
@ApiProperty({
description: 'Message ID',
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
})
id: string;
to: string;
from: string;
fromMe: boolean;
}
+13
View File
@@ -8,6 +8,8 @@ export enum WAHAEvents {
GROUP_JOIN = 'group.join',
GROUP_LEAVE = 'group.leave',
PRESENCE_UPDATE = 'presence.update',
POLL_VOTE = 'poll.vote',
POLL_VOTE_FAILED = 'poll.vote.failed',
}
export enum WAHASessionStatus {
@@ -22,6 +24,7 @@ export enum WAHAEngine {
VENOM = 'VENOM',
WEBJS = 'WEBJS',
NOWEB = 'NOWEB',
NOWEB_MOBILE = 'NOWEB_MOBILE',
}
export enum WAHAPresenceStatus {
@@ -31,3 +34,13 @@ export enum WAHAPresenceStatus {
RECORDING = 'recording',
PAUSED = 'paused',
}
export enum WAMessageAck {
ERROR = -1,
PENDING = 0,
SERVER = 1,
DEVICE = 2,
READ = 3,
PLAYED = 4,
}
export const ACK_UNKNOWN = 'UNKNOWN';
+79
View File
@@ -0,0 +1,79 @@
import { ApiProperty } from '@nestjs/swagger';
export class RemoteFile {
@ApiProperty({
description: 'MIME type of the attachment.',
example: 'image/jpeg',
})
mimetype: string;
@ApiProperty({
description: 'Document file name. Value can be null',
example: 'filename.jpg',
})
filename?: string | null;
@ApiProperty({
example:
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.jpg',
})
url: string;
}
export class BinaryFile {
@ApiProperty({
description: 'MIME type of the attachment.',
example: 'image/jpeg',
})
mimetype: string;
@ApiProperty({
description: 'Document file name. Optional',
example: 'filename.jpeg',
})
filename?: string | null;
@ApiProperty({
description: 'Base64-encoded data of the file',
example:
'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',
})
data: string;
}
export class VoiceBinaryFile {
@ApiProperty({
description: 'MIME type of the attachment.',
})
mimetype = 'audio/ogg; codecs=opus';
@ApiProperty({
description: 'Document file name. Optional',
})
filename = 'voice-message.mp3';
@ApiProperty({
description: 'Base64-encoded data of the file',
example:
'SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA//NkxAAW0D4AVN4YAAYAKqTsSE2Z0IlkQMKANEGBUWH7a99t8eFgGQqfrfJxAsocfid8uizPyg535NQgTSs/z6xwJlJc/XKOE8uQtJsTP7ym4u/ky78nQzJw+t+4TvEiw/UftU4TymUVBiqj2qVLlF8X+bWGBLTBZi2+uOml4gA4HEVKHj2Fw89os529yuxD//NkxCcUAGI0FOJGKNWEHAhP3y5/+LZc5OU6X9RI+9M5IZc/fOaJf5y6cTjSBfLxrzjCnoSUCNUgnzokmHoBlzC6CGSrXGjEOPedHzBCkYcOBBpTTNe9/8tGfO5M4inBdS7PsvVusm91LKv4Ycftpwq4YATDG98/QyhKd3V7mZn7wZb2yrt3Y0fiGYpP4aUa//NkxFokCoIwNOpMRV7E1Kqm9vJ123c1F9bGVSKnasqqcezN/4ar26Uz3P8M/AgXq/+6ctsqX+fW6UmfCJ79o9g/CqpFrrpE78sS2V8ApIpklEE02r9II4IDbdNNnFC4dREiIgJFHHcOZjvE9lBMgBQUZzqdOmMDc97ku5pyFwNDph6+LaOyjKaRA6x4x2sc//NkxEwZkcZIVNpGCBophiL5tglY4Lj3qeQceTdr6ur/R11nlO/6KuHORu5SCMucIAApqsZ1I2CBCFxtWZ1tzE3pcvJD87eocViSFHDFuRXMs+q62dVX4O2GtELkoUrIfEbh7506+CEA0VUxhsBLvWABIfGtS8OCcALHMrWskk2pbXJuOEheYHvtaFW9arRY//NkxGgc+ZY8COJGVJsJX1dZg8q3QVzqEQCJapAB18VGV0Kcf+J8F8mNnEbyRRigCGBwCm4AQGjWWcBwIbSN5OhOjQgmU8f6ry3stZNsXzEDM1r44o0zCIuyJMgAejNzadpOkuIVTwXA/BS9ep5F8IvMiz9tM16cuR2SKoggy2MZQtg8BNAidnuNVpsFLpx5//NkxHcgEjJFdOGGtCyrFeW67L62epUITeB47MPJE/N7DOhVOlzQ3shzlhFM2gkw2MBQ0zxwHeTFmAKsdmTYnkf6VQlp4GCOOLPWCLF3sYHocCoKThOYp41Wo93UtOhKLnDyelr+f407RfQ0loWe5+a43SqraakgBnwG4POuQSELhVgoHwVB0OoeCgYj442U//NkxHkjKbZIyuZQTO/HTN86uNWKDHSjaBJ/RdqIU9N6adPSJcjBpAjd5ZQMRpirXYsZphSRhl2BiwAD9ZAHGGWBEzHAU3Ws11wH6d2fdF/sqG9P8pbEqpa2MNUcah+aizKGY8v4+WWNtiSTbbMmyf3K+U5Rez8xte6f9ZqzAIjDxVp4AEmjlm3KWLyoJw1r//NkxG8kSXpQMu5MWDLiYSGiEC1DUiwsLihhGVbL4DpupqbFV9eMCtpMfeuq2rdVeZVjIMHTE8nDqsWwcQxmk3BgCBxhAbhgGDBVBEP6XAIhRMwmTmYYwZ247Uc+cECwUeim6NFyz5cgE0SixFn+fd5Sb0kClO5up2jHtvXhqfSgK48ZRY+xgCaKhmJRI9lr//NkxGAhIT5UFO4MTFtqa6x7kuCp0aq5TakV0uFEJq1b3N9XYzKlRXJPaSjSxK16nqUZU4nVxqUFGJXeenWhgstGlJWAiyYfWYQCQYElQINEwKcFLFqIG1Zg8UvWrXmn1urARzCgoBTosSTkRcfBUVbsDXM62KmDEAkBJIYuNDHnGAAaYAyAwGwK6dYB7GPQ//NkxF4e4R5YXOMGiLWuWmL28y2xduEEPX16UzKUPFHrolYu8zXv879xhWqbVph6jGInGdqQwcFDlqOMrjUxIck9jAYAaslNTOm3mOyEoAq0pPsjKCa8QSZV7WmomOiijP1smCamch4MWEIfTcdiD/8MysS3MIj1h6NBhMoXeaAARIGjNLI4WOMDQOLS4+kR//NkxGUgaXZYVOGGlIXFBZoDP07hI3oN61pXNfrczNjykgKtFEu64tDaAKkkkkkkkiuSIFcvY9AuCM1psOiZD1e2RL5UFGSfVFOxKiSZcOB/rRbEIby4QS/l7HwSQ50PdnO5vF8uaYLg9T7o/0LGAVoE+AnnUhEqHqN+2EiZOousyjbkKCoGzIXFQGBGBcEg//NkxGY11Dq1vnpNhsAoeI90gZTMogfZUtHJiDaiLhMLEIFjgi/rrIXF+zNMlMwtIAFwQc+pTaGwtePD6+tl7j0comzEIh1EyQYLbHpcQ5SF7kZd1NIWQyNQKRC9JnDD6UFiJIGrIKBzkPl2Uq4cmamQz7+7tOxl7FtEH0h06g4OqqDJTBgSqEvrWRlEECjC//NkxBEhKYKUANPSnD4CG3gCwLkHoBMqTKUaUkraqCzctWV+m1bkb8u0ouTmawYa28AxqoQtKHeXrBv5cEpBP6IK/CcgM8Ey2hPrVRHqMT4cYXQFwbZAcIhcmTBOaMnhVz81zINiN///8sMPC3/7n/+qticPkhrRgq5w0OPcxplVBAMcSK16xyfBu6V5E4VO//NkxA8fQe7SVnsE3sqlEuhK9fjDZLxzi6SLBUEewl/scsTkeqdE9uFRUHGoaSs6r+2lvfUOiQ9kXdOx7B+1x2jPQvKC7RPl2PzZ7uzXe1f/5SkZoy2lzQ2FA6cWKCptRB0u3/6Vf+0SoKiglKox7AQEgJh8ISADd6fuBIAUaKk9cslsqjVbKmbz0PPY5Q1+//NkxBUfIaK01sPSlh5ZFGEiMvdwnWn2oMTLJTM2J8t0RhfRR2tLEUkrDGLOos4rOIYcCqAiKoiuom7r2xebLJIlTx44fjk7d8n83YRe957vMH/cwSKL6Ey973mSpMgGTlosv1ecaUut/hE0ILvvGRox6nk1AL2mPfUBUZaSvgWZbhpRdzWxrQ2qsfS4Qmi6//NkxBsgWWKwFsPMnLLjPbZm/+Hp/Fw44c4jYeLAJohxuQaL0ZST7Y3rG5bVb0kahY09mDWHbJTQ7lcxyAsgLkCImGpSeLFqjKWO+szt1WrMpSDqwjc80JbaLzItpImQmNBnf/t1/SBQZHiEsLNJUhlwbQoEgDWNNauCKdiupuITWqBhmGTVEDhJolUxTqPG//NkxBwc4Wq81nsMlh7aUSRnfU3mOSHKcXHggNr2WWu5xRtopJoCBLrRrL1WX2xrcpnD6LkCyUmR0yUWoJjCR77Sw9mBXULCT54Yu0Xrdb/hVS0I3/mTSw7/w6GyoBSMHjLXpqqgkAv1krkkRYNGdtnWV3uRCZjY3Di3rqbqSNhlzSWI00OhwAAcQvlyZaGo//NkxCsdK+rM9njE+zCBnEkZT8iKUmbJBEcUCMSNAxxiWZoqSN//tlV5mb7aKrPeulvuyohyLP////rv03ns6IrqtNN6p3LrKhFRn0akUwsm8ZkkyhudZqIlO/a1dASkgCFg25A5g4wH3DYgeONWRWR5F0rSuv35UUCWGdFkme7Wioowdlp+tKTOglxQIdDs//NkxDkcgZ7U/lmEllYr/sU44cWEhi+sCpF31D0rAKFKjD0tGAkIsfWlkkkieDodcZLB37qXxcNOSQIHaGhVNV6qqhR1SlSSUm1lRiYWROH6A2xUkPSoFND1ytKPu+bBqK93TKimC1i0iELlCdh2brUzdCKYEJBmO22uLhva2uLZ3EQvGqNqW2+KiYpnIZqf//NkxEodEoLEXmIE3uFdKU1ft6UWWu/NtOV2MFJBARFv915Fwi//ijKZ5QPtahDwbPvMOOPta9UJJad1sibIEcAnXUzOin78wDRaiSuCjubzcE4XF3jy82iLCgZdHNdXe0NttCoCkSCqQUsqtTX1rrQ0VUezN/9/F8WRyxaMdBFmkPGnrCA4XUpoG/xKGlVK//NkxFgdUa6w3nmQkoUGufspBpZEMpRb92LyRGYOuPLBUNETxFC+eZYI1QBiWo7bv9cbC4RBNgU2ZOzJlF2I6hncW0XFxQq5joOD4qV1k0mFqK+c0awzlTSR0s1t3Fwl6uZIKmCg7bWIdsjgSDKRpnRGr8vcxrIbKzUVl7316SOkxjs9CJen///Npyf///f///NkxGUc4869vkoEv/MjM0zQT+PLHf03+FAppQiO26Si0wExQTgQZNKKhYVXL/KdqhblIpuWqJEkZJao2zpmnQJzwo/KzF2SsevxgrWgg8ogPCHJC+ZGYI2ADMGCNweE3kigIuDQFGDEH4qBwcUYUz+bXbe4AMoPu/7s24QUHaPJvozFL7kkmPLhgUeLCPpU//NkxHQdQYKAPsmGkBUB40UDw1eB2YYqrAq9/o+y1xKjtHtsXBr0nSf2Vwmm+QRJlXz1m1IVzyOFGEgDDGZVaz3eRW2U4RFBamZ93qsjBtD5znsr2aIZnVX2RKpDvajaEnvzn37TvfX9f/faT/t///8i6rX3INPKHWGrJkprEf6xqTGg6UAvtPBqpnPTuNw1//NkxIIcA758FspEWAuYStUOFnAwRayKry+iKyESNAqfXOu5qY5aIXsruZdM/Z6b1jIKxB4uh2+ai4eSFYcK4fVqaeiZnNyp575zQoX5/c34fn8L/0fInJ48l+F09KZX4/8eN+rndzx47njOiPmj3Nd6SOQRWBYTMnDxEcIuFllGECkpxosYIVIcSiVJ51w+//NkxJUf9DqIFMGGMBolGBvMSYphcBJDXSmhMFFUsV27MeGBQHxCBzaaMCtwETeeCALA0JwkK1nBt2pRB7HrBckAWrpF4kJn0LYWQwa0URuPUN5l7LLlrWrvMhMuPBVwCCIFDLUqLslhZQTOEZoFjKZSQq07QHBQoX0DkWRTrgWmFVJSeW5NkVpKKXrclWbD//NkxJgcYPqSNoGGDBXTKSmTqRYo2Geff0vxs/Ve9dGEwGFWSYiWdaxBoqdDLZ+WPRaFSICcLmgghZVzUDgKz1IZYOfD3YlyEq1PNCo0XEoCUwGh0Ep0TLyRYDDhRQUDKSXe Line truncated
})
data: string;
}
export class VoiceRemoteFile {
@ApiProperty({
description: 'MIME type of the attachment.',
})
mimetype = 'audio/ogg; codecs=opus';
@ApiProperty({
description: 'Document file name. Optional',
})
filename = 'voice-message.mp3';
@ApiProperty({
example:
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.mp3',
})
url: string;
}
+18
View File
@@ -0,0 +1,18 @@
import { ApiProperty } from '@nestjs/swagger';
function ChatIdProperty(options = undefined) {
options = options || {};
if (!options.example) {
options.example = '11111111111@c.us';
}
return ApiProperty(options);
}
function MessageIdProperty() {
return ApiProperty({
description: 'Message ID',
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
});
}
export { ChatIdProperty, MessageIdProperty };
+10 -61
View File
@@ -1,52 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { WAHAEngine, WAHAEvents } from './enums.dto';
export class WANumberExistResult {
numberExists: boolean;
}
export class WAGroupNotification {
@ApiProperty({
description: 'ID that represents the groupNotification',
})
id: any;
@ApiProperty({
description: 'Unix timestamp for when the groupNotification was created',
})
timestamp: number;
@ApiProperty({
description: 'ID for the Chat that this groupNotification was sent for',
})
chatId: string;
@ApiProperty({
description: 'ContactId for the user that produced the GroupNotification',
})
author: string;
@ApiProperty({
description: 'Extra content',
})
body: string;
@ApiProperty({
description:
'Contact IDs for the users that were affected by this GroupNotification',
})
recipientIds: string[];
}
export enum WAMessageAck {
ACK_ERROR = -1,
ACK_PENDING = 0,
ACK_SERVER = 1,
ACK_DEVICE = 2,
ACK_READ = 3,
ACK_PLAYED = 4,
}
import { WAMessageAck } from './enums.dto';
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
export class WALocation {
description?: string | null;
@@ -55,10 +10,7 @@ export class WALocation {
}
export class WAMessage {
@ApiProperty({
description: 'Message ID',
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
})
@MessageIdProperty()
id: string;
/** */
@@ -68,7 +20,7 @@ export class WAMessage {
})
timestamp: number;
@ApiProperty({
@ChatIdProperty({
description:
'ID for the Chat that this message was sent to, except if the message was sent by the current user ',
})
@@ -79,7 +31,7 @@ export class WAMessage {
})
fromMe: boolean;
@ApiProperty({
@ChatIdProperty({
description: `
* ID for who this message is for.
* If the message is sent by the current user, it will be the Chat to which the message is being sent.
@@ -115,6 +67,11 @@ export class WAMessage {
})
ack: WAMessageAck;
@ApiProperty({
description: 'ACK status name for the message',
})
ackName: string;
@ApiProperty({
description:
'If the message was sent to a group, this field will contain the user that sent the message.',
@@ -139,11 +96,3 @@ export class WAMessage {
})
_data?: any;
}
export class WAWebhook {
event: WAHAEvents;
session: string;
engine: WAHAEngine;
// eslint-disable-next-line @typescript-eslint/ban-types
payload: WAMessage | WAGroupNotification | object;
}
+12 -1
View File
@@ -1,7 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { WAHASessionStatus } from './enums.dto';
import { WebhookConfig } from './webhooks.dto';
import { WebhookConfig } from './webhooks.config.dto';
import { ChatIdProperty } from './properties.dto';
/**
* Queries
@@ -66,3 +67,13 @@ export class SessionDTO {
status: WAHASessionStatus;
config: SessionConfig;
}
export class MeInfo {
@ChatIdProperty()
id: string;
pushName: string;
}
export class SessionInfo extends SessionDTO {
me: MeInfo | null;
}
+60
View File
@@ -0,0 +1,60 @@
import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
import {
BinaryFile,
RemoteFile,
VoiceBinaryFile,
VoiceRemoteFile,
} from './files.dto';
export const BROADCAST_ID = 'status@broadcast';
class StatusRequest {
@ApiProperty({
description:
'it is always necessary to inform the list of contacts that will have access to the posted status',
example: ['55xxxxxxxxxxx@c.us'],
})
contacts = ['55xxxxxxxxxxx@c.us'];
}
export class TextStatus extends StatusRequest {
text = 'Have a look! https://waha.devlike.pro/';
backgroundColor = '#38b42f';
font = 1;
}
@ApiExtraModels(BinaryFile, RemoteFile)
export class ImageStatus extends StatusRequest {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(BinaryFile) },
{ $ref: getSchemaPath(RemoteFile) },
],
})
file: BinaryFile | RemoteFile;
caption: string;
}
@ApiExtraModels(VoiceBinaryFile, VoiceRemoteFile)
export class VoiceStatus extends StatusRequest {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(VoiceBinaryFile) },
{ $ref: getSchemaPath(VoiceRemoteFile) },
],
})
file: VoiceBinaryFile | VoiceRemoteFile;
backgroundColor = '#38b42f';
}
@ApiExtraModels(BinaryFile, RemoteFile)
export class VideoStatus extends StatusRequest {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(BinaryFile) },
{ $ref: getSchemaPath(RemoteFile) },
],
})
file: BinaryFile | RemoteFile;
caption: string;
}
+60
View File
@@ -0,0 +1,60 @@
import { ApiProperty } from '@nestjs/swagger';
export class RetriesConfiguration {
@ApiProperty({
example: 2,
})
delaySeconds: number;
@ApiProperty({
example: 15,
})
attempts: number;
}
export class CustomHeader {
@ApiProperty({
example: 'X-My-Custom-Header',
})
name: string;
@ApiProperty({
example: 'Value',
})
value: string;
}
export class HmacConfiguration {
@ApiProperty({
example: 'your-secret-key',
})
key: string;
}
export class WebhookConfig {
@ApiProperty({
example: 'https://httpbin.org/post',
required: true,
})
url: string;
@ApiProperty({
example: ['message'],
required: true,
})
events: string[];
@ApiProperty({
example: null,
})
hmac?: HmacConfiguration;
@ApiProperty({
example: null,
})
retries?: RetriesConfiguration;
@ApiProperty({
example: null,
})
customHeaders?: CustomHeader[];
}
+71 -42
View File
@@ -1,60 +1,89 @@
import { ApiProperty } from '@nestjs/swagger';
export class RetriesConfiguration {
@ApiProperty({
example: 2,
})
delaySeconds: number;
import { WAHAEngine, WAHAEvents, WAMessageAck } from './enums.dto';
import { WAMessage } from './responses.dto';
import { MessageDestination } from './chatting.dto';
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
@ApiProperty({
example: 15,
})
attempts: number;
}
export class CustomHeader {
@ApiProperty({
example: 'X-My-Custom-Header',
})
name: string;
export class WAMessageAckBody {
@MessageIdProperty()
id: string;
@ApiProperty({
example: 'Value',
})
value: string;
@ChatIdProperty()
from: string;
@ChatIdProperty()
to: string;
@ChatIdProperty()
participant: string;
fromMe: boolean;
ack: WAMessageAck;
ackName: string;
}
export class HmacConfiguration {
export class WAGroupPayload {
@ApiProperty({
example: 'your-secret-key',
description: 'ID that represents the groupNotification',
})
key: string;
id: any;
@ApiProperty({
description: 'Unix timestamp for when the groupNotification was created',
})
timestamp: number;
@ChatIdProperty({
description: 'ID for the Chat that this groupNotification was sent for',
})
chatId: string;
@ApiProperty({
description: 'ContactId for the user that produced the GroupNotification',
})
author: string;
@ApiProperty({
description: 'Extra content',
})
body: string;
@ApiProperty({
description:
'Contact IDs for the users that were affected by this GroupNotification',
})
recipientIds: string[];
}
export class WebhookConfig {
export class PollVote extends MessageDestination {
@ApiProperty({
example: 'https://httpbin.org/post',
required: true,
description: 'Option that user has selected',
example: ['Awesome!'],
})
url: string;
selectedOptions: string[];
@ApiProperty({
example: ['message'],
required: true,
description: 'Timestamp, ms',
example: 1692861369,
})
events: string[];
timestamp: number;
}
@ApiProperty({
example: null,
})
hmac?: HmacConfiguration;
export class PollVotePayload {
vote: PollVote;
poll: MessageDestination;
}
@ApiProperty({
example: null,
})
retries?: RetriesConfiguration;
@ApiProperty({
example: null,
})
customHeaders?: CustomHeader[];
export class WAWebhook {
event: WAHAEvents;
session: string;
engine: WAHAEngine;
payload:
| WAMessage
| WAGroupPayload
| WAMessageAckBody
| PollVotePayload
// eslint-disable-next-line @typescript-eslint/ban-types
| object;
}
+1 -1
View File
@@ -26,7 +26,7 @@ export function getWAHAVersion(): WAHAVersion {
}
export const VERSION = {
version: '2023.7.16',
version: '2023.9.7',
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
};
+1529 -1407
View File
File diff suppressed because it is too large. Load diff