Compare commits
65
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41b29cbf60 | ||
|
|
44463c199a | ||
|
|
9071adcdcc | ||
|
|
21cd92ca13 | ||
|
|
d8a3e8dc43 | ||
|
|
3f5707952c | ||
|
|
38a9590be0 | ||
|
|
a7ce117b87 | ||
|
|
c3b24a243c | ||
|
|
cb2c11df43 | ||
|
|
98f88dcf68 | ||
|
|
9fb12e0e23 | ||
|
|
0ca106f16b | ||
|
|
33210c6374 | ||
|
|
045ab67f5d | ||
|
|
19259c93ea | ||
|
|
9b6784a018 | ||
|
|
2fe3706e9c | ||
|
|
a4e310a7f8 | ||
|
|
802da9f3b2 | ||
|
|
0692979b19 | ||
|
|
fb8b146bc4 | ||
|
|
fdbaa43ec9 | ||
|
|
4a807530c1 | ||
|
|
84784faa1e | ||
|
|
bd40978dee | ||
|
|
d4d94ffe0e | ||
|
|
d984382a33 | ||
|
|
02d05812d2 | ||
|
|
1dbf568bac | ||
|
|
6bc3aed348 | ||
|
|
c1e8e0f09c | ||
|
|
8f8bcfc474 | ||
|
|
b386df982d | ||
|
|
fa846ddf9b | ||
|
|
16685ed8ea | ||
|
|
4addccbd55 | ||
|
|
e9a20c4326 | ||
|
|
f073aeef13 | ||
|
|
eb08739eea | ||
|
|
aff3b01e39 | ||
|
|
2d64b26e86 | ||
|
|
7b7bd02503 | ||
|
|
7904516c97 | ||
|
|
6e0bafdd89 | ||
|
|
2b1e605e5f | ||
|
|
f454310a09 | ||
|
|
b6579b5b96 | ||
|
|
4ef06d7b92 | ||
|
|
e0c594a71b | ||
|
|
a48d386d8b | ||
|
|
d592858a90 | ||
|
|
a3cb3e983a | ||
|
|
4eb9029b2c | ||
|
|
b3835b0aa0 | ||
|
|
b2db58a922 | ||
|
|
acb6b8e0d3 | ||
|
|
21c6e1e860 | ||
|
|
571b65c423 | ||
|
|
1631a62abe | ||
|
|
e88a7ca8ce | ||
|
|
d54f64a9cc | ||
|
|
03dbd5db82 | ||
|
|
2446c292bf | ||
|
|
5ab99dfd69 |
No files matched your search
+25
-22
@@ -1,33 +1,36 @@
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
sourceType: "module"
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: [
|
||||
"@typescript-eslint/eslint-plugin",
|
||||
"simple-import-sort"
|
||||
],
|
||||
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort'],
|
||||
extends: [
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true
|
||||
jest: true,
|
||||
},
|
||||
rules: {
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
}
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'lines-between-class-members': [
|
||||
'error',
|
||||
'always',
|
||||
{ exceptAfterSingleLine: true },
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: '[BUG][WEBJS] - '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
> A clear and concise description of what the bug is. Feel free to remove
|
||||
> sections that you don't feel to make the text shorter!
|
||||
|
||||
**Version**
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2023.11.1",
|
||||
"engine": "NOWEB",
|
||||
"tier": "PLUS",
|
||||
"browser": "/usr/bin/google-chrome-stable"
|
||||
}
|
||||
```
|
||||
|
||||
> You can get the WAHA version by calling `GET /api/version` Try to update to
|
||||
> [the latest version](https://github.com/devlikeapro/whatsapp-http-api/releases)
|
||||
> before creating an issue!
|
||||
|
||||
**To Reproduce** Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
> A clear and concise description of what you expected to happen.
|
||||
|
||||
**Engine** I'm using **WEBJS** engine.
|
||||
|
||||
> If you set `WHATSAPP_DEFAULT_ENGINE` explicitly to
|
||||
> [one of the engine](https://waha.devlike.pro/docs/how-to/engines/) - please
|
||||
> provide that information here and in the issue title.
|
||||
|
||||
**Requests - Responses**
|
||||
|
||||
> Please attach all sent requests, responses that you sent.
|
||||
|
||||
**Docker Logs**
|
||||
|
||||
> Collect and attach related docker logs if you have any
|
||||
> https://waha.devlike.pro/docs/how-to/deploy/#viewing-the-logs
|
||||
|
||||
**Screenshots**
|
||||
|
||||
> If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
|
||||
> Add any other context about the problem here.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: '[Feature Request]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.** I'm always
|
||||
frustrated when [...]
|
||||
|
||||
> A clear and concise description of what the problem is. Ex.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
> A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
|
||||
> A clear and concise description of any alternative solutions or features
|
||||
> you've considered.
|
||||
|
||||
**Additional context** Add any other context or screenshots about the feature
|
||||
request here.
|
||||
@@ -3,11 +3,43 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
docker-amd64:
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
docker-build:
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.platform }} - ${{ matrix.browser }} - ${{ matrix.tag }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Chromium
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'latest'
|
||||
platform: 'amd64'
|
||||
browser: 'chromium'
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
|
||||
tag: 'arm'
|
||||
platform: 'linux/arm64'
|
||||
browser: 'chromium'
|
||||
# Chrome
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'chrome'
|
||||
platform: 'amd64'
|
||||
browser: 'chrome'
|
||||
# Chrome is not supported on arm64
|
||||
# - runner: "buildjet-4vcpu-ubuntu-2204-arm"
|
||||
# tag: "chrome-arm"
|
||||
# platform: "linux/arm64"
|
||||
# browser: "chrome"
|
||||
# No browser
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'noweb'
|
||||
platform: 'amd64'
|
||||
browser: 'none'
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
|
||||
tag: 'noweb-arm'
|
||||
platform: 'linux/arm64'
|
||||
browser: 'none'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -22,7 +54,7 @@ jobs:
|
||||
images: ${{ secrets.DOCKER_IMAGE }}
|
||||
flavor: latest=false
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=raw,value=${{ matrix.tag }}
|
||||
|
||||
- name: Login to image repository
|
||||
if: github.ref_type == 'tag'
|
||||
@@ -36,42 +68,9 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: 'amd64'
|
||||
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'
|
||||
platforms: ${{ matrix.platform }}
|
||||
build-args: |
|
||||
USE_BROWSER=${{ matrix.browser }}
|
||||
push: ${{ github.ref_type == 'tag' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
+16
-4
@@ -1,13 +1,25 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: 'v2.7.1'
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [ javascript, ts ]
|
||||
- id: lint
|
||||
name: lint
|
||||
language: system
|
||||
entry: bash -c '$HOME/.nvm/nvm-exec npm run lint'
|
||||
pass_filenames: false
|
||||
files: |
|
||||
(?x)^(
|
||||
src/.*|
|
||||
)$
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: 'v3.1.0'
|
||||
hooks:
|
||||
- id: prettier
|
||||
exclude: |
|
||||
(?x)^(
|
||||
docs/.*|
|
||||
README.md|
|
||||
)$
|
||||
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
|
||||
rev: v1.1.2
|
||||
hooks:
|
||||
|
||||
+11
-2
@@ -1,4 +1,13 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "auto",
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"quoteProps": "as-needed",
|
||||
"proseWrap": "always",
|
||||
"printWidth": 80
|
||||
}
|
||||
+3
-1
@@ -1,2 +1,4 @@
|
||||
# Changelog
|
||||
You can find changelog on [the changelog page](https://waha.devlike.pro/docs/help/changelog/)
|
||||
|
||||
You can find changelog on
|
||||
[the changelog page](https://waha.devlike.pro/docs/help/changelog/)
|
||||
+7
-3
@@ -24,11 +24,15 @@ FROM node:20-bullseye as release
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
ARG USE_BROWSER=chromium
|
||||
|
||||
# Install fonts
|
||||
RUN apt-get update \
|
||||
RUN echo "USE_BROWSER=$USE_BROWSER"
|
||||
|
||||
# Install fonts if using either chromium or chrome
|
||||
RUN if [ "$USE_BROWSER" = "chromium" ] || [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
apt-get update \
|
||||
&& apt-get install -y fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Install Chromium
|
||||
RUN if [ "$USE_BROWSER" = "chromium" ]; then \
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
build:
|
||||
docker build . -t devlikeapro/whatsapp-http-api
|
||||
|
||||
build-chrome:
|
||||
docker build . -t devlikeapro/whatsapp-http-api:chrome --build-arg USE_BROWSER=chrome
|
||||
|
||||
build-noweb:
|
||||
docker build . -t devlikeapro/whatsapp-http-api:noweb --build-arg USE_BROWSER=none
|
||||
|
||||
build-all: build build-chrome build-noweb
|
||||
|
||||
build-plus:
|
||||
docker build . -t devlikeapro/whatsapp-http-api-plus
|
||||
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ services:
|
||||
image: devlikeapro/whatsapp-http-api
|
||||
container_name: whatsapp-http-api
|
||||
ports:
|
||||
- "3000:3000/tcp"
|
||||
- '3000:3000/tcp'
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
- WHATSAPP_HOOK_URL=https://httpbin.org/post
|
||||
@@ -16,9 +16,9 @@ services:
|
||||
image: devlikeapro/whatsapp-http-api-plus
|
||||
container_name: whatsapp-http-api-plus
|
||||
ports:
|
||||
- "3000:3000/tcp"
|
||||
- '3000:3000/tcp'
|
||||
volumes:
|
||||
- "./.sessions:/app/.sessions"
|
||||
- './.sessions:/app/.sessions'
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
- WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
|
||||
@@ -42,6 +42,11 @@ The following environment variables can be used to configure the WAHA.
|
||||
### Swagger
|
||||
- `WHATSAPP_SWAGGER_CONFIG_ADVANCED=true` - enables advanced configuration options for Swagger documentation - you can customize host, port and base URL for the requests.
|
||||
Disabled by default.
|
||||
- `WHATSAPP_SWAGGER_ENABLED=false` - disables Swagger documentation. Enabled by default. Available in **WAHA Plus** only.
|
||||
- `WHATSAPP_SWAGGER_USERNAME=admin` and `WHATSAPP_SWAGGER_PASSWORD=admin` - these variables can be used to protect the Swagger panel
|
||||
with `admin / admin` credentials. This does not affect API access. Available in **WAHA Plus** only.
|
||||
|
||||
Read more about security settings for Swagger and API on [**Security page** ->]({{< relref "/docs/how-to/security" >}}).
|
||||
|
||||
### Proxy
|
||||
#### Global proxy configuration
|
||||
@@ -72,14 +77,34 @@ Keep in mind that session's proxy configuration takes precedence over proxy conf
|
||||
|
||||
The following environment variables can be used to configure the file storage options for the WAHA:
|
||||
|
||||
- `WHATSAPP_FILES_MIMETYPES`: This variable can be used to download only specific mimetypes from messages. By default,
|
||||
all files are downloaded. The mimetypes must be separated by a comma, without spaces. For
|
||||
example: `audio,image/png,image/gif`. To choose a specific type, use a prefix (like `audio,image`).
|
||||
- `WHATSAPP_FILES_MIMETYPES`: This variable can be used to download only specific mimetypes from messages.
|
||||
By default, all files are downloaded. The mimetypes must be separated by a comma, without spaces.
|
||||
For example: `audio,image/png,image/gif`. To choose a specific type, use a prefix (like `audio,image`). See usage below.
|
||||
- `WHATSAPP_DOWNLOAD_MEDIA=true` - this variable can be used to **completely** disable downloading media files. By default, all files are downloaded.
|
||||
Set this variable to `WHATSAPP_DOWNLOAD_MEDIA=false` to disable downloading media files.
|
||||
- Under the hood, it sets `WHATSAPP_FILES_MIMETYPES=mimetype/ignore-all-media` to ignore all media files.
|
||||
- `WHATSAPP_FILES_LIFETIME`: This variable can be used to set the time (in seconds) after which files will be removed to
|
||||
free up space. The default value is `180`.
|
||||
- `WHATSAPP_FILES_FOLDER`: This variable can be used to set the folder where files from chats (images, voice messages)
|
||||
will be stored. The default value is `/tmp/whatsapp-files`.
|
||||
|
||||
💡 Even if WAHA doesn't process the message media because of `WHATSAPP_FILES_MIMETYPES` or `WHATSAPP_DOWNLOAD_MEDIA`
|
||||
you'll get a webhook event with `hasMedia: True` field, but with no `media.url`.
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"payload": {
|
||||
"hasMedia": true,
|
||||
"media": {
|
||||
"url": null,
|
||||
"mimetype": "video/mp4",
|
||||
"filename": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
#### Debug Mode
|
||||
@@ -138,6 +163,13 @@ comma-separated list of mimetypes:
|
||||
WHATSAPP_FILES_MIMETYPES=audio,image/png,image/gif
|
||||
```
|
||||
|
||||
#### Disable Downloading Media Files
|
||||
To disable downloading media files, set the `WHATSAPP_DOWNLOAD_MEDIA` environment variable to `false`:
|
||||
|
||||
```
|
||||
WHATSAPP_DOWNLOAD_MEDIA=false
|
||||
```
|
||||
|
||||
#### Setting the File Lifetime
|
||||
|
||||
To set the time (in seconds) after which files will be removed to free up space, set the `WHATSAPP_FILES_LIFETIME`
|
||||
|
||||
@@ -57,6 +57,21 @@ Not running Chromium saves you CPU and Memory, so you can run more instances on
|
||||
It's a high-performance system developed with JavaScript to create a bot for WhatsApp.
|
||||
It uses Puppeteer to run a real instance of Whatsapp Web to avoid getting blocked.
|
||||
|
||||
## Docker images
|
||||
WAHA provides few docker images with different setup.
|
||||
{{< alert icon="👉" text="Use `devlikeapro/whatsapp-http-api` instead of `devlikeapro/whatsapp-http-api-plus` to get **WAHA Plus** version." />}}
|
||||
|
||||
For **x86** processors use following images:
|
||||
- `devlikeapro/whatsapp-http-api:latest` - latest version of WAHA, using **Chromium** (does not support video receiving and receiving in Plus version)
|
||||
- `devlikeapro/whatsapp-http-api:chrome` - latest version of WAHA, using **Chrome** (supports video receiving and receiving in Plus version)
|
||||
- `devlikeapro/whatsapp-http-api:noweb`- latest version of WAHA, **no browser installed** - use it only for **NOWEB** engine
|
||||
|
||||
For **ARM** processors use following images:
|
||||
- `devlikeapro/whatsapp-http-api:arm` - **Chromium**
|
||||
- `devlikeapro/whatsapp-http-api:arm-noweb`- **no browser installed**
|
||||
|
||||
{{< alert icon="💡" text="Chrome version is not available in ARM" />}}
|
||||
|
||||
## Features
|
||||
|
||||
Some engines may not support certain features.
|
||||
@@ -79,6 +94,7 @@ it's the engine WAHA runs by default.
|
||||
| Communicate with WhatsApp via websocket (no browser) | ➖ | ✔️ | ➖ |
|
||||
|
||||
### Endpoints
|
||||
|
||||
If you find any inconsistency with actual endpoints -
|
||||
please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues/new?title=Error+in+engine+features )
|
||||
|
||||
@@ -91,7 +107,7 @@ please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues
|
||||
| `GET /api/sessions/` | ✔️ | ✔️ | ✔️ |
|
||||
| `GET /api/sessions/{session}/me` | ✔️ | ✔️ | ➖ |
|
||||
| **Authentication** | | | |
|
||||
| `POST /api/{session}/auth/qr` | ✔️ | ✔️ | ➖ |
|
||||
| `POST /api/{session}/auth/qr` | ✔️ | ✔️ | ✔️ |
|
||||
| `POST /api/{session}/auth/request-code` | ➖ | ✔️ | ➖ |
|
||||
| `POST /api/{session}/auth/authorize-code` | ➖ | ✔️ | ➖ |
|
||||
| **Screenshot** | | | |
|
||||
@@ -109,6 +125,7 @@ please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues
|
||||
| `POST /api/sendImage`  | ✔️ | ✔️ | ✔️ |
|
||||
| `POST /api/sendFile`  | ✔️ | ✔️ | ✔️ |
|
||||
| `POST /api/sendVoice`  | ✔️ | ✔️ | ✔️ |
|
||||
| `POST /api/sendVideo`  | ✔️ | ✔️ | ➖ |
|
||||
| `POST /api/reply` | ✔️ | ✔️ | ✔️ |
|
||||
| `POST /api/sendSeen` | ✔️ | ✔️ | ✔️ |
|
||||
| `POST /api/startTyping` | ✔️ | ✔️ | ✔️ |
|
||||
@@ -143,6 +160,7 @@ please [create an issue](https://github.com/devlikeapro/whatsapp-http-api/issues
|
||||
| `GET /api/{session}/groups` | ✔️ | ✔️ | ➖ |
|
||||
| `GET /api/{session}/groups/{id}` | ✔️ | ✔️ | ➖ |
|
||||
| `DELETE /api/{session}/groups/{id}` | ✔️ | ➖ | ➖ |
|
||||
| `GET /api/{session}/groups/{id}/settings/security/info-admin-only` | ✔️ | ➖ | ➖ |
|
||||
| `PUT /api/{session}/groups/{id}/settings/security/info-admin-only` | ✔️ | ➖ | ➖ |
|
||||
| `POST /api/{session}/groups/{id}/leave` | ✔️ | ✔️ | ➖ |
|
||||
| `PUT /api/{session}/groups/{id}/description` | ✔️ | ✔️ | ➖ |
|
||||
|
||||
@@ -90,7 +90,12 @@ Request:
|
||||
### Settings
|
||||
#### Security for group info
|
||||
Updates the group settings to only allow admins to edit group info (title, description, photo).
|
||||
|
||||
`GET /api/{session}/groups/{groupId}/settings/security/info-admin-only`
|
||||
```json
|
||||
{
|
||||
"adminsOnly": true
|
||||
}
|
||||
```
|
||||
`PUT /api/{session}/groups/{groupId}/settings/security/info-admin-only`
|
||||
|
||||
The request doesn't require any request's body and
|
||||
|
||||
@@ -154,10 +154,10 @@ Receive events when server or recipient gets the message, read or played it.
|
||||
|
||||
## Files 
|
||||
|
||||
When people send you files - images, voice messages, and documents - WAHA saves it in the file storage.
|
||||
In your application you must download it and use it as you want to. You can find the URL in `mediaUrl` field
|
||||
When people send you media - images, voice messages, and documents - WAHA saves it in the file storage.
|
||||
In your application you must download it and use it as you want to. You can find the URL in `media.url` field.
|
||||
|
||||
For example, you can get the webhook like this with `mediaUrl` value (we've skipped other fields):
|
||||
For example, you can get the webhook like this with `media` value (we've skipped other fields):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -167,12 +167,35 @@ For example, you can get the webhook like this with `mediaUrl` value (we've skip
|
||||
"id": "true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 1667561485,
|
||||
"from": "11111111111@c.us",
|
||||
"mediaUrl": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.jpg"
|
||||
"hasMedia": true,
|
||||
"media": {
|
||||
"url": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.jpg",
|
||||
"mimetype": "image/jpeg",
|
||||
"filename": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Then you can use the link to download the file `http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.jpg`.
|
||||
|
||||
For documents (file attachments) there's also `filename` field with the original file name.
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"payload": {
|
||||
"id": "true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 1667561485,
|
||||
"from": "11111111111@c.us",
|
||||
"media": {
|
||||
"url": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.pdf",
|
||||
"filename": "some-file.pdf",
|
||||
"mimetype": "application/pdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To configure the url you can use environment variables `WHATSAPP_API_HOSTNAME` and `WHATSAPP_API_PORT`.
|
||||
|
||||
By default, WAHA download all files that the account receive.
|
||||
|
||||
@@ -16,6 +16,7 @@ Either protect the API with [Api Key](https://www.fortinet.com/resources/cybergl
|
||||
firewalls.
|
||||
|
||||
## Swagger Security 
|
||||
### Username and password
|
||||
If you want to hide the project Swagger panel under the password - run the following command to hide under `admin/admin`
|
||||
login and password.
|
||||
|
||||
@@ -29,6 +30,12 @@ Open http://localhost:3000/ and enter `admin / admin` in the inputs:
|
||||
|
||||
{{< alert icon="👉" text="Protecting Swagger under the password does not protect your API from other request! Use both techniques to protect your API and Swagger!" />}}
|
||||
|
||||
### Disable Swagger
|
||||
You also can hide swagger completely by setting `WHATSAPP_SWAGGER_ENABLED=false` environment variable.
|
||||
|
||||
{{< alert icon="👉" text="Disabling Swagger does not protect the API, please use API security as well" />}}
|
||||
|
||||
|
||||
## API security 
|
||||
|
||||
You can protect the API by requiring Api Key in a request's headers.
|
||||
|
||||
@@ -169,6 +169,44 @@ You can send voice messages in two ways:
|
||||
}
|
||||
```
|
||||
|
||||
### Send video 
|
||||
Use `POST /api/sendVideo` to send a message with a video attached.
|
||||
|
||||
For **WEBJS** engine please use `devlikeapro/whatsapp-http-api-plus:chrome` docker image.
|
||||
Read more about [**Docker images and engines →**]({{< relref "/docs/how-to/engines" >}}).
|
||||
|
||||
You can send voice messages in two ways:
|
||||
1. Provide a URL for the file and the API will download it and send it in the request body.
|
||||
2. Provide the file as a base64 string in the request body.
|
||||
|
||||
#### URL
|
||||
```json
|
||||
{
|
||||
"session": "default",
|
||||
"chatId": "11111111111@c.us",
|
||||
"caption": "Watch this video!",
|
||||
"file": {
|
||||
"mimetype": "video/mp4",
|
||||
"filename": "video.mp4",
|
||||
"url": "https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/dev.likeapro.mp3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Base64
|
||||
```json
|
||||
{
|
||||
"chatId": "11111111111@c.us",
|
||||
"caption": "Watch this video!",
|
||||
"file": {
|
||||
"mimetype": "video/mp4",
|
||||
"filename": "video.mp4",
|
||||
"data": "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 truncated
|
||||
},
|
||||
"session": "default"
|
||||
}
|
||||
```
|
||||
|
||||
### Send file 
|
||||
Use `POST /api/sendFile` to send a file as a document.
|
||||
|
||||
|
||||
@@ -128,6 +128,32 @@ In order to log out the session - call `POST /api/sessions/logout`
|
||||
"logout": true
|
||||
}
|
||||
```
|
||||
### Get screenshot
|
||||
Get screenshot of the session's screen.
|
||||
|
||||
```bash
|
||||
GET /api/screenshot?session=default
|
||||
```
|
||||
|
||||
#### Get screenshot in Base64
|
||||
You can get screenshot in base64 format by adding `Accept: application/json` header to the request.
|
||||
|
||||
```bash
|
||||
GET /api/screenshot?session=default
|
||||
Accept: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"mimetype": "image/png",
|
||||
"data": "base64-encoded-data"
|
||||
}
|
||||
```
|
||||
|
||||
You can change it in Swagger by clicking on **Media Type** dropdown and selecting **application/json**:
|
||||
|
||||

|
||||
|
||||
|
||||
### Get me
|
||||
|
||||
@@ -160,6 +186,55 @@ GET /api/{session}/auth/qr
|
||||
```
|
||||
You'll get QR image that you can scan and get authenticated
|
||||
|
||||
#### QR formats
|
||||
You can get QR in different formats:
|
||||
1. **binary image** - `GET /api/{session}/auth/qr`
|
||||
2. **base64 image** - `GET /api/{session}/auth/qr` and set `Accept: application/json` header
|
||||
3. **raw** - `GET /api/{session}/auth/qr?format=raw`
|
||||
|
||||
Here's detailed information about each format:
|
||||
|
||||
1. **binary image**, binary image - **default** format, you'll get image in response
|
||||
```bash
|
||||
# Get image - binary
|
||||
GET /api/{session}/auth/qr
|
||||
|
||||
# OR
|
||||
GET /api/{session}/auth/qr?format=image
|
||||
|
||||
# OR specify Accept header as well
|
||||
GET /api/{session}/auth/qr?format=image
|
||||
Accept: image/png
|
||||
```
|
||||
|
||||
2. **base64 image** - you'll get image in base64 format in response if you set `Accept: application/json` header.
|
||||
```bash
|
||||
GET /api/{session}/auth/qr?format=image
|
||||
Accept: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"mimetype": "image/png",
|
||||
"data": "base64-encoded-data"
|
||||
}
|
||||
```
|
||||
|
||||
You can change it in Swagger by clicking on **Media Type** dropdown and selecting **application/json**:
|
||||
|
||||

|
||||
|
||||
3. **raw** - you'll get raw data in response, you can use it to generate QR code on your side
|
||||
```bash
|
||||
GET /api/{session}/auth/qr?format=raw
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"value": "value-that-you-need-to-use-to-generate-qr-code"
|
||||
}
|
||||
```
|
||||
|
||||
### Get pairing code
|
||||
See the list of engines [**that support the features ->**]({{< relref "/docs/how-to/engines#features" >}}).
|
||||
|
||||
|
||||
@@ -19,6 +19,39 @@ If you wish to get and read Release Notes - read them on one of our platforms, *
|
||||
|
||||
You even can **subscribe to get new updates** there!
|
||||
|
||||
## 2023.11
|
||||
November 2023
|
||||
- Add different formats for QR code:
|
||||
1. **binary image** - `GET /api/{session}/auth/qr`
|
||||
2. **base64 image** - `GET /api/{session}/auth/qr` and set `Accept: application/json` header
|
||||
3. **raw** - `GET /api/{session}/auth/qr?format=raw`
|
||||
- Add different formats for the screenshot:
|
||||
1. **binary image** - `GET /api/{session}/screenshot`
|
||||
2. **base64 image** - `GET /api/{session}/screenshot` and set `Accept: application/json` header
|
||||
- Add `WHATSAPP_SWAGGER_ENABLED=false` so you can hide Swagger documentation (available only in Plus version). [#185](https://github.com/devlikeapro/whatsapp-http-api/issues/185)
|
||||
- Add dedicated `media` field in webhook payload - now you can know which media is attached to the message.
|
||||
```json
|
||||
{
|
||||
"event": "message",
|
||||
"session": "default",
|
||||
"payload": {
|
||||
"hasMedia": true,
|
||||
"media": {
|
||||
"url": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.pdf",
|
||||
"mimetype": "application/pdf",
|
||||
"filename": "file.pdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
- Add `WHATSAPP_DOWNLOAD_MEDIA` environment variable to control media download. Available only in Plus version.
|
||||
- `WHATSAPP_DOWNLOAD_MEDIA=True` - download media
|
||||
- `WHATSAPP_DOWNLOAD_MEDIA=False` - do not download media at all.
|
||||
- Add `filename` field for document (file attachments) - the original filename of the document.
|
||||
- Add `/api/sendVideo` endpoint to **NOWEB** and **WEBJS** engines. Available only in Plus version.
|
||||
- Add security settings for edit group info `GET /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.10
|
||||
October 2023
|
||||
- Add `session.status` event for all engines! Now you can get the latest session's status immediately after it changes.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -1 +1 @@
|
||||
{"openapi":"3.1.0","paths":{"/api/{session}/auth/qr":{"get":{"operationId":"AuthController_getQR","summary":"Get QR code for pairing WhatsApp Web.","parameters":[{"name":"session","required":true,"in":"path","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.","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.","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/SessionInfo"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/{session}/me":{"get":{"operationId":"SessionController_getMe","summary":"Get information about the authenticated account","parameters":[{"name":"session","required":true,"in":"path","description":"WhatsApp session name","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeInfo"}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/checkNumberStatus":{"get":{"operationId":"ChattingController_checkNumberStatus","summary":"Check number status","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WANumberExistResult"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcardRequest"}}}},"responses":{"201":{"description":"","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":[]}]}},"/apLine truncated
|
||||
{"openapi":"3.1.0","paths":{"/api/{session}/auth/qr":{"get":{"operationId":"AuthController_getQR","summary":"Get QR code for pairing WhatsApp Web.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"},{"name":"format","required":true,"in":"query","schema":{"default":"image","enum":["image","raw"],"type":"string"}}],"responses":{"200":{"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Base64File"},{"$ref":"#/components/schemas/QRCodeValue"}]}}},"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/request-code":{"post":{"operationId":"AuthController_requestCode","summary":"Request authentication code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestCodeRequest"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/{session}/auth/authorize-code":{"post":{"operationId":"AuthController_authorizeCode","summary":"Send OTP authentication code.","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OTPRequest"}}}},"responses":{"201":{"description":""}},"tags":["auth"],"security":[{"api_key":[]}]}},"/api/sessions/start":{"post":{"operationId":"SessionsController_start","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStartRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDTO"}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/stop":{"post":{"operationId":"SessionsController_stop","summary":"Stop session","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStopRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/logout":{"post":{"operationId":"SessionsController_clean","summary":"Logout from session.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLogoutRequest"}}}},"responses":{"201":{"description":""}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions":{"get":{"operationId":"SessionsController_list","parameters":[{"name":"all","required":false,"in":"query","example":false,"description":"Return all sessions, including those that are in the STOPPED state.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SessionInfo"}}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/sessions/{session}/me":{"get":{"operationId":"SessionController_getMe","summary":"Get information about the authenticated account","parameters":[{"name":"session","required":true,"in":"path","schema":{"default":"default"},"description":"WhatsApp session name"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeInfo"}}}}},"tags":["sessions"],"security":[{"api_key":[]}]}},"/api/checkNumberStatus":{"get":{"operationId":"ChattingController_checkNumberStatus","summary":"Check number status","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WANumberExistResult"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcardRequest"}}}},"responses":{"201":{"description":""}},"tags":["chatting"],"security":[{"api_key":[]}]}},"/api/sendText":{"get":{"operationId":"ChattingController_sendTextGet","summary":"Send a text message","parameters":[{"name":"phone","required":true,"in":"query","schema":{"type":"string"}},{"name":"text","required":true,"in":"query","schema":{"type":"string"}},{"name":"session","required":true,"in":"query","schema":{"default":"default","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"],"security":[{"api_key":[]}]},"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,Line truncated
|
||||
+30
-20
@@ -9,10 +9,11 @@ We assume that you have installed software:
|
||||
|
||||
### Download and start image
|
||||
|
||||
First of all, you must run WhatsApp HTTP API locally (which under the hood it runs real WhatsApp Web instance and expose
|
||||
HTTP API for interaction).
|
||||
First of all, you must run WhatsApp HTTP API locally (which under the hood it
|
||||
runs real WhatsApp Web instance and expose HTTP API for interaction).
|
||||
|
||||
Here are the steps from [Quick Start](https://waha.devlike.pro/docs/overview/quick-start/):
|
||||
Here are the steps from
|
||||
[Quick Start](https://waha.devlike.pro/docs/overview/quick-start/):
|
||||
|
||||
Download and start WhatsApp HTTP API docker container
|
||||
|
||||
@@ -28,9 +29,11 @@ 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
|
||||
|
||||
 For Core version the command is
|
||||
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/whatsapp-http-api:arm
|
||||
@@ -47,12 +50,16 @@ docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bo
|
||||
|
||||
2. Open Swagger API in the browser http://localhost:3000/
|
||||
3. Start session and scan QR code in swagger
|
||||
1. Find `POST /api/session/start` request press **Try it out** and **Execute** it with `default` session name
|
||||
2. Find `GET /api/screenshot` and execute it - it must show QR code
|
||||
3. Scan QR code on your mobile WhatsApp application (that installed on your phone)
|
||||
4. Execute `GET /api/screenshot` once again - it must show the screenshot from WhatsApp Web.
|
||||
4. Send test text message - find `POST /api/sendText` and execute it with payload (change `12132132130` in the `chatId`
|
||||
to phone number that is registered in WhatsApp).
|
||||
1. Find `POST /api/session/start` request press **Try it out** and
|
||||
**Execute** it with `default` session name
|
||||
2. Find `GET /api/screenshot` and execute it - it must show QR code
|
||||
3. Scan QR code on your mobile WhatsApp application (that installed on your
|
||||
phone)
|
||||
4. Execute `GET /api/screenshot` once again - it must show the screenshot
|
||||
from WhatsApp Web.
|
||||
4. Send test text message - find `POST /api/sendText` and execute it with
|
||||
payload (change `12132132130` in the `chatId` to phone number that is
|
||||
registered in WhatsApp).
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -66,9 +73,9 @@ If you see **Hi there!** message then you're ready to run bots!
|
||||
|
||||
## WhatsApp Echo Bot
|
||||
|
||||
The WhatsApp Echo Bot is a sample flask webhook server application that echoes back to you whatever you send it. It can
|
||||
serve
|
||||
as a basic reference for how to set up webhooks and reply to incoming messages.
|
||||
The WhatsApp Echo Bot is a sample flask webhook server application that echoes
|
||||
back to you whatever you send it. It can serve as a basic reference for how to
|
||||
set up webhooks and reply to incoming messages.
|
||||
|
||||
```bash
|
||||
# Clone the git repository with example
|
||||
@@ -81,16 +88,19 @@ python -mpip install -r requirements.txt
|
||||
FLASK_APP=whatsapp_echo_bot.py flask run
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Echo Bot is ready!** then the bot is ready to receive message!
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Echo Bot is ready!** then
|
||||
the bot is ready to receive message!
|
||||
|
||||
Send message to the WhatsApp (that you used to scan QR code) and it'll echo text back to you!
|
||||
Send message to the WhatsApp (that you used to scan QR code) and it'll echo text
|
||||
back to you!
|
||||
|
||||
## WhatsApp Download Files Bot
|
||||
|
||||
The WhatsApp Download Image Bot downloads all files people send to your WhatsApp and log the path for the file.
|
||||
The WhatsApp Download Image Bot downloads all files people send to your WhatsApp
|
||||
and log the path for the file.
|
||||
|
||||
**The bot works only with WAHA Plus version** available with donations.
|
||||
Visit [read more about difference between Core and Plus versions](https://waha.devlike.pro/docs/how-to/plus-version/).
|
||||
**The bot works only with WAHA Plus version** available with donations. Visit
|
||||
[read more about difference between Core and Plus versions](https://waha.devlike.pro/docs/how-to/plus-version/).
|
||||
|
||||
Download **WAHA Plus** version:
|
||||
|
||||
@@ -114,5 +124,5 @@ python -mpip install -r requirements.txt
|
||||
FLASK_APP=whatsapp_download_files_bot.py flask run
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Download Files Bot!** then the bot is ready to receive message
|
||||
with files!
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Download Files Bot!**
|
||||
then the bot is ready to receive message with files!
|
||||
Binary file not shown.
+1
-3
@@ -2,8 +2,6 @@
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"plugins": [
|
||||
"@nestjs/swagger"
|
||||
]
|
||||
"plugins": ["@nestjs/swagger"]
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node --unhandled-rejections=none dist/main",
|
||||
"start:prod-exit": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --max-warnings=0",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
StreamableFile,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { Base64File } from '../structures/files.dto';
|
||||
|
||||
@Injectable()
|
||||
/**
|
||||
* Interceptor to convert Buffer response to Base64File or StreamableFile
|
||||
* based on Accept header.
|
||||
* It should go together with @ApiFileAcceptHeader in controller method
|
||||
* So we have proper Swagger documentation
|
||||
*/
|
||||
export class BufferResponseInterceptor<T>
|
||||
implements NestInterceptor<Buffer, Base64File | StreamableFile>
|
||||
{
|
||||
intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler,
|
||||
): Observable<Base64File | StreamableFile> {
|
||||
const ctx = context.switchToHttp();
|
||||
const request = ctx.getRequest<Request>();
|
||||
const response = ctx.getResponse<Response>();
|
||||
return next.handle().pipe(map(processBuffer));
|
||||
|
||||
function processBuffer(buffer: Buffer) {
|
||||
// Check buffer is Buffer
|
||||
if (!Buffer.isBuffer(buffer)) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const accept = request.headers['accept'];
|
||||
if (accept == 'application/json') {
|
||||
return {
|
||||
mimetype: 'image/png',
|
||||
data: buffer.toString('base64'),
|
||||
};
|
||||
}
|
||||
const file = new StreamableFile(buffer);
|
||||
response.set({
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-19
@@ -1,14 +1,27 @@
|
||||
import { Body, Controller, Get, Post, Res } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
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 {
|
||||
OTPRequest,
|
||||
QRCodeFormat,
|
||||
QRCodeQuery,
|
||||
QRCodeValue,
|
||||
RequestCodeRequest,
|
||||
} from '../structures/auth.dto';
|
||||
import { WAHASessionStatus } from '../structures/enums.dto';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import { Readable } from 'stream';
|
||||
import { Response } from 'express';
|
||||
import { Base64File } from '../structures/files.dto';
|
||||
import { BufferResponseInterceptor } from './BufferResponseInterceptor';
|
||||
import { ApiFileAcceptHeader, SessionApiParam, SessionParam } from './helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/auth')
|
||||
@@ -17,26 +30,25 @@ 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) {
|
||||
@SessionApiParam
|
||||
@ApiFileAcceptHeader(Base64File, QRCodeValue)
|
||||
@UseInterceptors(new BufferResponseInterceptor())
|
||||
async getQR(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Query() query: QRCodeQuery,
|
||||
): Promise<Buffer | QRCodeValue> {
|
||||
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);
|
||||
const qr = session.getQR();
|
||||
if (query.format == QRCodeFormat.RAW) {
|
||||
return { value: qr.raw };
|
||||
}
|
||||
return qr.get();
|
||||
}
|
||||
|
||||
@Post('request-code')
|
||||
|
||||
@@ -3,9 +3,9 @@ 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 { GetChatMessagesQuery } from '../structures/chats.dto';
|
||||
import { parseBool } from '../helpers';
|
||||
import { GetChatMessagesQuery } from '../structures/chats.dto';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/chats')
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
MessageReplyRequest,
|
||||
MessageTextQuery,
|
||||
MessageTextRequest,
|
||||
MessageVideoRequest,
|
||||
MessageVoiceRequest,
|
||||
SendSeenRequest,
|
||||
WANumberExistResult,
|
||||
@@ -111,6 +112,16 @@ export class ChattingController {
|
||||
return whatsapp.sendVoice(request);
|
||||
}
|
||||
|
||||
@Post('/sendVideo')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Send a video. Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendVideo(@Body() request: MessageVideoRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendVideo(request);
|
||||
}
|
||||
|
||||
@Post('/reply')
|
||||
@ApiOperation({ summary: 'Reply to a text message' })
|
||||
reply(@Body() request: MessageReplyRequest) {
|
||||
|
||||
@@ -67,6 +67,7 @@ export class ContactsController {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.blockContact(request);
|
||||
}
|
||||
|
||||
@Post('/unblock')
|
||||
@ApiOperation({ summary: 'Unblock contact' })
|
||||
unblock(@Body() request: ContactRequest) {
|
||||
|
||||
@@ -23,16 +23,42 @@ export function serializeError(err: unknown) {
|
||||
|
||||
@Catch()
|
||||
export class AllExceptionsFilter implements ExceptionFilter {
|
||||
catch(exception: HttpException | Error, host: ArgumentsHost): void {
|
||||
catch(exception: any | Error, host: ArgumentsHost): void {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
const request = ctx.getRequest<Request>();
|
||||
|
||||
/**
|
||||
* If file not found - we get weird 500 error
|
||||
* So we convert that exception manually to 404 and send appropriate JSON response
|
||||
* @issue https://github.com/devlikeapro/whatsapp-http-api/issues/134
|
||||
* @solution https://github.com/nestjs/serve-static/issues/139#issuecomment-612429557
|
||||
*/
|
||||
if (exception.code === 'ENOENT') {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
error: {
|
||||
code: 404,
|
||||
key: 'FILE_NOT_FOUND',
|
||||
message: 'File not found',
|
||||
details: 'File not found or no longer available',
|
||||
},
|
||||
});
|
||||
response.send();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If it's HttpException - pass it as is
|
||||
*/
|
||||
if (exception instanceof HttpException) {
|
||||
response.status(exception.getStatus()).json(exception.getResponse());
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If it's not HttpException - pass it as 500 error
|
||||
* And send JSON response with error details
|
||||
*/
|
||||
const httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
response.status(httpStatus).json({
|
||||
statusCode: httpStatus,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
CreateGroupRequest,
|
||||
DescriptionRequest,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
SubjectRequest,
|
||||
} from '../structures/groups.dto';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
@@ -62,6 +63,19 @@ export class GroupsController {
|
||||
return session.setInfoAdminsOnly(id, true);
|
||||
}
|
||||
|
||||
@Get(':id/settings/security/info-admin-only')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Gets the group settings to only allow admins to edit group info (title, description, photo).',
|
||||
})
|
||||
getInfoAdminOnly(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<SettingsSecurityChangeInfo> {
|
||||
return session.getInfoAdminsOnly(id);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Delete the group.' })
|
||||
|
||||
+47
-2
@@ -1,8 +1,19 @@
|
||||
import { Injectable, Param, PipeTransform } from '@nestjs/common';
|
||||
import { ApiParam } from '@nestjs/swagger';
|
||||
import {
|
||||
applyDecorators,
|
||||
Injectable,
|
||||
Param,
|
||||
PipeTransform,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiExtraModels,
|
||||
ApiParam,
|
||||
ApiResponse,
|
||||
getSchemaPath,
|
||||
} from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { Base64File } from '../structures/files.dto';
|
||||
|
||||
/**
|
||||
* Get session name and return Whatsapp session back (if exists)
|
||||
@@ -25,6 +36,9 @@ export const SessionApiParam = ApiParam({
|
||||
name: 'session',
|
||||
required: true,
|
||||
type: 'string',
|
||||
schema: {
|
||||
default: 'default',
|
||||
},
|
||||
description: 'WhatsApp session name',
|
||||
});
|
||||
|
||||
@@ -33,3 +47,34 @@ export const SessionApiParam = ApiParam({
|
||||
@SessionParam session: WhatsappSession,
|
||||
*/
|
||||
export const SessionParam = Param('session', SessionPipe);
|
||||
|
||||
function getRefSchemaPaths(models) {
|
||||
return models.map((model) => {
|
||||
return { $ref: getSchemaPath(model) };
|
||||
});
|
||||
}
|
||||
|
||||
export function ApiFileAcceptHeader(...models) {
|
||||
models = models.length ? models : [Base64File];
|
||||
return applyDecorators(
|
||||
// Add extra models, otherwise it'll give a error
|
||||
// $ref not found
|
||||
ApiExtraModels(...models),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
content: {
|
||||
'image/png': {
|
||||
schema: {
|
||||
type: 'string',
|
||||
format: 'binary',
|
||||
},
|
||||
},
|
||||
'application/json': {
|
||||
schema: {
|
||||
oneOf: getRefSchemaPaths(models),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -10,12 +10,12 @@ import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { WAHAPresenceStatus } from '../structures/enums.dto';
|
||||
import {
|
||||
WAHAChatPresences,
|
||||
WAHASessionPresence,
|
||||
} from '../structures/presence.dto';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
import { WAHAPresenceStatus } from '../structures/enums.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/presence')
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { Controller, Get, Query, Res } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Query,
|
||||
Res,
|
||||
StreamableFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { Response } from 'express';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { SessionQuery } from '../structures/base.dto';
|
||||
import { BufferResponseInterceptor } from './BufferResponseInterceptor';
|
||||
import { ApiFileAcceptHeader } from './helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api')
|
||||
@@ -13,17 +21,13 @@ export class ScreenshotController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/screenshot')
|
||||
async screenshot(@Res() res: Response, @Query() sessionQuery: SessionQuery) {
|
||||
@UseInterceptors(new BufferResponseInterceptor())
|
||||
@ApiFileAcceptHeader()
|
||||
async screenshot(
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Query() sessionQuery: SessionQuery,
|
||||
) {
|
||||
const whatsappService = this.manager.getSession(sessionQuery.session);
|
||||
const buffer = await whatsappService.getScreenshot();
|
||||
const stream = new Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
stream.pipe(res);
|
||||
return await whatsappService.getScreenshot();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import {
|
||||
ApiExtraModels,
|
||||
ApiOperation,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
} from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { parseBool } from '../helpers';
|
||||
import {
|
||||
ListSessionsQuery,
|
||||
@@ -19,9 +19,6 @@ import {
|
||||
SessionStopRequest,
|
||||
} from '../structures/sessions.dto';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { WAHAChatPresences } from '../structures/presence.dto';
|
||||
import { WAHAWebhook } from '../structures/webhooks.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/sessions')
|
||||
@@ -34,7 +31,7 @@ class SessionsController {
|
||||
const result = await this.manager.start(request);
|
||||
await this.manager.sessionStorage.configRepository.save(
|
||||
request.name,
|
||||
request.config,
|
||||
request.config || null,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
@@ -71,7 +68,7 @@ class SessionController {
|
||||
@Get('me')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get information about the authenticated account' })
|
||||
getMe(@SessionParam session: WhatsappSession): Promise<MeInfo> {
|
||||
getMe(@SessionParam session: WhatsappSession): Promise<MeInfo | null> {
|
||||
return session.getSessionMeInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ 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';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/status')
|
||||
|
||||
+16
-3
@@ -36,9 +36,17 @@ export class WhatsappConfigService {
|
||||
return this.configService.get<number>('WHATSAPP_FILES_LIFETIME', 180);
|
||||
}
|
||||
|
||||
get mimetypes(): string[] | null {
|
||||
get mimetypes(): string[] {
|
||||
if (!this.shouldDownloadMedia) {
|
||||
return ['mimetype/ignore-all-media'];
|
||||
}
|
||||
const types = this.configService.get('WHATSAPP_FILES_MIMETYPES', '');
|
||||
return types ? types.split(',') : null;
|
||||
return types ? types.split(',') : [];
|
||||
}
|
||||
|
||||
get shouldDownloadMedia(): boolean {
|
||||
const value = this.configService.get('WHATSAPP_DOWNLOAD_MEDIA', 'true');
|
||||
return parseBool(value);
|
||||
}
|
||||
|
||||
get startSessions(): string[] {
|
||||
@@ -111,7 +119,7 @@ export class WhatsappConfigService {
|
||||
return WAHAEngine.WEBJS;
|
||||
}
|
||||
|
||||
get(name: string, defaultValue = undefined): any {
|
||||
get(name: string, defaultValue: any = undefined): any {
|
||||
return this.configService.get(name, defaultValue);
|
||||
}
|
||||
|
||||
@@ -119,6 +127,11 @@ export class WhatsappConfigService {
|
||||
return this.configService.get('WHATSAPP_API_KEY', '');
|
||||
}
|
||||
|
||||
getSwaggerEnabled(): boolean {
|
||||
const value = this.configService.get('WHATSAPP_SWAGGER_ENABLED', 'true');
|
||||
return parseBool(value);
|
||||
}
|
||||
|
||||
getSwaggerUsernamePassword(): [string, string] | undefined {
|
||||
const user = this.configService.get('WHATSAPP_SWAGGER_USERNAME', undefined);
|
||||
const password = this.configService.get(
|
||||
|
||||
+3
-1
@@ -1,8 +1,10 @@
|
||||
export class QR {
|
||||
private base64: string;
|
||||
public raw?: string;
|
||||
|
||||
save(base64) {
|
||||
save(base64: string, raw?: string) {
|
||||
this.base64 = base64.replace(/^data:image\/png;base64,/, '');
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
get(): Buffer {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
SessionStopRequest,
|
||||
} from '../../structures/sessions.dto';
|
||||
import { WhatsappSession } from './session.abc';
|
||||
import { LocalSessionStorage, MediaStorage } from './storage.abc';
|
||||
import { LocalSessionStorage } from './storage.abc';
|
||||
import { WebhookConductor } from './webhooks.abc';
|
||||
|
||||
export abstract class SessionManager implements OnApplicationShutdown {
|
||||
@@ -21,8 +21,6 @@ export abstract class SessionManager implements OnApplicationShutdown {
|
||||
|
||||
protected abstract get WebhookConductorClass(): typeof WebhookConductor;
|
||||
|
||||
protected abstract get MediaStorageClass(): typeof MediaStorage;
|
||||
|
||||
abstract onApplicationShutdown(signal?: string);
|
||||
|
||||
//
|
||||
@@ -34,7 +32,7 @@ export abstract class SessionManager implements OnApplicationShutdown {
|
||||
|
||||
abstract logout(request: SessionLogoutRequest): Promise<void>;
|
||||
|
||||
abstract getSession(name: string, error?: boolean): WhatsappSession;
|
||||
abstract getSession(name: string): WhatsappSession;
|
||||
|
||||
abstract getSessions(all: boolean): Promise<SessionInfo[]>;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
abstract class MediaStorage {
|
||||
abstract save(
|
||||
messageId: string,
|
||||
mimetype: string,
|
||||
buffer: Buffer,
|
||||
): Promise<string>;
|
||||
}
|
||||
|
||||
interface IEngineMediaProcessor<Message> {
|
||||
hasMedia(message: Message): boolean;
|
||||
|
||||
getFilename(message: Message): string | null;
|
||||
|
||||
getMimetype(message: Message): string;
|
||||
|
||||
getMessageId(message: Message): string;
|
||||
|
||||
getMediaBuffer(message: Message): Promise<Buffer | null>;
|
||||
}
|
||||
|
||||
interface MediaManager {
|
||||
processMedia<Message>(
|
||||
processor: IEngineMediaProcessor<Message>,
|
||||
message: Message,
|
||||
): Promise<Message>;
|
||||
}
|
||||
|
||||
export { IEngineMediaProcessor, MediaManager, MediaStorage };
|
||||
+23
-11
@@ -3,6 +3,7 @@ import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
import { MessageId } from 'whatsapp-web.js';
|
||||
|
||||
import { OTPRequest, RequestCodeRequest } from '../../structures/auth.dto';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageTextRequest,
|
||||
MessageVideoRequest,
|
||||
MessageVoiceRequest,
|
||||
} from '../../structures/chatting.dto';
|
||||
import { ContactQuery, ContactRequest } from '../../structures/contacts.dto';
|
||||
@@ -28,6 +30,7 @@ import {
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '../../structures/groups.dto';
|
||||
import { WAHAChatPresences } from '../../structures/presence.dto';
|
||||
import {
|
||||
@@ -35,9 +38,6 @@ import {
|
||||
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,
|
||||
@@ -45,6 +45,10 @@ import {
|
||||
VoiceStatus,
|
||||
} from '../../structures/status.dto';
|
||||
import { WASessionStatusBody } from '../../structures/webhooks.dto';
|
||||
import { NotImplementedByEngineError } from '../exceptions';
|
||||
import { QR } from '../QR';
|
||||
import { MediaManager } from './media.abc';
|
||||
import { LocalSessionStorage } from './storage.abc';
|
||||
|
||||
const CHROME_PATH = '/usr/bin/google-chrome-stable';
|
||||
const CHROMIUM_PATH = '/usr/bin/chromium';
|
||||
@@ -71,11 +75,11 @@ export enum WAHAInternalEvent {
|
||||
|
||||
export interface SessionParams {
|
||||
name: string;
|
||||
storage: MediaStorage;
|
||||
mediaManager: MediaManager;
|
||||
log: ConsoleLogger;
|
||||
sessionStorage: LocalSessionStorage;
|
||||
proxyConfig?: ProxyConfig;
|
||||
sessionConfig: SessionConfig;
|
||||
sessionConfig?: SessionConfig;
|
||||
}
|
||||
|
||||
export abstract class WhatsappSession {
|
||||
@@ -83,11 +87,11 @@ export abstract class WhatsappSession {
|
||||
public engine: WAHAEngine;
|
||||
|
||||
public name: string;
|
||||
protected storage: MediaStorage;
|
||||
protected mediaManager: MediaManager;
|
||||
protected log: ConsoleLogger;
|
||||
protected sessionStorage: LocalSessionStorage;
|
||||
protected proxyConfig?: ProxyConfig;
|
||||
public sessionConfig: SessionConfig;
|
||||
public sessionConfig?: SessionConfig;
|
||||
|
||||
private _status: WAHASessionStatus;
|
||||
|
||||
@@ -96,7 +100,7 @@ export abstract class WhatsappSession {
|
||||
log,
|
||||
sessionStorage,
|
||||
proxyConfig,
|
||||
storage,
|
||||
mediaManager,
|
||||
sessionConfig,
|
||||
}: SessionParams) {
|
||||
this.events = new EventEmitter();
|
||||
@@ -104,7 +108,7 @@ export abstract class WhatsappSession {
|
||||
this.proxyConfig = proxyConfig;
|
||||
this.log = log;
|
||||
this.sessionStorage = sessionStorage;
|
||||
this.storage = storage;
|
||||
this.mediaManager = mediaManager;
|
||||
this.sessionConfig = sessionConfig;
|
||||
}
|
||||
|
||||
@@ -191,7 +195,7 @@ export abstract class WhatsappSession {
|
||||
* Auth methods
|
||||
*/
|
||||
|
||||
public getQR(): Promise<Buffer> {
|
||||
public getQR(): QR {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -203,7 +207,7 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract getScreenshot(): Promise<Buffer | string>;
|
||||
abstract getScreenshot(): Promise<Buffer>;
|
||||
|
||||
public getSessionMeInfo(): Promise<MeInfo | null> {
|
||||
throw new NotImplementedByEngineError();
|
||||
@@ -236,6 +240,10 @@ export abstract class WhatsappSession {
|
||||
|
||||
abstract sendVoice(request: MessageVoiceRequest);
|
||||
|
||||
sendVideo(request: MessageVideoRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract reply(request: MessageReplyRequest);
|
||||
|
||||
abstract sendSeen(chat: ChatRequest);
|
||||
@@ -315,6 +323,10 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getInfoAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setInfoAdminsOnly(id, value) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import { SessionConfig } from '../../structures/sessions.dto';
|
||||
|
||||
abstract class MediaStorage {
|
||||
abstract save(
|
||||
messageId: string,
|
||||
mimetype: string,
|
||||
buffer: Buffer,
|
||||
): Promise<string>;
|
||||
}
|
||||
|
||||
abstract class SessionConfigRepository {
|
||||
abstract save(sessionName: string, config: SessionConfig): Promise<void>;
|
||||
abstract save(
|
||||
sessionName: string,
|
||||
config: SessionConfig | null,
|
||||
): Promise<void>;
|
||||
|
||||
abstract get(sessionName: string): Promise<SessionConfig>;
|
||||
abstract get(sessionName: string): Promise<SessionConfig | null>;
|
||||
}
|
||||
|
||||
abstract class SessionStorage {
|
||||
@@ -37,4 +32,4 @@ abstract class LocalSessionStorage extends SessionStorage {
|
||||
abstract getFolderPath(sessionName: string): string;
|
||||
}
|
||||
|
||||
export { LocalSessionStorage, MediaStorage, SessionConfigRepository };
|
||||
export { LocalSessionStorage, SessionConfigRepository };
|
||||
@@ -3,6 +3,8 @@ import { ConfigModule } from '@nestjs/config';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
|
||||
import { AuthController } from '../api/auth.controller';
|
||||
import { ChatsController } from '../api/chats.controller';
|
||||
import { ChattingController } from '../api/chatting.controller';
|
||||
import { ContactsController } from '../api/contacts.controller';
|
||||
import { GroupsController } from '../api/groups.controller';
|
||||
@@ -12,13 +14,11 @@ import {
|
||||
SessionController,
|
||||
SessionsController,
|
||||
} from '../api/sessions.controller';
|
||||
import { StatusController } from '../api/status.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({
|
||||
|
||||
@@ -9,7 +9,7 @@ import { WhatsappSession } from './abc/session.abc';
|
||||
export function getProxyConfig(
|
||||
config: WhatsappConfigService,
|
||||
sessions: Record<string, WhatsappSession>,
|
||||
sessionName?: string,
|
||||
sessionName: string,
|
||||
): ProxyConfig | undefined {
|
||||
// Single proxy server configuration
|
||||
if (typeof config.proxyServer === 'string') {
|
||||
@@ -23,7 +23,7 @@ export function getProxyConfig(
|
||||
// Multiple proxy server configuration
|
||||
if (Array.isArray(config.proxyServer)) {
|
||||
const prefix = config.proxyServerIndexPrefix;
|
||||
let indexToUse: number = undefined;
|
||||
let indexToUse: number | undefined = undefined;
|
||||
if (prefix && sessionName.includes(prefix)) {
|
||||
// match numbers at the end of the string
|
||||
const matches = sessionName.match(/\d+$/);
|
||||
@@ -66,8 +66,8 @@ function getAuthenticatedUrl(
|
||||
export function createAgentProxy(proxyConfig: ProxyConfig): Agent | undefined {
|
||||
const url = getAuthenticatedUrl(
|
||||
proxyConfig.server,
|
||||
proxyConfig.username,
|
||||
proxyConfig.password,
|
||||
proxyConfig.username || '',
|
||||
proxyConfig.password || '',
|
||||
);
|
||||
return new HttpsProxyAgent(url);
|
||||
}
|
||||
+19
-19
@@ -18,17 +18,14 @@ import {
|
||||
} from '../structures/sessions.dto';
|
||||
import { WebhookConfig } from '../structures/webhooks.config.dto';
|
||||
import { SessionManager } from './abc/manager.abc';
|
||||
import {
|
||||
SessionParams,
|
||||
WAHAInternalEvent,
|
||||
WhatsappSession,
|
||||
} from './abc/session.abc';
|
||||
import { SessionParams, WhatsappSession } from './abc/session.abc';
|
||||
import { DOCS_URL } from './exceptions';
|
||||
import { getProxyConfig } from './helpers.proxy';
|
||||
import { CoreMediaManager, MediaStorageCore } from './media.core';
|
||||
import { WhatsappSessionNoWebCore } from './session.noweb.core';
|
||||
import { WhatsappSessionVenomCore } from './session.venom.core';
|
||||
import { WhatsappSessionWebJSCore } from './session.webjs.core';
|
||||
import { MediaStorageCore, SessionStorageCore } from './storage.core';
|
||||
import { SessionStorageCore } from './storage.core';
|
||||
import { WebhookConductorCore } from './webhooks.core';
|
||||
|
||||
export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
|
||||
@@ -45,11 +42,9 @@ export function buildLogger(name) {
|
||||
|
||||
@Injectable()
|
||||
export class SessionManagerCore extends SessionManager {
|
||||
private session: WhatsappSession;
|
||||
private session?: WhatsappSession;
|
||||
DEFAULT = 'default';
|
||||
|
||||
// @ts-ignore
|
||||
protected MediaStorageClass = MediaStorageCore;
|
||||
// @ts-ignore
|
||||
protected WebhookConductorClass = WebhookConductorCore;
|
||||
protected readonly EngineClass: typeof WhatsappSession;
|
||||
@@ -110,13 +105,16 @@ export class SessionManagerCore extends SessionManager {
|
||||
const name = request.name;
|
||||
this.log.log(`'${name}' - starting session...`);
|
||||
const log = buildLogger(`WhatsappSession - ${name}`);
|
||||
const storage = new this.MediaStorageClass();
|
||||
const mediaManager = new CoreMediaManager(
|
||||
new MediaStorageCore(),
|
||||
this.config.mimetypes,
|
||||
);
|
||||
const webhookLog = buildLogger(`Webhook - ${name}`);
|
||||
const webhook = new this.WebhookConductorClass(webhookLog);
|
||||
const proxyConfig = this.getProxyConfig(request);
|
||||
const sessionConfig: SessionParams = {
|
||||
name,
|
||||
storage,
|
||||
mediaManager,
|
||||
log,
|
||||
sessionStorage: this.sessionStorage,
|
||||
proxyConfig: proxyConfig,
|
||||
@@ -149,7 +147,9 @@ export class SessionManagerCore extends SessionManager {
|
||||
webhooks = webhooks.concat(request.config.webhooks);
|
||||
}
|
||||
const globalWebhookConfig = this.config.getWebhookConfig();
|
||||
webhooks.push(globalWebhookConfig);
|
||||
if (globalWebhookConfig) {
|
||||
webhooks.push(globalWebhookConfig);
|
||||
}
|
||||
return webhooks;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,9 @@ export class SessionManagerCore extends SessionManager {
|
||||
if (request.config?.proxy) {
|
||||
return request.config.proxy;
|
||||
}
|
||||
if (!this.session) {
|
||||
return undefined;
|
||||
}
|
||||
const sessions = { [request.name]: this.session };
|
||||
return getProxyConfig(this.config, sessions, request.name);
|
||||
}
|
||||
@@ -181,16 +184,13 @@ export class SessionManagerCore extends SessionManager {
|
||||
await this.sessionStorage.clean(request.name);
|
||||
}
|
||||
|
||||
getSession(name: string, error = true): WhatsappSession {
|
||||
getSession(name: string): WhatsappSession {
|
||||
this.onlyDefault(name);
|
||||
const session = this.session;
|
||||
if (!session) {
|
||||
if (error) {
|
||||
throw new NotFoundException(
|
||||
`We didn't find a session with name '${name}'. Please start it first by using POST /sessions/start request`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
throw new NotFoundException(
|
||||
`We didn't find a session with name '${name}'. Please start it first by using POST /sessions/start request`,
|
||||
);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { WAMedia } from '../structures/responses.dto';
|
||||
import {
|
||||
IEngineMediaProcessor,
|
||||
MediaManager,
|
||||
MediaStorage,
|
||||
} from './abc/media.abc';
|
||||
import { DOCS_URL } from './exceptions';
|
||||
|
||||
export class MediaStorageCore implements MediaStorage {
|
||||
async save(
|
||||
messageId: string,
|
||||
mimetype: string,
|
||||
buffer: Buffer,
|
||||
): Promise<string> {
|
||||
return Promise.resolve(
|
||||
`Media attachment's available only in WAHA Plus version. ${DOCS_URL}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreMediaManager implements MediaManager {
|
||||
constructor(
|
||||
private storage: MediaStorage,
|
||||
private mimetypes: string[],
|
||||
) {}
|
||||
|
||||
async processMedia<Message>(
|
||||
processor: IEngineMediaProcessor<Message>,
|
||||
message: Message,
|
||||
): Promise<Message> {
|
||||
if (!processor.hasMedia(message)) {
|
||||
return message;
|
||||
}
|
||||
const mimetype = '';
|
||||
const filename = processor.getFilename(message);
|
||||
const url = await this.storage.save('', mimetype, Buffer.from(''));
|
||||
const media: WAMedia = {
|
||||
mimetype: mimetype,
|
||||
filename: filename,
|
||||
url: url,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
message.media = media;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,11 @@ import makeWASocket, {
|
||||
WAMessageKey,
|
||||
} from '@adiwajshing/baileys';
|
||||
import { UnprocessableEntityException } from '@nestjs/common';
|
||||
import * as Buffer from 'buffer';
|
||||
import * as fs from 'fs/promises';
|
||||
import { Agent } from 'https';
|
||||
import * as lodash from 'lodash';
|
||||
import { PairingCodeResponse } from 'src/structures/auth.dto';
|
||||
import { Message } from 'whatsapp-web.js';
|
||||
|
||||
import { flipObject, splitAt } from '../helpers';
|
||||
import {
|
||||
@@ -55,12 +55,14 @@ import {
|
||||
WAHAPresenceData,
|
||||
} from '../structures/presence.dto';
|
||||
import { WAMessage } from '../structures/responses.dto';
|
||||
import { MeInfo } from '../structures/sessions.dto';
|
||||
import { BROADCAST_ID, TextStatus } from '../structures/status.dto';
|
||||
import {
|
||||
PollVote,
|
||||
PollVotePayload,
|
||||
WAMessageAckBody,
|
||||
} from '../structures/webhooks.dto';
|
||||
import { IEngineMediaProcessor } from './abc/media.abc';
|
||||
import {
|
||||
ensureSuffix,
|
||||
WAHAInternalEvent,
|
||||
@@ -72,7 +74,6 @@ 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');
|
||||
@@ -179,7 +180,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
if (!this.store) {
|
||||
return proto.Message.fromObject({});
|
||||
}
|
||||
const msg = await this.store.loadMessage(key.remoteJid!, key.id!);
|
||||
const msg = await this.store.loadMessage(key.remoteJid, key.id);
|
||||
return msg?.message || undefined;
|
||||
}
|
||||
|
||||
@@ -221,7 +222,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
if (qr) {
|
||||
this.status = WAHASessionStatus.SCAN_QR_CODE;
|
||||
QRCode.toDataURL(qr).then((url) => {
|
||||
this.qr.save(url);
|
||||
this.qr.save(url, qr);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -255,8 +256,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
/**
|
||||
* Auth methods
|
||||
*/
|
||||
public async getQR(): Promise<Buffer> {
|
||||
return Promise.resolve(this.qr.get());
|
||||
public getQR(): QR {
|
||||
return this.qr;
|
||||
}
|
||||
|
||||
public async requestCode(
|
||||
@@ -287,7 +288,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return { code: codeRepr };
|
||||
}
|
||||
|
||||
async getScreenshot(): Promise<Buffer | string> {
|
||||
async getScreenshot(): Promise<Buffer> {
|
||||
if (this.status === WAHASessionStatus.STARTING) {
|
||||
throw new UnprocessableEntityException(
|
||||
`The session is starting, please try again after few seconds`,
|
||||
@@ -659,10 +660,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
body: body,
|
||||
to: toCusFormat(fromToParticipant.to),
|
||||
participant: toCusFormat(fromToParticipant.participant),
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
// Media
|
||||
hasMedia: Boolean(message.media),
|
||||
media: message.media,
|
||||
mediaUrl: message.media?.url,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
// @ts-ignore
|
||||
@@ -811,14 +812,39 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return { id: chatId, presences: presences };
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: Message) {
|
||||
if (!message.hasMedia) {
|
||||
return message;
|
||||
}
|
||||
protected downloadMedia(message) {
|
||||
const processor = new EngineMediaProcessor(this);
|
||||
return this.mediaManager.processMedia(processor, message);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(message.key.id, '', undefined);
|
||||
return message;
|
||||
export class EngineMediaProcessor implements IEngineMediaProcessor<any> {
|
||||
constructor(public session: WhatsappSessionNoWebCore) {}
|
||||
|
||||
hasMedia(message: any): boolean {
|
||||
const messageType = Object.keys(message.message)[0];
|
||||
const hasMedia =
|
||||
messageType === 'imageMessage' ||
|
||||
messageType == 'audioMessage' ||
|
||||
messageType == 'documentMessage' ||
|
||||
messageType == 'videoMessage';
|
||||
return hasMedia;
|
||||
}
|
||||
|
||||
getMessageId(message: any): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getMimetype(message: any): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getMediaBuffer(message: any): Promise<Buffer | null> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
getFilename(message: any): string | null {
|
||||
return message.message?.documentMessage?.fileName || null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import * as Buffer from 'buffer';
|
||||
import { create, CreateConfig, Message, Whatsapp } from 'venom-bot';
|
||||
|
||||
import {
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
WAHASessionStatus,
|
||||
} from '../structures/enums.dto';
|
||||
import { WAMessage } from '../structures/responses.dto';
|
||||
import { IEngineMediaProcessor } from './abc/media.abc';
|
||||
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
|
||||
import { NotImplementedByEngineError } from './exceptions';
|
||||
import { QR } from './QR';
|
||||
@@ -65,7 +67,7 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
|
||||
protected getCatchQR() {
|
||||
return (base64Qrimg, asciiQR, attempts, urlCode) => {
|
||||
this.qr.save(base64Qrimg);
|
||||
this.qr.save(base64Qrimg, urlCode);
|
||||
this.status = WAHASessionStatus.SCAN_QR_CODE;
|
||||
this.log.debug('Number of attempts to read the qrcode: ', attempts);
|
||||
this.log.log('Terminal qrcode:');
|
||||
@@ -124,7 +126,15 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
getScreenshot(): Promise<Buffer | string> {
|
||||
|
||||
/**
|
||||
* Auth methods
|
||||
*/
|
||||
public getQR(): QR {
|
||||
return this.qr;
|
||||
}
|
||||
|
||||
getScreenshot(): Promise<Buffer> {
|
||||
if (this.status === WAHASessionStatus.STARTING) {
|
||||
throw new UnprocessableEntityException(
|
||||
`The session is starting, please try again after few seconds`,
|
||||
@@ -246,17 +256,13 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
protected async downloadAndDecryptMedia(message: Message) {
|
||||
if (!message.isMMS || !message.isMedia) {
|
||||
return message;
|
||||
}
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(message.id, '', undefined);
|
||||
return message;
|
||||
protected downloadMedia(message: Message) {
|
||||
const processor = new EngineMediaProcessor(this);
|
||||
return this.mediaManager.processMedia(processor, message);
|
||||
}
|
||||
|
||||
private processIncomingMessage(message: Message) {
|
||||
return this.downloadAndDecryptMedia(message).then(this.toWAMessage);
|
||||
return this.downloadMedia(message).then(this.toWAMessage);
|
||||
}
|
||||
|
||||
protected toWAMessage(message: Message): Promise<WAMessage> {
|
||||
@@ -268,10 +274,13 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
fromMe: message.fromMe,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// Media
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
hasMedia: Boolean(message.media),
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
media: message.media,
|
||||
// @ts-ignore
|
||||
mediaUrl: message.media?.url,
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
location: undefined,
|
||||
@@ -280,3 +289,29 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class EngineMediaProcessor implements IEngineMediaProcessor<Message> {
|
||||
constructor(public session: WhatsappSessionVenomCore) {}
|
||||
|
||||
hasMedia(message: any): boolean {
|
||||
if (!message.isMMS || !message.isMedia) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
getMessageId(message: any): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getMimetype(message: any): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getMediaBuffer(message: any): Promise<Buffer | null> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
getFilename(message: Message): string | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -37,16 +37,18 @@ import {
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '../structures/groups.dto';
|
||||
import { WAMessage } from '../structures/responses.dto';
|
||||
import { MeInfo } from '../structures/sessions.dto';
|
||||
import { WAMessageRevokedBody } from '../structures/webhooks.dto';
|
||||
import { IEngineMediaProcessor } from './abc/media.abc';
|
||||
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
|
||||
import {
|
||||
AvailableInPlusVersion,
|
||||
NotImplementedByEngineError,
|
||||
} from './exceptions';
|
||||
import { QR } from './QR';
|
||||
import { MeInfo } from '../structures/sessions.dto';
|
||||
import { WAMessageRevokedBody } from '../structures/webhooks.dto';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const QRCode = require('qrcode');
|
||||
@@ -144,7 +146,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
this.log.debug('QR received');
|
||||
// Convert to image and save
|
||||
QRCode.toDataURL(qr).then((url) => {
|
||||
this.qr.save(url);
|
||||
this.qr.save(url, qr);
|
||||
});
|
||||
// Print in terminal
|
||||
qrcode.generate(qr, { small: true });
|
||||
@@ -171,17 +173,20 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* Auth methods
|
||||
*/
|
||||
public async getQR(): Promise<Buffer> {
|
||||
return Promise.resolve(this.qr.get());
|
||||
public getQR(): QR {
|
||||
return this.qr;
|
||||
}
|
||||
|
||||
async getScreenshot(): Promise<Buffer | string> {
|
||||
async getScreenshot(): Promise<Buffer> {
|
||||
if (this.status === WAHASessionStatus.FAILED) {
|
||||
throw new UnprocessableEntityException(
|
||||
`The session under FAILED status. Please try to restart it.`,
|
||||
);
|
||||
}
|
||||
return await this.whatsapp.pupPage.screenshot();
|
||||
const screenshot = await this.whatsapp.pupPage.screenshot({
|
||||
encoding: 'binary',
|
||||
});
|
||||
return screenshot as Buffer;
|
||||
}
|
||||
|
||||
async checkNumberStatus(
|
||||
@@ -348,6 +353,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return this.whatsapp.createGroup(request.name, participantIds);
|
||||
}
|
||||
|
||||
public async getInfoAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return {
|
||||
// @ts-ignore
|
||||
adminsOnly: groupChat.groupMetadata.restrict,
|
||||
};
|
||||
}
|
||||
|
||||
public async setInfoAdminsOnly(id, value) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setInfoAdminsOnly(value);
|
||||
@@ -532,10 +545,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
fromMe: message.fromMe,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// Media
|
||||
// @ts-ignore
|
||||
hasMedia: Boolean(message.mediaUrl),
|
||||
hasMedia: Boolean(message.media),
|
||||
// @ts-ignore
|
||||
media: message.media,
|
||||
// @ts-ignore
|
||||
mediaUrl: message.media?.url,
|
||||
// @ts-ignore
|
||||
mediaUrl: message.mediaUrl,
|
||||
ack: message.ack,
|
||||
ackName: WAMessageAck[message.ack] || ACK_UNKNOWN,
|
||||
location: message.location,
|
||||
@@ -550,17 +567,35 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return contact;
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: Message) {
|
||||
protected downloadMedia(message: Message) {
|
||||
const processor = new EngineMediaProcessor();
|
||||
return this.mediaManager.processMedia(processor, message);
|
||||
}
|
||||
}
|
||||
|
||||
export class EngineMediaProcessor implements IEngineMediaProcessor<Message> {
|
||||
hasMedia(message: Message): boolean {
|
||||
if (!message.hasMedia) {
|
||||
return message;
|
||||
return false;
|
||||
}
|
||||
// Can't get media for revoked messages
|
||||
return message.type !== 'revoked';
|
||||
}
|
||||
|
||||
getMessageId(message: Message): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getMimetype(message: Message): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getMediaBuffer(message: Message): Promise<Buffer | null> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
getFilename(message: Message): string | null {
|
||||
// @ts-ignore
|
||||
message.mediaUrl = await this.storage.save(
|
||||
message.id._serialized,
|
||||
'',
|
||||
undefined,
|
||||
);
|
||||
return message;
|
||||
return message.rawData?.filename || null;
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,8 @@ import * as path from 'path';
|
||||
import { SessionConfig } from '../structures/sessions.dto';
|
||||
import {
|
||||
LocalSessionStorage,
|
||||
MediaStorage,
|
||||
SessionConfigRepository,
|
||||
} from './abc/storage.abc';
|
||||
import { DOCS_URL } from './exceptions';
|
||||
|
||||
export class MediaStorageCore implements MediaStorage {
|
||||
async save(
|
||||
messageId: string,
|
||||
mimetype: string,
|
||||
buffer: Buffer,
|
||||
): Promise<string> {
|
||||
return Promise.resolve(
|
||||
`Media attachment's available only in WAHA Plus version. ${DOCS_URL}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LocalSessionConfigRepository extends SessionConfigRepository {
|
||||
FILENAME = '.waha.session.config.json';
|
||||
@@ -30,13 +16,13 @@ class LocalSessionConfigRepository extends SessionConfigRepository {
|
||||
super();
|
||||
}
|
||||
|
||||
async get(sessionName: string): Promise<SessionConfig> {
|
||||
async get(sessionName: string): Promise<SessionConfig | null> {
|
||||
const filepath = this.getFilePath(sessionName);
|
||||
// Check file exists
|
||||
try {
|
||||
await fs.access(filepath, fs.constants.F_OK);
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to load config
|
||||
@@ -44,13 +30,13 @@ class LocalSessionConfigRepository extends SessionConfigRepository {
|
||||
try {
|
||||
content = await fs.readFile(filepath, 'utf-8');
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
async save(sessionName: string, config: SessionConfig) {
|
||||
async save(sessionName: string, config: SessionConfig | null) {
|
||||
// Create folder if not exist
|
||||
const folder = this.storage.getFolderPath(sessionName);
|
||||
await fs.mkdir(folder, { recursive: true });
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';
|
||||
import { DECORATORS } from '@nestjs/swagger/dist/constants';
|
||||
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
import { VERSION } from '../version';
|
||||
import { DECORATORS } from '@nestjs/swagger/dist/constants';
|
||||
|
||||
export class SwaggerModuleCore {
|
||||
configure(app: INestApplication, webhooks: any[]) {
|
||||
this.setUpAuth(app);
|
||||
const builder = new DocumentBuilder();
|
||||
|
||||
builder
|
||||
@@ -130,8 +129,4 @@ export class SwaggerModuleCore {
|
||||
document.webhooks = webhooks;
|
||||
return document;
|
||||
}
|
||||
|
||||
protected setUpAuth(app: INestApplication) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -5,10 +5,9 @@ import { AllExceptionsFilter } from './api/exception.filter';
|
||||
import { WhatsappConfigService } from './config.service';
|
||||
import { AppModuleCore } from './core/app.module.core';
|
||||
import { SwaggerModuleCore } from './core/swagger.module.core';
|
||||
import { getWAHAVersion, VERSION, WAHAVersion } from './version';
|
||||
import { WAHA_WEBHOOKS } from './structures/webhooks.dto';
|
||||
import { LogLevel } from '@nestjs/common';
|
||||
import { getLogLevels } from './helpers';
|
||||
import { WAHA_WEBHOOKS } from './structures/webhooks.dto';
|
||||
import { getWAHAVersion, VERSION, WAHAVersion } from './version';
|
||||
|
||||
async function loadModules(): Promise<
|
||||
[typeof AppModuleCore, typeof SwaggerModuleCore]
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export enum QRCodeFormat {
|
||||
IMAGE = 'image',
|
||||
RAW = 'raw',
|
||||
}
|
||||
export class QRCodeQuery {
|
||||
format: QRCodeFormat = QRCodeFormat.IMAGE;
|
||||
}
|
||||
|
||||
export class QRCodeValue {
|
||||
value: string;
|
||||
}
|
||||
|
||||
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.',
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
|
||||
import {
|
||||
ApiExtraModels,
|
||||
ApiHideProperty,
|
||||
ApiProperty,
|
||||
getSchemaPath,
|
||||
} from '@nestjs/swagger';
|
||||
import { IsNumber, IsString } from 'class-validator';
|
||||
|
||||
import { SessionBaseRequest, SessionQuery } from './base.dto';
|
||||
import {
|
||||
BinaryFile,
|
||||
RemoteFile,
|
||||
VideoBinaryFile,
|
||||
VideoRemoteFile,
|
||||
VoiceBinaryFile,
|
||||
VoiceRemoteFile,
|
||||
} from './files.dto';
|
||||
@@ -21,6 +28,7 @@ export class CheckNumberStatusQuery extends SessionQuery {
|
||||
export class MessageTextQuery extends SessionQuery {
|
||||
@IsString()
|
||||
phone: string;
|
||||
|
||||
@IsString()
|
||||
text: string;
|
||||
}
|
||||
@@ -33,6 +41,7 @@ export class ChatQuery extends SessionQuery {
|
||||
export class GetMessageQuery extends ChatQuery {
|
||||
@IsNumber()
|
||||
limit: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
required: false,
|
||||
@@ -83,12 +92,7 @@ export class MessageContactVcardRequest extends ChatRequest {
|
||||
|
||||
export class MessageTextRequest extends ChatRequest {
|
||||
text = 'Hi there!';
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Mention contact in the message. ' +
|
||||
"The message MUST contain '@123456789' text in order to work with 'mentions' field.",
|
||||
example: ['123456789@c.us'],
|
||||
})
|
||||
@ApiHideProperty()
|
||||
mentions?: string[];
|
||||
}
|
||||
|
||||
@@ -136,6 +140,19 @@ export class MessageVoiceRequest extends ChatRequest {
|
||||
file: VoiceBinaryFile | VoiceRemoteFile;
|
||||
}
|
||||
|
||||
@ApiExtraModels(VideoRemoteFile, VideoBinaryFile)
|
||||
export class MessageVideoRequest extends ChatRequest {
|
||||
@ApiProperty({
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(VideoRemoteFile) },
|
||||
{ $ref: getSchemaPath(VideoBinaryFile) },
|
||||
],
|
||||
})
|
||||
file: VideoRemoteFile | VideoBinaryFile;
|
||||
|
||||
caption: string = 'Just watch at this!';
|
||||
}
|
||||
|
||||
export class MessageLinkPreviewRequest extends ChatRequest {
|
||||
url: string;
|
||||
title: string;
|
||||
@@ -178,6 +195,7 @@ export class MessageDestination {
|
||||
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
})
|
||||
id: string;
|
||||
|
||||
to: string;
|
||||
from: string;
|
||||
fromMe: boolean;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
export class Base64File {
|
||||
mimetype: string;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export class RemoteFile {
|
||||
@ApiProperty({
|
||||
@@ -11,7 +15,7 @@ export class RemoteFile {
|
||||
description: 'Document file name. Value can be null',
|
||||
example: 'filename.jpg',
|
||||
})
|
||||
filename?: string | null;
|
||||
filename?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example:
|
||||
@@ -31,7 +35,7 @@ export class BinaryFile {
|
||||
description: 'Document file name. Optional',
|
||||
example: 'filename.jpeg',
|
||||
})
|
||||
filename?: string | null;
|
||||
filename?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Base64-encoded data of the file',
|
||||
@@ -77,3 +81,40 @@ export class VoiceRemoteFile {
|
||||
})
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class VideoBinaryFile {
|
||||
@ApiProperty({
|
||||
description: 'MIME type of the attachment.',
|
||||
})
|
||||
mimetype = 'video/mp4';
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Document file name. Optional',
|
||||
})
|
||||
filename = 'video.mp4';
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Base64-encoded data of the file',
|
||||
example:
|
||||
'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
Line truncated
|
||||
})
|
||||
data: string;
|
||||
}
|
||||
|
||||
export class VideoRemoteFile {
|
||||
@ApiProperty({
|
||||
description: 'MIME type of the attachment.',
|
||||
})
|
||||
mimetype = 'video/mp4';
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Document file name. Optional',
|
||||
})
|
||||
filename = 'video.mp4';
|
||||
|
||||
@ApiProperty({
|
||||
example:
|
||||
'https://github.com/devlikeapro/whatsapp-http-api/raw/core/examples/video.mp4',
|
||||
})
|
||||
url: string;
|
||||
}
|
||||
@@ -8,6 +8,10 @@ export class Participant {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class SettingsSecurityChangeInfo {
|
||||
adminsOnly: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ export class WAHAPresenceData {
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
participant: string;
|
||||
|
||||
lastKnownPresence: WAHAPresenceStatus;
|
||||
@ApiProperty({
|
||||
example: 1686568773,
|
||||
@@ -20,6 +21,7 @@ export class WAHAChatPresences {
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
id: string;
|
||||
|
||||
presences: WAHAPresenceData[];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { WAHAEvents } from './enums.dto';
|
||||
import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger';
|
||||
|
||||
function ChatIdProperty(options = undefined) {
|
||||
function ChatIdProperty(options: ApiPropertyOptions | null = null) {
|
||||
options = options || {};
|
||||
if (!options.example) {
|
||||
options.example = '11111111111@c.us';
|
||||
|
||||
@@ -4,11 +4,32 @@ import { WAMessageAck } from './enums.dto';
|
||||
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
|
||||
|
||||
export class WALocation {
|
||||
description?: string | null;
|
||||
description?: string;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
}
|
||||
|
||||
export class WAMedia {
|
||||
@ApiProperty({
|
||||
description: 'The URL for the media in the message if any',
|
||||
example:
|
||||
'http://localhost:3000/api/files/false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA.oga',
|
||||
})
|
||||
url: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'mimetype for the media in the message if any',
|
||||
example: 'audio/jpeg',
|
||||
})
|
||||
mimetype: string | null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'The original filename in mediaUrl in the message if any',
|
||||
example: 'example.pdf',
|
||||
})
|
||||
filename: string | null;
|
||||
}
|
||||
|
||||
export class WAMessage {
|
||||
@MessageIdProperty()
|
||||
id: string;
|
||||
@@ -55,8 +76,12 @@ export class WAMessage {
|
||||
})
|
||||
hasMedia: boolean;
|
||||
|
||||
media: WAMedia | null = null;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'The URL for the media in the message if any',
|
||||
description:
|
||||
'Use `media.url` instead! The URL for the media in the message if any',
|
||||
deprecated: true,
|
||||
example:
|
||||
'http://localhost:3000/api/files/false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA.oga',
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
import { WAHASessionStatus } from './enums.dto';
|
||||
import { WebhookConfig } from './webhooks.config.dto';
|
||||
import { ChatIdProperty } from './properties.dto';
|
||||
import { WebhookConfig } from './webhooks.config.dto';
|
||||
|
||||
/**
|
||||
* Queries
|
||||
@@ -25,10 +25,12 @@ export class ProxyConfig {
|
||||
example: 'localhost:3128',
|
||||
})
|
||||
server: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: null,
|
||||
})
|
||||
username?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: null,
|
||||
})
|
||||
@@ -65,15 +67,16 @@ export class SessionLogoutRequest {
|
||||
export class SessionDTO {
|
||||
name = 'default';
|
||||
status: WAHASessionStatus;
|
||||
config: SessionConfig;
|
||||
config?: SessionConfig;
|
||||
}
|
||||
|
||||
export class MeInfo {
|
||||
@ChatIdProperty()
|
||||
id: string;
|
||||
|
||||
pushName: string;
|
||||
}
|
||||
|
||||
export class SessionInfo extends SessionDTO {
|
||||
me: MeInfo | null;
|
||||
me?: MeInfo;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
|
||||
|
||||
import {
|
||||
BinaryFile,
|
||||
RemoteFile,
|
||||
@@ -32,6 +33,7 @@ export class ImageStatus extends StatusRequest {
|
||||
],
|
||||
})
|
||||
file: BinaryFile | RemoteFile;
|
||||
|
||||
caption: string;
|
||||
}
|
||||
|
||||
@@ -44,6 +46,7 @@ export class VoiceStatus extends StatusRequest {
|
||||
],
|
||||
})
|
||||
file: VoiceBinaryFile | VoiceRemoteFile;
|
||||
|
||||
backgroundColor = '#38b42f';
|
||||
}
|
||||
|
||||
@@ -56,5 +59,6 @@ export class VideoStatus extends StatusRequest {
|
||||
],
|
||||
})
|
||||
file: BinaryFile | RemoteFile;
|
||||
|
||||
caption: string;
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
import { MessageDestination } from './chatting.dto';
|
||||
import {
|
||||
WAHAEngine,
|
||||
WAHAEvents,
|
||||
WAHASessionStatus,
|
||||
WAMessageAck,
|
||||
} from './enums.dto';
|
||||
import { WAMessage } from './responses.dto';
|
||||
import { MessageDestination } from './chatting.dto';
|
||||
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
|
||||
import { WAHAChatPresences } from './presence.dto';
|
||||
import { MeInfo } from './sessions.dto';
|
||||
import { WAHAEnvironment } from './environment.dto';
|
||||
import { WAHAChatPresences } from './presence.dto';
|
||||
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
|
||||
import { WAMessage } from './responses.dto';
|
||||
import { MeInfo } from './sessions.dto';
|
||||
|
||||
export class WAMessageAckBody {
|
||||
@MessageIdProperty()
|
||||
@@ -92,6 +92,7 @@ export class WASessionStatusBody {
|
||||
example: 'default',
|
||||
})
|
||||
name: string;
|
||||
|
||||
status: WAHASessionStatus;
|
||||
}
|
||||
|
||||
@@ -122,12 +123,14 @@ class WAHAWebhookSessionStatus extends WAHAWebhook {
|
||||
description: 'The event is triggered when the session status changes.',
|
||||
})
|
||||
event = WAHAEvents.SESSION_STATUS;
|
||||
|
||||
payload: WASessionStatusBody;
|
||||
}
|
||||
|
||||
class WAHAWebhookMessage extends WAHAWebhook {
|
||||
@ApiProperty({ description: 'Incoming message.' })
|
||||
event = WAHAEvents.MESSAGE;
|
||||
|
||||
payload: WAMessage;
|
||||
}
|
||||
|
||||
@@ -136,6 +139,7 @@ class WAHAWebhookMessageAny extends WAHAWebhook {
|
||||
description: 'Fired on all message creations, including your own.',
|
||||
})
|
||||
event = WAHAEvents.MESSAGE_ANY;
|
||||
|
||||
payload: WAMessage;
|
||||
}
|
||||
|
||||
@@ -145,6 +149,7 @@ class WAHAWebhookMessageAck extends WAHAWebhook {
|
||||
'Receive events when server or recipient gets the message, read or played it.',
|
||||
})
|
||||
event = WAHAEvents.MESSAGE_ACK;
|
||||
|
||||
payload: WAMessageAckBody;
|
||||
}
|
||||
|
||||
@@ -155,6 +160,7 @@ class WAHAWebhookMessageRevoked extends WAHAWebhook {
|
||||
'revokes a previously sent message.',
|
||||
})
|
||||
event = WAHAEvents.MESSAGE_REVOKED;
|
||||
|
||||
payload: WAMessageRevokedBody;
|
||||
}
|
||||
|
||||
@@ -163,6 +169,7 @@ class WAHAWebhookStateChange extends WAHAWebhook {
|
||||
description: 'It’s an internal engine’s state, not session status.',
|
||||
})
|
||||
event = WAHAEvents.STATE_CHANGE;
|
||||
|
||||
payload: any;
|
||||
}
|
||||
|
||||
@@ -171,6 +178,7 @@ class WAHAWebhookGroupJoin extends WAHAWebhook {
|
||||
description: 'Some one join a group.',
|
||||
})
|
||||
event = WAHAEvents.GROUP_JOIN;
|
||||
|
||||
payload: any;
|
||||
}
|
||||
|
||||
@@ -179,6 +187,7 @@ class WAHAWebhookGroupLeave extends WAHAWebhook {
|
||||
description: 'Some one left a group.',
|
||||
})
|
||||
event = WAHAEvents.GROUP_LEAVE;
|
||||
|
||||
payload: any;
|
||||
}
|
||||
|
||||
@@ -187,6 +196,7 @@ class WAHAWebhookPresenceUpdate extends WAHAWebhook {
|
||||
description: 'The most recent presence information for a chat.',
|
||||
})
|
||||
event = WAHAEvents.PRESENCE_UPDATE;
|
||||
|
||||
payload: WAHAChatPresences;
|
||||
}
|
||||
|
||||
@@ -195,6 +205,7 @@ class WAHAWebhookPollVote extends WAHAWebhook {
|
||||
description: 'With this event, you receive new votes for the poll sent.',
|
||||
})
|
||||
event = WAHAEvents.POLL_VOTE;
|
||||
|
||||
payload: PollVotePayload;
|
||||
}
|
||||
|
||||
@@ -205,6 +216,7 @@ class WAHAWebhookPollVoteFailed extends WAHAWebhook {
|
||||
'Read more about how to handle such events: https://waha.devlike.pro/docs/how-to/polls/#pollvotefailed',
|
||||
})
|
||||
event = WAHAEvents.POLL_VOTE_FAILED;
|
||||
|
||||
payload: PollVotePayload;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ export function getEngineName(): string {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2023.10.18',
|
||||
version: '2023.12.1',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowserExecutablePath(),
|
||||
|
||||
+1
-6
@@ -1,9 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"test",
|
||||
"dist",
|
||||
"**/*spec.ts"
|
||||
]
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
+1
-1
@@ -12,6 +12,6 @@
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@adiwajshing/baileys@github:WhiskeySockets/Baileys":
|
||||
version: 6.4.1
|
||||
resolution: "@adiwajshing/baileys@https://github.com/WhiskeySockets/Baileys.git#commit=cd8542ff7c072b5e58d2d3de2396983a5304e5e1"
|
||||
version: 6.5.0
|
||||
resolution: "@adiwajshing/baileys@https://github.com/WhiskeySockets/Baileys.git#commit=e24947e55ad6e3370de867b258bb33bb9e828c56"
|
||||
dependencies:
|
||||
"@adiwajshing/keyed-db": ^0.2.4
|
||||
"@hapi/boom": ^9.1.3
|
||||
@@ -44,7 +44,7 @@ __metadata:
|
||||
optional: true
|
||||
sharp:
|
||||
optional: true
|
||||
checksum: 4778b35869210130b938b5fb1d49a5b7263d7aaff734fe3ea4747fb06231ef9ffcf646ec653054e086c08d2a1b8833d167ebb975e0ec93251658dd9a081bd790
|
||||
checksum: b15ff56171157df1d4b55e984c09bf67a5537a179be57bf36e2e97cbdeaf70f1d3ee1dfb3e9d95a916b3e44373dc5f35a9bb8481e9c49bbba83b5ec926b6755d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -13130,8 +13130,8 @@ __metadata:
|
||||
linkType: soft
|
||||
|
||||
"whatsapp-web.js@https://github.com/pedroslopez/whatsapp-web.js":
|
||||
version: 1.22.2-alpha.3
|
||||
resolution: "whatsapp-web.js@https://github.com/pedroslopez/whatsapp-web.js.git#commit=f5555f6aa0d34e5c5b2f6fd5d8e1ee5559528c55"
|
||||
version: 1.23.1-alpha.0
|
||||
resolution: "whatsapp-web.js@https://github.com/pedroslopez/whatsapp-web.js.git#commit=58fc39f97d5663f7651faff4c60c12f2e91ce63e"
|
||||
dependencies:
|
||||
"@pedroslopez/moduleraid": ^5.0.2
|
||||
archiver: ^5.3.1
|
||||
@@ -13150,7 +13150,7 @@ __metadata:
|
||||
optional: true
|
||||
unzipper:
|
||||
optional: true
|
||||
checksum: d4d030cef0955cadfd199684d9176effdac1e4222100ec994c9d91004618b070bd23d01b04060e6fd988e3c1329a8ba0f38d208093a75f78115676eb6f6b4a4b
|
||||
checksum: ac8b24ccc79f26dfe28c6ae3b39fa18dae6e3ea3b3831358eaf84e78fa78e67e05adfa1a37ddb6684c88de89a4244d9b589bad375d0ce2e3e9d197f281d15de6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user