Compare commits

..
70 Commits
Author SHA1 Message Date
devlikepro 05cb89779b [core] 2024.10.12
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-30 16:56:44 +07:00
devlikepro c0b2524952 [core] remove single process 2024-10-30 16:56:30 +07:00
devlikepro f1bb445593 [core] 2024.10.11
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-29 17:09:58 +07:00
devlikepro 7f777f338f [core] WEBJS - window size 1280x720
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-28 16:37:26 +07:00
devlikepro 4449dfdbd6 [core] 2024.10.10 2024-10-28 15:58:01 +07:00
devlikepro 6226e65e97 [core] Fix Singleton Issues 2024-10-28 15:57:39 +07:00
devlikepro be1783c029 [core] Fix Singleton 2024-10-28 15:57:36 +07:00
devlikepro 83a002ae5b [core] format question.md 2024-10-28 15:57:36 +07:00
devlikepro f0fcb2b329 [core] Update chrome version 2024-10-28 15:57:36 +07:00
devlikepro 5ff695a5b3 [core] Install zip unzip only for WEBJS 2024-10-28 15:57:36 +07:00
devlikepro 43fe1f076e [core] Update puppeter 2024-10-28 15:57:36 +07:00
devlikepro 1afd7e042c [core] fix channels swagger 2024-10-27 17:34:41 +07:00
devlikepro ba6474f97e [core] bufferutil is optional (no arm)
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-27 16:52:03 +07:00
devlikepro d8eed114aa Merge remote-tracking branch 'core/core' into core 2024-10-27 15:18:07 +07:00
devlikepro e61ad6f79c [core] Up dashboard 2024-10-27 14:48:32 +07:00
devlikepro 9d27616003 [core] Add UNPAIRING status 2024-10-27 14:47:59 +07:00
devlikepro 0f2641909f [core] Adjust logs 2024-10-27 14:47:58 +07:00
devlikepro 9ca96acda2 [core] Update WEBJS 2024-10-25 17:15:39 +07:00
devlikepro 77db6c5e98 [core] Fix logout (unpair) from device
fix #148 fix #327 fix #194 fix #211
2024-10-25 16:56:06 +07:00
devlikepro c4aa9804d2 [core] 2024.10.9 2024-10-25 15:18:56 +07:00
devlikepro eb7bfe3a62 [core] API - messages filters
fix #449, fix #140, fix #458
2024-10-25 15:18:55 +07:00
devlikepro 01fd7a8d56 [core] WEBJS --disable-features=site-per-process 2024-10-25 15:18:55 +07:00
devlikepro 81fb93e5d6 [core] WEBJS puppeteer headless: new 2024-10-25 15:18:54 +07:00
devlikepro 64716ec6e6 [core] poll message attributes 2024-10-25 15:18:54 +07:00
devlikepro e0f787be32 [core] Update NOWEB
fix #586
2024-10-25 15:18:54 +07:00
devlikepro 138da79599 [core] Pin whatwg-url 2024-10-25 15:18:54 +07:00
devlikepro b8b290c2a5 [core] Update puppeter and ws 2024-10-25 15:18:53 +07:00
devlikepro f35dcc0ec6 [core] Get message by id
fix #159 #484
2024-10-25 15:18:52 +07:00
devlikepro 62704e261a [core] Lint templates 2024-10-25 15:18:52 +07:00
devlikepro f16ace6169 [core] API limit, offset, sort 2024-10-25 15:18:52 +07:00
devlikepro 35628aa93b [core] Fix WEBJS restart on state change 2024-10-25 15:18:52 +07:00
devlikepro 64bf42dc1c [core] Add chat unread
fix #498
2024-10-25 15:18:52 +07:00
devlikeapro 55fc6a4282 Create question.md 2024-10-23 18:23:25 +07:00
devlikepro 1a59300cd7 [core] 2024.10.8
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
2024-10-20 16:11:06 +07:00
devlikepro 9c7d6d835e [core] Add WAHA_LOCAL_STORE_BASE_DIR
fix #597
2024-10-20 16:10:31 +07:00
devlikeapro 2a20242654 Update feature_request.md 2024-10-20 13:39:19 +07:00
devlikeapro 171f9de073 Update bug_report.md 2024-10-20 13:38:37 +07:00
devlikepro 3b4d896bcd [core] 2024.10.7
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-19 22:49:09 +07:00
devlikepro 4b602333bc [core] fix webhooks send
fix #595
2024-10-19 22:49:08 +07:00
devlikepro 347cd193dd [core] 2024.10.6
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-17 13:22:09 +07:00
devlikepro 964d8dde9b [core] await start
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-10-16 14:04:59 +07:00
devlikepro bf794ce67f [core] default 0 seconds delay 2024-10-16 14:03:32 +07:00
devlikepro 51dc9ea8c1 [core] Add restart back 2024-10-16 13:56:58 +07:00
devlikepro 791a1fd475 [core] 2024.10.5 2024-10-16 13:54:38 +07:00
devlikepro 244eb5f692 [core] Dashboard 2024-10-16 13:54:20 +07:00
devlikepro 003bc33c25 [core] Adjust logs 2024-10-16 13:53:16 +07:00
devlikepro b488b9f1df [core] Get body for buttons response 2024-10-16 13:53:15 +07:00
devlikepro ff4f0a59af [core] Provide button.id 2024-10-16 13:53:14 +07:00
devlikepro 10e2c4bbd4 [core] WEBJS - handle possible stuck STARTING
fix #586
2024-10-16 13:53:14 +07:00
devlikepro 18536330ce [core] Add WAHA_AUTO_START_DELAY_SECONDS
fix #586
2024-10-16 13:53:14 +07:00
devlikepro e894069d4e [core] fix NOWEB stuck in STARTING
fix #586
2024-10-16 13:53:13 +07:00
devlikepro 5be2ca0deb [core] delay policy
fix #587
2024-10-16 13:53:12 +07:00
devlikepro 83e2405151 [core] Button messages
fix #544
2024-10-16 13:53:12 +07:00
devlikepro 220c0dfb01 [core] Use waha logo 2024-10-16 13:53:10 +07:00
devlikepro 5474f1ec0e [core] Forward message
fix #158
2024-10-16 13:53:10 +07:00
devlikepro dbd01abde8 [core] Set FAILED before end 2024-10-16 13:53:10 +07:00
devlikepro 83bbab0969 [core] Add request id for webhooks in logs
fix #580
2024-10-16 13:53:09 +07:00
devlikepro cc6808a6cd [core] add asNote for /api/sendVideo
fix #585
2024-10-16 13:53:09 +07:00
devlikepro 191fd89cfb [core] Dashboard at the top 2024-10-16 13:53:09 +07:00
devlikepro 42e5b46365 [core] move docker-compose workers 2024-10-16 13:53:06 +07:00
devlikepro f444ce86f9 [core] no RESTART_ALL by default 2024-10-16 13:53:06 +07:00
devlikepro ce05b3d087 [core] Restore sessions 2024-10-16 13:53:06 +07:00
devlikepro ab4dd28c51 [core] docker-compose.workers.yaml 2024-10-16 13:53:05 +07:00
devlikepro 9e23b287ff [core] Use Sqlite3SchemaValidation for me repo 2024-10-16 13:53:05 +07:00
devlikepro 135c1ab10b [core] typo 2024-10-16 13:53:04 +07:00
devlikepro de680c93f6 [core] Do not check current session status 2024-10-16 13:53:04 +07:00
devlikepro 57ac57824f [core] Move startPredefinedSessions to SessionManager 2024-10-16 13:53:04 +07:00
devlikepro 38df64c191 [core] Move config to SessionManager 2024-10-16 13:53:04 +07:00
devlikepro 575969f938 [core] Move log to SessionManager 2024-10-16 13:53:04 +07:00
devlikepro 42c388e5c9 [core] Add waha logo in examples 2024-10-14 15:56:49 +07:00
59 changed files with 20217 additions and 497 deletions

No files matched your search

