Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e11312b75 | ||
|
|
1c7ce47dbb | ||
|
|
1d72e2666d | ||
|
|
7ae2f7999e | ||
|
|
68dc4ad642 | ||
|
|
e8f63aeaad | ||
|
|
f4e19c7664 | ||
|
|
6d8ef2e375 | ||
|
|
024cbffb13 | ||
|
|
ad399d9b01 | ||
|
|
6053c162c0 | ||
|
|
6654a7b3d0 | ||
|
|
4ff1c01e38 | ||
|
|
dfe0a3c8c4 | ||
|
|
04121bda52 | ||
|
|
7b6f522f92 |
No files matched your search
+1
-1
@@ -247,7 +247,7 @@ ENV CHOKIDAR_INTERVAL=5000
|
||||
ENV WAHA_ZIPPER=ZIPUNZIP
|
||||
|
||||
# GOWS - use libc DNS resolver
|
||||
ENV GODEBUG netdns=cgo
|
||||
ENV GODEBUG=netdns=cgo
|
||||
|
||||
# Run command, etc
|
||||
EXPOSE 3000
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"plugins": ["@nestjs/swagger"],
|
||||
"watchPathIgnorePatterns": ["node_modules", "src/dashboard", "dist"],
|
||||
"assets": [
|
||||
"dashboard/**",
|
||||
"core/engines/webjs/*",
|
||||
|
||||
+6
-2
@@ -39,7 +39,9 @@
|
||||
"@casl/ability": "^6.8.0",
|
||||
"@figuro/chatwoot-sdk": "^1.1.17",
|
||||
"@liaoliaots/nestjs-redis": "^9",
|
||||
"@lottiefiles/dotlottie-web": "^0.72.1",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@napi-rs/canvas": "^1.0.0",
|
||||
"@nestjs/axios": "^3.0.2",
|
||||
"@nestjs/bullmq": "^11.0.2",
|
||||
"@nestjs/common": "^11.0.0",
|
||||
@@ -58,8 +60,8 @@
|
||||
"@types/passport": "^1.0.17",
|
||||
"@types/sqlite3": "^5.1.0",
|
||||
"@types/ws": "^8.5.4",
|
||||
"@wppconnect-team/wppconnect": "^2.0.2",
|
||||
"@wppconnect/wa-js": "^4.1.0",
|
||||
"@wppconnect-team/wppconnect": "^2.2.1",
|
||||
"@wppconnect/wa-js": "^4.3.0",
|
||||
"adm-zip": "0.5.10",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"async-lock": "^1.4.1",
|
||||
@@ -94,9 +96,11 @@
|
||||
"mustache": "^4.2.0",
|
||||
"nestjs-pino": "^4.1.0",
|
||||
"node-cache": "5.1.2",
|
||||
"node-webpmux": "^3.2.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-headerapikey": "^1.2.2",
|
||||
"pg": "^8.13.1",
|
||||
"pg-copy-streams": "^6.0.0",
|
||||
"pino-http": "^10.2.0",
|
||||
"pino-pretty": "^11.2.1",
|
||||
"pretty-bytes": "5.6.0",
|
||||
|
||||
@@ -3000,6 +3000,8 @@ export namespace messages {
|
||||
#one_of_decls: number[][] = [];
|
||||
constructor(data?: any[] | {
|
||||
duration?: number;
|
||||
gifPlayback?: boolean;
|
||||
externalShareFullVideoDurationInSeconds?: number;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||
@@ -3007,6 +3009,12 @@ export namespace messages {
|
||||
if ("duration" in data && data.duration != undefined) {
|
||||
this.duration = data.duration;
|
||||
}
|
||||
if ("gifPlayback" in data && data.gifPlayback != undefined) {
|
||||
this.gifPlayback = data.gifPlayback;
|
||||
}
|
||||
if ("externalShareFullVideoDurationInSeconds" in data && data.externalShareFullVideoDurationInSeconds != undefined) {
|
||||
this.externalShareFullVideoDurationInSeconds = data.externalShareFullVideoDurationInSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
get duration() {
|
||||
@@ -3015,22 +3023,50 @@ export namespace messages {
|
||||
set duration(value: number) {
|
||||
pb_1.Message.setField(this, 1, value);
|
||||
}
|
||||
get gifPlayback() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 2, false) as boolean;
|
||||
}
|
||||
set gifPlayback(value: boolean) {
|
||||
pb_1.Message.setField(this, 2, value);
|
||||
}
|
||||
get externalShareFullVideoDurationInSeconds() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 3, 0) as number;
|
||||
}
|
||||
set externalShareFullVideoDurationInSeconds(value: number) {
|
||||
pb_1.Message.setField(this, 3, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
duration?: number;
|
||||
gifPlayback?: boolean;
|
||||
externalShareFullVideoDurationInSeconds?: number;
|
||||
}): VideoInfo {
|
||||
const message = new VideoInfo({});
|
||||
if (data.duration != null) {
|
||||
message.duration = data.duration;
|
||||
}
|
||||
if (data.gifPlayback != null) {
|
||||
message.gifPlayback = data.gifPlayback;
|
||||
}
|
||||
if (data.externalShareFullVideoDurationInSeconds != null) {
|
||||
message.externalShareFullVideoDurationInSeconds = data.externalShareFullVideoDurationInSeconds;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
const data: {
|
||||
duration?: number;
|
||||
gifPlayback?: boolean;
|
||||
externalShareFullVideoDurationInSeconds?: number;
|
||||
} = {};
|
||||
if (this.duration != null) {
|
||||
data.duration = this.duration;
|
||||
}
|
||||
if (this.gifPlayback != null) {
|
||||
data.gifPlayback = this.gifPlayback;
|
||||
}
|
||||
if (this.externalShareFullVideoDurationInSeconds != null) {
|
||||
data.externalShareFullVideoDurationInSeconds = this.externalShareFullVideoDurationInSeconds;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -3039,6 +3075,10 @@ export namespace messages {
|
||||
const writer = w || new pb_1.BinaryWriter();
|
||||
if (this.duration != 0)
|
||||
writer.writeFloat(1, this.duration);
|
||||
if (this.gifPlayback != false)
|
||||
writer.writeBool(2, this.gifPlayback);
|
||||
if (this.externalShareFullVideoDurationInSeconds != 0)
|
||||
writer.writeUint32(3, this.externalShareFullVideoDurationInSeconds);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -3051,6 +3091,12 @@ export namespace messages {
|
||||
case 1:
|
||||
message.duration = reader.readFloat();
|
||||
break;
|
||||
case 2:
|
||||
message.gifPlayback = reader.readBool();
|
||||
break;
|
||||
case 3:
|
||||
message.externalShareFullVideoDurationInSeconds = reader.readUint32();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7749,7 +7749,9 @@ proto.messages.VideoInfo.prototype.toObject = function(opt_includeInstance) {
|
||||
*/
|
||||
proto.messages.VideoInfo.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
duration: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0)
|
||||
duration: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0),
|
||||
gifplayback: jspb.Message.getBooleanFieldWithDefault(msg, 2, false),
|
||||
externalsharefullvideodurationinseconds: jspb.Message.getFieldWithDefault(msg, 3, 0)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -7790,6 +7792,14 @@ proto.messages.VideoInfo.deserializeBinaryFromReader = function(msg, reader) {
|
||||
var value = /** @type {number} */ (reader.readFloat());
|
||||
msg.setDuration(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setGifplayback(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {number} */ (reader.readUint32());
|
||||
msg.setExternalsharefullvideodurationinseconds(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -7826,6 +7836,20 @@ proto.messages.VideoInfo.serializeBinaryToWriter = function(message, writer) {
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getGifplayback();
|
||||
if (f) {
|
||||
writer.writeBool(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getExternalsharefullvideodurationinseconds();
|
||||
if (f !== 0) {
|
||||
writer.writeUint32(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7847,6 +7871,42 @@ proto.messages.VideoInfo.prototype.setDuration = function(value) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bool gifPlayback = 2;
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.messages.VideoInfo.prototype.getGifplayback = function() {
|
||||
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
* @return {!proto.messages.VideoInfo} returns this
|
||||
*/
|
||||
proto.messages.VideoInfo.prototype.setGifplayback = function(value) {
|
||||
return jspb.Message.setProto3BooleanField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint32 externalShareFullVideoDurationInSeconds = 3;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.messages.VideoInfo.prototype.getExternalsharefullvideodurationinseconds = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @return {!proto.messages.VideoInfo} returns this
|
||||
*/
|
||||
proto.messages.VideoInfo.prototype.setExternalsharefullvideodurationinseconds = function(value) {
|
||||
return jspb.Message.setProto3IntField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
NotImplementedByEngineError,
|
||||
} from '@waha/core/exceptions';
|
||||
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
|
||||
import { LottieMediaProcessorWrapper } from '@waha/core/media/LottieMediaProcessorWrapper';
|
||||
import { QR } from '@waha/core/QR';
|
||||
import { ExtractMessageKeysForRead } from '@waha/core/utils/convertors';
|
||||
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
|
||||
@@ -2224,7 +2225,10 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
protected async downloadMedia(message) {
|
||||
const processor = new GOWSEngineMediaProcessor(this);
|
||||
let processor: IMediaEngineProcessor<any> = new GOWSEngineMediaProcessor(
|
||||
this,
|
||||
);
|
||||
processor = new LottieMediaProcessorWrapper(processor, this.logger);
|
||||
const media = await this.mediaManager.processMedia(
|
||||
processor,
|
||||
message,
|
||||
@@ -2551,6 +2555,50 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Many encrypted stickers carry URL "https://a.whatsapp.net" with no path. If
|
||||
* that string is passed to DownloadMedia, the Go client may attempt HTTP GET to
|
||||
* that host instead of decrypting via directPath. Real CDN links use hosts
|
||||
* such as mmg.whatsapp.net with a full path.
|
||||
*
|
||||
* GOWS also exposes stickerMessage.URL (uppercase); some paths expect url
|
||||
* (lowercase). We mirror URL -> url only for real HTTP-style URLs.
|
||||
*/
|
||||
function isPlaceholderWhatsAppMediaUrl(url: unknown): boolean {
|
||||
if (typeof url !== 'string' || url.length === 0) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const parsed = new URL(url.trim());
|
||||
if (parsed.hostname !== 'a.whatsapp.net') {
|
||||
return false;
|
||||
}
|
||||
const path = parsed.pathname.replace(/\/+$/, '');
|
||||
return path === '';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeGowsStickerUrlForDownload(message: any): any {
|
||||
const sticker = message?.Message?.stickerMessage;
|
||||
if (!sticker) {
|
||||
return message;
|
||||
}
|
||||
|
||||
if (isPlaceholderWhatsAppMediaUrl(sticker.URL)) {
|
||||
delete sticker.URL;
|
||||
}
|
||||
if (isPlaceholderWhatsAppMediaUrl(sticker.url)) {
|
||||
delete sticker.url;
|
||||
}
|
||||
|
||||
if (sticker.URL && !sticker.url) {
|
||||
sticker.url = sticker.URL;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
export class GOWSEngineMediaProcessor implements IMediaEngineProcessor<any> {
|
||||
constructor(public session: WhatsappSessionGoWSCore) {}
|
||||
|
||||
@@ -2574,6 +2622,8 @@ export class GOWSEngineMediaProcessor implements IMediaEngineProcessor<any> {
|
||||
async getMediaBuffer(message: any): Promise<Buffer | null> {
|
||||
const mediaDownloadTimeoutMs = 600_000; // 10 minutes
|
||||
|
||||
message = normalizeGowsStickerUrlForDownload(message);
|
||||
|
||||
const data = JSON.stringify(message.Message);
|
||||
const tmpdir = new TmpDir(
|
||||
this.session.logger,
|
||||
|
||||
@@ -66,6 +66,7 @@ import { toVcardV3 } from '@waha/core/vcard';
|
||||
import { createAgentProxy } from '@waha/core/helpers.proxy';
|
||||
import type { Agent } from 'https';
|
||||
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
|
||||
import { LottieMediaProcessorWrapper } from '@waha/core/media/LottieMediaProcessorWrapper';
|
||||
import { QR } from '@waha/core/QR';
|
||||
import { AckToStatus, StatusToAck } from '@waha/core/utils/acks';
|
||||
import { pairs } from '@waha/utils/pairs';
|
||||
@@ -204,7 +205,13 @@ import { Agents } from '@waha/core/engines/noweb/types';
|
||||
import {
|
||||
IsEditedMessage,
|
||||
IsHistorySyncNotification,
|
||||
IsSecretEncryptedMessageEdit,
|
||||
} from '@waha/core/utils/pwa';
|
||||
import {
|
||||
decryptSecretEncryptedMessageEditProto,
|
||||
getOrigSenderJidForMsgSecret,
|
||||
jidToNonAD,
|
||||
} from '@waha/core/utils/secretEncryptedMessageEdit';
|
||||
import { extractWALocation } from '@waha/core/engines/waproto/locaiton';
|
||||
import { extractVCards } from '@waha/core/engines/waproto/vcards';
|
||||
import { Activity } from '@waha/core/abc/activity';
|
||||
@@ -2116,14 +2123,25 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
|
||||
// Handle edited messages
|
||||
const messagesEdited$ = messagesUpsert$.pipe(
|
||||
filter((message) => IsEditedMessage(message.message)),
|
||||
filter(
|
||||
(message) =>
|
||||
IsEditedMessage(message.message) ||
|
||||
IsSecretEncryptedMessageEdit(message.message),
|
||||
),
|
||||
mergeMap(async (message): Promise<WAMessageEditedBody> => {
|
||||
const waMessage = this.toWAMessage(message);
|
||||
// Extract the body from editedMessage using extractBody function
|
||||
const content = normalizeMessageContent(message.message);
|
||||
const body = extractBody(content.protocolMessage.editedMessage) || '';
|
||||
// Extract the original message ID from protocolMessage.key
|
||||
const editedMessageId = content.protocolMessage.key?.id;
|
||||
let body = '';
|
||||
let editedMessageId: string | undefined;
|
||||
if (IsEditedMessage(message.message)) {
|
||||
const content = normalizeMessageContent(message.message);
|
||||
body = extractBody(content.protocolMessage.editedMessage) || '';
|
||||
editedMessageId = content.protocolMessage.key?.id;
|
||||
} else if (IsSecretEncryptedMessageEdit(message.message)) {
|
||||
const sem = message.message.secretEncryptedMessage;
|
||||
editedMessageId = sem.targetMessageKey?.id;
|
||||
body =
|
||||
(await this.tryDecryptNOWEBSecretMessageEdit(message, sem)) || '';
|
||||
}
|
||||
return {
|
||||
...waMessage,
|
||||
body: body,
|
||||
@@ -2443,6 +2461,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return;
|
||||
// Ignore edit, we have a dedicated event for that
|
||||
if (IsEditedMessage(message.message)) return;
|
||||
// Ignore secret-encrypted message edits (mobile app format), dedicated handler routes them
|
||||
if (IsSecretEncryptedMessageEdit(message.message)) return;
|
||||
|
||||
// Ignore history sync notifications
|
||||
if (IsHistorySyncNotification(message.message)) return;
|
||||
@@ -2473,6 +2493,99 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async tryDecryptNOWEBSecretMessageEdit(
|
||||
editMessage: proto.IWebMessageInfo,
|
||||
sem: proto.Message.ISecretEncryptedMessage,
|
||||
): Promise<string> {
|
||||
const targetKey = sem.targetMessageKey;
|
||||
const origMsgId = targetKey?.id;
|
||||
if (!origMsgId) {
|
||||
return '';
|
||||
}
|
||||
const jidsToTry = [targetKey.remoteJid, editMessage.key?.remoteJid].filter(
|
||||
Boolean,
|
||||
);
|
||||
let stored: proto.IWebMessageInfo | undefined;
|
||||
for (const jid of jidsToTry) {
|
||||
stored = await this.store?.loadMessage(jid, origMsgId);
|
||||
if (stored) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stored) {
|
||||
this.logger.debug(
|
||||
{ origMsgId: origMsgId },
|
||||
'NOWEB message edit decrypt: original message not found in store',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
const secretBytes =
|
||||
normalizeMessageContent(stored.message)?.messageContextInfo
|
||||
?.messageSecret ?? stored.message?.messageContextInfo?.messageSecret;
|
||||
if (!secretBytes || secretBytes.length !== 32) {
|
||||
this.logger.debug(
|
||||
{ origMsgId: origMsgId },
|
||||
'NOWEB message edit decrypt: missing messageSecret on original',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
const origSecret = Buffer.from(secretBytes);
|
||||
const encPayload = sem.encPayload ? Buffer.from(sem.encPayload) : null;
|
||||
const encIv = sem.encIv ? Buffer.from(sem.encIv) : null;
|
||||
if (!encPayload || !encIv) {
|
||||
return '';
|
||||
}
|
||||
const editInfo = {
|
||||
Chat: editMessage.key?.remoteJid,
|
||||
Sender:
|
||||
editMessage.key?.participant ||
|
||||
(editMessage.key?.fromMe ? undefined : editMessage.key?.remoteJid),
|
||||
};
|
||||
const modificationSenderJid = jidToNonAD(editInfo.Sender || '');
|
||||
const primaryOrig = getOrigSenderJidForMsgSecret(editInfo, {
|
||||
fromMe: targetKey.fromMe,
|
||||
remoteJID: targetKey.remoteJid,
|
||||
participant: targetKey.participant,
|
||||
});
|
||||
const candidates: string[] = [primaryOrig];
|
||||
const remoteNonAD = targetKey.remoteJid
|
||||
? jidToNonAD(targetKey.remoteJid)
|
||||
: '';
|
||||
if (remoteNonAD && !candidates.includes(remoteNonAD)) {
|
||||
candidates.push(remoteNonAD);
|
||||
}
|
||||
const participantNonAD = targetKey.participant
|
||||
? jidToNonAD(targetKey.participant)
|
||||
: '';
|
||||
if (participantNonAD && !candidates.includes(participantNonAD)) {
|
||||
candidates.push(participantNonAD);
|
||||
}
|
||||
let lastErr: unknown;
|
||||
for (const origSenderJid of candidates) {
|
||||
try {
|
||||
const decoded = decryptSecretEncryptedMessageEditProto({
|
||||
encPayload: encPayload,
|
||||
encIv: encIv,
|
||||
origMsgId: origMsgId,
|
||||
origSenderJid: origSenderJid,
|
||||
modificationSenderJid: modificationSenderJid,
|
||||
origMsgSecret: origSecret,
|
||||
});
|
||||
const text = extractBody(decoded) || '';
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
} catch (err) {
|
||||
lastErr = err;
|
||||
}
|
||||
}
|
||||
this.logger.debug(
|
||||
{ err: lastErr, origMsgId: origMsgId, candidates: candidates },
|
||||
'NOWEB message edit decrypt: AES-GCM or protobuf decode failed',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
protected async processIncomingMessage(
|
||||
message,
|
||||
downloadMedia: boolean,
|
||||
@@ -2770,7 +2883,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
protected async downloadMedia(message): Promise<WAMedia | null> {
|
||||
const processor = new NOWEBEngineMediaProcessor(this, this.loggerBuilder);
|
||||
let processor: IMediaEngineProcessor<any> = new NOWEBEngineMediaProcessor(
|
||||
this,
|
||||
this.loggerBuilder,
|
||||
);
|
||||
processor = new LottieMediaProcessorWrapper(processor, this.logger);
|
||||
return this.mediaManager.processMedia(processor, message, this.name);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
NotImplementedByEngineError,
|
||||
} from '@waha/core/exceptions';
|
||||
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
|
||||
import { LottieMediaProcessorWrapper } from '@waha/core/media/LottieMediaProcessorWrapper';
|
||||
import { QR } from '@waha/core/QR';
|
||||
import { StatusToAck } from '@waha/core/utils/acks';
|
||||
import {
|
||||
@@ -2224,7 +2225,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: Message) {
|
||||
const processor = new WEBJSEngineMediaProcessor();
|
||||
let processor = new WEBJSEngineMediaProcessor();
|
||||
processor = new LottieMediaProcessorWrapper(processor, this.logger);
|
||||
const media = await this.mediaManager.processMedia(
|
||||
processor,
|
||||
message,
|
||||
|
||||
@@ -126,6 +126,7 @@ import {
|
||||
NotImplementedByEngineError,
|
||||
} from '@waha/core/exceptions';
|
||||
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
|
||||
import { LottieMediaProcessorWrapper } from '@waha/core/media/LottieMediaProcessorWrapper';
|
||||
import { IWPPAuthManager } from '@waha/core/engines/wpp/IWPPAuthManager';
|
||||
import { QR } from '@waha/core/QR';
|
||||
import { removeSingletonFiles } from '@waha/core/utils/chrome';
|
||||
@@ -2118,7 +2119,8 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
protected async downloadMedia(message: any): Promise<WAMedia | null> {
|
||||
const processor = new WPPEngineMediaProcessor(this.wpp);
|
||||
let processor = new WPPEngineMediaProcessor(this.wpp);
|
||||
processor = new LottieMediaProcessorWrapper(processor, this.logger);
|
||||
return this.mediaManager.processMedia(processor, message, this.name);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
|
||||
import { convertLottieZipToWebp } from '@waha/core/utils/lottie-converter';
|
||||
|
||||
const LOTTIE_MIMETYPE = 'application/was';
|
||||
|
||||
/**
|
||||
* Wraps any engine media processor to transparently convert Lottie stickers
|
||||
* (mimetype `application/was`, delivered as a ZIP archive) into animated WebP.
|
||||
* The inner processor downloads the raw ZIP; this wrapper converts it before
|
||||
* the result reaches the media storage layer, so callers see `image/webp`.
|
||||
*/
|
||||
export class LottieMediaProcessorWrapper implements IMediaEngineProcessor<any> {
|
||||
constructor(
|
||||
private inner: IMediaEngineProcessor<any>,
|
||||
private logger: any,
|
||||
) {}
|
||||
|
||||
hasMedia(msg: any): boolean {
|
||||
return this.inner.hasMedia(msg);
|
||||
}
|
||||
|
||||
getChatId(msg: any): string {
|
||||
return this.inner.getChatId(msg);
|
||||
}
|
||||
|
||||
getMessageId(msg: any): string {
|
||||
return this.inner.getMessageId(msg);
|
||||
}
|
||||
|
||||
getMimetype(msg: any): string {
|
||||
const mime = this.inner.getMimetype(msg);
|
||||
return mime === LOTTIE_MIMETYPE ? 'image/webp' : mime;
|
||||
}
|
||||
|
||||
getFilename(msg: any): string | null {
|
||||
if (this.inner.getMimetype(msg) !== LOTTIE_MIMETYPE) {
|
||||
return this.inner.getFilename(msg);
|
||||
}
|
||||
return `${this.inner.getMessageId(msg)}.webp`;
|
||||
}
|
||||
|
||||
async getMediaBuffer(msg: any): Promise<Buffer | null> {
|
||||
const buffer = await this.inner.getMediaBuffer(msg);
|
||||
if (!buffer || !buffer.length) {
|
||||
return null;
|
||||
}
|
||||
if (this.inner.getMimetype(msg) !== LOTTIE_MIMETYPE) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const id = this.inner.getMessageId(msg);
|
||||
this.logger.info(
|
||||
`Converting Lottie sticker '${id}' to WebP (input ${buffer.length} bytes)...`,
|
||||
);
|
||||
try {
|
||||
const webp = await convertLottieZipToWebp(buffer);
|
||||
this.logger.info(
|
||||
`Lottie sticker '${id}' converted to WebP (${webp.byteLength} bytes)`,
|
||||
);
|
||||
return webp;
|
||||
} catch (err) {
|
||||
this.logger.warn(
|
||||
{ err: err },
|
||||
`Lottie conversion failed for '${id}', returning original buffer`,
|
||||
);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
import { createCanvas, Canvas } from '@napi-rs/canvas';
|
||||
import { DotLottie } from '@lottiefiles/dotlottie-web';
|
||||
// sharp, adm-zip and node-webpmux are CJS modules whose module.exports is the
|
||||
// callable/constructor itself — static default imports resolve to undefined at
|
||||
// runtime because there is no .default property. require() is the correct interop.
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const sharp = require('sharp');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const AdmZip = require('adm-zip');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { Image: WebpImage } = require('node-webpmux');
|
||||
|
||||
// DotLottie._draw() guards canvas rendering behind:
|
||||
// canvas instanceof HTMLCanvasElement || canvas instanceof OffscreenCanvas
|
||||
// Both are undefined in Node.js, so the guard fails and _draw() returns without
|
||||
// calling putImageData – producing a fully black canvas. Registering the
|
||||
// @napi-rs/canvas Canvas constructor as HTMLCanvasElement makes the instanceof
|
||||
// check pass so DotLottie acquires the 2D context and paints frames normally.
|
||||
//
|
||||
// ImageData must NOT be registered globally. When it is, DotLottie uses:
|
||||
// new ImageData(Uint8ClampedArray_view_into_WASM_memory, w, h)
|
||||
// @napi-rs/canvas copies the bytes once at construction and never re-reads the
|
||||
// WASM buffer, so every frame produces the same cached pixels. Without a global
|
||||
// ImageData, DotLottie falls back to:
|
||||
// ctx.createImageData(w, h) + imageData.data.set(currentPixelBuffer)
|
||||
// which copies fresh pixel data from the WASM buffer on every frame.
|
||||
const globalAny = global as Record<string, unknown>;
|
||||
if (!globalAny['HTMLCanvasElement']) {
|
||||
globalAny['HTMLCanvasElement'] = Canvas;
|
||||
}
|
||||
|
||||
export class LottieConvertError extends Error {
|
||||
constructor(message: string, options?: { cause?: unknown }) {
|
||||
super(message);
|
||||
this.name = 'LottieConvertError';
|
||||
if (options?.cause !== undefined) {
|
||||
(this as any).cause = options.cause;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const STICKER_SIZE = 256;
|
||||
const envDelayMs = parseInt(
|
||||
process.env['WAHA_LOTTIE_DEFAULT_DELAY_MS'] ?? '',
|
||||
10,
|
||||
);
|
||||
const DEFAULT_DELAY_MS = envDelayMs > 0 ? envDelayMs : 100;
|
||||
|
||||
function isZipBuffer(buffer: Buffer): boolean {
|
||||
return (
|
||||
buffer.length >= 4 &&
|
||||
buffer[0] === 0x50 &&
|
||||
buffer[1] === 0x4b &&
|
||||
buffer[2] === 0x03 &&
|
||||
buffer[3] === 0x04
|
||||
);
|
||||
}
|
||||
|
||||
function extractAnimationJson(zipBuffer: Buffer): string | null {
|
||||
const zip = new AdmZip(zipBuffer);
|
||||
const entry =
|
||||
zip.getEntry('animation/animation.json') ??
|
||||
zip.getEntries().find((e) => e.entryName.endsWith('.json'));
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
return zip.readAsText(entry);
|
||||
}
|
||||
|
||||
export async function convertLottieZipToWebp(
|
||||
zipBuffer: Buffer,
|
||||
): Promise<Buffer> {
|
||||
if (!isZipBuffer(zipBuffer)) {
|
||||
throw new LottieConvertError('Lottie buffer is not a valid ZIP');
|
||||
}
|
||||
|
||||
let animationJson: string | null;
|
||||
try {
|
||||
animationJson = extractAnimationJson(zipBuffer);
|
||||
} catch (err) {
|
||||
throw new LottieConvertError(`Failed to unzip Lottie file: ${err}`, {
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
|
||||
if (!animationJson) {
|
||||
throw new LottieConvertError('Lottie ZIP has no animation.json entry');
|
||||
}
|
||||
|
||||
let animationData: Record<string, unknown>;
|
||||
try {
|
||||
animationData = JSON.parse(animationJson);
|
||||
} catch (err) {
|
||||
throw new LottieConvertError(
|
||||
`Lottie animation.json is not valid JSON: ${err}`,
|
||||
{
|
||||
cause: err,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return renderLottieToWebp(animationData);
|
||||
}
|
||||
|
||||
function renderLottieToWebp(
|
||||
animationData: Record<string, unknown>,
|
||||
): Promise<Buffer> {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
let settled = false;
|
||||
|
||||
function finish(result: Buffer) {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
function fail(reason: unknown) {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
reject(
|
||||
reason instanceof LottieConvertError
|
||||
? reason
|
||||
: new LottieConvertError(String(reason), { cause: reason }),
|
||||
);
|
||||
}
|
||||
|
||||
let canvas: Canvas;
|
||||
let dotLottie: DotLottie;
|
||||
|
||||
try {
|
||||
canvas = createCanvas(STICKER_SIZE, STICKER_SIZE);
|
||||
} catch (err) {
|
||||
return fail(err);
|
||||
}
|
||||
|
||||
// DotLottie dispatches 'load' via setTimeout. Instead of relying on the
|
||||
// setImmediate-based animation loop (timing-dependent, unreliable for
|
||||
// frame capture), we drive frames manually with setFrame(i) after load.
|
||||
// setFrame() renders synchronously: set_frame → render → putImageData.
|
||||
try {
|
||||
dotLottie = new DotLottie({
|
||||
loop: false,
|
||||
useFrameInterpolation: false,
|
||||
autoplay: false,
|
||||
canvas: canvas as unknown as HTMLCanvasElement,
|
||||
data: animationData,
|
||||
renderConfig: {
|
||||
autoResize: false,
|
||||
devicePixelRatio: 1,
|
||||
freezeOnOffscreen: false,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
return fail(err);
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
if (settled) return;
|
||||
|
||||
const totalFrames = dotLottie.totalFrames;
|
||||
const duration = dotLottie.duration;
|
||||
let delayMs: number;
|
||||
let framePixels: Buffer[];
|
||||
|
||||
try {
|
||||
if (totalFrames <= 0) {
|
||||
throw new Error(
|
||||
`Animation has no frames (totalFrames=${totalFrames})`,
|
||||
);
|
||||
}
|
||||
|
||||
let rawDelayMs = DEFAULT_DELAY_MS;
|
||||
if (duration > 0 && isFinite(duration)) {
|
||||
const fps = totalFrames / duration;
|
||||
const computed = Math.round(1000 / fps);
|
||||
if (computed > 0 && isFinite(computed)) {
|
||||
rawDelayMs = computed;
|
||||
}
|
||||
}
|
||||
// WebP delay is in milliseconds. Clamp to at least 30ms to stay above
|
||||
// the ~20ms threshold where some players impose their own minimum.
|
||||
// When clamping, subsample frames proportionally so total duration is
|
||||
// preserved — e.g. a 60fps animation must not play 1.8× too slowly
|
||||
// because all 180 frames are kept at 30ms each.
|
||||
delayMs = Math.max(30, rawDelayMs);
|
||||
const frameStep = delayMs / rawDelayMs;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
throw new Error('canvas.getContext("2d") returned null');
|
||||
}
|
||||
|
||||
// Collect raw RGBA pixels. When frameStep > 1 we subsample frames to
|
||||
// keep playback speed correct despite the minimum-delay clamp.
|
||||
// Pass the float position to setFrame so the renderer interpolates
|
||||
// smoothly along the animation curve rather than snapping to the
|
||||
// nearest integer frame (which causes uneven motion).
|
||||
framePixels = [];
|
||||
for (let t = 0; t < totalFrames; t += frameStep) {
|
||||
// setFrame(0) is a no-op (already rendered after load), so skip it.
|
||||
if (t > 0) {
|
||||
dotLottie.setFrame(t);
|
||||
}
|
||||
framePixels.push(
|
||||
Buffer.from(
|
||||
ctx.getImageData(0, 0, STICKER_SIZE, STICKER_SIZE).data,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
dotLottie.destroy();
|
||||
fail(err);
|
||||
return;
|
||||
}
|
||||
|
||||
dotLottie.destroy();
|
||||
|
||||
// Encode each RGBA frame as a single-frame WebP, then assemble into an
|
||||
// animated WebP via node-webpmux. This preserves full alpha transparency
|
||||
// and avoids GIF's 256-colour palette limitation.
|
||||
if (framePixels.length === 1) {
|
||||
sharp(framePixels[0], {
|
||||
raw: { width: STICKER_SIZE, height: STICKER_SIZE, channels: 4 },
|
||||
})
|
||||
.webp({ quality: 80, effort: 3 })
|
||||
.toBuffer()
|
||||
.then(finish)
|
||||
.catch(fail);
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.all(
|
||||
framePixels.map((px) =>
|
||||
sharp(px, {
|
||||
raw: { width: STICKER_SIZE, height: STICKER_SIZE, channels: 4 },
|
||||
})
|
||||
.webp({ quality: 80, effort: 3 })
|
||||
.toBuffer(),
|
||||
),
|
||||
)
|
||||
.then((webpFrames) =>
|
||||
Promise.all(
|
||||
webpFrames.map((buf) =>
|
||||
WebpImage.generateFrame({
|
||||
buffer: buf,
|
||||
delay: delayMs,
|
||||
x: 0,
|
||||
y: 0,
|
||||
blend: false,
|
||||
dispose: true,
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
.then((frames) =>
|
||||
WebpImage.save(null, {
|
||||
width: STICKER_SIZE,
|
||||
height: STICKER_SIZE,
|
||||
bgColor: [0, 0, 0, 0],
|
||||
loops: 0,
|
||||
frames: frames,
|
||||
}),
|
||||
)
|
||||
.then(finish)
|
||||
.catch(fail);
|
||||
}
|
||||
|
||||
function onLoadError(payload: any) {
|
||||
if (settled) return;
|
||||
dotLottie.destroy();
|
||||
fail(
|
||||
new LottieConvertError(
|
||||
`DotLottie failed to load animation: ${
|
||||
payload?.error ?? 'unknown error'
|
||||
}`,
|
||||
{ cause: payload?.error },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
dotLottie.addEventListener('load', onLoad);
|
||||
dotLottie.addEventListener('loadError', onLoadError);
|
||||
});
|
||||
}
|
||||
@@ -22,6 +22,19 @@ export function IsEditedMessage(message: proto.IMessage): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function IsSecretEncryptedMessageEdit(
|
||||
message: proto.IMessage | null | undefined,
|
||||
): boolean {
|
||||
const sem = message?.secretEncryptedMessage;
|
||||
if (!sem) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
sem.secretEncType ===
|
||||
proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT
|
||||
);
|
||||
}
|
||||
|
||||
export function IsHistorySyncNotification(message: proto.IMessage): boolean {
|
||||
message = normalizeMessageContent(message);
|
||||
if (!message) {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Decrypt secret-encrypted message edits (secretEncType MESSAGE_EDIT).
|
||||
*
|
||||
* Mirrors whatsmeow's generateMsgSecretKey + gcmutil.Decrypt for EncSecretMessageEdit.
|
||||
* @see https://github.com/tulir/whatsmeow/blob/main/msgsecret.go
|
||||
*
|
||||
* Key derivation uses Baileys' own hkdf (whatsapp-rust-bridge), which passes nil/undefined
|
||||
* salt = 32-byte zeros, matching Go's golang.org/x/crypto/hkdf with nil salt.
|
||||
*/
|
||||
import { aesDecryptGCM, hkdf, proto } from '@adiwajshing/baileys';
|
||||
|
||||
const ENC_SECRET_MESSAGE_EDIT = 'Message Edit';
|
||||
|
||||
export function jidToNonAD(jid: string): string {
|
||||
if (!jid || !jid.includes('@')) {
|
||||
return jid;
|
||||
}
|
||||
const [user, server] = jid.split('@', 2);
|
||||
return `${user.split(':')[0]}@${server || ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirrors whatsmeow getOrigSenderFromKey — resolves the JID used as the
|
||||
* "origMsgSender" slot in the HKDF info block.
|
||||
*/
|
||||
export function getOrigSenderJidForMsgSecret(
|
||||
editMessageInfo: { Chat?: string; Sender?: string },
|
||||
targetMessageKey: {
|
||||
fromMe?: boolean;
|
||||
FromMe?: boolean;
|
||||
remoteJID?: string;
|
||||
RemoteJID?: string;
|
||||
participant?: string;
|
||||
Participant?: string;
|
||||
},
|
||||
): string {
|
||||
const chat = editMessageInfo.Chat || '';
|
||||
const server = chat.includes('@') ? chat.split('@')[1] : '';
|
||||
const sender = editMessageInfo.Sender || '';
|
||||
|
||||
const fromMe =
|
||||
targetMessageKey.fromMe === true || targetMessageKey.FromMe === true;
|
||||
const remoteJid =
|
||||
targetMessageKey.remoteJID || targetMessageKey.RemoteJID || '';
|
||||
const participant =
|
||||
targetMessageKey.participant || targetMessageKey.Participant || '';
|
||||
|
||||
if (fromMe) {
|
||||
return jidToNonAD(sender);
|
||||
}
|
||||
if (server === 's.whatsapp.net' || server === 'lid') {
|
||||
return jidToNonAD(remoteJid);
|
||||
}
|
||||
return jidToNonAD(participant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives the AES-256-GCM key for a MESSAGE_EDIT secret-encrypted payload.
|
||||
*
|
||||
* Matches whatsmeow's generateMsgSecretKey:
|
||||
* useCaseSecret = msgId + origSenderJid + modSenderJid + "Message Edit"
|
||||
* key = HKDF-SHA256(ikm=origMsgSecret, salt=nil→32×0, info=useCaseSecret)
|
||||
*/
|
||||
export function generateMsgSecretKeyForMessageEdit(
|
||||
origMsgId: string,
|
||||
origSenderJid: string,
|
||||
modificationSenderJid: string,
|
||||
origMsgSecret: Uint8Array,
|
||||
): Buffer {
|
||||
const useCaseInfo = Buffer.concat([
|
||||
Buffer.from(origMsgId, 'utf8'),
|
||||
Buffer.from(jidToNonAD(origSenderJid), 'utf8'),
|
||||
Buffer.from(jidToNonAD(modificationSenderJid), 'utf8'),
|
||||
Buffer.from(ENC_SECRET_MESSAGE_EDIT, 'utf8'),
|
||||
]);
|
||||
// No salt → Rust bridge uses 32 zero bytes, matching whatsmeow nil salt.
|
||||
// info must be passed as latin1 string so each byte maps 1:1 (same as binary concat).
|
||||
return Buffer.from(
|
||||
hkdf(origMsgSecret, 32, { info: useCaseInfo.toString('latin1') }),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a secretEncryptedMessage (MESSAGE_EDIT) payload and decodes the
|
||||
* resulting proto.Message. Throws on AES-GCM auth failure or proto decode error.
|
||||
*/
|
||||
export function decryptSecretEncryptedMessageEditProto(params: {
|
||||
encPayload: Uint8Array;
|
||||
encIv: Uint8Array;
|
||||
origMsgId: string;
|
||||
origSenderJid: string;
|
||||
modificationSenderJid: string;
|
||||
origMsgSecret: Uint8Array;
|
||||
}): proto.Message {
|
||||
const {
|
||||
encPayload,
|
||||
encIv,
|
||||
origMsgId,
|
||||
origSenderJid,
|
||||
modificationSenderJid,
|
||||
origMsgSecret,
|
||||
} = params;
|
||||
const secretKey = generateMsgSecretKeyForMessageEdit(
|
||||
origMsgId,
|
||||
origSenderJid,
|
||||
modificationSenderJid,
|
||||
origMsgSecret,
|
||||
);
|
||||
// MESSAGE_EDIT uses an empty additionalData (unlike Poll Vote which uses msgId+voterJid)
|
||||
const plaintext = aesDecryptGCM(
|
||||
encPayload,
|
||||
secretKey,
|
||||
encIv,
|
||||
Buffer.alloc(0),
|
||||
);
|
||||
return proto.Message.decode(plaintext);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDefined,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
@@ -265,13 +266,15 @@ export class OverviewFilter {
|
||||
}
|
||||
|
||||
export class OverviewBodyRequest {
|
||||
@IsDefined()
|
||||
@ValidateNested()
|
||||
@Type(() => GetChatsOverviewParams)
|
||||
pagination: GetChatsOverviewParams;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => OverviewFilter)
|
||||
filter: OverviewFilter;
|
||||
filter?: OverviewFilter;
|
||||
}
|
||||
|
||||
export class ChatSummary {
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ function getPlatform() {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2026.4.3',
|
||||
version: '2026.5.1',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowser(),
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.38"
|
||||
"ref": "v1.0.40"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
"ref": "35c218fc9d365552ac356a07103f61205acee0d3"
|
||||
"ref": "47244c7d9202e0f4c1b23d12d45d5c208cd1184f"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,19 +6,19 @@ __metadata:
|
||||
cacheKey: 10
|
||||
|
||||
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-04-28":
|
||||
version: 7.0.0-rc10
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=a56f2c88eac278b589101278a2d5e8f343fd0321"
|
||||
version: 7.0.0-rc13
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=f619b7e841450140e444a0e93ff3f04ab50519b6"
|
||||
dependencies:
|
||||
"@cacheable/node-cache": "npm:^1.4.0"
|
||||
"@hapi/boom": "npm:^9.1.3"
|
||||
async-mutex: "npm:^0.5.0"
|
||||
libsignal: "git+https://github.com/whiskeysockets/libsignal-node"
|
||||
libsignal: "npm:^6.0.0"
|
||||
lru-cache: "npm:^11.1.0"
|
||||
music-metadata: "npm:^11.12.3"
|
||||
p-queue: "npm:^9.0.0"
|
||||
pino: "npm:^9.6"
|
||||
protobufjs: "npm:^7.5.6"
|
||||
whatsapp-rust-bridge: "npm:0.5.3"
|
||||
whatsapp-rust-bridge: "npm:0.5.4"
|
||||
ws: "npm:^8.13.0"
|
||||
peerDependencies:
|
||||
audio-decode: ^2.1.3
|
||||
@@ -32,7 +32,7 @@ __metadata:
|
||||
optional: true
|
||||
link-preview-js:
|
||||
optional: true
|
||||
checksum: 10/a6eb9438f1d32ccad2d46c8ee84e8e3c448c6ba43bd0d28f2b88f414bcbe46d5e1802a59a8a2866ed73c02e13ada98c35aa996a4545b9db02068a5ca8ad41116
|
||||
checksum: 10/535e52bf0b5bad381c985fcead24cb8c262a97662b9aaf3a4c0081c47ba02204e9f015e96dfb043f9d8e75295498e3e7468ee056f82f832af882a321c475d7bd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2250,6 +2250,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lottiefiles/dotlottie-web@npm:^0.72.1":
|
||||
version: 0.72.1
|
||||
resolution: "@lottiefiles/dotlottie-web@npm:0.72.1"
|
||||
checksum: 10/79bec5037fb07f38a676d32a11597277c0b15c8ad9716dbde008fe79b2e4e0374aaefcffd9bb68672a8bb35d02b57af4ed01a857238166f8ed85786ad29756b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lukeed/csprng@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "@lukeed/csprng@npm:1.1.0"
|
||||
@@ -2367,6 +2374,129 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-android-arm64@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-android-arm64@npm:1.0.0"
|
||||
conditions: os=android & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-darwin-arm64@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-darwin-arm64@npm:1.0.0"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-darwin-x64@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-darwin-x64@npm:1.0.0"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-linux-arm-gnueabihf@npm:1.0.0"
|
||||
conditions: os=linux & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-gnu@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-linux-arm64-gnu@npm:1.0.0"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-musl@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-linux-arm64-musl@npm:1.0.0"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-linux-riscv64-gnu@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-linux-riscv64-gnu@npm:1.0.0"
|
||||
conditions: os=linux & cpu=riscv64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-linux-x64-gnu@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-linux-x64-gnu@npm:1.0.0"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-linux-x64-musl@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-linux-x64-musl@npm:1.0.0"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-win32-arm64-msvc@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-win32-arm64-msvc@npm:1.0.0"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas-win32-x64-msvc@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas-win32-x64-msvc@npm:1.0.0"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/canvas@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@napi-rs/canvas@npm:1.0.0"
|
||||
dependencies:
|
||||
"@napi-rs/canvas-android-arm64": "npm:1.0.0"
|
||||
"@napi-rs/canvas-darwin-arm64": "npm:1.0.0"
|
||||
"@napi-rs/canvas-darwin-x64": "npm:1.0.0"
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf": "npm:1.0.0"
|
||||
"@napi-rs/canvas-linux-arm64-gnu": "npm:1.0.0"
|
||||
"@napi-rs/canvas-linux-arm64-musl": "npm:1.0.0"
|
||||
"@napi-rs/canvas-linux-riscv64-gnu": "npm:1.0.0"
|
||||
"@napi-rs/canvas-linux-x64-gnu": "npm:1.0.0"
|
||||
"@napi-rs/canvas-linux-x64-musl": "npm:1.0.0"
|
||||
"@napi-rs/canvas-win32-arm64-msvc": "npm:1.0.0"
|
||||
"@napi-rs/canvas-win32-x64-msvc": "npm:1.0.0"
|
||||
dependenciesMeta:
|
||||
"@napi-rs/canvas-android-arm64":
|
||||
optional: true
|
||||
"@napi-rs/canvas-darwin-arm64":
|
||||
optional: true
|
||||
"@napi-rs/canvas-darwin-x64":
|
||||
optional: true
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf":
|
||||
optional: true
|
||||
"@napi-rs/canvas-linux-arm64-gnu":
|
||||
optional: true
|
||||
"@napi-rs/canvas-linux-arm64-musl":
|
||||
optional: true
|
||||
"@napi-rs/canvas-linux-riscv64-gnu":
|
||||
optional: true
|
||||
"@napi-rs/canvas-linux-x64-gnu":
|
||||
optional: true
|
||||
"@napi-rs/canvas-linux-x64-musl":
|
||||
optional: true
|
||||
"@napi-rs/canvas-win32-arm64-msvc":
|
||||
optional: true
|
||||
"@napi-rs/canvas-win32-x64-msvc":
|
||||
optional: true
|
||||
canvas:
|
||||
built: true
|
||||
skia-canvas:
|
||||
built: true
|
||||
checksum: 10/1b2838833988c98d86105980ccd52fb53c4059e30732c7bebdf21774d83a351bf389337c8e6eb4d55fc31359accb90e27d8d80c94cc3bb08d415ab0586227464
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nestjs/axios@npm:^3.0.2":
|
||||
version: 3.1.3
|
||||
resolution: "@nestjs/axios@npm:3.1.3"
|
||||
@@ -2900,6 +3030,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@protobufjs/eventemitter@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@protobufjs/eventemitter@npm:1.1.1"
|
||||
checksum: 10/a54dc1aff4475ffad4fdf3235c71a553f5e40e3b4cf6a2e217151895a61cb4eb0be20d63791db22441ca25e594671f1021977133f9939540750231ff7d8e9dd6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@protobufjs/fetch@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "@protobufjs/fetch@npm:1.1.0"
|
||||
@@ -2910,6 +3047,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@protobufjs/fetch@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@protobufjs/fetch@npm:1.1.1"
|
||||
dependencies:
|
||||
"@protobufjs/aspromise": "npm:^1.1.1"
|
||||
checksum: 10/427cf2da8c69b494b0df3b2fb1f43c97f0f71ca2c8ef8232dac7e44f2527ad0cc9cecb243eda14a918e86018bfa6d54d92252240d2b37ed205b13adb5506fa1d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@protobufjs/float@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "@protobufjs/float@npm:1.0.2"
|
||||
@@ -2931,6 +3077,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@protobufjs/inquire@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "@protobufjs/inquire@npm:1.1.2"
|
||||
checksum: 10/259756489c75a751552df60d18f82503d2534855646397b96b91cf15807fa852e99bd9eb73dabb64da37aec7913844032ecb031a4326d82aae622f5e4c2f8a17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@protobufjs/path@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "@protobufjs/path@npm:1.1.2"
|
||||
@@ -4464,15 +4617,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@wppconnect-team/wppconnect@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "@wppconnect-team/wppconnect@npm:2.0.2"
|
||||
"@wppconnect-team/wppconnect@npm:^2.2.1":
|
||||
version: 2.2.1
|
||||
resolution: "@wppconnect-team/wppconnect@npm:2.2.1"
|
||||
dependencies:
|
||||
"@img/sharp-win32-x64": "npm:^0.34.5"
|
||||
"@wppconnect/wa-js": "npm:^4.1.0"
|
||||
"@wppconnect/wa-version": "npm:^1.5.3829"
|
||||
"@wppconnect/wa-js": "npm:^4.3.0"
|
||||
"@wppconnect/wa-version": "npm:^1.5.3941"
|
||||
atob: "npm:^2.1.2"
|
||||
axios: "npm:^1.16.0"
|
||||
axios: "npm:^1.16.1"
|
||||
boxen: "npm:^5.1.2"
|
||||
catch-exit: "npm:^2.0.0"
|
||||
chalk: "npm:~4.1.2"
|
||||
@@ -4485,7 +4638,7 @@ __metadata:
|
||||
logform: "npm:^2.7.0"
|
||||
lookpath: "npm:^1.2.3"
|
||||
mime-types: "npm:^3.0.2"
|
||||
puppeteer: "npm:^24.37.5"
|
||||
puppeteer: "npm:^24.43.1"
|
||||
puppeteer-extra: "npm:^3.3.6"
|
||||
puppeteer-extra-plugin-stealth: "npm:^2.11.2"
|
||||
puppeteer-extra-plugin-user-data-dir: "npm:^2.4.1"
|
||||
@@ -4498,30 +4651,30 @@ __metadata:
|
||||
tmp: "npm:^0.2.5"
|
||||
tree-kill: "npm:^1.2.2"
|
||||
winston: "npm:^3.19.0"
|
||||
ws: "npm:^8.19.0"
|
||||
ws: "npm:^8.21.0"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-win32-x64":
|
||||
optional: true
|
||||
fsevents:
|
||||
optional: true
|
||||
checksum: 10/b1e5d83174e5786d17ceceec0917d077428d8f3b15f4f9e0df667f23b350d2fd984f322e7fc69270751e55aa10a4747ec95c7f5a6c5d7a6048094520e0e0686d
|
||||
checksum: 10/d7940928c2874ddd0e2412b0628d2750c2457bf9b08a8840e79de24e8666f792e2e7a78c0bc16c13c4ab870e7d37683ad1b08522fb9ea7da90f695e5b21cc044
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@wppconnect/wa-js@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "@wppconnect/wa-js@npm:4.1.0"
|
||||
checksum: 10/e807b6f3b0bdd1db0400937fe24fbdd27414d09067c734e4a7fa852155be28cda57cf64a6b7547c7f9559bd483e933a321156d87e39579bb342d7bbdcf273c9d
|
||||
"@wppconnect/wa-js@npm:^4.3.0":
|
||||
version: 4.3.0
|
||||
resolution: "@wppconnect/wa-js@npm:4.3.0"
|
||||
checksum: 10/844e63b58b62d4057ab51f114b3f9b12e13637a026b6df50c5f53801181a3800065342c0f79478db10d43dfe96a6ec60bf9d8fab7840caaf94970a774572279d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@wppconnect/wa-version@npm:^1.5.3829":
|
||||
version: 1.5.3874
|
||||
resolution: "@wppconnect/wa-version@npm:1.5.3874"
|
||||
"@wppconnect/wa-version@npm:^1.5.3941":
|
||||
version: 1.5.4014
|
||||
resolution: "@wppconnect/wa-version@npm:1.5.4014"
|
||||
dependencies:
|
||||
node-fetch: "npm:^2.7.0"
|
||||
semver: "npm:^7.7.4"
|
||||
checksum: 10/37812da2cb3f63c93a6519251da3bc61f52fdd874842a9d31792430ff56e533e9d3f42feed9630bf1c77ec3a5e4d7347ffa4732d58e742d8463019f7770fb9c1
|
||||
checksum: 10/97916af06fc2d1817cb3122233577b39047c46aa607ba0a14ff8d0ba9decd62a1d0220fc658da97dea378f433cc4b3bae89960d25320ae456d905b687ba40ad3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9359,16 +9512,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"libsignal@git+https://github.com/whiskeysockets/libsignal-node":
|
||||
version: 2.0.1
|
||||
resolution: "libsignal@https://github.com/whiskeysockets/libsignal-node.git#commit=e81ecfc32eb74951d789ab37f7e341ab66d5fff1"
|
||||
dependencies:
|
||||
curve25519-js: "npm:^0.0.4"
|
||||
protobufjs: "npm:6.8.8"
|
||||
checksum: 10/b3244ebb2f2580bda960180fcb91a20f47dfd68acc04ab3fff3163cd7205876307fb4a50af8830977d650c5719389a8acf547bf63fb722bcd3273e8fbdb94b95
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"libsignal@github:WhiskeySockets/libsignal-node#master":
|
||||
version: 2.0.1
|
||||
resolution: "libsignal@https://github.com/WhiskeySockets/libsignal-node.git#commit=e81ecfc32eb74951d789ab37f7e341ab66d5fff1"
|
||||
@@ -9379,6 +9522,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"libsignal@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "libsignal@npm:6.0.0"
|
||||
dependencies:
|
||||
curve25519-js: "npm:^0.0.4"
|
||||
protobufjs: "npm:^7.5.5"
|
||||
checksum: 10/d2e2463f9bb630fe1c8bb2c6dd8d52312040dce8ab6acefd4a9c37a080156e22db52198d13fa4b5d16bc63ff4b01420f2748e6f5c05b1fd010316cf8bb69f5a8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lighthouse-logger@npm:^1.0.0":
|
||||
version: 1.4.2
|
||||
resolution: "lighthouse-logger@npm:1.4.2"
|
||||
@@ -10498,6 +10651,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-webpmux@npm:^3.2.1":
|
||||
version: 3.2.1
|
||||
resolution: "node-webpmux@npm:3.2.1"
|
||||
checksum: 10/8797ebe4846707443629800c70d95c592ab1c18f313b360dd26832b8443efa9b89966269ad6b1e3aa59a18e790406b29a55e19c10212fc3068dc476e48cee6e9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "nopt@npm:5.0.0"
|
||||
@@ -10597,6 +10757,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"obuf@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "obuf@npm:1.1.2"
|
||||
checksum: 10/53ff4ab3a13cc33ba6c856cf281f2965c0aec9720967af450e8fd06cfd50aceeefc791986a16bcefa14e7898b3ca9acdfcf15b9d9a1b9c7e1366581a8ad6e65e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ogg-opus-decoder@npm:^1.6.12":
|
||||
version: 1.7.0
|
||||
resolution: "ogg-opus-decoder@npm:1.7.0"
|
||||
@@ -11038,6 +11205,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pg-copy-streams@npm:^6.0.0":
|
||||
version: 6.0.6
|
||||
resolution: "pg-copy-streams@npm:6.0.6"
|
||||
dependencies:
|
||||
obuf: "npm:^1.1.2"
|
||||
checksum: 10/58bd4a44f618e0706de402b401bad42fbb3b3743985ac5891536a239331469db3be734352955259bfdf46d1b772604a8da493edd16f0399f3685fcb4517bf165
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pg-int8@npm:1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "pg-int8@npm:1.0.1"
|
||||
@@ -11440,6 +11616,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"protobufjs@npm:^7.5.5":
|
||||
version: 7.6.1
|
||||
resolution: "protobufjs@npm:7.6.1"
|
||||
dependencies:
|
||||
"@protobufjs/aspromise": "npm:^1.1.2"
|
||||
"@protobufjs/base64": "npm:^1.1.2"
|
||||
"@protobufjs/codegen": "npm:^2.0.5"
|
||||
"@protobufjs/eventemitter": "npm:^1.1.1"
|
||||
"@protobufjs/fetch": "npm:^1.1.1"
|
||||
"@protobufjs/float": "npm:^1.0.2"
|
||||
"@protobufjs/inquire": "npm:^1.1.2"
|
||||
"@protobufjs/path": "npm:^1.1.2"
|
||||
"@protobufjs/pool": "npm:^1.1.0"
|
||||
"@protobufjs/utf8": "npm:^1.1.1"
|
||||
"@types/node": "npm:>=13.7.0"
|
||||
long: "npm:^5.3.2"
|
||||
checksum: 10/010dcf77ab435da50d62887d57cc4b68384768e4d1cbe85948ec747996222ea659501b7c6b6d3426f96fdbaf59f577213788ce461c0938f89b8126569d8be7e2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"protobufjs@npm:^7.5.6":
|
||||
version: 7.5.6
|
||||
resolution: "protobufjs@npm:7.5.6"
|
||||
@@ -13795,7 +13991,9 @@ __metadata:
|
||||
"@grpc/grpc-js": "npm:^1.14.1"
|
||||
"@grpc/proto-loader": "npm:^0.8.0"
|
||||
"@liaoliaots/nestjs-redis": "npm:^9"
|
||||
"@lottiefiles/dotlottie-web": "npm:^0.72.1"
|
||||
"@modelcontextprotocol/sdk": "npm:^1.29.0"
|
||||
"@napi-rs/canvas": "npm:^1.0.0"
|
||||
"@nestjs/axios": "npm:^3.0.2"
|
||||
"@nestjs/bullmq": "npm:^11.0.2"
|
||||
"@nestjs/cli": "npm:^11.0.0"
|
||||
@@ -13824,8 +14022,8 @@ __metadata:
|
||||
"@types/supertest": "npm:^2.0.8"
|
||||
"@types/user-agents": "npm:^1"
|
||||
"@types/ws": "npm:^8.5.4"
|
||||
"@wppconnect-team/wppconnect": "npm:^2.0.2"
|
||||
"@wppconnect/wa-js": "npm:^4.1.0"
|
||||
"@wppconnect-team/wppconnect": "npm:^2.2.1"
|
||||
"@wppconnect/wa-js": "npm:^4.3.0"
|
||||
adm-zip: "npm:0.5.10"
|
||||
agentkeepalive: "npm:^4.5.0"
|
||||
async-lock: "npm:^1.4.1"
|
||||
@@ -13864,10 +14062,12 @@ __metadata:
|
||||
mustache: "npm:^4.2.0"
|
||||
nestjs-pino: "npm:^4.1.0"
|
||||
node-cache: "npm:5.1.2"
|
||||
node-webpmux: "npm:^3.2.1"
|
||||
oxlint: "npm:^1.13.0"
|
||||
passport: "npm:^0.7.0"
|
||||
passport-headerapikey: "npm:^1.2.2"
|
||||
pg: "npm:^8.13.1"
|
||||
pg-copy-streams: "npm:^6.0.0"
|
||||
pino-http: "npm:^10.2.0"
|
||||
pino-pretty: "npm:^11.2.1"
|
||||
prettier: "npm:^1.19.1"
|
||||
|
||||
Reference in new issue
Block a user