@pontalabs/baileys 1.0.10 → 1.1.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.
Files changed (119) hide show
  1. package/README.md +96 -85
  2. package/lib/Defaults/connection.js +39 -0
  3. package/lib/Defaults/constants.js +50 -0
  4. package/lib/Defaults/history.js +13 -0
  5. package/lib/Defaults/index.js +36 -151
  6. package/lib/Defaults/media.js +43 -0
  7. package/lib/Defaults/phonenumber-mcc.json +221 -221
  8. package/lib/Defaults/prefix.js +12 -0
  9. package/lib/Defaults/vialeys-version.json +7 -0
  10. package/lib/Signal/Group/ciphertext-message.js +10 -15
  11. package/lib/Signal/Group/group-session-builder.js +34 -60
  12. package/lib/Signal/Group/group_cipher.js +69 -76
  13. package/lib/Signal/Group/index.js +41 -60
  14. package/lib/Signal/Group/keyhelper.js +12 -57
  15. package/lib/Signal/Group/sender-chain-key.js +20 -33
  16. package/lib/Signal/Group/sender-key-distribution-message.js +43 -48
  17. package/lib/Signal/Group/sender-key-message.js +58 -53
  18. package/lib/Signal/Group/sender-key-name.js +21 -31
  19. package/lib/Signal/Group/sender-key-record.js +21 -34
  20. package/lib/Signal/Group/sender-key-state.js +59 -106
  21. package/lib/Signal/Group/sender-message-key.js +16 -21
  22. package/lib/Signal/libsignal.js +282 -203
  23. package/lib/Signal/lid-mapping.js +164 -184
  24. package/lib/Socket/Client/index.js +31 -22
  25. package/lib/Socket/Client/types.js +9 -15
  26. package/lib/Socket/Client/websocket.js +42 -37
  27. package/lib/Socket/business.js +212 -336
  28. package/lib/Socket/chats.js +696 -940
  29. package/lib/Socket/community.js +454 -369
  30. package/lib/Socket/groups.js +278 -280
  31. package/lib/Socket/index.js +9 -21
  32. package/lib/Socket/messages-recv.js +1027 -1570
  33. package/lib/Socket/messages-send.js +1180 -1136
  34. package/lib/Socket/mex.js +59 -0
  35. package/lib/Socket/newsletter.js +193 -251
  36. package/lib/Socket/socket.js +758 -898
  37. package/lib/Store/index.js +35 -24
  38. package/lib/Store/make-cache-manager-store.js +53 -66
  39. package/lib/Store/make-in-memory-store.js +319 -325
  40. package/lib/Store/make-ordered-dictionary.js +49 -57
  41. package/lib/Store/object-repository.js +10 -15
  42. package/lib/Types/Auth.js +2 -3
  43. package/lib/Types/Bussines.js +2 -3
  44. package/lib/Types/Call.js +2 -3
  45. package/lib/Types/Chat.js +10 -9
  46. package/lib/Types/Contact.js +2 -3
  47. package/lib/Types/Events.js +2 -3
  48. package/lib/Types/GroupMetadata.js +2 -3
  49. package/lib/Types/Label.js +6 -12
  50. package/lib/Types/LabelAssociation.js +4 -13
  51. package/lib/Types/Message.js +10 -18
  52. package/lib/Types/MexUpdates.js +15 -18
  53. package/lib/Types/Newsletter.js +26 -34
  54. package/lib/Types/Product.js +2 -3
  55. package/lib/Types/Signal.js +2 -3
  56. package/lib/Types/Socket.js +2 -3
  57. package/lib/Types/State.js +8 -13
  58. package/lib/Types/USync.js +2 -3
  59. package/lib/Types/index.js +49 -40
  60. package/lib/Utils/auth-utils.js +190 -495
  61. package/lib/Utils/browser-utils.js +29 -0
  62. package/lib/Utils/business.js +177 -199
  63. package/lib/Utils/chat-utils.js +711 -624
  64. package/lib/Utils/crypto.js +116 -161
  65. package/lib/Utils/decode-wa-message.js +225 -321
  66. package/lib/Utils/event-buffer.js +363 -405
  67. package/lib/Utils/generics.js +318 -472
  68. package/lib/Utils/history.js +72 -76
  69. package/lib/Utils/index.js +49 -41
  70. package/lib/Utils/link-preview.js +59 -103
  71. package/lib/Utils/logger.js +4 -7
  72. package/lib/Utils/lt-hash.js +29 -42
  73. package/lib/Utils/make-mutex.js +26 -42
  74. package/lib/Utils/message-retry-manager.js +134 -160
  75. package/lib/Utils/messages-media.js +665 -731
  76. package/lib/Utils/messages.js +518 -1
  77. package/lib/Utils/noise-handler.js +127 -125
  78. package/lib/Utils/pre-key-manager.js +86 -0
  79. package/lib/Utils/process-message.js +739 -351
  80. package/lib/Utils/signal.js +146 -129
  81. package/lib/Utils/use-multi-file-auth-state.js +68 -135
  82. package/lib/Utils/validate-connection.js +183 -142
  83. package/lib/Utils/vialeys-event-stream.js +41 -0
  84. package/lib/WABinary/constants.js +1281 -1289
  85. package/lib/WABinary/decode.js +145 -200
  86. package/lib/WABinary/encode.js +149 -202
  87. package/lib/WABinary/generic-utils.js +79 -97
  88. package/lib/WABinary/index.js +36 -25
  89. package/lib/WABinary/jid-utils.js +91 -123
  90. package/lib/WABinary/types.js +2 -3
  91. package/lib/WAM/BinaryInfo.js +8 -13
  92. package/lib/WAM/constants.js +16584 -11464
  93. package/lib/WAM/encode.js +117 -133
  94. package/lib/WAM/index.js +34 -23
  95. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +62 -69
  96. package/lib/WAUSync/Protocols/USyncContactProtocol.js +11 -25
  97. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +32 -44
  98. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +13 -23
  99. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +24 -38
  100. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +17 -31
  101. package/lib/WAUSync/Protocols/index.js +37 -26
  102. package/lib/WAUSync/USyncQuery.js +66 -66
  103. package/lib/WAUSync/USyncUser.js +13 -18
  104. package/lib/WAUSync/index.js +34 -23
  105. package/lib/index.js +95 -28
  106. package/package.json +4 -3
  107. package/lib/Defaults/baileys-version.json +0 -3
  108. package/lib/Signal/Group/queue-job.d.ts +0 -1
  109. package/lib/Signal/Group/queue-job.js +0 -57
  110. package/lib/Socket/usync.d.ts +0 -37
  111. package/lib/Socket/usync.js +0 -83
  112. package/lib/Utils/baileys-event-stream.d.ts +0 -18
  113. package/lib/Utils/baileys-event-stream.js +0 -70
  114. package/lib/Utils/signal-housekeeping.d.ts +0 -19
  115. package/lib/Utils/signal-housekeeping.js +0 -73
  116. package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
  117. package/lib/Utils/use-mongo-file-auth-state.js +0 -84
  118. package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
  119. package/lib/Utils/use-single-file-auth-state.js +0 -80
