whalibmob 5.5.21 → 5.5.23

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/README.md CHANGED
@@ -6,6 +6,20 @@
6
6
  > [!CAUTION]
7
7
  > Use a dedicated phone number with this library. Connecting with a number that is already active on a real device will cause WhatsApp to log that device out.
8
8
 
9
+ > [!CAUTION]
10
+ > Whalibmob now It needs to be rewritten because WhatsApp mobile and has changed the protocol lately and now whalibmob is in testing and some updates by Me Any pull request is accepted.
11
+
12
+
13
+ > [!IMPORTANT]
14
+ > If you like what I do and want to support me I can leave you here my Crypto usdc address for any donation and support any Small donation is accepted because the WhatsApp protocol changes very often : "0x8AD64F47a715eC24DeF193FBb9aC64d4E857f0f3"
15
+
16
+ Usdc ethereum network.
17
+
18
+
19
+
20
+
21
+
22
+
9
23
  > [!IMPORTANT]
10
24
  > This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with WhatsApp or any of its subsidiaries or affiliates. "WhatsApp" and related names are registered trademarks of their respective owners. Use at your own discretion.
11
25
 
@@ -102,6 +116,11 @@ npm install -g whalibmob
102
116
  - [Register a New Number](#register-a-new-number)
103
117
  - [Connect](#connect)
104
118
  - [Saving & Restoring Sessions](#saving--restoring-sessions)
119
+ - [Signal Store Utilities](#signal-store-utilities)
120
+ - [makeCacheableSignalKeyStore](#makecacheablesignalkeystore)
121
+ - [addTransactionCapability](#addtransactioncapability)
122
+ - [assertMeId](#assertmeid)
123
+ - [initAuthCreds](#initauthcreds)
105
124
  - [Handling Events](#handling-events)
106
125
  - [Example to Start](#example-to-start)
107
126
  - [All Events](#all-events)
@@ -267,6 +286,138 @@ await client.init('919634847671')
267
286
  > [!NOTE]
268
287
  > Each phone number uses its own session file. The library handles Signal Protocol key persistence automatically.
269
288
 
289
+ ## Signal Store Utilities
290
+
291
+ `auth-utils` is a collection of optional helpers for power users who manage their own `SignalStore` instances directly (e.g. custom storage backends, multi-account servers).
292
+
293
+ ```js
294
+ const {
295
+ makeCacheableSignalKeyStore,
296
+ addTransactionCapability,
297
+ assertMeId,
298
+ initAuthCreds
299
+ } = require('whalibmob')
300
+ ```
301
+
302
+ ### `makeCacheableSignalKeyStore`
303
+
304
+ Wraps a `SignalStore` with an in-memory NodeCache layer (5-minute TTL). All `get` calls for `sessions`, `preKeys`, `signedPreKeys`, and `identities` are served from cache on subsequent accesses. Writes invalidate the cache automatically.
305
+
306
+ `useClones` is set to `false` so that `SessionRecord` objects — which carry internal state and methods — are returned by reference and never deep-cloned.
307
+
308
+ The wrapper also forwards `transaction()` and `isInTransaction()` calls to the underlying store when present, making it safe to stack with `addTransactionCapability`.
309
+
310
+ ```js
311
+ const { SignalStore } = require('whalibmob')
312
+ const { makeCacheableSignalKeyStore } = require('whalibmob')
313
+
314
+ const store = new SignalStore(/* ... */)
315
+ const cached = makeCacheableSignalKeyStore(store)
316
+
317
+ // reads hit cache after first access
318
+ const session = await cached.getSession('919634847671@s.whatsapp.net:0')
319
+ ```
320
+
321
+ **When to use:** whenever your `SignalStore` is backed by a remote or disk-based store (database, Redis, file system) and you want to reduce repeated lookups for sessions that haven't changed between sends.
322
+
323
+ ### `addTransactionCapability`
324
+
325
+ Wraps a `SignalStore` with batched-write (transaction) semantics. During a transaction all writes are buffered in memory; they are flushed to the underlying store atomically when `commit()` is called at the end of the transaction.
326
+
327
+ Uses `AsyncLocalStorage` to propagate transaction context across async call chains, and a per-key-type `Mutex` with reference-counting to serialize concurrent writers safely.
328
+
329
+ ```js
330
+ const { addTransactionCapability, makeCacheableSignalKeyStore } = require('whalibmob')
331
+
332
+ // recommended: cache first, then transactions on top
333
+ const base = new SignalStore(/* ... */)
334
+ const cached = makeCacheableSignalKeyStore(base)
335
+ const txnStore = addTransactionCapability(cached)
336
+
337
+ // inside a send flow
338
+ await txnStore.transaction(async () => {
339
+ // all writes are buffered
340
+ await txnStore.setSession('919634847671@s.whatsapp.net:0', sessionRecord)
341
+ await txnStore.setPreKey(1, preKeyPair)
342
+ // commit is called automatically at the end of the transaction callback
343
+ })
344
+ ```
345
+
346
+ Stacking order matters: put `makeCacheableSignalKeyStore` below `addTransactionCapability` so that the cache always sees the committed state.
347
+
348
+ **When to use:** for high-throughput servers that send to many recipients concurrently and need to batch Signal key writes into a single atomic flush per message.
349
+
350
+ ### `assertMeId`
351
+
352
+ Validates that a store object has a registered phone number and returns the canonical `@s.whatsapp.net` JID. Throws an `Error` if the store lacks a `phoneNumber` or has `registered !== true`.
353
+
354
+ ```js
355
+ const { assertMeId } = require('whalibmob')
356
+
357
+ const store = loadStore(sessFile)
358
+
359
+ try {
360
+ const jid = assertMeId(store)
361
+ // jid === '919634847671@s.whatsapp.net'
362
+ console.log('account JID:', jid)
363
+ } catch (err) {
364
+ console.error('store is not registered:', err.message)
365
+ }
366
+ ```
367
+
368
+ **When to use:** as a guard before calling `client.init()` to give a clear error message when a corrupted or unregistered session file is accidentally loaded.
369
+
370
+ ### `initAuthCreds`
371
+
372
+ Creates a fresh credential store for the given phone number. Functionally equivalent to `createNewStore` but also initialises the Baileys-compatible extra fields that the library expects for account sync: `nextPreKeyId`, `firstUnuploadedPreKeyId`, `accountSyncCounter`, `accountSettings`, and `advSecretKey`.
373
+
374
+ ```js
375
+ const { initAuthCreds, saveStore } = require('whalibmob')
376
+ const path = require('path')
377
+ const fs = require('fs')
378
+
379
+ const phone = '919634847671'
380
+ const sessDir = path.join(process.env.HOME, '.waSession')
381
+ const sessFile = path.join(sessDir, phone + '.json')
382
+
383
+ fs.mkdirSync(sessDir, { recursive: true })
384
+
385
+ const store = initAuthCreds(phone)
386
+ saveStore(store, sessFile)
387
+ ```
388
+
389
+ This is the function used internally by the CLI for all new session creation. Prefer it over `createNewStore` for forward compatibility.
390
+
391
+ > [!NOTE]
392
+ > `initAuthCreds` and `createNewStore` produce equivalent stores for all current library operations. The additional fields from `initAuthCreds` are there for future-proofing and interoperability.
393
+
394
+ ### Recommended Stacking Pattern
395
+
396
+ For a production multi-account server:
397
+
398
+ ```js
399
+ const {
400
+ SignalStore,
401
+ makeCacheableSignalKeyStore,
402
+ addTransactionCapability,
403
+ initAuthCreds,
404
+ saveStore,
405
+ loadStore
406
+ } = require('whalibmob')
407
+
408
+ // 1. load or create the credential store
409
+ let store = loadStore(sessFile) || initAuthCreds(phone)
410
+
411
+ // 2. build the layered Signal key store
412
+ const signalStore = new SignalStore(store)
413
+ const cachedStore = makeCacheableSignalKeyStore(signalStore)
414
+ const txnStore = addTransactionCapability(cachedStore)
415
+
416
+ // 3. pass to the client (advanced usage — most users should use WhalibmobClient directly)
417
+ ```
418
+
419
+ For standard usage, `WhalibmobClient` handles all of this internally. These helpers are for advanced scenarios where you need direct control over Signal key storage.
420
+
270
421
  ## Handling Events
271
422
 
272
423
  whalibmob uses the EventEmitter syntax for events.
package/cli.js CHANGED
@@ -35,6 +35,8 @@ const {
35
35
  loadStore
36
36
  } = require('./lib/Client');
37
37
 
38
+ const { assertMeId, initAuthCreds } = require('./lib/auth-utils');
39
+
38
40
  const VERSION = '5.1.21';
39
41
 
40
42
  // ─── output helpers ───────────────────────────────────────────────────────────
@@ -435,14 +437,18 @@ async function handleLine(line) {
435
437
  if (_client) { try { _client.disconnect(); } catch (_) {} }
436
438
  process.exit(0);
437
439
 
438
- case '/session':
440
+ case '/session': {
439
441
  if (!_client || !_client.store) { fail('not connected'); break; }
442
+ let _meJid = '—';
443
+ try { _meJid = assertMeId(_client.store); } catch (_) {}
440
444
  hr();
441
445
  kv('phone', _client.store.phoneNumber);
446
+ kv('jid', _meJid);
442
447
  kv('name', _client.store.pushName || _client.store.name || '—');
443
448
  kv('session', _sessDir);
444
449
  hr();
445
450
  break;
451
+ }
446
452
 
447
453
  case '/connect': {
448
454
  const ph = p[1];
@@ -1153,7 +1159,7 @@ async function handleLine(line) {
1153
1159
  const sessFile = path.join(_sessDir, `${ph}.json`);
1154
1160
  let store = loadStore(sessFile);
1155
1161
  if (!store) {
1156
- store = createNewStore(ph);
1162
+ store = initAuthCreds(ph);
1157
1163
  saveStore(store, sessFile);
1158
1164
  } else if (!store.codePending && !store.registered) {
1159
1165
  // Only check /exist when keys were never used to request a code.
@@ -1162,7 +1168,7 @@ async function handleLine(line) {
1162
1168
  const fresh = await assertRegistrationKeys(store, waVersion);
1163
1169
  if (!fresh) {
1164
1170
  out(' device keys already registered — generating new keys...');
1165
- store = createNewStore(ph);
1171
+ store = initAuthCreds(ph);
1166
1172
  saveStore(store, sessFile);
1167
1173
  out(' new keys saved — proceed with code below');
1168
1174
  }
@@ -1180,7 +1186,7 @@ async function handleLine(line) {
1180
1186
  const code = p[3];
1181
1187
  if (!ph || !code) { fail('usage: /reg confirm <phone> <code>'); break; }
1182
1188
  const file = path.join(_sessDir, `${ph}.json`);
1183
- const store = loadStore(file) || createNewStore(ph);
1189
+ const store = loadStore(file) || initAuthCreds(ph);
1184
1190
  out('verifying...');
1185
1191
  const r = await verifyCode(store, code);
1186
1192
  if (r && (r.status === 'ok' || r.status === 'sent' || r.status === 'verified')) {
@@ -1477,7 +1483,7 @@ async function main() {
1477
1483
  let store = loadStore(sessFile);
1478
1484
  if (!store) {
1479
1485
  // Brand new — generate fresh keys, save immediately, no need to check /exist
1480
- store = createNewStore(ph);
1486
+ store = initAuthCreds(ph);
1481
1487
  saveStore(store, sessFile);
1482
1488
  } else if (!store.codePending && !store.registered) {
1483
1489
  // Existing store but code was never sent and not registered — check if
@@ -1488,7 +1494,7 @@ async function main() {
1488
1494
  const fresh = await assertRegistrationKeys(store, waVersion);
1489
1495
  if (!fresh) {
1490
1496
  out(' device keys already registered — generating new keys...');
1491
- store = createNewStore(ph);
1497
+ store = initAuthCreds(ph);
1492
1498
  saveStore(store, sessFile);
1493
1499
  }
1494
1500
  }
@@ -1518,7 +1524,7 @@ async function main() {
1518
1524
  if (!ph) { fail('phone number required'); process.exit(1); }
1519
1525
  if (!code) { fail('--code is required'); process.exit(1); }
1520
1526
  const file = path.join(_sessDir, `${ph}.json`);
1521
- const store = loadStore(file) || createNewStore(ph);
1527
+ const store = loadStore(file) || initAuthCreds(ph);
1522
1528
  out('verifying code for +' + ph + '...');
1523
1529
  try {
1524
1530
  const r = await verifyCode(store, code);
package/index.js CHANGED
@@ -11,6 +11,12 @@ const { SenderKeyStore, SenderKeyCrypto } = require('./lib/signal/SenderKey');
11
11
  const { DeviceManager } = require('./lib/DeviceManager');
12
12
  const { encryptMedia, decryptMedia, uploadMedia, downloadMedia } = require('./lib/MediaService');
13
13
  const { MessageSender, makeJid, generateMessageId } = require('./lib/messages/MessageSender');
14
+ const {
15
+ makeCacheableSignalKeyStore,
16
+ addTransactionCapability,
17
+ assertMeId,
18
+ initAuthCreds
19
+ } = require('./lib/auth-utils');
14
20
 
15
21
  module.exports = {
16
22
  WhalibmobClient,
@@ -48,5 +54,10 @@ module.exports = {
48
54
  // Message helpers
49
55
  MessageSender,
50
56
  makeJid,
51
- generateMessageId
57
+ generateMessageId,
58
+ // Auth utilities (mirrors Baileys' auth-utils)
59
+ makeCacheableSignalKeyStore,
60
+ addTransactionCapability,
61
+ assertMeId,
62
+ initAuthCreds
52
63
  };
package/lib/Client.js CHANGED
@@ -175,6 +175,17 @@ class WhalibmobClient extends EventEmitter {
175
175
  this._signal = SignalProtocol.fromStore(this._store, signalFile, skFile);
176
176
  this._devMgr = new DeviceManager(this);
177
177
 
178
+ // Restore LID ↔ phone mappings persisted from previous sessions.
179
+ // This ensures we can route to LID JIDs even on fresh connections where no
180
+ // incoming message has yet populated _pnToLid during this session.
181
+ const persistedLid = this._signal.store.getLidMappings
182
+ ? this._signal.store.getLidMappings() : {};
183
+ for (const [phone, lid] of Object.entries(persistedLid)) {
184
+ this._pnToLid.set(phone, lid);
185
+ this._lidToPn.set(lid, phone);
186
+ }
187
+ process.stderr.write('[DBG] LID_RESTORED count=' + Object.keys(persistedLid).length + '\n');
188
+
178
189
  await this._connectSocket();
179
190
  return this;
180
191
  }
@@ -530,6 +541,10 @@ class WhalibmobClient extends EventEmitter {
530
541
  if (lidUser && pnUser && lidUser !== pnUser) {
531
542
  this._lidToPn.set(lidUser, pnUser);
532
543
  this._pnToLid.set(pnUser, lidUser);
544
+ // Persist so the mapping survives reconnects / process restarts
545
+ if (this._signal && this._signal.store && this._signal.store.setLidMapping) {
546
+ this._signal.store.setLidMapping(pnUser, lidUser);
547
+ }
533
548
  }
534
549
  }
535
550
  process.stderr.write('[DBG] _handleMessage from=' + from + ' participant=' + participant + ' senderPn=' + senderPn + ' id=' + id + '\n');
@@ -1196,6 +1211,8 @@ class WhalibmobClient extends EventEmitter {
1196
1211
  const pnUser = pPhone.split('@')[0].split(':')[0];
1197
1212
  this._lidToPn.set(lidUser, pnUser);
1198
1213
  this._pnToLid.set(pnUser, lidUser);
1214
+ if (this._signal && this._signal.store && this._signal.store.setLidMapping)
1215
+ this._signal.store.setLidMapping(pnUser, lidUser);
1199
1216
  }
1200
1217
  }
1201
1218
  // Case B: participant JID is a PN, lid attribute is their LID
@@ -1204,6 +1221,8 @@ class WhalibmobClient extends EventEmitter {
1204
1221
  const lidUser = pLid.split('@')[0].split(':')[0];
1205
1222
  this._lidToPn.set(lidUser, pnUser);
1206
1223
  this._pnToLid.set(pnUser, lidUser);
1224
+ if (this._signal && this._signal.store && this._signal.store.setLidMapping)
1225
+ this._signal.store.setLidMapping(pnUser, lidUser);
1207
1226
  }
1208
1227
  return { jid: pJid, role: pRole };
1209
1228
  });