whalibmob 5.5.64 → 5.5.65
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/lib/BinaryNode.js +27 -3
- package/lib/Client.js +36 -8
- package/package.json +1 -1
package/lib/BinaryNode.js
CHANGED
|
@@ -127,9 +127,25 @@ function _writeBytes(buf, parts) {
|
|
|
127
127
|
parts.push(buf);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// AD_JID agent byte ↔ domain. 0 = phone JIDs, 1 = LIDs.
|
|
131
|
+
const _AGENT_SERVER = { 0: 's.whatsapp.net', 1: 'lid' };
|
|
132
|
+
function _serverForAgent(agent) {
|
|
133
|
+
return _AGENT_SERVER[agent] || 's.whatsapp.net';
|
|
134
|
+
}
|
|
135
|
+
function _agentForServer(server) {
|
|
136
|
+
return server === 'lid' ? 1 : 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
function _writeJid(jid, parts) {
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
// Derive the agent from the domain only when this JID is going out as AD_JID
|
|
141
|
+
// anyway (explicit agent, or a device suffix). A plain LID keeps its
|
|
142
|
+
// JID_PAIR + 'lid' server-token encoding, which is what the server expects
|
|
143
|
+
// and what the rest of the codebase already produces.
|
|
144
|
+
const agent = (jid.agent !== undefined && jid.agent !== null)
|
|
145
|
+
? jid.agent
|
|
146
|
+
: (jid.device ? _agentForServer(jid.server) : 0);
|
|
147
|
+
if (agent || jid.device) {
|
|
148
|
+
parts.push(Buffer.from([TAG.AD_JID, agent || 0, jid.device || 0]));
|
|
133
149
|
_writeString(jid.user, parts);
|
|
134
150
|
} else {
|
|
135
151
|
parts.push(Buffer.from([TAG.JID_PAIR]));
|
|
@@ -289,7 +305,15 @@ class NodeDecoder {
|
|
|
289
305
|
const device = this._readByte();
|
|
290
306
|
const userToken = this._readByte();
|
|
291
307
|
const user = this._readString(userToken);
|
|
292
|
-
|
|
308
|
+
// The agent byte selects the domain — it is not decoration. Hardcoding
|
|
309
|
+
// s.whatsapp.net turned every LID participant (agent 1) into a phone JID
|
|
310
|
+
// that does not exist, so device queries for it came back empty, no Signal
|
|
311
|
+
// session was ever built, and group sends were rejected with ack 479.
|
|
312
|
+
const server = _serverForAgent(agent);
|
|
313
|
+
return {
|
|
314
|
+
user, agent, device, server,
|
|
315
|
+
toString() { return `${user}@${server}`; }
|
|
316
|
+
};
|
|
293
317
|
}
|
|
294
318
|
}
|
|
295
319
|
|
package/lib/Client.js
CHANGED
|
@@ -148,8 +148,11 @@ class WhalibmobClient extends EventEmitter {
|
|
|
148
148
|
this._pnToLid = new Map(); // phone number → LID user
|
|
149
149
|
this._myLid = null; // own LID JID received from <success> node
|
|
150
150
|
this._groupAddressingMode = new Map(); // groupJid → 'lid' | 'pn'
|
|
151
|
-
this._retryPending = new Map(); // msgId → {node,
|
|
152
|
-
this._retryPreKeyIdx = 0; // rotating index
|
|
151
|
+
this._retryPending = new Map(); // msgId → {node, count, pkId}
|
|
152
|
+
this._retryPreKeyIdx = 0; // rotating index, fallback when every prekey is reserved
|
|
153
|
+
// keyIds handed out by unresolved retries — never advertise one twice, or
|
|
154
|
+
// the second sender's pkmsg arrives after the key was consumed and deleted.
|
|
155
|
+
this._retryAdvertisedPreKeys = new Set();
|
|
153
156
|
this._appStateVersions = {}; // collectionName → version (int)
|
|
154
157
|
this._sentMsgCache = new Map(); // msgId → {plaintext, toJid, msgId, mediaType, options, recipientPhone}
|
|
155
158
|
this._tcTokenStore = null; // TcTokenStore — loaded in init()
|
|
@@ -686,6 +689,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
686
689
|
|
|
687
690
|
this._signal.decrypt(sigJid, encType, cipherBuf)
|
|
688
691
|
.then(async plaintext => {
|
|
692
|
+
this._resolveRetry(id);
|
|
689
693
|
const decoded = this._decodeMsg(plaintext);
|
|
690
694
|
_whaDbg('[DBG] DM_DECODED id=' + id + ' type=' + decoded.type + ' ptLen=' + (plaintext ? plaintext.length : 0) + ' hasSKDM=' + !!(decoded.skdm || decoded.type === 'senderKeyDistribution'));
|
|
691
695
|
|
|
@@ -756,6 +760,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
756
760
|
|
|
757
761
|
this._signal.senderKeyDecrypt(from, participant, cipherBuf)
|
|
758
762
|
.then(plaintext => {
|
|
763
|
+
this._resolveRetry(id);
|
|
759
764
|
const decoded = this._decodeMsg(plaintext);
|
|
760
765
|
if (decoded.type === 'senderKeyDistribution') return;
|
|
761
766
|
if (decoded.type === 'protocol') {
|
|
@@ -959,6 +964,17 @@ class WhalibmobClient extends EventEmitter {
|
|
|
959
964
|
// - <registration> 4-byte reg-id
|
|
960
965
|
// - <keys> block (from retry #1): type + identity + key + skey + device-identity
|
|
961
966
|
|
|
967
|
+
// A message we asked to be re-sent finally decrypted (or we gave up on it).
|
|
968
|
+
// Release its reserved prekey so the pool does not drain over a long session.
|
|
969
|
+
_resolveRetry(msgId) {
|
|
970
|
+
const pending = this._retryPending.get(msgId);
|
|
971
|
+
if (!pending) return;
|
|
972
|
+
if (pending.pkId !== null && pending.pkId !== undefined) {
|
|
973
|
+
this._retryAdvertisedPreKeys.delete(pending.pkId);
|
|
974
|
+
}
|
|
975
|
+
this._retryPending.delete(msgId);
|
|
976
|
+
}
|
|
977
|
+
|
|
962
978
|
_sendRetryRequest(msgId, origNode) {
|
|
963
979
|
const existing = this._retryPending.get(msgId);
|
|
964
980
|
const count = existing ? existing.count + 1 : 1;
|
|
@@ -966,13 +982,25 @@ class WhalibmobClient extends EventEmitter {
|
|
|
966
982
|
|
|
967
983
|
if (!this._socket || !this._connected) return;
|
|
968
984
|
|
|
969
|
-
//
|
|
970
|
-
|
|
971
|
-
|
|
985
|
+
// Pick a prekey that is not already advertised by another unresolved retry.
|
|
986
|
+
// A prekey is deleted the moment a pkmsg using it is decrypted, so handing
|
|
987
|
+
// the same one to two senders means the second arrives after the key is
|
|
988
|
+
// gone and dies with "Invalid PreKey ID". Indexing modulo the key count
|
|
989
|
+
// could not prevent that: the list shrinks as keys are consumed, so the
|
|
990
|
+
// rotating index wraps back onto ids that are still in flight.
|
|
991
|
+
let pkId = existing ? existing.pkId : null;
|
|
992
|
+
if (pkId === null && this._signal) {
|
|
972
993
|
const allKeys = this._signal.getPreKeysForUpload(800);
|
|
973
|
-
|
|
994
|
+
const free = allKeys.find(k => !this._retryAdvertisedPreKeys.has(k.keyId));
|
|
995
|
+
// If every key is already spoken for, fall back to rotating rather than
|
|
996
|
+
// sending no bundle at all — a stale id beats no retry.
|
|
997
|
+
const chosen = free || allKeys[this._retryPreKeyIdx++ % Math.max(1, allKeys.length)];
|
|
998
|
+
if (chosen) {
|
|
999
|
+
pkId = chosen.keyId;
|
|
1000
|
+
this._retryAdvertisedPreKeys.add(pkId);
|
|
1001
|
+
}
|
|
974
1002
|
}
|
|
975
|
-
this._retryPending.set(msgId, { node: origNode, count,
|
|
1003
|
+
this._retryPending.set(msgId, { node: origNode, count, pkId });
|
|
976
1004
|
|
|
977
1005
|
// Use original message timestamp (not current time)
|
|
978
1006
|
const origAttrs = (origNode && origNode.attrs) || {};
|
|
@@ -991,7 +1019,7 @@ class WhalibmobClient extends EventEmitter {
|
|
|
991
1019
|
const spk = this._signal.getSignedPreKeyForUpload();
|
|
992
1020
|
const identKey = this._signal.getIdentityKey();
|
|
993
1021
|
const allPreKeys = this._signal.getPreKeysForUpload(800);
|
|
994
|
-
const pk = allPreKeys
|
|
1022
|
+
const pk = allPreKeys.find(k => k.keyId === pkId) || allPreKeys[0];
|
|
995
1023
|
|
|
996
1024
|
if (spk && identKey && pk) {
|
|
997
1025
|
const keysChildren = [
|