+14 -3
View File
@@ -57,19 +57,22 @@ WHATSAPP_DEFAULT_ENGINE=WEBJS
# Use NOWEB engine for the fastest performance
#WHATSAPP_DEFAULT_ENGINE=NOWEB
# Worker
# WAHA_WORKER_ID=waha1
#
# Sessions
# https://waha.devlike.pro/docs/how-to/config/#sessions
#
# Restart all sessions when container starts
WHATSAPP_RESTART_ALL_SESSIONS=True
# Do not print QR codes in logs
WAHA_PRINT_QR=False
#
# OR you can specify sessions by name, if you have handful amount of sessions
#WHATSAPP_START_SESSION=session1,session2
# Do not use both WHATSAPP_RESTART_ALL_SESSIONS and WHATSAPP_START_SESSION parameters
#
# Restart all sessions when container starts
# !!! Do not use it with multiple workers against one database !!!
# WHATSAPP_RESTART_ALL_SESSIONS=True
#
# Swagger
@@ -158,6 +161,14 @@ WHATSAPP_FILES_FOLDER=/app/.media
#WHATSAPP_HOOK_EVENTS=session.status,message,message.reaction
#
#
# Local Configuration
#
# WAHA_LOCAL_STORE_BASE_DIR=/app/sessions
# Remember to map the volume to the host machine to the right direction in "volumes" field in docker-compose.yml
# volumes:
# - './.sessions:/app/sessions'
#
# MongoDB Configuration
# https://waha.devlike.pro/docs/how-to/storages/#sessions---mongodb
+25 -28
View File
@@ -6,25 +6,28 @@ labels: bug
assignees: ''
---
**Describe the bug**
### 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!
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**
### Version
Get the WAHA version by calling `GET /api/version`
```json
{
"version": "2023.11.1",
"engine": "NOWEB",
"tier": "PLUS",
"browser": "/usr/bin/google-chrome-stable"
"version": "YYYY.MM.DD",
"engine": "ENGINE",
"tier": "TIER"
}
```
> You can get the WAHA version by calling `GET /api/version` Try to update to
> [the latest version](https://github.com/devlikeapro/waha/releases) before
> creating an issue!
Try to update to
[the latest version](https://github.com/devlikeapro/waha/releases) before
creating an issue!
## Steps
**To Reproduce** Steps to reproduce the behavior:
@@ -33,29 +36,23 @@ assignees: ''
3. Scroll down to '....'
4. See error
**Expected behavior**
### Expected behavior
> A clear and concise description of what you expected to happen.
A clear and concise description of what you expected to happen.
**Engine** I'm using **WEBJS** engine.
### Requests - Responses
> 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.
Please attach all sent requests, responses that you sent.
**Requests - Responses**
### Docker Logs
> Please attach all sent requests, responses that you sent.
Collect and attach related docker logs if you have any
https://waha.devlike.pro/docs/how-to/deploy/#viewing-the-logs
**Docker Logs**
### Screenshots
> Collect and attach related docker logs if you have any
> https://waha.devlike.pro/docs/how-to/deploy/#viewing-the-logs
If applicable, add screenshots to help explain your problem.
**Screenshots**
### Additional context
> If applicable, add screenshots to help explain your problem.
**Additional context**
> Add any other context about the problem here.
Add any other context about the problem here.
+4 -4
View File
@@ -9,16 +9,16 @@ 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.
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.
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.
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.
+11
View File
@@ -0,0 +1,11 @@
---
name: Question
about: I have a question about the project or feature
title: '[Question] '
labels: 'question'
assignees: ''
---
👉 Kindly use
[GitHub Discussions](https://github.com/devlikeapro/waha/discussions) to ask
questions!
+1
View File
@@ -72,4 +72,5 @@ repos:
entrypoint.sh|
.pre-commit-config.yaml|
^src/core/engines/webjs/session.webjs.core.ts|
^src/core/engines/webjs/_lodash.js|
)$
+8 -4
View File
@@ -26,7 +26,7 @@ RUN yarn build && find ./dist -name "*.d.ts" -delete
FROM node:${NODE_VERSION} AS dashboard
# Download WAHA Dashboard
ENV WAHA_DASHBOARD_SHA a3e3e31850af55161ca58cd8edb7dc9562707d14
ENV WAHA_DASHBOARD_SHA e6a72c5de2effc97d83e997cb1f361851604bae8
RUN \
wget https://github.com/devlikeapro/dashboard/archive/${WAHA_DASHBOARD_SHA}.zip \
&& unzip ${WAHA_DASHBOARD_SHA}.zip -d /tmp/dashboard \
@@ -50,8 +50,12 @@ RUN echo "USE_BROWSER=$USE_BROWSER"
# Install ffmpeg to generate previews for videos
RUN apt-get update && apt-get install -y ffmpeg --no-install-recommends && rm -rf /var/lib/apt/lists/*
# Install zip and unzip
RUN apt-get update && apt-get install -y zip unzip --no-install-recommends && rm -rf /var/lib/apt/lists/*
# Install zip and unzip - either for chromium or chrome
RUN if [ "$USE_BROWSER" = "chromium" ] || [ "$USE_BROWSER" = "chrome" ]; then \
apt-get update \
&& apt-get install -y zip unzip \
&& rm -rf /var/lib/apt/lists/*; \
fi
# Install fonts if using either chromium or chrome
RUN if [ "$USE_BROWSER" = "chromium" ] || [ "$USE_BROWSER" = "chrome" ]; then \
@@ -73,7 +77,7 @@ RUN if [ "$USE_BROWSER" = "chromium" ]; then \
# Install Chrome
# Available versions:
# https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable
ARG CHROME_VERSION="126.0.6478.182-1"
ARG CHROME_VERSION="130.0.6723.69-1"
RUN if [ "$USE_BROWSER" = "chrome" ]; then \
wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \
&& apt-get update \
@@ -0,0 +1,97 @@
# https://waha.devlike.pro/docs/how-to/install/
services:
# Just to connect
waha-dashboard:
restart: unless-stopped
image: devlikeapro/waha-plus:latest
ports:
- '127.0.0.1:3000:3000/tcp'
env_file:
- .env
environment:
- WAHA_WORKER_ID=waha-dashboard
waha1:
restart: unless-stopped
image: devlikeapro/waha-plus:latest
ports:
- '127.0.0.1:3001:3000/tcp'
volumes:
- './.media:/app/.media'
env_file:
- .env
environment:
- WAHA_WORKER_ID=waha1
- WAHA_BASE_URL=http://localhost:3001
- WHATSAPP_DEFAULT_ENGINE=NOWEB
# MongoDB
- WHATSAPP_SESSIONS_MONGO_URL=mongodb://mongodb:27017
# S3
- WAHA_MEDIA_STORAGE=S3
- WAHA_S3_REGION=eu-west-2
- WAHA_S3_BUCKET=waha
- WAHA_S3_ACCESS_KEY_ID=minioadmin
- WAHA_S3_SECRET_ACCESS_KEY=minioadmin
- WAHA_S3_ENDPOINT=http://minio:9000 # Not required if you're using AWS S3
- WAHA_S3_FORCE_PATH_STYLE=True # Required for Minio
- WAHA_S3_PROXY_FILES=True # Required for docker-compose setup
waha2:
restart: unless-stopped
image: devlikeapro/waha-plus:latest
ports:
- '127.0.0.1:3002:3000/tcp'
volumes:
- './.media:/app/.media'
env_file:
- .env
environment:
- WAHA_WORKER_ID=waha2
- WAHA_BASE_URL=http://localhost:3002
- WHATSAPP_DEFAULT_ENGINE=NOWEB
# MongoDB
- WHATSAPP_SESSIONS_MONGO_URL=mongodb://mongodb:27017
# S3
- WAHA_MEDIA_STORAGE=S3
- WAHA_S3_REGION=eu-west-2
- WAHA_S3_BUCKET=waha
- WAHA_S3_ACCESS_KEY_ID=minioadmin
- WAHA_S3_SECRET_ACCESS_KEY=minioadmin
- WAHA_S3_ENDPOINT=http://minio:9000 # Not required if you're using AWS S3
- WAHA_S3_FORCE_PATH_STYLE=True # Required for Minio
- WAHA_S3_PROXY_FILES=True # Required for docker-compose setup
# NOTE: Only if you're using MongoDB
# https://waha.devlike.pro/docs/how-to/storages/#sessions---mongodb
# Comment this block if you're not using MongoDB
mongodb:
image: mongo
container_name: mongodb
ports:
- '127.0.0.1:27017:27017/tcp'
volumes:
- mongodb_data:/data/db
restart: always
# NOTE: Only if you're using S3 to save media files
# https://waha.devlike.pro/docs/how-to/storages/#media---s3
# Comment this block if you're using S3
minio:
image: quay.io/minio/minio
container_name: minio
restart: always
ports:
- '127.0.0.1:9000:9000'
- '127.0.0.1:9001:9001'
environment:
MINIO_REGION: 'eu-west-2'
MINIO_ROOT_USER: 'minioadmin'
MINIO_ROOT_PASSWORD: 'minioadmin'
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
volumes:
# NOTE: Only if you're using MongoDB
mongodb_data: {}
minio_data: {}
-1
View File
@@ -14,7 +14,6 @@ services:
- WAHA_DASHBOARD_USERNAME=waha
- WAHA_DASHBOARD_PASSWORD=waha
- WHATSAPP_DEFAULT_ENGINE=WEBJS
- WHATSAPP_RESTART_ALL_SESSIONS=True
- WAHA_PRINT_QR=False
- WAHA_MEDIA_STORAGE=LOCAL
- WHATSAPP_FILES_LIFETIME=0
-2
View File
@@ -25,7 +25,6 @@ services:
- './.sessions:/app/.sessions'
environment:
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
# - WHATSAPP_RESTART_ALL_SESSIONS=True
# - WHATSAPP_HOOK_URL=https://webhook.site/11111111-1111-1111-1111-11111111
# - WHATSAPP_HOOK_EVENTS=message
# - WHATSAPP_API_HOSTNAME=localhost
@@ -54,7 +53,6 @@ services:
- './.sessions:/app/.sessions'
environment:
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
# - WHATSAPP_RESTART_ALL_SESSIONS=True
# - WHATSAPP_HOOK_URL=https://webhook.site/11111111-1111-1111-1111-11111111
# - WHATSAPP_HOOK_EVENTS=message
# - WHATSAPP_API_KEY=321
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+10 -1
View File
@@ -70,7 +70,7 @@
"pino-pretty": "^11.2.1",
"pretty-bytes": "5.6.0",
"promise-retry": "^2.0.1",
"puppeteer": "^21.0.0",
"puppeteer": "^23.6.0",
"qrcode": "^1.5.1",
"qrcode-terminal": "^0.12.0",
"reflect-metadata": "^0.1.13",
@@ -80,6 +80,15 @@
"swagger-ui-express": "^4.1.4",
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-channels"
},
"optionalDependencies": {
"bufferutil": "^4.0.8"
},
"resolutions": {
"bufferutil": "^4.0.8",
"ws": "^8.18.0",
"puppeteer": "^23.6.0",
"whatwg-url": "13.0.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.1",
-1
View File
@@ -14,7 +14,6 @@ docker run -d \
--env WAHA_LOG_FORMAT=JSON \
--env WAHA_LOG_LEVEL=info \
--env WHATSAPP_DEFAULT_ENGINE=WEBJS \
--env WHATSAPP_RESTART_ALL_SESSIONS=True \
--env WAHA_PRINT_QR=False \
--env WHATSAPP_FILES_LIFETIME=0 \
--env WHATSAPP_FILES_FOLDER=/app/.media \
+4 -4
View File
@@ -82,7 +82,7 @@ export class ChannelsController {
}
}
@Post('follow')
@Post(':id/follow')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Follow the channel.' })
@@ -93,7 +93,7 @@ export class ChannelsController {
return session.channelsFollowChannel(id);
}
@Post('unfollow')
@Post(':id/unfollow')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Unfollow the channel.' })
@@ -104,7 +104,7 @@ export class ChannelsController {
return session.channelsUnfollowChannel(id);
}
@Post('mute')
@Post(':id/mute')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Mute the channel.' })
@@ -115,7 +115,7 @@ export class ChannelsController {
return session.channelsMuteChannel(id);
}
@Post('unmute')
@Post(':id/unmute')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Unmute the channel.' })
+40 -5
View File
@@ -3,6 +3,7 @@ import {
Controller,
Delete,
Get,
NotFoundException,
Param,
Post,
Put,
@@ -21,7 +22,12 @@ import {
import { SessionManager } from '../core/abc/manager.abc';
import { WhatsappSession } from '../core/abc/session.abc';
import { parseBool } from '../helpers';
import { GetChatMessagesQuery, GetChatsQuery } from '../structures/chats.dto';
import {
ChatsPaginationParams,
GetChatMessageQuery,
GetChatMessagesFilter,
GetChatMessagesQuery,
} from '../structures/chats.dto';
import { EditMessageRequest } from '../structures/chatting.dto';
@ApiSecurity('api_key')
@@ -36,9 +42,9 @@ class ChatsController {
@ApiOperation({ summary: 'Get chats' })
getChats(
@WorkingSessionParam session: WhatsappSession,
@Query() query: GetChatsQuery,
@Query() pagination: ChatsPaginationParams,
) {
return session.getChats(query);
return session.getChats(pagination);
}
@Delete(':chatId')
@@ -56,13 +62,31 @@ class ChatsController {
@SessionApiParam
@ApiOperation({ summary: 'Gets messages in the chat' })
@ChatIdApiParam
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
getChatMessages(
@Query() query: GetChatMessagesQuery,
@Query() filter: GetChatMessagesFilter,
@WorkingSessionParam session: WhatsappSession,
@Param('chatId') chatId: string,
) {
const downloadMedia = parseBool(query.downloadMedia);
return session.getChatMessages(chatId, query.limit, downloadMedia);
return session.getChatMessages(chatId, query, filter);
}
@Get(':chatId/messages/:messageId')
@SessionApiParam
@ApiOperation({ summary: 'Gets message by id' })
@ChatIdApiParam
async getChatMessage(
@Query() query: GetChatMessageQuery,
@WorkingSessionParam session: WhatsappSession,
@Param('chatId') chatId: string,
@Param('messageId') messageId: string,
) {
const message = await session.getChatMessage(chatId, messageId, query);
if (!message) {
throw new NotFoundException('Message not found');
}
return message;
}
@Delete(':chatId/messages')
@@ -124,6 +148,17 @@ class ChatsController {
) {
return session.chatsUnarchiveChat(chatId);
}
@Post(':chatId/unread')
@SessionApiParam
@ChatIdApiParam
@ApiOperation({ summary: 'Unread the chat' })
unreadChat(
@WorkingSessionParam session: WhatsappSession,
@Param('chatId') chatId: string,
) {
return session.chatsUnreadChat(chatId);
}
}
export { ChatsController };
+37 -3
View File
@@ -1,5 +1,16 @@
import { Body, Controller, Get, Post, Put, Query } from '@nestjs/common';
import {
Body,
Controller,
Get,
Post,
Put,
Query,
UsePipes,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
import { SessionManager } from '../core/abc/manager.abc';
import {
@@ -8,6 +19,7 @@ import {
GetMessageQuery,
MessageContactVcardRequest,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
MessageLinkPreviewRequest,
MessageLocationRequest,
@@ -81,6 +93,25 @@ export class ChattingController {
return whatsapp.sendVideo(request);
}
@Post('/sendButtons')
@ApiOperation({
summary: 'Send buttons (interactive message)',
description: 'Send Buttons',
})
@UsePipes(new WAHAValidationPipe())
async sendButtons(@Body() request: SendButtonsRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
return whatsapp.sendButtons(request);
}
@Post('/forwardMessage')
async forwardMessage(
@Body() request: MessageForwardRequest,
): Promise<WAMessage> {
const whatsapp = await this.manager.getWorkingSession(request.session);
return await whatsapp.forwardMessage(request);
}
@Post('/sendSeen')
async sendSeen(@Body() chat: SendSeenRequest) {
const whatsapp = await this.manager.getWorkingSession(chat.session);
@@ -141,9 +172,12 @@ export class ChattingController {
@Get('/messages')
@ApiOperation({ summary: 'Get messages in a chat' })
async getMessages(@Query() query: GetMessageQuery) {
async getMessages(
@Query() query: GetMessageQuery,
@Query() filter: GetChatMessagesFilter,
) {
const whatsapp = await this.manager.getWorkingSession(query.session);
return whatsapp.getMessages(query);
return whatsapp.getChatMessages(query.chatId, query, filter);
}
@Get('/sendText')
+25 -9
View File
@@ -1,4 +1,12 @@
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import {
Body,
Controller,
Get,
Post,
Query,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { SessionManager } from '../core/abc/manager.abc';
@@ -7,7 +15,11 @@ import {
CheckNumberStatusQuery,
WANumberExistResult,
} from '../structures/chatting.dto';
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
import {
ContactQuery,
ContactRequest,
ContactsPaginationParams,
} from '../structures/contacts.dto';
@ApiSecurity('api_key')
@Controller('api/contacts')
@@ -15,6 +27,17 @@ import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
export class ContactsController {
constructor(private manager: SessionManager) {}
@Get('/all')
@ApiOperation({ summary: 'Get all contacts' })
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
async getAll(
@Query() query: SessionQuery,
@Query() pagination: ContactsPaginationParams,
) {
const whatsapp = await this.manager.getWorkingSession(query.session);
return whatsapp.getContacts(pagination);
}
@Get('/')
@ApiOperation({
summary: 'Get contact basic info',
@@ -26,13 +49,6 @@ export class ContactsController {
return whatsapp.getContact(query);
}
@Get('/all')
@ApiOperation({ summary: 'Get all contacts' })
async getAll(@Query() query: SessionQuery) {
const whatsapp = await this.manager.getWorkingSession(query.session);
return whatsapp.getContacts();
}
@Get('/check-exists')
@ApiOperation({ summary: 'Check phone number is registered in WhatsApp.' })
async checkExists(
+4 -1
View File
@@ -32,7 +32,7 @@ import * as lodash from 'lodash';
export class ServerController {
private logger: Logger;
constructor() {
constructor(private config: WhatsappConfigService) {
this.logger = new Logger('ServerController');
}
@@ -77,6 +77,9 @@ export class ServerController {
return {
startTimestamp: startTimestamp,
uptime: uptime,
worker: {
id: this.config.workerId,
},
};
}
+15 -5
View File
@@ -91,6 +91,7 @@ class SessionsController {
const start = request.start || false;
await this.manager.upsert(name, config);
if (start) {
await this.manager.assign(name);
await this.manager.start(name);
}
});
@@ -133,6 +134,8 @@ class SessionsController {
@UsePipes(new WAHAValidationPipe())
async delete(@Param('session') name: string): Promise<void> {
await this.withLock(name, async () => {
await this.manager.unassign(name);
await this.manager.unpair(name);
await this.manager.stop(name, true);
await this.manager.logout(name);
await this.manager.delete(name);
@@ -144,7 +147,7 @@ class SessionsController {
@ApiOperation({
summary: 'Start the session',
description:
'Start the session with the given name. The session must exist. Identity operation.',
'Start the session with the given name. The session must exist. Idempotent operation.',
})
@UsePipes(new WAHAValidationPipe())
async start(@Param('session') name: string): Promise<SessionDTO> {
@@ -153,10 +156,8 @@ class SessionsController {
if (!exists) {
throw new NotFoundException('Session not found');
}
const isRunning = this.manager.isRunning(name);
if (!isRunning) {
await this.manager.start(name);
}
await this.manager.assign(name);
await this.manager.start(name);
});
return await this.manager.getSessionInfo(name);
}
@@ -170,6 +171,7 @@ class SessionsController {
@UsePipes(new WAHAValidationPipe())
async stop(@Param('session') name: string): Promise<SessionDTO> {
await this.withLock(name, async () => {
await this.manager.unassign(name);
await this.manager.stop(name, false);
});
return await this.manager.getSessionInfo(name);
@@ -189,6 +191,7 @@ class SessionsController {
throw new NotFoundException('Session not found');
}
const isRunning = this.manager.isRunning(name);
await this.manager.unpair(name);
await this.manager.stop(name, true);
await this.manager.logout(name);
if (isRunning) {
@@ -211,6 +214,7 @@ class SessionsController {
if (!exists) {
throw new NotFoundException('Session not found');
}
await this.manager.assign(name);
await this.manager.stop(name, true);
await this.manager.start(name);
});
@@ -237,6 +241,7 @@ class SessionsController {
return await this.withLock(name, async () => {
const config = request.config;
await this.manager.upsert(name, config);
await this.manager.assign(name);
return await this.manager.start(name);
});
}
@@ -254,12 +259,15 @@ class SessionsController {
if (request.logout) {
// Old API did remove the session complete
await this.withLock(name, async () => {
await this.manager.unassign(name);
await this.manager.unpair(name);
await this.manager.stop(name, true);
await this.manager.logout(name);
await this.manager.delete(name);
});
} else {
await this.withLock(name, async () => {
await this.manager.unassign(name);
await this.manager.stop(name, false);
});
}
@@ -277,6 +285,8 @@ class SessionsController {
): Promise<void> {
const name = request.name;
await this.withLock(name, async () => {
await this.manager.unassign(name);
await this.manager.unpair(name);
await this.manager.stop(name, true);
await this.manager.logout(name);
await this.manager.delete(name);
+21
View File
@@ -34,6 +34,27 @@ export class WhatsappConfigService {
return baseUrl.replace(/\/$/, '');
}
get workerId(): string {
return this.configService.get('WAHA_WORKER_ID', '');
}
get shouldRestartWorkerSessions(): boolean {
const value = this.configService.get(
'WAHA_WORKER_RESTART_SESSIONS',
'true',
);
return parseBool(value);
}
get autoStartDelaySeconds(): number {
const value = this.configService.get('WAHA_AUTO_START_DELAY_SECONDS', '0');
try {
return parseInt(value, 10);
} catch (error) {
return 0;
}
}
get mimetypes(): string[] {
if (!this.shouldDownloadMedia) {
return ['mimetype/ignore-all-media'];
+2 -2
View File
@@ -20,11 +20,11 @@ export class SwaggerConfiguratorCore {
get description() {
return (
'WhatsApp HTTP API that you can run in a click!<br/>' +
'<b>WhatsApp HTTP API</b> that you can run in a click!<br/>' +
'<a href="/dashboard"><b>📊 Dashboard</b></a><br/>' +
'<br/>' +
'Learn more:' +
'<ul>' +
'<li><a href="/dashboard">WAHA Dashboard</a></li>' +
'<li><a href="https://waha.devlike.pro/" target="_blank">Documentation</a></li>' +
'<li><a href="https://waha.devlike.pro/docs/how-to/engines/#features" target="_blank">Supported features in engines</a></li>' +
'<li><a href="https://github.com/devlikeapro/waha" target="_blank">GitHub - WAHA Core</a></li>' +
+37 -1
View File
@@ -2,11 +2,14 @@ import {
BeforeApplicationShutdown,
UnprocessableEntityException,
} from '@nestjs/common';
import { WhatsappConfigService } from '@waha/config.service';
import { ISessionMeRepository } from '@waha/core/storage/ISessionMeRepository';
import { ISessionWorkerRepository } from '@waha/core/storage/ISessionWorkerRepository';
import { WAHAWebhook } from '@waha/structures/webhooks.dto';
import { waitUntil } from '@waha/utils/promiseTimeout';
import { VERSION } from '@waha/version';
import { EventEmitter } from 'events';
import { PinoLogger } from 'nestjs-pino';
import {
WAHAEngine,
@@ -32,14 +35,33 @@ export abstract class SessionManager implements BeforeApplicationShutdown {
public sessionAuthRepository: ISessionAuthRepository;
public sessionConfigRepository: ISessionConfigRepository;
protected sessionMeRepository: ISessionMeRepository;
protected sessionWorkerRepository: ISessionWorkerRepository;
public events: EventEmitter;
private lock: any;
WAIT_STATUS_INTERVAL = 500;
WAIT_STATUS_TIMEOUT = 5_000;
protected constructor() {
protected constructor(
protected config: WhatsappConfigService,
protected log: PinoLogger,
) {
this.lock = new AsyncLock({ maxPending: Infinity });
this.log.setContext(SessionManager.name);
}
protected startPredefinedSessions() {
const startSessions = this.config.startSessions;
startSessions.forEach((sessionName) => {
this.withLock(sessionName, async () => {
const log = this.log.logger.child({ session: sessionName });
log.info(`Restarting PREDEFINED session...`);
await this.start(sessionName).catch((error) => {
log.error(`Failed to start PREDEFINED session: ${error}`);
log.error(error.stack);
});
});
});
}
public withLock(name: string, fn: () => any) {
@@ -72,12 +94,26 @@ export abstract class SessionManager implements BeforeApplicationShutdown {
abstract logout(name: string): Promise<void>;
abstract unpair(name: string): Promise<void>;
abstract getSession(name: string): WhatsappSession;
abstract getSessionInfo(name: string): Promise<SessionDetailedInfo | null>;
abstract getSessions(all: boolean): Promise<SessionInfo[]>;
get workerId() {
return this.config.workerId;
}
async assign(name: string) {
await this.sessionWorkerRepository?.assign(name, this.workerId);
}
async unassign(name: string) {
await this.sessionWorkerRepository?.unassign(name, this.workerId);
}
protected handleSessionEvent(event: WAHAEvents, session: WhatsappSession) {
return (payload: any) => {
const me = session.getSessionMeInfo();
+45 -11
View File
@@ -3,8 +3,15 @@ import {
CreateChannelRequest,
ListChannelsQuery,
} from '@waha/structures/channels.dto';
import { GetChatsQuery } from '@waha/structures/chats.dto';
import {
GetChatMessageQuery,
GetChatMessagesFilter,
GetChatMessagesQuery,
} from '@waha/structures/chats.dto';
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
import { Label, LabelID } from '@waha/structures/labels.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { WAMessage } from '@waha/structures/responses.dto';
import { LoggerBuilder } from '@waha/utils/logging';
import { EventEmitter } from 'events';
import * as fs from 'fs';
@@ -15,9 +22,9 @@ import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
GetMessageQuery,
MessageContactVcardRequest,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
MessageLinkPreviewRequest,
MessageLocationRequest,
@@ -108,6 +115,7 @@ export abstract class WhatsappSession {
protected proxyConfig?: ProxyConfig;
public sessionConfig?: SessionConfig;
protected engineConfig?: any;
protected unpairing: boolean = false;
private _status: WAHASessionStatus;
private shouldPrintQR: boolean;
@@ -135,6 +143,11 @@ export abstract class WhatsappSession {
}
protected set status(value: WAHASessionStatus) {
if (this.unpairing && value !== WAHASessionStatus.STOPPED) {
// In case of unpairing
// wait for STOPPED event, ignore the rest
return;
}
this._status = value;
const body: WASessionStatusBody = { name: this.name, status: value };
this.events.emit(WAHAEvents.SESSION_STATUS, body);
@@ -165,6 +178,7 @@ export abstract class WhatsappSession {
'--disable-default-apps',
'--disable-dev-shm-usage',
'--disable-extensions',
'--disable-features=site-per-process',
'--disable-gpu',
'--disable-offer-store-unmasked-wallet-cards',
'--disable-offline-load-stale-cache',
@@ -190,8 +204,9 @@ export abstract class WhatsappSession {
'--password-store=basic',
'--renderer-process-limit=2', // https://superuser.com/questions/654565/how-to-run-google-chrome-in-a-single-process
'--safebrowsing-disable-auto-update',
'--single-process',
// '--single-process',
'--use-mock-keychain',
'--window-size=1280,720',
];
}
@@ -205,6 +220,11 @@ export abstract class WhatsappSession {
/** Stop the session */
abstract stop(): Promise<void>;
/* Unpair the account */
async unpair(): Promise<void> {
return;
}
/** Subscribe the handler to specific hook */
subscribeSessionEvent(
hook: WAHAEvents | string,
@@ -279,6 +299,8 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
abstract forwardMessage(request: MessageForwardRequest): Promise<WAMessage>;
abstract sendImage(request: MessageImageRequest);
abstract sendFile(request: MessageFileRequest);
@@ -289,6 +311,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
sendButtons(request: SendButtonsRequest) {
throw new NotImplementedByEngineError();
}
abstract reply(request: MessageReplyRequest);
abstract sendSeen(chat: ChatRequest);
@@ -297,10 +323,6 @@ export abstract class WhatsappSession {
abstract stopTyping(chat: ChatRequest);
getMessages(query: GetMessageQuery) {
throw new NotImplementedByEngineError();
}
abstract setReaction(request: MessageReactionRequest);
setStar(request: MessageStarRequest): Promise<void> {
@@ -310,7 +332,7 @@ export abstract class WhatsappSession {
/**
* Chats methods
*/
public getChats(query: GetChatsQuery) {
public getChats(pagination: PaginationParams) {
throw new NotImplementedByEngineError();
}
@@ -320,12 +342,20 @@ export abstract class WhatsappSession {
public getChatMessages(
chatId: string,
limit: number,
downloadMedia: boolean,
query: GetChatMessagesQuery,
filter: GetChatMessagesFilter,
) {
throw new NotImplementedByEngineError();
}
public getChatMessage(
chatId: string,
messageId: string,
query: GetChatMessageQuery,
): Promise<null | WAMessage> {
throw new NotImplementedByEngineError();
}
public deleteMessage(chatId: string, messageId: string) {
throw new NotImplementedByEngineError();
}
@@ -350,6 +380,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public chatsUnreadChat(chatId: string): Promise<any> {
throw new NotImplementedByEngineError();
}
/**
* Labels methods
*/
@@ -377,7 +411,7 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
public getContacts() {
public getContacts(pagination: PaginationParams) {
throw new NotImplementedByEngineError();
}
+107
View File
@@ -0,0 +1,107 @@
import { generateWAMessageFromContent, proto } from '@adiwajshing/baileys';
import { Button, ButtonType } from '@waha/structures/chatting.buttons.dto';
function toName(type: ButtonType) {
switch (type) {
case ButtonType.REPLY:
return 'quick_reply';
case ButtonType.URL:
return 'cta_url';
case ButtonType.CALL:
return 'cta_call';
case ButtonType.COPY:
return 'cta_copy';
}
}
export function randomId() {
// generate 16 random digits
return Math.random().toString().slice(2, 18);
}
export function buttonToJson(button: Button) {
const name = toName(button.type);
const buttonParams: any = {
display_text: button.text,
id: button.id || randomId(),
disabled: false,
};
switch (button.type) {
case ButtonType.REPLY:
break;
case ButtonType.CALL:
buttonParams.phone_number = button.phoneNumber;
break;
case ButtonType.COPY:
buttonParams.copy_code = button.copyCode;
break;
case ButtonType.URL:
buttonParams.url = button.url;
buttonParams.merchant_url = button.url;
break;
}
return {
name: name,
buttonParamsJson: JSON.stringify(buttonParams),
};
}
export async function sendButtonMessage(
sock: any,
chatId: string,
buttons: Button[],
header?: string,
headerImage?: any,
body?: string,
footer?: string,
) {
const data = {
viewOnceMessage: {
message: {
messageContextInfo: {
deviceListMetadata: {},
deviceListMetadataVersion: 2,
},
interactiveMessage: {
body: undefined,
header: undefined,
footer: undefined,
nativeFlowMessage: {
buttons: buttons.map(buttonToJson),
messageParamsJson: JSON.stringify({
from: 'api',
templateId: randomId(),
}),
},
},
},
},
};
if (header || headerImage) {
data.viewOnceMessage.message.interactiveMessage.header = {
title: header,
hasMediaAttachment: !!headerImage,
imageMessage: headerImage,
};
}
if (body) {
data.viewOnceMessage.message.interactiveMessage.body = {
text: body,
};
}
if (footer) {
data.viewOnceMessage.message.interactiveMessage.footer = {
text: footer,
};
}
const msg = proto.Message.fromObject(data);
const fullMessage = generateWAMessageFromContent(chatId, msg, {
userJid: sock?.user?.id,
});
await sock.relayMessage(chatId, fullMessage.message, {
messageId: fullMessage.key.id,
});
return fullMessage;
}
+124 -29
View File
@@ -26,9 +26,10 @@ import { LabelAssociationType } from '@adiwajshing/baileys/lib/Types/LabelAssoci
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import { Logger as BaileysLogger } from '@adiwajshing/baileys/node_modules/pino';
import { UnprocessableEntityException } from '@nestjs/common';
import { sendButtonMessage } from '@waha/core/engines/noweb/noweb.buttons';
import { NowebInMemoryStore } from '@waha/core/engines/noweb/store/NowebInMemoryStore';
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
import { flipObject, parseBool, splitAt } from '@waha/helpers';
import { flipObject, splitAt } from '@waha/helpers';
import { PairingCodeResponse } from '@waha/structures/auth.dto';
import { CallData } from '@waha/structures/calls.dto';
import {
@@ -37,37 +38,43 @@ import {
CreateChannelRequest,
ListChannelsQuery,
} from '@waha/structures/channels.dto';
import { GetChatsQuery } from '@waha/structures/chats.dto';
import {
GetChatMessageQuery,
GetChatMessagesFilter,
GetChatMessagesQuery,
} from '@waha/structures/chats.dto';
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
import { ContactQuery, ContactRequest } from '@waha/structures/contacts.dto';
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
import {
Label,
LabelChatAssociation,
LabelID,
} from '@waha/structures/labels.dto';
import { ReplyToMessage } from '@waha/structures/message.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import {
PollVote,
PollVotePayload,
WAMessageAckBody,
} from '@waha/structures/webhooks.dto';
import { LoggerBuilder } from '@waha/utils/logging';
import { waitUntil } from '@waha/utils/promiseTimeout';
import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import { SinglePeriodicJobRunner } from '@waha/utils/SinglePeriodicJobRunner';
import * as Buffer from 'buffer';
import { Agent } from 'https';
import * as lodash from 'lodash';
import { toNumber } from 'lodash';
import * as NodeCache from 'node-cache';
import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
GetMessageQuery,
MessageContactVcardRequest,
MessageDestination,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
MessageLinkPreviewRequest,
MessageLocationRequest,
@@ -209,7 +216,18 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
async start() {
this.status = WAHASessionStatus.STARTING;
await this.buildClient();
await this.buildClient().catch((err) => {
this.logger.error('Failed to start the client');
this.logger.error(err, err.stack);
this.status = WAHASessionStatus.FAILED;
this.restartClient();
});
}
async unpair() {
this.unpairing = true;
this.shouldRestart = false;
await this.sock?.logout();
}
getSocketConfig(agent, state): any {
@@ -279,13 +297,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.loggerBuilder.child({ name: NowebPersistentStore.name }),
storage,
);
await this.store.init().catch((err) => {
this.logger.error(`Failed to initialize storage or store: ${err}`);
this.status = WAHASessionStatus.FAILED;
this.end();
this.store?.close();
throw err;
});
await this.store.init();
} else {
this.logger.debug('Using NowebInMemoryStore');
this.store = new NowebInMemoryStore();
@@ -360,6 +372,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
);
return;
}
await this.end();
await this.start();
});
}
@@ -393,8 +406,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.logger.error(
`Connection closed due to '${lastDisconnect.error}', do not reconnect the session.`,
);
await this.end();
this.status = WAHASessionStatus.FAILED;
await this.failed();
}
}
@@ -412,6 +424,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.shouldRestart = false;
this.startDelayedJob.cancel();
this.autoRestartJob.stop();
if (this.authNOWEBStore && this.status == WAHASessionStatus.WORKING) {
this.logger.info('Saving creds before stopping...');
await this.authNOWEBStore.saveCreds().catch((e) => {
@@ -423,9 +436,26 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
this.status = WAHASessionStatus.STOPPED;
this.events.removeAllListeners();
await this.end();
await this.store?.close();
}
protected async failed() {
this.shouldRestart = false;
this.startDelayedJob.cancel();
this.autoRestartJob.stop();
// We'll restart the client if it's in the process of unpairing
this.status = WAHASessionStatus.FAILED;
if (this.unpairing) {
// Wait for unpairing to complete before ending the socket
await sleep(1_000);
}
await this.end();
await this.store?.close();
return;
}
private issueMessageUpdateOnEdits() {
@@ -660,6 +690,30 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
throw new AvailableInPlusVersion();
}
protected async uploadMedia(
file: RemoteFile | BinaryFile,
type,
): Promise<any> {
if (file && ('url' in file || 'data' in file)) {
throw new AvailableInPlusVersion('Sending media (image, video, pdf)');
}
return;
}
async sendButtons(request: SendButtonsRequest) {
const chatId = toJID(this.ensureSuffix(request.chatId));
const headerImage = await this.uploadMedia(request.headerImage, 'image');
return await sendButtonMessage(
this.sock,
chatId,
request.buttons,
request.header,
headerImage,
request.body,
request.footer,
);
}
sendLocation(request: MessageLocationRequest) {
return this.sock.sendMessage(request.chatId, {
location: {
@@ -669,6 +723,23 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
});
}
async forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
const key = parseMessageIdSerialized(request.messageId);
const forwardMessage = await this.store.loadMessage(key.remoteJid, key.id);
if (!forwardMessage) {
throw new UnprocessableEntityException(
`Message with id '${request.messageId}' not found`,
);
}
const chatId = toJID(this.ensureSuffix(request.chatId));
const message = {
forward: forwardMessage,
force: true,
};
const result = await this.sock.sendMessage(chatId, message, {});
return this.toWAMessage(result);
}
sendLinkPreview(request: MessageLinkPreviewRequest) {
const text = `${request.title}\n${request.url}`;
const chatId = toJID(this.ensureSuffix(request.chatId));
@@ -698,19 +769,17 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.sock.sendPresenceUpdate('paused', chatId);
}
async getMessages(query: GetMessageQuery) {
return this.getChatMessages(query.chatId, query.limit, query.downloadMedia);
}
public async getChatMessages(
chatId: string,
limit: number,
downloadMedia: boolean,
query: GetChatMessagesQuery,
filter: GetChatMessagesFilter,
) {
downloadMedia = parseBool(downloadMedia);
const downloadMedia = query.downloadMedia;
const pagination = query as PaginationParams;
const messages = await this.store.getMessagesByJid(
toJID(chatId),
toNumber(limit),
filter,
pagination,
);
const promises = [];
@@ -722,6 +791,17 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return result;
}
public async getChatMessage(
chatId: string,
messageId: string,
query: GetChatMessageQuery,
): Promise<null | WAMessage> {
const key = parseMessageIdSerialized(messageId, true);
const message = await this.store.getMessageById(toJID(chatId), key.id);
if (!message) return null;
return await this.processIncomingMessage(message, query.downloadMedia);
}
async setReaction(request: MessageReactionRequest) {
const key = parseMessageIdSerialized(request.messageId);
const reactionMessage = {
@@ -750,8 +830,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
* Chats methods
*/
async getChats(query: GetChatsQuery) {
const chats = await this.store.getChats(query.limit, query.offset);
async getChats(pagination: PaginationParams) {
const chats = await this.store.getChats(pagination);
// Remove unreadCount, it's not ready yet
chats.forEach((chat) => delete chat.unreadCount);
return chats;
@@ -762,7 +842,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
archive: boolean,
): Promise<any> {
const jid = toJID(chatId);
const messages = await this.store.getMessagesByJid(jid, 1);
const messages = await this.store.getMessagesByJid(jid, {}, { limit: 1 });
return await this.sock.chatModify(
{ archive: archive, lastMessages: messages },
jid,
@@ -777,6 +857,15 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.chatsPutArchive(chatId, false);
}
public async chatsUnreadChat(chatId: string): Promise<any> {
const jid = toJID(chatId);
const messages = await this.store.getMessagesByJid(jid, {}, { limit: 1 });
return await this.sock.chatModify(
{ markRead: false, lastMessages: messages },
jid,
);
}
/**
* Labels methods
*/
@@ -836,8 +925,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.toWAContact(contact);
}
async getContacts() {
const contacts = await this.store.getContacts();
async getContacts(pagination: PaginationParams) {
const contacts = await this.store.getContacts(pagination);
return contacts.map(this.toWAContact);
}
@@ -1348,6 +1437,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
// @ts-ignore - AudioMessage doesn't have caption field
body = mediaContent?.caption;
}
// Response for buttons
if (!body) {
body = message.templateButtonReplyMessage?.selectedDisplayText;
}
return body;
}
@@ -1642,6 +1735,8 @@ function toCusFormat(remoteJid) {
return ensureSuffix(number);
}
export const ALL_JID = 'all@s.whatsapp.net';
/**
* Convert from 11111111111@c.us to 11111111111@s.whatsapp.net
* @param chatId
@@ -1,11 +1,12 @@
import { Chat } from '@adiwajshing/baileys';
import { PaginationParams } from '@waha/structures/pagination.dto';
export interface IChatRepository {
getAll(): Promise<Chat[]>;
getAllByIds(ids: string[]): Promise<Chat[]>;
getAllWithMessages(limit?: number, offset?: number): Promise<Chat[]>;
getAllWithMessages(pagination: PaginationParams): Promise<Chat[]>;
getById(id: string): Promise<Chat | null>;
@@ -1,7 +1,8 @@
import { Contact } from '@adiwajshing/baileys';
import { PaginationParams } from '@waha/structures/pagination.dto';
export interface IContactRepository {
getAll(): Promise<Contact[]>;
getAll(pagination?: PaginationParams): Promise<Contact[]>;
getById(id: string): Promise<Contact | null>;
@@ -1,3 +1,6 @@
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
export interface IMessagesRepository {
deleteAll(): Promise<void>;
@@ -5,7 +8,11 @@ export interface IMessagesRepository {
upsertOne(message: any): Promise<void>;
getAllByJid(jid: string, limit: number): Promise<any[]>;
getAllByJid(
jid: string,
filter: GetChatMessagesFilter,
pagination: PaginationParams,
): Promise<any[]>;
getByJidById(jid: string, id: string): Promise<any | null>;
+11 -3
View File
@@ -5,6 +5,8 @@ import {
proto,
} from '@adiwajshing/baileys';
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
export interface INowebStore {
presences: any;
@@ -17,11 +19,17 @@ export interface INowebStore {
loadMessage(jid: string, id: string): Promise<proto.IWebMessageInfo>;
getMessagesByJid(chatId: string, limit: number): Promise<any>;
getMessagesByJid(
chatId: string,
filter: GetChatMessagesFilter,
pagination: PaginationParams,
): Promise<any>;
getChats(limit?: number, offset?: number): Promise<Chat[]>;
getMessageById(chatId: string, messageId: string): Promise<any>;
getContacts(): Promise<Contact[]>;
getChats(pagination: PaginationParams): Promise<Chat[]>;
getContacts(pagination: PaginationParams): Promise<Contact[]>;
getContactById(jid: string): Promise<Contact>;
@@ -1,6 +1,8 @@
import { Chat, Contact, makeInMemoryStore, proto } from '@adiwajshing/baileys';
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
import { BadRequestException } from '@nestjs/common';
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { INowebStore } from './INowebStore';
@@ -37,15 +39,23 @@ export class NowebInMemoryStore implements INowebStore {
return this.store.loadMessage(jid, id);
}
getMessagesByJid(chatId: string, limit: number): Promise<any> {
getMessagesByJid(
chatId: string,
filter: GetChatMessagesFilter,
pagination: PaginationParams,
): Promise<any> {
throw new BadRequestException(this.errorMessage);
}
getChats(limit?: number, offset?: number): Promise<Chat[]> {
getMessageById(chatId: string, messageId: string): Promise<any> {
throw new BadRequestException(this.errorMessage);
}
getContacts(): Promise<Contact[]> {
getChats(pagination: PaginationParams): Promise<Chat[]> {
throw new BadRequestException(this.errorMessage);
}
getContacts(pagination: PaginationParams): Promise<Contact[]> {
throw new BadRequestException(this.errorMessage);
}
@@ -16,6 +16,8 @@ import {
} from '@adiwajshing/baileys/lib/Types/LabelAssociation';
import { ILabelAssociationRepository } from '@waha/core/engines/noweb/store/ILabelAssociationsRepository';
import { ILabelsRepository } from '@waha/core/engines/noweb/store/ILabelsRepository';
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams, SortOrder } from '@waha/structures/pagination.dto';
import { toNumber } from 'lodash';
import { Logger } from 'pino';
@@ -322,20 +324,32 @@ export class NowebPersistentStore implements INowebStore {
return proto.WebMessageInfo.fromObject(data);
}
getMessagesByJid(chatId: string, limit: number) {
return this.messagesRepo.getAllByJid(toJID(chatId), toNumber(limit));
getMessagesByJid(
chatId: string,
filter: GetChatMessagesFilter,
pagination: PaginationParams,
): Promise<any> {
pagination.sortBy = 'messageTimestamp';
pagination.sortOrder = SortOrder.DESC;
return this.messagesRepo.getAllByJid(chatId, filter, pagination);
}
getChats(limit?: number, offset?: number): Promise<Chat[]> {
return this.chatRepo.getAllWithMessages(limit, offset);
getMessageById(chatId: string, messageId: string): Promise<any> {
return this.messagesRepo.getByJidById(chatId, messageId);
}
getChats(pagination: PaginationParams): Promise<Chat[]> {
pagination.sortBy ||= 'conversationTimestamp';
pagination.sortOrder ||= SortOrder.DESC;
return this.chatRepo.getAllWithMessages(pagination);
}
getContactById(jid) {
return this.contactRepo.getById(jid);
}
getContacts() {
return this.contactRepo.getAll();
getContacts(pagination: PaginationParams) {
return this.contactRepo.getAll(pagination);
}
getLabels(): Promise<Label[]> {
@@ -1,23 +1,24 @@
import { Chat } from '@adiwajshing/baileys';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { KnexPaginator } from '@waha/utils/Paginator';
import { IChatRepository } from '../IChatRepository';
import { NOWEBSqlite3KVRepository } from './NOWEBSqlite3KVRepository';
class ChatPaginator extends KnexPaginator {
indexes = ['id', 'conversationTimestamp'];
}
export class Sqlite3ChatRepository
extends NOWEBSqlite3KVRepository<Chat>
implements IChatRepository
{
async getAllWithMessages(limit?: number, offset?: number): Promise<Chat[]> {
protected Paginator = ChatPaginator;
async getAllWithMessages(pagination: PaginationParams): Promise<Chat[]> {
// Get chats with conversationTimestamp is not Null
let query = this.select()
.whereNotNull('conversationTimestamp')
.orderBy('conversationTimestamp', 'desc');
if (limit != null) {
query = query.limit(limit);
}
if (offset != null) {
query = query.offset(offset);
}
let query = this.select().whereNotNull('conversationTimestamp');
query = this.pagination(query, pagination);
return await this.all(query);
}
}
@@ -1,8 +1,16 @@
import { Contact } from '@adiwajshing/baileys';
import { KnexPaginator } from '@waha/utils/Paginator';
import { IContactRepository } from '../IContactRepository';
import { NOWEBSqlite3KVRepository } from './NOWEBSqlite3KVRepository';
class ContactPaginator extends KnexPaginator {
indexes = ['id'];
}
export class Sqlite3ContactRepository
extends NOWEBSqlite3KVRepository<Contact>
implements IContactRepository {}
implements IContactRepository
{
protected Paginator = ContactPaginator;
}
@@ -1,3 +1,7 @@
import { ALL_JID } from '@waha/core/engines/noweb/session.noweb.core';
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { IMessagesRepository } from '../IMessagesRepository';
import { NOWEBSqlite3KVRepository } from './NOWEBSqlite3KVRepository';
@@ -9,11 +13,36 @@ export class Sqlite3MessagesRepository
return this.upsertMany(messages);
}
getAllByJid(jid: string, limit: number): Promise<any[]> {
const query = this.select()
.where({ jid: jid })
.limit(limit)
.orderBy('messageTimestamp', 'DESC');
async getAllByJid(
jid: string,
filter: GetChatMessagesFilter,
pagination: PaginationParams,
): Promise<any[]> {
let query = this.select();
if (jid !== ALL_JID) {
query = this.select().where({ jid: jid });
}
if (filter['filter.timestamp.lte'] != null) {
query = query.where(
'messageTimestamp',
'<=',
filter['filter.timestamp.lte'],
);
}
if (filter['filter.timestamp.gte'] != null) {
query = query.where(
'messageTimestamp',
'>=',
filter['filter.timestamp.gte'],
);
}
if (filter['filter.fromMe'] != null) {
// filter by data->"$.key.fromMe"
query = query.whereRaw("data->'$.key.fromMe' = ?", [
filter['filter.fromMe'] ? 'true' : 'false',
]);
}
query = this.pagination(query, pagination);
return this.all(query);
}
+141
View File
@@ -0,0 +1,141 @@
import * as fs from 'fs';
import * as path from 'path';
import pino, { Logger } from 'pino';
import { AuthStrategy, Client } from 'whatsapp-web.js';
async function isValidPath(path: string) {
try {
await fs.promises.access(path);
return true;
} catch {
return false;
}
}
export class LocalAuth implements AuthStrategy {
private readonly logger: Logger;
private client: Client;
private readonly clientId: string;
private readonly dataPath: string;
private readonly rmMaxRetries: number;
private userDataDir: string;
setup(client: Client) {
this.client = client;
}
constructor({ clientId, dataPath, rmMaxRetries, logger }) {
const idRegex = /^[-_\w]+$/i;
if (clientId && !idRegex.test(clientId)) {
throw new Error(
'Invalid clientId. Only alphanumeric characters, underscores and hyphens are allowed.',
);
}
this.dataPath = path.resolve(dataPath || './.wwebjs_auth/');
this.clientId = clientId;
this.rmMaxRetries = rmMaxRetries ?? 4;
this.logger = logger || pino({ name: LocalAuth.name });
}
async beforeBrowserInitialized() {
// @ts-ignore
const puppeteerOpts = this.client.options.puppeteer;
const sessionDirName = this.clientId
? `session-${this.clientId}`
: 'session';
const dirPath = path.join(this.dataPath, sessionDirName);
if (puppeteerOpts.userDataDir && puppeteerOpts.userDataDir !== dirPath) {
throw new Error(
'LocalAuth is not compatible with a user-supplied userDataDir.',
);
}
fs.mkdirSync(dirPath, { recursive: true });
// @ts-ignore
this.client.options.puppeteer = {
...puppeteerOpts,
userDataDir: dirPath,
};
this.userDataDir = dirPath;
await this.removeSingletonFiles(dirPath);
}
/**
* Find in direction Singleton* files and try to remove it
* Fix for SingletonLock and other files
*/
private async removeSingletonFiles(dir: string) {
const files = await fs.promises.readdir(dir);
for (const file of files) {
if (file.startsWith('Singleton')) {
const filePath = path.join(dir, file);
try {
await fs.promises.rm(filePath, {
maxRetries: this.rmMaxRetries,
recursive: true,
force: true,
});
} catch (err) {
this.logger.error(err, `Error deleting: ${filePath}`);
}
}
}
}
async logout() {
if (this.userDataDir) {
await this.removePathSilently(this.userDataDir);
}
}
private async removePathSilently(path: string) {
const exists = await isValidPath(path);
if (!exists) {
return;
}
try {
await fs.promises.rm(path, {
maxRetries: this.rmMaxRetries,
recursive: true,
force: true,
});
} catch (err) {
this.logger.error(err, `Error deleting: ${path}`);
}
}
async afterBrowserInitialized() {
return;
}
async onAuthenticationNeeded() {
return {
failed: false,
restart: false,
failureEventPayload: undefined,
};
}
async getAuthEventPayload() {
return;
}
async afterAuthReady() {
return;
}
async disconnect() {
return;
}
async destroy() {
return;
}
}
-15
View File
@@ -1,15 +0,0 @@
exports.LoadUtils = () => {
window.WAHA = {};
window.WAHA.getChats = async (limit, offset) => {
let chats = window.Store.Chat.getModelsArray();
if (limit || offset) {
offset = offset || 0;
limit = limit || Infinity;
chats = chats.slice(offset, offset + limit);
}
const chatPromises = chats.map((chat) => window.WWebJS.getChatModel(chat));
return await Promise.all(chatPromises);
};
};
+109 -16
View File
@@ -1,7 +1,17 @@
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import * as lodash from 'lodash';
import { Client } from 'whatsapp-web.js';
import { Message } from 'whatsapp-web.js/src/structures';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { LoadUtils } = require('./WahaInjected');
const { LoadWAHA } = require('./_WAHA.js');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { LoadLodash } = require('./_lodash.js');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { LoadPaginator } = require('./_Paginator.js');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ChatFactory = require('whatsapp-web.js/src/factories/ChatFactory');
@@ -12,27 +22,110 @@ export class WebjsClient extends Client {
// We can still call and override it
// @ts-ignore
await super.inject();
//Load util functions (serializers, helper functions)
await this.pupPage.evaluate(LoadUtils);
// Load util functions (serializers, helper functions)
await this.pupPage.evaluate(LoadLodash);
await this.pupPage.evaluate(LoadPaginator);
await this.pupPage.evaluate(LoadWAHA);
}
async getChats(limit?: number, offset?: number) {
if (limit == null && offset == null) {
async unpair() {
await this.pupPage.evaluate(async () => {
if (
// @ts-ignore
window.Store &&
// @ts-ignore
window.Store.AppState &&
// @ts-ignore
typeof window.Store.AppState.logout === 'function'
) {
// @ts-ignore
await window.Store.AppState.logout();
}
});
}
async getChats(pagination?: PaginationParams) {
if (lodash.isEmpty(pagination)) {
return await super.getChats();
}
return await this.getChats2(limit, offset);
}
private async getChats2(limit?: number, offset?: number) {
const chats = await this.pupPage.evaluate(
async (limit, offset) => {
// @ts-ignore
return await window.WAHA.getChats(limit, offset);
},
limit,
offset,
);
// Get paginated chats
pagination.limit ||= Infinity;
pagination.offset ||= 0;
const chats = await this.pupPage.evaluate(async (pagination) => {
// @ts-ignore
return await window.WAHA.getChats(pagination);
}, pagination);
return chats.map((chat) => ChatFactory.create(this, chat));
}
async getMessages(
chatId: string,
filter: GetChatMessagesFilter,
pagination: PaginationParams,
) {
const messages = await this.pupPage.evaluate(
async (chatId, filter, pagination) => {
pagination.limit ||= Infinity;
pagination.offset ||= 0;
const msgFilter = (m) => {
if (m.isNotification) {
return false;
}
if (
filter['filter.fromMe'] != null &&
m.id.fromMe !== filter['filter.fromMe']
) {
return false;
}
if (
filter['filter.timestamp.gte'] != null &&
m.t < filter['filter.timestamp.gte']
) {
return false;
}
if (
filter['filter.timestamp.lte'] != null &&
m.t > filter['filter.timestamp.lte']
) {
return false;
}
return true;
};
// @ts-ignore
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
let msgs = chat.msgs.getModelsArray().filter(msgFilter);
while (msgs.length < pagination.limit + pagination.offset) {
const loadedMessages =
// @ts-ignore
await window.Store.ConversationMsgs.loadEarlierMsgs(chat);
if (!loadedMessages || !loadedMessages.length) break;
msgs = [...loadedMessages.filter(msgFilter), ...msgs];
}
if (msgs.length > pagination.limit + pagination.offset) {
// sort by t - new first
msgs = msgs.sort((a, b) => b.t - a.t);
msgs = msgs.slice(
pagination.offset,
pagination.limit + pagination.offset,
);
}
// @ts-ignore
return msgs.map((m) => window.WWebJS.getMessageModel(m));
},
chatId,
filter,
pagination,
);
return messages.map((m) => new Message(this, m));
}
}
+35
View File
@@ -0,0 +1,35 @@
exports.LoadPaginator = () => {
class Paginator {
constructor(pagination) {
this.pagination = pagination;
}
apply(data) {
if (window.lodash.isEmpty(this.pagination)) {
return data;
}
return this.limit(this.sort(data));
}
sort(data) {
if (!this.pagination?.sortBy) {
return data;
}
return window.lodash.orderBy(
data,
[this.pagination.sortBy],
[this.pagination.sortOrder || 'asc'],
);
}
limit(data) {
if (!this.pagination?.limit) {
return data;
}
const offset = this.pagination.offset || 0;
const limit = this.pagination.limit || Infinity;
return data.slice(offset, offset + limit);
}
}
window.Paginator = Paginator;
};
+11
View File
@@ -0,0 +1,11 @@
exports.LoadWAHA = () => {
window.WAHA = {};
window.WAHA.getChats = async (pagination) => {
let chats = window.Store.Chat.getModelsArray().slice();
const paginator = new window.Paginator(pagination);
chats = paginator.apply(chats);
const chatPromises = chats.map((chat) => window.WWebJS.getChatModel(chat));
return await Promise.all(chatPromises);
};
};
File diff suppressed because it is too large. Load diff
+103 -31
View File
@@ -3,6 +3,7 @@ import {
WAHAInternalEvent,
WhatsappSession,
} from '@waha/core/abc/session.abc';
import { LocalAuth } from '@waha/core/engines/webjs/LocalAuth';
import { WebjsClient } from '@waha/core/engines/webjs/WebjsClient';
import {
AvailableInPlusVersion,
@@ -10,7 +11,7 @@ import {
} from '@waha/core/exceptions';
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
import { QR } from '@waha/core/QR';
import { parseBool, splitAt } from '@waha/helpers';
import { splitAt } from '@waha/helpers';
import { PairingCodeResponse } from '@waha/structures/auth.dto';
import { CallData } from '@waha/structures/calls.dto';
import {
@@ -19,13 +20,19 @@ import {
CreateChannelRequest,
ListChannelsQuery,
} from '@waha/structures/channels.dto';
import { ChatArchiveEvent, GetChatsQuery } from '@waha/structures/chats.dto';
import {
ChatArchiveEvent,
ChatSortField,
GetChatMessageQuery,
GetChatMessagesFilter,
GetChatMessagesQuery,
} from '@waha/structures/chats.dto';
import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
GetMessageQuery,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
MessageLocationRequest,
MessageReactionRequest,
@@ -53,10 +60,12 @@ import {
} from '@waha/structures/groups.dto';
import { Label, LabelID } from '@waha/structures/labels.dto';
import { ReplyToMessage } from '@waha/structures/message.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
import { MeInfo } from '@waha/structures/sessions.dto';
import { WAMessageRevokedBody } from '@waha/structures/webhooks.dto';
import { waitUntil } from '@waha/utils/promiseTimeout';
import { PaginatorInMemory } from '@waha/utils/Paginator';
import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import {
Call,
@@ -143,6 +152,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
protected async buildClient() {
const clientOptions = this.getClientOptions();
clientOptions.authStrategy = new LocalAuth({
clientId: this.name,
dataPath: undefined,
logger: this.logger,
rmMaxRetries: undefined,
});
this.addProxyConfig(clientOptions);
return new WebjsClient(clientOptions);
}
@@ -192,15 +207,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
.then(() => {
// Listen for browser disconnected event
this.whatsapp.pupBrowser.on('disconnected', () => {
this.status = WAHASessionStatus.FAILED;
this.logger.error('The browser has been disconnected');
this.restartClient();
this.failed();
});
// Listen for page close event
this.whatsapp.pupPage.on('close', () => {
this.status = WAHASessionStatus.FAILED;
this.logger.error('The WhatsApp Web page has been closed');
this.restartClient();
this.failed();
});
// Listen for page error event
@@ -215,9 +228,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
}
})
.catch((error) => {
this.status = WAHASessionStatus.FAILED;
this.logger.error(error);
this.restartClient();
this.failed();
return;
});
if (this.isDebugEnabled()) {
@@ -229,7 +241,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
async start() {
this.status = WAHASessionStatus.STARTING;
await this.init();
await this.init().catch((err) => {
this.logger.error('Failed to start the client');
this.logger.error(err, err.stack);
this.failed();
});
return this;
}
@@ -241,6 +257,20 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
await this.end();
}
protected failed() {
// We'll restart the client if it's in the process of unpairing
this.status = WAHASessionStatus.FAILED;
this.restartClient();
}
async unpair() {
this.unpairing = true;
this.shouldRestart = false;
await this.whatsapp.unpair();
// Wait for unpairing to complete
await sleep(2_000);
}
private async end() {
this.engineStateCheckDelayedJob.cancel();
try {
@@ -312,13 +342,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
});
this.whatsapp.on(Events.AUTHENTICATION_FAILURE, (args) => {
this.status = WAHASessionStatus.FAILED;
this.failed();
this.qr.save('');
this.logger.info({ args: args }, `Session has failed to authenticate!`);
});
this.whatsapp.on(Events.DISCONNECTED, (args) => {
this.status = WAHASessionStatus.FAILED;
this.failed();
this.qr.save('');
this.logger.info({ args: args }, `Session has been disconnected!`);
});
@@ -334,7 +364,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
log.info(`Session state changed to bad state, waiting for recovery...`);
this.engineStateCheckDelayedJob.schedule(async () => {
if (!this.startDelayedJob.scheduled) {
if (this.startDelayedJob.scheduled) {
log.info('Session is restarting already, skip check.');
return;
}
@@ -384,7 +414,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
// show it as ABCD-ABCD
const parts = splitAt(code, 4);
const codeRepr = parts.join('-');
this.logger.info(`Your code: ${codeRepr}`);
this.logger.debug(`Your code: ${codeRepr}`);
return { code: codeRepr };
}
@@ -463,6 +493,15 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return this.whatsapp.sendMessage(request.chatId, location, options);
}
async forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
const forwardMessage = this.recreateMessage(request.messageId);
const msg = await forwardMessage.forward(request.chatId);
// Return "sent: true" for now
// need to research how to get the data from WebJS
// @ts-ignore
return { sent: msg || false };
}
async sendSeen(request: SendSeenRequest) {
const chat: Chat = await this.whatsapp.getChatById(request.chatId);
await chat.sendSeen();
@@ -478,10 +517,6 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
await chat.clearState();
}
async getMessages(query: GetMessageQuery) {
return this.getChatMessages(query.chatId, query.limit, query.downloadMedia);
}
async setReaction(request: MessageReactionRequest) {
const message = this.recreateMessage(request.messageId);
return message.react(request.reaction);
@@ -510,18 +545,39 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
/**
* Chats methods
*/
getChats(query: GetChatsQuery) {
return this.whatsapp.getChats(query.limit, query.offset);
getChats(pagination: PaginationParams) {
switch (pagination.sortBy) {
case ChatSortField.ID:
pagination.sortBy = 'id._serialized';
break;
case ChatSortField.CONVERSATION_TIMESTAMP:
pagination.sortBy = 't';
break;
}
return this.whatsapp.getChats(pagination);
}
async getChatMessages(chatId: string, limit: number, downloadMedia: boolean) {
downloadMedia = parseBool(downloadMedia);
const chat: Chat = await this.whatsapp.getChatById(
public async getChatMessages(
chatId: string,
query: GetChatMessagesQuery,
filter: GetChatMessagesFilter,
) {
if (chatId == 'all') {
throw new NotImplementedByEngineError(
"Can not get messages from 'all' in WEBJS",
);
}
const limit = query.limit;
const downloadMedia = query.downloadMedia;
// Test there's chat with id
await this.whatsapp.getChatById(this.ensureSuffix(chatId));
const pagination: PaginationParams = query;
const messages = await this.whatsapp.getMessages(
this.ensureSuffix(chatId),
filter,
pagination,
);
const messages = await chat.fetchMessages({
limit: limit,
});
const promises = [];
for (const msg of messages) {
promises.push(this.processIncomingMessage(msg, downloadMedia));
@@ -531,6 +587,16 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return result;
}
public async getChatMessage(
chatId: string,
messageId: string,
query: GetChatMessageQuery,
): Promise<null | WAMessage> {
const message = await this.whatsapp.getMessageById(messageId);
if (!message) return null;
return await this.processIncomingMessage(message, query.downloadMedia);
}
async deleteChat(chatId) {
const chat = await this.whatsapp.getChatById(chatId);
return chat.delete();
@@ -551,6 +617,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return this.whatsapp.unarchiveChat(id);
}
public chatsUnreadChat(chatId: string): Promise<any> {
const id = this.ensureSuffix(chatId);
return this.whatsapp.markChatUnread(id);
}
/**
*
* Label methods
@@ -596,10 +667,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
.then(this.toWAContact);
}
getContacts() {
return this.whatsapp
.getContacts()
.then((contacts) => contacts.map(this.toWAContact));
async getContacts(pagination: PaginationParams) {
const contactsWEBJS = await this.whatsapp.getContacts();
const contacts = contactsWEBJS.map(this.toWAContact);
const paginator = new PaginatorInMemory(pagination);
return paginator.apply(contacts);
}
public async getContactAbout(query: ContactQuery) {
+3 -3
View File
@@ -6,16 +6,16 @@ export class NotImplementedByEngineError extends UnprocessableEntityException {
constructor(msg = '') {
let error = 'The method is not implemented by the engine.';
if (msg) {
error += ` ${msg}`;
error = `${msg} ${error}`;
}
super(error);
}
}
export class AvailableInPlusVersion extends UnprocessableEntityException {
constructor() {
constructor(feature: string = 'The feature') {
super(
`The feature is available only in Plus version. Check this out: ${DOCS_URL}`,
`${feature} is available only in Plus version. Check this out: ${DOCS_URL}`,
);
}
}
+16 -11
View File
@@ -64,16 +64,15 @@ export class SessionManagerCore extends SessionManager {
protected readonly EngineClass: typeof WhatsappSession;
constructor(
private config: WhatsappConfigService,
config: WhatsappConfigService,
private engineConfigService: EngineConfigService,
private log: PinoLogger,
log: PinoLogger,
private mediaStorageFactory: MediaStorageFactory,
) {
super();
super(config, log);
this.events = new EventEmitter();
this.session = DefaultSessionStatus.STOPPED;
this.sessionConfig = null;
this.log.setContext(SessionManagerCore.name);
const engineName = this.engineConfigService.getDefaultEngineName();
this.EngineClass = this.getEngine(engineName);
this.store = new LocalStoreCore(engineName.toLowerCase());
@@ -84,13 +83,6 @@ export class SessionManagerCore extends SessionManager {
});
}
protected startPredefinedSessions() {
const startSessions = this.config.startSessions;
startSessions.forEach((sessionName) => {
this.start(sessionName);
});
}
protected getEngine(engine: WAHAEngine): typeof WhatsappSession {
if (engine === WAHAEngine.WEBJS) {
return WhatsappSessionWebJSCore;
@@ -219,6 +211,19 @@ export class SessionManagerCore extends SessionManager {
await sleep(this.SESSION_STOP_TIMEOUT);
}
async unpair(name: string) {
if (!this.session) {
return;
}
const session = this.session as WhatsappSession;
this.log.info('Unpairing the device from account...', { session: name });
await session.unpair().catch((err) => {
this.log.warn(`Error while unpairing from device: ${err}`);
});
await sleep(1000);
}
async logout(name: string): Promise<void> {
this.onlyDefault(name);
await this.sessionAuthRepository.clean(name);
@@ -0,0 +1,18 @@
export class SessionWorkerInfo {
id: string; // aka session name here
worker: string;
}
export abstract class ISessionWorkerRepository {
abstract assign(session: string, worker: string): Promise<void>;
abstract unassign(session: string, worker: string): Promise<void>;
abstract remove(session: string): Promise<void>;
abstract getAll(): Promise<SessionWorkerInfo[]>;
abstract getSessionsByWorker(worker: string): Promise<string[]>;
abstract init(): Promise<void>;
}
+2 -2
View File
@@ -1,8 +1,8 @@
import { NOWEBSqlite3KVRepository } from '@waha/core/engines/noweb/store/sqlite3/NOWEBSqlite3KVRepository';
import { Sqlite3SchemaValidation } from '@waha/core/engines/noweb/store/sqlite3/Sqlite3SchemaValidation';
import { ISessionMeRepository } from '@waha/core/storage/ISessionMeRepository';
import { LocalStore } from '@waha/core/storage/LocalStore';
import { Field, Index, Schema } from '@waha/core/storage/sqlite3/Schema';
import { Sqlite3KVRepository } from '@waha/core/storage/sqlite3/Sqlite3KVRepository';
import { MeInfo } from '@waha/structures/sessions.dto';
import * as path from 'path';
@@ -21,7 +21,7 @@ class SessionMeInfo {
}
export class LocalSessionMeRepository
extends NOWEBSqlite3KVRepository<SessionMeInfo>
extends Sqlite3KVRepository<SessionMeInfo>
implements ISessionMeRepository
{
constructor(store: LocalStore) {
@@ -0,0 +1,74 @@
import { Sqlite3SchemaValidation } from '@waha/core/engines/noweb/store/sqlite3/Sqlite3SchemaValidation';
import {
ISessionWorkerRepository,
SessionWorkerInfo,
} from '@waha/core/storage/ISessionWorkerRepository';
import { LocalStore } from '@waha/core/storage/LocalStore';
import { Field, Index, Schema } from '@waha/core/storage/sqlite3/Schema';
import { Sqlite3KVRepository } from '@waha/core/storage/sqlite3/Sqlite3KVRepository';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Database = require('better-sqlite3');
const SCHEMA = new Schema(
'session_worker',
[
new Field('id', 'TEXT'),
new Field('worker', 'TEXT'),
new Field('data', 'TEXT'),
],
[
new Index('session_worker_id_idx', ['id']),
new Index('session_worker_worker_idx', ['worker']),
],
);
export class LocalSessionWorkerRepository
extends Sqlite3KVRepository<SessionWorkerInfo>
implements ISessionWorkerRepository
{
constructor(store: LocalStore) {
const db = store.getWAHADatabase();
super(db, SCHEMA);
}
assign(session: string, worker: string): Promise<void> {
return this.upsertOne({ id: session, worker: worker });
}
unassign(session: string, worker: string): Promise<void> {
return this.deleteBy({ id: session, worker: worker });
}
remove(session: string) {
return this.deleteById(session);
}
async getSessionsByWorker(worker: string): Promise<string[]> {
const data = await this.getAllBy({ worker: worker });
return data.map((d) => d.id);
}
async init(): Promise<void> {
this.migrations();
this.validateSchema();
}
private migrations() {
this.db.exec(
'CREATE TABLE IF NOT EXISTS session_worker (id TEXT, worker TEXT, data TEXT)',
);
// Session can have only one record
this.db.exec(
'CREATE UNIQUE INDEX IF NOT EXISTS session_worker_id_idx ON session_worker (id)',
);
// Worker can have multiple records
this.db.exec(
'CREATE INDEX IF NOT EXISTS session_worker_worker_idx ON session_worker (worker)',
);
}
private validateSchema() {
new Sqlite3SchemaValidation(SCHEMA, this.db).validate();
}
}
@@ -1,5 +1,7 @@
import { PaginationParams } from '@waha/structures/pagination.dto';
import { KnexPaginator } from '@waha/utils/Paginator';
import { Database } from 'better-sqlite3';
import Knex from 'knex';
import Knex, { QueryBuilder } from 'knex';
import { Field, Schema } from './Schema';
@@ -15,6 +17,8 @@ export class Sqlite3KVRepository<Entity> {
private readonly columns: Field[];
private knex: Knex.Knex;
protected Paginator = KnexPaginator;
constructor(
db: Database,
schema: Schema,
@@ -29,8 +33,10 @@ export class Sqlite3KVRepository<Entity> {
this.knex = Knex({ client: 'better-sqlite3', useNullAsDefault: true });
}
getAll() {
return this.all(this.select());
getAll(pagination?: PaginationParams) {
let query = this.select();
query = this.pagination(query, pagination);
return this.all(query);
}
getAllByIds(ids: string[]) {
@@ -163,4 +169,9 @@ export class Sqlite3KVRepository<Entity> {
protected parse(row: any) {
return JSON.parse(row.data);
}
protected pagination(query: any, pagination?: PaginationParams) {
const paginator = new this.Paginator(pagination);
return paginator.apply(query);
}
}
+16 -4
View File
@@ -42,20 +42,32 @@ export class WebhookSenderCore extends WebhookSender {
}
send(json: any, headers?: Record<string, string>) {
this.logger.info({ url: this.url }, `Sending POST ...`);
this.logger.debug({ data: json }, `POST DATA`);
headers = headers || {};
this.logger.info(
{ id: headers['X-Webhook-Request-Id'], url: this.url },
`Sending POST...`,
);
this.logger.debug(
{ id: headers['X-Webhook-Request-Id'], data: json },
`POST DATA`,
);
this.axios
.post(this.url, json, { headers: headers })
.then((response) => {
this.logger.info(
{ id: headers['X-Webhook-Request-Id'] },
`POST request was sent with status code: ${response.status}`,
);
this.logger.debug({ body: response.data }, `Response`);
this.logger.debug(
{ id: headers['X-Webhook-Request-Id'], body: response.data },
`Response`,
);
})
.catch((error) => {
this.logger.error(
{
id: headers['X-Webhook-Request-Id'],
error: error.message,
data: error.response?.data,
},
@@ -160,7 +172,7 @@ export class WebhookConductorCore implements WebhookConductor {
if (!found) {
return false;
}
this.logger.info(`Event '${event}' is enabled for url: ${url}`);
this.logger.debug(`Event '${event}' is enabled for url: ${url}`);
return true;
}
+68 -12
View File
@@ -1,35 +1,91 @@
import { ApiProperty } from '@nestjs/swagger';
import { BooleanString } from '@waha/nestjs/validation/BooleanString';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { ChatIdProperty } from '@waha/structures/properties.dto';
import { Type } from 'class-transformer';
import { IsNumber, IsOptional } from 'class-validator';
import { SessionQuery } from './base.dto';
import { Transform, Type } from 'class-transformer';
import { IsBoolean, IsEnum, IsNumber, IsOptional } from 'class-validator';
/**
* Queries
*/
export class GetChatMessagesQuery extends SessionQuery {
export class GetChatMessagesFilter {
@ApiProperty({
required: false,
description: 'Filter messages before this timestamp (inclusive)',
})
@IsNumber()
@IsOptional()
@Type(() => Number)
'filter.timestamp.lte'?: number;
@ApiProperty({
required: false,
description: 'Filter messages after this timestamp (inclusive)',
})
@IsNumber()
@IsOptional()
@Type(() => Number)
'filter.timestamp.gte'?: number;
@ApiProperty({
required: false,
description: 'From me filter (by default shows all messages)',
})
@Transform(BooleanString)
@IsBoolean()
@IsOptional()
'filter.fromMe'?: boolean;
}
export class GetChatMessagesQuery {
@IsNumber()
@IsOptional()
@Type(() => Number)
limit: number = 100;
@IsNumber()
@IsOptional()
@Type(() => Number)
offset?: number;
@ApiProperty({
example: true,
required: false,
description: 'Download media for messages',
})
@Transform(BooleanString)
@IsBoolean()
@IsOptional()
downloadMedia: boolean = true;
}
export class GetChatsQuery {
@IsNumber()
export class GetChatMessageQuery {
@ApiProperty({
example: true,
required: false,
description: 'Download media for messages',
})
@Transform(BooleanString)
@IsBoolean()
@IsOptional()
@Type(() => Number)
limit?: number;
downloadMedia: boolean = true;
}
@IsNumber()
export enum ChatSortField {
CONVERSATION_TIMESTAMP = 'conversationTimestamp',
ID = 'id',
NAME = 'name',
}
export class ChatsPaginationParams extends PaginationParams {
@ApiProperty({
description: 'Sort by field',
enum: ChatSortField,
})
@IsOptional()
@Type(() => Number)
offset?: number;
@IsEnum(ChatSortField)
sortBy?: string;
}
/**
+137
View File
@@ -0,0 +1,137 @@
import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger';
import { WHATSAPP_DEFAULT_SESSION_NAME } from '@waha/structures/base.dto';
import { ChatRequest } from '@waha/structures/chatting.dto';
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
import { ChatIdProperty } from '@waha/structures/properties.dto';
import { Type } from 'class-transformer';
import {
ArrayMaxSize,
ArrayMinSize,
IsArray,
IsEnum,
IsNotEmpty,
IsOptional,
IsString,
ValidateIf,
ValidateNested,
} from 'class-validator';
export enum ButtonType {
REPLY = 'reply',
URL = 'url',
CALL = 'call',
COPY = 'copy',
}
/**
* buttons:
* - type: reply|url|call|copy
* text: Display Text
* url: only for url (required)
* phone_number: only for call (required)
*/
export class Button {
@IsEnum(ButtonType)
type: ButtonType = ButtonType.REPLY;
@ApiProperty({
example: 'Button Text',
})
@IsString()
text: string;
@ApiProperty({
example: '321321',
})
@IsOptional()
@IsString()
id?: string;
@ApiProperty({
example: 'https://example.com',
})
@ValidateIf((o) => o.type === ButtonType.URL)
@IsNotEmpty()
url?: string;
@ApiProperty({
example: '+1234567890',
})
@ValidateIf((o) => o.type === ButtonType.CALL)
@IsNotEmpty()
phoneNumber?: string;
@ApiProperty({
example: '4321',
})
@ValidateIf((o) => o.type === ButtonType.COPY)
@IsNotEmpty()
copyCode?: string;
}
@ApiExtraModels(RemoteFile, BinaryFile)
export class SendButtonsRequest {
@IsString()
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
@ChatIdProperty()
@IsString()
chatId: string;
@ApiProperty({
example: 'How are you?',
})
@IsOptional()
header: string;
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(RemoteFile) },
{ $ref: getSchemaPath(BinaryFile) },
],
})
@IsOptional()
headerImage?: RemoteFile | BinaryFile;
@ApiProperty({
example: 'Tell us how are you please 🙏',
})
@IsOptional()
body: string;
@ApiProperty({
example: 'If you have any questions, please send it in the chat',
})
@IsOptional()
footer: string;
@ValidateNested({ each: true })
@Type(() => Button)
@IsArray()
@ArrayMinSize(1)
@ArrayMaxSize(4)
@ApiProperty({
example: [
{
type: 'reply',
text: 'I am good!',
},
{
type: 'call',
text: 'Call us',
phoneNumber: '+1234567890',
},
{
type: 'copy',
text: 'Copy code',
copyCode: '4321',
},
{
type: 'url',
text: 'How did you do that?',
url: 'https://waha.devlike.pro',
},
],
})
buttons: Button[];
}
+25 -11
View File
@@ -4,9 +4,14 @@ import {
ApiProperty,
getSchemaPath,
} from '@nestjs/swagger';
import { IsNumber, IsString } from 'class-validator';
import { GetChatMessagesQuery } from '@waha/structures/chats.dto';
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
import { SessionBaseRequest, SessionQuery } from './base.dto';
import {
SessionBaseRequest,
SessionQuery,
WHATSAPP_DEFAULT_SESSION_NAME,
} from './base.dto';
import {
BinaryFile,
RemoteFile,
@@ -42,16 +47,12 @@ export class ChatQuery extends SessionQuery {
chatId: string;
}
export class GetMessageQuery extends ChatQuery {
@IsNumber()
limit: number;
export class GetMessageQuery extends GetChatMessagesQuery {
@IsNotEmpty()
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
@ApiProperty({
example: true,
required: false,
description: 'Download media for messages',
})
downloadMedia: true;
@ChatIdProperty()
chatId: string;
}
export class GetPresenceQuery extends ChatQuery {}
@@ -243,6 +244,12 @@ export class MessageVideoRequest extends ChatRequest {
example: null,
})
reply_to?: string;
@ApiProperty({
description: 'Send as video note (aka instant or round video).',
example: false,
})
asNote?: boolean;
}
export class MessageLinkPreviewRequest extends ChatRequest {
@@ -250,6 +257,13 @@ export class MessageLinkPreviewRequest extends ChatRequest {
title: string;
}
export class MessageForwardRequest extends ChatRequest {
@ApiProperty({
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
})
messageId: string;
}
export class MessageReactionRequest extends MessageRequest {
@ApiProperty({
description:
+18 -1
View File
@@ -1,5 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { ChatIdProperty } from '@waha/structures/properties.dto';
import { IsString } from 'class-validator';
import { IsEnum, IsOptional, IsString } from 'class-validator';
import { SessionBaseRequest, SessionQuery } from './base.dto';
@@ -13,6 +15,21 @@ export class ContactQuery extends SessionQuery {
contactId: string;
}
enum ContactSortField {
ID = 'id',
NAME = 'name',
}
export class ContactsPaginationParams extends PaginationParams {
@ApiProperty({
description: 'Sort by field',
enum: ContactSortField,
})
@IsOptional()
@IsEnum(ContactSortField)
sortBy?: string;
}
/**
* Requests
*/
+2 -2
View File
@@ -21,7 +21,7 @@ export class RemoteFile {
@ApiProperty({
example:
process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL ||
'https://github.com/devlikeapro/waha/raw/core/examples/dev.likeapro.jpg',
'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
})
url: string;
}
@@ -42,7 +42,7 @@ export class BinaryFile {
@ApiProperty({
description: 'Base64-encoded data of the file',
example:
'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 Line truncated
})
data: string;
}
+33
View File
@@ -0,0 +1,33 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsNumber, IsOptional, ValidateIf } from 'class-validator';
export enum SortOrder {
DESC = 'desc',
ASC = 'asc',
}
export class PaginationParams {
@IsNumber()
@IsOptional()
@Type(() => Number)
limit?: number;
@IsNumber()
@IsOptional()
@Type(() => Number)
offset?: number;
@ApiProperty({
description: 'Sort by field',
})
@IsOptional()
sortBy?: string;
@ApiProperty({
description:
'Sort order - <b>desc</b>ending (Z => A, New first) or <b>asc</b>ending (A => Z, Old first)',
})
@IsOptional()
sortOrder?: SortOrder;
}
+10
View File
@@ -36,6 +36,14 @@ export class StopResponse {
stopping: boolean = true;
}
export class WorkerInfo {
@ApiProperty({
example: 'waha',
description: 'The worker ID.',
})
id: string;
}
export class ServerStatusResponse {
@ApiProperty({
example: 1723788847247,
@@ -48,4 +56,6 @@ export class ServerStatusResponse {
description: 'The uptime of the server in milliseconds.',
})
uptime: number;
worker: WorkerInfo;
}
+1
View File
@@ -159,6 +159,7 @@ export class MeInfo {
export class SessionInfo extends SessionDTO {
me?: MeInfo;
assignedWorker?: string;
}
export class SessionDetailedInfo extends SessionInfo {
+13
View File
@@ -11,6 +11,12 @@ import {
ValidateNested,
} from 'class-validator';
export enum RetryPolicy {
LINEAR = 'linear',
EXPONENTIAL = 'exponential',
CONSTANT = 'constant',
}
export class RetriesConfiguration {
@ApiProperty({
example: 2,
@@ -25,6 +31,13 @@ export class RetriesConfiguration {
@IsNumber()
@IsOptional()
attempts?: number;
@ApiProperty({
example: RetryPolicy.LINEAR,
})
@IsOptional()
@IsEnum(RetryPolicy)
policy?: RetryPolicy;
}
export class CustomHeader {
+93
View File
@@ -0,0 +1,93 @@
import { PaginationParams } from '@waha/structures/pagination.dto';
import * as lodash from 'lodash';
export abstract class Paginator {
constructor(protected readonly pagination: PaginationParams) {}
apply<T>(data: T): T {
if (lodash.isEmpty(this.pagination)) {
return data;
}
return this.limit(this.sort(data));
}
protected abstract sort(data: any);
protected abstract limit(data: any);
}
export class PaginatorInMemory extends Paginator {
protected sort(data: any[]) {
if (!this.pagination?.sortBy) {
return data;
}
return lodash.orderBy(
data,
this.pagination.sortBy,
this.pagination.sortOrder || 'asc',
);
}
protected limit(data: any[]) {
if (!this.pagination?.limit) {
return data;
}
const offset = this.pagination.offset || 0;
const limit = this.pagination.limit || Infinity;
return data.slice(offset, offset + limit);
}
}
export class KnexPaginator extends Paginator {
indexes: string[] = [];
dataField: string = 'data';
protected sort(query: any) {
if (!this.pagination?.sortBy) {
return query;
}
const sortBy = this.pagination.sortBy;
const direction = this.pagination.sortOrder || 'asc';
if (this.indexes.includes(sortBy)) {
return query.orderBy(sortBy, direction);
}
// Use data->"$.{field}"
// Make sure to sanitize sortBy to prevent SQL injection
// sqlite3 doesn't support binding for column names and direction
return query.orderByRaw(`${this.dataField}->'$.${sortBy}' ${direction}`);
}
protected limit(query: any) {
const limit = this.pagination.limit;
const offset = this.pagination.offset;
if (limit != null) {
query = query.limit(limit);
}
if (offset != null) {
query = query.offset(offset);
}
return query;
}
}
export class MongoPaginator extends Paginator {
protected sort(query: any) {
if (!this.pagination?.sortBy) {
return query;
}
const mongoDirection = this.pagination.sortOrder === 'asc' ? 1 : -1;
return query.sort({ [this.pagination.sortBy]: mongoDirection });
}
protected limit(query: any) {
const limit = this.pagination.limit;
const offset = this.pagination.offset;
if (limit != null) {
query = query.limit(limit);
}
if (offset != null) {
query = query.skip(offset);
}
return query;
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ export function getEngineName(): string {
}
export const VERSION: WAHAEnvironment = {
version: '2024.10.4',
version: '2024.10.12',
engine: getEngineName(),
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
+245 -240
View File
@@ -7,7 +7,7 @@ __metadata:
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels-678":
version: 6.7.8
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=d0323b92eb8365e6f32a61e41dd00010602d07f1"
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=1a3d827b5d9633e0b82d95362fc07cf1060aa32f"
dependencies:
"@adiwajshing/keyed-db": ^0.2.4
"@hapi/boom": ^9.1.3
@@ -39,7 +39,7 @@ __metadata:
optional: true
sharp:
optional: true
checksum: a3b461006443713074401a17c3481aef571301e83f1fba193d06e7f9ea4c47359b5c973d0b3804e34d4e48c35bdc1a64d8c3e59a0a744e5bc9bcca591d180901
checksum: fd4053af48069c3cdd6017075490d2061302e8006acbcf4d9a831159a1ae213548d135acbc4f2b8217c48bf989dbdc5c96a904df71becf2b0576a7a61998cc83
languageName: node
linkType: hard
@@ -2266,20 +2266,21 @@ __metadata:
languageName: node
linkType: hard
"@puppeteer/browsers@npm:1.9.1":
version: 1.9.1
resolution: "@puppeteer/browsers@npm:1.9.1"
"@puppeteer/browsers@npm:2.4.0":
version: 2.4.0
resolution: "@puppeteer/browsers@npm:2.4.0"
dependencies:
debug: 4.3.4
extract-zip: 2.0.1
progress: 2.0.3
proxy-agent: 6.3.1
tar-fs: 3.0.4
unbzip2-stream: 1.4.3
yargs: 17.7.2
debug: ^4.3.6
extract-zip: ^2.0.1
progress: ^2.0.3
proxy-agent: ^6.4.0
semver: ^7.6.3
tar-fs: ^3.0.6
unbzip2-stream: ^1.4.3
yargs: ^17.7.2
bin:
browsers: lib/cjs/main-cli.js
checksum: 1ea82e34af882dc6d7e8392a88ec4196e206a7f65743be39c196c7068d66b9bdfa370e28c6ab09946bd2baa2182adbcbf445e79cc9bcc5242f05878ae7045b27
checksum: c5f9890f1bf355783574c00b42e8a9bf9e4788d0dce76cc4cd50fcf80b5e5f99ae2b4325edd5350d4fb289f7dfa89f67edf8bc2da9abd53eba1f815caae97beb
languageName: node
linkType: hard
@@ -3752,15 +3753,6 @@ __metadata:
languageName: node
linkType: hard
"agent-base@npm:6":
version: 6.0.2
resolution: "agent-base@npm:6.0.2"
dependencies:
debug: 4
checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d
languageName: node
linkType: hard
"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1":
version: 7.1.1
resolution: "agent-base@npm:7.1.1"
@@ -4315,6 +4307,13 @@ __metadata:
languageName: node
linkType: hard
"bare-events@npm:^2.0.0":
version: 2.5.0
resolution: "bare-events@npm:2.5.0"
checksum: 5aa10716e7f33c5dfc471fd657eee2a33f2db0f78b3c83b5cdd1a45a7e7871114a69460ea96cd838807c55eb470b9e53dd0dfda8c83cced1352cc8253cebff48
languageName: node
linkType: hard
"bare-events@npm:^2.2.0":
version: 2.4.2
resolution: "bare-events@npm:2.4.2"
@@ -4322,6 +4321,42 @@ __metadata:
languageName: node
linkType: hard
"bare-fs@npm:^2.1.1":
version: 2.3.5
resolution: "bare-fs@npm:2.3.5"
dependencies:
bare-events: ^2.0.0
bare-path: ^2.0.0
bare-stream: ^2.0.0
checksum: 071b1dff94a213eaf0b41693953959bf10af2deade597a56ff206a5d833579d56bc8530aa4614bb88bf39fd6d52f2404f7c36af4695109ffa756a13837ac3d91
languageName: node
linkType: hard
"bare-os@npm:^2.1.0":
version: 2.4.4
resolution: "bare-os@npm:2.4.4"
checksum: e90088a7dc0307c020350a28df8ec5564cae5a4b7a213d8509d70831d7064308e2ed31de801b68f474cb004ad3a0a66bd28c38374d270484d9025ee71af20396
languageName: node
linkType: hard
"bare-path@npm:^2.0.0, bare-path@npm:^2.1.0":
version: 2.1.3
resolution: "bare-path@npm:2.1.3"
dependencies:
bare-os: ^2.1.0
checksum: 20301aeb05b735852a396515464908e51e896922c3bb353ef2a09ff54e81ced94e6ad857bb0a36d2ce659c42bd43dd5c3d5643edd8faaf910ee9950c4e137b88
languageName: node
linkType: hard
"bare-stream@npm:^2.0.0":
version: 2.3.2
resolution: "bare-stream@npm:2.3.2"
dependencies:
streamx: ^2.20.0
checksum: 051c817f0b74617d99002a91c61e6fa2b56bff3bb329c5ab4a817287bc1f46f3aef5f13bc11201b344a91873782a45f755d3910012381f16e47feee2194fd792
languageName: node
linkType: hard
"base64-js@npm:^1.3.1":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
@@ -4599,6 +4634,16 @@ __metadata:
languageName: node
linkType: hard
"bufferutil@npm:^4.0.8":
version: 4.0.8
resolution: "bufferutil@npm:4.0.8"
dependencies:
node-gyp: latest
node-gyp-build: ^4.3.0
checksum: 7e9a46f1867dca72fda350966eb468eca77f4d623407b0650913fadf73d5750d883147d6e5e21c56f9d3b0bdc35d5474e80a600b9f31ec781315b4d2469ef087
languageName: node
linkType: hard
"busboy@npm:^1.0.0":
version: 1.6.0
resolution: "busboy@npm:1.6.0"
@@ -4838,15 +4883,16 @@ __metadata:
languageName: node
linkType: hard
"chromium-bidi@npm:0.5.8":
version: 0.5.8
resolution: "chromium-bidi@npm:0.5.8"
"chromium-bidi@npm:0.8.0":
version: 0.8.0
resolution: "chromium-bidi@npm:0.8.0"
dependencies:
mitt: 3.0.1
urlpattern-polyfill: 10.0.0
zod: 3.23.8
peerDependencies:
devtools-protocol: "*"
checksum: 4e4bb8d3907b3a17adf15e95220fc16733e0c4b03d0eccb071010a78ca85d122a0435da95ab657ade6d15e8c9b3cd917088b6128a0aec45e4444916625a4076f
checksum: 5c5b12e00564b6f145511ca4c159db24b07bfc6a1eb6add26d88c4331d74ef6cc8cd2f58bc169e0726c689910a1888313722f39ccab1bac14284d1918155d5e9
languageName: node
linkType: hard
@@ -5191,7 +5237,20 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:9.0.0":
"cosmiconfig@npm:^7.0.1":
version: 7.1.0
resolution: "cosmiconfig@npm:7.1.0"
dependencies:
"@types/parse-json": ^4.0.0
import-fresh: ^3.2.1
parse-json: ^5.0.0
path-type: ^4.0.0
yaml: ^1.10.0
checksum: c53bf7befc1591b2651a22414a5e786cd5f2eeaa87f3678a3d49d6069835a9d8d1aef223728e98aa8fec9a95bf831120d245096db12abe019fecb51f5696c96f
languageName: node
linkType: hard
"cosmiconfig@npm:^9.0.0":
version: 9.0.0
resolution: "cosmiconfig@npm:9.0.0"
dependencies:
@@ -5208,19 +5267,6 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:^7.0.1":
version: 7.1.0
resolution: "cosmiconfig@npm:7.1.0"
dependencies:
"@types/parse-json": ^4.0.0
import-fresh: ^3.2.1
parse-json: ^5.0.0
path-type: ^4.0.0
yaml: ^1.10.0
checksum: c53bf7befc1591b2651a22414a5e786cd5f2eeaa87f3678a3d49d6069835a9d8d1aef223728e98aa8fec9a95bf831120d245096db12abe019fecb51f5696c96f
languageName: node
linkType: hard
"crc-32@npm:^1.2.0":
version: 1.2.2
resolution: "crc-32@npm:1.2.2"
@@ -5273,15 +5319,6 @@ __metadata:
languageName: node
linkType: hard
"cross-fetch@npm:4.0.0":
version: 4.0.0
resolution: "cross-fetch@npm:4.0.0"
dependencies:
node-fetch: ^2.6.12
checksum: ecca4f37ffa0e8283e7a8a590926b66713a7ef7892757aa36c2d20ffa27b0ac5c60dcf453119c809abe5923fc0bae3702a4d896bfb406ef1077b0d0018213e24
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
@@ -5409,6 +5446,18 @@ __metadata:
languageName: node
linkType: hard
"debug@npm:^4.3.6, debug@npm:^4.3.7":
version: 4.3.7
resolution: "debug@npm:4.3.7"
dependencies:
ms: ^2.1.3
peerDependenciesMeta:
supports-color:
optional: true
checksum: 822d74e209cd910ef0802d261b150314bbcf36c582ccdbb3e70f0894823c17e49a50d3e66d96b633524263975ca16b6a833f3e3b7e030c157169a5fabac63160
languageName: node
linkType: hard
"decamelize@npm:^1.2.0":
version: 1.2.0
resolution: "decamelize@npm:1.2.0"
@@ -5551,17 +5600,10 @@ __metadata:
languageName: node
linkType: hard
"devtools-protocol@npm:0.0.1045489":
version: 0.0.1045489
resolution: "devtools-protocol@npm:0.0.1045489"
checksum: 670b2ecc8e890358b08da3095b4c927ffc5ef846a39e1526ff99aecaa623b339450dfe308e421e64bf94308441832b8e9497ebda97fb02266fef3f455286742f
languageName: node
linkType: hard
"devtools-protocol@npm:0.0.1232444":
version: 0.0.1232444
resolution: "devtools-protocol@npm:0.0.1232444"
checksum: b421a3c20506d597211d101c3ed4e550db2ca195d59c3371b2e04a8d057f33be60e732bafc80dc53b34546593a7751857321971e235e9ce9f1c5a9523fd8fa20
"devtools-protocol@npm:0.0.1354347":
version: 0.0.1354347
resolution: "devtools-protocol@npm:0.0.1354347"
checksum: f3e29033319579f7e49c8fd05752faf811870cb6f4ec795b69b6f606984f11af52f317b98a229f39fe8e420346203a9f18df9c8053181e368e43440def1a9a5f
languageName: node
linkType: hard
@@ -6389,7 +6431,7 @@ __metadata:
languageName: node
linkType: hard
"extract-zip@npm:2.0.1":
"extract-zip@npm:^2.0.1":
version: 2.0.1
resolution: "extract-zip@npm:2.0.1"
dependencies:
@@ -7147,7 +7189,7 @@ __metadata:
languageName: node
linkType: hard
"http-proxy-agent@npm:^7.0.0":
"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
dependencies:
@@ -7157,16 +7199,6 @@ __metadata:
languageName: node
linkType: hard
"https-proxy-agent@npm:5.0.1":
version: 5.0.1
resolution: "https-proxy-agent@npm:5.0.1"
dependencies:
agent-base: 6
debug: 4
checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765
languageName: node
linkType: hard
"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2":
version: 7.0.4
resolution: "https-proxy-agent@npm:7.0.4"
@@ -7177,6 +7209,16 @@ __metadata:
languageName: node
linkType: hard
"https-proxy-agent@npm:^7.0.3":
version: 7.0.5
resolution: "https-proxy-agent@npm:7.0.5"
dependencies:
agent-base: ^7.0.2
debug: 4
checksum: 2e1a28960f13b041a50702ee74f240add8e75146a5c37fc98f1960f0496710f6918b3a9fe1e5aba41e50f58e6df48d107edd9c405c5f0d73ac260dabf2210857
languageName: node
linkType: hard
"human-signals@npm:^1.1.1":
version: 1.1.1
resolution: "human-signals@npm:1.1.1"
@@ -9027,7 +9069,7 @@ __metadata:
languageName: node
linkType: hard
"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1":
"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
@@ -9164,7 +9206,7 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.9":
"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.9":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
@@ -9178,6 +9220,17 @@ __metadata:
languageName: node
linkType: hard
"node-gyp-build@npm:^4.3.0":
version: 4.8.2
resolution: "node-gyp-build@npm:4.8.2"
bin:
node-gyp-build: bin.js
node-gyp-build-optional: optional.js
node-gyp-build-test: build-test.js
checksum: 1a57bba8c4c193f808bd8ad1484d4ebdd8106dd9f04a3e82554dc716e3a2d87d7e369e9503c145e0e6a7e2c663fec0d8aaf52bd8156342ec7fc388195f37824e
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 10.1.0
resolution: "node-gyp@npm:10.1.0"
@@ -9956,7 +10009,7 @@ __metadata:
languageName: node
linkType: hard
"progress@npm:2.0.3, progress@npm:^2.0.0":
"progress@npm:^2.0.0, progress@npm:^2.0.3":
version: 2.0.3
resolution: "progress@npm:2.0.3"
checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7
@@ -10044,23 +10097,23 @@ __metadata:
languageName: node
linkType: hard
"proxy-agent@npm:6.3.1":
version: 6.3.1
resolution: "proxy-agent@npm:6.3.1"
"proxy-agent@npm:^6.4.0":
version: 6.4.0
resolution: "proxy-agent@npm:6.4.0"
dependencies:
agent-base: ^7.0.2
debug: ^4.3.4
http-proxy-agent: ^7.0.0
https-proxy-agent: ^7.0.2
http-proxy-agent: ^7.0.1
https-proxy-agent: ^7.0.3
lru-cache: ^7.14.1
pac-proxy-agent: ^7.0.1
proxy-from-env: ^1.1.0
socks-proxy-agent: ^8.0.2
checksum: 31030da419da31809340ac2521090c9a5bf4fe47a944843f829b3502883208c8586a468955e64b694140a41d70af6f45cf4793f5efd4a6f3ed94e5ac8023e36d
checksum: 4d3794ad5e07486298902f0a7f250d0f869fa0e92d790767ca3f793a81374ce0ab6c605f8ab8e791c4d754da96656b48d1c24cb7094bfd310a15867e4a0841d7
languageName: node
linkType: hard
"proxy-from-env@npm:1.1.0, proxy-from-env@npm:^1.1.0":
"proxy-from-env@npm:^1.1.0":
version: 1.1.0
resolution: "proxy-from-env@npm:1.1.0"
checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4
@@ -10098,60 +10151,33 @@ __metadata:
languageName: node
linkType: hard
"puppeteer-core@npm:18.2.1":
version: 18.2.1
resolution: "puppeteer-core@npm:18.2.1"
"puppeteer-core@npm:23.6.0":
version: 23.6.0
resolution: "puppeteer-core@npm:23.6.0"
dependencies:
cross-fetch: 3.1.5
debug: 4.3.4
devtools-protocol: 0.0.1045489
extract-zip: 2.0.1
https-proxy-agent: 5.0.1
proxy-from-env: 1.1.0
rimraf: 3.0.2
tar-fs: 2.1.1
unbzip2-stream: 1.4.3
ws: 8.9.0
checksum: 27ed65a7335ee3f4356425ebe4bbb5202f7b39493398643554a12b981922b2243dc331d9a478215706b79696bcf391637dd5e5405d60aff6ba8f94f0c3fea742
"@puppeteer/browsers": 2.4.0
chromium-bidi: 0.8.0
debug: ^4.3.7
devtools-protocol: 0.0.1354347
typed-query-selector: ^2.12.0
ws: ^8.18.0
checksum: cb2d659a94d4cb8f466f6e56ac5aff1374506326c2cd2a964dcd556e872f00db8c4079f1e7149ffb8200b30da9d31d4cd299ad28ac38a4c445ac0b66cf90fafc
languageName: node
linkType: hard
"puppeteer-core@npm:21.11.0":
version: 21.11.0
resolution: "puppeteer-core@npm:21.11.0"
"puppeteer@npm:^23.6.0":
version: 23.6.0
resolution: "puppeteer@npm:23.6.0"
dependencies:
"@puppeteer/browsers": 1.9.1
chromium-bidi: 0.5.8
cross-fetch: 4.0.0
debug: 4.3.4
devtools-protocol: 0.0.1232444
ws: 8.16.0
checksum: 953018ca679542d747e5974fd732349d73f4c685c0b49dd6027d493ad95d64a23b2d6e3e72f0a0a12c7335d3e08a0b3e6bc9ac3adfb3fb0316fb1ca62f46f129
languageName: node
linkType: hard
"puppeteer@npm:^18.2.1":
version: 18.2.1
resolution: "puppeteer@npm:18.2.1"
dependencies:
https-proxy-agent: 5.0.1
progress: 2.0.3
proxy-from-env: 1.1.0
puppeteer-core: 18.2.1
checksum: 58ba8c7abd82071dc12e280622fa352a8ac253375a514c7a3527b468672a4ccdfe20b37aa09498774da2f0666bcdd9a47838c6661de483a3d6e10b47ef666113
languageName: node
linkType: hard
"puppeteer@npm:^21.0.0":
version: 21.11.0
resolution: "puppeteer@npm:21.11.0"
dependencies:
"@puppeteer/browsers": 1.9.1
cosmiconfig: 9.0.0
puppeteer-core: 21.11.0
"@puppeteer/browsers": 2.4.0
chromium-bidi: 0.8.0
cosmiconfig: ^9.0.0
devtools-protocol: 0.0.1354347
puppeteer-core: 23.6.0
typed-query-selector: ^2.12.0
bin:
puppeteer: lib/esm/puppeteer/node/cli.js
checksum: 0f2e6f6d9269e511022ec8e8478f42bf5f3dbddbdac7b50d31c90504993eaef436e8729a5b3f105576ec2031519de723d4a9d0ad797821d0f16540073fabc37a
puppeteer: lib/cjs/puppeteer/node/cli.js
checksum: c0cf82393418154d392dcccca8614aaf894f489fa0a5de664556816d70eff773e8a0bb60331a727b5d954a30c0cab74bff968ff11e37defa1a75ae524655ac2c
languageName: node
linkType: hard
@@ -10555,17 +10581,6 @@ __metadata:
languageName: node
linkType: hard
"rimraf@npm:3.0.2, rimraf@npm:^3.0.2":
version: 3.0.2
resolution: "rimraf@npm:3.0.2"
dependencies:
glob: ^7.1.3
bin:
rimraf: bin.js
checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0
languageName: node
linkType: hard
"rimraf@npm:4.4.1":
version: 4.4.1
resolution: "rimraf@npm:4.4.1"
@@ -10577,6 +10592,17 @@ __metadata:
languageName: node
linkType: hard
"rimraf@npm:^3.0.2":
version: 3.0.2
resolution: "rimraf@npm:3.0.2"
dependencies:
glob: ^7.1.3
bin:
rimraf: bin.js
checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0
languageName: node
linkType: hard
"run-async@npm:^2.4.0":
version: 2.4.1
resolution: "run-async@npm:2.4.1"
@@ -10689,6 +10715,15 @@ __metadata:
languageName: node
linkType: hard
"semver@npm:^7.6.3":
version: 7.6.3
resolution: "semver@npm:7.6.3"
bin:
semver: bin/semver.js
checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8
languageName: node
linkType: hard
"send@npm:0.18.0":
version: 0.18.0
resolution: "send@npm:0.18.0"
@@ -11125,6 +11160,21 @@ __metadata:
languageName: node
linkType: hard
"streamx@npm:^2.20.0":
version: 2.20.1
resolution: "streamx@npm:2.20.1"
dependencies:
bare-events: ^2.2.0
fast-fifo: ^1.3.2
queue-tick: ^1.0.1
text-decoder: ^1.1.0
dependenciesMeta:
bare-events:
optional: true
checksum: 48605ddd3abdd86d2e3ee945ec7c9317f36abb5303347a8fff6e4c7926a72c33ec7ac86b50734ccd1cf65602b6a38e247966e8199b24e5a7485d9cec8f5327bd
languageName: node
linkType: hard
"string-length@npm:^4.0.1":
version: 4.0.2
resolution: "string-length@npm:4.0.2"
@@ -11419,7 +11469,7 @@ __metadata:
languageName: node
linkType: hard
"tar-fs@npm:2.1.1, tar-fs@npm:^2.0.0":
"tar-fs@npm:^2.0.0":
version: 2.1.1
resolution: "tar-fs@npm:2.1.1"
dependencies:
@@ -11431,14 +11481,20 @@ __metadata:
languageName: node
linkType: hard
"tar-fs@npm:3.0.4":
version: 3.0.4
resolution: "tar-fs@npm:3.0.4"
"tar-fs@npm:^3.0.6":
version: 3.0.6
resolution: "tar-fs@npm:3.0.6"
dependencies:
mkdirp-classic: ^0.5.2
bare-fs: ^2.1.1
bare-path: ^2.1.0
pump: ^3.0.0
tar-stream: ^3.1.5
checksum: dcf4054f9e92ca0efe61c2b3f612914fb259a47900aa908a63106513a6d006c899b426ada53eb88d9dbbf089b5724c8e90b96a2c4ca6171845fa14203d734e30
dependenciesMeta:
bare-fs:
optional: true
bare-path:
optional: true
checksum: b4fa09c70f75caf05bf5cf87369cd2862f1ac5fb75c4ddf9d25d55999f7736a94b58ad679d384196cba837c5f5ff14086e060fafccef5474a16e2d3058ffa488
languageName: node
linkType: hard
@@ -11640,13 +11696,6 @@ __metadata:
languageName: node
linkType: hard
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
checksum: 726321c5eaf41b5002e17ffbd1fb7245999a073e8979085dacd47c4b4e8068ff5777142fc6726d6ca1fd2ff16921b48788b87225cbc57c72636f6efa8efbffe3
languageName: node
linkType: hard
"traverse@npm:>=0.3.0 <0.4":
version: 0.3.9
resolution: "traverse@npm:0.3.9"
@@ -11926,6 +11975,13 @@ __metadata:
languageName: node
linkType: hard
"typed-query-selector@npm:^2.12.0":
version: 2.12.0
resolution: "typed-query-selector@npm:2.12.0"
checksum: c4652f2eec16112d69e0da30c2effab3f03d1710f9559da1e1209bbfc9a20990d5de4ba97890c11f9d17d85c8ae3310953a86c198166599d4c36abc63664f169
languageName: node
linkType: hard
"typedarray@npm:^0.0.6":
version: 0.0.6
resolution: "typedarray@npm:0.0.6"
@@ -11974,7 +12030,7 @@ __metadata:
languageName: node
linkType: hard
"unbzip2-stream@npm:1.4.3":
"unbzip2-stream@npm:^1.4.3":
version: 1.4.3
resolution: "unbzip2-stream@npm:1.4.3"
dependencies:
@@ -12189,6 +12245,7 @@ __metadata:
axios: ^1.7.7
axios-retry: ^4.5.0
better-sqlite3: 11.3.0
bufferutil: ^4.0.8
check-disk-space: ^3.4.0
chokidar: ^3.6.0
class-transformer: ^0.5.1
@@ -12218,7 +12275,7 @@ __metadata:
prettier: ^1.19.1
pretty-bytes: 5.6.0
promise-retry: ^2.0.1
puppeteer: ^21.0.0
puppeteer: ^23.6.0
qrcode: ^1.5.1
qrcode-terminal: ^0.12.0
reflect-metadata: ^0.1.13
@@ -12233,6 +12290,9 @@ __metadata:
tsconfig-paths: ^4.1.0
typescript: ^4.8.4
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-channels"
dependenciesMeta:
bufferutil:
optional: true
languageName: unknown
linkType: soft
@@ -12264,13 +12324,6 @@ __metadata:
languageName: node
linkType: hard
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
checksum: c92a0a6ab95314bde9c32e1d0a6dfac83b578f8fa5f21e675bc2706ed6981bc26b7eb7e6a1fab158e5ce4adf9caa4a0aee49a52505d4d13c7be545f15021b17c
languageName: node
linkType: hard
"webidl-conversions@npm:^7.0.0":
version: 7.0.0
resolution: "webidl-conversions@npm:7.0.0"
@@ -12331,7 +12384,7 @@ __metadata:
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-channels":
version: 1.26.0
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=4c4cc9817bac3fc1369f09571f9efd2477ba10a9"
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=76329eedc27b07c11e38bf1b60e6c2476c11dd73"
dependencies:
"@pedroslopez/moduleraid": ^5.0.2
archiver: ^5.3.1
@@ -12349,11 +12402,11 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: ac30714d3e55fe9da1a1cd23db6988e1f060414a2b2c60e9d280938c4e620098da75edf85e54cd24efd976e884f5bc401e96d3cdabe583fae0332258f4d62b4d
checksum: b8d4fcb1fd1365a38f07f95dd2eb1fd743c6c406fc7ac679662ddeded80c441720bc8400222a37d61765ebcbb5c6799f169d5ac45f12f58b5421c55bb9f2ab7e
languageName: node
linkType: hard
"whatwg-url@npm:^13.0.0":
"whatwg-url@npm:13.0.0":
version: 13.0.0
resolution: "whatwg-url@npm:13.0.0"
dependencies:
@@ -12363,16 +12416,6 @@ __metadata:
languageName: node
linkType: hard
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
dependencies:
tr46: ~0.0.3
webidl-conversions: ^3.0.0
checksum: b8daed4ad3356cc4899048a15b2c143a9aed0dfae1f611ebd55073310c7b910f522ad75d727346ad64203d7e6c79ef25eafd465f4d12775ca44b90fa82ed9e2c
languageName: node
linkType: hard
"which-boxed-primitive@npm:^1.0.2":
version: 1.0.2
resolution: "which-boxed-primitive@npm:1.0.2"
@@ -12523,9 +12566,9 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:8.13.0":
version: 8.13.0
resolution: "ws@npm:8.13.0"
"ws@npm:^8.18.0":
version: 8.18.0
resolution: "ws@npm:8.18.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
@@ -12534,52 +12577,7 @@ __metadata:
optional: true
utf-8-validate:
optional: true
checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c
languageName: node
linkType: hard
"ws@npm:8.16.0":
version: 8.16.0
resolution: "ws@npm:8.16.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b
languageName: node
linkType: hard
"ws@npm:8.9.0":
version: 8.9.0
resolution: "ws@npm:8.9.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
checksum: 23aa0f021b2eb65c108ec4c3e08c0d81ba01f82b500432dfe327fd6be36079c1d81fdb0eac6464d2a0eb49904d34a9ab8c59619d673fa07b8346f83aeb0cbf12
languageName: node
linkType: hard
"ws@npm:^8.13.0":
version: 8.17.1
resolution: "ws@npm:8.17.1"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
checksum: 442badcce1f1178ec87a0b5372ae2e9771e07c4929a3180321901f226127f252441e8689d765aa5cfba5f50ac60dd830954afc5aeae81609aefa11d3ddf5cecf
checksum: 91d4d35bc99ff6df483bdf029b9ea4bfd7af1f16fc91231a96777a63d263e1eabf486e13a2353970efc534f9faa43bdbf9ee76525af22f4752cbc5ebda333975
languageName: node
linkType: hard
@@ -12642,21 +12640,6 @@ __metadata:
languageName: node
linkType: hard
"yargs@npm:17.7.2, yargs@npm:^17.3.1":
version: 17.7.2
resolution: "yargs@npm:17.7.2"
dependencies:
cliui: ^8.0.1
escalade: ^3.1.1
get-caller-file: ^2.0.5
require-directory: ^2.1.1
string-width: ^4.2.3
y18n: ^5.0.5
yargs-parser: ^21.1.1
checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a
languageName: node
linkType: hard
"yargs@npm:^15.3.1":
version: 15.4.1
resolution: "yargs@npm:15.4.1"
@@ -12676,6 +12659,21 @@ __metadata:
languageName: node
linkType: hard
"yargs@npm:^17.3.1, yargs@npm:^17.7.2":
version: 17.7.2
resolution: "yargs@npm:17.7.2"
dependencies:
cliui: ^8.0.1
escalade: ^3.1.1
get-caller-file: ^2.0.5
require-directory: ^2.1.1
string-width: ^4.2.3
y18n: ^5.0.5
yargs-parser: ^21.1.1
checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a
languageName: node
linkType: hard
"yauzl@npm:^2.10.0":
version: 2.10.0
resolution: "yauzl@npm:2.10.0"
@@ -12710,3 +12708,10 @@ __metadata:
checksum: 33bd5ee7017656c2ad728b5d4ba510e15bd65ce1ec180c5bbdc7a5f063256353ec482e6a2bc74de7515219d8494147924b9aae16e63fdaaf37cdf7d1ee8df125
languageName: node
linkType: hard
"zod@npm:3.23.8":
version: 3.23.8
resolution: "zod@npm:3.23.8"
checksum: 15949ff82118f59c893dacd9d3c766d02b6fa2e71cf474d5aa888570c469dbf5446ac5ad562bb035bf7ac9650da94f290655c194f4a6de3e766f43febd432c5c
languageName: node
linkType: hard