@alannxd/baileys 6.0.6 → 6.0.9
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/LICENSE +1 -1
- package/README.md +341 -286
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +6902 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +79257 -0
- package/WAProto/index.js +205861 -60565
- package/engine-requirements.js +1 -1
- package/lib/Defaults/index.js +119 -136
- package/lib/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.js +7 -41
- package/lib/Signal/Group/group_cipher.js +37 -51
- package/lib/Signal/Group/index.js +12 -57
- package/lib/Signal/Group/keyhelper.js +7 -44
- package/lib/Signal/Group/sender-chain-key.js +7 -15
- package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
- package/lib/Signal/Group/sender-key-message.js +9 -12
- package/lib/Signal/Group/sender-key-name.js +2 -5
- package/lib/Signal/Group/sender-key-record.js +9 -21
- package/lib/Signal/Group/sender-key-state.js +27 -42
- package/lib/Signal/Group/sender-message-key.js +4 -7
- package/lib/Signal/libsignal.js +347 -90
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.js +3 -19
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/chats.js +627 -427
- package/lib/Socket/communities.js +90 -80
- package/lib/Socket/groups.js +154 -161
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.js +315 -469
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.js +1150 -799
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.js +152 -204
- package/lib/Socket/socket.js +544 -313
- package/lib/Store/index.js +10 -10
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +43 -41
- package/lib/Store/make-in-memory-store.js +112 -341
- package/lib/Store/make-ordered-dictionary.js +14 -20
- package/lib/Store/object-repository.js +11 -6
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +3 -5
- package/lib/Types/LabelAssociation.js +3 -5
- package/lib/Types/Message.js +11 -9
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/State.js +56 -2
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +15 -31
- package/lib/Utils/auth-utils.js +239 -143
- package/lib/Utils/browser-utils.js +48 -0
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/chat-utils.js +396 -253
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.js +57 -90
- package/lib/Utils/decode-wa-message.js +236 -84
- package/lib/Utils/event-buffer.js +185 -77
- package/lib/Utils/generics.js +189 -209
- package/lib/Utils/history.js +93 -55
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +23 -33
- package/lib/Utils/link-preview.js +16 -24
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/message-composer.js +273 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.js +451 -482
- package/lib/Utils/messages.js +795 -369
- package/lib/Utils/noise-handler.js +145 -99
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.js +120 -72
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/use-multi-file-auth-state.js +29 -27
- package/lib/Utils/validate-connection.js +73 -99
- package/lib/WABinary/constants.js +1281 -20
- package/lib/WABinary/decode.js +52 -42
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/generic-utils.js +55 -49
- package/lib/WABinary/index.js +6 -21
- package/lib/WABinary/jid-utils.js +76 -40
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +2 -5
- package/lib/WAM/constants.js +19071 -11568
- package/lib/WAM/encode.js +17 -22
- package/lib/WAM/index.js +4 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncUser.js +10 -5
- package/lib/WAUSync/index.js +4 -19
- package/lib/index.js +13 -36
- package/package.json +85 -51
- package/WAProto/fix-import.js +0 -29
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Defaults/index.d.ts +0 -53
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -14
- package/lib/Signal/Group/group_cipher.d.ts +0 -17
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -10
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
- package/lib/Signal/Group/sender-key-message.d.ts +0 -18
- package/lib/Signal/Group/sender-key-name.d.ts +0 -17
- package/lib/Signal/Group/sender-key-record.d.ts +0 -30
- package/lib/Signal/Group/sender-key-state.d.ts +0 -38
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -3
- package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
- package/lib/Socket/Client/abstract-socket-client.js +0 -13
- package/lib/Socket/Client/index.d.ts +0 -3
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/Client/web-socket-client.js +0 -62
- package/lib/Socket/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -267
- package/lib/Socket/communities.d.ts +0 -180
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/luxu.d.ts +0 -266
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -183
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/socket.d.ts +0 -44
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Socket/usync.js +0 -70
- package/lib/Store/index.d.ts +0 -3
- package/lib/Store/make-cache-manager-store.d.ts +0 -13
- package/lib/Store/make-in-memory-store.d.ts +0 -118
- package/lib/Store/make-ordered-dictionary.d.ts +0 -13
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -110
- package/lib/Types/Call.d.ts +0 -13
- package/lib/Types/Chat.d.ts +0 -102
- package/lib/Types/Contact.d.ts +0 -19
- package/lib/Types/Events.d.ts +0 -157
- package/lib/Types/GroupMetadata.d.ts +0 -55
- package/lib/Types/Label.d.ts +0 -35
- package/lib/Types/LabelAssociation.d.ts +0 -29
- package/lib/Types/Message.d.ts +0 -273
- package/lib/Types/Newsletter.d.ts +0 -103
- package/lib/Types/Newsletter.js +0 -38
- package/lib/Types/Product.d.ts +0 -78
- package/lib/Types/Signal.d.ts +0 -57
- package/lib/Types/Socket.d.ts +0 -111
- package/lib/Types/State.d.ts +0 -27
- package/lib/Types/USync.d.ts +0 -25
- package/lib/Types/index.d.ts +0 -57
- package/lib/Utils/auth-utils.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/baileys-event-stream.js +0 -63
- package/lib/Utils/business.d.ts +0 -22
- package/lib/Utils/chat-utils.d.ts +0 -71
- package/lib/Utils/crypto.d.ts +0 -41
- package/lib/Utils/decode-wa-message.d.ts +0 -19
- package/lib/Utils/event-buffer.d.ts +0 -35
- package/lib/Utils/generics.d.ts +0 -92
- package/lib/Utils/history.d.ts +0 -15
- package/lib/Utils/index.d.ts +0 -17
- package/lib/Utils/link-preview.d.ts +0 -21
- package/lib/Utils/logger.d.ts +0 -4
- package/lib/Utils/lt-hash.d.ts +0 -12
- package/lib/Utils/make-mutex.d.ts +0 -7
- package/lib/Utils/messages-media.d.ts +0 -116
- package/lib/Utils/messages.d.ts +0 -77
- package/lib/Utils/noise-handler.d.ts +0 -21
- package/lib/Utils/process-message.d.ts +0 -41
- package/lib/Utils/signal.d.ts +0 -32
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/validate-connection.d.ts +0 -11
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -17
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -31
- package/lib/WABinary/types.d.ts +0 -18
- package/lib/WAM/BinaryInfo.d.ts +0 -17
- package/lib/WAM/constants.d.ts +0 -38
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
- package/lib/WAUSync/Protocols/index.d.ts +0 -4
- package/lib/WAUSync/USyncQuery.d.ts +0 -28
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/index.d.ts +0 -12
package/lib/Utils/chat-utils.js
CHANGED
|
@@ -1,51 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const messages_media_1 = require("./messages-media");
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { expandAppStateKeys } from 'whatsapp-rust-bridge';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { LabelAssociationType } from '../Types/LabelAssociation.js';
|
|
5
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, jidNormalizedUser } from '../WABinary/index.js';
|
|
6
|
+
import { aesDecrypt, aesEncrypt, hmacSign } from './crypto.js';
|
|
7
|
+
import { toNumber } from './generics.js';
|
|
8
|
+
import { LT_HASH_ANTI_TAMPERING } from './lt-hash.js';
|
|
9
|
+
import { downloadContentFromMessage } from './messages-media.js';
|
|
10
|
+
import { emitSyncActionResults, processContactAction } from './sync-action-utils.js';
|
|
12
11
|
const mutationKeys = (keydata) => {
|
|
13
|
-
const
|
|
12
|
+
const keys = expandAppStateKeys(keydata);
|
|
14
13
|
return {
|
|
15
|
-
indexKey:
|
|
16
|
-
valueEncryptionKey:
|
|
17
|
-
valueMacKey:
|
|
18
|
-
snapshotMacKey:
|
|
19
|
-
patchMacKey:
|
|
14
|
+
indexKey: keys.indexKey,
|
|
15
|
+
valueEncryptionKey: keys.valueEncryptionKey,
|
|
16
|
+
valueMacKey: keys.valueMacKey,
|
|
17
|
+
snapshotMacKey: keys.snapshotMacKey,
|
|
18
|
+
patchMacKey: keys.patchMacKey
|
|
20
19
|
};
|
|
21
20
|
};
|
|
22
21
|
const generateMac = (operation, data, keyId, key) => {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const keyData = getKeyData();
|
|
37
|
-
const last = Buffer.alloc(8); // 8 bytes
|
|
38
|
-
last.set([keyData.length], last.length - 1);
|
|
39
|
-
const total = Buffer.concat([keyData, data, last]);
|
|
40
|
-
const hmac = (0, crypto_1.hmacSign)(total, key, 'sha512');
|
|
41
|
-
return hmac.slice(0, 32);
|
|
22
|
+
const opByte = operation === proto.SyncdMutation.SyncdOperation.SET ? 0x01 : 0x02;
|
|
23
|
+
const keyIdBuffer = typeof keyId === 'string' ? Buffer.from(keyId, 'base64') : keyId;
|
|
24
|
+
const keyData = new Uint8Array(1 + keyIdBuffer.length);
|
|
25
|
+
keyData[0] = opByte;
|
|
26
|
+
keyData.set(keyIdBuffer, 1);
|
|
27
|
+
const last = new Uint8Array(8);
|
|
28
|
+
last[7] = keyData.length;
|
|
29
|
+
const total = new Uint8Array(keyData.length + data.length + last.length);
|
|
30
|
+
total.set(keyData, 0);
|
|
31
|
+
total.set(data, keyData.length);
|
|
32
|
+
total.set(last, keyData.length + data.length);
|
|
33
|
+
const hmac = hmacSign(total, key, 'sha512');
|
|
34
|
+
return hmac.subarray(0, 32);
|
|
42
35
|
};
|
|
43
36
|
const to64BitNetworkOrder = (e) => {
|
|
44
37
|
const buff = Buffer.alloc(8);
|
|
45
38
|
buff.writeUint32BE(e, 4);
|
|
46
39
|
return buff;
|
|
47
40
|
};
|
|
48
|
-
const makeLtHashGenerator = ({ indexValueMap, hash }) => {
|
|
41
|
+
export const makeLtHashGenerator = ({ indexValueMap, hash }) => {
|
|
49
42
|
indexValueMap = { ...indexValueMap };
|
|
50
43
|
const addBuffs = [];
|
|
51
44
|
const subBuffs = [];
|
|
@@ -53,71 +46,85 @@ const makeLtHashGenerator = ({ indexValueMap, hash }) => {
|
|
|
53
46
|
mix: ({ indexMac, valueMac, operation }) => {
|
|
54
47
|
const indexMacBase64 = Buffer.from(indexMac).toString('base64');
|
|
55
48
|
const prevOp = indexValueMap[indexMacBase64];
|
|
56
|
-
if (operation ===
|
|
49
|
+
if (operation === proto.SyncdMutation.SyncdOperation.REMOVE) {
|
|
57
50
|
if (!prevOp) {
|
|
58
|
-
|
|
51
|
+
// WA Web does not throw here — it logs a warning and skips the subtract.
|
|
52
|
+
// The missing REMOVE will cause an LTHash mismatch, which is handled
|
|
53
|
+
// by the MAC validation layer (snapshot recovery or retry).
|
|
54
|
+
return;
|
|
59
55
|
}
|
|
60
56
|
// remove from index value mac, since this mutation is erased
|
|
61
57
|
delete indexValueMap[indexMacBase64];
|
|
62
58
|
}
|
|
63
59
|
else {
|
|
64
|
-
addBuffs.push(
|
|
60
|
+
addBuffs.push(valueMac);
|
|
65
61
|
// add this index into the history map
|
|
66
62
|
indexValueMap[indexMacBase64] = { valueMac };
|
|
67
63
|
}
|
|
68
64
|
if (prevOp) {
|
|
69
|
-
subBuffs.push(
|
|
65
|
+
subBuffs.push(prevOp.valueMac);
|
|
70
66
|
}
|
|
71
67
|
},
|
|
72
68
|
finish: () => {
|
|
73
|
-
const
|
|
74
|
-
const result = lt_hash_1.LT_HASH_ANTI_TAMPERING.subtractThenAdd(hashArrayBuffer, addBuffs, subBuffs);
|
|
75
|
-
const buffer = Buffer.from(result);
|
|
69
|
+
const result = LT_HASH_ANTI_TAMPERING.subtractThenAdd(hash, subBuffs, addBuffs);
|
|
76
70
|
return {
|
|
77
|
-
hash:
|
|
71
|
+
hash: Buffer.from(result),
|
|
78
72
|
indexValueMap
|
|
79
73
|
};
|
|
80
74
|
}
|
|
81
75
|
};
|
|
82
76
|
};
|
|
83
77
|
const generateSnapshotMac = (lthash, version, name, key) => {
|
|
84
|
-
const total = Buffer.concat([
|
|
85
|
-
|
|
86
|
-
to64BitNetworkOrder(version),
|
|
87
|
-
Buffer.from(name, 'utf-8')
|
|
88
|
-
]);
|
|
89
|
-
return (0, crypto_1.hmacSign)(total, key, 'sha256');
|
|
78
|
+
const total = Buffer.concat([lthash, to64BitNetworkOrder(version), Buffer.from(name, 'utf-8')]);
|
|
79
|
+
return hmacSign(total, key, 'sha256');
|
|
90
80
|
};
|
|
91
81
|
const generatePatchMac = (snapshotMac, valueMacs, version, type, key) => {
|
|
92
|
-
const total = Buffer.concat([
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
82
|
+
const total = Buffer.concat([snapshotMac, ...valueMacs, to64BitNetworkOrder(version), Buffer.from(type, 'utf-8')]);
|
|
83
|
+
return hmacSign(total, key);
|
|
84
|
+
};
|
|
85
|
+
export const newLTHashState = () => ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} });
|
|
86
|
+
export const ensureLTHashStateVersion = (state) => {
|
|
87
|
+
if (typeof state.version !== 'number' || isNaN(state.version)) {
|
|
88
|
+
state.version = 0;
|
|
89
|
+
}
|
|
90
|
+
return state;
|
|
91
|
+
};
|
|
92
|
+
export const MAX_SYNC_ATTEMPTS = 2;
|
|
93
|
+
/**
|
|
94
|
+
* Check if an error is a missing app state sync key.
|
|
95
|
+
* WA Web treats these as "Blocked" (waits for key arrival), not fatal.
|
|
96
|
+
* In Baileys we retry with a snapshot which may use a different key.
|
|
97
|
+
*/
|
|
98
|
+
export const isMissingKeyError = (error) => {
|
|
99
|
+
return error?.data?.isMissingKey === true;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Determines if an app state sync error is unrecoverable.
|
|
103
|
+
* TypeError indicates a WASM crash; otherwise we give up after MAX_SYNC_ATTEMPTS.
|
|
104
|
+
* Missing keys are NOT checked here — they are handled separately as "Blocked".
|
|
105
|
+
*/
|
|
106
|
+
export const isAppStateSyncIrrecoverable = (error, attempts) => {
|
|
107
|
+
return attempts >= MAX_SYNC_ATTEMPTS || error?.name === 'TypeError';
|
|
99
108
|
};
|
|
100
|
-
const
|
|
101
|
-
exports.newLTHashState = newLTHashState;
|
|
102
|
-
const encodeSyncdPatch = async ({ type, index, syncAction, apiVersion, operation }, myAppStateKeyId, state, getAppStateSyncKey) => {
|
|
109
|
+
export const encodeSyncdPatch = async ({ type, index, syncAction, apiVersion, operation }, myAppStateKeyId, state, getAppStateSyncKey) => {
|
|
103
110
|
const key = !!myAppStateKeyId ? await getAppStateSyncKey(myAppStateKeyId) : undefined;
|
|
104
111
|
if (!key) {
|
|
105
|
-
throw new
|
|
112
|
+
throw new Boom(`myAppStateKey ("${myAppStateKeyId}") not present`, { data: { isMissingKey: true } });
|
|
106
113
|
}
|
|
107
114
|
const encKeyId = Buffer.from(myAppStateKeyId, 'base64');
|
|
108
115
|
state = { ...state, indexValueMap: { ...state.indexValueMap } };
|
|
109
116
|
const indexBuffer = Buffer.from(JSON.stringify(index));
|
|
110
|
-
const dataProto =
|
|
117
|
+
const dataProto = proto.SyncActionData.fromObject({
|
|
111
118
|
index: indexBuffer,
|
|
112
119
|
value: syncAction,
|
|
113
120
|
padding: new Uint8Array(0),
|
|
114
121
|
version: apiVersion
|
|
115
122
|
});
|
|
116
|
-
const encoded =
|
|
123
|
+
const encoded = proto.SyncActionData.encode(dataProto).finish();
|
|
117
124
|
const keyValue = mutationKeys(key.keyData);
|
|
118
|
-
const encValue =
|
|
125
|
+
const encValue = aesEncrypt(encoded, keyValue.valueEncryptionKey);
|
|
119
126
|
const valueMac = generateMac(operation, encValue, encKeyId, keyValue.valueMacKey);
|
|
120
|
-
const indexMac =
|
|
127
|
+
const indexMac = hmacSign(indexBuffer, keyValue.indexKey);
|
|
121
128
|
// update LT hash
|
|
122
129
|
const generator = makeLtHashGenerator(state);
|
|
123
130
|
generator.mix({ indexMac, valueMac, operation });
|
|
@@ -147,33 +154,52 @@ const encodeSyncdPatch = async ({ type, index, syncAction, apiVersion, operation
|
|
|
147
154
|
state.indexValueMap[base64Index] = { valueMac };
|
|
148
155
|
return { patch, state };
|
|
149
156
|
};
|
|
150
|
-
|
|
151
|
-
const decodeSyncdMutations = async (msgMutations, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
|
|
157
|
+
export const decodeSyncdMutations = async (msgMutations, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
|
|
152
158
|
const ltGenerator = makeLtHashGenerator(initialState);
|
|
159
|
+
const derivedKeyCache = new Map();
|
|
153
160
|
// indexKey used to HMAC sign record.index.blob
|
|
154
161
|
// valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32]
|
|
155
162
|
// the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId
|
|
156
163
|
for (const msgMutation of msgMutations) {
|
|
157
164
|
// if it's a syncdmutation, get the operation property
|
|
158
165
|
// otherwise, if it's only a record -- it'll be a SET mutation
|
|
159
|
-
const operation = 'operation' in msgMutation ? msgMutation.operation :
|
|
160
|
-
const record =
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdOperation.SET;
|
|
167
|
+
const record = 'record' in msgMutation && !!msgMutation.record ? msgMutation.record : msgMutation;
|
|
168
|
+
let key;
|
|
169
|
+
try {
|
|
170
|
+
key = await getKey(record.keyId.id);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
// Missing-key errors must propagate so the orchestrator can park the
|
|
174
|
+
// collection (Blocked) and retry when APP_STATE_SYNC_KEY_SHARE arrives.
|
|
175
|
+
// Other errors → individual record corruption, skip and keep going.
|
|
176
|
+
if (isMissingKeyError(err))
|
|
177
|
+
throw err;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const content = record.value.blob;
|
|
181
|
+
const encContent = content.subarray(0, -32);
|
|
182
|
+
const ogValueMac = content.subarray(-32);
|
|
165
183
|
if (validateMacs) {
|
|
166
184
|
const contentHmac = generateMac(operation, encContent, record.keyId.id, key.valueMacKey);
|
|
167
185
|
if (Buffer.compare(contentHmac, ogValueMac) !== 0) {
|
|
168
|
-
|
|
186
|
+
// HMAC verification failed — skip this record
|
|
187
|
+
continue;
|
|
169
188
|
}
|
|
170
189
|
}
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
let result;
|
|
191
|
+
try {
|
|
192
|
+
result = aesDecrypt(encContent, key.valueEncryptionKey);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// decrypt failed — skip this record instead of aborting
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const syncAction = proto.SyncActionData.decode(result);
|
|
173
199
|
if (validateMacs) {
|
|
174
|
-
const hmac =
|
|
200
|
+
const hmac = hmacSign(syncAction.index, key.indexKey);
|
|
175
201
|
if (Buffer.compare(hmac, record.index.blob) !== 0) {
|
|
176
|
-
throw new
|
|
202
|
+
throw new Boom('HMAC index verification failed');
|
|
177
203
|
}
|
|
178
204
|
}
|
|
179
205
|
const indexStr = Buffer.from(syncAction.index).toString();
|
|
@@ -187,40 +213,46 @@ const decodeSyncdMutations = async (msgMutations, initialState, getAppStateSyncK
|
|
|
187
213
|
return ltGenerator.finish();
|
|
188
214
|
async function getKey(keyId) {
|
|
189
215
|
const base64Key = Buffer.from(keyId).toString('base64');
|
|
216
|
+
const cached = derivedKeyCache.get(base64Key);
|
|
217
|
+
if (cached) {
|
|
218
|
+
return cached;
|
|
219
|
+
}
|
|
190
220
|
const keyEnc = await getAppStateSyncKey(base64Key);
|
|
191
221
|
if (!keyEnc) {
|
|
192
|
-
throw new
|
|
222
|
+
throw new Boom(`failed to find key "${base64Key}" to decode mutation`, {
|
|
223
|
+
data: { isMissingKey: true, msgMutations }
|
|
224
|
+
});
|
|
193
225
|
}
|
|
194
|
-
|
|
226
|
+
const keys = mutationKeys(keyEnc.keyData);
|
|
227
|
+
derivedKeyCache.set(base64Key, keys);
|
|
228
|
+
return keys;
|
|
195
229
|
}
|
|
196
230
|
};
|
|
197
|
-
|
|
198
|
-
const decodeSyncdPatch = async (msg, name, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
|
|
231
|
+
export const decodeSyncdPatch = async (msg, name, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
|
|
199
232
|
if (validateMacs) {
|
|
200
233
|
const base64Key = Buffer.from(msg.keyId.id).toString('base64');
|
|
201
234
|
const mainKeyObj = await getAppStateSyncKey(base64Key);
|
|
202
235
|
if (!mainKeyObj) {
|
|
203
|
-
throw new
|
|
236
|
+
throw new Boom(`failed to find key "${base64Key}" to decode patch`, { data: { isMissingKey: true, msg } });
|
|
204
237
|
}
|
|
205
238
|
const mainKey = mutationKeys(mainKeyObj.keyData);
|
|
206
239
|
const mutationmacs = msg.mutations.map(mutation => mutation.record.value.blob.slice(-32));
|
|
207
|
-
const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs,
|
|
240
|
+
const patchMac = generatePatchMac(msg.snapshotMac, mutationmacs, toNumber(msg.version.version), name, mainKey.patchMacKey);
|
|
208
241
|
if (Buffer.compare(patchMac, msg.patchMac) !== 0) {
|
|
209
|
-
throw new
|
|
242
|
+
throw new Boom('Invalid patch mac');
|
|
210
243
|
}
|
|
211
244
|
}
|
|
212
|
-
const result = await
|
|
245
|
+
const result = await decodeSyncdMutations(msg.mutations, initialState, getAppStateSyncKey, onMutation, validateMacs);
|
|
213
246
|
return result;
|
|
214
247
|
};
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
const collectionNodes = (0, WABinary_1.getBinaryNodeChildren)(syncNode, 'collection');
|
|
248
|
+
export const extractSyncdPatches = async (result, options) => {
|
|
249
|
+
const syncNode = getBinaryNodeChild(result, 'sync');
|
|
250
|
+
const collectionNodes = getBinaryNodeChildren(syncNode, 'collection');
|
|
219
251
|
const final = {};
|
|
220
252
|
await Promise.all(collectionNodes.map(async (collectionNode) => {
|
|
221
|
-
const patchesNode =
|
|
222
|
-
const patches =
|
|
223
|
-
const snapshotNode =
|
|
253
|
+
const patchesNode = getBinaryNodeChild(collectionNode, 'patches');
|
|
254
|
+
const patches = getBinaryNodeChildren(patchesNode || collectionNode, 'patch');
|
|
255
|
+
const snapshotNode = getBinaryNodeChild(collectionNode, 'snapshot');
|
|
224
256
|
const syncds = [];
|
|
225
257
|
const name = collectionNode.attrs.name;
|
|
226
258
|
const hasMorePatches = collectionNode.attrs.has_more_patches === 'true';
|
|
@@ -229,16 +261,16 @@ const extractSyncdPatches = async (result, options) => {
|
|
|
229
261
|
if (!Buffer.isBuffer(snapshotNode)) {
|
|
230
262
|
snapshotNode.content = Buffer.from(Object.values(snapshotNode.content));
|
|
231
263
|
}
|
|
232
|
-
const blobRef =
|
|
233
|
-
const data = await
|
|
234
|
-
snapshot =
|
|
264
|
+
const blobRef = proto.ExternalBlobReference.decode(snapshotNode.content);
|
|
265
|
+
const data = await downloadExternalBlob(blobRef, options);
|
|
266
|
+
snapshot = proto.SyncdSnapshot.decode(data);
|
|
235
267
|
}
|
|
236
268
|
for (let { content } of patches) {
|
|
237
269
|
if (content) {
|
|
238
270
|
if (!Buffer.isBuffer(content)) {
|
|
239
271
|
content = Buffer.from(Object.values(content));
|
|
240
272
|
}
|
|
241
|
-
const syncd =
|
|
273
|
+
const syncd = proto.SyncdPatch.decode(content);
|
|
242
274
|
if (!syncd.version) {
|
|
243
275
|
syncd.version = { version: +collectionNode.attrs.version + 1 };
|
|
244
276
|
}
|
|
@@ -249,32 +281,27 @@ const extractSyncdPatches = async (result, options) => {
|
|
|
249
281
|
}));
|
|
250
282
|
return final;
|
|
251
283
|
};
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
const stream = await (0, messages_media_1.downloadContentFromMessage)(blob, 'md-app-state', { options });
|
|
284
|
+
export const downloadExternalBlob = async (blob, options) => {
|
|
285
|
+
const stream = await downloadContentFromMessage(blob, 'md-app-state', { options });
|
|
255
286
|
const bufferArray = [];
|
|
256
287
|
for await (const chunk of stream) {
|
|
257
288
|
bufferArray.push(chunk);
|
|
258
289
|
}
|
|
259
290
|
return Buffer.concat(bufferArray);
|
|
260
291
|
};
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
const syncData = WAProto_1.proto.SyncdMutations.decode(buffer);
|
|
292
|
+
export const downloadExternalPatch = async (blob, options) => {
|
|
293
|
+
const buffer = await downloadExternalBlob(blob, options);
|
|
294
|
+
const syncData = proto.SyncdMutations.decode(buffer);
|
|
265
295
|
return syncData;
|
|
266
296
|
};
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
newState.version = (0, generics_1.toNumber)(snapshot.version.version);
|
|
297
|
+
export const decodeSyncdSnapshot = async (name, snapshot, getAppStateSyncKey, minimumVersionNumber, validateMacs = true, logger) => {
|
|
298
|
+
const newState = newLTHashState();
|
|
299
|
+
newState.version = toNumber(snapshot.version.version);
|
|
271
300
|
const mutationMap = {};
|
|
272
|
-
const areMutationsRequired = typeof minimumVersionNumber === 'undefined'
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
var _a;
|
|
277
|
-
const index = (_a = mutation.syncAction.index) === null || _a === void 0 ? void 0 : _a.toString();
|
|
301
|
+
const areMutationsRequired = typeof minimumVersionNumber === 'undefined' || newState.version > minimumVersionNumber;
|
|
302
|
+
const { hash, indexValueMap } = await decodeSyncdMutations(snapshot.records, newState, getAppStateSyncKey, areMutationsRequired
|
|
303
|
+
? mutation => {
|
|
304
|
+
const index = mutation.syncAction.index?.toString();
|
|
278
305
|
mutationMap[index] = mutation;
|
|
279
306
|
}
|
|
280
307
|
: () => { }, validateMacs);
|
|
@@ -284,12 +311,17 @@ const decodeSyncdSnapshot = async (name, snapshot, getAppStateSyncKey, minimumVe
|
|
|
284
311
|
const base64Key = Buffer.from(snapshot.keyId.id).toString('base64');
|
|
285
312
|
const keyEnc = await getAppStateSyncKey(base64Key);
|
|
286
313
|
if (!keyEnc) {
|
|
287
|
-
throw new
|
|
314
|
+
throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { data: { isMissingKey: true } });
|
|
288
315
|
}
|
|
289
316
|
const result = mutationKeys(keyEnc.keyData);
|
|
290
317
|
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey);
|
|
291
318
|
if (Buffer.compare(snapshot.mac, computedSnapshotMac) !== 0) {
|
|
292
|
-
|
|
319
|
+
// LTHash verification may fail when decodeSyncdMutations skipped undecryptable
|
|
320
|
+
// records (poisoned server-side snapshot); the aggregate client hash diverges
|
|
321
|
+
// from the server-computed mac. Fall through with a warning so the session stays
|
|
322
|
+
// alive with partial state, symmetric to how decodePatches handles its own
|
|
323
|
+
// LTHash mismatch a few lines below.
|
|
324
|
+
logger?.warn({ name, version: newState.version }, 'LTHash verification failed on snapshot, continuing with partial state');
|
|
293
325
|
}
|
|
294
326
|
}
|
|
295
327
|
return {
|
|
@@ -297,45 +329,51 @@ const decodeSyncdSnapshot = async (name, snapshot, getAppStateSyncKey, minimumVe
|
|
|
297
329
|
mutationMap
|
|
298
330
|
};
|
|
299
331
|
};
|
|
300
|
-
|
|
301
|
-
const decodePatches = async (name, syncds, initial, getAppStateSyncKey, options, minimumVersionNumber, logger, validateMacs = true) => {
|
|
302
|
-
var _a;
|
|
332
|
+
export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, options, minimumVersionNumber, logger, validateMacs = true) => {
|
|
303
333
|
const newState = {
|
|
304
334
|
...initial,
|
|
305
335
|
indexValueMap: { ...initial.indexValueMap }
|
|
306
336
|
};
|
|
307
337
|
const mutationMap = {};
|
|
308
|
-
for (
|
|
309
|
-
const syncd = syncds[i];
|
|
338
|
+
for (const syncd of syncds) {
|
|
310
339
|
const { version, keyId, snapshotMac } = syncd;
|
|
311
340
|
if (syncd.externalMutations) {
|
|
312
|
-
logger
|
|
313
|
-
const ref = await
|
|
314
|
-
logger
|
|
315
|
-
|
|
341
|
+
logger?.trace({ name, version }, 'downloading external patch');
|
|
342
|
+
const ref = await downloadExternalPatch(syncd.externalMutations, options);
|
|
343
|
+
logger?.debug({ name, version, mutations: ref.mutations.length }, 'downloaded external patch');
|
|
344
|
+
syncd.mutations?.push(...ref.mutations);
|
|
316
345
|
}
|
|
317
|
-
const patchVersion =
|
|
346
|
+
const patchVersion = toNumber(version.version);
|
|
318
347
|
newState.version = patchVersion;
|
|
319
348
|
const shouldMutate = typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
349
|
+
let decodeResult;
|
|
350
|
+
try {
|
|
351
|
+
decodeResult = await decodeSyncdPatch(syncd, name, newState, getAppStateSyncKey, shouldMutate
|
|
352
|
+
? mutation => {
|
|
353
|
+
const index = mutation.syncAction.index?.toString();
|
|
354
|
+
mutationMap[index] = mutation;
|
|
355
|
+
}
|
|
356
|
+
: () => { }, validateMacs);
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
if (isMissingKeyError(err))
|
|
360
|
+
throw err;
|
|
361
|
+
logger?.warn({ name, version: patchVersion, error: err.message }, 'failed to decode patch, skipping');
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
327
364
|
newState.hash = decodeResult.hash;
|
|
328
365
|
newState.indexValueMap = decodeResult.indexValueMap;
|
|
329
366
|
if (validateMacs) {
|
|
330
367
|
const base64Key = Buffer.from(keyId.id).toString('base64');
|
|
331
368
|
const keyEnc = await getAppStateSyncKey(base64Key);
|
|
332
369
|
if (!keyEnc) {
|
|
333
|
-
throw new
|
|
370
|
+
throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { data: { isMissingKey: true } });
|
|
334
371
|
}
|
|
335
372
|
const result = mutationKeys(keyEnc.keyData);
|
|
336
373
|
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey);
|
|
337
374
|
if (Buffer.compare(snapshotMac, computedSnapshotMac) !== 0) {
|
|
338
|
-
|
|
375
|
+
logger?.warn({ name, version: newState.version }, 'LTHash verification failed, skipping remaining patches');
|
|
376
|
+
break;
|
|
339
377
|
}
|
|
340
378
|
}
|
|
341
379
|
// clear memory used up by the mutations
|
|
@@ -343,31 +381,31 @@ const decodePatches = async (name, syncds, initial, getAppStateSyncKey, options,
|
|
|
343
381
|
}
|
|
344
382
|
return { state: newState, mutationMap };
|
|
345
383
|
};
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
const OP = WAProto_1.proto.SyncdMutation.SyncdOperation;
|
|
384
|
+
export const chatModificationToAppPatch = (mod, jid) => {
|
|
385
|
+
const OP = proto.SyncdMutation.SyncdOperation;
|
|
349
386
|
const getMessageRange = (lastMessages) => {
|
|
350
387
|
let messageRange;
|
|
351
388
|
if (Array.isArray(lastMessages)) {
|
|
352
389
|
const lastMsg = lastMessages[lastMessages.length - 1];
|
|
353
390
|
messageRange = {
|
|
354
|
-
lastMessageTimestamp: lastMsg
|
|
355
|
-
messages:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
391
|
+
lastMessageTimestamp: lastMsg?.messageTimestamp,
|
|
392
|
+
messages: lastMessages?.length
|
|
393
|
+
? lastMessages.map(m => {
|
|
394
|
+
if (!m.key?.id || !m.key?.remoteJid) {
|
|
395
|
+
throw new Boom('Incomplete key', { statusCode: 400, data: m });
|
|
396
|
+
}
|
|
397
|
+
if (isJidGroup(m.key.remoteJid) && !m.key.fromMe && !m.key.participant) {
|
|
398
|
+
throw new Boom('Expected not from me message to have participant', { statusCode: 400, data: m });
|
|
399
|
+
}
|
|
400
|
+
if (!m.messageTimestamp || !toNumber(m.messageTimestamp)) {
|
|
401
|
+
throw new Boom('Missing timestamp in last message list', { statusCode: 400, data: m });
|
|
402
|
+
}
|
|
403
|
+
if (m.key.participant) {
|
|
404
|
+
m.key.participant = jidNormalizedUser(m.key.participant);
|
|
405
|
+
}
|
|
406
|
+
return m;
|
|
407
|
+
})
|
|
408
|
+
: undefined
|
|
371
409
|
};
|
|
372
410
|
}
|
|
373
411
|
else {
|
|
@@ -418,25 +456,33 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
418
456
|
operation: OP.SET
|
|
419
457
|
};
|
|
420
458
|
}
|
|
459
|
+
else if ('deleteForMe' in mod) {
|
|
460
|
+
const { timestamp, key, deleteMedia } = mod.deleteForMe;
|
|
461
|
+
patch = {
|
|
462
|
+
syncAction: {
|
|
463
|
+
deleteMessageForMeAction: {
|
|
464
|
+
deleteMedia,
|
|
465
|
+
messageTimestamp: timestamp
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
index: ['deleteMessageForMe', jid, key.id, key.fromMe ? '1' : '0', '0'],
|
|
469
|
+
type: 'regular_high',
|
|
470
|
+
apiVersion: 3,
|
|
471
|
+
operation: OP.SET
|
|
472
|
+
};
|
|
473
|
+
}
|
|
421
474
|
else if ('clear' in mod) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
},
|
|
434
|
-
index: ['deleteMessageForMe', jid, key.id, key.fromMe ? '1' : '0', '0'],
|
|
435
|
-
type: 'regular_high',
|
|
436
|
-
apiVersion: 3,
|
|
437
|
-
operation: OP.SET
|
|
438
|
-
};
|
|
439
|
-
}
|
|
475
|
+
patch = {
|
|
476
|
+
syncAction: {
|
|
477
|
+
clearChatAction: {
|
|
478
|
+
messageRange: getMessageRange(mod.lastMessages)
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
index: ['clearChat', jid, '1' /*the option here is 0 when keep starred messages is enabled*/, '0'],
|
|
482
|
+
type: 'regular_high',
|
|
483
|
+
apiVersion: 6,
|
|
484
|
+
operation: OP.SET
|
|
485
|
+
};
|
|
440
486
|
}
|
|
441
487
|
else if ('pin' in mod) {
|
|
442
488
|
patch = {
|
|
@@ -451,6 +497,28 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
451
497
|
operation: OP.SET
|
|
452
498
|
};
|
|
453
499
|
}
|
|
500
|
+
else if ('contact' in mod) {
|
|
501
|
+
patch = {
|
|
502
|
+
syncAction: {
|
|
503
|
+
contactAction: mod.contact || {}
|
|
504
|
+
},
|
|
505
|
+
index: ['contact', jid],
|
|
506
|
+
type: 'critical_unblock_low',
|
|
507
|
+
apiVersion: 2,
|
|
508
|
+
operation: mod.contact ? OP.SET : OP.REMOVE
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
else if ('disableLinkPreviews' in mod) {
|
|
512
|
+
patch = {
|
|
513
|
+
syncAction: {
|
|
514
|
+
privacySettingDisableLinkPreviewsAction: mod.disableLinkPreviews || {}
|
|
515
|
+
},
|
|
516
|
+
index: ['setting_disableLinkPreviews'],
|
|
517
|
+
type: 'regular',
|
|
518
|
+
apiVersion: 8,
|
|
519
|
+
operation: OP.SET
|
|
520
|
+
};
|
|
521
|
+
}
|
|
454
522
|
else if ('star' in mod) {
|
|
455
523
|
const key = mod.star.messages[0];
|
|
456
524
|
patch = {
|
|
@@ -469,7 +537,7 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
469
537
|
patch = {
|
|
470
538
|
syncAction: {
|
|
471
539
|
deleteChatAction: {
|
|
472
|
-
messageRange: getMessageRange(mod.lastMessages)
|
|
540
|
+
messageRange: getMessageRange(mod.lastMessages)
|
|
473
541
|
}
|
|
474
542
|
},
|
|
475
543
|
index: ['deleteChat', jid, '1'],
|
|
@@ -488,64 +556,90 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
488
556
|
index: ['setting_pushName'],
|
|
489
557
|
type: 'critical_block',
|
|
490
558
|
apiVersion: 1,
|
|
491
|
-
operation: OP.SET
|
|
559
|
+
operation: OP.SET
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
else if ('quickReply' in mod) {
|
|
563
|
+
patch = {
|
|
564
|
+
syncAction: {
|
|
565
|
+
quickReplyAction: {
|
|
566
|
+
count: 0,
|
|
567
|
+
deleted: mod.quickReply.deleted || false,
|
|
568
|
+
keywords: [],
|
|
569
|
+
message: mod.quickReply.message || '',
|
|
570
|
+
shortcut: mod.quickReply.shortcut || ''
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
index: ['quick_reply', mod.quickReply.timestamp || String(Math.floor(Date.now() / 1000))],
|
|
574
|
+
type: 'regular',
|
|
575
|
+
apiVersion: 2,
|
|
576
|
+
operation: OP.SET
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
else if ('addLabel' in mod) {
|
|
580
|
+
patch = {
|
|
581
|
+
syncAction: {
|
|
582
|
+
labelEditAction: {
|
|
583
|
+
name: mod.addLabel.name,
|
|
584
|
+
color: mod.addLabel.color,
|
|
585
|
+
predefinedId: mod.addLabel.predefinedId,
|
|
586
|
+
deleted: mod.addLabel.deleted
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
index: ['label_edit', mod.addLabel.id],
|
|
590
|
+
type: 'regular',
|
|
591
|
+
apiVersion: 3,
|
|
592
|
+
operation: OP.SET
|
|
492
593
|
};
|
|
493
594
|
}
|
|
494
595
|
else if ('addChatLabel' in mod) {
|
|
495
596
|
patch = {
|
|
496
597
|
syncAction: {
|
|
497
598
|
labelAssociationAction: {
|
|
498
|
-
labeled: true
|
|
599
|
+
labeled: true
|
|
499
600
|
}
|
|
500
601
|
},
|
|
501
|
-
index: [
|
|
602
|
+
index: [LabelAssociationType.Chat, mod.addChatLabel.labelId, jid],
|
|
502
603
|
type: 'regular',
|
|
503
604
|
apiVersion: 3,
|
|
504
|
-
operation: OP.SET
|
|
605
|
+
operation: OP.SET
|
|
505
606
|
};
|
|
506
607
|
}
|
|
507
608
|
else if ('removeChatLabel' in mod) {
|
|
508
609
|
patch = {
|
|
509
610
|
syncAction: {
|
|
510
611
|
labelAssociationAction: {
|
|
511
|
-
labeled: false
|
|
612
|
+
labeled: false
|
|
512
613
|
}
|
|
513
614
|
},
|
|
514
|
-
index: [
|
|
615
|
+
index: [LabelAssociationType.Chat, mod.removeChatLabel.labelId, jid],
|
|
515
616
|
type: 'regular',
|
|
516
617
|
apiVersion: 3,
|
|
517
|
-
operation: OP.SET
|
|
618
|
+
operation: OP.SET
|
|
518
619
|
};
|
|
519
620
|
}
|
|
520
621
|
else if ('addMessageLabel' in mod) {
|
|
521
622
|
patch = {
|
|
522
623
|
syncAction: {
|
|
523
624
|
labelAssociationAction: {
|
|
524
|
-
labeled: true
|
|
625
|
+
labeled: true
|
|
525
626
|
}
|
|
526
627
|
},
|
|
527
|
-
index: [
|
|
528
|
-
LabelAssociation_1.LabelAssociationType.Message,
|
|
529
|
-
mod.addMessageLabel.labelId,
|
|
530
|
-
jid,
|
|
531
|
-
mod.addMessageLabel.messageId,
|
|
532
|
-
'0',
|
|
533
|
-
'0'
|
|
534
|
-
],
|
|
628
|
+
index: [LabelAssociationType.Message, mod.addMessageLabel.labelId, jid, mod.addMessageLabel.messageId, '0', '0'],
|
|
535
629
|
type: 'regular',
|
|
536
630
|
apiVersion: 3,
|
|
537
|
-
operation: OP.SET
|
|
631
|
+
operation: OP.SET
|
|
538
632
|
};
|
|
539
633
|
}
|
|
540
634
|
else if ('removeMessageLabel' in mod) {
|
|
541
635
|
patch = {
|
|
542
636
|
syncAction: {
|
|
543
637
|
labelAssociationAction: {
|
|
544
|
-
labeled: false
|
|
638
|
+
labeled: false
|
|
545
639
|
}
|
|
546
640
|
},
|
|
547
641
|
index: [
|
|
548
|
-
|
|
642
|
+
LabelAssociationType.Message,
|
|
549
643
|
mod.removeMessageLabel.labelId,
|
|
550
644
|
jid,
|
|
551
645
|
mod.removeMessageLabel.messageId,
|
|
@@ -554,34 +648,30 @@ const chatModificationToAppPatch = (mod, jid) => {
|
|
|
554
648
|
],
|
|
555
649
|
type: 'regular',
|
|
556
650
|
apiVersion: 3,
|
|
557
|
-
operation: OP.SET
|
|
651
|
+
operation: OP.SET
|
|
558
652
|
};
|
|
559
653
|
}
|
|
560
654
|
else {
|
|
561
|
-
throw new
|
|
655
|
+
throw new Boom('not supported');
|
|
562
656
|
}
|
|
563
657
|
patch.syncAction.timestamp = Date.now();
|
|
564
658
|
return patch;
|
|
565
659
|
};
|
|
566
|
-
|
|
567
|
-
const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
568
|
-
var _a, _b, _c, _d;
|
|
660
|
+
export const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
569
661
|
const isInitialSync = !!initialSyncOpts;
|
|
570
|
-
const accountSettings = initialSyncOpts
|
|
571
|
-
logger
|
|
662
|
+
const accountSettings = initialSyncOpts?.accountSettings;
|
|
663
|
+
logger?.trace({ syncAction, initialSync: !!initialSyncOpts }, 'processing sync action');
|
|
572
664
|
const { syncAction: { value: action }, index: [type, id, msgId, fromMe] } = syncAction;
|
|
573
|
-
if (action
|
|
665
|
+
if (action?.muteAction) {
|
|
574
666
|
ev.emit('chats.update', [
|
|
575
667
|
{
|
|
576
668
|
id,
|
|
577
|
-
muteEndTime:
|
|
578
|
-
? (0, generics_1.toNumber)(action.muteAction.muteEndTimestamp)
|
|
579
|
-
: null,
|
|
669
|
+
muteEndTime: action.muteAction?.muted ? toNumber(action.muteAction.muteEndTimestamp) : null,
|
|
580
670
|
conditional: getChatUpdateConditional(id, undefined)
|
|
581
671
|
}
|
|
582
672
|
]);
|
|
583
673
|
}
|
|
584
|
-
else if (
|
|
674
|
+
else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
|
|
585
675
|
// okay so we've to do some annoying computation here
|
|
586
676
|
// when we're initially syncing the app state
|
|
587
677
|
// there are a few cases we need to handle
|
|
@@ -592,36 +682,38 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
592
682
|
// we compare the timestamp of latest message from the other person to determine this
|
|
593
683
|
// 2. if the account unarchiveChats setting is false -- then it doesn't matter,
|
|
594
684
|
// it'll always take an app state action to mark in unarchived -- which we'll get anyway
|
|
595
|
-
const archiveAction = action
|
|
596
|
-
const isArchived = archiveAction
|
|
597
|
-
? archiveAction.archived
|
|
598
|
-
: type === 'archive';
|
|
685
|
+
const archiveAction = action?.archiveChatAction;
|
|
686
|
+
const isArchived = archiveAction ? archiveAction.archived : type === 'archive';
|
|
599
687
|
// // basically we don't need to fire an "archive" update if the chat is being marked unarchvied
|
|
600
688
|
// // this only applies for the initial sync
|
|
601
689
|
// if(isInitialSync && !isArchived) {
|
|
602
690
|
// isArchived = false
|
|
603
691
|
// }
|
|
604
|
-
const msgRange = !
|
|
692
|
+
const msgRange = !accountSettings?.unarchiveChats ? undefined : archiveAction?.messageRange;
|
|
605
693
|
// logger?.debug({ chat: id, syncAction }, 'message range archive')
|
|
606
|
-
ev.emit('chats.update', [
|
|
694
|
+
ev.emit('chats.update', [
|
|
695
|
+
{
|
|
607
696
|
id,
|
|
608
697
|
archived: isArchived,
|
|
609
698
|
conditional: getChatUpdateConditional(id, msgRange)
|
|
610
|
-
}
|
|
699
|
+
}
|
|
700
|
+
]);
|
|
611
701
|
}
|
|
612
|
-
else if (action
|
|
702
|
+
else if (action?.markChatAsReadAction) {
|
|
613
703
|
const markReadAction = action.markChatAsReadAction;
|
|
614
704
|
// basically we don't need to fire an "read" update if the chat is being marked as read
|
|
615
705
|
// because the chat is read by default
|
|
616
706
|
// this only applies for the initial sync
|
|
617
707
|
const isNullUpdate = isInitialSync && markReadAction.read;
|
|
618
|
-
ev.emit('chats.update', [
|
|
708
|
+
ev.emit('chats.update', [
|
|
709
|
+
{
|
|
619
710
|
id,
|
|
620
|
-
unreadCount: isNullUpdate ? null : !!
|
|
621
|
-
conditional: getChatUpdateConditional(id, markReadAction
|
|
622
|
-
}
|
|
711
|
+
unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1,
|
|
712
|
+
conditional: getChatUpdateConditional(id, markReadAction?.messageRange)
|
|
713
|
+
}
|
|
714
|
+
]);
|
|
623
715
|
}
|
|
624
|
-
else if (
|
|
716
|
+
else if (action?.deleteMessageForMeAction || type === 'deleteMessageForMe') {
|
|
625
717
|
ev.emit('messages.delete', {
|
|
626
718
|
keys: [
|
|
627
719
|
{
|
|
@@ -632,37 +724,35 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
632
724
|
]
|
|
633
725
|
});
|
|
634
726
|
}
|
|
635
|
-
else if (action
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
else if (action === null || action === void 0 ? void 0 : action.pushNameSetting) {
|
|
644
|
-
const name = (_b = action === null || action === void 0 ? void 0 : action.pushNameSetting) === null || _b === void 0 ? void 0 : _b.name;
|
|
645
|
-
if (name && (me === null || me === void 0 ? void 0 : me.name) !== name) {
|
|
727
|
+
else if (action?.contactAction) {
|
|
728
|
+
const results = processContactAction(action.contactAction, id, logger);
|
|
729
|
+
emitSyncActionResults(ev, results);
|
|
730
|
+
}
|
|
731
|
+
else if (action?.pushNameSetting) {
|
|
732
|
+
const name = action?.pushNameSetting?.name;
|
|
733
|
+
if (name && me?.name !== name) {
|
|
646
734
|
ev.emit('creds.update', { me: { ...me, name } });
|
|
647
735
|
}
|
|
648
736
|
}
|
|
649
|
-
else if (action
|
|
650
|
-
ev.emit('chats.update', [
|
|
737
|
+
else if (action?.pinAction) {
|
|
738
|
+
ev.emit('chats.update', [
|
|
739
|
+
{
|
|
651
740
|
id,
|
|
652
|
-
pinned:
|
|
741
|
+
pinned: action.pinAction?.pinned ? toNumber(action.timestamp) : null,
|
|
653
742
|
conditional: getChatUpdateConditional(id, undefined)
|
|
654
|
-
}
|
|
743
|
+
}
|
|
744
|
+
]);
|
|
655
745
|
}
|
|
656
|
-
else if (action
|
|
746
|
+
else if (action?.unarchiveChatsSetting) {
|
|
657
747
|
const unarchiveChats = !!action.unarchiveChatsSetting.unarchiveChats;
|
|
658
748
|
ev.emit('creds.update', { accountSettings: { unarchiveChats } });
|
|
659
|
-
logger
|
|
749
|
+
logger?.info(`archive setting updated => '${action.unarchiveChatsSetting.unarchiveChats}'`);
|
|
660
750
|
if (accountSettings) {
|
|
661
751
|
accountSettings.unarchiveChats = unarchiveChats;
|
|
662
752
|
}
|
|
663
753
|
}
|
|
664
|
-
else if (
|
|
665
|
-
let starred =
|
|
754
|
+
else if (action?.starAction || type === 'star') {
|
|
755
|
+
let starred = action?.starAction?.starred;
|
|
666
756
|
if (typeof starred !== 'boolean') {
|
|
667
757
|
starred = syncAction.index[syncAction.index.length - 1] === '1';
|
|
668
758
|
}
|
|
@@ -673,46 +763,99 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
673
763
|
}
|
|
674
764
|
]);
|
|
675
765
|
}
|
|
676
|
-
else if (
|
|
766
|
+
else if (action?.deleteChatAction || type === 'deleteChat') {
|
|
677
767
|
if (!isInitialSync) {
|
|
678
768
|
ev.emit('chats.delete', [id]);
|
|
679
769
|
}
|
|
680
770
|
}
|
|
681
|
-
else if (action
|
|
771
|
+
else if (action?.labelEditAction) {
|
|
682
772
|
const { name, color, deleted, predefinedId } = action.labelEditAction;
|
|
683
773
|
ev.emit('labels.edit', {
|
|
684
|
-
id,
|
|
774
|
+
id: id,
|
|
685
775
|
name: name,
|
|
686
776
|
color: color,
|
|
687
777
|
deleted: deleted,
|
|
688
778
|
predefinedId: predefinedId ? String(predefinedId) : undefined
|
|
689
779
|
});
|
|
690
780
|
}
|
|
691
|
-
else if (action
|
|
781
|
+
else if (action?.labelAssociationAction) {
|
|
692
782
|
ev.emit('labels.association', {
|
|
693
|
-
type: action.labelAssociationAction.labeled
|
|
694
|
-
|
|
695
|
-
: 'remove',
|
|
696
|
-
association: type === LabelAssociation_1.LabelAssociationType.Chat
|
|
783
|
+
type: action.labelAssociationAction.labeled ? 'add' : 'remove',
|
|
784
|
+
association: type === LabelAssociationType.Chat
|
|
697
785
|
? {
|
|
698
|
-
type:
|
|
786
|
+
type: LabelAssociationType.Chat,
|
|
699
787
|
chatId: syncAction.index[2],
|
|
700
788
|
labelId: syncAction.index[1]
|
|
701
789
|
}
|
|
702
790
|
: {
|
|
703
|
-
type:
|
|
791
|
+
type: LabelAssociationType.Message,
|
|
704
792
|
chatId: syncAction.index[2],
|
|
705
793
|
messageId: syncAction.index[3],
|
|
706
794
|
labelId: syncAction.index[1]
|
|
707
795
|
}
|
|
708
796
|
});
|
|
709
797
|
}
|
|
798
|
+
else if (action?.localeSetting?.locale) {
|
|
799
|
+
ev.emit('settings.update', { setting: 'locale', value: action.localeSetting.locale });
|
|
800
|
+
}
|
|
801
|
+
else if (action?.timeFormatAction) {
|
|
802
|
+
ev.emit('settings.update', { setting: 'timeFormat', value: action.timeFormatAction });
|
|
803
|
+
}
|
|
804
|
+
else if (action?.pnForLidChatAction) {
|
|
805
|
+
if (action.pnForLidChatAction.pnJid) {
|
|
806
|
+
ev.emit('lid-mapping.update', { lid: id, pn: action.pnForLidChatAction.pnJid });
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
else if (action?.privacySettingRelayAllCalls) {
|
|
810
|
+
ev.emit('settings.update', {
|
|
811
|
+
setting: 'privacySettingRelayAllCalls',
|
|
812
|
+
value: action.privacySettingRelayAllCalls
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
else if (action?.statusPrivacy) {
|
|
816
|
+
ev.emit('settings.update', { setting: 'statusPrivacy', value: action.statusPrivacy });
|
|
817
|
+
}
|
|
818
|
+
else if (action?.lockChatAction) {
|
|
819
|
+
ev.emit('chats.lock', { id: id, locked: !!action.lockChatAction.locked });
|
|
820
|
+
}
|
|
821
|
+
else if (action?.privacySettingDisableLinkPreviewsAction) {
|
|
822
|
+
ev.emit('settings.update', {
|
|
823
|
+
setting: 'disableLinkPreviews',
|
|
824
|
+
value: action.privacySettingDisableLinkPreviewsAction
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
else if (action?.notificationActivitySettingAction?.notificationActivitySetting) {
|
|
828
|
+
ev.emit('settings.update', {
|
|
829
|
+
setting: 'notificationActivitySetting',
|
|
830
|
+
value: action.notificationActivitySettingAction.notificationActivitySetting
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
else if (action?.lidContactAction) {
|
|
834
|
+
ev.emit('contacts.upsert', [
|
|
835
|
+
{
|
|
836
|
+
id: id,
|
|
837
|
+
name: action.lidContactAction.fullName ||
|
|
838
|
+
action.lidContactAction.firstName ||
|
|
839
|
+
action.lidContactAction.username ||
|
|
840
|
+
undefined,
|
|
841
|
+
username: action.lidContactAction.username || undefined,
|
|
842
|
+
lid: id,
|
|
843
|
+
phoneNumber: undefined
|
|
844
|
+
}
|
|
845
|
+
]);
|
|
846
|
+
}
|
|
847
|
+
else if (action?.privacySettingChannelsPersonalisedRecommendationAction) {
|
|
848
|
+
ev.emit('settings.update', {
|
|
849
|
+
setting: 'channelsPersonalisedRecommendation',
|
|
850
|
+
value: action.privacySettingChannelsPersonalisedRecommendationAction
|
|
851
|
+
});
|
|
852
|
+
}
|
|
710
853
|
else {
|
|
711
|
-
logger
|
|
854
|
+
logger?.debug({ syncAction, id }, 'unprocessable update');
|
|
712
855
|
}
|
|
713
856
|
function getChatUpdateConditional(id, msgRange) {
|
|
714
857
|
return isInitialSync
|
|
715
|
-
?
|
|
858
|
+
? data => {
|
|
716
859
|
const chat = data.historySets.chats[id] || data.chatUpserts[id];
|
|
717
860
|
if (chat) {
|
|
718
861
|
return msgRange ? isValidPatchBasedOnMessageRange(chat, msgRange) : true;
|
|
@@ -721,9 +864,9 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
|
|
|
721
864
|
: undefined;
|
|
722
865
|
}
|
|
723
866
|
function isValidPatchBasedOnMessageRange(chat, msgRange) {
|
|
724
|
-
const lastMsgTimestamp = Number(
|
|
725
|
-
const chatLastMsgTimestamp = Number(
|
|
867
|
+
const lastMsgTimestamp = Number(msgRange?.lastMessageTimestamp || msgRange?.lastSystemMessageTimestamp || 0);
|
|
868
|
+
const chatLastMsgTimestamp = Number(chat?.lastMessageRecvTimestamp || 0);
|
|
726
869
|
return lastMsgTimestamp >= chatLastMsgTimestamp;
|
|
727
870
|
}
|
|
728
871
|
};
|
|
729
|
-
|
|
872
|
+
//# sourceMappingURL=chat-utils.js.map
|