@xbibzlibrary/whatsbibz 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/LICENSE +22 -0
- package/LICENSE.upstream +21 -0
- package/README.md +280 -0
- package/VERIFIKASI-IDENTITAS-2026-09-03.md +138 -0
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6035 -0
- package/WAProto/fix-imports.js +81 -0
- package/WAProto/index.d.ts +15314 -0
- package/WAProto/index.js +132326 -0
- package/assets/logo/README.md +21 -0
- package/assets/logo/png/banner-1200x360.png +0 -0
- package/assets/logo/png/icon-1024.png +0 -0
- package/assets/logo/png/icon-128.png +0 -0
- package/assets/logo/png/icon-16.png +0 -0
- package/assets/logo/png/icon-24.png +0 -0
- package/assets/logo/png/icon-256.png +0 -0
- package/assets/logo/png/icon-32.png +0 -0
- package/assets/logo/png/icon-48.png +0 -0
- package/assets/logo/png/icon-512.png +0 -0
- package/assets/logo/png/icon-64.png +0 -0
- package/assets/logo/png/mark-512.png +0 -0
- package/assets/logo/preview.png +0 -0
- package/assets/logo/whatsbibz.ico +0 -0
- package/lib/BibzWhats/client.d.ts +95 -0
- package/lib/BibzWhats/client.js +501 -0
- package/lib/BibzWhats/device-identity.d.ts +60 -0
- package/lib/BibzWhats/device-identity.js +234 -0
- package/lib/BibzWhats/extract.d.ts +29 -0
- package/lib/BibzWhats/extract.js +115 -0
- package/lib/BibzWhats/index.d.ts +7 -0
- package/lib/BibzWhats/index.js +8 -0
- package/lib/BibzWhats/jid.d.ts +22 -0
- package/lib/BibzWhats/jid.js +98 -0
- package/lib/BibzWhats/pairing.d.ts +35 -0
- package/lib/BibzWhats/pairing.js +186 -0
- package/lib/BibzWhats/send.d.ts +9 -0
- package/lib/BibzWhats/send.js +107 -0
- package/lib/BibzWhats/version.d.ts +6 -0
- package/lib/BibzWhats/version.js +7 -0
- package/lib/Defaults/index.d.ts +73 -0
- package/lib/Defaults/index.js +141 -0
- package/lib/Modded/message_builder.d.ts +15 -0
- package/lib/Modded/message_builder.js +2330 -0
- package/lib/Signal/Group/ciphertext-message.d.ts +9 -0
- package/lib/Signal/Group/ciphertext-message.js +11 -0
- package/lib/Signal/Group/group-session-builder.d.ts +14 -0
- package/lib/Signal/Group/group-session-builder.js +29 -0
- package/lib/Signal/Group/group_cipher.d.ts +16 -0
- package/lib/Signal/Group/group_cipher.js +81 -0
- package/lib/Signal/Group/index.d.ts +11 -0
- package/lib/Signal/Group/index.js +11 -0
- package/lib/Signal/Group/keyhelper.d.ts +10 -0
- package/lib/Signal/Group/keyhelper.js +17 -0
- package/lib/Signal/Group/sender-chain-key.d.ts +13 -0
- package/lib/Signal/Group/sender-chain-key.js +25 -0
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +62 -0
- package/lib/Signal/Group/sender-key-message.d.ts +18 -0
- package/lib/Signal/Group/sender-key-message.js +65 -0
- package/lib/Signal/Group/sender-key-name.d.ts +17 -0
- package/lib/Signal/Group/sender-key-name.js +47 -0
- package/lib/Signal/Group/sender-key-record.d.ts +30 -0
- package/lib/Signal/Group/sender-key-record.js +40 -0
- package/lib/Signal/Group/sender-key-state.d.ts +38 -0
- package/lib/Signal/Group/sender-key-state.js +83 -0
- package/lib/Signal/Group/sender-message-key.d.ts +11 -0
- package/lib/Signal/Group/sender-message-key.js +25 -0
- package/lib/Signal/libsignal.d.ts +4 -0
- package/lib/Signal/libsignal.js +406 -0
- package/lib/Signal/lid-mapping.d.ts +18 -0
- package/lib/Signal/lid-mapping.js +276 -0
- package/lib/Socket/Client/index.d.ts +2 -0
- package/lib/Socket/Client/index.js +2 -0
- package/lib/Socket/Client/types.d.ts +15 -0
- package/lib/Socket/Client/types.js +10 -0
- package/lib/Socket/Client/websocket.d.ts +12 -0
- package/lib/Socket/Client/websocket.js +53 -0
- package/lib/Socket/business.d.ts +537 -0
- package/lib/Socket/business.js +421 -0
- package/lib/Socket/chats.d.ts +110 -0
- package/lib/Socket/chats.js +1006 -0
- package/lib/Socket/communities.d.ts +646 -0
- package/lib/Socket/communities.js +430 -0
- package/lib/Socket/engine.d.ts +129 -0
- package/lib/Socket/engine.js +767 -0
- package/lib/Socket/groups.d.ts +149 -0
- package/lib/Socket/groups.js +326 -0
- package/lib/Socket/index.d.ts +637 -0
- package/lib/Socket/index.js +14 -0
- package/lib/Socket/messages-recv.d.ts +239 -0
- package/lib/Socket/messages-recv.js +1307 -0
- package/lib/Socket/messages-send.d.ts +580 -0
- package/lib/Socket/messages-send.js +1639 -0
- package/lib/Socket/mex.d.ts +2 -0
- package/lib/Socket/mex.js +47 -0
- package/lib/Socket/newsletter.d.ts +176 -0
- package/lib/Socket/newsletter.js +456 -0
- package/lib/Socket/socket.d.ts +56 -0
- package/lib/Socket/socket.js +998 -0
- package/lib/Types/Auth.d.ts +115 -0
- package/lib/Types/Auth.js +1 -0
- package/lib/Types/Bussines.d.ts +36 -0
- package/lib/Types/Bussines.js +1 -0
- package/lib/Types/Call.d.ts +14 -0
- package/lib/Types/Call.js +1 -0
- package/lib/Types/Chat.d.ts +124 -0
- package/lib/Types/Chat.js +7 -0
- package/lib/Types/Contact.d.ts +23 -0
- package/lib/Types/Contact.js +1 -0
- package/lib/Types/Events.d.ts +241 -0
- package/lib/Types/Events.js +1 -0
- package/lib/Types/GroupMetadata.d.ts +67 -0
- package/lib/Types/GroupMetadata.js +1 -0
- package/lib/Types/Label.d.ts +46 -0
- package/lib/Types/Label.js +24 -0
- package/lib/Types/LabelAssociation.d.ts +29 -0
- package/lib/Types/LabelAssociation.js +6 -0
- package/lib/Types/Message.d.ts +427 -0
- package/lib/Types/Message.js +10 -0
- package/lib/Types/Newsletter.d.ts +134 -0
- package/lib/Types/Newsletter.js +30 -0
- package/lib/Types/Product.d.ts +78 -0
- package/lib/Types/Product.js +1 -0
- package/lib/Types/Signal.d.ts +77 -0
- package/lib/Types/Signal.js +1 -0
- package/lib/Types/Socket.d.ts +173 -0
- package/lib/Types/Socket.js +2 -0
- package/lib/Types/State.d.ts +38 -0
- package/lib/Types/State.js +12 -0
- package/lib/Types/USync.d.ts +25 -0
- package/lib/Types/USync.js +1 -0
- package/lib/Types/index.d.ts +75 -0
- package/lib/Types/index.js +32 -0
- package/lib/Utils/auth-utils.d.ts +18 -0
- package/lib/Utils/auth-utils.js +289 -0
- package/lib/Utils/browser-utils.d.ts +3 -0
- package/lib/Utils/browser-utils.js +47 -0
- package/lib/Utils/business.d.ts +22 -0
- package/lib/Utils/business.js +230 -0
- package/lib/Utils/chat-utils.d.ts +69 -0
- package/lib/Utils/chat-utils.js +811 -0
- package/lib/Utils/companion-reg-client-utils.d.ts +20 -0
- package/lib/Utils/companion-reg-client-utils.js +66 -0
- package/lib/Utils/crypto.d.ts +41 -0
- package/lib/Utils/crypto.js +117 -0
- package/lib/Utils/decode-wa-message.d.ts +47 -0
- package/lib/Utils/decode-wa-message.js +282 -0
- package/lib/Utils/event-buffer.d.ts +35 -0
- package/lib/Utils/event-buffer.js +590 -0
- package/lib/Utils/generics.d.ts +90 -0
- package/lib/Utils/generics.js +384 -0
- package/lib/Utils/history.d.ts +21 -0
- package/lib/Utils/history.js +130 -0
- package/lib/Utils/identity-change-handler.d.ts +36 -0
- package/lib/Utils/identity-change-handler.js +48 -0
- package/lib/Utils/index.d.ts +25 -0
- package/lib/Utils/index.js +24 -0
- package/lib/Utils/link-preview.d.ts +20 -0
- package/lib/Utils/link-preview.js +84 -0
- package/lib/Utils/logger.d.ts +12 -0
- package/lib/Utils/logger.js +2 -0
- package/lib/Utils/lt-hash.d.ts +10 -0
- package/lib/Utils/lt-hash.js +7 -0
- package/lib/Utils/make-mutex.d.ts +8 -0
- package/lib/Utils/make-mutex.js +32 -0
- package/lib/Utils/message-retry-manager.d.ts +109 -0
- package/lib/Utils/message-retry-manager.js +224 -0
- package/lib/Utils/messages-media.d.ts +129 -0
- package/lib/Utils/messages-media.js +784 -0
- package/lib/Utils/messages.d.ts +90 -0
- package/lib/Utils/messages.js +1240 -0
- package/lib/Utils/noise-handler.d.ts +19 -0
- package/lib/Utils/noise-handler.js +200 -0
- package/lib/Utils/offline-node-processor.d.ts +16 -0
- package/lib/Utils/offline-node-processor.js +39 -0
- package/lib/Utils/platform-identity.d.ts +14 -0
- package/lib/Utils/platform-identity.js +148 -0
- package/lib/Utils/pre-key-manager.d.ts +27 -0
- package/lib/Utils/pre-key-manager.js +105 -0
- package/lib/Utils/process-message.d.ts +59 -0
- package/lib/Utils/process-message.js +544 -0
- package/lib/Utils/reporting-utils.d.ts +10 -0
- package/lib/Utils/reporting-utils.js +257 -0
- package/lib/Utils/rich-messages.d.ts +276 -0
- package/lib/Utils/rich-messages.js +1143 -0
- package/lib/Utils/signal.d.ts +33 -0
- package/lib/Utils/signal.js +158 -0
- package/lib/Utils/stanza-ack.d.ts +10 -0
- package/lib/Utils/stanza-ack.js +37 -0
- package/lib/Utils/sticker-pack.d.ts +10 -0
- package/lib/Utils/sticker-pack.js +111 -0
- package/lib/Utils/sync-action-utils.d.ts +18 -0
- package/lib/Utils/sync-action-utils.js +47 -0
- package/lib/Utils/tc-token-utils.d.ts +8 -0
- package/lib/Utils/tc-token-utils.js +45 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +12 -0
- package/lib/Utils/use-multi-file-auth-state.js +120 -0
- package/lib/Utils/validate-connection.d.ts +10 -0
- package/lib/Utils/validate-connection.js +222 -0
- package/lib/VoIP/audio-feeder.d.ts +15 -0
- package/lib/VoIP/audio-feeder.js +136 -0
- package/lib/VoIP/index.d.ts +12 -0
- package/lib/VoIP/index.js +282 -0
- package/lib/VoIP/relay-transport.d.ts +43 -0
- package/lib/VoIP/relay-transport.js +559 -0
- package/lib/VoIP/signaling.js +603 -0
- package/lib/VoIP/types.d.ts +69 -0
- package/lib/VoIP/types.js +17 -0
- package/lib/VoIP/wasm-engine.d.ts +103 -0
- package/lib/VoIP/wasm-engine.js +1214 -0
- package/lib/VoIP/worker-bootstrap.js +1042 -0
- package/lib/WABinary/constants.d.ts +27 -0
- package/lib/WABinary/constants.js +1300 -0
- package/lib/WABinary/decode.d.ts +6 -0
- package/lib/WABinary/decode.js +261 -0
- package/lib/WABinary/encode.d.ts +2 -0
- package/lib/WABinary/encode.js +219 -0
- package/lib/WABinary/generic-utils.d.ts +16 -0
- package/lib/WABinary/generic-utils.js +190 -0
- package/lib/WABinary/index.d.ts +5 -0
- package/lib/WABinary/index.js +5 -0
- package/lib/WABinary/jid-utils.d.ts +47 -0
- package/lib/WABinary/jid-utils.js +95 -0
- package/lib/WABinary/types.d.ts +18 -0
- package/lib/WABinary/types.js +1 -0
- package/lib/WAM/BinaryInfo.d.ts +8 -0
- package/lib/WAM/BinaryInfo.js +9 -0
- package/lib/WAM/constants.d.ts +39 -0
- package/lib/WAM/constants.js +22852 -0
- package/lib/WAM/encode.d.ts +2 -0
- package/lib/WAM/encode.js +149 -0
- package/lib/WAM/index.d.ts +3 -0
- package/lib/WAM/index.js +3 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +9 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
- package/lib/WAUSync/Protocols/index.d.ts +4 -0
- package/lib/WAUSync/Protocols/index.js +4 -0
- package/lib/WAUSync/USyncQuery.d.ts +28 -0
- package/lib/WAUSync/USyncQuery.js +93 -0
- package/lib/WAUSync/USyncUser.d.ts +12 -0
- package/lib/WAUSync/USyncUser.js +22 -0
- package/lib/WAUSync/index.d.ts +3 -0
- package/lib/WAUSync/index.js +3 -0
- package/lib/assets/wasm/loader.js +5 -0
- package/lib/assets/wasm/whatsapp.wasm +0 -0
- package/lib/assets/wasm/worker-modules.js +273 -0
- package/lib/index.d.ts +21 -0
- package/lib/index.js +22 -0
- package/package.json +111 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { proto } from '../../WAProto/index.js';
|
|
2
|
+
import type { KeyPair } from '../Types/index.js';
|
|
3
|
+
import type { BinaryNode } from '../WABinary/index.js';
|
|
4
|
+
import type { ILogger } from './logger.js';
|
|
5
|
+
export declare const makeNoiseHandler: ({ keyPair: { private: privateKey, public: publicKey }, NOISE_HEADER, logger, routingInfo }: {
|
|
6
|
+
keyPair: KeyPair;
|
|
7
|
+
NOISE_HEADER: Uint8Array;
|
|
8
|
+
logger: ILogger;
|
|
9
|
+
routingInfo?: Buffer | undefined;
|
|
10
|
+
}) => {
|
|
11
|
+
encrypt: (plaintext: Uint8Array) => Uint8Array;
|
|
12
|
+
decrypt: (ciphertext: Uint8Array) => Uint8Array;
|
|
13
|
+
authenticate: (data: Uint8Array) => void;
|
|
14
|
+
mixIntoKey: (data: Uint8Array) => void;
|
|
15
|
+
finishInit: () => Promise<void>;
|
|
16
|
+
processHandshake: ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => Uint8Array<ArrayBufferLike>;
|
|
17
|
+
encodeFrame: (data: Buffer | Uint8Array) => Buffer;
|
|
18
|
+
decodeFrame: (newData: Buffer | Uint8Array, onFrame: (buff: Uint8Array | BinaryNode) => void) => Promise<void>;
|
|
19
|
+
};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { proto } from '../../WAProto/index.js';
|
|
3
|
+
import { NOISE_MODE, WA_CERT_DETAILS } from '../Defaults/index.js';
|
|
4
|
+
import { decodeBinaryNode } from '../WABinary/index.js';
|
|
5
|
+
import { aesDecryptGCM, aesEncryptGCM, Curve, hkdf, sha256 } from './crypto.js';
|
|
6
|
+
const IV_LENGTH = 12;
|
|
7
|
+
const EMPTY_BUFFER = Buffer.alloc(0);
|
|
8
|
+
const generateIV = (counter) => {
|
|
9
|
+
const iv = new ArrayBuffer(IV_LENGTH);
|
|
10
|
+
new DataView(iv).setUint32(8, counter);
|
|
11
|
+
return new Uint8Array(iv);
|
|
12
|
+
};
|
|
13
|
+
class TransportState {
|
|
14
|
+
constructor(encKey, decKey) {
|
|
15
|
+
this.encKey = encKey;
|
|
16
|
+
this.decKey = decKey;
|
|
17
|
+
this.readCounter = 0;
|
|
18
|
+
this.writeCounter = 0;
|
|
19
|
+
this.iv = new Uint8Array(IV_LENGTH);
|
|
20
|
+
}
|
|
21
|
+
encrypt(plaintext) {
|
|
22
|
+
const c = this.writeCounter++;
|
|
23
|
+
this.iv[8] = (c >>> 24) & 0xff;
|
|
24
|
+
this.iv[9] = (c >>> 16) & 0xff;
|
|
25
|
+
this.iv[10] = (c >>> 8) & 0xff;
|
|
26
|
+
this.iv[11] = c & 0xff;
|
|
27
|
+
return aesEncryptGCM(plaintext, this.encKey, this.iv, EMPTY_BUFFER);
|
|
28
|
+
}
|
|
29
|
+
decrypt(ciphertext) {
|
|
30
|
+
const c = this.readCounter++;
|
|
31
|
+
this.iv[8] = (c >>> 24) & 0xff;
|
|
32
|
+
this.iv[9] = (c >>> 16) & 0xff;
|
|
33
|
+
this.iv[10] = (c >>> 8) & 0xff;
|
|
34
|
+
this.iv[11] = c & 0xff;
|
|
35
|
+
return aesDecryptGCM(ciphertext, this.decKey, this.iv, EMPTY_BUFFER);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publicKey }, NOISE_HEADER, logger, routingInfo }) => {
|
|
39
|
+
logger = logger.child({ class: 'ns' });
|
|
40
|
+
const data = Buffer.from(NOISE_MODE);
|
|
41
|
+
let hash = data.byteLength === 32 ? data : sha256(data);
|
|
42
|
+
let salt = hash;
|
|
43
|
+
let encKey = hash;
|
|
44
|
+
let decKey = hash;
|
|
45
|
+
let counter = 0;
|
|
46
|
+
let sentIntro = false;
|
|
47
|
+
let inBytes = Buffer.alloc(0);
|
|
48
|
+
let transport = null;
|
|
49
|
+
let isWaitingForTransport = false;
|
|
50
|
+
let pendingOnFrame = null;
|
|
51
|
+
let introHeader;
|
|
52
|
+
if (routingInfo) {
|
|
53
|
+
introHeader = Buffer.alloc(7 + routingInfo.byteLength + NOISE_HEADER.length);
|
|
54
|
+
introHeader.write('ED', 0, 'utf8');
|
|
55
|
+
introHeader.writeUint8(0, 2);
|
|
56
|
+
introHeader.writeUint8(1, 3);
|
|
57
|
+
introHeader.writeUint8(routingInfo.byteLength >> 16, 4);
|
|
58
|
+
introHeader.writeUint16BE(routingInfo.byteLength & 65535, 5);
|
|
59
|
+
introHeader.set(routingInfo, 7);
|
|
60
|
+
introHeader.set(NOISE_HEADER, 7 + routingInfo.byteLength);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
introHeader = Buffer.from(NOISE_HEADER);
|
|
64
|
+
}
|
|
65
|
+
const authenticate = (data) => {
|
|
66
|
+
if (!transport) {
|
|
67
|
+
hash = sha256(Buffer.concat([hash, data]));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const encrypt = (plaintext) => {
|
|
71
|
+
if (transport) {
|
|
72
|
+
return transport.encrypt(plaintext);
|
|
73
|
+
}
|
|
74
|
+
const result = aesEncryptGCM(plaintext, encKey, generateIV(counter++), hash);
|
|
75
|
+
authenticate(result);
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
78
|
+
const decrypt = (ciphertext) => {
|
|
79
|
+
if (transport) {
|
|
80
|
+
return transport.decrypt(ciphertext);
|
|
81
|
+
}
|
|
82
|
+
const result = aesDecryptGCM(ciphertext, decKey, generateIV(counter++), hash);
|
|
83
|
+
authenticate(ciphertext);
|
|
84
|
+
return result;
|
|
85
|
+
};
|
|
86
|
+
const localHKDF = (data) => {
|
|
87
|
+
const key = hkdf(Buffer.from(data), 64, { salt, info: '' });
|
|
88
|
+
return [key.subarray(0, 32), key.subarray(32)];
|
|
89
|
+
};
|
|
90
|
+
const mixIntoKey = (data) => {
|
|
91
|
+
const [write, read] = localHKDF(data);
|
|
92
|
+
salt = write;
|
|
93
|
+
encKey = read;
|
|
94
|
+
decKey = read;
|
|
95
|
+
counter = 0;
|
|
96
|
+
};
|
|
97
|
+
const finishInit = async () => {
|
|
98
|
+
isWaitingForTransport = true;
|
|
99
|
+
const [write, read] = localHKDF(new Uint8Array(0));
|
|
100
|
+
transport = new TransportState(write, read);
|
|
101
|
+
isWaitingForTransport = false;
|
|
102
|
+
logger.trace('Noise handler transitioned to Transport state');
|
|
103
|
+
if (pendingOnFrame) {
|
|
104
|
+
logger.trace({ length: inBytes.length }, 'Flushing buffered frames after transport ready');
|
|
105
|
+
await processData(pendingOnFrame);
|
|
106
|
+
pendingOnFrame = null;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const processData = async (onFrame) => {
|
|
110
|
+
let size;
|
|
111
|
+
while (true) {
|
|
112
|
+
if (inBytes.length < 3)
|
|
113
|
+
return;
|
|
114
|
+
size = (inBytes[0] << 16) | (inBytes[1] << 8) | inBytes[2];
|
|
115
|
+
if (inBytes.length < size + 3)
|
|
116
|
+
return;
|
|
117
|
+
let frame = inBytes.subarray(3, size + 3);
|
|
118
|
+
inBytes = inBytes.subarray(size + 3);
|
|
119
|
+
if (transport) {
|
|
120
|
+
const result = transport.decrypt(frame);
|
|
121
|
+
frame = await decodeBinaryNode(result);
|
|
122
|
+
}
|
|
123
|
+
if (logger.level === 'trace') {
|
|
124
|
+
logger.trace({ msg: frame?.attrs?.id }, 'recv frame');
|
|
125
|
+
}
|
|
126
|
+
onFrame(frame);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
authenticate(NOISE_HEADER);
|
|
130
|
+
authenticate(publicKey);
|
|
131
|
+
return {
|
|
132
|
+
encrypt,
|
|
133
|
+
decrypt,
|
|
134
|
+
authenticate,
|
|
135
|
+
mixIntoKey,
|
|
136
|
+
finishInit,
|
|
137
|
+
processHandshake: ({ serverHello }, noiseKey) => {
|
|
138
|
+
authenticate(serverHello.ephemeral);
|
|
139
|
+
mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral));
|
|
140
|
+
const decStaticContent = decrypt(serverHello.static);
|
|
141
|
+
mixIntoKey(Curve.sharedKey(privateKey, decStaticContent));
|
|
142
|
+
const certDecoded = decrypt(serverHello.payload);
|
|
143
|
+
const { intermediate: certIntermediate, leaf } = proto.CertChain.decode(certDecoded);
|
|
144
|
+
// leaf
|
|
145
|
+
if (!leaf?.details || !leaf?.signature) {
|
|
146
|
+
throw new Boom('invalid noise leaf certificate', { statusCode: 400 });
|
|
147
|
+
}
|
|
148
|
+
if (!certIntermediate?.details || !certIntermediate?.signature) {
|
|
149
|
+
throw new Boom('invalid noise intermediate certificate', { statusCode: 400 });
|
|
150
|
+
}
|
|
151
|
+
const details = proto.CertChain.NoiseCertificate.Details.decode(certIntermediate.details);
|
|
152
|
+
const { issuerSerial } = details;
|
|
153
|
+
const verify = Curve.verify(details.key, leaf.details, leaf.signature);
|
|
154
|
+
const verifyIntermediate = Curve.verify(WA_CERT_DETAILS.PUBLIC_KEY, certIntermediate.details, certIntermediate.signature);
|
|
155
|
+
if (!verify) {
|
|
156
|
+
throw new Boom('noise certificate signature invalid', { statusCode: 400 });
|
|
157
|
+
}
|
|
158
|
+
if (!verifyIntermediate) {
|
|
159
|
+
throw new Boom('noise intermediate certificate signature invalid', { statusCode: 400 });
|
|
160
|
+
}
|
|
161
|
+
if (issuerSerial !== WA_CERT_DETAILS.SERIAL) {
|
|
162
|
+
throw new Boom('certification match failed', { statusCode: 400 });
|
|
163
|
+
}
|
|
164
|
+
const keyEnc = encrypt(noiseKey.public);
|
|
165
|
+
mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello.ephemeral));
|
|
166
|
+
return keyEnc;
|
|
167
|
+
},
|
|
168
|
+
encodeFrame: (data) => {
|
|
169
|
+
if (transport) {
|
|
170
|
+
data = transport.encrypt(data);
|
|
171
|
+
}
|
|
172
|
+
const dataLen = data.byteLength;
|
|
173
|
+
const introSize = sentIntro ? 0 : introHeader.length;
|
|
174
|
+
const frame = Buffer.allocUnsafe(introSize + 3 + dataLen);
|
|
175
|
+
if (!sentIntro) {
|
|
176
|
+
frame.set(introHeader);
|
|
177
|
+
sentIntro = true;
|
|
178
|
+
}
|
|
179
|
+
frame[introSize] = (dataLen >>> 16) & 0xff;
|
|
180
|
+
frame[introSize + 1] = (dataLen >>> 8) & 0xff;
|
|
181
|
+
frame[introSize + 2] = dataLen & 0xff;
|
|
182
|
+
frame.set(data, introSize + 3);
|
|
183
|
+
return frame;
|
|
184
|
+
},
|
|
185
|
+
decodeFrame: async (newData, onFrame) => {
|
|
186
|
+
if (isWaitingForTransport) {
|
|
187
|
+
inBytes = Buffer.concat([inBytes, newData]);
|
|
188
|
+
pendingOnFrame = onFrame;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (inBytes.length === 0) {
|
|
192
|
+
inBytes = Buffer.from(newData);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
inBytes = Buffer.concat([inBytes, newData]);
|
|
196
|
+
}
|
|
197
|
+
await processData(onFrame);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BinaryNode } from '../WABinary/index.js';
|
|
2
|
+
export type MessageType = 'message' | 'call' | 'receipt' | 'notification';
|
|
3
|
+
export type OfflineNodeProcessorDeps = {
|
|
4
|
+
isWsOpen: () => boolean;
|
|
5
|
+
onUnexpectedError: (error: Error, msg: string) => void;
|
|
6
|
+
yieldToEventLoop: () => Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Creates a processor for offline stanza nodes that:
|
|
10
|
+
* - Queues nodes for sequential processing
|
|
11
|
+
* - Yields to the event loop periodically to avoid blocking
|
|
12
|
+
* - Catches handler errors to prevent the processing loop from crashing
|
|
13
|
+
*/
|
|
14
|
+
export declare function makeOfflineNodeProcessor(nodeProcessorMap: Map<MessageType, (node: BinaryNode) => Promise<void>>, deps: OfflineNodeProcessorDeps, batchSize?: number): {
|
|
15
|
+
enqueue: (type: MessageType, node: BinaryNode) => void;
|
|
16
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a processor for offline stanza nodes that:
|
|
3
|
+
* - Queues nodes for sequential processing
|
|
4
|
+
* - Yields to the event loop periodically to avoid blocking
|
|
5
|
+
* - Catches handler errors to prevent the processing loop from crashing
|
|
6
|
+
*/
|
|
7
|
+
export function makeOfflineNodeProcessor(nodeProcessorMap, deps, batchSize = 10) {
|
|
8
|
+
const nodes = [];
|
|
9
|
+
let isProcessing = false;
|
|
10
|
+
const enqueue = (type, node) => {
|
|
11
|
+
nodes.push({ type, node });
|
|
12
|
+
if (isProcessing) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
isProcessing = true;
|
|
16
|
+
const promise = async () => {
|
|
17
|
+
let processedInBatch = 0;
|
|
18
|
+
while (nodes.length && deps.isWsOpen()) {
|
|
19
|
+
const { type, node } = nodes.shift();
|
|
20
|
+
const nodeProcessor = nodeProcessorMap.get(type);
|
|
21
|
+
if (!nodeProcessor) {
|
|
22
|
+
deps.onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node');
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
await nodeProcessor(node).catch(err => deps.onUnexpectedError(err, `processing offline ${type}`));
|
|
26
|
+
processedInBatch++;
|
|
27
|
+
// Yield to event loop after processing a batch
|
|
28
|
+
// This prevents blocking the event loop for too long when there are many offline nodes
|
|
29
|
+
if (processedInBatch >= batchSize) {
|
|
30
|
+
processedInBatch = 0;
|
|
31
|
+
await deps.yieldToEventLoop();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
isProcessing = false;
|
|
35
|
+
};
|
|
36
|
+
promise().catch(error => deps.onUnexpectedError(error, 'processing offline nodes'));
|
|
37
|
+
};
|
|
38
|
+
return { enqueue };
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { WABrowserDescription } from '../Types/index.js';
|
|
2
|
+
export declare const PAIRING_ACCEPTED_OS: readonly string[];
|
|
3
|
+
export declare const PAIRING_OS_FALLBACK: Readonly<Record<string, string>>;
|
|
4
|
+
export declare const PAIRING_ACCEPTED_BROWSERS: readonly string[];
|
|
5
|
+
/** OS valid untuk companion_platform_display, atau null bila tidak dikenal */
|
|
6
|
+
export declare function resolvePairingOs(os: string | undefined): string | null;
|
|
7
|
+
export declare function resolvePairingBrowser(browser: string | undefined): string;
|
|
8
|
+
/** display yang pasti lolos validasi server, diturunkan dari [os, browser, version] */
|
|
9
|
+
export declare function derivePairingDisplay(browser: WABrowserDescription | readonly string[], hostOs?: string): string;
|
|
10
|
+
export declare function isPairingDisplayAccepted(display: string | undefined): boolean;
|
|
11
|
+
export declare function hostOsLabel(hostOs?: string): string;
|
|
12
|
+
export declare function linuxDistroName(file?: string): string | null;
|
|
13
|
+
export declare function hostOsVersion(): string;
|
|
14
|
+
export declare function lintIdentity(opts?: { browser?: readonly string[]; companionPlatformDisplay?: string | null; syncFullHistory?: boolean }): Array<{ level: 'warn' | 'info'; message: string }>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// BibzWhats — identitas platform companion (apa yang WhatsApp "lihat" dari library ini)
|
|
2
|
+
//
|
|
3
|
+
// Tiga tempat identitas dipakai, dan ketiganya berbeda aturannya:
|
|
4
|
+
//
|
|
5
|
+
// 1. DeviceProps.os (= browser[0]) → teks di daftar "Perangkat tertaut" di HP.
|
|
6
|
+
// Bebas; server menerima nama apa pun lewat QR.
|
|
7
|
+
// 2. companion_platform_display (pairing kode) → DIVALIDASI server dengan
|
|
8
|
+
// allow-list. Nilai tidak dikenal = <error code='400' text='bad-request'/>
|
|
9
|
+
// dan HP tidak pernah dapat notifikasi. Diukur langsung ke server produksi
|
|
10
|
+
// (2026-09-03, WA Web 2.3000.1046672143):
|
|
11
|
+
// OS diterima : Mac OS, macOS, Windows, Linux, Ubuntu, Debian, Fedora,
|
|
12
|
+
// CentOS, Gentoo, Manjaro, Chromium OS, Android, iOS
|
|
13
|
+
// OS ditolak : Arch Linux, Arch, ArchLinux, GNU/Linux, Mac OS X, Linux Mint,
|
|
14
|
+
// openSUSE, Kali, NixOS, Pop!_OS, FreeBSD, Chrome OS,
|
|
15
|
+
// nama produk (BibzWhats/Foo) — semuanya 400
|
|
16
|
+
// Browser : Chrome, Chromium, Firefox, Safari, Edge, Opera, Brave, Vivaldi,
|
|
17
|
+
// Arc diterima; "Desktop" DITOLAK (400)
|
|
18
|
+
// Karena itu library menurunkan display dari OS terdekat yang valid:
|
|
19
|
+
// Arch Linux → "Chrome (Linux)", Mac OS Desktop → "Chrome (Mac OS)".
|
|
20
|
+
// 3. WebInfo.webSubPlatform (ClientPayload) → hanya relevan untuk identitas
|
|
21
|
+
// "Desktop" + syncFullHistory. DARWIN (Mac OS Desktop) dan APP_STORE
|
|
22
|
+
// ditutup server: 428 sebelum QR. WIN_HYBRID (Windows) dan WEB_BROWSER
|
|
23
|
+
// diterima. Mac OS Desktop otomatis diturunkan ke WEB_BROWSER.
|
|
24
|
+
//
|
|
25
|
+
// Hasil pengukuran lengkap ada di test/identity-live.test.js (opsional, live).
|
|
26
|
+
|
|
27
|
+
import { platform, release } from 'os';
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
|
|
30
|
+
/** OS yang diterima server pada companion_platform_display (huruf besar-kecil bebas) */
|
|
31
|
+
export const PAIRING_ACCEPTED_OS = Object.freeze([
|
|
32
|
+
'Mac OS', 'macOS', 'Windows', 'Linux', 'Ubuntu', 'Debian', 'Fedora',
|
|
33
|
+
'CentOS', 'Gentoo', 'Manjaro', 'Chromium OS', 'Android', 'iOS'
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
/** OS yang terbukti DITOLAK (400) — dipetakan ke padanan yang valid */
|
|
37
|
+
export const PAIRING_OS_FALLBACK = Object.freeze({
|
|
38
|
+
'arch linux': 'Linux', 'archlinux': 'Linux', 'arch': 'Linux', 'arch linux arm': 'Linux',
|
|
39
|
+
'gnu/linux': 'Linux', 'linux mint': 'Linux', 'mint': 'Linux', 'opensuse': 'Linux',
|
|
40
|
+
'kali': 'Linux', 'nixos': 'Linux', 'pop!_os': 'Linux', 'alpine': 'Linux', 'void': 'Linux',
|
|
41
|
+
'endeavouros': 'Linux', 'garuda': 'Linux', 'raspbian': 'Linux', 'red hat': 'Linux',
|
|
42
|
+
'rocky': 'Linux', 'almalinux': 'Linux', 'elementary os': 'Linux', 'zorin': 'Linux',
|
|
43
|
+
'mac os x': 'Mac OS', 'osx': 'Mac OS', 'os x': 'Mac OS', 'darwin': 'Mac OS',
|
|
44
|
+
'chrome os': 'Chromium OS', 'chromeos': 'Chromium OS',
|
|
45
|
+
'freebsd': 'Linux', 'openbsd': 'Linux', 'netbsd': 'Linux', 'solaris': 'Linux', 'aix': 'Linux'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/** Nama browser yang diterima server pada companion_platform_display */
|
|
49
|
+
export const PAIRING_ACCEPTED_BROWSERS = Object.freeze([
|
|
50
|
+
'Chrome', 'Chromium', 'Firefox', 'Safari', 'Edge', 'Opera', 'Brave', 'Vivaldi', 'Arc'
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const lower = (s) => String(s ?? '').trim().toLowerCase();
|
|
54
|
+
|
|
55
|
+
/** OS untuk companion_platform_display: kembalikan nama valid, atau null bila tidak dikenal */
|
|
56
|
+
export function resolvePairingOs(os) {
|
|
57
|
+
const l = lower(os);
|
|
58
|
+
if (!l) return null;
|
|
59
|
+
const exact = PAIRING_ACCEPTED_OS.find((o) => o.toLowerCase() === l);
|
|
60
|
+
if (exact) return exact;
|
|
61
|
+
if (PAIRING_OS_FALLBACK[l]) return PAIRING_OS_FALLBACK[l];
|
|
62
|
+
// heuristik: apa pun yang menyebut linux/ubuntu/debian → Linux; mac → Mac OS; win → Windows
|
|
63
|
+
if (/\blinux\b|ubuntu|debian|fedora|arch/.test(l)) return 'Linux';
|
|
64
|
+
if (/\bmac|darwin|osx/.test(l)) return 'Mac OS';
|
|
65
|
+
if (/windows|win\d/.test(l)) return 'Windows';
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Browser untuk companion_platform_display: "Desktop"/nama tak dikenal → Chrome */
|
|
70
|
+
export function resolvePairingBrowser(browser) {
|
|
71
|
+
const l = lower(browser);
|
|
72
|
+
const exact = PAIRING_ACCEPTED_BROWSERS.find((b) => b.toLowerCase() === l);
|
|
73
|
+
if (exact) return exact;
|
|
74
|
+
return 'Chrome';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Turunkan companion_platform_display yang PASTI lolos validasi dari deskripsi
|
|
79
|
+
* browser [os, browser, version]. Contoh:
|
|
80
|
+
* ['Arch Linux','Chrome','6.16'] → 'Chrome (Linux)'
|
|
81
|
+
* ['Mac OS','Desktop','14.4.1'] → 'Chrome (Mac OS)'
|
|
82
|
+
* ['BibzWhats','Chrome','1.2.0'] → 'Chrome (Linux)' (di host Linux) — nama produk tidak valid
|
|
83
|
+
* ['Mac OS','Safari','14.4.1'] → 'Safari (Mac OS)'
|
|
84
|
+
*/
|
|
85
|
+
export function derivePairingDisplay(browser, hostOs = platform()) {
|
|
86
|
+
const [os, name] = browser || [];
|
|
87
|
+
const resolvedOs = resolvePairingOs(os) ?? resolvePairingOs(hostOsLabel(hostOs)) ?? 'Linux';
|
|
88
|
+
return `${resolvePairingBrowser(name)} (${resolvedOs})`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Apakah string display ini akan diterima server (berdasarkan tabel di atas)? */
|
|
92
|
+
export function isPairingDisplayAccepted(display) {
|
|
93
|
+
const m = /^(.+?) \((.+)\)$/.exec(String(display ?? '').trim());
|
|
94
|
+
if (!m) return false;
|
|
95
|
+
const browserOk = PAIRING_ACCEPTED_BROWSERS.some((b) => b.toLowerCase() === lower(m[1]));
|
|
96
|
+
const osOk = PAIRING_ACCEPTED_OS.some((o) => o.toLowerCase() === lower(m[2]));
|
|
97
|
+
return browserOk && osOk;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Label OS host: 'Mac OS' | 'Windows' | 'Arch Linux' | 'Ubuntu' | ... (dibaca dari /etc/os-release) */
|
|
101
|
+
export function hostOsLabel(hostOs = platform()) {
|
|
102
|
+
if (hostOs === 'darwin') return 'Mac OS';
|
|
103
|
+
if (hostOs === 'win32') return 'Windows';
|
|
104
|
+
if (hostOs === 'android') return 'Android';
|
|
105
|
+
if (hostOs === 'linux') return linuxDistroName() || 'Linux';
|
|
106
|
+
return 'Linux';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Nama distro dari /etc/os-release (NAME=), tanpa embel "GNU/Linux". null bila tak terbaca. */
|
|
110
|
+
export function linuxDistroName(file = '/etc/os-release') {
|
|
111
|
+
try {
|
|
112
|
+
const txt = fs.readFileSync(file, 'utf8');
|
|
113
|
+
const m = /^NAME="?([^"\n]+)"?/m.exec(txt);
|
|
114
|
+
if (!m) return null;
|
|
115
|
+
return m[1].replace(/\s+GNU\/Linux/i, '').trim();
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Versi OS host yang layak tampil di browser[2] (mis. '14.4.1' / '6.16.4-arch1-1') */
|
|
122
|
+
export function hostOsVersion() {
|
|
123
|
+
return release();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Verifikasi sebuah konfigurasi identitas SEBELUM konek. Mengembalikan daftar
|
|
128
|
+
* catatan {level:'warn'|'info', message}. Dipakai createBibzWhats untuk memberi
|
|
129
|
+
* peringatan dini, dan bisa dipanggil sendiri oleh pemakai.
|
|
130
|
+
*/
|
|
131
|
+
export function lintIdentity({ browser, companionPlatformDisplay, syncFullHistory } = {}) {
|
|
132
|
+
const notes = [];
|
|
133
|
+
if (!Array.isArray(browser) || browser.length < 2) {
|
|
134
|
+
notes.push({ level: 'warn', message: 'browser harus [os, browser, version]' });
|
|
135
|
+
return notes;
|
|
136
|
+
}
|
|
137
|
+
const [os, name] = browser;
|
|
138
|
+
if (companionPlatformDisplay && !isPairingDisplayAccepted(companionPlatformDisplay)) {
|
|
139
|
+
notes.push({ level: 'warn', message: `companionPlatformDisplay '${companionPlatformDisplay}' tidak ada di daftar yang diterima server (pairing kode akan 400). Disarankan: '${derivePairingDisplay(browser)}'.` });
|
|
140
|
+
}
|
|
141
|
+
if (!companionPlatformDisplay && !resolvePairingOs(os)) {
|
|
142
|
+
notes.push({ level: 'info', message: `OS '${os}' tidak dikenal server untuk pairing kode → display otomatis '${derivePairingDisplay(browser)}' (nama di daftar perangkat tetap '${os}').` });
|
|
143
|
+
}
|
|
144
|
+
if (name === 'Desktop' && syncFullHistory && os === 'Mac OS') {
|
|
145
|
+
notes.push({ level: 'info', message: 'Mac OS Desktop + syncFullHistory: webSubPlatform DARWIN ditolak server (428) → diturunkan ke WEB_BROWSER; riwayat penuh tetap diminta lewat requireFullSync.' });
|
|
146
|
+
}
|
|
147
|
+
return notes;
|
|
148
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SignalDataSet, SignalDataTypeMap, SignalKeyStore } from '../Types/index.js';
|
|
2
|
+
import type { ILogger } from './logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages pre-key operations with proper concurrency control
|
|
5
|
+
*/
|
|
6
|
+
export declare class PreKeyManager {
|
|
7
|
+
private readonly store;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
private readonly queues;
|
|
10
|
+
constructor(store: SignalKeyStore, logger: ILogger);
|
|
11
|
+
/**
|
|
12
|
+
* Get or create a queue for a specific key type
|
|
13
|
+
*/
|
|
14
|
+
private getQueue;
|
|
15
|
+
/**
|
|
16
|
+
* Process pre-key operations (updates and deletions)
|
|
17
|
+
*/
|
|
18
|
+
processOperations(data: SignalDataSet, keyType: keyof SignalDataTypeMap, transactionCache: SignalDataSet, mutations: SignalDataSet, isInTransaction: boolean): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Process deletions with validation
|
|
21
|
+
*/
|
|
22
|
+
private processDeletions;
|
|
23
|
+
/**
|
|
24
|
+
* Validate and process pre-key deletions outside transactions
|
|
25
|
+
*/
|
|
26
|
+
validateDeletions(data: SignalDataSet, keyType: keyof SignalDataTypeMap): Promise<void>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import PQueue from 'p-queue';
|
|
2
|
+
/**
|
|
3
|
+
* Manages pre-key operations with proper concurrency control
|
|
4
|
+
*/
|
|
5
|
+
export class PreKeyManager {
|
|
6
|
+
constructor(store, logger) {
|
|
7
|
+
this.store = store;
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
this.queues = new Map();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get or create a queue for a specific key type
|
|
13
|
+
*/
|
|
14
|
+
getQueue(keyType) {
|
|
15
|
+
if (!this.queues.has(keyType)) {
|
|
16
|
+
this.queues.set(keyType, new PQueue({ concurrency: 1 }));
|
|
17
|
+
}
|
|
18
|
+
return this.queues.get(keyType);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Process pre-key operations (updates and deletions)
|
|
22
|
+
*/
|
|
23
|
+
async processOperations(data, keyType, transactionCache, mutations, isInTransaction) {
|
|
24
|
+
const keyData = data[keyType];
|
|
25
|
+
if (!keyData)
|
|
26
|
+
return;
|
|
27
|
+
return this.getQueue(keyType).add(async () => {
|
|
28
|
+
// Ensure structures exist
|
|
29
|
+
transactionCache[keyType] = transactionCache[keyType] || {};
|
|
30
|
+
mutations[keyType] = mutations[keyType] || {};
|
|
31
|
+
// Separate deletions from updates
|
|
32
|
+
const deletions = [];
|
|
33
|
+
const updates = {};
|
|
34
|
+
for (const keyId in keyData) {
|
|
35
|
+
if (keyData[keyId] === null) {
|
|
36
|
+
deletions.push(keyId);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
updates[keyId] = keyData[keyId];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Process updates (no validation needed)
|
|
43
|
+
if (Object.keys(updates).length > 0) {
|
|
44
|
+
Object.assign(transactionCache[keyType], updates);
|
|
45
|
+
Object.assign(mutations[keyType], updates);
|
|
46
|
+
}
|
|
47
|
+
// Process deletions with validation
|
|
48
|
+
if (deletions.length > 0) {
|
|
49
|
+
await this.processDeletions(keyType, deletions, transactionCache, mutations, isInTransaction);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Process deletions with validation
|
|
55
|
+
*/
|
|
56
|
+
async processDeletions(keyType, ids, transactionCache, mutations, isInTransaction) {
|
|
57
|
+
if (isInTransaction) {
|
|
58
|
+
// In transaction, only allow deletion if key exists in cache
|
|
59
|
+
for (const keyId of ids) {
|
|
60
|
+
if (transactionCache[keyType]?.[keyId]) {
|
|
61
|
+
transactionCache[keyType][keyId] = null;
|
|
62
|
+
mutations[keyType][keyId] = null;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.logger.warn(`Skipping deletion of non-existent ${keyType} in transaction: ${keyId}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Outside transaction, validate against store
|
|
71
|
+
const existingKeys = await this.store.get(keyType, ids);
|
|
72
|
+
for (const keyId of ids) {
|
|
73
|
+
if (existingKeys[keyId]) {
|
|
74
|
+
transactionCache[keyType][keyId] = null;
|
|
75
|
+
mutations[keyType][keyId] = null;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate and process pre-key deletions outside transactions
|
|
85
|
+
*/
|
|
86
|
+
async validateDeletions(data, keyType) {
|
|
87
|
+
const keyData = data[keyType];
|
|
88
|
+
if (!keyData)
|
|
89
|
+
return;
|
|
90
|
+
return this.getQueue(keyType).add(async () => {
|
|
91
|
+
// Find all deletion requests
|
|
92
|
+
const deletionIds = Object.keys(keyData).filter(id => keyData[id] === null);
|
|
93
|
+
if (deletionIds.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
// Validate deletions
|
|
96
|
+
const existingKeys = await this.store.get(keyType, deletionIds);
|
|
97
|
+
for (const keyId of deletionIds) {
|
|
98
|
+
if (!existingKeys[keyId]) {
|
|
99
|
+
this.logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`);
|
|
100
|
+
delete data[keyType][keyId];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { proto } from '../../WAProto/index.js';
|
|
2
|
+
import type { AuthenticationCreds, BaileysEventEmitter, CacheStore, SignalKeyStoreWithTransaction, SignalRepositoryWithLIDStore, SocketConfig, WAMessage, WAMessageKey } from '../Types/index.js';
|
|
3
|
+
import type { ILogger } from './logger.js';
|
|
4
|
+
type ProcessMessageContext = {
|
|
5
|
+
shouldProcessHistoryMsg: boolean;
|
|
6
|
+
placeholderResendCache?: CacheStore;
|
|
7
|
+
creds: AuthenticationCreds;
|
|
8
|
+
keyStore: SignalKeyStoreWithTransaction;
|
|
9
|
+
ev: BaileysEventEmitter;
|
|
10
|
+
logger?: ILogger;
|
|
11
|
+
options: RequestInit;
|
|
12
|
+
signalRepository: SignalRepositoryWithLIDStore;
|
|
13
|
+
getMessage: SocketConfig['getMessage'];
|
|
14
|
+
};
|
|
15
|
+
/** Cleans a received message to further processing */
|
|
16
|
+
export declare const cleanMessage: (message: WAMessage, meId: string, meLid: string) => void;
|
|
17
|
+
export declare const isRealMessage: (message: WAMessage) => boolean;
|
|
18
|
+
export declare const shouldIncrementChatUnread: (message: WAMessage) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get the ID of the chat from the given key.
|
|
21
|
+
* Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
|
|
22
|
+
*/
|
|
23
|
+
export declare const getChatId: ({ remoteJid, participant, fromMe }: WAMessageKey) => string;
|
|
24
|
+
type PollContext = {
|
|
25
|
+
/** normalised jid of the person that created the poll */
|
|
26
|
+
pollCreatorJid: string;
|
|
27
|
+
/** ID of the poll creation message */
|
|
28
|
+
pollMsgId: string;
|
|
29
|
+
/** poll creation message enc key */
|
|
30
|
+
pollEncKey: Uint8Array;
|
|
31
|
+
/** jid of the person that voted */
|
|
32
|
+
voterJid: string;
|
|
33
|
+
};
|
|
34
|
+
type EventContext = {
|
|
35
|
+
/** normalised jid of the person that created the event */
|
|
36
|
+
eventCreatorJid: string;
|
|
37
|
+
/** ID of the event creation message */
|
|
38
|
+
eventMsgId: string;
|
|
39
|
+
/** event creation message enc key */
|
|
40
|
+
eventEncKey: Uint8Array;
|
|
41
|
+
/** jid of the person that responded */
|
|
42
|
+
responderJid: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Decrypt a poll vote
|
|
46
|
+
* @param vote encrypted vote
|
|
47
|
+
* @param ctx additional info about the poll required for decryption
|
|
48
|
+
* @returns list of SHA256 options
|
|
49
|
+
*/
|
|
50
|
+
export declare function decryptPollVote({ encPayload, encIv }: proto.Message.IPollEncValue, { pollCreatorJid, pollMsgId, pollEncKey, voterJid }: PollContext): proto.Message.PollVoteMessage;
|
|
51
|
+
/**
|
|
52
|
+
* Decrypt an event response
|
|
53
|
+
* @param response encrypted event response
|
|
54
|
+
* @param ctx additional info about the event required for decryption
|
|
55
|
+
* @returns event response message
|
|
56
|
+
*/
|
|
57
|
+
export declare function decryptEventResponse({ encPayload, encIv }: proto.Message.IPollEncValue, { eventCreatorJid, eventMsgId, eventEncKey, responderJid }: EventContext): proto.Message.EventResponseMessage;
|
|
58
|
+
declare const processMessage: (message: WAMessage, { shouldProcessHistoryMsg, placeholderResendCache, ev, creds, signalRepository, keyStore, logger, options, getMessage }: ProcessMessageContext) => Promise<void>;
|
|
59
|
+
export default processMessage;
|