Compare commits

...
38 Commits
Author SHA1 Message Date
devlikepro 2f0efd0223 [core] 2025.7.4
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
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
2025-07-06 16:48:47 +07:00
devlikepro 5c31ed53df [core] WEBJS - fix error for message.ack
TypeError: You provided 'null' where a stream was expected.
2025-07-06 16:48:47 +07:00
devlikepro 87640ea238 [core] WEBJS - avoid STARTING => FAILED cycles
fix #1071
2025-07-06 16:48:47 +07:00
devlikepro eb8f771b06 [core] WEBJS - protocolTimeout to 300s (5m)
fix #1103
2025-07-06 16:48:46 +07:00
devlikepro b080b5862e [core] NOWEB - no lock on 'lids' updates 2025-07-06 16:48:46 +07:00
devlikepro df50e7527e [core] NOWEB - save @lid to @c.us from the first message
fix #1073
fix #1097
2025-07-06 16:48:46 +07:00
devlikepro e2b43575c0 [core] Up NOWEB - 2025-07-04
fix #531
fix #1098
2025-07-06 16:48:45 +07:00
devlikepro 2059793562 [core] Log update for unknown jid 2025-07-06 16:48:45 +07:00
devlikepro e5df21e165 [core] 2025.7.3
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-07-03 17:13:06 +07:00
devlikepro 792029b13f [core] Up GOWS
fix #1043
fix #1068
fix #1094
2025-07-03 17:13:06 +07:00
devlikepro 0ef49d7116 [core] GOWS - fallback to storage in case of JSON errors
2 UNKNOWN: json: cannot unmarshal object into Go struct field InteractiveAnnotation.imageMessage.annotations. Action of type waE2
E.isInteractiveAnnotation_Action
2025-07-03 17:13:06 +07:00
devlikepro 600d3291eb [core] add dns in waha by default 2025-07-03 17:13:06 +07:00
devlikepro b78413190c [core] WEBJS - disable GPU 2025-07-03 17:13:06 +07:00
devlikepro c9729af139 [core] 2025.7.2
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 / WEBJS - chrome - amd64 - chrome (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-07-02 14:47:05 +07:00
devlikepro 697f267fc3 [core] Add - in prefix 2025-07-02 14:47:05 +07:00
devlikepro ac49ce3cc5 [core] trace 2025-07-02 14:47:05 +07:00
devlikepro 555daf6888 [core] fix heap API 2025-07-02 14:47:05 +07:00
devlikepro 8f0c2994a6 [core] One build-all 2025-07-02 14:47:05 +07:00
devlikepro 873e66db19 [core] Add browser tracing debug API 2025-07-02 14:45:52 +07:00
devlikepro 38b542def4 [core] Add TmpDir (from PLUS) 2025-07-02 14:45:51 +07:00
devlikepro a646e5efc0 [core] remove healthcheck 2025-07-02 14:45:50 +07:00
devlikepro 31590c9229 [core] Add server.debug.controller.ts 2025-07-02 14:45:50 +07:00
devlikepro 98c2172908 [core] WEBJS - onTag handler
related #1092
2025-07-02 14:45:49 +07:00
devlikepro 314b02daf1 [core] WEBJS add waha arguments to chrome to identify it better 2025-07-02 14:45:49 +07:00
devlikepro d2ab78b65c [core] WEBJS Up user agent 2025-07-02 14:45:49 +07:00
devlikepro db2c352fd1 [core] WEBJS --renderer-process-limit=1 2025-07-02 14:45:49 +07:00
devlikepro 6e79ad2793 [core] WEBJS - rm --single-process at all 2025-07-02 14:45:49 +07:00
devlikepro 77eaa287fe [core] WEBJS - remove disable-background-networking, it's default puppeter flag 2025-07-02 14:45:49 +07:00
devlikepro a64820048c [core] Add health-cmd
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-07-01 13:35:00 +07:00
devlikepro 4ff4a5a893 [core] 2025.7.1 2025-07-01 13:23:38 +07:00
devlikepro 5ea5681d2b [core] Use PORT first
fix #1090
2025-07-01 13:23:38 +07:00
devlikepro 2bb327b747 [core] move HEALTHCHECK from Dockerfile to docker-compose.yaml
fix #1090
2025-07-01 13:23:38 +07:00
devlikepro 4dcdf60309 [core] make build-all 2025-07-01 13:23:37 +07:00
devlikepro 009fd5b2ec [core] NOWEB - share() events 2025-07-01 13:23:37 +07:00
devlikepro 091e7e5bc1 [core] Use this.knex or query methods directly 2025-07-01 13:23:37 +07:00
devlikepro ea1202bb65 [core] Use knex by for sqlite3, not database connection 2025-07-01 13:23:32 +07:00
devlikepro cb413febaa [core] Handle WhatsApp News profile picture 2025-07-01 13:23:32 +07:00
devlikepro 364e55d477 [core] Add KnexEngine (copy paste from psql) 2025-07-01 13:23:32 +07:00
35 changed files with 571 additions and 491 deletions

No files matched your search

-4
View File
@@ -128,10 +128,6 @@ jobs:
run: |
docker run -d \
--name smoke \
--health-interval 1s \
--health-retries 60 \
--health-timeout 2s \
--health-start-period 2s \
--rm -p3000:3000 \
${{ vars.DOCKER_IMAGE }}:${{ matrix.tag }}
-5
View File
@@ -200,11 +200,6 @@ ENV WAHA_GOWS_SOCKET /tmp/gows.sock
COPY entrypoint.sh /entrypoint.sh
# Add healthcheck
COPY ping-check.sh /ping-check.sh
RUN chmod +x /ping-check.sh
HEALTHCHECK --interval=60s --timeout=10s --retries=3 --start-period=30s CMD ["/bin/bash", "/ping-check.sh"]
# Chokidar options to monitor file changes
ENV CHOKIDAR_USEPOLLING=1
ENV CHOKIDAR_INTERVAL=5000
+8 -8
View File
@@ -1,20 +1,20 @@
build-all: build-plus build-chrome build-gows build-noweb
build:
docker build . -t devlikeapro/waha
build-plus:
docker build . -t devlikeapro/waha-plus
build-chrome:
docker build . -t devlikeapro/waha-plus:chrome --build-arg USE_BROWSER=chrome
build-noweb:
docker build . -t devlikeapro/waha:noweb --build-arg USE_BROWSER=none --build-arg WHATSAPP_DEFAULT_ENGINE=NOWEB
docker build . -t devlikeapro/waha-plus:noweb --build-arg USE_BROWSER=none --build-arg WHATSAPP_DEFAULT_ENGINE=NOWEB
build-plus-gows:
build-gows:
docker build . -t devlikeapro/waha-plus:gows --build-arg USE_BROWSER=none --build-arg WHATSAPP_DEFAULT_ENGINE=GOWS
build-all: build build-chrome build-noweb
build-plus:
docker build . -t devlikeapro/waha-plus
build-ssh:
# check IMAGE provided
@[ "${IMAGE}" ] || ( echo "Add APP: make build-ssh image=devlikeapro/waha"; exit 1 );
@@ -35,7 +35,7 @@ for-swagger:
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
up-noweb:
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-05-01
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-07-04
up-noweb-libsignal:
. ${NVM_DIR}/nvm.sh && nvm exec yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
+5
View File
@@ -8,6 +8,11 @@ services:
# WAHA Core
# image: devlikeapro/waha:latest
# Add "dns" if you have a problem with resolving "web.whatsapp.com"
dns:
- 1.1.1.1
- 8.8.8.8
logging:
driver: 'json-file'
options:
+1 -1
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-05-01",
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-07-04",
"@adiwajshing/keyed-db": "^0.2.4",
"@aws-sdk/client-s3": "^3.633.0",
"@aws-sdk/s3-request-presigner": "^3.633.0",
-14
View File
@@ -1,14 +0,0 @@
#!/bin/bash
# Default port is 3000
PORT=${WHATSAPP_API_PORT:-3000}
# Check if HTTPS is enabled
if [[ "${WAHA_HTTPS_ENABLED}" == "true" || "${WAHA_HTTPS_ENABLED}" == "1" ]]; then
PROTOCOL="https"
else
PROTOCOL="http"
fi
# Execute curl command with SSL verification disabled
curl -f -s -k "${PROTOCOL}://127.0.0.1:${PORT}/ping" || exit 1
-34
View File
@@ -1,15 +1,12 @@
import * as process from 'node:process';
import * as v8 from 'node:v8';
import {
Body,
Controller,
Get,
Logger,
NotFoundException,
Post,
Query,
StreamableFile,
UsePipes,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
@@ -112,34 +109,3 @@ export class ServerController {
return { stopping: true };
}
}
@ApiSecurity('api_key')
@Controller('api/server/debug')
@ApiTags('🔍 Observability')
export class ServerDebugController {
private logger: Logger;
private readonly enabled: boolean;
constructor(protected config: WhatsappConfigService) {
this.logger = new Logger('ServerDebugController');
this.enabled = this.config.debugModeEnabled;
}
@Get('heapsnapshot')
@ApiOperation({
summary: 'Return a heapsnapshot',
description: "Return a heapsnapshot of the server's memory",
})
async heapsnapshot() {
if (!this.enabled) {
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
}
this.logger.log('Creating a heap snapshot...');
const heap = v8.getHeapSnapshot();
const fileName = `${Date.now()}.heapsnapshot`;
return new StreamableFile(heap, {
type: 'application/octet-stream',
disposition: `attachment; filename=${fileName}`,
});
}
}
+79
View File
@@ -0,0 +1,79 @@
import * as v8 from 'node:v8';
import {
Controller,
Get,
Logger,
NotFoundException,
Query,
StreamableFile,
UsePipes,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { WhatsappConfigService } from '@waha/config.service';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { WhatsappSession } from '@waha/core/abc/session.abc';
import {
SessionApiParam,
WorkingSessionParam,
} from '@waha/nestjs/params/SessionApiParam';
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
import { createReadStream } from 'fs';
@ApiSecurity('api_key')
@Controller('api/server/debug')
@ApiTags('🔍 Observability')
export class ServerDebugController {
private logger: Logger;
private readonly enabled: boolean;
constructor(
private config: WhatsappConfigService,
private manager: SessionManager,
) {
this.logger = new Logger('ServerDebugController');
this.enabled = this.config.debugModeEnabled;
}
@Get('heapsnapshot')
@ApiOperation({
summary: 'Return a heapsnapshot for the current nodejs process',
description: "Return a heapsnapshot of the server's memory",
})
async heapsnapshot() {
if (!this.enabled) {
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
}
this.logger.log('Creating a heap snapshot...');
const heap = v8.getHeapSnapshot();
const fileName = `${Date.now()}.heapsnapshot`;
return new StreamableFile(heap, {
type: 'application/octet-stream',
disposition: `attachment; filename=${fileName}`,
});
}
@Get('browser/trace/:session')
@ApiOperation({
summary: 'Collect and get a trace.json for Chrome DevTools ',
description: 'Uses https://pptr.dev/api/puppeteer.tracing',
})
@SessionApiParam
@UsePipes(new WAHAValidationPipe())
async browserTrace(
@WorkingSessionParam session: WhatsappSession,
@Query() query: BrowserTraceQuery,
) {
if (!this.enabled) {
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
}
const filepath = await session.browserTrace(query);
const stream = createReadStream(filepath);
const filename = `trace - ${session.name} - ${new Date()}.json`;
return new StreamableFile(stream, {
type: 'application/octet-stream',
disposition: `attachment; filename=${filename}`,
});
}
}
+17 -7
View File
@@ -30,6 +30,7 @@ import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
import { LidToPhoneNumber } from '@waha/structures/lids.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { MessageSource, WAMessage } from '@waha/structures/responses.dto';
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
import { DefaultMap } from '@waha/utils/DefaultMap';
import { generatePrefixedId } from '@waha/utils/ids';
import { LoggerBuilder } from '@waha/utils/logging';
@@ -285,7 +286,6 @@ export abstract class WhatsappSession {
'--aggressive-cache-discard',
'--disable-accelerated-2d-canvas',
'--disable-application-cache',
// '--disable-background-networking', // COMMENTED to test WEBJS stability
// DO NOT disable software rasterizer, it will break the video
// https://github.com/devlikeapro/waha/issues/629
// '--disable-software-rasterizer',
@@ -296,7 +296,7 @@ export abstract class WhatsappSession {
'--disable-dev-shm-usage',
'--disable-extensions',
// '--disable-features=site-per-process', // COMMENTED to test WEBJS stability
//'--disable-gpu', // COMMENTED to test WEBJS stability
'--disable-gpu', // COMMENTED to test WEBJS stability
'--disable-offer-store-unmasked-wallet-cards',
'--disable-offline-load-stale-cache',
'--disable-popup-blocking',
@@ -320,9 +320,8 @@ export abstract class WhatsappSession {
'--no-sandbox',
'--no-zygote',
'--password-store=basic',
// '--renderer-process-limit=2', // COMMENTED to test WEBJS stability
'--renderer-process-limit=1',
'--safebrowsing-disable-auto-update',
// '--single-process',
'--use-mock-keychain',
'--window-size=1280,720',
];
@@ -351,6 +350,10 @@ export abstract class WhatsappSession {
* START - Methods for API
*/
public browserTrace(query: BrowserTraceQuery): Promise<string> {
throw new NotImplementedByEngineError();
}
/**
* Auth methods
*/
@@ -682,10 +685,17 @@ export abstract class WhatsappSession {
}
protected async refreshProfilePicture(id: string) {
let fn: Promise<string>;
if (isJidBroadcast(id)) {
this.logger.debug(`Refreshing profile picture for id "${id}"...`);
// Have no pictures
if (id === '0@c.us' || id === '0@s.whatsapp.net') {
return null;
} else if (isJidNewsletter(id)) {
} else if (isJidBroadcast(id)) {
return null;
}
// Find the right method
let fn: Promise<string>;
if (isJidNewsletter(id)) {
fn = this.channelsGetChannel(id).then(
(channel: Channel) => channel.picture || channel.preview,
);
+2 -4
View File
@@ -10,10 +10,8 @@ import { TerminusModule } from '@nestjs/terminus';
import { ChannelsController } from '@waha/api/channels.controller';
import { LidsController } from '@waha/api/lids.controller';
import { ProfileController } from '@waha/api/profile.controller';
import {
ServerController,
ServerDebugController,
} from '@waha/api/server.controller';
import { ServerController } from '@waha/api/server.controller';
import { ServerDebugController } from '@waha/api/server.debug.controller';
import { WebsocketGatewayCore } from '@waha/api/websocket.gateway.core';
import { ApiKeyStrategy } from '@waha/core/auth/apiKey.strategy';
import { IApiKeyAuth } from '@waha/core/auth/auth';
+46
View File
@@ -7031,6 +7031,8 @@ export namespace messages {
constructor(data?: any[] | {
session?: Session;
message?: string;
jid?: string;
messageId?: string;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
@@ -7041,6 +7043,12 @@ export namespace messages {
if ("message" in data && data.message != undefined) {
this.message = data.message;
}
if ("jid" in data && data.jid != undefined) {
this.jid = data.jid;
}
if ("messageId" in data && data.messageId != undefined) {
this.messageId = data.messageId;
}
}
}
get session() {
@@ -7058,9 +7066,23 @@ export namespace messages {
set message(value: string) {
pb_1.Message.setField(this, 2, value);
}
get jid() {
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
}
set jid(value: string) {
pb_1.Message.setField(this, 3, value);
}
get messageId() {
return pb_1.Message.getFieldWithDefault(this, 4, "") as string;
}
set messageId(value: string) {
pb_1.Message.setField(this, 4, value);
}
static fromObject(data: {
session?: ReturnType<typeof Session.prototype.toObject>;
message?: string;
jid?: string;
messageId?: string;
}): DownloadMediaRequest {
const message = new DownloadMediaRequest({});
if (data.session != null) {
@@ -7069,12 +7091,20 @@ export namespace messages {
if (data.message != null) {
message.message = data.message;
}
if (data.jid != null) {
message.jid = data.jid;
}
if (data.messageId != null) {
message.messageId = data.messageId;
}
return message;
}
toObject() {
const data: {
session?: ReturnType<typeof Session.prototype.toObject>;
message?: string;
jid?: string;
messageId?: string;
} = {};
if (this.session != null) {
data.session = this.session.toObject();
@@ -7082,6 +7112,12 @@ export namespace messages {
if (this.message != null) {
data.message = this.message;
}
if (this.jid != null) {
data.jid = this.jid;
}
if (this.messageId != null) {
data.messageId = this.messageId;
}
return data;
}
serialize(): Uint8Array;
@@ -7092,6 +7128,10 @@ export namespace messages {
writer.writeMessage(1, this.session, () => this.session.serialize(writer));
if (this.message.length)
writer.writeString(2, this.message);
if (this.jid.length)
writer.writeString(3, this.jid);
if (this.messageId.length)
writer.writeString(4, this.messageId);
if (!w)
return writer.getResultBuffer();
}
@@ -7107,6 +7147,12 @@ export namespace messages {
case 2:
message.message = reader.readString();
break;
case 3:
message.jid = reader.readString();
break;
case 4:
message.messageId = reader.readString();
break;
default: reader.skipField();
}
}
+61 -1
View File
@@ -14889,7 +14889,9 @@ proto.messages.DownloadMediaRequest.prototype.toObject = function(opt_includeIns
proto.messages.DownloadMediaRequest.toObject = function(includeInstance, msg) {
var f, obj = {
session: (f = msg.getSession()) && proto.messages.Session.toObject(includeInstance, f),
message: jspb.Message.getFieldWithDefault(msg, 2, "")
message: jspb.Message.getFieldWithDefault(msg, 2, ""),
jid: jspb.Message.getFieldWithDefault(msg, 3, ""),
messageid: jspb.Message.getFieldWithDefault(msg, 4, "")
};
if (includeInstance) {
@@ -14935,6 +14937,14 @@ proto.messages.DownloadMediaRequest.deserializeBinaryFromReader = function(msg,
var value = /** @type {string} */ (reader.readString());
msg.setMessage(value);
break;
case 3:
var value = /** @type {string} */ (reader.readString());
msg.setJid(value);
break;
case 4:
var value = /** @type {string} */ (reader.readString());
msg.setMessageid(value);
break;
default:
reader.skipField();
break;
@@ -14979,6 +14989,20 @@ proto.messages.DownloadMediaRequest.serializeBinaryToWriter = function(message,
f
);
}
f = message.getJid();
if (f.length > 0) {
writer.writeString(
3,
f
);
}
f = message.getMessageid();
if (f.length > 0) {
writer.writeString(
4,
f
);
}
};
@@ -15037,6 +15061,42 @@ proto.messages.DownloadMediaRequest.prototype.setMessage = function(value) {
};
/**
* optional string jid = 3;
* @return {string}
*/
proto.messages.DownloadMediaRequest.prototype.getJid = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
};
/**
* @param {string} value
* @return {!proto.messages.DownloadMediaRequest} returns this
*/
proto.messages.DownloadMediaRequest.prototype.setJid = function(value) {
return jspb.Message.setProto3StringField(this, 3, value);
};
/**
* optional string messageId = 4;
* @return {string}
*/
proto.messages.DownloadMediaRequest.prototype.getMessageid = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
};
/**
* @param {string} value
* @return {!proto.messages.DownloadMediaRequest} returns this
*/
proto.messages.DownloadMediaRequest.prototype.setMessageid = function(value) {
return jspb.Message.setProto3StringField(this, 4, value);
};
@@ -2152,6 +2152,8 @@ export class GOWSEngineMediaProcessor implements IMediaEngineProcessor<any> {
// double "session" it's not a mistake here
session: this.session.session,
message: data,
jid: message.Info.Chat,
messageId: message.Info.ID,
});
const response = await promisify(this.session.client.DownloadMedia)(
request,
@@ -0,0 +1,47 @@
export interface NOWEBNewsletterMetadata {
id: string;
state: string;
creation_time: number;
name: string;
nameTime: number;
description: string;
descriptionTime: number;
invite: string;
handle: string;
picture: string | null;
preview: string | null;
reaction_codes: string | null;
subscribers: number;
verification: string;
viewer_metadata: any;
}
export function toNewsletterMetadata(
data: any,
): NOWEBNewsletterMetadata | null {
if (data.state?.type === 'DELETED') {
return null;
}
if (data.state?.type === 'NON_EXISTING') {
return null;
}
return {
id: data.id,
state: data.state?.type,
creation_time: +data.thread_metadata.creation_time,
name: data.thread_metadata.name.text,
nameTime: +data.thread_metadata.name.update_time,
description: data.thread_metadata.description.text,
descriptionTime: +data.thread_metadata.description.update_time,
invite: data.thread_metadata.invite,
handle: data.thread_metadata.handle,
picture: data.thread_metadata.picture?.direct_path || null,
preview: data.thread_metadata.preview?.direct_path || null,
reaction_codes: data.thread_metadata?.settings?.reaction_codes?.value,
subscribers: +data.thread_metadata.subscribers_count,
verification: data.thread_metadata.verification,
viewer_metadata: data.viewer_metadata,
};
}
+39 -28
View File
@@ -56,6 +56,10 @@ import {
ToGroupV2UpdateEvent,
} from '@waha/core/engines/noweb/groups.noweb';
import { sendButtonMessage } from '@waha/core/engines/noweb/noweb.buttons';
import {
NOWEBNewsletterMetadata,
toNewsletterMetadata,
} from '@waha/core/engines/noweb/noweb.newsletter';
import { NowebAuthFactoryCore } from '@waha/core/engines/noweb/NowebAuthFactoryCore';
import { NowebInMemoryStore } from '@waha/core/engines/noweb/store/NowebInMemoryStore';
import {
@@ -477,7 +481,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return;
} else if (connection === 'close') {
this.qr.save('');
const statusCode = lastDisconnect.error?.output?.statusCode;
const error = lastDisconnect.error as any;
const statusCode = error?.output?.statusCode;
// Restart required from the server
const restartRequired = statusCode === DisconnectReason.restartRequired;
@@ -752,7 +757,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
if (this.status == WAHASessionStatus.STARTING) {
this.logger.debug('Waiting for connection update...');
await this.sock.waitForConnectionUpdate((update) => !!update.qr);
await this.sock.waitForConnectionUpdate(async (update) => !!update.qr);
}
if (this.status != WAHASessionStatus.SCAN_QR_CODE) {
@@ -1711,9 +1716,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
throw new AvailableInPlusVersion();
}
protected toChannel(newsletter: NewsletterMetadata): Channel {
protected toChannel(newsletter: NOWEBNewsletterMetadata): Channel {
const role =
// @ts-ignore
newsletter.viewer_metadata?.role ||
(newsletter.viewer_metadata?.view_role as ChannelRole) ||
ChannelRole.GUEST;
@@ -1738,7 +1742,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
public async channelsList(query: ListChannelsQuery): Promise<Channel[]> {
const newsletters = await this.sock.newsletterSubscribed();
let channels = newsletters.map(this.toChannel);
let channels = newsletters.map(toNewsletterMetadata).map(this.toChannel);
if (query.role) {
// @ts-ignore
channels = channels.filter((channel) => channel.role === query.role);
@@ -1751,37 +1755,37 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
request.name,
request.description,
);
return this.toChannel(newsletter);
return this.toChannel(toNewsletterMetadata(newsletter));
}
public async channelsGetChannel(id: string) {
const newsletter = await this.sock.newsletterMetadata('jid', id);
return this.toChannel(newsletter);
return this.toChannel(toNewsletterMetadata(newsletter));
}
public async channelsGetChannelByInviteCode(inviteCode: string) {
const newsletter = await this.sock.newsletterMetadata('invite', inviteCode);
return this.toChannel(newsletter);
return this.toChannel(toNewsletterMetadata(newsletter));
}
public async channelsDeleteChannel(id: string) {
return await this.sock.newsletterDelete(id);
}
public async channelsFollowChannel(id: string): Promise<void> {
return await this.sock.newsletterAction(id, 'follow');
public async channelsFollowChannel(id: string): Promise<any> {
return await this.sock.newsletterFollow(id);
}
public async channelsUnfollowChannel(id: string): Promise<void> {
return await this.sock.newsletterAction(id, 'unfollow');
public async channelsUnfollowChannel(id: string): Promise<any> {
return await this.sock.newsletterUnfollow(id);
}
public async channelsMuteChannel(id: string): Promise<void> {
return await this.sock.newsletterAction(id, 'mute');
public async channelsMuteChannel(id: string): Promise<any> {
return await this.sock.newsletterMute(id);
}
public async channelsUnmuteChannel(id: string): Promise<void> {
return await this.sock.newsletterAction(id, 'unmute');
public async channelsUnmuteChannel(id: string): Promise<any> {
return await this.sock.newsletterUnmute(id);
}
subscribeEngineEvents2() {
@@ -1805,6 +1809,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
const messagesUpsert$ = fromEvent(this.sock.ev, 'messages.upsert').pipe(
map((event: BaileysEventMap['messages.upsert']) => event.messages),
mergeAll(),
share(),
);
let [messagesFromMe$, messagesFromOthers$] = partition(
messagesUpsert$,
@@ -1888,6 +1893,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
).pipe(
// @ts-ignore
mergeAll(),
share(),
);
const messageAckDirect$ = messageUpdates$.pipe(
filter(isMine), // ack comes only for MY messages
@@ -1898,6 +1904,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
fromEvent(this.sock.ev, 'message-receipt.update').pipe(
// @ts-ignore
mergeAll(),
share(),
);
const messageAckGroups$ = messageReceiptUpdate$.pipe(
@@ -1914,7 +1921,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
//
this.events2
.get(WAHAEvents.STATE_CHANGE)
.switch(fromEvent(this.sock.ev, 'connection.update'));
.switch(fromEvent(this.sock.ev, 'connection.update').pipe(share()));
const groupsUpsert$: Observable<GroupMetadata> = fromEvent(
this.sock.ev,
@@ -1922,6 +1929,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
).pipe(
// @ts-ignore
mergeAll(),
share(),
);
const groupsUpdate$: Observable<Partial<GroupMetadata>> = fromEvent(
this.sock.ev,
@@ -1929,11 +1937,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
).pipe(
// @ts-ignore
mergeAll(),
share(),
);
const groupsParticipantsUpdate$: Observable<any> = fromEvent(
this.sock.ev,
'group-participants.update',
);
).pipe(share());
this.events2.get(WAHAEvents.GROUP_JOIN).switch(groupsUpsert$);
@@ -1960,13 +1969,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
);
this.events2.get(WAHAEvents.GROUP_V2_LEAVE).switch(groupV2Leave$);
this.events2
.get(WAHAEvents.PRESENCE_UPDATE)
.switch(
fromEvent(this.sock.ev, 'presence.update').pipe(
map((data: any) => this.toWahaPresences(data.id, data.presences)),
),
);
this.events2.get(WAHAEvents.PRESENCE_UPDATE).switch(
fromEvent(this.sock.ev, 'presence.update').pipe(
map((data: any) => this.toWahaPresences(data.id, data.presences)),
share(),
),
);
//
// Poll votes
@@ -1993,7 +2001,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
//
// @ts-ignore
const calls$: Observable<WACallEvent[]> = fromEvent(this.sock.ev, 'call');
const call$ = calls$.pipe(mergeMap(identity));
const call$ = calls$.pipe(mergeMap(identity), share());
this.events2.get(WAHAEvents.CALL_RECEIVED).switch(
call$.pipe(
filter((call: WACallEvent) => call.status === 'offer'),
@@ -2026,7 +2034,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
const labelsEdit$: Observable<NOWEBLabel> = fromEvent(
this.sock.ev,
'labels.edit',
);
).pipe(share());
this.events2.get(WAHAEvents.LABEL_UPSERT).switch(
labelsEdit$.pipe(
exclude((data: NOWEBLabel) => data.deleted),
@@ -2039,7 +2047,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
map(this.toLabel.bind(this)),
),
);
const labelsAssociation$ = fromEvent(this.sock.ev, 'labels.association');
const labelsAssociation$ = fromEvent(
this.sock.ev,
'labels.association',
).pipe(share());
const labelsAssociationAdd$: Observable<ChatLabelAssociation> =
labelsAssociation$.pipe(
filter(({ type }: any) => type === 'add'),
@@ -12,6 +12,7 @@ import makeWASocket, {
proto,
updateMessageWithReaction,
updateMessageWithReceipt,
WAMessage,
} from '@adiwajshing/baileys';
import { GroupMetadata } from '@adiwajshing/baileys/lib/Types/GroupMetadata';
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
@@ -64,7 +65,6 @@ export class NowebPersistentStore implements INowebStore {
public presences: any;
private lock: any = new AsyncLock({
timeout: 5_000,
maxPending: Infinity,
maxExecutionTime: 60_000,
});
@@ -102,9 +102,43 @@ export class NowebPersistentStore implements INowebStore {
// All
ev.on('messaging-history.set', (data) => this.onMessagingHistorySet(data));
// Messages
ev.on('messages.upsert', (data) =>
this.withLock('messages', () => this.onMessagesUpsert(data)),
);
ev.on('messages.upsert', (data) => {
this.withLock('messages', () => this.onMessagesUpsert(data));
this.withNoLock('lids', async () => {
const messages: WAMessage[] = data.messages;
if (!messages) {
return;
}
const contacts: Partial<Contact>[] = messages
.map((message) => {
if (!message.key) {
return null;
}
// Phone
let pn = message.key.senderPn || message.key.participantPn;
// 123 => 123@s.whatsapp.net
if (pn && !pn.includes('@')) {
pn = `${pn}@s.whatsapp.net`;
}
// 123@c.us => 123@s.whatsapp.net
if (pn && !isJidUser(pn)) {
pn = jidNormalizedUser(pn);
}
// 999@lid
const lid = message.key.senderLid || message.key.participantLid;
return {
id: message.key.remoteJid,
lid: lid,
jid: pn,
};
})
.filter(Boolean);
const lids = await this.handleLidPNUpdates(contacts);
this.logger.debug(
`messages.upsert - '${lids.length}' synced lid to pn mapping`,
);
});
});
ev.on('messages.update', (data) =>
this.withLock('messages', () => this.onMessageUpdate(data)),
);
@@ -133,7 +167,7 @@ export class NowebPersistentStore implements INowebStore {
);
ev.on('groups.update', (data) => {
this.withLock('groups', () => this.onGroupUpdate(data));
this.withLock('lids', async () => {
this.withNoLock('lids', async () => {
const participants = lodash.flatMap(data, (g) => g?.participants || []);
const lids = await this.handleLidPNUpdates(participants);
this.logger.debug(
@@ -150,7 +184,7 @@ export class NowebPersistentStore implements INowebStore {
// Contacts
ev.on('contacts.upsert', (data) => {
this.withLock('contacts', () => this.onContactsUpsert(data));
this.withLock('lids', async () => {
this.withNoLock('lids', async () => {
const lids = await this.handleLidPNUpdates(data);
this.logger.debug(
`contacts.upsert - '${lids.length}' synced lid to pn mapping`,
@@ -159,7 +193,7 @@ export class NowebPersistentStore implements INowebStore {
});
ev.on('contacts.update', (data) => {
this.withLock('contacts', () => this.onContactUpdate(data));
this.withLock('lids', async () => {
this.withNoLock('lids', async () => {
const lids = await this.handleLidPNUpdates(data);
this.logger.debug(
`contacts.update - '${lids.length}' synced lid to pn mapping`,
@@ -200,7 +234,7 @@ export class NowebPersistentStore implements INowebStore {
await this.onContactsUpsert(contacts);
this.logger.info(`history sync - '${contacts.length}' synced contacts`);
}),
this.withLock('lids', async () => {
this.withNoLock('lids', async () => {
const lids = await this.handleLidPNUpdates(contacts);
this.logger.info(
`history sync - '${lids.length}' synced lid to pn mapping`,
@@ -239,6 +273,14 @@ export class NowebPersistentStore implements INowebStore {
if (!update.key.id) {
continue;
}
if (!jid) {
this.logger.warn(
`got message update for unknown jid. update: '${JSON.stringify(
update,
)}'`,
);
continue;
}
const message = await this.messagesRepo.getByJidById(jid, update.key.id);
if (!message) {
this.logger.warn(
@@ -396,6 +438,10 @@ export class NowebPersistentStore implements INowebStore {
return this.lock.acquire(key, fn);
}
private withNoLock(key, fn) {
return fn();
}
private async onContactsUpsert(contacts: Contact[]) {
const upserts = [];
const ids = contacts.map((c) => c.id);
@@ -625,16 +671,16 @@ export class NowebPersistentStore implements INowebStore {
});
}
// contact.pn = pn, contact.lid = lid
else if (isJidUser(contact.pn) && isLidUser(contact.lid)) {
else if (isJidUser(contact.jid) && isLidUser(contact.lid)) {
lids.push({
pn: contact.pn,
pn: contact.jid,
id: contact.lid,
});
}
// contact.pn = pn, contact.id = lid
else if (isJidUser(contact.pn) && isLidUser(contact.id)) {
else if (isJidUser(contact.jid) && isLidUser(contact.id)) {
lids.push({
pn: contact.pn,
pn: contact.jid,
id: contact.id,
});
}
@@ -287,7 +287,7 @@ export default (config: BaileysInMemoryStoreConfig) => {
ev.emit('chats.upsert', [
{
id: jid,
conversationTimestamp: toNumber(msg.messageTimestamp),
conversationTimestamp: toNumber(msg.messageTimestamp as any),
unreadCount: 1,
},
]);
@@ -1,20 +1,18 @@
import { Schema } from '@waha/core/storage/Schema';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Database = require('better-sqlite3');
import Knex from 'knex';
export class Sqlite3SchemaValidation {
constructor(
private table: Schema,
private db,
private knex: Knex.Knex,
) {}
validate() {
async validate() {
const table = this.table;
// Check table has the columns
const columns = this.db.prepare(`PRAGMA table_info(${table.name})`).all();
// Check exact number of columns
const columns = await this.knex.raw(`PRAGMA table_info(${table.name})`);
// Check the exact number of columns
if (columns.length !== table.columns.length) {
throw new Error(
`Table '${table.name}' does not have expected number of columns. Expected ${table.columns.length}, got ${columns.length}`,
@@ -37,7 +35,7 @@ export class Sqlite3SchemaValidation {
}
// Check table has expected indexes
const indexes = this.db.prepare(`PRAGMA index_list(${table.name})`).all();
const indexes = await this.knex.raw(`PRAGMA index_list(${table.name})`);
const indexNames = indexes.map((index) => index.name);
for (const index of table.indexes) {
if (!indexNames.includes(index.name)) {
@@ -6,6 +6,7 @@ import { Sqlite3LabelAssociationsRepository } from '@waha/core/engines/noweb/sto
import { Sqlite3LabelsRepository } from '@waha/core/engines/noweb/store/sqlite3/Sqlite3LabelsRepository';
import { Sqlite3LidPNRepository } from '@waha/core/engines/noweb/store/sqlite3/Sqlite3LidPNRepository';
import { Schema } from '@waha/core/storage/Schema';
import Knex from 'knex';
import { INowebStorage } from '../INowebStorage';
import { Migrations, NOWEB_STORE_SCHEMA } from '../schemas';
@@ -14,70 +15,71 @@ import { Sqlite3ContactRepository } from './Sqlite3ContactRepository';
import { Sqlite3MessagesRepository } from './Sqlite3MessagesRepository';
import { Sqlite3SchemaValidation } from './Sqlite3SchemaValidation';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Database = require('better-sqlite3');
export class Sqlite3Storage extends INowebStorage {
private readonly db: any;
private readonly tables: Schema[];
private readonly knex: Knex.Knex;
constructor(filePath: string) {
super();
this.db = new Database(filePath);
this.knex = Knex({
client: 'better-sqlite3',
connection: { filename: filePath },
useNullAsDefault: true,
});
this.tables = NOWEB_STORE_SCHEMA;
}
async init() {
this.db.pragma('journal_mode = WAL;');
this.migrate();
this.validateSchema();
await this.knex.raw('PRAGMA journal_mode = WAL;');
await this.migrate();
await this.validateSchema();
}
private migrate() {
this.migration0001init();
private async migrate() {
await this.migration0001init();
}
private validateSchema() {
private async validateSchema() {
for (const table of this.tables) {
new Sqlite3SchemaValidation(table, this.db).validate();
await new Sqlite3SchemaValidation(table, this.knex).validate();
}
}
private migration0001init() {
private async migration0001init() {
for (const migration of Migrations) {
this.db.exec(migration);
await this.knex.raw(migration);
}
}
async close() {
return this.db.close();
return this.knex.destroy();
}
getContactsRepository() {
return new Sqlite3ContactRepository(this.db);
return new Sqlite3ContactRepository(this.knex);
}
getChatRepository() {
return new Sqlite3ChatRepository(this.db);
return new Sqlite3ChatRepository(this.knex);
}
getGroupRepository() {
return new Sqlite3GroupRepository(this.db);
return new Sqlite3GroupRepository(this.knex);
}
getLabelsRepository(): ILabelsRepository {
return new Sqlite3LabelsRepository(this.db);
return new Sqlite3LabelsRepository(this.knex);
}
getLabelAssociationRepository(): ILabelAssociationRepository {
return new Sqlite3LabelAssociationsRepository(this.db);
return new Sqlite3LabelAssociationsRepository(this.knex);
}
getMessagesRepository() {
return new Sqlite3MessagesRepository(this.db);
return new Sqlite3MessagesRepository(this.knex);
}
getLidPNRepository(): INowebLidPNRepository {
return new Sqlite3LidPNRepository(this.db);
return new Sqlite3LidPNRepository(this.knex);
}
}
+4
View File
@@ -119,4 +119,8 @@ export class WPage extends EventEmitter {
this.page.on(event, listener);
return this.page as any;
}
get tracing() {
return this.page.tracing;
}
}
+41 -4
View File
@@ -106,6 +106,7 @@ import {
WAHAPresenceData,
} from '@waha/structures/presence.dto';
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
import { MeInfo } from '@waha/structures/sessions.dto';
import { StatusRequest, TextStatus } from '@waha/structures/status.dto';
import {
@@ -117,7 +118,9 @@ import {
import { PaginatorInMemory } from '@waha/utils/Paginator';
import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import { TmpDir } from '@waha/utils/tmpdir';
import * as lodash from 'lodash';
import * as path from 'path';
import { ProtocolError } from 'puppeteer';
import { filter, fromEvent, merge, mergeMap, Observable, share } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -194,13 +197,20 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
if (cacheType === 'local') {
this.logger.info(`Using web version: '${webVersion}'`);
}
const args = this.getBrowserArgsForPuppeteer();
// add at the start
args.unshift(`--a-waha-timestamp=${new Date()}`);
args.unshift(`--a-waha-session=${this.name}`);
return {
puppeteer: {
protocolTimeout: 300_000,
headless: true,
executablePath: this.getBrowserExecutablePath(),
args: this.getBrowserArgsForPuppeteer(),
args: args,
dumpio: this.isDebugEnabled(),
},
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
webVersion: webVersion,
webVersionCache: {
type: cacheType,
@@ -228,6 +238,9 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
this.logger.debug(
'Should not restart the client, ignoring restart request',
);
this.end().catch((error) => {
this.logger.error(error, 'Failed to end() the client');
});
return;
}
@@ -459,15 +472,20 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
});
this.whatsapp.on(Events.AUTHENTICATION_FAILURE, (args) => {
this.failed();
this.qr.save('');
this.shouldRestart = false;
this.logger.info({ args: args }, `Session has failed to authenticate!`);
this.failed();
});
this.whatsapp.on(Events.DISCONNECTED, (args) => {
this.failed();
if (args === 'LOGOUT') {
this.logger.warn({ args: args }, `Session has been logged out!`);
this.shouldRestart = false;
}
this.qr.save('');
this.logger.info({ args: args }, `Session has been disconnected!`);
this.failed();
});
this.whatsapp.on(Events.STATE_CHANGED, (state: WAState) => {
@@ -498,7 +516,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
});
log.setBindings({ currentState: currentState });
if (!currentState) {
log.warn('Session has no current state, skip restarting.');
log.warn('Session has no current state, restarting...');
this.restartClient();
return;
} else if (badStates.includes(currentState)) {
log.info('Session is still in bad state, restarting...');
@@ -513,6 +532,23 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
/**
* START - Methods for API
*/
public async browserTrace(query: BrowserTraceQuery): Promise<string> {
const tmpdir = new TmpDir(
this.logger,
`waha-browser-trace-${this.name}-`,
(10 * query.seconds + 120) * 1000,
);
const page = this.whatsapp.pupPage;
return await tmpdir.use(async (dir) => {
this.logger.info({ query }, `Starting browser tracing...`);
const filepath = path.join(dir, 'trace.json');
await page.tracing.start({ path: filepath });
await sleep(query.seconds * 1000);
await page.tracing.stop();
this.logger.info(`Browser tracing finished, saved to ${filepath}`);
return filepath;
});
}
/**
* Auth methods
@@ -1473,6 +1509,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
mergeMap((node) =>
TagReceiptNodeToReceiptEvent(node as any, this.getSessionMeInfo()),
),
filter(Boolean),
mergeMap(this.TagReceiptToMessageAck.bind(this)),
filter((ack) => isJidGroup(ack.to) || isJidStatusBroadcast(ack.to)),
);
+3 -1
View File
@@ -1,3 +1,5 @@
import Knex from 'knex';
import { DataStore } from '../abc/DataStore';
export abstract class LocalStore extends DataStore {
@@ -23,5 +25,5 @@ export abstract class LocalStore extends DataStore {
*/
abstract getFilePath(session: string, file: string): string;
abstract getWAHADatabase(): any;
abstract getWAHADatabase(): Knex.Knex;
}
+22 -12
View File
@@ -1,18 +1,16 @@
import { safeJoin } from '@waha/utils/files';
import * as fs from 'fs/promises';
import Knex from 'knex';
import * as path from 'path';
import { LocalStore } from './LocalStore';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Database = require('better-sqlite3');
export class LocalStoreCore extends LocalStore {
protected readonly baseDirectory: string =
process.env.WAHA_LOCAL_STORE_BASE_DIR || './.sessions';
private readonly engine: string;
private db: any;
private knex: Knex.Knex;
constructor(engine: string) {
super();
@@ -21,6 +19,11 @@ export class LocalStoreCore extends LocalStore {
async init(sessionName?: string) {
await fs.mkdir(this.getEngineDirectory(), { recursive: true });
if (!this.knex) {
this.knex = this.buildKnex();
await this.knex.raw('PRAGMA journal_mode = WAL;');
await this.knex.raw('PRAGMA foreign_keys = ON;');
}
if (sessionName) {
await fs.mkdir(this.getSessionDirectory(sessionName), {
recursive: true,
@@ -54,17 +57,24 @@ export class LocalStoreCore extends LocalStore {
return safeJoin(this.getEngineDirectory(), name);
}
getWAHADatabase(): any {
if (!this.db) {
const engineDir = this.getEngineDirectory();
const database = safeJoin(engineDir, 'waha.sqlite3');
this.db = new Database(database);
this.db.pragma('journal_mode = WAL;');
getWAHADatabase(): Knex.Knex {
if (!this.knex) {
throw new Error('Knex is not initialized, call LocalStore.init() first');
}
return this.db;
return this.knex;
}
buildKnex(): Knex.Knex {
const engineDir = this.getEngineDirectory();
const database = path.join(engineDir, 'waha.sqlite3');
return Knex({
client: 'better-sqlite3',
connection: { filename: database },
useNullAsDefault: true,
});
}
async close() {
this.db?.close();
await this.knex.destroy();
}
}
-28
View File
@@ -1,28 +0,0 @@
import Knex from 'knex';
export interface ISQLEngine {
/**
* Run a query that does not return any data
*/
run(query: Knex.QueryBuilder): Promise<void>;
/**
* Get a single row from the database
*/
get(query: Knex.QueryBuilder): Promise<any | null>;
/**
* Get all rows from the database
*/
all(query: Knex.QueryBuilder): Promise<any[]>;
/**
* Run a raw SQL query with bindings
*/
raw(sql: string, bindings: any[]): Promise<void>;
/**
* Execute a raw SQL query
*/
exec(sql: string): Promise<void>;
}
+9 -13
View File
@@ -1,9 +1,8 @@
import { Field, Schema } from '@waha/core/storage/Schema';
import { IJsonQuery } from '@waha/core/storage/sql/IJsonQuery';
import { ISQLEngine } from '@waha/core/storage/sql/ISQLEngine';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { KnexPaginator } from '@waha/utils/Paginator';
import Knex from 'knex';
import { Knex } from 'knex';
import * as lodash from 'lodash';
export type Migration = string;
@@ -26,10 +25,7 @@ export class SqlKVRepository<Entity> {
return [];
}
constructor(
private engine: ISQLEngine,
protected knex: Knex.Knex,
) {}
constructor(protected knex: Knex) {}
get columns(): Field[] {
return this.schema.columns;
@@ -49,7 +45,7 @@ export class SqlKVRepository<Entity> {
protected async applyMigrations() {
for (const migration of this.migrations) {
await this.engine.exec(migration);
await this.knex.raw(migration);
}
}
@@ -119,7 +115,7 @@ export class SqlKVRepository<Entity> {
async getCount(): Promise<number> {
const query = this.select().count({ count: 'id' });
const row = await this.engine.get(query);
const row = await query.first();
if (!row) {
return 0;
}
@@ -138,7 +134,7 @@ export class SqlKVRepository<Entity> {
return new Map();
}
const rows = await this.engine.all(this.select().whereIn('id', ids));
const rows = await this.select().whereIn('id', ids);
const entitiesMap = new Map<string, Entity | null>();
// Initialize a map with null values for all requested IDs
@@ -191,15 +187,15 @@ export class SqlKVRepository<Entity> {
* SQL Implementation details
*/
public async raw(sql: string, bindings: any[]): Promise<void> {
await this.engine.raw(sql, bindings);
await this.knex.raw(sql, bindings);
}
protected async run(query: Knex.QueryBuilder): Promise<void> {
await this.engine.run(query);
await query;
}
protected async get(query: Knex.QueryBuilder): Promise<Entity | null> {
const row = await this.engine.get(query);
const row = await query.first();
if (!row) {
return null;
}
@@ -207,7 +203,7 @@ export class SqlKVRepository<Entity> {
}
public async all(query: Knex.QueryBuilder): Promise<Entity[]> {
const rows = await this.engine.all(query);
const rows = await query;
return rows.map((row) => this.parse(row));
}
-38
View File
@@ -1,38 +0,0 @@
import { ISQLEngine } from '@waha/core/storage/sql/ISQLEngine';
import { Database } from 'better-sqlite3';
import { Knex } from 'knex';
export class Sqlite3Engine implements ISQLEngine {
constructor(protected db: Database) {}
async all(query: Knex.QueryBuilder): Promise<any[]> {
const sql = query.toSQL().sql;
const bind = query.toSQL().bindings;
return this.db.prepare(sql).all(bind);
}
async get(query: Knex.QueryBuilder): Promise<any | null> {
const sql = query.toSQL().sql;
const bind = query.toSQL().bindings;
const row: any = this.db.prepare(sql).get(bind);
if (!row) {
return null;
}
return row;
}
async run(query: Knex.QueryBuilder): Promise<void> {
const sql = query.toSQL().sql;
const bind = query.toSQL().bindings;
this.db.prepare(sql).run(bind);
return;
}
async raw(sql: string, bindings: any[]) {
this.db.prepare(sql).run(bindings);
}
async exec(sql): Promise<void> {
this.db.exec(sql);
}
}
@@ -1,23 +1,17 @@
import { SqlKVRepository } from '@waha/core/storage/sql/SqlKVRepository';
import { Sqlite3Engine } from '@waha/core/storage/sqlite3/Sqlite3Engine';
import { Sqlite3JsonQuery } from '@waha/core/storage/sqlite3/Sqlite3JsonQuery';
import { sleep } from '@waha/utils/promiseTimeout';
import { Database } from 'better-sqlite3';
import Knex from 'knex';
/**
* Key value repository with extra metadata
*/
export class Sqlite3KVRepository<Entity> extends SqlKVRepository<Entity> {
protected db: Database;
protected knex: Knex.Knex;
protected jsonQuery = new Sqlite3JsonQuery();
constructor(db: Database) {
// sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide this warning. (see docs https://knexjs.org/guide/query-builder.html#insert).
const knex = Knex({ client: 'better-sqlite3', useNullAsDefault: true });
const engine = new Sqlite3Engine(db);
super(engine, knex);
this.db = db;
constructor(knex: Knex.Knex) {
super(knex);
}
protected async upsertBatch(entities: Entity[]): Promise<void> {
@@ -23,8 +23,8 @@ export class Sqlite3SessionMeRepository
}
constructor(store: LocalStore) {
const db = store.getWAHADatabase();
super(db);
const knex = store.getWAHADatabase();
super(knex);
}
upsertMe(sessionName: string, me: MeInfo): Promise<void> {
@@ -41,7 +41,7 @@ export class Sqlite3SessionMeRepository
}
protected async validateSchema() {
const validation = new Sqlite3SchemaValidation(this.schema, this.db);
validation.validate();
const validation = new Sqlite3SchemaValidation(this.schema, this.knex);
await validation.validate();
}
}
@@ -23,8 +23,8 @@ export class Sqlite3SessionWorkerRepository
}
constructor(store: LocalStore) {
const db = store.getWAHADatabase();
super(db);
const knex = store.getWAHADatabase();
super(knex);
}
assign(session: string, worker: string): Promise<void> {
@@ -45,7 +45,7 @@ export class Sqlite3SessionWorkerRepository
}
protected async validateSchema() {
const validation = new Sqlite3SchemaValidation(this.schema, this.db);
validation.validate();
const validation = new Sqlite3SchemaValidation(this.schema, this.knex);
await validation.validate();
}
}
+25
View File
@@ -0,0 +1,25 @@
import { ApiProperty } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator';
export class BrowserTraceQuery {
@Type(() => Number)
@IsNumber()
@ApiProperty({
description: 'How many seconds to trace',
example: 30,
required: true,
})
seconds: number = 30;
@IsArray()
@IsString({ each: true })
@Transform(({ value }) => (Array.isArray(value) ? value : [value]))
@IsOptional()
@ApiProperty({
description: 'Categories to trace (all by default)',
example: ['*'],
required: true,
})
categories: string[] = ['*'];
}
+47
View File
@@ -0,0 +1,47 @@
import * as fs from 'fs/promises';
import * as os from 'os';
import * as path from 'path';
import { Logger } from 'pino';
function sanitize(input: string) {
return input.replace(/[^a-zA-Z0-9-_]/g, '_');
}
export class TmpDir {
private readonly prefix: string;
constructor(
private logger: Logger,
prefix: string,
private cleanupTimeout = 10_000,
) {
this.prefix = sanitize(prefix);
if (this.prefix.length > 249) {
this.prefix = this.prefix.slice(0, 249);
}
}
/**
* Creates a temporary directory and passes it to the callback.
* The directory is automatically cleaned up after the callback returns.
* @param callback Function that receives the temporary directory path
* @returns The result of the callback
*/
public async use<T>(callback: (dir: string) => Promise<T>): Promise<T> {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), this.prefix));
try {
return await callback(dir);
} finally {
setTimeout(async () => {
fs.rm(dir, { recursive: true, force: true })
.then(() => {
this.logger.trace(`Cleaned up temporary directory '${dir}'`);
})
.catch((err) => {
this.logger.warn(
`Failed to clean up temporary directory '${dir}': ${err.message}`,
);
});
}, this.cleanupTimeout);
}
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ export function getEngineName(): string {
}
export const VERSION: WAHAEnvironment = {
version: '2025.6.7',
version: '2025.7.4',
engine: getEngineName(),
tier: getWAHAVersion(),
browser:
+1 -9
View File
@@ -4,12 +4,4 @@ http:
allow-insecure: true
no-follow-redirects: true
timeout: 5000
body: ['version']
command:
docker smoke health:
exec: "docker inspect --format '{{`{{if .State.Health}}{{.State.Health.Status}}{{else}}no healthcheck{{end}}`}}' smoke"
exit-status: 0
stdout:
- 'healthy'
timeout: 5000
body: ['version']
+1 -1
View File
@@ -2,7 +2,7 @@
"waha": {
"gows": {
"repo": "devlikeapro/gows",
"ref": "v1.0.8"
"ref": "v1.0.9"
},
"dashboard": {
"repo": "devlikeapro/dashboard",
+10 -218
View File
@@ -5,13 +5,12 @@ __metadata:
version: 6
cacheKey: 8
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-05-01":
version: 6.7.16
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=17db13bd44d54e00d5740b002aca0b33c15b6e7b"
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-07-04":
version: 6.7.18
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=599231e9f18be6ebe6e8eb48a5c57ec9f587ee5e"
dependencies:
"@cacheable/node-cache": ^1.4.0
"@hapi/boom": ^9.1.3
"@whiskeysockets/eslint-config": "github:whiskeysockets/eslint-config"
async-mutex: ^0.5.0
axios: ^1.6.0
libsignal: "github:WhiskeySockets/libsignal-node"
@@ -34,7 +33,7 @@ __metadata:
optional: true
sharp:
optional: true
checksum: f89150418538cc3d11ed94da91ff5ba89a49b9a29c1d8a060fd8a211c3ab2e9cdf84c2f8bd3962e67835decc4fb78783dd8f1d297b8caf6d4b026dfa3bd42126
checksum: 4a0d0221ea30efbbe50dd3c639c45bed677da6688d550787b7c4fa2b3a1bb3992e3d3ebb920be2429113996c02e87a0801f94e6d6609f4ceaf8637b7490691ee
languageName: node
linkType: hard
@@ -1217,24 +1216,6 @@ __metadata:
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.7.0":
version: 4.7.0
resolution: "@eslint-community/eslint-utils@npm:4.7.0"
dependencies:
eslint-visitor-keys: ^3.4.3
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
checksum: b177e3b75c0b8d0e5d71f1c532edb7e40b31313db61f0c879f9bf19c3abb2783c6c372b5deb2396dab4432f2946b9972122ac682e77010376c029dfd0149c681
languageName: node
linkType: hard
"@eslint-community/regexpp@npm:^4.10.0":
version: 4.12.1
resolution: "@eslint-community/regexpp@npm:4.12.1"
checksum: 0d628680e204bc316d545b4993d3658427ca404ae646ce541fcc65306b8c712c340e5e573e30fb9f85f4855c0c5f6dca9868931f2fcced06417fbe1a0c6cd2d6
languageName: node
linkType: hard
"@grpc/grpc-js@npm:^1.13.4":
version: 1.13.4
resolution: "@grpc/grpc-js@npm:1.13.4"
@@ -3800,27 +3781,6 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^8.32.0":
version: 8.34.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.34.0"
dependencies:
"@eslint-community/regexpp": ^4.10.0
"@typescript-eslint/scope-manager": 8.34.0
"@typescript-eslint/type-utils": 8.34.0
"@typescript-eslint/utils": 8.34.0
"@typescript-eslint/visitor-keys": 8.34.0
graphemer: ^1.4.0
ignore: ^7.0.0
natural-compare: ^1.4.0
ts-api-utils: ^2.1.0
peerDependencies:
"@typescript-eslint/parser": ^8.34.0
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: f5d4a57d0661bea3b15b8842abeae3327828647540e8127c882d44cbf793e84d4c9e33b6740f682891ddf20ca664a634b7016289e1bc98ae21f02808498a28ec
languageName: node
linkType: hard
"@typescript-eslint/experimental-utils@npm:3.9.1":
version: 3.9.1
resolution: "@typescript-eslint/experimental-utils@npm:3.9.1"
@@ -3854,69 +3814,6 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^8.32.0":
version: 8.34.0
resolution: "@typescript-eslint/parser@npm:8.34.0"
dependencies:
"@typescript-eslint/scope-manager": 8.34.0
"@typescript-eslint/types": 8.34.0
"@typescript-eslint/typescript-estree": 8.34.0
"@typescript-eslint/visitor-keys": 8.34.0
debug: ^4.3.4
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: 7b6797c4d87e8b2e24f99bd2e9c4102366b8f77f4a2912f810df01d7b655524304859e0adef12c21dbbe986bacc7e45d35845d8c4439193350c38d8a14bb7ef7
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/project-service@npm:8.34.0"
dependencies:
"@typescript-eslint/tsconfig-utils": ^8.34.0
"@typescript-eslint/types": ^8.34.0
debug: ^4.3.4
peerDependencies:
typescript: ">=4.8.4 <5.9.0"
checksum: 194440e5f350c284faa760548d16b389c6d285262d5528c6fb83c6adaf7765c9d4c57e5dc9e48e10941c8833942dae0d543438bf25083ccf2a83208bbf293d38
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/scope-manager@npm:8.34.0"
dependencies:
"@typescript-eslint/types": 8.34.0
"@typescript-eslint/visitor-keys": 8.34.0
checksum: 074464b1dc7efc7311eb54a74c1b592c1693a20abc46b6ed73f2d91377e83f85aa06780699208205b129b098b83f2c9a7c38c9f016617dd336cced3716fa0991
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.34.0, @typescript-eslint/tsconfig-utils@npm:^8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.0"
peerDependencies:
typescript: ">=4.8.4 <5.9.0"
checksum: cbbca9526bd9c0309c77f9436f68c2c06712779a593a17757f1f7558ece27d9f40db2b37ebf12bd9e19cf227479083b7973c502436a0954a08406d8a598910ba
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/type-utils@npm:8.34.0"
dependencies:
"@typescript-eslint/typescript-estree": 8.34.0
"@typescript-eslint/utils": 8.34.0
debug: ^4.3.4
ts-api-utils: ^2.1.0
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: 97046c06edc6d15363f9a1e08ace2f774def8b903b42bf32db6e7f944f0f308273583b0ddd86c013e3f945bc15862d11625ff0d63578cc0b6f94a881f7337cef
languageName: node
linkType: hard
"@typescript-eslint/types@npm:3.9.1":
version: 3.9.1
resolution: "@typescript-eslint/types@npm:3.9.1"
@@ -3924,13 +3821,6 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.34.0, @typescript-eslint/types@npm:^8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/types@npm:8.34.0"
checksum: 08e5ff523df38d6db3abc41314825fe7b079340385e7b0618567a53e17da20062b71a7d7e8d5d62283d97296c58fdd3d52f126878d82042df18792c2c7f275ae
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:3.9.1":
version: 3.9.1
resolution: "@typescript-eslint/typescript-estree@npm:3.9.1"
@@ -3950,41 +3840,6 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/typescript-estree@npm:8.34.0"
dependencies:
"@typescript-eslint/project-service": 8.34.0
"@typescript-eslint/tsconfig-utils": 8.34.0
"@typescript-eslint/types": 8.34.0
"@typescript-eslint/visitor-keys": 8.34.0
debug: ^4.3.4
fast-glob: ^3.3.2
is-glob: ^4.0.3
minimatch: ^9.0.4
semver: ^7.6.0
ts-api-utils: ^2.1.0
peerDependencies:
typescript: ">=4.8.4 <5.9.0"
checksum: b752314f2ff05ddb39c7831d208aa1b2a77ea6829f656ee8e385f90cd44555590831b6d733d3d8ff7a37460e1feeeaf9fd2341f337b92bda2f07f594dc53766e
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/utils@npm:8.34.0"
dependencies:
"@eslint-community/eslint-utils": ^4.7.0
"@typescript-eslint/scope-manager": 8.34.0
"@typescript-eslint/types": 8.34.0
"@typescript-eslint/typescript-estree": 8.34.0
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: ad55f2ec0a901da1f44064fdad06f4c3eabc2e42f26e6017e3b594a513d928f1fa5d58043b336f97019350f6e81c1fa508585b7877ec5f6a357f8c5d634d5cba
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:3.9.1":
version: 3.9.1
resolution: "@typescript-eslint/visitor-keys@npm:3.9.1"
@@ -3994,16 +3849,6 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.34.0":
version: 8.34.0
resolution: "@typescript-eslint/visitor-keys@npm:8.34.0"
dependencies:
"@typescript-eslint/types": 8.34.0
eslint-visitor-keys: ^4.2.0
checksum: b356267516faf17b0a6db07f44a3271bd05e5ab7f1f329cc03abac951ce38db495f3ecf5331e61d8378de67be4209166f8d630be91b0fa915a9ae9ff77222cfd
languageName: node
linkType: hard
"@wasm-audio-decoders/common@npm:9.0.7":
version: 9.0.7
resolution: "@wasm-audio-decoders/common@npm:9.0.7"
@@ -4194,20 +4039,6 @@ __metadata:
languageName: node
linkType: hard
"@whiskeysockets/eslint-config@github:whiskeysockets/eslint-config":
version: 1.0.0
resolution: "@whiskeysockets/eslint-config@https://github.com/whiskeysockets/eslint-config.git#commit=f264cd06d24a43f76e4ad2d81528f8485a1e7efe"
dependencies:
"@typescript-eslint/eslint-plugin": ^8.32.0
"@typescript-eslint/parser": ^8.32.0
eslint-plugin-simple-import-sort: ^12.1.1
peerDependencies:
eslint: "*"
typescript: ">=4"
checksum: c803aa0752112376261b0f5f6560832f90b3e700169b148cdc1984906b52c9cb15747714ca337ca6f7a90f7338310a17e112886aaeef800dadfa2088790e6259
languageName: node
linkType: hard
"@xtuc/ieee754@npm:^1.2.0":
version: 1.2.0
resolution: "@xtuc/ieee754@npm:1.2.0"
@@ -6712,15 +6543,6 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-simple-import-sort@npm:^12.1.1":
version: 12.1.1
resolution: "eslint-plugin-simple-import-sort@npm:12.1.1"
peerDependencies:
eslint: ">=5.0.0"
checksum: 6d73e43ecf6221c1952e0cc820e6867e3e1fe973575e23d438d1d3de52284ceb2a01d31e20a76de11feb158bbba98f113150a676cc5526a8b3a5844d63ca37f8
languageName: node
linkType: hard
"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.0.0, eslint-scope@npm:^5.1.0":
version: 5.1.1
resolution: "eslint-scope@npm:5.1.1"
@@ -6747,20 +6569,6 @@ __metadata:
languageName: node
linkType: hard
"eslint-visitor-keys@npm:^3.4.3":
version: 3.4.3
resolution: "eslint-visitor-keys@npm:3.4.3"
checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60
languageName: node
linkType: hard
"eslint-visitor-keys@npm:^4.2.0":
version: 4.2.1
resolution: "eslint-visitor-keys@npm:4.2.1"
checksum: 3a77e3f99a49109f6fb2c5b7784bc78f9743b834d238cdba4d66c602c6b52f19ed7bcd0a5c5dbbeae3a8689fd785e76c001799f53d2228b278282cf9f699fff5
languageName: node
linkType: hard
"eslint@npm:7.7.0":
version: 7.7.0
resolution: "eslint@npm:7.7.0"
@@ -7046,7 +6854,7 @@ __metadata:
languageName: node
linkType: hard
"fast-glob@npm:^3.3.2, fast-glob@npm:^3.3.3":
"fast-glob@npm:^3.3.3":
version: 3.3.3
resolution: "fast-glob@npm:3.3.3"
dependencies:
@@ -7746,13 +7554,6 @@ __metadata:
languageName: node
linkType: hard
"graphemer@npm:^1.4.0":
version: 1.4.0
resolution: "graphemer@npm:1.4.0"
checksum: bab8f0be9b568857c7bec9fda95a89f87b783546d02951c40c33f84d05bb7da3fd10f863a9beb901463669b6583173a8c8cc6d6b306ea2b9b9d5d3d943c3a673
languageName: node
linkType: hard
"grpc-tools@npm:^1.13.0":
version: 1.13.0
resolution: "grpc-tools@npm:1.13.0"
@@ -7974,7 +7775,7 @@ __metadata:
languageName: node
linkType: hard
"ignore@npm:^7.0.0, ignore@npm:^7.0.3":
"ignore@npm:^7.0.3":
version: 7.0.5
resolution: "ignore@npm:7.0.5"
checksum: d0862bf64d3d58bf34d5fb0a9f725bec9ca5ce8cd1aecc8f28034269e8f69b8009ffd79ca3eda96962a6a444687781cd5efdb8c7c8ddc0a6996e36d31c217f14
@@ -11618,7 +11419,7 @@ __metadata:
languageName: node
linkType: hard
"semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3, semver@npm:^7.7.2":
"semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.2":
version: 7.7.2
resolution: "semver@npm:7.7.2"
bin:
@@ -12665,15 +12466,6 @@ __metadata:
languageName: node
linkType: hard
"ts-api-utils@npm:^2.1.0":
version: 2.1.0
resolution: "ts-api-utils@npm:2.1.0"
peerDependencies:
typescript: ">=4.8.4"
checksum: 5b1ef89105654d93d67582308bd8dfe4bbf6874fccbcaa729b08fbb00a940fd4c691ca6d0d2b18c3c70878d9a7e503421b7cc473dbc3d0d54258b86401d4b15d
languageName: node
linkType: hard
"ts-jest@npm:^29.1.3":
version: 29.4.0
resolution: "ts-jest@npm:29.4.0"
@@ -13215,7 +13007,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "waha@workspace:."
dependencies:
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-05-01"
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-07-04"
"@adiwajshing/keyed-db": ^0.2.4
"@aws-sdk/client-s3": ^3.633.0
"@aws-sdk/s3-request-presigner": ^3.633.0
@@ -13399,7 +13191,7 @@ __metadata:
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-06-09":
version: 1.30.1-alpha.2
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=48fcc59575b46386747673d1b01b8f89e8c49c2d"
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=c133ae4a347ba7b21b647c8c6811f58c55b21069"
dependencies:
"@pedroslopez/moduleraid": ^5.0.2
archiver: ^5.3.1
@@ -13417,7 +13209,7 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: 7d867a4180321e85477e57b15f9ea85d2e26b6d1cf875f7e40131e4a047aaae1ad5e8b977c1ff9b3a64538276271fed06202ac6eb9168c7c5dd01eb155402f7f
checksum: e99e0c5172e4045098ef0cd027304b1f5d37b6ce1ae00f1b50f0ca59e5a0894faeca9972e7db88f1d0e6aebb3ca67c3d3cac333f594ab31ffeb166414e4eeb19
languageName: node
linkType: hard