Compare commits

..
42 Commits
Author SHA1 Message Date
devlikepro 41b669a35c [core] 2025.10.4
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-10-19 17:08:06 +07:00
devlikepro 7b5ca3bc57 [core] Up Dashboard 2025-10-19 17:08:06 +07:00
devlikepro bb0c7fd22b [core] NOWEB, GOWS - better handle message.edited
fix #1394
2025-10-19 17:08:05 +07:00
devlikepro c79958541e [core] GOWS - use proto type 2025-10-19 17:08:05 +07:00
devlikepro 94ee8863d6 [core] Up Dashboard - headers, agent name 2025-10-19 17:08:04 +07:00
devlikepro 011477262d [core] Up NOWEB
fix #1538
2025-10-19 17:08:04 +07:00
devlikeproandbergpinheiro 2e8e4cefb0 [core] Fixes inverted logic of messages read in ChatWoot
closes devlikeapro/waha-plus#20

Adapted from: bergpinheiro/read-chatwoot-invertida@ef2ff1b9de

(cherry picked from commit ef2ff1b9deceeea3c26747531e26d66e96f931fd)
(cherry picked from commit 3dc3440d2d5077279934577007f89cd640f60d9e)

Co-authored-by: bergpinheiro <bergpinheiro@users.noreply.github.com>"
2025-10-19 17:08:04 +07:00
devlikepro 5e2802c098 [core] metadata is Record 2025-10-19 17:08:04 +07:00
devlikepro 9a6250a055 [core] Add jid to MeInfo type 2025-10-19 17:08:03 +07:00
devlikepro f8cb56525a [core] has recent 2025-10-19 17:08:03 +07:00
devlikepro b5b491cb7b [core] en in waha 2025-10-19 17:08:02 +07:00
devlikepro 7eb6be70f9 [core] dev image 2025-10-19 17:08:02 +07:00
devlikepro 0de8a5fabc [core] NOWEB - Fix sending media to channels
fix #1523
2025-10-19 17:08:02 +07:00
Berg Pinheiro 9fbc18fb70 [core] Fix URL breaking in ChatWoot integration
- Fix WhatsappToMarkdown function to preserve URLs with underscores
- URLs are now protected before markdown transformations
- Prevents Sarah_Jessica_Parker from becoming Sarah*Jessica*Parker
- Add comprehensive tests for URL preservation scenarios
- Maintains existing markdown formatting for non-URL text
- All comments in English for code consistency

Fixes: Links with underscores being broken in ChatWoot messages

fix devlikeapro/waha#1522
closes devlikeapro/waha-plus#19
2025-10-19 17:08:02 +07:00
devlikepro e7ce3368b0 [core] Fix tests 2025-10-19 17:08:02 +07:00
devlikepro 729600e9c6 [core] voice 2025-10-17 11:44:55 +07:00
devlikepro 54886f385b [core] Disable pt 2025-10-17 10:41:35 +07:00
devlikepro 81854ad7ec [core] Fix smoke test
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-10-16 17:07:47 +07:00
devlikepro 5261d4f281 [core] 2025.10.3 2025-10-16 16:57:01 +07:00
devlikepro 72d9d94342 [core] NOWEB - use upstream libsignal 2025-10-16 16:57:01 +07:00
devlikepro b782ad27a2 [core] ChatWoot - mark read fix one more time 2025-10-16 16:56:36 +07:00
devlikepro d7dad5e6c2 [core] rm unused imports 2025-10-16 16:56:35 +07:00
devlikepro 8765bb4412 [core] init-waha 2025-10-16 16:56:35 +07:00
devlikepro 984be25d51 [core] Replace 111 as well 2025-10-16 16:56:30 +07:00
devlikepro 70620207dd [core] Adjust .env 2025-10-16 16:56:29 +07:00
devlikepro a96200f981 [core] Adjust message 2025-10-16 16:56:29 +07:00
devlikepro 4978abf3d8 [core] Up GOWS
fix #1503
fix #1511
2025-10-16 16:56:27 +07:00
devlikepro a67d84f21e [core] ChatWoot - List Message 2025-10-16 16:56:27 +07:00
devlikepro 91f59b38fd [core] Dashboard - Chats
fix #1454
2025-10-16 16:56:27 +07:00
devlikepro d0ec351910 [core] Fix /api/sendImage when using proxy
fix #1449
fix #1510
2025-10-16 16:56:26 +07:00
devlikepro a7b12874fb [core] Require auth by default 2025-10-16 16:56:24 +07:00
devlikepro cd2c7bdd86 [core] Up creds in .env 2025-10-16 16:56:24 +07:00
devlikepro 045077143f [core] no warning about plain key 2025-10-16 16:56:24 +07:00
devlikepro bcd2199687 [core] ChatWoot - remove (WAHA) from contact name 2025-10-16 16:56:20 +07:00
devlikepro af108eede4 [core] NOWEB - normalize @lid
fix #1493
2025-10-16 16:56:19 +07:00
devlikepro ee1cf838b8 [core] NOWEB - use IPv4 by default 2025-10-16 16:56:19 +07:00
devlikepro 0fc74895da [core] Up NOWEB - add more logs for media 2025-10-16 16:56:19 +07:00
devlikepro 394937c154 [core] 2025.10.2
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-10-14 15:21:40 +07:00
devlikepro 71f9adeff4 [core] ChatWoot - ignore fromMe in ack
mention #1492
2025-10-14 15:21:40 +07:00
devlikepro e13ccdab9c [core] ChatWoot - disable error/succeeded messages
mention #1492
mention #1395
2025-10-14 15:21:40 +07:00
devlikepro 2074748a7a [core] ChatWoot - no message.ack error
mention #1492
2025-10-14 15:21:39 +07:00
devlikepro 283d63394e [core] Up NOWEB
fix #1495
fix #1499
2025-10-14 15:21:39 +07:00
67 changed files with 1790 additions and 253 deletions

No files matched your search