@@ -1,285 +1,364 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.makeLibSignalRepository = makeLibSignalRepository;
37
- const libsignal = __importStar(require("libsignal"));
38
- const { PreKeyWhisperMessage } = require("libsignal/src/protobufs");
39
- const Utils_1 = require("../Utils");
40
- const WABinary_1 = require("../WABinary");
41
- const sender_key_name_1 = require("./Group/sender-key-name");
42
- const sender_key_record_1 = require("./Group/sender-key-record");
43
- const Group_1 = require("./Group");
44
- const LIDMappingStore_1 = require("./lid-mapping")
45
-
46
- // FIX: sebelumnya pontalabs gak punya deteksi perubahan identity key sama
47
- // sekali. Kalau identity kontak berubah (kontak install ulang WA, atau kena
48
- // resync multi-device pas ada linked device lain online), sesi lama tetap
49
- // dipakai terus dan selalu gagal decrypt "Bad MAC" berulang-ulang sampai
50
- // akhirnya kena threshold retry enableAutoSessionRecreation. Dengan deteksi
51
- // ini, begitu identity berubah, sesi langsung direset SAAT ITU JUGA - gak
52
- // perlu nunggu beberapa pesan gagal dulu.
53
- function extractIdentityFromPkmsg(ciphertext) {
54
- try {
55
- if (!ciphertext || ciphertext.length < 2) {
56
- return undefined;
57
- }
58
- const version = ciphertext[0];
59
- if ((version & 0xf) !== 3) {
60
- return undefined;
61
- }
62
- const preKeyProto = PreKeyWhisperMessage.decode(ciphertext.slice(1));
63
- if (preKeyProto.identityKey && preKeyProto.identityKey.length === 33) {
64
- return Buffer.from(preKeyProto.identityKey);
65
- }
66
- return undefined;
67
- } catch {
68
- return undefined;
69
- }
70
- }
71
-
72
- function makeLibSignalRepository(auth, onWhatsAppFunc, logger) {
73
- const lidMapping = new LIDMappingStore_1.LIDMappingStore(auth.keys, onWhatsAppFunc, logger)
74
- const storage = signalStorage(auth);
75
- return {
76
- lidMapping,
77
- decryptGroupMessage({ group, authorJid, msg }) {
3
+ const {
4
+ SessionCipher: SessionCipher,
5
+ SessionBuilder: SessionBuilder,
6
+ SessionRecord: SessionRecord,
7
+ ProtocolAddress: ProtocolAddress,
8
+ } = require("libsignal");
9
+ const { generateSignalPubKey: generateSignalPubKey } = require("../Utils");
10
+ const {
11
+ isHostedLidUser: isHostedLidUser,
12
+ isHostedPnUser: isHostedPnUser,
13
+ isLidUser: isLidUser,
14
+ isPnUser: isPnUser,
15
+ jidDecode: jidDecode,
16
+ transferDevice: transferDevice,
17
+ WAJIDDomains: WAJIDDomains,
18
+ } = require("../WABinary");
19
+ const { SenderKeyName: SenderKeyName } = require("./Group/sender-key-name");
20
+ const { SenderKeyRecord: SenderKeyRecord } = require("./Group/sender-key-record");
21
+ const {
22
+ GroupCipher: GroupCipher,
23
+ GroupSessionBuilder: GroupSessionBuilder,
24
+ SenderKeyDistributionMessage: SenderKeyDistributionMessage,
25
+ } = require("./Group");
26
+ const { LRUCache: LRUCache } = require("lru-cache");
27
+ const { LIDMappingStore: LIDMappingStore } = require("./lid-mapping");
28
+ function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
29
+ const lidMapping = new LIDMappingStore(auth.keys, logger, pnToLIDFunc);
30
+ const storage = signalStorage(auth, lidMapping);
31
+ const parsedKeys = auth.keys;
32
+ const migratedSessionCache = new LRUCache({
33
+ ttl: 3 * 24 * 60 * 60 * 1e3,
34
+ ttlAutopurge: true,
35
+ updateAgeOnGet: true,
36
+ });
37
+ const repository = {
38
+ decryptGroupMessage({ group: group, authorJid: authorJid, msg: msg }) {
78
39
  const senderName = jidToSignalSenderKeyName(group, authorJid);
79
- const cipher = new Group_1.GroupCipher(storage, senderName);
80
- // Dibungkus transaction() biar atomic - konsisten sama operasi Signal
81
- // lainnya, cegah race condition kalau ada beberapa pesan grup dari
82
- // sender yang sama numpuk barengan.
83
- return auth.keys.transaction(async () => {
84
- return cipher.decrypt(msg);
85
- }, group);
40
+ const cipher = new GroupCipher(storage, senderName);
41
+ return parsedKeys.transaction(async () => cipher.decrypt(msg), group);
86
42
  },
87
- async processSenderKeyDistributionMessage({ item, authorJid }) {
88
- const builder = new Group_1.GroupSessionBuilder(storage);
43
+ async processSenderKeyDistributionMessage({ item: item, authorJid: authorJid }) {
44
+ const builder = new GroupSessionBuilder(storage);
89
45
  if (!item.groupId) {
90
- throw new Error('Group ID is required for sender key distribution message');
46
+ throw new Error("Group ID is required for sender key distribution message");
91
47
  }
92
48
  const senderName = jidToSignalSenderKeyName(item.groupId, authorJid);
93
- const senderMsg = new Group_1.SenderKeyDistributionMessage(null, null, null, null, item.axolotlSenderKeyDistributionMessage);
49
+ const senderMsg = new SenderKeyDistributionMessage(
50
+ null,
51
+ null,
52
+ null,
53
+ null,
54
+ item.axolotlSenderKeyDistributionMessage
55
+ );
94
56
  const senderNameStr = senderName.toString();
95
- return auth.keys.transaction(async () => {
96
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
57
+ const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [
58
+ senderNameStr,
59
+ ]);
60
+ if (!senderKey) {
61
+ await storage.storeSenderKey(senderName, new SenderKeyRecord());
62
+ }
63
+ return parsedKeys.transaction(async () => {
64
+ const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [
65
+ senderNameStr,
66
+ ]);
97
67
  if (!senderKey) {
98
- await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
68
+ await storage.storeSenderKey(senderName, new SenderKeyRecord());
99
69
  }
100
70
  await builder.process(senderName, senderMsg);
101
71
  }, item.groupId);
102
72
  },
103
- async decryptMessage({ jid, type, ciphertext }) {
73
+ async decryptMessage({ jid: jid, type: type, ciphertext: ciphertext }) {
104
74
  const addr = jidToSignalProtocolAddress(jid);
105
- const session = new libsignal.SessionCipher(storage, addr);
106
- // Sebelum decrypt pkmsg (pesan yang buka sesi baru), cek dulu apakah
107
- // identity key pengirim berubah dibanding yang tersimpan. Kalau iya,
108
- // sesi lama otomatis direset supaya gak nyoba decrypt pakai sesi basi.
109
- if (type === 'pkmsg') {
110
- const identityKey = extractIdentityFromPkmsg(ciphertext);
111
- if (identityKey) {
112
- const addrStr = addr.toString();
113
- const identityChanged = await storage.saveIdentity(addrStr, identityKey);
114
- if (identityChanged) {
115
- logger?.info?.({ jid, addr: addrStr }, 'identity key changed or new contact, session will be re-established');
116
- }
75
+ const session = new SessionCipher(storage, addr);
76
+ async function doDecrypt() {
77
+ let result;
78
+ switch (type) {
79
+ case "pkmsg":
80
+ result = await session.decryptPreKeyWhisperMessage(ciphertext);
81
+ break;
82
+ case "msg":
83
+ result = await session.decryptWhisperMessage(ciphertext);
84
+ break;
117
85
  }
86
+ return result;
118
87
  }
119
- try {
120
- return await auth.keys.transaction(async () => {
121
- let result;
122
- switch (type) {
123
- case 'pkmsg':
124
- result = await session.decryptPreKeyWhisperMessage(ciphertext);
125
- break;
126
- case 'msg':
127
- result = await session.decryptWhisperMessage(ciphertext);
128
- break;
129
- default:
130
- throw new Error(`Unknown message type: ${type}`);
131
- }
132
- return result;
133
- }, jid);
134
- } catch (error) {
135
- const errText = String(error?.message || error || '')
136
- if (/bad mac|no session|invalid message|duplicate message|cannot decrypt/i.test(errText)) {
137
- await storage.removeSession(addr.toString()).catch(() => null)
138
- logger?.warn?.({ jid, type, errText }, 'reset stale session after decrypt failure')
139
- }
140
- throw error
141
- }
88
+ return parsedKeys.transaction(async () => await doDecrypt(), jid);
142
89
  },
143
- async encryptMessage({ jid, data }) {
90
+ async encryptMessage({ jid: jid, data: data }) {
144
91
  const addr = jidToSignalProtocolAddress(jid);
145
- const cipher = new libsignal.SessionCipher(storage, addr);
146
- return auth.keys.transaction(async () => {
147
- const { type: sigType, body } = await cipher.encrypt(data);
148
- const type = sigType === 3 ? 'pkmsg' : 'msg';
149
- return { type, ciphertext: Buffer.from(body, 'binary') };
92
+ const cipher = new SessionCipher(storage, addr);
93
+ return parsedKeys.transaction(async () => {
94
+ const { type: sigType, body: body } = await cipher.encrypt(data);
95
+ const type = sigType === 3 ? "pkmsg" : "msg";
96
+ return { type: type, ciphertext: Buffer.from(body, "binary") };
150
97
  }, jid);
151
98
  },
152
- async encryptGroupMessage({ group, meId, data }) {
153
- return auth.keys.transaction(async () => {
154
- const senderName = jidToSignalSenderKeyName(group, meId);
155
- const builder = new Group_1.GroupSessionBuilder(storage);
156
- const senderNameStr = senderName.toString();
157
- const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]);
99
+ async encryptGroupMessage({ group: group, meId: meId, data: data }) {
100
+ const senderName = jidToSignalSenderKeyName(group, meId);
101
+ const builder = new GroupSessionBuilder(storage);
102
+ const senderNameStr = senderName.toString();
103
+ return parsedKeys.transaction(async () => {
104
+ const { [senderNameStr]: senderKey } = await auth.keys.get("sender-key", [
105
+ senderNameStr,
106
+ ]);
158
107
  if (!senderKey) {
159
- await storage.storeSenderKey(senderName, new sender_key_record_1.SenderKeyRecord());
108
+ await storage.storeSenderKey(senderName, new SenderKeyRecord());
160
109
  }
161
110
  const senderKeyDistributionMessage = await builder.create(senderName);
162
- const session = new Group_1.GroupCipher(storage, senderName);
111
+ const session = new GroupCipher(storage, senderName);
163
112
  const ciphertext = await session.encrypt(data);
164
113
  return {
165
- ciphertext,
166
- senderKeyDistributionMessage: senderKeyDistributionMessage.serialize()
114
+ ciphertext: ciphertext,
115
+ senderKeyDistributionMessage: senderKeyDistributionMessage.serialize(),
167
116
  };
168
117
  }, group);
169
118
  },
170
- async injectE2ESession({ jid, session }) {
171
- const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid));
172
- await cipher.initOutgoing(session);
119
+ async injectE2ESession({ jid: jid, session: session }) {
120
+ logger.trace({ jid: jid }, "injecting E2EE session");
121
+ const cipher = new SessionBuilder(storage, jidToSignalProtocolAddress(jid));
122
+ return parsedKeys.transaction(async () => {
123
+ await cipher.initOutgoing(session);
124
+ }, jid);
173
125
  },
174
126
  jidToSignalProtocolAddress(jid) {
175
127
  return jidToSignalProtocolAddress(jid).toString();
176
128
  },
177
- // Dipakai oleh sendRetryRequest (messages-recv.js) untuk enableAutoSessionRecreation.
178
- // Sebelumnya dipanggil tapi tidak pernah diimplementasikan di sini, jadi fitur auto
179
- // session recreation diam-diam selalu gagal (ketangkep try/catch, cuma log warning).
129
+ lidMapping: lidMapping,
180
130
  async validateSession(jid) {
181
131
  try {
182
132
  const addr = jidToSignalProtocolAddress(jid);
183
133
  const session = await storage.loadSession(addr.toString());
184
134
  if (!session) {
185
- return { exists: false, reason: 'no session' };
135
+ return { exists: false, reason: "no session" };
186
136
  }
187
- if (typeof session.haveOpenSession === 'function' && !session.haveOpenSession()) {
188
- return { exists: false, reason: 'no open session' };
137
+ if (!session.haveOpenSession()) {
138
+ return { exists: false, reason: "no open session" };
189
139
  }
190
140
  return { exists: true };
191
141
  } catch (error) {
192
- return { exists: false, reason: 'validation error' };
142
+ return { exists: false, reason: "validation error" };
193
143
  }
194
- }
144
+ },
145
+ async deleteSession(jids) {
146
+ if (!jids.length) return;
147
+ const sessionUpdates = {};
148
+ jids.forEach((jid) => {
149
+ const addr = jidToSignalProtocolAddress(jid);
150
+ sessionUpdates[addr.toString()] = null;
151
+ });
152
+ return parsedKeys.transaction(async () => {
153
+ await auth.keys.set({ session: sessionUpdates });
154
+ }, `delete-${jids.length}-sessions`);
155
+ },
156
+ async migrateSession(fromJid, toJid) {
157
+ if (!fromJid || (!isLidUser(toJid) && !isHostedLidUser(toJid))) {
158
+ return { migrated: 0, skipped: 0, total: 0 };
159
+ }
160
+ if (!isPnUser(fromJid) && !isHostedPnUser(fromJid)) {
161
+ return { migrated: 0, skipped: 0, total: 1 };
162
+ }
163
+ const { user: user } = jidDecode(fromJid);
164
+ logger.debug({ fromJid: fromJid }, "bulk device migration - loading all user devices");
165
+ const { [user]: userDevices } = await parsedKeys.get("device-list", [user]);
166
+ if (!userDevices) {
167
+ return { migrated: 0, skipped: 0, total: 0 };
168
+ }
169
+ const { device: fromDevice } = jidDecode(fromJid);
170
+ const fromDeviceStr = fromDevice?.toString() || "0";
171
+ if (!userDevices.includes(fromDeviceStr)) {
172
+ userDevices.push(fromDeviceStr);
173
+ }
174
+ const uncachedDevices = userDevices.filter((device) => {
175
+ const deviceKey = `${user}.${device}`;
176
+ return !migratedSessionCache.has(deviceKey);
177
+ });
178
+ const deviceSessionKeys = uncachedDevices.map((device) => `${user}.${device}`);
179
+ const existingSessions = await parsedKeys.get("session", deviceSessionKeys);
180
+ const deviceJids = [];
181
+ for (const [sessionKey, sessionData] of Object.entries(existingSessions)) {
182
+ if (sessionData) {
183
+ const deviceStr = sessionKey.split(".")[1];
184
+ if (!deviceStr) continue;
185
+ const deviceNum = parseInt(deviceStr);
186
+ let jid =
187
+ deviceNum === 0
188
+ ? `${user}@s.whatsapp.net`
189
+ : `${user}:${deviceNum}@s.whatsapp.net`;
190
+ if (deviceNum === 99) {
191
+ jid = `${user}:99@hosted`;
192
+ }
193
+ deviceJids.push(jid);
194
+ }
195
+ }
196
+ logger.debug(
197
+ {
198
+ fromJid: fromJid,
199
+ totalDevices: userDevices.length,
200
+ devicesWithSessions: deviceJids.length,
201
+ devices: deviceJids,
202
+ },
203
+ "bulk device migration complete - all user devices processed"
204
+ );
205
+ return parsedKeys.transaction(
206
+ async () => {
207
+ const migrationOps = deviceJids.map((jid) => {
208
+ const lidWithDevice = transferDevice(jid, toJid);
209
+ const fromDecoded = jidDecode(jid);
210
+ const toDecoded = jidDecode(lidWithDevice);
211
+ return {
212
+ fromJid: jid,
213
+ toJid: lidWithDevice,
214
+ pnUser: fromDecoded.user,
215
+ lidUser: toDecoded.user,
216
+ deviceId: fromDecoded.device || 0,
217
+ fromAddr: jidToSignalProtocolAddress(jid),
218
+ toAddr: jidToSignalProtocolAddress(lidWithDevice),
219
+ };
220
+ });
221
+ const totalOps = migrationOps.length;
222
+ let migratedCount = 0;
223
+ const pnAddrStrings = Array.from(
224
+ new Set(migrationOps.map((op) => op.fromAddr.toString()))
225
+ );
226
+ const pnSessions = await parsedKeys.get("session", pnAddrStrings);
227
+ const sessionUpdates = {};
228
+ for (const op of migrationOps) {
229
+ const pnAddrStr = op.fromAddr.toString();
230
+ const lidAddrStr = op.toAddr.toString();
231
+ const pnSession = pnSessions[pnAddrStr];
232
+ if (pnSession) {
233
+ const fromSession = SessionRecord.deserialize(pnSession);
234
+ if (fromSession.haveOpenSession()) {
235
+ sessionUpdates[lidAddrStr] = fromSession.serialize();
236
+ sessionUpdates[pnAddrStr] = null;
237
+ migratedCount++;
238
+ }
239
+ }
240
+ }
241
+ if (Object.keys(sessionUpdates).length > 0) {
242
+ await parsedKeys.set({ session: sessionUpdates });
243
+ logger.debug(
244
+ { migratedSessions: migratedCount },
245
+ "bulk session migration complete"
246
+ );
247
+ for (const op of migrationOps) {
248
+ if (sessionUpdates[op.toAddr.toString()]) {
249
+ const deviceKey = `${op.pnUser}.${op.deviceId}`;
250
+ migratedSessionCache.set(deviceKey, true);
251
+ }
252
+ }
253
+ }
254
+ const skippedCount = totalOps - migratedCount;
255
+ return {
256
+ migrated: migratedCount,
257
+ skipped: skippedCount,
258
+ total: totalOps,
259
+ };
260
+ },
261
+ `migrate-${deviceJids.length}-sessions-${jidDecode(toJid)?.user}`
262
+ );
263
+ },
195
264
  };
265
+ return repository;
196
266
  }
197
267
  const jidToSignalProtocolAddress = (jid) => {
198
- const { user, device } = (0, WABinary_1.jidDecode)(jid);
199
- return new libsignal.ProtocolAddress(user, device || 0);
200
- };
201
- const jidToSignalSenderKeyName = (group, user) => {
202
- return new sender_key_name_1.SenderKeyName(group, jidToSignalProtocolAddress(user));
268
+ const decoded = jidDecode(jid);
269
+ const { user: user, device: device, server: server, domainType: domainType } = decoded;
270
+ if (!user) {
271
+ throw new Error(
272
+ `JID decoded but user is empty: "${jid}" -> user: "${user}", server: "${server}", device: ${device}`
273
+ );
274
+ }
275
+ const signalUser = domainType !== WAJIDDomains.WHATSAPP ? `${user}_${domainType}` : user;
276
+ const finalDevice = device || 0;
277
+ if (device === 99 && decoded.server !== "hosted" && decoded.server !== "hosted.lid") {
278
+ throw new Error(
279
+ "Unexpected non-hosted device JID with device 99. This ID seems invalid. ID:" + jid
280
+ );
281
+ }
282
+ return new ProtocolAddress(signalUser, finalDevice);
203
283
  };
204
- function signalStorage({ creds, keys }) {
284
+ const jidToSignalSenderKeyName = (group, user) =>
285
+ new SenderKeyName(group, jidToSignalProtocolAddress(user));
286
+ function signalStorage({ creds: creds, keys: keys }, lidMapping) {
287
+ const resolveLIDSignalAddress = async (id) => {
288
+ if (id.includes(".")) {
289
+ const [deviceId, device] = id.split(".");
290
+ const [user, domainType_] = deviceId.split("_");
291
+ const domainType = parseInt(domainType_ || "0");
292
+ if (domainType === WAJIDDomains.LID || domainType === WAJIDDomains.HOSTED_LID)
293
+ return id;
294
+ const pnJid = `${user}${device !== "0" ? `:${device}` : ""}@${domainType === WAJIDDomains.HOSTED ? "hosted" : "s.whatsapp.net"}`;
295
+ const lidForPN = await lidMapping.getLIDForPN(pnJid);
296
+ if (lidForPN) {
297
+ const lidAddr = jidToSignalProtocolAddress(lidForPN);
298
+ return lidAddr.toString();
299
+ }
300
+ }
301
+ return id;
302
+ };
205
303
  return {
206
304
  loadSession: async (id) => {
207
- const { [id]: sess } = await keys.get('session', [id]);
208
- if (sess) {
209
- return libsignal.SessionRecord.deserialize(sess);
305
+ try {
306
+ const wireJid = await resolveLIDSignalAddress(id);
307
+ const { [wireJid]: sess } = await keys.get("session", [wireJid]);
308
+ if (sess) {
309
+ return SessionRecord.deserialize(sess);
310
+ }
311
+ } catch (e) {
312
+ return null;
210
313
  }
314
+ return null;
211
315
  },
212
316
  storeSession: async (id, session) => {
213
- await keys.set({ session: { [id]: session.serialize() } });
214
- },
215
- removeSession: async (id) => {
216
- await keys.set({ session: { [id]: null } });
217
- },
218
- isTrustedIdentity: () => {
219
- return true;
220
- },
221
- loadIdentityKey: async (id) => {
222
- const { [id]: key } = await keys.get('identity-key', [id]);
223
- return key || undefined;
224
- },
225
- saveIdentity: async (id, identityKey) => {
226
- const { [id]: existingKey } = await keys.get('identity-key', [id]);
227
- const keysMatch = existingKey && existingKey.length === identityKey.length &&
228
- Buffer.compare(Buffer.from(existingKey), Buffer.from(identityKey)) === 0;
229
- if (existingKey && !keysMatch) {
230
- // Identity berubah - hapus sesi lama (bakal ke-generate ulang dari
231
- // pkmsg yang lagi diproses) dan simpan identity key yang baru.
232
- await keys.set({
233
- session: { [id]: null },
234
- 'identity-key': { [id]: identityKey }
235
- });
236
- return true;
237
- }
238
- if (!existingKey) {
239
- // Kontak baru - Trust On First Use (TOFU), sama kayak WhatsApp Web.
240
- await keys.set({ 'identity-key': { [id]: identityKey } });
241
- return true;
242
- }
243
- return false;
317
+ const wireJid = await resolveLIDSignalAddress(id);
318
+ await keys.set({ session: { [wireJid]: session.serialize() } });
244
319
  },
320
+ isTrustedIdentity: () => true,
245
321
  loadPreKey: async (id) => {
246
322
  const keyId = id.toString();
247
- const { [keyId]: key } = await keys.get('pre-key', [keyId]);
323
+ const { [keyId]: key } = await keys.get("pre-key", [keyId]);
248
324
  if (key) {
249
325
  return {
250
326
  privKey: Buffer.from(key.private),
251
- pubKey: Buffer.from(key.public)
327
+ pubKey: Buffer.from(key.public),
252
328
  };
253
329
  }
254
330
  },
255
- removePreKey: (id) => keys.set({ 'pre-key': { [id]: null } }),
331
+ removePreKey: (id) => keys.set({ "pre-key": { [id]: null } }),
256
332
  loadSignedPreKey: () => {
257
333
  const key = creds.signedPreKey;
258
334
  return {
259
335
  privKey: Buffer.from(key.keyPair.private),
260
- pubKey: Buffer.from(key.keyPair.public)
336
+ pubKey: Buffer.from(key.keyPair.public),
261
337
  };
262
338
  },
263
339
  loadSenderKey: async (senderKeyName) => {
264
340
  const keyId = senderKeyName.toString();
265
- const { [keyId]: key } = await keys.get('sender-key', [keyId]);
341
+ const { [keyId]: key } = await keys.get("sender-key", [keyId]);
266
342
  if (key) {
267
- return sender_key_record_1.SenderKeyRecord.deserialize(key);
343
+ return SenderKeyRecord.deserialize(key);
268
344
  }
269
- return new sender_key_record_1.SenderKeyRecord();
345
+ return new SenderKeyRecord();
270
346
  },
271
347
  storeSenderKey: async (senderKeyName, key) => {
272
348
  const keyId = senderKeyName.toString();
273
349
  const serialized = JSON.stringify(key.serialize());
274
- await keys.set({ 'sender-key': { [keyId]: Buffer.from(serialized, 'utf-8') } });
350
+ await keys.set({
351
+ "sender-key": { [keyId]: Buffer.from(serialized, "utf-8") },
352
+ });
275
353
  },
276
354
  getOurRegistrationId: () => creds.registrationId,
277
355
  getOurIdentity: () => {
278
- const { signedIdentityKey } = creds;
356
+ const { signedIdentityKey: signedIdentityKey } = creds;
279
357
  return {
280
358
  privKey: Buffer.from(signedIdentityKey.private),
281
- pubKey: (0, Utils_1.generateSignalPubKey)(signedIdentityKey.public)
359
+ pubKey: Buffer.from(generateSignalPubKey(signedIdentityKey.public)),
282
360
  };
283
- }
361
+ },
284
362
  };
285
363
  }
364
+ module.exports = { makeLibSignalRepository: makeLibSignalRepository };