Compare commits

..
40 Commits
Author SHA1 Message Date
allburov 37c4a85e76 Add httpbin in readme.md
Release / deploy-docker (push) Waiting to run
2022-10-13 10:31:39 +07:00
allburov 8732af0f43 Add onAnyMessage hook 2022-10-13 10:14:43 +07:00
allburov dfa7bc2ca8 Log errors
Release / deploy-docker (push) Waiting to run
2022-10-03 12:04:13 +07:00
allburov f857bab540 Change entrypoint to commands 2022-10-03 12:00:50 +07:00
allburov 59f2e1526d Ignore Dockerfile 2022-10-03 11:59:22 +07:00
allburov abe273166b Log QR code on a new line
Release / deploy-docker (push) Waiting to run
2022-10-03 11:53:38 +07:00
allburov 3e37e933b2 Add start:prod-exit to exit on any fails 2022-10-03 11:48:02 +07:00
allburov 6d2cae0784 Add how to build docker image locally 2022-09-30 11:47:31 +07:00
allburov 0bf44dae88 Add table of content pre-commit hook 2022-09-30 11:45:04 +07:00
FelipeVeiga 9c9d6fda10 adding message with buttons (#17) 2022-09-30 11:40:23 +07:00
allburov 8b82f76668 Start session at the launch time with WHATSAPP_START_SESSION
Release / deploy-docker (push) Waiting to run
2022-09-28 10:28:07 +07:00
allburov 5fb5eace6e Add GET /checkNumberStatus
Release / deploy-docker (push) Waiting to run
2022-09-27 17:01:36 +07:00
allburov ac865abe67 Send a message to group
Release / deploy-docker (push) Waiting to run
fix #16
2022-09-26 13:25:09 +07:00
allburov 0b4e8e31fb Add more screenshots 2022-09-17 15:27:36 +07:00
allburov 3695c5c25f Merge remote-tracking branch 'origin/main' into main 2022-09-17 15:21:48 +07:00
allburov ea17f45263 Build
Release / deploy-docker (push) Waiting to run
2022-09-17 15:21:08 +07:00
Aleksey Burov eaea661be2 Just 2 options 2022-08-22 19:23:09 +07:00
Aleksey Burov 2a5d5e9d13 Add direct support link to boosty 2022-08-22 18:42:05 +07:00
Aleksey Burov 0afa354a0f Merge pull request #14 from allburov/multisessions
Multisessions
2022-08-22 17:38:49 +07:00
allburov a1112262a7 Add support section 2022-08-22 13:37:34 +03:00
allburov 51b10e0282 Add support section 2022-08-22 13:37:04 +03:00
allburov 340acc1173 Add support section 2022-08-22 13:36:50 +03:00
allburov 40f6d833eb Change readme.md 2022-08-22 13:08:05 +03:00
allburov 97c4a8f77d Buid using cache 2022-08-22 13:02:09 +03:00
allburov 115eb1fb85 Generate new swagger 2022-08-22 12:51:50 +03:00
allburov 9093dbd86c Fix README.md with new version 2022-08-22 12:47:22 +03:00
allburov 3a373ed700 Merge branch 'main' into multisessions
# Conflicts:
#	README.md
2022-08-22 12:28:04 +03:00
allburov 95aab5cba5 stop unautohorized sessions 2022-08-22 12:14:52 +03:00
allburov 8cbed3b46d Ignore unhandled rejections 2022-08-22 12:08:43 +03:00
allburov 6f2cf83425 Make stop work 2022-08-22 11:45:16 +03:00
allburov 23fb543776 Don't start default session 2022-08-19 16:56:13 +03:00
allburov 18c3ef991a Handle errors and show fullstack for 500 2022-08-19 10:25:05 +03:00
allburov b5d8471a67 Use sessionName everywhere 2022-08-19 09:58:39 +03:00
allburov 16f7c5eb9d Get QR or screenshot 2022-08-19 09:39:02 +03:00
allburov aff6e78b36 First version with multisessions 2022-08-18 18:44:37 +03:00
allburov ebf5532f27 Move ensureSuffix 2022-08-18 17:15:06 +03:00
allburov 270be1ee77 Use camelcases 2022-08-18 17:10:41 +03:00
Aleksey Burov 53589a7968 Update README.md 2022-08-18 21:01:44 +07:00
allburov e8d88b411a Listen on all ports 2022-08-15 12:37:47 +03:00
allburov e14b89f964 Venom to 4.3.0
Release / deploy-docker (push) Waiting to run
2022-08-15 09:37:17 +03:00
28 changed files with 13315 additions and 338 deletions

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
Dockerfile
tokens
files
venv
+23 -23
View File
@@ -1,25 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
],
root: true,
env: {
node: true,
jest: true,
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
],
root: true,
env: {
node: true,
jest: true,
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
+8 -3
View File
@@ -10,7 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Docker meta
id: meta
@@ -19,9 +23,8 @@ jobs:
images: allburov/whatsapp-http-api
flavor: latest=true
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
- name: Login to image repository
if: github.ref_type == 'tag'
uses: docker/login-action@v2
@@ -37,3 +40,5 @@ jobs:
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha
+5
View File
@@ -0,0 +1,5 @@
repos:
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
rev: v1.1.2
hooks:
- id: markdown-toc
+2 -1
View File
@@ -19,5 +19,6 @@ RUN npm install
WORKDIR /app
ADD . /app
RUN npm run build
ENTRYPOINT npm run start:prod
EXPOSE 3000
CMD npm run start:prod
+1 -1
View File
@@ -2,7 +2,7 @@ build:
docker build . -t allburov/whatsapp-http-api
run:
docker run -it -v `pwd`/tokens:/app/tokens -p 127.0.0.1:3000:3000/tcp allburov/whatsapp-http-api
docker run --rm -v `pwd`/tokens:/app/tokens -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api allburov/whatsapp-http-api
push:
docker push allburov/whatsapp-http-api
+73 -65
View File
@@ -1,32 +1,32 @@
# WhatsApp HTTP API Free
**NOTE**
If you have any problem with docker version of API - try to use the fork https://github.com/diazzaid/whatsapp-api-venom. It uses the latest Venom and probably works better. I haven't updated the venom here for a long time...
----------
WhatsApp HTTP API that you can configure in a click! It's really Free! :)
> The article [Make a WhatsApp Bot Via HTTP API For Free And Fun!](https://allburov.medium.com/make-a-whatsapp-bot-for-free-and-fun-via-http-api-b3e6afcdf395) on Medium about this repository.
The project provides you HTTP API for Whatsapp that you can use to send and receive messages. You can
go [through currently supported methods in Swagger](https://allburov.github.io/whatsapp-http-api/)
You can go [through currently supported methods in Swagger](https://allburov.github.io/whatsapp-http-api/)
The project is an HTTP wrapper around https://github.com/orkestral/venom, so we can also support these methods:
The project is an HTTP API wrapper around https://github.com/orkestral/venom
| | |
| ---------------------------------------------------------- | --- |
| Automatic QR Refresh | ✔ |
| Send **text, image, video, audio and docs** | ✔ |
| Get **contacts, chats, groups, group members, Block List** | ✔ |
| Send contacts | ✔ |
| Send stickers | ✔ |
| Send stickers GIF | ✔ |
| Multiple Sessions | ✔ |
| Forward Messages | ✔ |
| Receive message | ✔ |
| insert user section | ✔ |
| 📍 Send location!! | ✔ |
| 🕸🕸 **and much more** | ✔ |
# Tables of Contents
<!-- toc -->
- [Installation](#installation)
- [First Steps](#first-steps)
* [Run WhatsAPP HTTP API](#run-whatsapp-http-api)
* [Create a new session and login](#create-a-new-session-and-login)
* [Send a text message](#send-a-text-message)
- [Receive messages](#receive-messages)
+ [Python echo server](#python-echo-server)
- [Environment variables](#environment-variables)
* [Common](#common)
* [Webhooks](#webhooks)
* [File storage](#file-storage)
- [How to](#how-to)
* [How to build the newest Docker image locally](#how-to-build-the-newest-docker-image-locally)
- [Support](#support)
<!-- tocstop -->
# Installation
@@ -39,25 +39,45 @@ docker pull allburov/whatsapp-http-api
# First Steps
## Run and login
We're going to go through basic steps and send a text message at the end!
## Run WhatsAPP HTTP API
Run WhatsApp HTTP API:
```bash
docker pull allburov/whatsapp-http-api
docker run -it -v `pwd`/tokens:/app/tokens -p 127.0.0.1:3000:3000/tcp allburov/whatsapp-http-api
docker run -it --rm -v `pwd`/tokens:/app/tokens -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api allburov/whatsapp-http-api
# It prints logs and the last line must be
# WhatsApp HTTP API is running on: http://[::1]:3000
```
If you are not logged in, it will print a QR code in the terminal. Scan it with your phone and you are ready to go!
[How to log in - the instruction on WhatsApp site](https://faq.whatsapp.com/general/download-and-installation/how-to-log-in-or-out/?lang=en)
We will remember the session so there is no need to authenticate everytime.
Open the link in your browser: http://localhost:3000/
After that open in a browser the link and you'll see Swagger (OpenApi) API specification for WhatsApp HTTP API
http://localhost:3000/#/
Note: We don't recommend expose the API outside the world because it does not
support ([yet](https://github.com/allburov/whatsapp-http-api/issues/4)) authorization!
## Create a new session and login
1. To start a new session you should have your mobile phone with installed WhatsApp application close to you. Please go
and read how what we'll need to a bit
later: [How to log in - the instruction on WhatsApp site](https://faq.whatsapp.com/381777293328336/?helpref=hc_fnav)
1. Open API documentation at http://localhost:3000/
1. **Start a new session with a name** (you can use `default` for the start) - find `POST /api/session/start`, click
on **Try it out**, then **Execute** a bit below.
![](./docs/images/session-start.png)
1. **Scan QR Code** - find `GET /api/screenshot` and execute it, it'll show you QR code that you must scan with your
device.
![](./docs/images/qr.png)
1. **Get a screenshot again** - it'll show you the screenshot of you Whatsapp instance. If you can get the actual
screenshot - then you're ready to start sending messages!
![](./docs/images/screenshot.png)
## Send a text message
Let's try to send a message:
Let's try to send a message - you can either find `POST /api/sendText` in swagger (http://localhost:3000/) or
use `curl` or just open a link in a browser (change the phone
before!) http://localhost:3000/api/sendText?phone=79776772457&text=Hello+from+WhatsApp+HTTP+API+Free!
```bash
# Phone without +
@@ -69,19 +89,15 @@ export PHONE=79776772457
curl -d "{\"chatId\": \"${PHONE}@c.us\", \"text\": \"Hello from WhatsApp HTTP API Free\" }" -H "Content-Type: application/json" -X POST http://localhost:3000/api/sendText
```
## Get a screenshot
Go to the "screenshot" section and get a screenshot http://localhost:3000/#/screenshot
![](./docs/screenshot.png)
## Receive messages
# Receive messages
To show how to receive messages we'll create a simple "echo" server with two functions:
1. When we receive a text message - just send the text back
2. When we receive a message with a file (an image, a voice message) - download it and send the path back
In order to send you messages we use webhooks and configure them via environments variables. So what you need to
In order to send you messages we use **Webhooks** (look at them below) and configure them via environments variables. So
what you need to
create "echo" server is HTTP server that will receive JSON POST request and then call back WhatsApp HTTP API
via `POST /api/sendText` endpoint with JSON body.
@@ -120,6 +136,7 @@ If you try to send an image the "echo server" will send a path to the downloaded
- `DEBUG` - show debug and verbose logs, set in any value
- `WHATSAPP_API_PORT` - listen port for HTTP server (default: `3000`)
- `WHATSAPP_API_HOSTNAME` - Hostname for HTTP server (default: `localhost`)
- `WHATSAPP_START_SESSION` - start session with that name right after launching the app
## Webhooks
@@ -127,11 +144,18 @@ The description of [webhooks you can in Venom README.md, section Events](https:/
All webhooks are disabled by default:
- `WHATSAPP_HOOK_ONANYMESSAGE=http://localhost/uri`
- `WHATSAPP_HOOK_ONMESSAGE=http://localhost/uri`
- `WHATSAPP_HOOK_ONSTATECHANGE=http://localhost/uri`
- `WHATSAPP_HOOK_ONACK=http://localhost/uri`
- `WHATSAPP_HOOK_ONADDEDTOGROUP=http://localhost/uri`
If you want to look at the payload of the webhooks - use the url for that `https://httpbin.org/post`.
```bash
docker run -it -v `pwd`/tokens:/app/tokens --network=host -e WHATSAPP_HOOK_ONMESSAGE=https://httpbin.org/post allburov/whatsapp-http-api
```
## File storage
- `WHATSAPP_FILES_FOLDER` - folder where will be stored files from chats (images, voice messages) (
@@ -141,36 +165,20 @@ All webhooks are disabled by default:
like `audio,image`).
- `WHATSAPP_FILES_LIFETIME`- to keep free space files will be removed after this time (default: `180`, in seconds)
# Development
Use node 10 version:
# How to
## How to build the newest Docker image locally
If the changes haven't been released yet from `main` branch - you can build your own version from the git repository
```bash
$ npm install
git clone https://github.com/allburov/whatsapp-http-api.git
cd whatsapp-http-api
docker build . -t allburov/whatsapp-http-api
```
## Running the app
# Support
```bash
# development
$ npm run start
If you want to support the project - you can either:
# watch mode
$ npm run start:dev
1. Create a Pull Request for desired functionality
2. Support [the project one time, every month or get access to telegram channel on Boosty](https://boosty.to/allburov)
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+43 -46
View File
@@ -1,60 +1,57 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<meta charset="UTF-8">
<title>allburov/whatsapp-http-api</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<link href="./swagger-ui.css" rel="stylesheet" type="text/css">
<link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/>
<link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/>
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
body {
margin: 0;
background: #fafafa;
}
</style>
</head>
</head>
<body>
<div id="swagger-ui"></div>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "openapi.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// End Swagger UI call region
<script charset="UTF-8" src="./swagger-ui-bundle.js"></script>
<script charset="UTF-8" src="./swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function () {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "openapi.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// End Swagger UI call region
window.ui = ui
window.ui = ui
}
</script>
</body>
</script>
</body>
</html>
+14 -11
View File
@@ -8,7 +8,8 @@
</html>
<script>
'use strict';
function run () {
function run() {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
@@ -21,18 +22,20 @@
}
arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
arr.forEach(function (v, i, _arr) {
_arr[i] = '"' + v.replace('=', '":"') + '"';
})
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
) : {}
isValid = qp.state === sentState
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
@@ -50,9 +53,9 @@
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
oauthErrorMsg = "[" + qp.error + "]: " +
(qp.error_description ? qp.error_description + ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: " + qp.error_uri : "");
}
oauth2.errCb({
@@ -69,6 +72,6 @@
}
window.addEventListener('DOMContentLoaded', function () {
run();
run();
});
</script>
+908 -1
View File
@@ -1 +1,908 @@
{"openapi":"3.0.0","info":{"title":"WhatsApp HTTP API","description":"WhatsApp HTTP API that you can configure in a click!","version":"1.0","contact":{}},"tags":[{"name":"device","description":"Device information"},{"name":"chatting","description":"Chat methods"}],"servers":[],"components":{"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"X-VENOM-TOKEN","description":"Your secret key"}},"schemas":{"MessageContactVcard":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"contactsId":{"type":"string"},"name":{"type":"string"}},"required":["chatId","contactsId","name"]},"MessageText":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"text":{"type":"string"}},"required":["chatId","text"]},"MessageLocation":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"latitude":{"type":"string"},"longitude":{"type":"string"},"title":{"type":"string"}},"required":["chatId","latitude","longitude","title"]},"MessageLinkPreview":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"url":{"type":"string"},"title":{"type":"string"}},"required":["chatId","url","title"]},"MessageImage":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"path":{"type":"string"},"filename":{"type":"string"},"caption":{"type":"string"}},"required":["chatId","path","filename","caption"]},"MessageFile":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"path":{"type":"string"},"filename":{"type":"string"},"caption":{"type":"string"}},"required":["chatId","path","filename","caption"]},"MessageReply":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"reply_to":{"type":"string","example":"message.id"},"text":{"type":"string"}},"required":["chatId","reply_to","text"]},"Chat":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"}},"required":["chatId"]}}},"externalDocs":{"description":"Github WhatsApp HTTP API","url":"https://github.com/allburov/whatsapp-http-api"},"paths":{"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcard"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendText":{"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageText"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendLocation":{"post":{"operationId":"ChattingController_sendLocation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLocation"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendLinkPreview":{"post":{"operationId":"ChattingController_sendLinkPreview","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLinkPreview"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendImage":{"post":{"operationId":"ChattingController_sendImage","summary":"NOT IMPLEMENTED YET","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageImage"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendFile":{"post":{"operationId":"ChattingController_sendFile","summary":"NOT IMPLEMENTED YET","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFile"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/reply":{"post":{"operationId":"ChattingController_reply","summary":"Reply to a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageReply"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendSeen":{"post":{"operationId":"ChattingController_sendSeen","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}},"responses":{"201":{"description":""}},"tags":["chatting"]}},"/api/startTyping":{"post":{"operationId":"ChattingController_startTyping","parameters":[],"requestBody":{"required":trLine truncated
{
"openapi": "3.0.0",
"paths": {
"/api/sessions/start": {
"post": {
"operationId": "SessionsController_start",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"201": {
"description": ""
}
},
"tags": [
"sessions"
]
}
},
"/api/sessions/stop": {
"post": {
"operationId": "SessionsController_stop",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"201": {
"description": ""
}
},
"tags": [
"sessions"
]
}
},
"/api/sessions": {
"get": {
"operationId": "SessionsController_list",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"sessions"
]
}
},
"/api/sendContactVcard": {
"post": {
"operationId": "ChattingController_sendContactVcard",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageContactVcard"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendText": {
"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": "sessionName",
"required": true,
"in": "query",
"schema": {
"default": "default",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
},
"post": {
"operationId": "ChattingController_sendText",
"summary": "Send a text message",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageText"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendLocation": {
"post": {
"operationId": "ChattingController_sendLocation",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageLocation"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendLinkPreview": {
"post": {
"operationId": "ChattingController_sendLinkPreview",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageLinkPreview"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendImage": {
"post": {
"operationId": "ChattingController_sendImage",
"summary": "NOT IMPLEMENTED YET",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageImage"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendFile": {
"post": {
"operationId": "ChattingController_sendFile",
"summary": "NOT IMPLEMENTED YET",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageFile"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/reply": {
"post": {
"operationId": "ChattingController_reply",
"summary": "Reply to a text message",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageReply"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendSeen": {
"post": {
"operationId": "ChattingController_sendSeen",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatRequest"
}
}
}
},
"responses": {
"201": {
"description": ""
}
},
"tags": [
"chatting"
]
}
},
"/api/startTyping": {
"post": {
"operationId": "ChattingController_startTyping",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatRequest"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/stopTyping": {
"post": {
"operationId": "ChattingController_stopTyping",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatRequest"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/killServiceWorker": {
"post": {
"operationId": "DeviceController_killServiceWorker",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/restartService": {
"post": {
"operationId": "DeviceController_restartService",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/getHostDevice": {
"get": {
"operationId": "DeviceController_getHostDevice",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/getConnectionState": {
"get": {
"operationId": "DeviceController_getConnectionState",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"200": {
"description": ""
}
},
"tags": [
"device"
]
}
},
"/api/getBatteryLevel": {
"get": {
"operationId": "DeviceController_getBatteryLevel",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "number"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/isConnected": {
"get": {
"operationId": "DeviceController_isConnected",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/getWAVersion": {
"get": {
"operationId": "DeviceController_getWAVersion",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionRequest"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/screenshot": {
"get": {
"operationId": "ScreenshotController_screenshot",
"parameters": [
{
"name": "sessionName",
"required": true,
"in": "query",
"schema": {
"default": "default",
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"screenshot"
]
}
}
},
"info": {
"title": "WhatsApp HTTP API",
"description": "WhatsApp HTTP API that you can configure in a click!",
"version": "1.0",
"contact": {}
},
"tags": [
{
"name": "sessions",
"description": "Control your WhatsApp sessions"
},
{
"name": "screenshot",
"description": "Get screenshot of WhatsApp and show QR code"
},
{
"name": "chatting",
"description": "Chat methods"
},
{
"name": "device",
"description": "Device information"
}
],
"servers": [],
"components": {
"securitySchemes": {
"api_key": {
"type": "apiKey",
"in": "header",
"name": "X-VENOM-TOKEN",
"description": "Your secret key"
}
},
"schemas": {
"SessionRequest": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
}
},
"required": [
"sessionName"
]
},
"MessageContactVcard": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"contactsId": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"contactsId",
"name"
]
},
"MessageText": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"text": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"text"
]
},
"MessageLocation": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"latitude": {
"type": "string"
},
"longitude": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"latitude",
"longitude",
"title"
]
},
"MessageLinkPreview": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"url",
"title"
]
},
"MessageImage": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"path": {
"type": "string"
},
"filename": {
"type": "string"
},
"caption": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"path",
"filename",
"caption"
]
},
"MessageFile": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"path": {
"type": "string"
},
"filename": {
"type": "string"
},
"caption": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"path",
"filename",
"caption"
]
},
"MessageReply": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"reply_to": {
"type": "string",
"example": "message.id"
},
"text": {
"type": "string"
}
},
"required": [
"sessionName",
"chatId",
"reply_to",
"text"
]
},
"ChatRequest": {
"type": "object",
"properties": {
"sessionName": {
"type": "string",
"default": "default"
},
"chatId": {
"type": "string",
"example": "791231234567@c.us"
}
},
"required": [
"sessionName",
"chatId"
]
}
}
},
"externalDocs": {
"description": "Github WhatsApp HTTP API",
"url": "https://github.com/allburov/whatsapp-http-api"
}
}
+11056 -3
View File
File diff suppressed because it is too large. Load diff
+80 -19
View File
@@ -22,7 +22,7 @@
"rimraf": "^3.0.2",
"rxjs": "^7.1.0",
"swagger-ui-express": "^4.1.4",
"venom-bot": "^4.2.7"
"venom-bot": "^4.3.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
@@ -3493,6 +3493,14 @@
"@xtuc/long": "4.2.2"
}
},
"node_modules/@wppconnect/wa-js": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@wppconnect/wa-js/-/wa-js-2.11.0.tgz",
"integrity": "sha512-yKgEOgDdmt8GtPJLGPkKmRDEfQZcTrix6BHAQIe1utkTizE923aefPxT2bC16MaWuCbmuJTgjY8sbJi23dZBUA==",
"engines": {
"whatsapp-web": ">=2.2212.4-beta"
}
},
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -4271,6 +4279,11 @@
"node": ">= 6"
}
},
"node_modules/blueimp-canvas-to-blob": {
"version": "3.29.0",
"resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg=="
},
"node_modules/body-parser": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
@@ -5219,6 +5232,15 @@
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
},
"node_modules/compressorjs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.1.1.tgz",
"integrity": "sha512-SysRuUPfmUNoq+RviE0iMFVUmoX2q/x+7PkEPUmk6NGkd85hDrmvujx0Qtp8UCGA6KMe5kuodsylPQcNaLf60w==",
"dependencies": {
"blueimp-canvas-to-blob": "^3.29.0",
"is-blob": "^2.1.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -6526,6 +6548,11 @@
"node": ">= 0.6"
}
},
"node_modules/eventemitter2": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
"integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg=="
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -8250,6 +8277,17 @@
"node": ">=8"
}
},
"node_modules/is-blob": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -13196,11 +13234,6 @@
"node": ">=6"
}
},
"node_modules/packloader": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/packloader/-/packloader-0.0.2.tgz",
"integrity": "sha512-gQx+Z9cCK2xa4jrN4l/Wu8TfBFkECKTMpXCHkQXwlBrPUQ+Q3vWCOV/z4QL2UFNpDFkw+QyrYkq20CtH51L2LA=="
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -16636,12 +16669,13 @@
}
},
"node_modules/venom-bot": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/venom-bot/-/venom-bot-4.2.7.tgz",
"integrity": "sha512-uAjlq99Yd5vTf7LOcD/dws80baJrT6/kQlfF6biToNo/ckzyiEXi6VnwX+ny2R8c6PkbCHXlIvpBX/kgURELDw==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/venom-bot/-/venom-bot-4.3.0.tgz",
"integrity": "sha512-nTBWnZClonAtKnJ7pB9L5MJxwVH2u9wJwGjPeNePEMwvNf7uG+H8hVYtBDyr8bpAHE1w5dlRz/YT+BkEkELnXA==",
"dependencies": {
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"@wppconnect/wa-js": "^2.11.0",
"atob": "^2.1.2",
"axios": "^0.27.2",
"boxen": "^5.1.1",
@@ -16649,15 +16683,16 @@
"chalk": "^4.0.0",
"chrome-launcher": "^0.15.1",
"commitizen": "^4.2.4",
"compressorjs": "^1.1.1",
"cz-conventional-changelog": "^3.3.0",
"dotenv": "^16.0.1",
"eventemitter2": "^6.4.7",
"fsevents": "^2.2.1",
"futoin-hkdf": "^1.5.1",
"i": "^0.3.7",
"latest-lib": "^0.2.1",
"mime-types": "^2.1.35",
"ora": "^6.1.2",
"packloader": "^0.0.2",
"puppeteer": "^14.4.1",
"puppeteer-extra": "^3.2.3",
"puppeteer-extra-plugin-stealth": "^2.11.1",
@@ -20581,6 +20616,11 @@
"@xtuc/long": "4.2.2"
}
},
"@wppconnect/wa-js": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@wppconnect/wa-js/-/wa-js-2.11.0.tgz",
"integrity": "sha512-yKgEOgDdmt8GtPJLGPkKmRDEfQZcTrix6BHAQIe1utkTizE923aefPxT2bC16MaWuCbmuJTgjY8sbJi23dZBUA=="
},
"@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -21190,6 +21230,11 @@
}
}
},
"blueimp-canvas-to-blob": {
"version": "3.29.0",
"resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg=="
},
"body-parser": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
@@ -21879,6 +21924,15 @@
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
},
"compressorjs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.1.1.tgz",
"integrity": "sha512-SysRuUPfmUNoq+RviE0iMFVUmoX2q/x+7PkEPUmk6NGkd85hDrmvujx0Qtp8UCGA6KMe5kuodsylPQcNaLf60w==",
"requires": {
"blueimp-canvas-to-blob": "^3.29.0",
"is-blob": "^2.1.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -22887,6 +22941,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
},
"eventemitter2": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
"integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg=="
},
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -24211,6 +24270,11 @@
"binary-extensions": "^2.0.0"
}
},
"is-blob": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw=="
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -28137,11 +28201,6 @@
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"packloader": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/packloader/-/packloader-0.0.2.tgz",
"integrity": "sha512-gQx+Z9cCK2xa4jrN4l/Wu8TfBFkECKTMpXCHkQXwlBrPUQ+Q3vWCOV/z4QL2UFNpDFkw+QyrYkq20CtH51L2LA=="
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -30718,12 +30777,13 @@
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
},
"venom-bot": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/venom-bot/-/venom-bot-4.2.7.tgz",
"integrity": "sha512-uAjlq99Yd5vTf7LOcD/dws80baJrT6/kQlfF6biToNo/ckzyiEXi6VnwX+ny2R8c6PkbCHXlIvpBX/kgURELDw==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/venom-bot/-/venom-bot-4.3.0.tgz",
"integrity": "sha512-nTBWnZClonAtKnJ7pB9L5MJxwVH2u9wJwGjPeNePEMwvNf7uG+H8hVYtBDyr8bpAHE1w5dlRz/YT+BkEkELnXA==",
"requires": {
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"@wppconnect/wa-js": "^2.11.0",
"atob": "^2.1.2",
"axios": "^0.27.2",
"boxen": "^5.1.1",
@@ -30731,15 +30791,16 @@
"chalk": "^4.0.0",
"chrome-launcher": "^0.15.1",
"commitizen": "^4.2.4",
"compressorjs": "^1.1.1",
"cz-conventional-changelog": "^3.3.0",
"dotenv": "^16.0.1",
"eventemitter2": "^6.4.7",
"fsevents": "^2.2.1",
"futoin-hkdf": "^1.5.1",
"i": "^0.3.7",
"latest-lib": "^0.2.1",
"mime-types": "^2.1.35",
"ora": "^6.1.2",
"packloader": "^0.0.2",
"puppeteer": "^14.4.1",
"puppeteer-extra": "^3.2.3",
"puppeteer-extra-plugin-stealth": "^2.11.1",
+3 -2
View File
@@ -12,7 +12,8 @@
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"start:prod": "node --unhandled-rejections=none dist/main",
"start:prod-exit": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
@@ -35,7 +36,7 @@
"rimraf": "^3.0.2",
"rxjs": "^7.1.0",
"swagger-ui-express": "^4.1.4",
"venom-bot": "^4.2.7"
"venom-bot": "^4.3.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
+52 -25
View File
@@ -1,30 +1,65 @@
import {ApiProperty} from "@nestjs/swagger";
import {IsNotEmpty, IsString} from "class-validator";
const chatId = ApiProperty({
export const WHATSAPP_DEFAULT_SESSION_NAME = "default"
//
// Queries
//
export class SessionQuery {
@IsNotEmpty()
sessionName: string = WHATSAPP_DEFAULT_SESSION_NAME;
}
export class CheckNumberStatusQuery extends SessionQuery {
@IsString()
phone: string
}
export class MessageTextQuery extends SessionQuery {
@IsString()
phone: string
@IsString()
text: string
}
//
// Requests
//
const chatIdProperty = ApiProperty({
example: '791231234567@c.us'
})
const sessionNameProperty = ApiProperty({
default: WHATSAPP_DEFAULT_SESSION_NAME,
})
export class Chat {
@chatId
export class SessionRequest {
@sessionNameProperty
sessionName: string;
}
export class ChatRequest extends SessionRequest {
@chatIdProperty
chatId: string;
}
export class MessageContactVcard {
@chatId
chatId: string;
export class MessageContactVcard extends ChatRequest {
contactsId: string;
name: string
}
export class MessageText {
@chatId
chatId: string;
export class MessageText extends ChatRequest {
text: string;
}
export class MessageReply {
@chatId
chatId: string;
export class MessageTextButtons extends ChatRequest {
text: string;
title: string;
buttons: any[];
}
export class MessageReply extends ChatRequest {
text: string;
@ApiProperty({
example: 'message.id',
@@ -32,33 +67,25 @@ export class MessageReply {
reply_to: string;
}
export class MessageLocation {
@chatId
chatId: string;
export class MessageLocation extends ChatRequest {
latitude: string;
longitude: string;
title: string;
}
export class MessageImage {
@chatId
chatId: string;
export class MessageImage extends ChatRequest {
path: string;
filename: string;
caption: string;
}
export class MessageFile {
@chatId
chatId: string;
export class MessageFile extends ChatRequest {
path: string;
filename: string;
caption: string;
}
export class MessageLinkPreview {
@chatId
chatId: string;
export class MessageLinkPreview extends ChatRequest {
url: string;
title: string;
}
}
+59 -30
View File
@@ -1,60 +1,83 @@
import {Body, Controller, Get, Inject, NotImplementedException, Post, Query} from '@nestjs/common';
import {Body, Controller, Get, NotImplementedException, Post, Query} from '@nestjs/common';
import {ApiOperation, ApiTags} from "@nestjs/swagger";
import {
Chat,
ChatRequest,
CheckNumberStatusQuery,
MessageContactVcard,
MessageFile,
MessageImage,
MessageLinkPreview,
MessageLocation,
MessageReply,
MessageText
MessageText,
MessageTextButtons,
MessageTextQuery
} from "./all.dto";
import {Whatsapp} from "venom-bot";
const SUFFIX_DIRECT_MESSAGE = "@c.us"
function ensureSuffix(phone){
if (phone.endsWith(SUFFIX_DIRECT_MESSAGE)){
return phone
}
return phone + SUFFIX_DIRECT_MESSAGE
}
import {ensureSuffix, WhatsappSessionManager} from "../whatsapp.service";
@Controller('api')
@ApiTags('chatting')
export class ChattingController {
constructor(@Inject('WHATSAPP') private whatsapp: Whatsapp) {
constructor(private whatsappSessionManager: WhatsappSessionManager) {
}
@Get('/checkNumberStatus')
@ApiOperation({summary: 'Check number status'})
async checkNumberStatus(
@Query() request: CheckNumberStatusQuery,
) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
try {
const result = await whatsapp.checkNumberStatus(ensureSuffix(request.phone))
return {numberExists: result['numberExists']}
} catch (error) {
// We need to "touch" the error in order to get unhandled rejections
// It logs out the session and stop the app
console.log(typeof error)
console.log(error)
return {numberExists: false}
}
}
@Post('/sendContactVcard')
sendContactVcard(@Body() message: MessageContactVcard) {
return this.whatsapp.sendContactVcard(message.chatId, message.contactsId, message.name)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendContactVcard(message.chatId, message.contactsId, message.name)
}
@Get('/sendText')
@ApiOperation({summary: 'Send a text message'})
sendTextGet(
@Query('phone') phone: string,
@Query('text') text: string,
@Query() message: MessageTextQuery,
) {
return this.whatsapp.sendText(ensureSuffix(phone), text)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendText(ensureSuffix(message.phone), message.text)
}
@Post('/sendText')
@ApiOperation({summary: 'Send a text message'})
sendText(@Body() message: MessageText) {
return this.whatsapp.sendText(ensureSuffix(message.chatId), message.text)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendText(ensureSuffix(message.chatId), message.text)
}
@Post('/sendTextButtons')
@ApiOperation({summary: 'Send a text message with buttons'})
sendTextButtons(@Body() message: MessageTextButtons) {
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendButtons(ensureSuffix(message.chatId), message.title, message.buttons, message.text)
}
@Post('/sendLocation')
sendLocation(@Body() message: MessageLocation) {
return this.whatsapp.sendLocation(message.chatId, message.latitude, message.longitude, message.title)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendLocation(message.chatId, message.latitude, message.longitude, message.title)
}
@Post('/sendLinkPreview')
sendLinkPreview(@Body() message: MessageLinkPreview) {
return this.whatsapp.sendLinkPreview(message.chatId, message.url, message.title)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendLinkPreview(message.chatId, message.url, message.title)
}
@Post('/sendImage')
@@ -62,7 +85,8 @@ export class ChattingController {
sendImage(@Body() message: MessageImage) {
throw new NotImplementedException();
// TODO: Accept image URL, download it and then send with path
return this.whatsapp.sendImage(message.chatId, message.path, message.filename, message.caption)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendImage(message.chatId, message.path, message.filename, message.caption)
}
@Post('/sendFile')
@@ -70,30 +94,35 @@ export class ChattingController {
sendFile(@Body() message: MessageFile) {
throw new NotImplementedException();
// TODO: Accept File URL, download it and then send with path
return this.whatsapp.sendFile(message.chatId, message.path, message.filename, message.caption)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.sendFile(message.chatId, message.path, message.filename, message.caption)
}
@Post('/reply')
@ApiOperation({summary: 'Reply to a text message'})
reply(@Body() message: MessageReply) {
return this.whatsapp.reply(message.chatId, message.text, message.reply_to)
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
return whatsapp.reply(message.chatId, message.text, message.reply_to)
}
@Post('/sendSeen')
sendSeen(@Body() chat: Chat) {
return this.whatsapp.sendSeen(chat.chatId)
sendSeen(@Body() chat: ChatRequest) {
const whatsapp = this.whatsappSessionManager.getSession(chat.sessionName)
return whatsapp.sendSeen(chat.chatId)
}
@Post('/startTyping')
startTyping(@Body() chat: Chat) {
startTyping(@Body() chat: ChatRequest) {
// It's infinitive action
this.whatsapp.startTyping(chat.chatId)
const whatsapp = this.whatsappSessionManager.getSession(chat.sessionName)
whatsapp.startTyping(chat.chatId)
return true
}
@Post('/stopTyping')
stopTyping(@Body() chat: Chat) {
this.whatsapp.stopTyping(chat.chatId)
stopTyping(@Body() chat: ChatRequest) {
const whatsapp = this.whatsappSessionManager.getSession(chat.sessionName)
whatsapp.stopTyping(chat.chatId)
return true
}
}
+27 -17
View File
@@ -1,47 +1,57 @@
import {Controller, Get, Inject, Post} from '@nestjs/common';
import {Body, Controller, Get, Post} from '@nestjs/common';
import {ApiTags} from "@nestjs/swagger";
import {Whatsapp} from "venom-bot";
import {SessionRequest} from "./all.dto";
import {WhatsappSessionManager} from "../whatsapp.service";
@Controller('api')
@ApiTags('device')
export class DeviceController {
constructor(@Inject('WHATSAPP') private whatsapp: Whatsapp) {
constructor(private whatsappSessionManager: WhatsappSessionManager) {
}
@Post('/killServiceWorker')
killServiceWorker() {
return this.whatsapp.killServiceWorker()
killServiceWorker(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.killServiceWorker()
}
@Post('/restartService')
restartService() {
return this.whatsapp.restartService()
restartService(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.restartService()
}
@Get('/getHostDevice')
getHostDevice() {
return this.whatsapp.getHostDevice()
getHostDevice(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.getHostDevice()
}
@Get('/getConnectionState')
getConnectionState() {
return this.whatsapp.getConnectionState()
getConnectionState(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.getConnectionState()
}
@Get('/getBatteryLevel')
getBatteryLevel() {
return this.whatsapp.getBatteryLevel()
getBatteryLevel(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.getBatteryLevel()
}
@Get('/isConnected')
isConnected() {
return this.whatsapp.isConnected()
isConnected(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.isConnected()
}
@Get('/getWAVersion')
getWAVersion() {
return this.whatsapp.getWAVersion()
getWAVersion(@Body() request: SessionRequest) {
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
return whatsapp.getWAVersion()
}
}
+30
View File
@@ -0,0 +1,30 @@
import {ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus,} from '@nestjs/common';
import {Request, Response} from 'express';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: HttpException | Error, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
if (exception instanceof HttpException) {
response
.status(exception.getStatus())
.json(exception.getResponse());
return
}
const httpStatus = HttpStatus.INTERNAL_SERVER_ERROR
response
.status(httpStatus)
.json({
statusCode: httpStatus,
timestamp: new Date().toISOString(),
path: request.url,
error: exception.message,
stack: exception.stack,
});
}
}
+10 -5
View File
@@ -1,19 +1,24 @@
import {Controller, Get, Inject, Res} from '@nestjs/common';
import {Controller, Get, Query, Res} from '@nestjs/common';
import {ApiTags} from "@nestjs/swagger";
import {Whatsapp} from "venom-bot";
import {Readable} from "stream";
import {Response} from 'express';
import {WhatsappSessionManager} from "../whatsapp.service";
import {SessionQuery} from "./all.dto";
@Controller('api')
@ApiTags('screenshot')
export class ScreenshotController {
constructor(@Inject('WHATSAPP') private whatsapp: Whatsapp) {
constructor(private whatsappSessionManager: WhatsappSessionManager) {
}
@Get('/screenshot')
async screenshot(@Res() res: Response,) {
const buffer = await this.whatsapp.page.screenshot();
async screenshot(
@Res() res: Response,
@Query() sessionQuery: SessionQuery,
) {
const whatsappService = this.whatsappSessionManager.getService(sessionQuery.sessionName)
const buffer = await whatsappService.getScreenshotOrQRCode();
const stream = new Readable();
stream.push(buffer);
stream.push(null);
+30
View File
@@ -0,0 +1,30 @@
import {Body, Controller, Get, Post} from '@nestjs/common';
import {ApiTags} from "@nestjs/swagger";
import {WhatsappSessionManager} from "../whatsapp.service";
import {SessionRequest} from "./all.dto";
@Controller('api/sessions')
@ApiTags('sessions')
export class SessionsController {
constructor(private whatsappSessionManager: WhatsappSessionManager) {
}
@Post('/start/')
async start(@Body() request: SessionRequest) {
// Do not wait it, because we get venom instance only after scanning QR code
this.whatsappSessionManager.startSession(request.sessionName)
}
@Post('/stop/')
async stop(@Body() request: SessionRequest) {
await this.whatsappSessionManager.stopSession(request.sessionName)
}
@Get('/')
async list() {
return this.whatsappSessionManager.getAllSessions()
}
}
+13 -7
View File
@@ -1,11 +1,12 @@
import {ConsoleLogger, Logger, Module} from '@nestjs/common';
import {ChattingController} from './api/chatting.controller';
import {DeviceController} from "./api/device.controller";
import {whatsappProvider, WhatsappService} from "./whatsapp.service";
import {ConsoleLogger, Module} from '@nestjs/common';
import {WhatsappSessionManager} from "./whatsapp.service";
import {ScreenshotController} from "./api/screenshot.controller";
import {ConfigModule} from "@nestjs/config";
import {WhatsappConfigService} from "./config.service";
import {ServeStaticModule} from "@nestjs/serve-static";
import {SessionsController} from "./api/sessions.controller";
import {ChattingController} from "./api/chatting.controller";
import {DeviceController} from "./api/device.controller";
@Module({
imports: [
@@ -18,14 +19,19 @@ import {ServeStaticModule} from "@nestjs/serve-static";
inject: [WhatsappConfigService],
useFactory: (config: WhatsappConfigService) => {
return [{
rootPath: config.files_folder,
rootPath: config.filesFolder,
serveRoot: config.files_uri,
}]
},
}),
],
controllers: [ChattingController, DeviceController, ScreenshotController],
providers: [whatsappProvider, WhatsappService, ConsoleLogger, WhatsappConfigService],
controllers: [
SessionsController,
ChattingController,
DeviceController,
ScreenshotController,
],
providers: [WhatsappSessionManager, ConsoleLogger, WhatsappConfigService],
})
export class AppModule {
}
+8 -4
View File
@@ -9,7 +9,7 @@ export class WhatsappConfigService {
constructor(private configService: ConfigService) {
}
get files_url(): string {
get filesURL(): string {
return `${this.schema}://${this.hostname}:${this.port}${this.files_uri}/`
}
@@ -27,14 +27,14 @@ export class WhatsappConfigService {
)
}
get files_folder(): string {
get filesFolder(): string {
return this.configService.get(
'WHATSAPP_FILES_FOLDER',
'/tmp/whatsapp-files',
)
}
get files_lifetime(): number {
get filesLifetime(): number {
return this.configService.get<number>(
'WHATSAPP_FILES_LIFETIME',
180,
@@ -46,7 +46,11 @@ export class WhatsappConfigService {
return types ? types.split(',') : null
}
get startSession(): string | undefined {
return this.configService.get('WHATSAPP_START_SESSION', undefined)
}
get(name: string): any {
return this.configService.get(name)
}
}
}
+5 -1
View File
@@ -2,6 +2,7 @@ import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';
import {DocumentBuilder, SwaggerModule} from "@nestjs/swagger";
import {WhatsappConfigService} from "./config.service";
import {AllExceptionsFilter} from "./api/exception.filter";
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
@@ -10,13 +11,16 @@ async function bootstrap() {
});
app.enableShutdownHooks();
app.useGlobalFilters(new AllExceptionsFilter());
const options = new DocumentBuilder()
.setTitle('WhatsApp HTTP API')
.setDescription('WhatsApp HTTP API that you can configure in a click!')
.setExternalDoc("Github WhatsApp HTTP API", "https://github.com/allburov/whatsapp-http-api")
.setVersion('1.0')
.addTag('device', 'Device information')
.addTag('sessions', 'Control your WhatsApp sessions')
.addTag('screenshot', 'Get screenshot of WhatsApp and show QR code')
.addTag('chatting', 'Chat methods')
.addTag('device', 'Device information')
.addApiKey({
type: 'apiKey',
description: 'Your secret key',
+202 -72
View File
@@ -1,8 +1,8 @@
import {create, Message, Whatsapp} from "venom-bot";
import {ConsoleLogger, Inject, Injectable, Logger, OnApplicationShutdown} from "@nestjs/common";
import {ConfigService} from "@nestjs/config";
import {ConsoleLogger, Injectable, NotFoundException, OnApplicationShutdown} from "@nestjs/common";
import * as path from "path";
import {WhatsappConfigService} from "./config.service";
import {UnprocessableEntityException} from "@nestjs/common/exceptions/unprocessable-entity.exception";
import request = require('requestretry');
import mime = require('mime-types');
import fs = require('fs');
@@ -15,31 +15,11 @@ const writeFileAsync = promisify(fs.writeFile)
const SECOND = 1000;
export const whatsappProvider = {
provide: 'WHATSAPP',
useFactory: async (config: ConfigService) => create('sessionName',
(base64Qr, asciiQR) => {
console.log(asciiQR);
},
undefined,
{
headless: true,
devtools: false,
useChrome: true,
debug: false,
logQR: true,
browserArgs: ["--no-sandbox"],
autoClose: 60000,
createPathFileToken: true,
puppeteerOptions: {},
multidevice: false,
}
)
}
const ONMESSAGE_HOOK = "onMessage"
const ON_ANY_MESSAGE_HOOK = "onAnyMessage"
const ON_MESSAGE_HOOK = "onMessage"
const HOOKS = [
ONMESSAGE_HOOK,
ON_ANY_MESSAGE_HOOK,
ON_MESSAGE_HOOK,
"onStateChange",
"onAck",
// TODO: IMPLEMENTED THESE TOO
@@ -49,58 +29,85 @@ const HOOKS = [
]
const ENV_PREFIX = "WHATSAPP_HOOK_"
export enum WhatsappStatus {
STARTING = "STARTING",
SCAN_QR_CODE = "SCAN_QR_CODE",
WORKING = "WORKING",
FAILED = "FAILED",
}
@Injectable()
export class WhatsappService implements OnApplicationShutdown {
// TODO: Use environment variables
export class WhatsappService {
public status: WhatsappStatus;
private qrCodeBase64: string;
readonly filesFolder: string
readonly mimetypes: string[] | null
readonly filesLifetime: number
private RETRY_DELAY = 15
private RETRY_ATTEMPTS = 3;
readonly FILES_FOLDER: string
readonly mimetypes: string[] | null
readonly files_lifetime: number
private log: ConsoleLogger;
public whatsapp: Whatsapp;
constructor(
@Inject('WHATSAPP') private whatsapp: Whatsapp,
private config: WhatsappConfigService,
private log: ConsoleLogger,
public name: string,
) {
this.log.setContext('WhatsappService')
this.FILES_FOLDER = this.config.files_folder
this.clean_downloads()
this.name = name
this.status = WhatsappStatus.STARTING
this.log = new ConsoleLogger()
this.log.setContext(`WhatsappService - ${this.name}`)
this.filesFolder = this.config.filesFolder
this.mimetypes = this.config.mimetypes
this.files_lifetime = this.config.files_lifetime * SECOND
this.log.log('Configuring webhooks...')
for (const hook of HOOKS) {
const env_name = ENV_PREFIX + hook.toUpperCase()
const url = config.get(env_name)
if (!url) {
this.log.log(`Hook '${hook}' is disabled. Set ${env_name} environment variable to url if you want to enabled it.`)
continue
}
if (hook === ONMESSAGE_HOOK) {
this.whatsapp[hook](data => this.onMessageHook(data, url))
} else {
this.whatsapp[hook](data => this.callWebhook(data, url))
}
this.log.log(`Hook '${hook}' was enabled to url: ${url}`)
}
this.log.log('Webhooks were configured.')
this.filesLifetime = this.config.filesLifetime * SECOND
}
private clean_downloads() {
if (fs.existsSync(this.FILES_FOLDER)) {
del([`${this.FILES_FOLDER}/*`], {force: true}).then((paths) =>
console.log('Deleted files and directories:\n', paths.join('\n'))
public async start() {
try {
this.whatsapp = await create('sessionName',
(base64Qrimg, asciiQR, attempts, urlCode) => {
this.saveQRCode(base64Qrimg)
this.status = WhatsappStatus.SCAN_QR_CODE
console.log('Number of attempts to read the qrcode: ', attempts);
console.log('Terminal qrcode:');
console.log(asciiQR);
},
undefined,
{
headless: true,
devtools: false,
useChrome: true,
debug: false,
logQR: true,
browserArgs: ["--no-sandbox"],
autoClose: 60000,
createPathFileToken: true,
puppeteerOptions: {},
multidevice: false,
}
)
} else {
fs.mkdirSync(this.FILES_FOLDER)
this.log.log(`Directory '${this.FILES_FOLDER}' created from scratch`)
} catch (error) {
this.status = WhatsappStatus.FAILED
this.log.error(error)
this.saveQRCode("")
return
}
this.saveQRCode("")
this.configureWebhooks();
this.status = WhatsappStatus.WORKING
}
public async getScreenshotOrQRCode(): Promise<Buffer | string> {
if (this.status === WhatsappStatus.STARTING) {
throw new UnprocessableEntityException(`The session is starting, please try again after few seconds`);
} else if (this.status === WhatsappStatus.SCAN_QR_CODE) {
return this.getQRCode()
} else if (this.status === WhatsappStatus.WORKING) {
return await this.whatsapp.page.screenshot()
} else {
throw new UnprocessableEntityException(`Unknown status - ${this.status}`);
}
}
private callWebhook(data, url) {
this.log.log(`Sending POST to ${url}...`)
@@ -146,26 +153,149 @@ export class WhatsappService implements OnApplicationShutdown {
this.log.log(`The message ${message.id} has media, downloading it...`);
const fileName = `${message.id}.${mime.extension(message.mimetype)}`;
const filePath = path.resolve(`${this.FILES_FOLDER}/${fileName}`)
const filePath = path.resolve(`${this.filesFolder}/${fileName}`)
this.log.verbose(`Writing file to ${filePath}...`)
await writeFileAsync(filePath, buffer);
this.log.log(`The file from ${message.id} has been saved to ${filePath}`);
message.clientUrl = this.config.files_url + fileName
message.clientUrl = this.config.filesURL + fileName
this.removeFile(filePath)
return message
});
}
onApplicationShutdown(signal ?: string): any {
this.log.log('Close a browser...')
return this.whatsapp.close()
}
private removeFile(file: string) {
setTimeout(() => fs.unlink(file, () => {
this.log.log(`File ${file} was removed`)
}), this.files_lifetime)
}), this.filesLifetime)
}
/**
* Get venom instance if it's working (with no QR code required)
*/
public getWhatsapp() {
if (this.status != WhatsappStatus.WORKING) {
throw new UnprocessableEntityException(
`The session status is "${this.status}". Please scan QR code first by using GET /screenshot method.`,
);
}
return this.whatsapp
}
private configureWebhooks() {
this.log.log('Configuring webhooks...')
for (const hook of HOOKS) {
const env_name = ENV_PREFIX + hook.toUpperCase()
const url = this.config.get(env_name)
if (!url) {
this.log.log(`Hook '${hook}' is disabled. Set ${env_name} environment variable to url if you want to enabled it.`)
continue
}
if (hook === ON_MESSAGE_HOOK || hook == ON_ANY_MESSAGE_HOOK) {
this.whatsapp[hook](data => this.onMessageHook(data, url))
} else {
this.whatsapp[hook](data => this.callWebhook(data, url))
}
this.log.log(`Hook '${hook}' was enabled to url: ${url}`)
}
this.log.log('Webhooks were configured.')
}
private saveQRCode(base64Qrimg) {
base64Qrimg = base64Qrimg.replace(/^data:image\/png;base64,/, '');
this.qrCodeBase64 = base64Qrimg
}
private getQRCode() {
return Buffer.from(this.qrCodeBase64, "base64")
}
}
@Injectable()
export class WhatsappSessionManager implements OnApplicationShutdown {
private readonly sessions: Record<string, WhatsappService>;
constructor(
private config: WhatsappConfigService,
private log: ConsoleLogger,
) {
this.log.setContext('WhatsappSessionManager')
this.cleanDownloadsFolder(this.config.filesFolder)
this.sessions = {}
if (config.startSession) {
this.startSession(config.startSession)
}
}
async startSession(name: string) {
this.log.log(`Starting ${name} session...`)
const session = new WhatsappService(this.config, name)
session.start()
this.sessions[name] = session
}
getService(name: string): WhatsappService {
const session = this.sessions[name]
if (!session) {
throw new NotFoundException(
`We didn't find a session with name "${name}". Please start it first by using POST /sessions/start request`,
);
}
return session
}
getSession(name: string): Whatsapp {
const service = this.getService(name)
return service.getWhatsapp()
}
async stopSession(name: string) {
this.log.log(`Stopping ${name} session...`)
const service = this.getService(name)
if (service.whatsapp) {
await service.whatsapp.close()
}
this.log.log(`"${name}" has been stopped.`)
delete this.sessions[name]
}
getAllSessions() {
const result = Object.values(this.sessions).map((session) => {
return {name: session.name, status: session.status}
})
return result
}
async onApplicationShutdown(signal ?: string) {
this.log.log('Stop all sessions...')
for (const sessionName of Object.keys(this.sessions)) {
await this.stopSession(sessionName)
}
}
private cleanDownloadsFolder(filesFolder) {
if (fs.existsSync(filesFolder)) {
del([`${filesFolder}/*`], {force: true}).then((paths) =>
console.log('Deleted files and directories:\n', paths.join('\n'))
)
} else {
fs.mkdirSync(filesFolder)
this.log.log(`Directory '${filesFolder}' created from scratch`)
}
}
}
const SUFFIX_DIRECT_MESSAGE = "@c.us"
/**
* Add WhatsApp suffix (@c.us) to the phone number if it doesn't have it yet
* @param phone
*/
export function ensureSuffix(phone) {
if (phone.includes("@")) {
return phone
}
return phone + SUFFIX_DIRECT_MESSAGE
}
+656 -1
View File
@@ -1 +1,656 @@
{"openapi":"3.0.0","info":{"title":"WhatsApp HTTP API","description":"WhatsApp HTTP API that you can configure in a click","version":"1.0","contact":{}},"tags":[{"name":"device","description":"Device information"},{"name":"chatting","description":"Chat methods"}],"servers":[],"components":{"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"X-VENOM-TOKEN","description":"Your secret key"}},"schemas":{"MessageContactVcard":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"contactsId":{"type":"string"},"name":{"type":"string"}},"required":["chatId","contactsId","name"]},"MessageText":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"text":{"type":"string"}},"required":["chatId","text"]},"MessageLocation":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"latitude":{"type":"string"},"longitude":{"type":"string"},"title":{"type":"string"}},"required":["chatId","latitude","longitude","title"]},"MessageLinkPreview":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"url":{"type":"string"},"title":{"type":"string"}},"required":["chatId","url","title"]},"MessageImage":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"path":{"type":"string"},"filename":{"type":"string"},"caption":{"type":"string"}},"required":["chatId","path","filename","caption"]},"MessageFile":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"path":{"type":"string"},"filename":{"type":"string"},"caption":{"type":"string"}},"required":["chatId","path","filename","caption"]},"MessageReply":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"},"reply_to":{"type":"string","example":"message.id"},"text":{"type":"string"}},"required":["chatId","reply_to","text"]},"Chat":{"type":"object","properties":{"chatId":{"type":"string","example":"791231234567@c.us"}},"required":["chatId"]}}},"paths":{"/api/sendContactVcard":{"post":{"operationId":"ChattingController_sendContactVcard","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageContactVcard"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendText":{"post":{"operationId":"ChattingController_sendText","summary":"Send a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageText"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendLocation":{"post":{"operationId":"ChattingController_sendLocation","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLocation"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendLinkPreview":{"post":{"operationId":"ChattingController_sendLinkPreview","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageLinkPreview"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendImage":{"post":{"operationId":"ChattingController_sendImage","summary":"NOT IMPLEMENTED YET","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageImage"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendFile":{"post":{"operationId":"ChattingController_sendFile","summary":"NOT IMPLEMENTED YET","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFile"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/reply":{"post":{"operationId":"ChattingController_reply","summary":"Reply to a text message","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageReply"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["chatting"]}},"/api/sendSeen":{"post":{"operationId":"ChattingController_sendSeen","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}},"responses":{"201":{"description":""}},"tags":["chatting"]}},"/api/startTyping":{"post":{"operationId":"ChattingController_startTyping","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Chat"}}}},"responses":{"201":{"descriptiLine truncated
{
"openapi": "3.0.0",
"info": {
"title": "WhatsApp HTTP API",
"description": "WhatsApp HTTP API that you can configure in a click",
"version": "1.0",
"contact": {}
},
"tags": [
{
"name": "device",
"description": "Device information"
},
{
"name": "chatting",
"description": "Chat methods"
}
],
"servers": [],
"components": {
"securitySchemes": {
"api_key": {
"type": "apiKey",
"in": "header",
"name": "X-VENOM-TOKEN",
"description": "Your secret key"
}
},
"schemas": {
"MessageContactVcard": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"contactsId": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"chatId",
"contactsId",
"name"
]
},
"MessageText": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"text": {
"type": "string"
}
},
"required": [
"chatId",
"text"
]
},
"MessageLocation": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"latitude": {
"type": "string"
},
"longitude": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"chatId",
"latitude",
"longitude",
"title"
]
},
"MessageLinkPreview": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"url": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"chatId",
"url",
"title"
]
},
"MessageImage": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"path": {
"type": "string"
},
"filename": {
"type": "string"
},
"caption": {
"type": "string"
}
},
"required": [
"chatId",
"path",
"filename",
"caption"
]
},
"MessageFile": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"path": {
"type": "string"
},
"filename": {
"type": "string"
},
"caption": {
"type": "string"
}
},
"required": [
"chatId",
"path",
"filename",
"caption"
]
},
"MessageReply": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
},
"reply_to": {
"type": "string",
"example": "message.id"
},
"text": {
"type": "string"
}
},
"required": [
"chatId",
"reply_to",
"text"
]
},
"Chat": {
"type": "object",
"properties": {
"chatId": {
"type": "string",
"example": "791231234567@c.us"
}
},
"required": [
"chatId"
]
}
}
},
"paths": {
"/api/sendContactVcard": {
"post": {
"operationId": "ChattingController_sendContactVcard",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageContactVcard"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendText": {
"post": {
"operationId": "ChattingController_sendText",
"summary": "Send a text message",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageText"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendLocation": {
"post": {
"operationId": "ChattingController_sendLocation",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageLocation"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendLinkPreview": {
"post": {
"operationId": "ChattingController_sendLinkPreview",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageLinkPreview"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendImage": {
"post": {
"operationId": "ChattingController_sendImage",
"summary": "NOT IMPLEMENTED YET",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageImage"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendFile": {
"post": {
"operationId": "ChattingController_sendFile",
"summary": "NOT IMPLEMENTED YET",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageFile"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/reply": {
"post": {
"operationId": "ChattingController_reply",
"summary": "Reply to a text message",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MessageReply"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/sendSeen": {
"post": {
"operationId": "ChattingController_sendSeen",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Chat"
}
}
}
},
"responses": {
"201": {
"description": ""
}
},
"tags": [
"chatting"
]
}
},
"/api/startTyping": {
"post": {
"operationId": "ChattingController_startTyping",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Chat"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/stopTyping": {
"post": {
"operationId": "ChattingController_stopTyping",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Chat"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"chatting"
]
}
},
"/api/killServiceWorker": {
"post": {
"operationId": "DeviceController_killServiceWorker",
"parameters": [],
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/restartService": {
"post": {
"operationId": "DeviceController_restartService",
"parameters": [],
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/getHostDevice": {
"get": {
"operationId": "DeviceController_getHostDevice",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/getConnectionState": {
"get": {
"operationId": "DeviceController_getConnectionState",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"device"
]
}
},
"/api/getBatteryLevel": {
"get": {
"operationId": "DeviceController_getBatteryLevel",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "number"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/isConnected": {
"get": {
"operationId": "DeviceController_isConnected",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/getWAVersion": {
"get": {
"operationId": "DeviceController_getWAVersion",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
},
"tags": [
"device"
]
}
},
"/api/screenshot": {
"get": {
"operationId": "ScreenshotController_screenshot",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"screenshot"
]
}
}
}
}
+6 -1
View File
@@ -1,4 +1,9 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
"exclude": [
"node_modules",
"test",
"dist",
"**/*spec.ts"
]
}