Compare commits

..
27 Commits
Author SHA1 Message Date
devlikepro faf3c77508 [core] CI - no unit tests (rust bridge is too big)
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
2026-02-18 17:11:30 +07:00
devlikepro e6fdfb88ca [core] 2026.2.2 2026-02-18 17:11:20 +07:00
devlikepro 99746df626 [core] Dashboard
fix for crypto.randomUUID in apps
2026-02-18 17:11:16 +07:00
devlikepro 43aeeb55e8 [core] GOWS - Update engine
Fix "Edit message with image not working" - fix #1852
2026-02-18 17:11:16 +07:00
devlikepro 4f89f49d44 [core] README.md - build rust whatsapp bridge 2026-02-18 17:11:15 +07:00
devlikepro be583e7e40 [core] Dockerfile - build rust whatsapp bridge 2026-02-18 17:11:14 +07:00
devlikepro e8c8f89733 [core] AGENTS.md 2026-02-18 17:11:13 +07:00
devlikepro 13b6d3ddf7 [core] NOWEB - edit text messages in channels (doesn't work for image/video yet)
fix #1352
2026-02-18 17:11:13 +07:00
devlikepro 5e97fb74b1 [core] NOWEB - edit caption for media message
fix #1352
2026-02-18 17:11:12 +07:00
devlikepro cc47321cad [core] GOWS - add seconds to video messages
fix #1893
2026-02-18 17:11:11 +07:00
devlikepro 607dff8f7a [core] NOWEB - add seconds to video messages
fix #1893
2026-02-18 17:11:11 +07:00
devlikepro 0a74f8dbcd [core] picsum photos 2026-02-18 17:11:10 +07:00
devlikepro 796befcf87 [core] NOWEB - use rust-bridge fork 2026-02-18 17:11:10 +07:00
devlikepro 2c5610dd01 [core] NOWEB - update engine 2026-02-18 17:11:10 +07:00
devlikepro 43d110fa31 [core] WEBJS - update engine
fix "Lid is missing in chat table" - fix #1824

fix "No LID for user" - fix #1881
2026-02-18 17:11:09 +07:00
devlikepro bf52dd4874 [core] Fix linter 2026-02-18 17:11:09 +07:00
devlikepro f8268180bf [core] Use github runners instead of buildjet
RIP buildjet
2026-02-18 17:11:09 +07:00
devlikepro 04cf9bc8c0 [core] NOWEB - fix send message to group community - "TypeError: jid.endsWith is not a function" - fix #1901 2026-02-18 17:11:09 +07:00
devlikepro ff38cf7660 [core] init-waha 2026-02-18 17:11:09 +07:00
devlikepro 4c0b44c4b5 [core] NOWEB - fix WhatsApp template with an image
fix #1886
2026-02-18 17:11:08 +07:00
Dani LipariandDani Lipari e75c29d57f [core] fix: strip escaped newlines from ChatWoot v4.10.1+ messages (#1900)
ChatWoot v4.10.1 escapes newlines by prepending a trailing backslash
before each line break in webhook payloads. This caused literal
backslash characters to appear in WhatsApp messages.

Strip the trailing backslash before newlines in MarkdownToWhatsApp()
so line breaks are preserved correctly.

Closes #1833
Mentions chatwoot/chatwoot#13348

Co-authored-by: Dani Lipari <dani.lipari@dontouch.ch>
2026-02-16 14:37:32 +07:00
Dani LipariandDani Lipari f8329c29b1 [core] fix: harden auth middleware, WebSocket guard, and policies guard (#1899)
- Add missing return after socket.close() in WebSocket gateway to
  prevent cross-session event subscription on forbidden connections
- Return 401 Unauthorized instead of 500 Internal Server Error when
  API key header is missing or malformed
- Deny by default in PoliciesGuard when no @CheckPolicies handlers
  are defined, preventing accidental exposure of undecorated endpoints

Co-authored-by: Dani Lipari <dani.lipari@dontouch.ch>
2026-02-16 14:34:50 +07:00
devlikepro 52222a6b98 [core] 2026.2.1
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
2026-02-02 14:10:58 +07:00
devlikepro 99a0a19d8c [core] NOWEB - fix send image using proxy
fix #1859
2026-02-02 14:10:58 +07:00
devlikepro 48ed33bc94 [core] Up WEBJS
Fix GET groups internal error
 - fix #1860

Fix Send message to channel - fix #1863

Fix contact update - fix #1839

[WEBJS] - Get groups count and Get groups failed fix #1879 fix #1873
2026-02-02 14:10:57 +07:00
devlikepro b234e134bf [core] GOWS - exclude all AppState events 2026-02-02 14:10:57 +07:00
devlikepro f6004fd294 [core] openapi - POST /api/{session}/presence chatId param
fix #1842
2026-02-02 14:10:57 +07:00
27 changed files with 525 additions and 94 deletions

No files matched your search

+8 -8
View File
@@ -15,7 +15,7 @@ jobs:
matrix:
include:
# Chromium - x86
- runner: 'buildjet-4vcpu-ubuntu-2204'
- runner: 'ubuntu-latest'
tag: 'latest'
platform: 'amd64'
browser: 'chromium'
@@ -23,7 +23,7 @@ jobs:
goss: 'goss-linux-amd64'
# Chromium - ARM
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
- runner: 'ubuntu-24.04-arm'
tag: 'arm'
platform: 'linux/arm64'
browser: 'chromium'
@@ -31,7 +31,7 @@ jobs:
goss: 'goss-linux-arm'
# Chrome - x86
- runner: 'buildjet-4vcpu-ubuntu-2204'
- runner: 'ubuntu-latest'
tag: 'chrome'
platform: 'amd64'
browser: 'chrome'
@@ -39,14 +39,14 @@ jobs:
goss: 'goss-linux-amd64'
# Chrome - ARM (Chrome is not available for ARM)
#- runner: "buildjet-4vcpu-ubuntu-2204-arm"
#- runner: "ubuntu-24.04-arm"
# tag: "chrome-arm"
# platform: "linux/arm64"
# browser: "chrome"
# goss: 'goss-linux-arm'
# NOWEB - x86
- runner: 'buildjet-4vcpu-ubuntu-2204'
- runner: 'ubuntu-latest'
tag: 'noweb'
platform: 'amd64'
browser: 'none'
@@ -54,7 +54,7 @@ jobs:
goss: 'goss-linux-amd64'
# NOWEB - ARM
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
- runner: 'ubuntu-24.04-arm'
tag: 'noweb-arm'
platform: 'linux/arm64'
browser: 'none'
@@ -62,7 +62,7 @@ jobs:
goss: 'goss-linux-arm'
# GOWS - x86
- runner: 'buildjet-4vcpu-ubuntu-2204'
- runner: 'ubuntu-latest'
tag: 'gows'
platform: 'amd64'
browser: 'none'
@@ -70,7 +70,7 @@ jobs:
goss: 'goss-linux-amd64'
# No browser - ARM
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
- runner: 'ubuntu-24.04-arm'
tag: 'gows-arm'
platform: 'linux/arm64'
browser: 'none'
+31 -31
View File
@@ -6,37 +6,37 @@ on:
workflow_dispatch:
jobs:
unit-tests:
if: github.repository == 'devlikeapro/waha-plus'
runs-on: ubuntu-22.04
name: Unit tests
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
- name: Enable Corepack
run: |
corepack enable
corepack prepare yarn@4.9.2 --activate
- name: Install dependencies
run: |
unset GIT_CONFIG_PARAMETERS
yarn install --immutable
- name: Run unit tests
run: yarn test:unit
# unit-tests:
# if: github.repository == 'devlikeapro/waha-plus'
# runs-on: ubuntu-22.04
# name: Unit tests
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
#
# - name: Set up Node
# uses: actions/setup-node@v4
# with:
# node-version: 22
#
# - name: Enable Corepack
# run: |
# corepack enable
# corepack prepare yarn@4.9.2 --activate
#
# - name: Install dependencies
# run: |
# unset GIT_CONFIG_PARAMETERS
# yarn install --immutable
#
# - name: Run unit tests
# run: yarn test:unit
docker-build:
if: github.repository == 'devlikeapro/waha-plus'
needs: unit-tests
# needs: unit-tests
runs-on: ${{ matrix.runner }}
name:
${{ matrix.engine }} - ${{ matrix.browser }} - ${{ matrix.platform }} -
@@ -45,7 +45,7 @@ jobs:
matrix:
include:
# Chromium - x86
- runner: 'buildjet-4vcpu-ubuntu-2204'
- runner: 'ubuntu-latest'
tag: 'dev'
platform: 'amd64'
browser: 'chromium'
@@ -53,7 +53,7 @@ jobs:
goss: 'goss-linux-amd64'
# Chrome - x86
- runner: 'buildjet-4vcpu-ubuntu-2204'
- runner: 'ubuntu-latest'
tag: 'dev-chrome'
platform: 'amd64'
browser: 'chrome'
@@ -61,7 +61,7 @@ jobs:
goss: 'goss-linux-amd64'
# Chromium - ARM
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
- runner: 'ubuntu-24.04-arm'
tag: 'dev-arm'
platform: 'linux/arm64'
browser: 'chromium'
+3 -1
View File
@@ -132,7 +132,9 @@ You can find related source code in the following paths:
- WEBJS: `../whatsapp-web.js`
- NOWEB: `../WhiskeySockets-Baileys`
- GOWS: `../gows` + `../whatsmeow`
- whatsapp-rust-bridge - `../whatsapp-rust-bridge`
- GOWS: `../gows`
- whatsmeow - `../whatsmeow`
- ChatWoot: `../chatwoot`
Following this playbook keeps contributions aligned with WAHA’s structure,
+20 -1
View File
@@ -6,9 +6,28 @@ ARG GOLANG_IMAGE_TAG=1.24-bookworm
#
FROM node:${NODE_IMAGE_TAG} AS build
ENV PUPPETEER_SKIP_DOWNLOAD=True
ENV RUST_BUN_VERSION=1.3.9
ENV RUSTUP_TOOLCHAIN=nightly-2026-01-30
ENV RUST_WASM_PACK_VERSION=0.14.0
ENV RUST_BUN_INSTALL=/root/.bun
ENV PATH=${RUST_BUN_INSTALL}/bin:/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# git + build toolchain for git deps
RUN apt-get update && apt-get install -y git python3 build-essential && rm -rf /var/lib/apt/lists/*
RUN apt-get update && \
apt-get install -y --no-install-recommends git python3 build-essential curl ca-certificates unzip && \
rm -rf /var/lib/apt/lists/*
# bun + rust toolchains for whatsapp-rust-bridge prepare scripts
RUN set -eux; \
mkdir -p "${RUST_BUN_INSTALL}"; \
curl -fsSL https://bun.sh/install | bash -s -- bun-v${RUST_BUN_VERSION}; \
curl -fsSL https://sh.rustup.rs | bash -s -- -y --default-toolchain ${RUSTUP_TOOLCHAIN}; \
/root/.cargo/bin/rustup target add wasm32-unknown-unknown; \
/root/.cargo/bin/cargo install wasm-pack --vers ${RUST_WASM_PACK_VERSION} --locked; \
"${RUST_BUN_INSTALL}/bin/bun" --version; \
/root/.cargo/bin/cargo --version; \
/root/.cargo/bin/rustc --version; \
/root/.cargo/bin/wasm-pack --version
# npm packages
WORKDIR /git
+5 -3
View File
@@ -35,13 +35,16 @@ for-swagger:
export WHATSAPP_SWAGGER_CONFIG_ADVANCED=true && export WHATSAPP_SWAGGER_PASSWORD=666 && yarn start
up-noweb:
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-01-25
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-02-11
up-noweb-libsignal:
yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
up-webjs:
yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-12-17
yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2026-02-01
up-rust-bridge:
yarn up -R whatsapp-rust-bridge
start-proxy:
docker run --rm -d --name squid-container -e TZ=UTC -p 3128:3128 ubuntu/squid:5.2-22.04_beta
@@ -57,4 +60,3 @@ gows:
copy-dashboard:
cd ../waha-hub/ui && \
make copy-waha
+12 -1
View File
@@ -147,7 +147,18 @@ curl -d "{\"chatId\": \"${PHONE}@c.us\", \"text\": \"Hello from WhatsApp HTTP AP
## Start the project
1. Clone the repository
2. Make sure you're using node>=22 (check [.nvmrc](/.nvmrc) to get the version)
3. Run the following commands:
3. Install the **whatsapp-rust-bridge prerequisites**:
```bash
# Bun runtime used by whatsapp-rust-bridge prepare scripts
curl -fsSL https://bun.sh/install | bash -s -- bun-v1.3.9
# Rust nightly toolchain used for whatsapp-rust-bridge
curl -fsSL https://sh.rustup.rs | bash -s -- -y --default-toolchain nightly-2026-01-30
rustup target add wasm32-unknown-unknown
cargo install wasm-pack --vers 0.14.0 --locked
```
4. Run the following commands:
```bash
# Install dependencies
yarn install
+4 -3
View File
@@ -29,7 +29,7 @@
"gows:proto": "yarn gows:proto:fetch && yarn gows:proto:build"
},
"dependencies": {
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2026-01-25",
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2026-02-11",
"@adiwajshing/keyed-db": "^0.2.4",
"@aws-sdk/client-s3": "^3.633.0",
"@aws-sdk/s3-request-presigner": "^3.633.0",
@@ -111,7 +111,7 @@
"ulid": "^2.3.0",
"undici": "^7.16.0",
"uniqid": "^5.4.0",
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-12-17",
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2026-02-01",
"write-file-atomic": "^6.0.0",
"yaml": "^2.7.1"
},
@@ -124,7 +124,8 @@
"ws": "^8.18.0",
"puppeteer": "^24.31.0",
"whatwg-url": "13.0.0",
"axios": "^1.9.0"
"axios": "^1.9.0",
"whatsapp-rust-bridge": "github:devlikeapro/whatsapp-rust-bridge.git#fork-master-2026-02-04"
},
"devDependencies": {
"@grpc/grpc-js": "^1.14.1",
+17 -3
View File
@@ -76,17 +76,31 @@ const updatedContent = exampleContent
fs.writeFileSync(targetPath, updatedContent, { encoding: 'utf8' });
const generatedEnvValues = [
`WAHA_API_KEY=${apiKey}`,
`WAHA_API_KEY_PLAIN=${apiKey}`,
'WAHA_DASHBOARD_USERNAME=admin',
`WAHA_DASHBOARD_PASSWORD=${adminPassword}`,
'WHATSAPP_SWAGGER_USERNAME=admin',
`WHATSAPP_SWAGGER_PASSWORD=${adminPassword}`,
];
const lines = [
'Credentials generated.',
'',
'Dashboard and Swagger:',
'Generated env values:',
...generatedEnvValues.map((line) => ` - ${line}`),
'',
'Use these credentials to login in Dashboard or Swagger:',
' - Username: admin',
` - Password: ${adminPassword}`,
'',
'API key: ',
'Use this API key in the x-api-key header:',
` - ${apiKey}`,
'',
'Use it in the Dashboard connection flow: https://waha.devlike.pro/docs/how-to/dashboard/#api-key',
'Read more:',
' - https://waha.devlike.pro/docs/how-to/dashboard/#api-key',
' - https://waha.devlike.pro/docs/how-to/security/',
];
console.log(lines.join('\n'));
+1
View File
@@ -93,6 +93,7 @@ export class WebsocketGatewayCore
if (!ability.can(Action.Use, new SessionName(session))) {
socket.close(WebSocketCloseCode.POLICY_VIOLATION, 'Forbidden');
return;
}
this.logger.debug(`New client connected: ${request.url} - ${socket.id}`);
@@ -15,6 +15,14 @@ describe('MarkdownToWhatsApp', () => {
expect(MarkdownToWhatsApp(input)).toBe(expected);
});
// ChatWoot v4.10.1+ escapes newlines with trailing backslash
it('strips trailing backslash before newlines', () => {
const input = 'Hi \\\ntext message \\\nwith line \\\nbreak';
expect(MarkdownToWhatsApp(input)).toBe(
'Hi \ntext message \nwith line \nbreak',
);
});
// Tests for URL protection
it('preserves URLs with multiple underscores', () => {
const input = 'https://example.com/page__name__test';
@@ -4,6 +4,8 @@ export function MarkdownToWhatsApp(text: string): string {
}
return (
text
// Remove trailing backslash before newlines (ChatWoot v4.10.1+ escaping)
.replace(/\\\n/g, '\n')
// Triple-backtick blocks → WhatsApp monospace blocks (same syntax)
.replace(/```([\s\S]*?)```/g, '```$1```')
// Italic **first**, but only single‐star or single‐underscore
+1 -2
View File
@@ -1,6 +1,5 @@
import {
Injectable,
InternalServerErrorException,
NestMiddleware,
UnauthorizedException,
} from '@nestjs/common';
@@ -16,7 +15,7 @@ export class ApiKeyAuthMiddleware implements NestMiddleware {
const exception =
err instanceof UnauthorizedException
? err
: new InternalServerErrorException();
: new UnauthorizedException();
res.status(exception.getStatus()).json(exception.getResponse());
return;
}
+4
View File
@@ -26,6 +26,10 @@ export class PoliciesGuard implements CanActivate {
context.getClass(),
]) || [];
if (handlers.length === 0) {
throw new ForbiddenException();
}
const req = context.switchToHttp().getRequest();
const user = req.user;
const ability = this.caslAbilityFactory.createForUser(user);
+93
View File
@@ -2996,6 +2996,73 @@ export namespace messages {
return AudioInfo.deserialize(bytes);
}
}
export class VideoInfo extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
duration?: number;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("duration" in data && data.duration != undefined) {
this.duration = data.duration;
}
}
}
get duration() {
return pb_1.Message.getFieldWithDefault(this, 1, 0) as number;
}
set duration(value: number) {
pb_1.Message.setField(this, 1, value);
}
static fromObject(data: {
duration?: number;
}): VideoInfo {
const message = new VideoInfo({});
if (data.duration != null) {
message.duration = data.duration;
}
return message;
}
toObject() {
const data: {
duration?: number;
} = {};
if (this.duration != null) {
data.duration = this.duration;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.duration != 0)
writer.writeFloat(1, this.duration);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): VideoInfo {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new VideoInfo();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.duration = reader.readFloat();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): VideoInfo {
return VideoInfo.deserialize(bytes);
}
}
export class Media extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
@@ -3005,6 +3072,7 @@ export namespace messages {
audio?: AudioInfo;
filename?: string;
contentPath?: string;
video?: VideoInfo;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
@@ -3027,6 +3095,9 @@ export namespace messages {
if ("contentPath" in data && data.contentPath != undefined) {
this.contentPath = data.contentPath;
}
if ("video" in data && data.video != undefined) {
this.video = data.video;
}
}
}
get content() {
@@ -3068,6 +3139,15 @@ export namespace messages {
set contentPath(value: string) {
pb_1.Message.setField(this, 6, value);
}
get video() {
return pb_1.Message.getWrapperField(this, VideoInfo, 7) as VideoInfo;
}
set video(value: VideoInfo) {
pb_1.Message.setWrapperField(this, 7, value);
}
get has_video() {
return pb_1.Message.getField(this, 7) != null;
}
static fromObject(data: {
content?: Uint8Array;
type?: MediaType;
@@ -3075,6 +3155,7 @@ export namespace messages {
audio?: ReturnType<typeof AudioInfo.prototype.toObject>;
filename?: string;
contentPath?: string;
video?: ReturnType<typeof VideoInfo.prototype.toObject>;
}): Media {
const message = new Media({});
if (data.content != null) {
@@ -3095,6 +3176,9 @@ export namespace messages {
if (data.contentPath != null) {
message.contentPath = data.contentPath;
}
if (data.video != null) {
message.video = VideoInfo.fromObject(data.video);
}
return message;
}
toObject() {
@@ -3105,6 +3189,7 @@ export namespace messages {
audio?: ReturnType<typeof AudioInfo.prototype.toObject>;
filename?: string;
contentPath?: string;
video?: ReturnType<typeof VideoInfo.prototype.toObject>;
} = {};
if (this.content != null) {
data.content = this.content;
@@ -3124,6 +3209,9 @@ export namespace messages {
if (this.contentPath != null) {
data.contentPath = this.contentPath;
}
if (this.video != null) {
data.video = this.video.toObject();
}
return data;
}
serialize(): Uint8Array;
@@ -3142,6 +3230,8 @@ export namespace messages {
writer.writeString(5, this.filename);
if (this.contentPath.length)
writer.writeString(6, this.contentPath);
if (this.has_video)
writer.writeMessage(7, this.video, () => this.video.serialize(writer));
if (!w)
return writer.getResultBuffer();
}
@@ -3169,6 +3259,9 @@ export namespace messages {
case 6:
message.contentPath = reader.readString();
break;
case 7:
reader.readMessage(message.video, () => message.video = VideoInfo.deserialize(reader));
break;
default: reader.skipField();
}
}
+204 -1
View File
@@ -120,6 +120,7 @@ goog.exportSymbol('proto.messages.SubscribePresenceRequest', null, global);
goog.exportSymbol('proto.messages.UpdateContactRequest', null, global);
goog.exportSymbol('proto.messages.UpdateParticipantsRequest', null, global);
goog.exportSymbol('proto.messages.UpsertLabelRequest', null, global);
goog.exportSymbol('proto.messages.VideoInfo', null, global);
goog.exportSymbol('proto.messages.vCardContact', null, global);
/**
* Generated by JsPbCodeGenerator.
@@ -772,6 +773,27 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.messages.AudioInfo.displayName = 'proto.messages.AudioInfo';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.messages.VideoInfo = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.messages.VideoInfo, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.messages.VideoInfo.displayName = 'proto.messages.VideoInfo';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -7698,6 +7720,136 @@ proto.messages.AudioInfo.prototype.setWaveform = function(value) {
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.messages.VideoInfo.prototype.toObject = function(opt_includeInstance) {
return proto.messages.VideoInfo.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.messages.VideoInfo} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.messages.VideoInfo.toObject = function(includeInstance, msg) {
var f, obj = {
duration: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.messages.VideoInfo}
*/
proto.messages.VideoInfo.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.messages.VideoInfo;
return proto.messages.VideoInfo.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.messages.VideoInfo} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.messages.VideoInfo}
*/
proto.messages.VideoInfo.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {number} */ (reader.readFloat());
msg.setDuration(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.messages.VideoInfo.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.messages.VideoInfo.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.messages.VideoInfo} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.messages.VideoInfo.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getDuration();
if (f !== 0.0) {
writer.writeFloat(
1,
f
);
}
};
/**
* optional float duration = 1;
* @return {number}
*/
proto.messages.VideoInfo.prototype.getDuration = function() {
return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0));
};
/**
* @param {number} value
* @return {!proto.messages.VideoInfo} returns this
*/
proto.messages.VideoInfo.prototype.setDuration = function(value) {
return jspb.Message.setProto3FloatField(this, 1, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
@@ -7732,7 +7884,8 @@ proto.messages.Media.toObject = function(includeInstance, msg) {
mimetype: jspb.Message.getFieldWithDefault(msg, 3, ""),
audio: (f = msg.getAudio()) && proto.messages.AudioInfo.toObject(includeInstance, f),
filename: jspb.Message.getFieldWithDefault(msg, 5, ""),
contentpath: jspb.Message.getFieldWithDefault(msg, 6, "")
contentpath: jspb.Message.getFieldWithDefault(msg, 6, ""),
video: (f = msg.getVideo()) && proto.messages.VideoInfo.toObject(includeInstance, f)
};
if (includeInstance) {
@@ -7794,6 +7947,11 @@ proto.messages.Media.deserializeBinaryFromReader = function(msg, reader) {
var value = /** @type {string} */ (reader.readString());
msg.setContentpath(value);
break;
case 7:
var value = new proto.messages.VideoInfo;
reader.readMessage(value,proto.messages.VideoInfo.deserializeBinaryFromReader);
msg.setVideo(value);
break;
default:
reader.skipField();
break;
@@ -7866,6 +8024,14 @@ proto.messages.Media.serializeBinaryToWriter = function(message, writer) {
f
);
}
f = message.getVideo();
if (f != null) {
writer.writeMessage(
7,
f,
proto.messages.VideoInfo.serializeBinaryToWriter
);
}
};
@@ -8020,6 +8186,43 @@ proto.messages.Media.prototype.setContentpath = function(value) {
};
/**
* optional VideoInfo video = 7;
* @return {?proto.messages.VideoInfo}
*/
proto.messages.Media.prototype.getVideo = function() {
return /** @type{?proto.messages.VideoInfo} */ (
jspb.Message.getWrapperField(this, proto.messages.VideoInfo, 7));
};
/**
* @param {?proto.messages.VideoInfo|undefined} value
* @return {!proto.messages.Media} returns this
*/
proto.messages.Media.prototype.setVideo = function(value) {
return jspb.Message.setWrapperField(this, 7, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.messages.Media} returns this
*/
proto.messages.Media.prototype.clearVideo = function() {
return this.setVideo(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.messages.Media.prototype.hasVideo = function() {
return jspb.Message.getField(this, 7) != null;
};
+10 -6
View File
@@ -234,9 +234,11 @@ enum WhatsMeowEvent {
CALL_TERMINATE = 'events.CallTerminate',
CALL_OFFER_NOTICE = 'events.CallOfferNotice',
// Other
APP_STATE = 'events.AppState',
HISTORY_SYNC = 'events.HistorySync',
CONTACT = 'events.Contact',
AppState = 'events.AppState',
AppStateSyncComplete = 'AppStateSyncComplete',
AppStateSyncError = 'AppStateSyncError',
HistorySync = 'events.HistorySync',
Contact = 'events.Contact',
}
export interface GowsConfig {
@@ -339,9 +341,11 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
// https://github.com/devlikeapro/waha/issues/1826
// TODO: we need to make it more dynamic
const exclude = [
WhatsMeowEvent.APP_STATE,
WhatsMeowEvent.HISTORY_SYNC,
WhatsMeowEvent.CONTACT,
WhatsMeowEvent.AppState,
WhatsMeowEvent.AppStateSyncComplete,
WhatsMeowEvent.AppStateSyncError,
WhatsMeowEvent.HistorySync,
WhatsMeowEvent.Contact,
];
const request = new messages.StreamEventsRequest({
session: this.session,
+17 -3
View File
@@ -64,10 +64,19 @@ export function ToGroupParticipant(
interface GroupParticipantUpdate {
id: string;
author: string;
participants: string[];
participants: Array<string | NOWEBGroupParticipant>;
action: ParticipantAction;
}
function getParticipantId(
participant: string | NOWEBGroupParticipant,
): string | undefined {
if (typeof participant === 'string') {
return participant;
}
return participant?.id;
}
export function ToGroupV2Participants(
update: GroupParticipantUpdate,
): GroupV2ParticipantsEvent {
@@ -92,7 +101,8 @@ export function ToGroupV2Participants(
break;
}
const participants: GroupParticipant[] = update.participants.map((id) => {
const participants: GroupParticipant[] = update.participants.map((item) => {
const id = getParticipantId(item);
return {
id: toCusFormat(id),
role: role,
@@ -131,7 +141,11 @@ export function ToGroupV2LeaveEvent(
return null;
}
const meId = esm.b.jidNormalizedUser(me.id);
if (!update.participants.includes(meId)) {
const includesMe = update.participants.some((participant) => {
const id = getParticipantId(participant);
return id === meId;
});
if (!includesMe) {
return null;
}
+44 -7
View File
@@ -355,10 +355,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
return {
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,
// Baileys media upload uses Node https.request in Node runtime.
fetchAgent: agents?.fetch as Agent,
auth: state,
printQRInTerminal: false,
browser: browser,
@@ -956,24 +954,63 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
@Activity()
public editMessage(
public async editMessage(
chatId: string,
messageId: string,
request: EditMessageRequest,
) {
const jid = toJID(this.ensureSuffix(chatId));
const key = parseMessageIdSerialized(messageId);
const message = {
const stored = await this.store
?.loadMessage(key.remoteJid, key.id)
.catch(() => null);
const content = extractMessageContent(stored?.message);
let editedMessage = undefined;
if (content?.imageMessage) {
editedMessage = {
imageMessage: {
caption: request.text,
},
};
} else if (content?.videoMessage) {
editedMessage = {
videoMessage: {
caption: request.text,
},
};
} else if (content?.documentMessage) {
editedMessage = {
documentMessage: {
caption: request.text,
},
};
} else if (content?.documentWithCaptionMessage?.message?.documentMessage) {
editedMessage = {
documentWithCaptionMessage: {
message: {
documentMessage: {
caption: request.text,
},
},
},
};
}
let message: any = {
text: request.text,
mentions: request.mentions?.map(toJID),
edit: key,
editedMessage: editedMessage,
linkPreview: this.getLinkPreview(request),
linkPreviewHighQuality: request.linkPreviewHighQuality,
};
const options = {
messageId: this.generateMessageID(),
};
return this.sock.sendMessage(jid, message, options);
if (isJidNewsletter(jid)) {
// Newsletter edits reuse the original message ID
options.messageId = key.id;
}
return await this.sock.sendMessage(jid, message, options);
}
@Activity()
+1 -2
View File
@@ -1,7 +1,6 @@
import { Agent } from 'https';
import type { Dispatcher } from 'undici';
export interface Agents {
socket: Agent;
fetch: Dispatcher;
fetch: Agent;
}
+6 -1
View File
@@ -11,7 +11,12 @@ export function extractMediaContent(
content?.videoMessage ||
content?.audioMessage ||
content?.ptvMessage ||
content?.stickerMessage;
content?.stickerMessage ||
content?.templateMessage?.hydratedTemplate?.imageMessage ||
content?.templateMessage?.hydratedTemplate?.videoMessage ||
content?.templateMessage?.interactiveMessageTemplate?.header
?.imageMessage ||
content?.templateMessage?.interactiveMessageTemplate?.header?.videoMessage;
if (mediaContent) {
return mediaContent;
}
+3 -2
View File
@@ -1,6 +1,5 @@
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';
@@ -72,7 +71,9 @@ export function createAgentProxy(proxyConfig: ProxyConfig): Agents | undefined {
);
const socketAgent = new HttpsProxyAgent(url);
const fetchAgent = new ProxyAgent({ uri: url });
// Baileys media upload uses Node.js https.request in Node runtime,
// so fetchAgent must be a Node https.Agent (ProxyAgent from undici won't work).
const fetchAgent = new HttpsProxyAgent(url);
return { socket: socketAgent, fetch: fetchAgent };
}
+2 -1
View File
@@ -227,7 +227,8 @@ export class LinkPreviewData {
example: {
url:
process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL ||
'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
'https://picsum.photos/1024',
// 'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
},
})
image?: FileType;
+4 -2
View File
@@ -22,7 +22,8 @@ export class RemoteFile {
@ApiProperty({
example:
process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL ||
'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
'https://picsum.photos/1024',
// 'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
})
url: string;
}
@@ -130,7 +131,8 @@ export class FileURL {
@ApiProperty({
example:
process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL ||
'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
'https://picsum.photos/1024',
// 'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
})
url?: string;
}
+5 -4
View File
@@ -1,4 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { WAHAPresenceStatus } from './enums.dto';
@@ -28,9 +28,10 @@ export class WAHAChatPresences {
export class WAHASessionPresence {
presence: WAHAPresenceStatus;
@ApiProperty({
description: 'Chat ID - either group id or contact id',
@ApiPropertyOptional({
description:
'Chat ID - either group id or contact id. Required for chat-related presence statuses; omit for ONLINE/OFFLINE.',
example: '11111111111@c.us',
})
chatId: string;
chatId?: string;
}
+1 -1
View File
@@ -44,7 +44,7 @@ function getPlatform() {
}
export const VERSION: WAHAEnvironment = {
version: '2026.1.5',
version: '2026.2.2',
engine: getEngineName(),
tier: getWAHAVersion(),
browser: getBrowser(),
+2 -2
View File
@@ -2,11 +2,11 @@
"waha": {
"gows": {
"repo": "devlikeapro/gows",
"ref": "v1.0.32"
"ref": "v1.0.33"
},
"dashboard": {
"repo": "devlikeapro/dashboard",
"ref": "0cf1cb04fcfc878aade2b3c4f85f25f10ed0384f"
"ref": "9c3f343aad6a211524a0cebf1ba3dcc3fa094840"
}
}
}
+17 -9
View File
@@ -5,9 +5,9 @@ __metadata:
version: 8
cacheKey: 10
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-01-25":
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-02-11":
version: 7.0.0-rc.9
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=7a9463729776e6fb19ccfb9ce8312515db92b94a"
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=b00582cdfd9cef50a7f1a0e2faa28dbf7baef407"
dependencies:
"@cacheable/node-cache": "npm:^1.4.0"
"@hapi/boom": "npm:^9.1.3"
@@ -18,6 +18,7 @@ __metadata:
p-queue: "npm:^9.0.0"
pino: "npm:^9.6"
protobufjs: "npm:^7.2.4"
whatsapp-rust-bridge: "npm:0.5.2"
ws: "npm:^8.13.0"
peerDependencies:
audio-decode: ^2.1.3
@@ -31,7 +32,7 @@ __metadata:
optional: true
link-preview-js:
optional: true
checksum: 10/69fce0f910203bc1bf4c1c677110e2d887fb5d00e5ea4759da8506afa36606baf70dc19e5396b24997cb204c86b48cda5d88a1e7d333282f7499b52b356c3b00
checksum: 10/7229ea82f4849f3397201ae16ab202f57f9788d61ce80b37cc42b77e01808ad53c470443e4fef44297681c5c6211829095210aeff5fb717fd8c083d9d6c4e22f
languageName: node
linkType: hard
@@ -12593,7 +12594,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "waha@workspace:."
dependencies:
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2026-01-25"
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2026-02-11"
"@adiwajshing/keyed-db": "npm:^0.2.4"
"@aws-sdk/client-s3": "npm:^3.633.0"
"@aws-sdk/s3-request-presigner": "npm:^3.633.0"
@@ -12699,7 +12700,7 @@ __metadata:
ulid: "npm:^2.3.0"
undici: "npm:^7.16.0"
uniqid: "npm:^5.4.0"
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-2025-12-17"
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-2026-02-01"
write-file-atomic: "npm:^6.0.0"
yaml: "npm:^2.7.1"
dependenciesMeta:
@@ -12800,9 +12801,16 @@ __metadata:
languageName: node
linkType: hard
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-12-17":
version: 1.34.3
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=72ae41eda26b73f50232ef73f8daec5a17bf0265"
"whatsapp-rust-bridge@github:devlikeapro/whatsapp-rust-bridge.git#fork-master-2026-02-04":
version: 0.5.2
resolution: "whatsapp-rust-bridge@https://github.com/devlikeapro/whatsapp-rust-bridge.git#commit=9fb931fbb991f5ff01142f72de41c21b39ed3a8f"
checksum: 10/b96e5efaf70f975eb3e51513c65c1f73f45f88946196c8f4c7c5c940a3975b2fb3b696bddc5378798e80e242f21fe7cdf4141a81dd405b85ef44272a28d6e76f
languageName: node
linkType: hard
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2026-02-01":
version: 1.34.6
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=3602e0801d8ba3bd2549068e79536282b3028a0e"
dependencies:
"@pedroslopez/moduleraid": "npm:^5.0.2"
archiver: "npm:^5.3.1"
@@ -12820,7 +12828,7 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: 10/87fa2abf0ac92390ed523d3e750c7cf9974b334e25302b35ed9de32d031dc23b229102db8202d0d2ab7c5aeb9a27e73d9fe2321f4947706af16283338487fec6
checksum: 10/a7a177079c271c27675ca49e76d31f283c5472bcc478cc5e78c4d2ad5a6ed120346fd9a176791d28cc69f42c540d3430eb3809e9d1641f2dc1183e0d82d8df81
languageName: node
linkType: hard