+16 -13
View File
@@ -3,9 +3,25 @@
# https://waha.devlike.pro/docs/how-to/config/
#
# ====================
# ===== SECURITY =====
# ====================
WAHA_API_KEY=00000000000000000000000000000000
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=11111111111111111111111111111111
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=11111111111111111111111111111111
# Disable Dashboard or Swagger
WAHA_DASHBOARD_ENABLED=True
WHATSAPP_SWAGGER_ENABLED=True
# ==================
# ===== COMMON =====
# ==================
# WhatsApp engine (WEBJS is default, GOWS or NOWEB for better performance)
WHATSAPP_DEFAULT_ENGINE=WEBJS
# Base URL for the API (used for webhooks, file URLs, etc.)
WAHA_BASE_URL=http://localhost:3000
# WAHA_PUBLIC_URL=http://localhost:3000
@@ -15,20 +31,7 @@ WAHA_BASE_URL=http://localhost:3000
# WHATSAPP_API_PORT=3000
# WHATSAPP_API_HOSTNAME=localhost
# ====================
# ===== SECURITY =====
# ====================
# "sha512:{SHA}" format - below is "admin" api key
WAHA_API_KEY=sha512:c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec
WAHA_DASHBOARD_ENABLED=True
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=admin
WHATSAPP_SWAGGER_ENABLED=True
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=admin
# WhatsApp engine (WEBJS is default, GOWS or NOWEB for better performance)
WHATSAPP_DEFAULT_ENGINE=WEBJS
# ===================
# ===== LOGGING =====
+1
View File
@@ -129,6 +129,7 @@ jobs:
docker run -d \
--name smoke \
--rm -p3000:3000 \
-e WAHA_API_KEY=test \
${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
sleep 3
+126
View File
@@ -0,0 +1,126 @@
name: Dev
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
docker-build:
if: github.repository == 'devlikeapro/waha-plus'
runs-on: ${{ matrix.runner }}
name:
${{ matrix.engine }} - ${{ matrix.browser }} - ${{ matrix.platform }} -
${{ matrix.tag }}
strategy:
matrix:
include:
- runner: 'buildjet-4vcpu-ubuntu-2204'
tag: 'dev'
platform: 'amd64'
browser: 'chromium'
engine: 'WEBJS'
goss: 'goss-linux-amd64'
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
tag: 'dev-arm'
platform: 'linux/arm64'
browser: 'chromium'
engine: 'WEBJS'
goss: 'goss-linux-arm'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check recent changes
id: recent
run: |
count=$(git rev-list --after="24 hours ago" --count HEAD || true)
if [ "$count" -gt 0 ]; then
echo "has_recent=true" >> "$GITHUB_OUTPUT"
else
echo "has_recent=false" >> "$GITHUB_OUTPUT"
fi
- name: No recent changes summary
if: ${{ steps.recent.outputs.has_recent != 'true' }}
run:
echo "No commits in the last 24 hours; skipping build." >>
"$GITHUB_STEP_SUMMARY"
- name: Set up Docker Buildx
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/setup-buildx-action@v2
- name: Docker meta
if: ${{ steps.recent.outputs.has_recent == 'true' }}
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ vars.DOCKER_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=${{ matrix.tag }}
- name: Login to image repository
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
USE_BROWSER=${{ matrix.browser }}
WHATSAPP_DEFAULT_ENGINE=${{ matrix.engine }}
push: false
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Install goss
if: ${{ steps.recent.outputs.has_recent == 'true' }}
env:
GOSS_DOWNLOAD: 'https://github.com/goss-org/goss/releases/download'
GOSS_VERSION: 'v0.4.8'
run: |
curl -L https://github.com/goss-org/goss/releases/download/${{ env.GOSS_VERSION }}/${{ matrix.goss }} -o /usr/local/bin/goss
chmod +rx /usr/local/bin/goss
- name: Run smoke tests
if: ${{ steps.recent.outputs.has_recent == 'true' }}
working-directory: tests/smoke
timeout-minutes: 2
run: |
docker run -d \
--name smoke \
--rm -p3000:3000 \
-e WAHA_API_KEY=test \
${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
sleep 3
docker logs smoke
goss validate --retry-timeout 30s --sleep 1s
docker rm --force smoke
- name: Push
if: ${{ steps.recent.outputs.has_recent == 'true' }}
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
USE_BROWSER=${{ matrix.browser }}
WHATSAPP_DEFAULT_ENGINE=${{ matrix.engine }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+18 -18
View File
@@ -15,6 +15,7 @@ on:
jobs:
en:
if: github.repository == 'devlikeapro/waha'
permissions:
issues: write
discussions: write
@@ -31,21 +32,20 @@ jobs:
CUSTOM_BOT_NOTE: 'English'
TARGET_LANGUAGE: 'eng'
TRANSLATED_LANGUAGE: 'en'
pt:
permissions:
issues: write
discussions: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: devlikeapro/github-translate-action@main
env:
# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
IS_MODIFY_TITLE: false
CUSTOM_BOT_NOTE: 'Portuguese'
TARGET_LANGUAGE: 'por'
TRANSLATED_LANGUAGE: 'pt'
# pt:
# permissions:
# issues: write
# discussions: write
# pull-requests: write
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: devlikeapro/github-translate-action@main
# env:
# # GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# IS_MODIFY_TITLE: false
# CUSTOM_BOT_NOTE: 'Portuguese'
# TARGET_LANGUAGE: 'por'
# TRANSLATED_LANGUAGE: 'pt'
+1
View File
@@ -73,4 +73,5 @@ repos:
^src/core/engines/webjs/_lodash.js|
^tests/perf/.*|
^scripts/.*|
^scripts/init-waha.js|
)$
+5
View File
@@ -195,6 +195,11 @@ COPY --from=build /git/node_modules ./node_modules
COPY --from=build /git/dist ./dist
COPY --from=dashboard /dashboard ./dist/dashboard
COPY --from=gows /go/gows/bin/gows /app/gows
COPY .env.example ./.env.example
COPY scripts/init-waha.js ./scripts/init-waha.js
RUN chmod +x ./scripts/init-waha.js \
&& printf '%s\n' '#!/bin/sh' 'exec node /app/scripts/init-waha.js "$@"' > /usr/local/bin/init-waha \
&& chmod +x /usr/local/bin/init-waha
ENV WAHA_GOWS_PATH=/app/gows
ENV WAHA_GOWS_SOCKET=/tmp/gows.sock
+1 -1
View File
@@ -35,7 +35,7 @@ for-swagger:
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
up-noweb:
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-10
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-19
up-noweb-libsignal:
yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
+14 -14
View File
@@ -3,6 +3,20 @@
# https://waha.devlike.pro/docs/how-to/config/
#
# ====================
# ===== SECURITY =====
# ====================
WAHA_API_KEY_PLAIN=00000000000000000000000000000000
WAHA_API_KEY=00000000000000000000000000000000
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=11111111111111111111111111111111
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=11111111111111111111111111111111
# Disable Dashboard or Swagger
WAHA_DASHBOARD_ENABLED=True
WHATSAPP_SWAGGER_ENABLED=True
# ==================
# ===== COMMON =====
# ==================
@@ -15,20 +29,6 @@ WAHA_PUBLIC_URL=http://localhost:3000
# WHATSAPP_API_PORT=3000
# WHATSAPP_API_HOSTNAME=localhost
# ====================
# ===== SECURITY =====
# ====================
# WAHA Apps requires both sha and plain version
WAHA_API_KEY_PLAIN=00000000000000000000000000000000
WAHA_API_KEY=sha512:98b6d128682e280b74b324ca82a6bae6e8a3f7174e0605bfd52eb9948fad8984854ec08f7652f32055c4a9f12b69add4850481d9503a7f2225501671d6124648
WAHA_DASHBOARD_USERNAME=admin
WAHA_DASHBOARD_PASSWORD=11111111111111111111111111111111
WHATSAPP_SWAGGER_USERNAME=admin
WHATSAPP_SWAGGER_PASSWORD=11111111111111111111111111111111
WAHA_DASHBOARD_ENABLED=True
WHATSAPP_SWAGGER_ENABLED=True
# WhatsApp engine (WEBJS is default, GOWS or NOWEB for better performance)
WHATSAPP_DEFAULT_ENGINE=GOWS
-5
View File
@@ -41,11 +41,6 @@ if [ -n "$key" ]; then
# If already hashed, use it as is
export WAHA_API_KEY="$key"
else
# Display warning about using plain text API key
echo "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️"
echo "WARNING: Plain text API key detected. Converting to hashed format for security."
echo "For better security, use WAHA_API_KEY=sha512:{SHA512_HASH_FOR_YOUR_API_KEY}"
echo "⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️"
# Hash the key using sha512sum
HASHED_KEY=$(echo -n "$key" | sha512sum | awk '{print $1}')
export WAHA_API_KEY="sha512:$HASHED_KEY"
Binary file not shown.
Binary file not shown.
+3 -3
View File
@@ -26,7 +26,7 @@
"gows:proto": "yarn gows:proto:fetch && yarn gows:proto:build"
},
"dependencies": {
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-10",
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-19",
"@adiwajshing/keyed-db": "^0.2.4",
"@aws-sdk/client-s3": "^3.633.0",
"@aws-sdk/s3-request-presigner": "^3.633.0",
@@ -76,7 +76,7 @@
"joi": "^17.13.3",
"knex": "^3.1.0",
"libphonenumber-js": "^1.10.36",
"libsignal": "github:devlikeapro/libsignal-node#fork-master",
"libsignal": "github:WhiskeySockets/libsignal-node#master",
"link-preview-js": "^3.0.4",
"lodash": "^4.17.21",
"long": "^5.3.2",
@@ -102,6 +102,7 @@
"sqlite3": "^5.1.7",
"swagger-ui-express": "^4.1.4",
"ulid": "^2.3.0",
"undici": "^7.16.0",
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10",
"write-file-atomic": "^6.0.0",
"yaml": "^2.7.1"
@@ -114,7 +115,6 @@
"ws": "^8.18.0",
"puppeteer": "^24.19.0",
"whatwg-url": "13.0.0",
"libsignal": "github:devlikeapro/libsignal-node#fork-master",
"axios": "^1.9.0"
},
"devDependencies": {
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const projectRoot = path.resolve(__dirname, '..');
const args = process.argv.slice(2);
const forceIndex = args.indexOf('--force');
const forceOverwrite = forceIndex !== -1;
if (forceOverwrite) {
args.splice(forceIndex, 1);
}
const folderArg = args[0] || '.';
const sourceArgRaw = args[1];
const targetArg = args[2] || '.env';
const envDir = path.resolve(process.cwd(), folderArg);
const sourceArg = sourceArgRaw || '.env.example';
const templateBaseDir = sourceArgRaw ? envDir : projectRoot;
const templatePath = path.isAbsolute(sourceArg)
? sourceArg
: path.resolve(templateBaseDir, sourceArg);
const targetPath = path.isAbsolute(targetArg)
? targetArg
: path.resolve(envDir, targetArg);
function exitWithMessage(message, code = 1) {
console.error(message);
process.exit(code);
}
if (!fs.existsSync(envDir)) {
exitWithMessage(`Target directory does not exist: ${envDir}`);
}
if (!fs.existsSync(templatePath)) {
exitWithMessage(`Missing template file: ${templatePath}`);
}
if (!forceOverwrite && fs.existsSync(targetPath)) {
const stats = fs.statSync(targetPath);
if (stats.size > 0) {
exitWithMessage(
`${targetPath} already exists and is not empty. Remove it manually or rerun with --force to overwrite.`,
);
}
}
const exampleContent = fs.readFileSync(templatePath, 'utf8');
function generateSecret() {
return crypto.randomUUID({ disableEntropyCache: true }).replace(/-/g, '');
}
const apiKey = generateSecret();
const adminPassword = generateSecret();
const updatedContent = exampleContent
.replace(/^WAHA_API_KEY=.*/m, `WAHA_API_KEY=${apiKey}`)
.replace(/^WAHA_API_KEY_PLAIN=.*/m, `WAHA_API_KEY_PLAIN=${apiKey}`)
.replace(/^WAHA_DASHBOARD_USERNAME=.*/m, 'WAHA_DASHBOARD_USERNAME=admin')
.replace(
/^WAHA_DASHBOARD_PASSWORD=.*/m,
`WAHA_DASHBOARD_PASSWORD=${adminPassword}`,
)
.replace(/^WHATSAPP_SWAGGER_USERNAME=.*/m, 'WHATSAPP_SWAGGER_USERNAME=admin')
.replace(
/^WHATSAPP_SWAGGER_PASSWORD=.*/m,
`WHATSAPP_SWAGGER_PASSWORD=${adminPassword}`,
);
fs.writeFileSync(targetPath, updatedContent, { encoding: 'utf8' });
const lines = [
'Credentials generated.',
'',
'Dashboard and Swagger:',
' - Username: admin',
` - Password: ${adminPassword}`,
'',
'API key: ',
` - ${apiKey}`,
'',
'Use it in the Dashboard connection flow: https://waha.devlike.pro/docs/how-to/dashboard/#api-key',
];
console.log(lines.join('\n'));
+3 -2
View File
@@ -2,10 +2,11 @@ import * as process from 'node:process';
import { parseBool } from '@waha/helpers';
import * as basicAuth from 'express-basic-auth';
import { Auth } from '@waha/core/auth/config';
export function BullAuthMiddleware() {
let username = process.env.WAHA_DASHBOARD_USERNAME || '';
let password = process.env.WAHA_DASHBOARD_PASSWORD || '';
let username = Auth.dashboard.username.value || '';
let password = Auth.dashboard.password.value || '';
if (process.env.WAHA_DASHBOARD_ENABLED) {
const enabled = parseBool(process.env.WAHA_DASHBOARD_ENABLED);
if (!enabled) {
+3 -2
View File
@@ -13,6 +13,7 @@ import { ChatwootLocalesController } from '@waha/apps/chatwoot/api/chatwoot.loca
import { ChatWootExports } from '@waha/apps/chatwoot/chatwoot.module';
import { parseBool } from '@waha/helpers';
import { RMutexModule } from '@waha/modules/rmutex';
import { Auth } from '@waha/core/auth/config';
const IMPORTS = [
BullModule.forRoot({
@@ -93,11 +94,11 @@ const AppsDisabled = {
};
function checkApiKey() {
const key = process.env.WHATSAPP_API_KEY || process.env.WAHA_API_KEY;
const key = Auth.key.value;
if (!key) {
return;
}
const plain = process.env.WAHA_API_KEY_PLAIN;
const plain = Auth.keyplain.value;
if (!plain) {
throw Error(
'WAHA_API_KEY set, please provide WAHA_API_KEY_PLAIN when WAHA_APPS_ENABLED',
+11 -16
View File
@@ -13,12 +13,11 @@ import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { WAHAEvents, WAMessageAck } from '@waha/structures/enums.dto';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { WAHAWebhookMessageAck } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { isJidCusFormat } from '@waha/utils/wa';
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import { ShouldMarkAsReadInChatWoot } from '@waha/apps/chatwoot/consumers/waha/message.ack.utils';
@Processor(QueueName.WAHA_MESSAGE_ACK, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
@@ -30,18 +29,8 @@ export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
super(manager, log, rmutex, 'WAHAMessageAckConsumer');
}
ShouldProcess(event: WAHAWebhookMessageAck): boolean {
// Mark as seen only if it's DM
// Ignore groups and other multiple participants chats
const chatId = this.GetChatId(event);
if (!isJidCusFormat(chatId) && !isLidUser(chatId)) {
return false;
}
// Only READ and PLAYED
const payload = event.payload;
return (
payload.ack === WAMessageAck.READ || payload.ack == WAMessageAck.PLAYED
);
ShouldProcess(event: any): boolean {
return ShouldMarkAsReadInChatWoot(event);
}
GetChatId(event: WAHAWebhookMessageAck): string {
@@ -62,7 +51,13 @@ export class WAHAMessageAckConsumer extends ChatWootWAHABaseConsumer {
session,
container.Locale(),
);
await handler.handle(event);
try {
await handler.handle(event);
} catch (e) {
// TODO: Investigate errors
// https://github.com/devlikeapro/waha/issues/1492
this.logger.error(e);
}
}
}
@@ -0,0 +1,466 @@
import type { WAHAEngine, WAHAEvents } from '@waha/structures/enums.dto';
import { ShouldMarkAsReadInChatWoot } from '@waha/apps/chatwoot/consumers/waha/message.ack.utils';
interface TestParam {
name: string;
expected: boolean;
GOWS: any;
NOWEB: any;
WEBJS: any;
}
/**
* Me - 444444444444 (lid - 144444444444444)
* Participant - 11111111111 (lid - 011111111111111)
* Group - 222222222222222222
*/
const ShouldMarkAsReadCases: TestParam[] = [
{
name: 'Participant read My message (READ ack)',
expected: true,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack' as WAHAEvents,
payload: {
id: 'true_11111111111@c.us_3333333333333333333333',
from: '11111111111@c.us',
to: null,
participant: null,
fromMe: true,
ack: 3,
ackName: 'READ',
_data: {
Chat: '11111111111@s.whatsapp.net',
Sender: '11111111111@s.whatsapp.net',
IsFromMe: false,
IsGroup: false,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['3333333333333333333333'],
Type: 'read',
MessageSender: '',
},
},
metadata: {},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
engine: 'GOWS' as WAHAEngine,
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
from: '11111111111@c.us',
fromMe: true,
ack: 3,
ackName: 'READ',
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAA',
from: '444444444444@c.us',
fromMe: true,
source: 'app',
to: '11111111111@c.us',
body: 'Nn',
hasMedia: false,
media: null,
ack: 3,
ackName: 'READ',
vCards: [],
_data: {
id: {
fromMe: true,
remote: '11111111111@c.us',
id: 'AAA',
_serialized: 'true_11111111111@c.us_AAA',
},
viewed: false,
from: '444444444444@c.us',
to: '11111111111@c.us',
author: '444444444444@c.us',
ack: 3,
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
},
engine: 'WEBJS',
environment: {
version: '2025.10.4',
engine: 'WEBJS',
tier: 'PLUS',
browser: '/usr/bin/google-chrome-stable',
},
},
},
{
name: 'Participant received My message (DEVICE ack)',
expected: false,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack' as WAHAEvents,
payload: {
id: 'true_11111111111@c.us_3333333333333333333333',
from: '11111111111@c.us',
to: null,
participant: null,
fromMe: true,
ack: 2,
ackName: 'DEVICE',
_data: {
Chat: '11111111111@s.whatsapp.net',
Sender: '11111111111:3@s.whatsapp.net',
IsFromMe: false,
IsGroup: false,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['3333333333333333333333'],
Type: '',
MessageSender: '',
},
},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
engine: 'GOWS' as WAHAEngine,
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
from: '11111111111@c.us',
fromMe: true,
ack: 2,
ackName: 'DEVICE',
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '1144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_11111111111@c.us_AAA',
from: '444444444444@c.us',
fromMe: true,
source: 'app',
to: '11111111111@c.us',
body: 'Nn',
hasMedia: false,
media: null,
ack: 2,
ackName: 'DEVICE',
vCards: [],
_data: {
id: {
fromMe: true,
remote: '11111111111@c.us',
id: 'AAA',
_serialized: 'true_11111111111@c.us_AAA',
},
from: '444444444444@c.us',
to: '11111111111@c.us',
author: '444444444444@c.us',
ack: 2,
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
},
engine: 'WEBJS',
environment: {
version: '2025.10.4',
engine: 'WEBJS',
tier: 'PLUS',
browser: '/usr/bin/google-chrome-stable',
},
},
},
{
name: 'Me read Participant message on another device (READ ack)',
expected: false,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack' as WAHAEvents,
payload: {
id: 'false_11111111111@c.us_3333333333333333333333',
from: '11111111111@c.us',
to: null,
participant: null,
fromMe: false,
ack: 3,
ackName: 'READ',
_data: {
Chat: '11111111111@s.whatsapp.net',
Sender: '444444444444@s.whatsapp.net',
IsFromMe: true,
IsGroup: false,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['3333333333333333333333'],
Type: 'read',
MessageSender: '11111111111@s.whatsapp.net',
},
},
metadata: {},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
engine: 'GOWS' as WAHAEngine,
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: '11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
from: '11111111111@c.us',
fromMe: false,
ack: 3,
ackName: 'READ',
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '1144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: null,
},
{
name: 'Participant read My Group Message (READ ack)',
expected: false,
GOWS: {
id: 'evt_00000000000000000000000000',
session: 'default',
payload: {
id: 'true_222222222222222222@g.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_444444444444@c.us',
from: '222222222222222222@g.us',
to: '011111111111111@lid',
participant: '011111111111111@lid',
fromMe: true,
ack: 3,
ackName: 'READ',
_data: {
Chat: '222222222222222222@g.us',
Sender: '011111111111111@lid',
IsFromMe: false,
IsGroup: true,
AddressingMode: '',
SenderAlt: '',
RecipientAlt: '',
BroadcastListOwner: '',
BroadcastRecipients: null,
MessageIDs: ['AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
Type: 'read',
MessageSender: '',
},
},
metadata: {},
me: {
id: '444444444444@c.us',
lid: '144444444444444@lid',
jid: '444444444444:42@s.whatsapp.net',
pushName: '',
},
environment: {
version: '2025.10.4',
engine: 'GOWS',
tier: 'PLUS',
browser: null,
},
},
NOWEB: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_222222222222222222@g.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_444444444444@c.us',
from: '222222222222222222@g.us',
to: '222222222222222222@g.us',
participant: '011111111111111@lid',
fromMe: true,
ack: 3,
ackName: 'READ',
_data: {
key: {
remoteJid: '222222222222222222@g.us',
id: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
fromMe: true,
participant: '011111111111111@lid',
},
receipt: {
userJid: '011111111111111@lid',
},
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
lid: '1144444444444444@lid',
},
engine: 'NOWEB',
environment: {
version: '2025.10.4',
engine: 'NOWEB',
tier: 'PLUS',
browser: null,
},
},
WEBJS: {
id: 'evt_00000000000000000000000000',
session: 'default',
event: 'message.ack',
payload: {
id: 'true_222222222222222222@g.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_444444444444@c.us',
from: '222222222222222222@g.us',
to: '222222222222222222@g.us',
participant: '011111111111111@lid',
fromMe: false,
ack: 3,
ackName: 'READ',
_data: {
tag: 'receipt',
attrs: {
from: {
$1: {
type: 0,
user: '222222222222222222',
server: 'g.us',
},
},
type: 'read',
id: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
participant: {
$1: {
type: 1,
user: '011111111111111',
device: 0,
domainType: 1,
},
},
},
content: null,
},
},
metadata: {},
me: {
id: '444444444444@c.us',
pushName: '',
},
engine: 'WEBJS',
environment: {
version: '2025.10.4',
engine: 'WEBJS',
tier: 'PLUS',
browser: '/usr/bin/google-chrome-stable',
},
},
},
];
describe('ShouldMarkAsReadInChatWoot', () => {
const engines = ['GOWS', 'NOWEB', 'WEBJS'];
for (const engine of engines) {
describe(engine, () => {
for (const param of ShouldMarkAsReadCases) {
const name = `[${param.expected}] ${param.name}`;
const event = param[engine];
const testfn = event ? test : test.skip;
const expected = param.expected;
testfn(name, () => {
// Test
const result = ShouldMarkAsReadInChatWoot(event);
expect(result).toBe(expected);
});
}
});
}
});
@@ -0,0 +1,31 @@
import type { WAHAWebhookMessageAck } from '@waha/structures/webhooks.dto';
import { isJidCusFormat } from '@waha/utils/wa';
import { WAMessageAck } from '@waha/structures/enums.dto';
import { isLidUser, toCusFormat } from '@waha/core/utils/jids';
export function ShouldMarkAsReadInChatWoot(
event: WAHAWebhookMessageAck,
): boolean {
// Mark as seen only if it's DM
// Ignore groups and other multiple participants chats
const chatId = event.payload.from;
if (!isJidCusFormat(chatId) && !isLidUser(chatId)) {
return false;
}
const payload = event.payload;
// Only READ and PLAYED
const read = payload.ack === WAMessageAck.READ;
const played = payload.ack == WAMessageAck.PLAYED;
if (!read && !played) {
return false;
}
// Only process when OUR message (fromMe: true) was read by recipient
if (!payload.fromMe) {
return false;
}
// Mark ChatWoot conversation as read when recipient reads our message
return true;
}
@@ -24,6 +24,7 @@ import {
LocationMessage,
MessageToChatWootConverter,
PollMessage,
ListMessage,
ShareContactMessage,
TextMessage,
UnsupportedMessage,
@@ -104,6 +105,12 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
return msg;
}
converter = new ListMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
return msg;
}
converter = new EventMessage(this.l);
msg = await converter.convert(payload, protoMessage);
if (msg) {
@@ -39,6 +39,12 @@ export class ChatWootErrorReporter {
}
const template = this.l.key(TKey.JOB_REPORT_ERROR);
const nextDelay = NextAttemptDelayInWholeSeconds(this.job);
if (nextDelay) {
// TODO: Add a way to enable it back in config
// https://github.com/devlikeapro/waha/issues/1395
// There's retries more left - ignore it for now
return;
}
const attempts = {
current: this.job.attemptsMade + 1,
max: this.job.opts?.attempts || 1,
@@ -77,6 +83,10 @@ export class ChatWootErrorReporter {
type: MessageType,
replyTo?: number,
): Promise<void> {
// TODO: Add a way to enable it back in config
// https://github.com/devlikeapro/waha/issues/1395
return null;
const template = this.l.key(TKey.JOB_REPORT_SUCCEEDED);
const attempts = {
current: this.job.attemptsMade + 1,
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇦🇪 العربية (Arabic)'
# تستخدمه WAHA لعرض الأرقام والعملات والتاريخ/الوقت
locale.base: 'ar-AE'
app.inbox.contact.name: 'تكامل واتساب (WAHA)'
app.inbox.contact.name: 'تكامل واتساب'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "options" من date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇧🇩 বাংলা (Bengali)'
# WAHA এটি সংখ্যা, মুদ্রা এবং তারিখ/সময়ের ফরম্যাটিংয়ে ব্যবহার করে।
locale.base: 'bn-BD'
app.inbox.contact.name: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন (WAHA)'
app.inbox.contact.name: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString এর "options"
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇩🇪 Deutsch (German)'
# WAHA nutzt ihn zur Darstellung von Zahlen, Währungen sowie Datum und Uhrzeit
locale.base: 'de-DE'
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
app.inbox.contact.name: 'WhatsApp Integration'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "Optionen" von date.toLocaleString
@@ -286,6 +286,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇺🇸 English (English)'
# WAHA uses it for number, currency and datetime representation
locale.base: 'en-US'
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
app.inbox.contact.name: 'WhatsApp Integration'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "options" from date.toLocaleString
@@ -160,6 +160,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇪🇸 Español (Spanish)'
# WAHA lo utiliza para formatear números, moneda y fecha/hora
locale.base: 'es-ES'
app.inbox.contact.name: 'Integración de WhatsApp (WAHA)'
app.inbox.contact.name: 'Integración de WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "opciones" de date.toLocaleString
@@ -286,6 +286,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇮🇷 فارسی (Persian)'
# WAHA از آن برای نمایش اعداد، ارز و تاریخ/زمان استفاده می‌کند
locale.base: 'fa-IR'
app.inbox.contact.name: 'ادغام واتساپ (WAHA)'
app.inbox.contact.name: 'ادغام واتساپ'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# گزینه‌های "options" از date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇫🇷 Français (French)'
# WAHA l'utilise pour le formatage des nombres, devises et dates/heures
locale.base: 'fr-FR'
app.inbox.contact.name: 'Intégration WhatsApp (WAHA)'
app.inbox.contact.name: 'Intégration WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "options" de date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇮🇱 עברית (Hebrew)'
# WAHA משתמש בכך להצגת מספרים, מטבע ותאריך/שעה
locale.base: 'he-IL'
app.inbox.contact.name: 'אינטגרציית וואטסאפ (WAHA)'
app.inbox.contact.name: 'אינטגרציית וואטסאפ'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "אפשרויות" של date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇮🇳 हिंदी (Hindi)'
# WAHA इसे संख्याएँ, मुद्रा और दिनांक/समय प्रदर्शित करने के लिए उपयोग करता है।
locale.base: 'hi-IN'
app.inbox.contact.name: 'व्हाट्सएप इंटीग्रेशन (WAHA)'
app.inbox.contact.name: 'व्हाट्सएप इंटीग्रेशन'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString की "options"
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇮🇩 Bahasa Indonesia (Indonesian)'
# WAHA menggunakannya untuk format angka, mata uang, dan tanggal/waktu
locale.base: 'id-ID'
app.inbox.contact.name: 'Integrasi WhatsApp (WAHA)'
app.inbox.contact.name: 'Integrasi WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "opsi" dari date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)'
# WAHA ਇਸਨੂੰ ਅੰਕ, ਕਰੰਸੀ ਅਤੇ ਤਾਰੀਖ/ਸਮੇਂ ਦੀ ਨਮਾਇਸ਼ ਲਈ ਵਰਤਦਾ ਹੈ
locale.base: 'pa-PK'
app.inbox.contact.name: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ (WAHA)'
app.inbox.contact.name: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString ਦੀਆਂ "options"
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇧🇷🇵🇹 Português (Portuguese)'
# A WAHA usa isso para formatar números, moeda e data/hora
locale.base: 'pt-BR'
app.inbox.contact.name: 'Integração WhatsApp (WAHA)'
app.inbox.contact.name: 'Integração WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "opções" de date.toLocaleString
@@ -286,6 +286,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇷🇺 Русский (Russian)'
# WAHA использует его для форматирования чисел, валют и даты/времени
locale.base: 'ru-RU'
app.inbox.contact.name: 'WhatsApp Интеграция (WAHA)'
app.inbox.contact.name: 'WhatsApp Интеграция'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "параметры" из date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇹🇷 Türkçe (Turkish)'
# WAHA bunu sayı, para birimi ve tarih/saat gösterimi için kullanır
locale.base: 'tr-TR'
app.inbox.contact.name: 'WhatsApp Entegrasyonu (WAHA)'
app.inbox.contact.name: 'WhatsApp Entegrasyonu'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString için "options"
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇺🇦 Українська (Ukrainian)'
# WAHA використовує його для форматування чисел, валют і дати/часу
locale.base: 'uk-UA'
app.inbox.contact.name: 'Інтеграція WhatsApp (WAHA)'
app.inbox.contact.name: 'Інтеграція WhatsApp'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# "параметри" з date.toLocaleString
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+29 -1
View File
@@ -6,7 +6,7 @@ locale.name: '🇵🇰 اردو (Urdu)'
# WAHA اسے نمبرز، کرنسی اور تاریخ/وقت کی نمائندگی کے لیے استعمال کرتا ہے۔
locale.base: 'ur-PK'
app.inbox.contact.name: 'واٹس ایپ انٹیگریشن (WAHA)'
app.inbox.contact.name: 'واٹس ایپ انٹیگریشن'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
# date.toLocaleString کی "options"
@@ -283,6 +283,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+28
View File
@@ -285,6 +285,34 @@ whatsapp.to.chatwoot.message.poll: |-
{{/options}}
{{/poll}}
whatsapp.to.chatwoot.message.list: |-
{{#message.listMessage}}
{{#title}}
*{{{title}}}*
{{/title}}
{{#description}}
{{{description}}}
{{/description}}
{{#footerText}}
{{{footerText}}}
{{/footerText}}
{{#buttonText}}
{{{buttonText}}}
{{/buttonText}}
{{#sections}}
{{#title}}
{{{title}}}:
{{/title}}
{{#rows}}
{{#title}}
- {{{title}}}
{{/title}}
{{/rows}}
{{/sections}}
{{/message.listMessage}}
whatsapp.to.chatwoot.message.event: |-
{{#message}}
{{#eventMessage}}
+5
View File
@@ -56,6 +56,7 @@ export enum TKey {
WA_TO_CW_MESSAGE_PIX = 'whatsapp.to.chatwoot.message.pix',
WA_TO_CW_MESSAGE_UNSUPPORTED = 'whatsapp.to.chatwoot.message.unsupported',
WA_TO_CW_MESSAGE_FACEBOOK_AD = 'whatsapp.to.chatwoot.message.facebook.ad',
WA_TO_CW_MESSAGE_LIST = 'whatsapp.to.chatwoot.message.list',
//
// App Inbox
@@ -174,6 +175,10 @@ export type TemplatePayloads = {
eventMessage: Record<string, unknown>;
};
};
[TKey.WA_TO_CW_MESSAGE_LIST]: {
payload: WAMessage;
message: proto.Message;
};
[TKey.WA_TO_CW_MESSAGE_PIX]: {
payload: WAMessage;
message: proto.Message | null;
@@ -11,7 +11,8 @@ import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/uti
import { WAMessage } from '@waha/structures/responses.dto';
import type { proto } from '@adiwajshing/baileys';
import * as lodash from 'lodash';
import { fetchBuffer } from '@waha/core/abc/session.abc';
import { fetchBuffer } from '@waha/utils/fetch';
export class FacebookAdMessage implements MessageToChatWootConverter {
constructor(
@@ -0,0 +1,37 @@
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
import * as lodash from 'lodash';
import type { proto } from '@adiwajshing/baileys';
import { WAMessage } from '@waha/structures/responses.dto';
export class ListMessage implements MessageToChatWootConverter {
constructor(private readonly locale: Locale) {}
convert(
payload: WAMessage,
protoMessage: proto.Message | null,
): ChatWootMessagePartial | null {
const listMessage = protoMessage?.listMessage;
if (lodash.isEmpty(listMessage)) {
return null;
}
const content = this.locale
.key(TKey.WA_TO_CW_MESSAGE_LIST)
.r({ payload, message: protoMessage as proto.Message });
if (isEmptyString(content)) {
return null;
}
return {
content: WhatsappToMarkdown(content),
attachments: [],
private: undefined,
};
}
}
@@ -116,8 +116,7 @@ describe('PixMessage - pt-BR', () => {
****Tipo:**** PHONE
****Referência:**** 4TB11111111
💳 ****PIX Copia e Cola enviado****
`;
💳 ****PIX Copia e Cola enviado****`;
expect(protoMessage).not.toBeNull();
expect(result).not.toBeNull();
@@ -237,8 +236,7 @@ describe('PixMessage - pt-BR', () => {
****Valor:**** R$\u00A025,12
****Referência:**** 4TB11111111
💳 ****PIX Copia e Cola enviado****
`;
💳 ****PIX Copia e Cola enviado****`;
expect(protoMessage).not.toBeNull();
expect(result).not.toBeNull();
@@ -12,6 +12,7 @@ export { FacebookAdMessage } from './FacebookAdMessage';
export { PollMessage } from './PollMessage';
export { EventMessage } from './EventMessage';
export { PixMessage } from './PixMessage';
export { ListMessage } from './ListMessage';
export type Awaitable<T> = T | Promise<T>;
@@ -14,4 +14,43 @@ describe('WhatsappToMarkdown', () => {
const expected = `**bold** | *italic* | ~~strike~~ | \`one line code\` | \`\`\`\nmultiple lines\`\`\` `;
expect(WhatsappToMarkdown(input)).toBe(expected);
});
// Tests for URL protection
it('preserves URLs with single underscore', () => {
const input = 'https://example.com/page_name';
expect(WhatsappToMarkdown(input)).toBe('https://example.com/page_name');
});
it('preserves URLs with double underscores', () => {
const input = 'https://en.wikipedia.org/wiki/Sarah_Jessica_Parker';
expect(WhatsappToMarkdown(input)).toBe(
'https://en.wikipedia.org/wiki/Sarah_Jessica_Parker',
);
});
it('preserves URLs with multiple underscores', () => {
const input = 'https://example.com/a_b_c_test';
expect(WhatsappToMarkdown(input)).toBe('https://example.com/a_b_c_test');
});
it('handles mixed text with URLs and formatting', () => {
const input = 'Veja *isso* em https://example.com/test_link e _aquilo_';
const expected =
'Veja **isso** em https://example.com/test_link e *aquilo*';
expect(WhatsappToMarkdown(input)).toBe(expected);
});
it('handles multiple URLs in same text', () => {
const input =
'Links: https://example.com/page_1 e https://example.com/page_2';
expect(WhatsappToMarkdown(input)).toBe(
'Links: https://example.com/page_1 e https://example.com/page_2',
);
});
it('handles URLs with underscores and regular italic formatting', () => {
const input = 'URL: https://example.com/test_page e texto _em itálico_';
const expected = 'URL: https://example.com/test_page e texto *em itálico*';
expect(WhatsappToMarkdown(input)).toBe(expected);
});
});
@@ -1,3 +1,5 @@
import { UrlProtection } from '@waha/apps/chatwoot/messages/to/markdown';
export function WhatsappToMarkdown(text: string): string {
if (!text) {
return text;
@@ -5,13 +7,22 @@ export function WhatsappToMarkdown(text: string): string {
if (text == '') {
return '';
}
return (
text
// Bold: *bold* → **bold**
.replace(/\*(.*?)\*/g, '**$1**')
// Strikethrough: ~strike~ → ~~strike~~
.replace(/~(.*?)~/g, '~~$1~~')
// Italic: _italic_ → *italic*
.replace(/_(.*?)_/g, '*$1*')
);
// Extract and protect URLs before formatting transformations
const protection = new UrlProtection();
const urls = protection.protect(text);
text = urls.text;
// Apply markdown transformations to "clean" text
let result = text
// Bold: *bold* → **bold**
.replace(/\*(.*?)\*/g, '**$1**')
// Strikethrough: ~strike~ → ~~strike~~
.replace(/~(.*?)~/g, '~~$1~~')
// Italic: _italic_ → *italic*
.replace(/_(.*?)_/g, '*$1*');
// Restore original URLs after all transformations
result = urls.restore(result);
return result;
}
+36
View File
@@ -0,0 +1,36 @@
const DEFAULT_PLACEHOLDER_PREFIX = 'URLPLACEHOLDER';
const URL_REGEX = /https?:\/\/[^\s]+/g;
export class UrlProtection {
/** Replaces URLs with placeholders so downstream formatters don't touch them. */
constructor(
private readonly placeholder: string = DEFAULT_PLACEHOLDER_PREFIX,
) {}
protect(text: string): ProtectedUrlText {
const urls: string[] = [];
const result = text.replace(URL_REGEX, (url) => {
const index = urls.length;
urls.push(url);
return `${this.placeholder}${index}`;
});
return new ProtectedUrlText(result, urls, this.placeholder);
}
}
export class ProtectedUrlText {
/** Carries protected text and restores URLs back into place. */
constructor(
public text: string,
private readonly urls: string[],
private readonly placeholder: string,
) {}
restore(text: string = this.text): string {
return this.urls.reduce((acc, url, index) => {
const placeholder = `${this.placeholder}${index}`;
return acc.replace(placeholder, url);
}, text);
}
}
@@ -14,4 +14,41 @@ describe('MarkdownToWhatsApp', () => {
const expected = `Here is a code block:\n\`\`\`some code\`\`\`\n*bold* _italic_ ~strike~ example (http://example.com)\n* item`;
expect(MarkdownToWhatsApp(input)).toBe(expected);
});
// Tests for URL protection
it('preserves URLs with multiple underscores', () => {
const input = 'https://example.com/page__name__test';
expect(MarkdownToWhatsApp(input)).toBe(
'https://example.com/page__name__test',
);
});
it('preserves URLs with double underscores', () => {
const input = 'https://en.wikipedia.org/wiki/Sarah_Jessica_Parker';
expect(MarkdownToWhatsApp(input)).toBe(
'https://en.wikipedia.org/wiki/Sarah_Jessica_Parker',
);
});
it('preserves URLs inside markdown links', () => {
const input =
'Veja [perfil](https://en.wikipedia.org/wiki/Sarah_Jessica_Parker)';
const expected =
'Veja perfil (https://en.wikipedia.org/wiki/Sarah_Jessica_Parker)';
expect(MarkdownToWhatsApp(input)).toBe(expected);
});
it('handles mixed text with URLs and formatting', () => {
const input = 'Veja **isso** em https://example.com/test_link e _aquilo_';
const expected = 'Veja *isso* em https://example.com/test_link e _aquilo_';
expect(MarkdownToWhatsApp(input)).toBe(expected);
});
it('handles multiple URLs in the same text', () => {
const input =
'Links: https://example.com/page_1 e https://example.com/page_2';
expect(MarkdownToWhatsApp(input)).toBe(
'Links: https://example.com/page_1 e https://example.com/page_2',
);
});
});
+2 -1
View File
@@ -11,13 +11,14 @@ import {
} from '@waha/structures/chatting.dto';
import { SessionInfo } from '@waha/structures/sessions.dto';
import axios, { AxiosInstance } from 'axios';
import { Auth } from '@waha/core/auth/config';
export class WAHASelf {
public client: AxiosInstance;
constructor() {
// Set 'X-Api-Key'
const key = process.env.WAHA_API_KEY_PLAIN;
const key = Auth.keyplain.value;
const port =
parseInt(process.env.PORT) ||
parseInt(process.env.WHATSAPP_API_PORT) ||
+2 -4
View File
@@ -5,6 +5,7 @@ import { IgnoreJidConfig } from '@waha/core/utils/jids';
import { parseBool } from './helpers';
import { WebhookConfig } from './structures/webhooks.config.dto';
import { Auth } from '@waha/core/auth/config';
@Injectable()
export class WhatsappConfigService implements OnApplicationBootstrap {
@@ -139,10 +140,7 @@ export class WhatsappConfigService implements OnApplicationBootstrap {
}
getApiKey(): string | undefined {
return (
this.configService.get('WHATSAPP_API_KEY', '') ||
this.configService.get('WAHA_API_KEY', '')
);
return Auth.key.value;
}
getExcludedPaths(): string[] {
+2 -17
View File
@@ -5,8 +5,8 @@ import {
import { MessagesForRead } from '@waha/core/utils/convertors';
import {
IgnoreJidConfig,
isJidNewsletter,
isJidBroadcast,
isJidNewsletter,
JidFilter,
} from '@waha/core/utils/jids';
import {
@@ -43,7 +43,6 @@ import { complete } from '@waha/utils/reactive/complete';
import { SwitchObservable } from '@waha/utils/reactive/SwitchObservable';
import axios from 'axios';
import axiosRetry from 'axios-retry';
import { Agent as HttpsAgent } from 'https';
import * as fs from 'fs';
import * as lodash from 'lodash';
import * as NodeCache from 'node-cache';
@@ -129,6 +128,7 @@ import {
import { IMediaManager } from '../media/IMediaManager';
import { QR } from '../QR';
import { DataStore } from './DataStore';
import { fetchBuffer } from '@waha/utils/fetch';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const qrcode = require('qrcode-terminal');
@@ -1081,21 +1081,6 @@ export abstract class WhatsappSession {
}
}
const InsecureHttpsAgent = new HttpsAgent({
rejectUnauthorized: false,
});
export async function fetchBuffer(url: string): Promise<Buffer> {
return axios
.get(url, {
responseType: 'arraybuffer',
httpsAgent: InsecureHttpsAgent,
})
.then((res) => {
return Buffer.from(res.data);
});
}
export function getGroupInviteLink(code: string) {
if (code.startsWith('https://')) {
return code;
+4 -13
View File
@@ -6,11 +6,15 @@ import {
NoAuth,
PlainApiKeyAuth,
} from '@waha/core/auth/auth';
import { ReportGeneratedValue } from '@waha/core/auth/config';
export function ApiKeyAuthFactory(
config: WhatsappConfigService,
logger: LoggerService,
): IApiKeyAuth {
setTimeout(() => {
ReportGeneratedValue();
}, 4000);
const apiKey = config.getApiKey();
if (!apiKey) {
setTimeout(() => {
@@ -36,18 +40,5 @@ export function ApiKeyAuthFactory(
}
// Fallback to plain text
setTimeout(() => {
logger.warn('⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️');
logger.warn(
'WARNING: Plain text API key detected. This is a security risk.',
);
logger.warn(
'Your API key can be exposed in environment variables or process lists.',
);
logger.warn(
'For better security, use WAHA_API_KEY=sha512:{SHA512_HASH_FOR_YOUR_API_KEY}',
);
logger.warn('⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️');
}, 2000);
return new PlainApiKeyAuth(apiKey);
}
+154
View File
@@ -0,0 +1,154 @@
import { parseBool } from '@waha/helpers';
export interface SValue {
param: string;
value: string | null;
generated: boolean;
}
function rand() {
return crypto.randomUUID().toString().replace(/-/g, '');
}
function FromEnv(
param: string,
skip: boolean,
adefault: string,
search: any[],
): SValue {
let value = process.env[param];
const common = search.includes(value);
if (common && !skip) {
// Use generated value for the setting
return {
param: param,
value: adefault,
generated: true,
};
}
return {
param: param,
value: value,
generated: false,
};
}
const keys = [
'',
null,
undefined,
'123',
'321',
'waha',
'admin',
'00000000000000000000000000000000',
'11111111111111111111111111111111',
'sha512:98b6d128682e280b74b324ca82a6bae6e8a3f7174e0605bfd52eb9948fad8984854ec08f7652f32055c4a9f12b69add4850481d9503a7f2225501671d6124648',
];
const nulls = ['', null, undefined];
interface UserPassword {
username: SValue;
password: SValue;
}
export class AuthConfig {
public key: SValue;
public keyplain: SValue;
public dashboard: UserPassword;
public swagger: UserPassword;
constructor() {
if (process.env.WHATSAPP_API_KEY) {
process.env.WAHA_API_KEY = process.env.WHATSAPP_API_KEY;
}
this.key = FromEnv(
'WAHA_API_KEY',
parseBool(process.env.WAHA_NO_API_KEY),
rand(),
keys,
);
this.keyplain = FromEnv(
'WAHA_API_KEY_PLAIN',
false,
this.key.value?.startsWith('sha512:') ? null : this.key.value,
[],
);
this.dashboard = this.getDashboard();
this.swagger = this.getSwagger();
}
private getDashboard(): UserPassword {
const password = FromEnv(
'WAHA_DASHBOARD_PASSWORD',
parseBool(process.env.WAHA_DASHBOARD_NO_PASSWORD),
rand(),
keys,
);
const username = FromEnv(
'WAHA_DASHBOARD_USERNAME',
false,
'admin',
password.value ? nulls : [],
);
return {
username: username,
password: password,
};
}
private getSwagger(): UserPassword {
const password = FromEnv(
'WHATSAPP_SWAGGER_PASSWORD',
parseBool(process.env.WHATSAPP_SWAGGER_NO_PASSWORD),
this.dashboard.password.value,
keys,
);
const username = FromEnv(
'WHATSAPP_SWAGGER_USERNAME',
false,
'admin',
password.value ? nulls : [],
);
return {
username: username,
password: password,
};
}
}
export const Auth = new AuthConfig();
export function ReportGeneratedValue() {
let values = [
Auth.key,
Auth.dashboard.username,
Auth.dashboard.password,
Auth.swagger.username,
Auth.swagger.password,
];
values = values.filter((key) => key.generated);
if (values.length === 0) {
return;
}
console.warn('');
console.warn('⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️');
console.warn('Generated credentials (persist to .env or WAHA_* env vars)');
console.warn(
'Save these values to your environment (.env or WAHA_*) to reuse them; new keys are generated on every start otherwise.',
);
console.warn('');
console.warn("cat <<'EOF' > .env");
console.warn('');
for (const key of values) {
console.warn(`${key.param}=${key.value}`);
}
console.warn('EOF');
console.warn('');
console.warn('Generated credentials ready to copy');
console.warn('⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️');
}
@@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config';
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
import { parseBool } from '../../helpers';
import { Auth } from '@waha/core/auth/config';
@Injectable()
export class DashboardConfigServiceCore {
@@ -20,8 +21,8 @@ export class DashboardConfigServiceCore {
}
get credentials(): [string, string] | null {
const user = this.configService.get('WAHA_DASHBOARD_USERNAME', '');
const password = this.configService.get('WAHA_DASHBOARD_PASSWORD', '');
const user = Auth.dashboard.username.value || '';
const password = Auth.dashboard.password.value || '';
if (!user && !password) {
return null;
}
+3 -8
View File
@@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config';
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
import { parseBool } from '../../helpers';
import { Auth } from '@waha/core/auth/config';
@Injectable()
export class SwaggerConfigServiceCore {
@@ -26,14 +27,8 @@ export class SwaggerConfigServiceCore {
}
get credentials(): [string, string] | undefined {
const user = this.configService.get<string>(
'WHATSAPP_SWAGGER_USERNAME',
undefined,
);
const password = this.configService.get<string>(
'WHATSAPP_SWAGGER_PASSWORD',
undefined,
);
const user = Auth.swagger.username.value;
const password = Auth.swagger.password.value;
if (!user && !password) {
return null;
}
+19 -20
View File
@@ -1,4 +1,5 @@
import type { proto, WAMessageKey } from '@adiwajshing/baileys';
import { normalizeMessageContent, WAMessageKey } from '@adiwajshing/baileys';
import { proto } from '@adiwajshing/baileys';
import * as grpc from '@grpc/grpc-js';
import { connectivityState } from '@grpc/grpc-js';
import { UnprocessableEntityException } from '@nestjs/common';
@@ -166,6 +167,7 @@ import {
isLabelUpsertEvent,
} from './labels.gows';
import esm from '@waha/vendor/esm';
import { IsEditedMessage } from '@waha/core/utils/pwa';
enum WhatsMeowEvent {
CONNECTED = 'gows.ConnectedEventData',
@@ -491,19 +493,15 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
// Handle edited messages
const messagesEdited$ = messages$.pipe(
filter((msg) => {
return (
msg?.Message?.protocolMessage?.type === 14 &&
msg?.Message?.protocolMessage?.editedMessage !== undefined
);
}),
filter((message) => IsEditedMessage(message.Message)),
mergeMap(async (message): Promise<WAMessageEditedBody> => {
const waMessage = await this.toWAMessage(message);
const content = normalizeMessageContent(message.Message);
// Extract the body from editedMessage using extractBody function
const body =
extractBody(message.Message.protocolMessage.editedMessage) || '';
const body = extractBody(content.protocolMessage.editedMessage) || '';
// Extract the original message ID from protocolMessage.key
const editedMessageId = message.Message.protocolMessage.key?.ID;
// @ts-ignore
const editedMessageId = content.protocolMessage.key?.ID;
return {
...waMessage,
body: body,
@@ -1948,17 +1946,18 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
// if there is no text or media message
if (!message) return;
if (!message.Message) return;
// Ignore reactions, we have a dedicated handler for that
if (message.Message.reactionMessage) return;
// Ignore poll votes, we have a dedicated handler for that
if (message.Message.pollUpdateMessage) return;
// Ignore event response, we have a dedicated handler for that
if (message.Message.encEventResponseMessage) return;
// Ignore protocol messages
if (message.Message.protocolMessage) return;
const normalizedContent = esm.b.normalizeMessageContent(message.Message);
const contentType = esm.b.getContentType(normalizedContent);
const content = esm.b.normalizeMessageContent(message.Message);
// Ignore reactions, we have a dedicated handler for that
if (content.reactionMessage) return;
// Ignore poll votes, we have a dedicated handler for that
if (content.pollUpdateMessage) return;
// Ignore event response, we have a dedicated handler for that
if (content.encEventResponseMessage) return;
// Ignore protocol messages
if (content.protocolMessage) return;
const contentType = esm.b.getContentType(content);
// Ignore device sent message
if (contentType == 'deviceSentMessage') {
return;
+21 -64
View File
@@ -68,12 +68,13 @@ import {
} from '@waha/core/exceptions';
import { toVcardV3 } from '@waha/core/vcard';
import { createAgentProxy } from '@waha/core/helpers.proxy';
import type { Agent } from 'https';
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
import { QR } from '@waha/core/QR';
import { AckToStatus, StatusToAck } from '@waha/core/utils/acks';
import { ExtractMessageKeysForRead } from '@waha/core/utils/convertors';
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
import { isJidNewsletter, toJID } from '@waha/core/utils/jids';
import { isJidNewsletter, toCusFormat, toJID } from '@waha/core/utils/jids';
import { DistinctAck } from '@waha/core/utils/reactive';
import { flipObject, splitAt } from '@waha/helpers';
import { PairingCodeResponse } from '@waha/structures/auth.dto';
@@ -178,8 +179,6 @@ import { exclude } from '@waha/utils/reactive/ops/exclude';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import { SinglePeriodicJobRunner } from '@waha/utils/SinglePeriodicJobRunner';
import { StatusTracker } from '@waha/utils/StatusTracker';
import * as Buffer from 'buffer';
import { Agent } from 'https';
import * as lodash from 'lodash';
import * as NodeCache from 'node-cache';
import {
@@ -199,6 +198,8 @@ import { INowebStore } from './store/INowebStore';
import { NowebPersistentStore } from './store/NowebPersistentStore';
import { NowebStorageFactoryCore } from './store/NowebStorageFactoryCore';
import { ensureNumber, extractMediaContent } from './utils';
import { Agents } from '@waha/core/engines/noweb/types';
import { IsEditedMessage } from '@waha/core/utils/pwa';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const promiseRetry = require('promise-retry');
@@ -307,7 +308,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
await this.sock?.logout();
}
getSocketConfig(agent, state): Partial<SocketConfig> {
getSocketConfig(agents: Agents | undefined, state): Partial<SocketConfig> {
const fullSyncEnabled = this.sessionConfig?.noweb?.store?.fullSync || false;
const browser = ['Ubuntu', 'Chrome', '20.0.04'] as WABrowserDescription;
let markOnlineOnConnect = this.sessionConfig?.noweb?.markOnline;
@@ -315,8 +316,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
markOnlineOnConnect = true;
}
return {
agent: agent,
fetchAgent: agent,
agent: agents?.socket,
// Baileys types still expect a Node https.Agent here,
// but 'undici' fetch requires a Dispatcher.
// Cast keeps the compiler satisfied while we pass the ProxyAgent at runtime.
fetchAgent: agents?.fetch as unknown as Agent,
auth: state,
printQRInTerminal: false,
browser: browser,
@@ -346,9 +350,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.authNOWEBStore = store;
}
const { state, saveCreds } = this.authNOWEBStore;
const agent = this.makeAgent();
const agents = this.makeProxyAgents();
const socketConfig: SocketConfig = this.getSocketConfig(
agent,
agents,
state,
) as SocketConfig;
const sock = makeWASocket(socketConfig);
@@ -356,7 +360,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return sock;
}
protected makeAgent(): Agent {
protected makeProxyAgents(): Agents | undefined {
if (!this.proxyConfig) {
return undefined;
}
@@ -605,14 +609,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
// https://github.com/WhiskeySockets/Baileys/pull/855/
this.sock.ev.on('messages.upsert', ({ messages }) => {
for (const message of messages) {
const content = normalizeMessageContent(message.message);
const protocolMsg = content?.protocolMessage;
if (
protocolMsg !== null &&
protocolMsg !== undefined &&
protocolMsg.editedMessage &&
protocolMsg.key
) {
if (IsEditedMessage(message.message)) {
const content = normalizeMessageContent(message.message);
const protocolMsg = content?.protocolMessage;
this.sock?.ev.emit('messages.update', [
{
key: {
@@ -1894,22 +1893,15 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.events2.get(WAHAEvents.MESSAGE_REVOKED).switch(messagesRevoked$);
// Handle edited messages
// @ts-ignore
const messagesEdited$ = messagesUpsert$.pipe(
filter(
(message) =>
// @ts-ignore
message.message?.protocolMessage?.type ===
proto.Message.ProtocolMessage.Type.MESSAGE_EDIT &&
message.message?.protocolMessage?.editedMessage !== undefined,
),
filter((message) => IsEditedMessage(message.message)),
mergeMap(async (message): Promise<WAMessageEditedBody> => {
const waMessage = this.toWAMessage(message);
// Extract the body from editedMessage using extractBody function
const body =
extractBody(message.message.protocolMessage.editedMessage) || '';
const content = normalizeMessageContent(message.message);
const body = extractBody(content.protocolMessage.editedMessage) || '';
// Extract the original message ID from protocolMessage.key
const editedMessageId = message.message.protocolMessage.key?.id;
const editedMessageId = content.protocolMessage.key?.id;
return {
...waMessage,
body: body,
@@ -2205,11 +2197,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
)
return;
// Ignore edit, we have a dedicated event for that
if (
message.message?.protocolMessage?.type ===
proto.Message.ProtocolMessage.Type.MESSAGE_EDIT
)
return;
if (IsEditedMessage(message.message)) return;
if (
message.message?.protocolMessage?.type ===
proto.Message.ProtocolMessage.Type.EPHEMERAL_SYNC_RESPONSE
@@ -2633,37 +2621,6 @@ export class NOWEBEngineMediaProcessor implements IMediaEngineProcessor<any> {
}
}
/**
* Convert from 11111111111@s.whatsapp.net to 11111111111@c.us
*/
export function toCusFormat(remoteJid) {
if (!remoteJid) {
return remoteJid;
}
if (isJidGroup(remoteJid)) {
return remoteJid;
}
if (isJidBroadcast(remoteJid)) {
return remoteJid;
}
if (isLidUser(remoteJid)) {
return remoteJid;
}
if (isJidNewsletter(remoteJid)) {
return remoteJid;
}
if (!remoteJid) {
return;
}
if (remoteJid == 'me') {
return remoteJid;
}
let number = remoteJid.split('@')[0];
// remove :{device} part
number = number.split(':')[0];
return ensureSuffix(number);
}
export const ALL_JID = 'all@s.whatsapp.net';
/**
+7
View File
@@ -0,0 +1,7 @@
import { Agent } from 'https';
import type { Dispatcher } from 'undici';
export interface Agents {
socket: Agent;
fetch: Dispatcher;
}
+8 -3
View File
@@ -1,10 +1,11 @@
import { Agent } from 'https';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { URL } from 'url';
import { ProxyAgent } from 'undici';
import { WhatsappConfigService } from '../config.service';
import { ProxyConfig } from '../structures/sessions.dto';
import { WhatsappSession } from './abc/session.abc';
import { Agents } from '@waha/core/engines/noweb/types';
export function getProxyConfig(
config: WhatsappConfigService,
@@ -63,11 +64,15 @@ function getAuthenticatedUrl(
* Return https Agent proxy for the config
* @param proxyConfig
*/
export function createAgentProxy(proxyConfig: ProxyConfig): Agent | undefined {
export function createAgentProxy(proxyConfig: ProxyConfig): Agents | undefined {
const url = getAuthenticatedUrl(
proxyConfig.server,
proxyConfig.username || '',
proxyConfig.password || '',
);
return new HttpsProxyAgent(url);
const socketAgent = new HttpsProxyAgent(url);
const fetchAgent = new ProxyAgent({ uri: url });
return { socket: socketAgent, fetch: fetchAgent };
}
+5 -4
View File
@@ -36,6 +36,10 @@ export function isPnUser(jid: string) {
);
}
function normalizeJid(jid: string): string {
return jid.replace(/:\d+(?=@)/, '');
}
/**
* Convert from 11111111111@c.us to 11111111111@s.whatsapp.net
* @param chatId
@@ -135,14 +139,11 @@ export function toCusFormat(remoteJid) {
return remoteJid;
}
if (isLidUser(remoteJid)) {
return remoteJid;
return normalizeJid(remoteJid);
}
if (isJidNewsletter(remoteJid)) {
return remoteJid;
}
if (!remoteJid) {
return;
}
if (remoteJid == 'me') {
return remoteJid;
}
+22
View File
@@ -0,0 +1,22 @@
/**
* Proto WhatsApp Utils
*/
import { normalizeMessageContent, proto } from '@adiwajshing/baileys';
export function IsEditedMessage(message: proto.IMessage): boolean {
message = normalizeMessageContent(message);
if (!message) {
return false;
}
if (
message?.protocolMessage?.type !==
proto.Message.ProtocolMessage.Type.MESSAGE_EDIT
) {
return false;
}
if (message?.protocolMessage?.editedMessage == null) {
return false;
}
return true;
}
+3
View File
@@ -18,6 +18,9 @@ import { SwaggerConfiguratorCore } from './core/SwaggerConfiguratorCore';
import { AllExceptionsFilter } from './nestjs/AllExceptionsFilter';
import { getWAHAVersion, VERSION, WAHAVersion } from './version';
import { loadESMModules } from '@waha/vendor/esm';
import { setGlobalDispatcher, Agent } from 'undici';
setGlobalDispatcher(new Agent({ connect: { family: 4 } }));
const logger: Logger = pino({
level: getPinoLogLevel(),
+7 -1
View File
@@ -150,7 +150,7 @@ export class SessionConfig {
})
@IsDynamicObject()
@IsOptional()
metadata?: Map<string, string>;
metadata?: Record<string, string>;
@ApiProperty({
example: null,
@@ -225,6 +225,12 @@ export class MeInfo {
})
lid?: string;
@ApiProperty({
example: '123123:123@s.whatsapp.net',
description: 'Your id with device number',
})
jid?: string;
pushName: string;
}
+1 -1
View File
@@ -153,7 +153,7 @@ export class WAHAWebhook<Payload = any> {
},
description: 'Metadata for the session.',
})
metadata?: Map<string, string>;
metadata?: Record<string, string>;
@ApiProperty({
example: WAHAEngine.WEBJS,
+17
View File
@@ -0,0 +1,17 @@
import axios from 'axios';
import { Agent as HttpsAgent } from 'https';
const InsecureHttpsAgent = new HttpsAgent({
rejectUnauthorized: false,
});
export async function fetchBuffer(url: string): Promise<Buffer> {
return axios
.get(url, {
responseType: 'arraybuffer',
httpsAgent: InsecureHttpsAgent,
})
.then((res) => {
return Buffer.from(res.data);
});
}
+1 -1
View File
@@ -30,7 +30,7 @@ export function getWAHAVersion(): WAHAVersion {
}
export const VERSION: WAHAEnvironment = {
version: '2025.10.1',
version: '2025.10.4',
engine: getEngineName(),
tier: getWAHAVersion(),
browser:
+2
View File
@@ -4,4 +4,6 @@ http:
allow-insecure: true
no-follow-redirects: true
timeout: 5000
request-headers:
- "X-Api-Key: test"
body: ['version']
+2 -2
View File
@@ -2,11 +2,11 @@
"waha": {
"gows": {
"repo": "devlikeapro/gows",
"ref": "v1.0.17"
"ref": "v1.0.18"
},
"dashboard": {
"repo": "devlikeapro/dashboard",
"ref": "0d3523523e9d0e9d8aece8dec8a38b5fee23caad"
"ref": "8345fb8594a9f8f51fc0a593d5cc1a2e4e786960"
}
}
}
+26 -8
View File
@@ -5,9 +5,9 @@ __metadata:
version: 6
cacheKey: 8
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-10":
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-19":
version: 7.0.0-rc.5
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=9fea6b355c861fb016aad2d6930ecb880f9c63f4"
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=df07435e41a5b6062c51a4a74393558cecf8fc50"
dependencies:
"@cacheable/node-cache": ^1.4.0
"@hapi/boom": ^9.1.3
@@ -31,7 +31,7 @@ __metadata:
optional: true
link-preview-js:
optional: true
checksum: dfce2cbd4706f9b61effb9e3cf6b5c8dc4315d5cd6c71622f7d3ed9813bb37e4836feb0a7f80c6a7c6a5f90abe0220d1f71e3fa9a8066ca31e608431496671a9
checksum: 065a0a3e086e307ef805321355914c97282d279e7cbe2350118562373c0d36aaf877b0789d7e339f45f9c24e31a425f699b2f905411500522d0dc95a31c8986a
languageName: node
linkType: hard
@@ -8536,13 +8536,23 @@ __metadata:
languageName: node
linkType: hard
"libsignal@github:devlikeapro/libsignal-node#fork-master":
"libsignal@git+https://github.com/whiskeysockets/libsignal-node":
version: 2.0.1
resolution: "libsignal@https://github.com/devlikeapro/libsignal-node.git#commit=83bc9236ee3d96ae1747cf3ca2e505b10cf97250"
resolution: "libsignal@https://github.com/whiskeysockets/libsignal-node.git#commit=e81ecfc32eb74951d789ab37f7e341ab66d5fff1"
dependencies:
curve25519-js: ^0.0.4
protobufjs: 6.8.8
checksum: 3ee39bc7cbd123fa2c1f84ff53ab4e3dacfb5a00d10944960fd72156b24a5eb5af1fa5ca31b9f25b26aab107c712d8c295c85b0bed26ad6eba6d0e3f26f38927
checksum: bf7a7b93000c826958d3ba6de72545675d39579a7befff56e3db561d985267e402fdf485127f6f4b1c12855a99966867f28517123fbb4b882d7ef39e83057c3d
languageName: node
linkType: hard
"libsignal@github:WhiskeySockets/libsignal-node#master":
version: 2.0.1
resolution: "libsignal@https://github.com/WhiskeySockets/libsignal-node.git#commit=e81ecfc32eb74951d789ab37f7e341ab66d5fff1"
dependencies:
curve25519-js: ^0.0.4
protobufjs: 6.8.8
checksum: bf7a7b93000c826958d3ba6de72545675d39579a7befff56e3db561d985267e402fdf485127f6f4b1c12855a99966867f28517123fbb4b882d7ef39e83057c3d
languageName: node
linkType: hard
@@ -12327,6 +12337,13 @@ __metadata:
languageName: node
linkType: hard
"undici@npm:^7.16.0":
version: 7.16.0
resolution: "undici@npm:7.16.0"
checksum: dca60e323e44c2d408e0d1f5eb540632ad4e75631d5b6fcd7791876bab54e0e263a66e5606eaa717d436b2db5988c2f4af912dfa221ce53e3fc7fcb7d224c9cd
languageName: node
linkType: hard
"unicorn-magic@npm:^0.3.0":
version: 0.3.0
resolution: "unicorn-magic@npm:0.3.0"
@@ -12494,7 +12511,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "waha@workspace:."
dependencies:
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-10"
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-19"
"@adiwajshing/keyed-db": ^0.2.4
"@aws-sdk/client-s3": ^3.633.0
"@aws-sdk/s3-request-presigner": ^3.633.0
@@ -12558,7 +12575,7 @@ __metadata:
joi: ^17.13.3
knex: ^3.1.0
libphonenumber-js: ^1.10.36
libsignal: "github:devlikeapro/libsignal-node#fork-master"
libsignal: "github:WhiskeySockets/libsignal-node#master"
link-preview-js: ^3.0.4
lodash: ^4.17.21
long: ^5.3.2
@@ -12593,6 +12610,7 @@ __metadata:
tsconfig-paths: ^4.1.0
typescript: 5.9.2
ulid: ^2.3.0
undici: ^7.16.0
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10"
write-file-atomic: ^6.0.0
yaml: ^2.7.1