From 79233e09e34831b0ce23223d89b36e49b3024fd9 Mon Sep 17 00:00:00 2001 From: devlikepro Date: Wed, 29 Jul 2026 14:45:00 +0700 Subject: [PATCH] fix(NOWEB): Add WAHA_NOWEB_WA_VERSION / WAHA_NOWEB_WA_VERSION_FORCE - closes #2193 --- src/core/app.module.core.ts | 2 + src/core/config/NowebEngineConfigService.ts | 76 ++++++++++++++++++++ src/core/engines/noweb/session.noweb.core.ts | 12 ++++ src/core/engines/noweb/waversion.test.ts | 44 ++++++++++++ src/core/engines/noweb/waversion.ts | 22 ++++++ src/core/manager.core.ts | 4 ++ 6 files changed, 160 insertions(+) create mode 100644 src/core/config/NowebEngineConfigService.ts create mode 100644 src/core/engines/noweb/waversion.test.ts create mode 100644 src/core/engines/noweb/waversion.ts diff --git a/src/core/app.module.core.ts b/src/core/app.module.core.ts index 2ea5ece9..b6896654 100644 --- a/src/core/app.module.core.ts +++ b/src/core/app.module.core.ts @@ -22,6 +22,7 @@ import { ApiKeyAuthMiddleware } from '@waha/core/auth/api-key-auth.middleware'; import { BasicAuthFunction } from '@waha/core/auth/basicAuth'; import { WebSocketAuth } from '@waha/core/auth/WebSocketAuth'; import { GowsEngineConfigService } from '@waha/core/config/GowsEngineConfigService'; +import { NowebEngineConfigService } from '@waha/core/config/NowebEngineConfigService'; import { WPPEngineConfigService } from '@waha/core/config/WPPEngineConfigService'; import { WebJSEngineConfigService } from '@waha/core/config/WebJSEngineConfigService'; import { MediaLocalStorageModule } from '@waha/core/media/local/media.local.storage.module'; @@ -205,6 +206,7 @@ export const PROVIDERS_BASE: Provider[] = [ WebJSEngineConfigService, WPPEngineConfigService, GowsEngineConfigService, + NowebEngineConfigService, WhatsappConfigService, EngineConfigService, WebsocketGatewayCore, diff --git a/src/core/config/NowebEngineConfigService.ts b/src/core/config/NowebEngineConfigService.ts new file mode 100644 index 00000000..a5ab8e26 --- /dev/null +++ b/src/core/config/NowebEngineConfigService.ts @@ -0,0 +1,76 @@ +import type { WAVersion } from '@adiwajshing/baileys'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { NowebConfig } from '@waha/core/engines/noweb/session.noweb.core'; +import { + isWaVersionHigher, + parseWaVersion, +} from '@waha/core/engines/noweb/waversion'; +import { parseBool } from '@waha/helpers'; +import esm from '@waha/vendor/esm'; +import { InjectPinoLogger, PinoLogger } from 'nestjs-pino'; + +@Injectable() +export class NowebEngineConfigService { + private config: NowebConfig | null = null; + + constructor( + protected configService: ConfigService, + @InjectPinoLogger('NowebEngineConfigService') + protected logger: PinoLogger, + ) {} + + getConfig(): NowebConfig { + if (!this.config) { + const version = this.resolveWaVersion(); + this.config = { waVersion: version }; + } + return this.config; + } + + /** + * Resolves the WhatsApp Web version to use for NOWEB engine. + * Returns undefined when the built-in Baileys version should be used. + */ + private resolveWaVersion(): WAVersion { + const value = this.configService.get('WAHA_NOWEB_WA_VERSION', ''); + const envVersion = parseWaVersion(value); + const version = esm.b.DEFAULT_CONNECTION_CONFIG.version as WAVersion; + const force = parseBool( + this.configService.get('WAHA_NOWEB_WA_VERSION_FORCE', 'false'), + ); + + if (!envVersion && value) { + this.logger.warn( + `WAHA_NOWEB_WA_VERSION='${value}' - invalid format, expected 'x.y.z'.`, + ); + return version; + } + + if (!envVersion && force) { + this.logger.warn( + `WAHA_NOWEB_WA_VERSION_FORCE is set, but WAHA_NOWEB_WA_VERSION is not.`, + ); + return version; + } + + if (!envVersion) { + return version; + } + + if (force && envVersion) { + this.logger.debug( + `Using WAHA_NOWEB_WA_VERSION because of WAHA_NOWEB_WA_VERSION_FORCE.`, + ); + return envVersion; + } + + if (isWaVersionHigher(version, envVersion)) { + this.logger.debug( + `Built-in wa.version is higher than WAHA_NOWEB_WA_VERSION, using built-in version.`, + ); + return version; + } + return envVersion; + } +} diff --git a/src/core/engines/noweb/session.noweb.core.ts b/src/core/engines/noweb/session.noweb.core.ts index f61ca612..9ee500ad 100644 --- a/src/core/engines/noweb/session.noweb.core.ts +++ b/src/core/engines/noweb/session.noweb.core.ts @@ -23,6 +23,7 @@ import makeWASocket, { WAMessageContent, WAMessageKey, WAMessageUpdate, + WAVersion, } from '@adiwajshing/baileys'; import { WACallEvent } from '@adiwajshing/baileys/lib/Types/Call'; import { BaileysEventMap } from '@adiwajshing/baileys/lib/Types/Events'; @@ -245,6 +246,7 @@ import { detectMimetype } from '@waha/utils/files'; import esm from '@waha/vendor/esm'; import axios from 'axios'; import axiosRetry from 'axios-retry'; +import { formatWaVersion } from '@waha/core/engines/noweb/waversion'; // eslint-disable-next-line @typescript-eslint/no-var-requires const promiseRetry = require('promise-retry'); @@ -269,11 +271,16 @@ const PresenceStatuses = { }; const ToEnginePresenceStatus = flipObject(PresenceStatuses); +export interface NowebConfig { + waVersion: WAVersion; +} + export class WhatsappSessionNoWebCore extends WhatsappSession { private START_ATTEMPT_DELAY_SECONDS = 2; private AUTO_RESTART_AFTER_SECONDS = 28 * 60; engine = WAHAEngine.NOWEB; + protected engineConfig: NowebConfig; authFactory = new NowebAuthFactoryCore(); storageFactory = new NowebStorageFactoryCore(); private startDelayedJob: SingleDelayedJobRunner; @@ -391,7 +398,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { if (markOnlineOnConnect == undefined) { markOnlineOnConnect = true; } + const version = this.engineConfig?.waVersion; return { + version: version, agent: agents?.socket, // Baileys media upload uses Node https.request in Node runtime. fetchAgent: agents?.fetch as Agent, @@ -429,6 +438,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { agents, state, ) as SocketConfig; + this.logger.info( + `Connecting using wa.version = ${formatWaVersion(socketConfig.version)}`, + ); const sock = makeWASocket(socketConfig); sock.ev.on('creds.update', saveCreds); return sock; diff --git a/src/core/engines/noweb/waversion.test.ts b/src/core/engines/noweb/waversion.test.ts new file mode 100644 index 00000000..c151cafa --- /dev/null +++ b/src/core/engines/noweb/waversion.test.ts @@ -0,0 +1,44 @@ +import { + formatWaVersion, + isWaVersionHigher, + parseWaVersion, +} from '@waha/core/engines/noweb/waversion'; + +describe('parseWaVersion', () => { + it('parses a valid version', () => { + expect(parseWaVersion('2.3000.1043857760')).toEqual([2, 3000, 1043857760]); + }); + + it('trims whitespace', () => { + expect(parseWaVersion(' 2.3000.1 ')).toEqual([2, 3000, 1]); + }); + + it.each(['', '2.3000', '2.3000.1.2', '2.3000.abc', 'latest'])( + "returns null for invalid value '%s'", + (value) => { + expect(parseWaVersion(value)).toBeNull(); + }, + ); +}); + +describe('formatWaVersion', () => { + it('formats a version', () => { + expect(formatWaVersion([2, 3000, 1043857760])).toBe('2.3000.1043857760'); + }); +}); + +describe('isWaVersionHigher', () => { + it('compares by the last part', () => { + expect(isWaVersionHigher([2, 3000, 2], [2, 3000, 1])).toBe(true); + expect(isWaVersionHigher([2, 3000, 1], [2, 3000, 2])).toBe(false); + }); + + it('compares by the middle part first', () => { + expect(isWaVersionHigher([2, 3001, 1], [2, 3000, 999])).toBe(true); + expect(isWaVersionHigher([2, 3000, 999], [2, 3001, 1])).toBe(false); + }); + + it('returns false for equal versions', () => { + expect(isWaVersionHigher([2, 3000, 1], [2, 3000, 1])).toBe(false); + }); +}); diff --git a/src/core/engines/noweb/waversion.ts b/src/core/engines/noweb/waversion.ts new file mode 100644 index 00000000..7cb44bae --- /dev/null +++ b/src/core/engines/noweb/waversion.ts @@ -0,0 +1,22 @@ +import type { WAVersion } from '@adiwajshing/baileys'; + +export function parseWaVersion(value: string): WAVersion | null { + const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value.trim()); + if (!match) { + return null; + } + return [Number(match[1]), Number(match[2]), Number(match[3])]; +} + +export function formatWaVersion(version: WAVersion): string { + return version.join('.'); +} + +export function isWaVersionHigher(left: WAVersion, right: WAVersion): boolean { + for (let index = 0; index < 3; index++) { + if (left[index] !== right[index]) { + return left[index] > right[index]; + } + } + return false; +} diff --git a/src/core/manager.core.ts b/src/core/manager.core.ts index 5bf27db2..72706996 100644 --- a/src/core/manager.core.ts +++ b/src/core/manager.core.ts @@ -11,6 +11,7 @@ import { } from '@waha/apps/app_sdk/services/IAppsService'; import { EngineBootstrap } from '@waha/core/abc/EngineBootstrap'; import { GowsEngineConfigService } from '@waha/core/config/GowsEngineConfigService'; +import { NowebEngineConfigService } from '@waha/core/config/NowebEngineConfigService'; import { WPPEngineConfigService } from '@waha/core/config/WPPEngineConfigService'; import { WebJSEngineConfigService } from '@waha/core/config/WebJSEngineConfigService'; import { WhatsappSessionGoWSCore } from '@waha/core/engines/gows/session.gows.core'; @@ -96,6 +97,7 @@ export class SessionManagerCore private engineConfigService: EngineConfigService, private webjsEngineConfigService: WebJSEngineConfigService, private wppEngineConfigService: WPPEngineConfigService, + private nowebEngineConfigService: NowebEngineConfigService, gowsConfigService: GowsEngineConfigService, log: PinoLogger, private mediaStorageFactory: MediaStorageFactory, @@ -364,6 +366,8 @@ export class SessionManagerCore sessionConfig.engineConfig = this.wppEngineConfigService.getConfig(); } else if (this.EngineClass === WhatsappSessionGoWSCore) { sessionConfig.engineConfig = this.gowsConfigService.getConfig(); + } else if (this.EngineClass === WhatsappSessionNoWebCore) { + sessionConfig.engineConfig = this.nowebEngineConfigService.getConfig(); } // @ts-ignore const session = new this.EngineClass(sessionConfig);