whalibmob 5.5.56 → 5.5.57

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/.env.example CHANGED
@@ -47,16 +47,3 @@
47
47
  # Route registration HTTP traffic through a SOCKS5 proxy or Tor.
48
48
  # TOR_PROXY=socks5://127.0.0.1:9050
49
49
  # SOCKS_PROXY=socks5://user:pass@proxy.example.com:1080
50
-
51
- # ── Device attestation (Frida) ────────────────────────────────────────────────
52
- # WhatsApp gates registration behind device attestation (Play Integrity on
53
- # Android, App Attest on iOS). The Frida scripts in ./frida run on a
54
- # rooted/jailbroken device and expose a local HTTP server that mints those
55
- # tokens; whalibmob then attaches them to /exist, /code and /register — exactly
56
- # like Auties00/Cobalt. See frida/README.md. Disabled unless the flag is set.
57
- # WA_FRIDA_ATTESTATION=1
58
- # WA_FRIDA_HOST=127.0.0.1
59
- # WA_FRIDA_PORT=1119 # 1119 = WhatsApp, 1120 = WhatsApp Business
60
- # WA_FRIDA_TIMEOUT=20000
61
- # WA_FRIDA_OPTIONAL=1 # continue without attestation if server unreachable
62
- # WA_FRIDA_NONCE= # override the Play Integrity request nonce (advanced)
package/README.md CHANGED
@@ -3,9 +3,35 @@
3
3
 
4
4
  ##
5
5
 
6
+ > [!IMPORTANT]
7
+ > new repository whalibmob It will be maintained at the https://github.com/Kunboruto50/whalibmob.git Since I lost the Kunboruto20 account, whalibmob will be maintained and rewritten by me soon because I know that many functions in whalibmob no longer work after the WhatsApp mobile protocol change, these days I will take care of whalibmob, all good friends Fun enjoyable for all c
8
+
9
+
10
+ CONTACT ME ON TELEGRAM IF YOU WANT TO WORK WITH ME AND IF YOU HAVE PROBLEM WITH WHALIBMOB : @borutokun240
11
+
12
+ TELEGRAM NEW WHALIBMOB CHANNEL JOIN HERE https://t.me/+jWzq-I9o0Xc1Mzc8
13
+
14
+
15
+
16
+
17
+
6
18
  > [!CAUTION]
7
19
  > 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
20
 
21
+ > [!CAUTION]
22
+ > 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.
23
+
24
+
25
+ > [!IMPORTANT]
26
+ > 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"
27
+
28
+ Usdc ethereum network.
29
+
30
+
31
+
32
+
33
+
34
+
9
35
  > [!IMPORTANT]
10
36
  > 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
37
 
@@ -102,9 +128,21 @@ npm install -g whalibmob
102
128
  - [Register a New Number](#register-a-new-number)
103
129
  - [Connect](#connect)
104
130
  - [Saving & Restoring Sessions](#saving--restoring-sessions)
131
+ - [Signal Store Utilities](#signal-store-utilities)
132
+ - [makeCacheableSignalKeyStore](#makecacheablesignalkeystore)
133
+ - [addTransactionCapability](#addtransactioncapability)
134
+ - [assertMeId](#assertmeid)
135
+ - [initAuthCreds](#initauthcreds)
105
136
  - [Handling Events](#handling-events)
106
137
  - [Example to Start](#example-to-start)
107
138
  - [All Events](#all-events)
139
+ - [History Sync](#history-sync)
140
+ - [How It Works](#how-history-sync-works)
141
+ - [Listening to History Sync Events](#listening-to-history-sync-events)
142
+ - [Persistent Files Written to Disk](#persistent-files-written-to-disk)
143
+ - [Reading the History Store](#reading-the-history-store)
144
+ - [tcToken — Error 463 Defense](#tctoken--error-463-defense)
145
+ - [What Is Automatic vs What You Need to Do](#what-is-automatic-vs-what-you-need-to-do)
108
146
  - [Receiving Media](#receiving-media)
109
147
  - [Sending Messages](#sending-messages)
110
148
  - [Text Message](#text-message)
@@ -234,34 +272,6 @@ if (result.status === 'ok') {
234
272
  }
235
273
  ```
236
274
 
237
- #### Device attestation (Frida)
238
-
239
- WhatsApp gates registration behind **device attestation** — a Google **Play
240
- Integrity** verdict on Android and Apple **App Attest** on iOS. Those tokens can
241
- only be minted on a genuine device, which is why datacenter registrations are
242
- often refused.
243
-
244
- whalibmob ships the same Frida middleware [Auties00/Cobalt](https://github.com/Auties00/Cobalt)
245
- uses, under [`frida/`](frida/README.md). The scripts run on a rooted/jailbroken
246
- device and expose a local HTTP server (port `1119` = WhatsApp, `1120` = Business);
247
- whalibmob then attaches `gpia` (Play Integrity), the `H=` Keystore signature and
248
- the `Authorization` certificate chain to every `/exist`, `/code` and `/register`
249
- request — exactly like a real device.
250
-
251
- It is **off by default**. Enable it once the device server is reachable:
252
-
253
- ```sh
254
- # .env
255
- WA_FRIDA_ATTESTATION=1
256
- WA_FRIDA_HOST=127.0.0.1 # e.g. after: adb forward tcp:1119 tcp:1119
257
- WA_FRIDA_PORT=1119 # 1119 WhatsApp, 1120 Business
258
- # WA_FRIDA_OPTIONAL=1 # don't fail a request if the server is down
259
- ```
260
-
261
- See [`frida/README.md`](frida/README.md) for the full device setup. When the flag
262
- is unset, `requestSmsCode` / `verifyCode` behave exactly as before and never
263
- contact the device.
264
-
265
275
  ### Connect
266
276
 
267
277
  ```js
@@ -295,6 +305,138 @@ await client.init('919634847671')
295
305
  > [!NOTE]
296
306
  > Each phone number uses its own session file. The library handles Signal Protocol key persistence automatically.
297
307
 
308
+ ## Signal Store Utilities
309
+
310
+ `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).
311
+
312
+ ```js
313
+ const {
314
+ makeCacheableSignalKeyStore,
315
+ addTransactionCapability,
316
+ assertMeId,
317
+ initAuthCreds
318
+ } = require('whalibmob')
319
+ ```
320
+
321
+ ### `makeCacheableSignalKeyStore`
322
+
323
+ 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.
324
+
325
+ `useClones` is set to `false` so that `SessionRecord` objects — which carry internal state and methods — are returned by reference and never deep-cloned.
326
+
327
+ The wrapper also forwards `transaction()` and `isInTransaction()` calls to the underlying store when present, making it safe to stack with `addTransactionCapability`.
328
+
329
+ ```js
330
+ const { SignalStore } = require('whalibmob')
331
+ const { makeCacheableSignalKeyStore } = require('whalibmob')
332
+
333
+ const store = new SignalStore(/* ... */)
334
+ const cached = makeCacheableSignalKeyStore(store)
335
+
336
+ // reads hit cache after first access
337
+ const session = await cached.getSession('919634847671@s.whatsapp.net:0')
338
+ ```
339
+
340
+ **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.
341
+
342
+ ### `addTransactionCapability`
343
+
344
+ 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.
345
+
346
+ Uses `AsyncLocalStorage` to propagate transaction context across async call chains, and a per-key-type `Mutex` with reference-counting to serialize concurrent writers safely.
347
+
348
+ ```js
349
+ const { addTransactionCapability, makeCacheableSignalKeyStore } = require('whalibmob')
350
+
351
+ // recommended: cache first, then transactions on top
352
+ const base = new SignalStore(/* ... */)
353
+ const cached = makeCacheableSignalKeyStore(base)
354
+ const txnStore = addTransactionCapability(cached)
355
+
356
+ // inside a send flow
357
+ await txnStore.transaction(async () => {
358
+ // all writes are buffered
359
+ await txnStore.setSession('919634847671@s.whatsapp.net:0', sessionRecord)
360
+ await txnStore.setPreKey(1, preKeyPair)
361
+ // commit is called automatically at the end of the transaction callback
362
+ })
363
+ ```
364
+
365
+ Stacking order matters: put `makeCacheableSignalKeyStore` below `addTransactionCapability` so that the cache always sees the committed state.
366
+
367
+ **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.
368
+
369
+ ### `assertMeId`
370
+
371
+ 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`.
372
+
373
+ ```js
374
+ const { assertMeId } = require('whalibmob')
375
+
376
+ const store = loadStore(sessFile)
377
+
378
+ try {
379
+ const jid = assertMeId(store)
380
+ // jid === '919634847671@s.whatsapp.net'
381
+ console.log('account JID:', jid)
382
+ } catch (err) {
383
+ console.error('store is not registered:', err.message)
384
+ }
385
+ ```
386
+
387
+ **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.
388
+
389
+ ### `initAuthCreds`
390
+
391
+ 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`.
392
+
393
+ ```js
394
+ const { initAuthCreds, saveStore } = require('whalibmob')
395
+ const path = require('path')
396
+ const fs = require('fs')
397
+
398
+ const phone = '919634847671'
399
+ const sessDir = path.join(process.env.HOME, '.waSession')
400
+ const sessFile = path.join(sessDir, phone + '.json')
401
+
402
+ fs.mkdirSync(sessDir, { recursive: true })
403
+
404
+ const store = initAuthCreds(phone)
405
+ saveStore(store, sessFile)
406
+ ```
407
+
408
+ This is the function used internally by the CLI for all new session creation. Prefer it over `createNewStore` for forward compatibility.
409
+
410
+ > [!NOTE]
411
+ > `initAuthCreds` and `createNewStore` produce equivalent stores for all current library operations. The additional fields from `initAuthCreds` are there for future-proofing and interoperability.
412
+
413
+ ### Recommended Stacking Pattern
414
+
415
+ For a production multi-account server:
416
+
417
+ ```js
418
+ const {
419
+ SignalStore,
420
+ makeCacheableSignalKeyStore,
421
+ addTransactionCapability,
422
+ initAuthCreds,
423
+ saveStore,
424
+ loadStore
425
+ } = require('whalibmob')
426
+
427
+ // 1. load or create the credential store
428
+ let store = loadStore(sessFile) || initAuthCreds(phone)
429
+
430
+ // 2. build the layered Signal key store
431
+ const signalStore = new SignalStore(store)
432
+ const cachedStore = makeCacheableSignalKeyStore(signalStore)
433
+ const txnStore = addTransactionCapability(cachedStore)
434
+
435
+ // 3. pass to the client (advanced usage — most users should use WhalibmobClient directly)
436
+ ```
437
+
438
+ For standard usage, `WhalibmobClient` handles all of this internally. These helpers are for advanced scenarios where you need direct control over Signal key storage.
439
+
298
440
  ## Handling Events
299
441
 
300
442
  whalibmob uses the EventEmitter syntax for events.
@@ -421,6 +563,261 @@ The `decoded` object shape per message type:
421
563
  { type: 'protocol', subtype: string }
422
564
  ```
423
565
 
566
+ ---
567
+
568
+ ## History Sync
569
+
570
+ ### How History Sync Works
571
+
572
+ When whalibmob connects, WhatsApp automatically sends the account's chat history to the client.
573
+ The library handles the entire pipeline **without any code from you** — you only need to listen
574
+ to the events if you want to use the data.
575
+
576
+ The full internal flow:
577
+
578
+ 1. WhatsApp server sends an encrypted `ProtocolMessage` (type 6) containing a `HistorySyncNotification`.
579
+ 2. The library decrypts it via Signal Protocol.
580
+ 3. `HistorySyncHandler` downloads the encrypted blob from WhatsApp's CDN (`mmg.whatsapp.net`), or reads the inline payload if the server embedded it directly.
581
+ 4. The blob is decrypted with **AES-256-CBC** using an HKDF key derived from `"WhatsApp History Keys"`.
582
+ 5. The result is decompressed with **zlib** and decoded from **protobuf** (WAProto v2.3000.x — field numbers verified against the official proto definition).
583
+ 6. Chats, contacts, push names, LID↔PN mappings, and **tcTokens** are merged into the disk store.
584
+ 7. tcTokens from history are seeded into `TcTokenStore` in memory so the first outbound DM after reconnect already carries a valid `<tctoken>` node (prevents error 463 on cold start).
585
+ 8. The `history_sync` event fires with a summary of what was received.
586
+
587
+ History arrives in **multiple chunks**. Each chunk fires one `history_sync` event. The first chunk (sync type `INITIAL_BOOTSTRAP`) is usually the largest and carries the most recent conversations.
588
+
589
+ ### Listening to History Sync Events
590
+
591
+ ```js
592
+ const { WhalibmobClient } = require('whalibmob')
593
+ const path = require('path')
594
+
595
+ const client = new WhalibmobClient({
596
+ sessionDir: path.join(process.env.HOME, '.waSession')
597
+ })
598
+
599
+ // history_sync fires once per history chunk — may fire multiple times on first connect
600
+ client.on('history_sync', result => {
601
+ console.log('History sync chunk received:')
602
+ console.log(' type :', result.syncTypeName) // e.g. 'INITIAL_BOOTSTRAP', 'RECENT', 'FULL'
603
+ console.log(' progress:', result.progress) // 0-100 server-reported
604
+ console.log(' chunk :', result.chunkOrder)
605
+ console.log(' chats :', result.chats.length)
606
+ console.log(' contacts:', result.contacts.length)
607
+ console.log(' pushNames:', (result.pushNames || []).length)
608
+ })
609
+
610
+ // history_sync_error fires if a chunk fails to download or decrypt
611
+ client.on('history_sync_error', ({ err, notification }) => {
612
+ console.error('History sync failed:', err.message)
613
+ console.error(' syncType:', notification.syncType)
614
+ })
615
+
616
+ await client.init('919634847671')
617
+ ```
618
+
619
+ The `result` object shape emitted by `history_sync`:
620
+
621
+ ```js
622
+ {
623
+ syncType: number, // HistorySyncType enum value
624
+ syncTypeName: string, // 'INITIAL_BOOTSTRAP' | 'RECENT' | 'FULL' | 'PUSH_NAME' | 'NON_BLOCKING_DATA' | 'ON_DEMAND'
625
+ progress: number, // 0-100, server-reported progress
626
+ chunkOrder: number, // chunk sequence number
627
+ chats: [ // one entry per conversation in this chunk
628
+ {
629
+ id: string, // chat JID e.g. '919634847671@s.whatsapp.net' or '120363...@g.us'
630
+ name: string, // display name (may be undefined for unknown contacts)
631
+ unreadCount: number,
632
+ lastMsgTimestamp: number, // Unix seconds
633
+ messageCount: number // number of messages in this chunk for this chat
634
+ }
635
+ ],
636
+ contacts: [ // one entry per contact discovered in this chunk
637
+ {
638
+ id: string,
639
+ name: string,
640
+ username: string, // WhatsApp username if set
641
+ pnJid: string, // phone-number JID e.g. '919634847671@s.whatsapp.net'
642
+ lidJid: string // LID JID e.g. '112345678901234@lid'
643
+ }
644
+ ],
645
+ pushNames: Array, // push-name entries { id, pushname }
646
+ lidPnMappings: Array, // LID<->PN mapping entries { lidJid, pnJid }
647
+ merged: object // raw merged history store (see Reading the History Store below)
648
+ }
649
+ ```
650
+
651
+ Sync type values:
652
+
653
+ | `syncTypeName` | When it fires |
654
+ |---|---|
655
+ | `INITIAL_BOOTSTRAP` | First connect — most recent conversations |
656
+ | `RECENT` | Reconnect after a short offline period |
657
+ | `FULL` | Full historical sync (older messages) |
658
+ | `PUSH_NAME` | Contact name updates only |
659
+ | `NON_BLOCKING_DATA` | Background low-priority data |
660
+ | `ON_DEMAND` | Explicitly requested by the client |
661
+
662
+ ### Persistent Files Written to Disk
663
+
664
+ The library automatically writes these files to `sessionDir` per account. You do not need to create or manage them.
665
+
666
+ | File | Contents |
667
+ |---|---|
668
+ | `<phone>.history.json` | Chats, contacts, push names, LID↔PN mappings, tcTokens |
669
+ | `<phone>.messages.json` | Flat map of `msgId → message metadata` |
670
+ | `<phone>.appStateKeys.json` | App-state sync keys (used for app-state patch decryption) |
671
+ | `<phone>.tctoken.json` | Trusted-contact token store (tcToken per contact JID) |
672
+
673
+ ### Reading the History Store
674
+
675
+ After history sync completes you can read the on-disk files directly:
676
+
677
+ ```js
678
+ const fs = require('fs')
679
+ const path = require('path')
680
+
681
+ const sessDir = path.join(process.env.HOME, '.waSession')
682
+ const phone = '919634847671'
683
+
684
+ // ── Read chats ────────────────────────────────────────────────────────────────
685
+ const histPath = path.join(sessDir, phone + '.history.json')
686
+ const hist = JSON.parse(fs.readFileSync(histPath, 'utf8'))
687
+
688
+ // List all chats sorted by last message time
689
+ const chats = Object.values(hist.chats)
690
+ .sort((a, b) => (b.lastMsgTimestamp || 0) - (a.lastMsgTimestamp || 0))
691
+
692
+ for (const chat of chats.slice(0, 10)) {
693
+ console.log(chat.id, '|', chat.name || '(unknown)', '|', chat.unreadCount, 'unread')
694
+ }
695
+
696
+ // ── LID ↔ PN lookup ───────────────────────────────────────────────────────────
697
+ // Look up LID JID from phone number JID
698
+ const myLid = hist.pnLidMap['919634847671@s.whatsapp.net']
699
+ console.log('LID:', myLid) // e.g. '112345678901234@lid'
700
+
701
+ // Reverse: phone number JID from LID
702
+ const myPn = hist.lidPnMap[myLid]
703
+ console.log('PN:', myPn)
704
+
705
+ // ── Read message metadata ─────────────────────────────────────────────────────
706
+ const msgPath = path.join(sessDir, phone + '.messages.json')
707
+ const msgs = JSON.parse(fs.readFileSync(msgPath, 'utf8'))
708
+ const msgList = Object.values(msgs).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
709
+ console.log('Total messages indexed:', msgList.length)
710
+ console.log('Latest:', msgList[0])
711
+ ```
712
+
713
+ `hist.chats` schema per chat entry:
714
+
715
+ ```js
716
+ {
717
+ id: string, // JID
718
+ name: string,
719
+ displayName: string,
720
+ unreadCount: number,
721
+ lastMsgTimestamp: number, // Unix seconds
722
+ messageCount: number, // total messages indexed from history
723
+ ephemeralExpiry: number, // disappearing messages timer in seconds, if set
724
+ archived: boolean,
725
+ pinned: number, // pin sort order (0 = not pinned)
726
+ tcToken: string, // base64 — trusted-contact token (used internally by the library)
727
+ tcTokenTimestamp: number, // Unix seconds — when the token was issued by the server
728
+ tcTokenSenderTimestamp: number // Unix seconds — sender-side issuance timestamp for 7-day bucket dedup
729
+ }
730
+ ```
731
+
732
+ `msgs` schema per message entry:
733
+
734
+ ```js
735
+ {
736
+ id: string, // WhatsApp message ID
737
+ chatId: string, // JID of the conversation
738
+ fromMe: boolean,
739
+ fromJid: string, // sender JID
740
+ timestamp: number, // Unix seconds
741
+ pushName: string, // display name of sender at send time
742
+ status: number // 0=error 1=pending 2=server 3=delivered 4=read 5=played
743
+ }
744
+ ```
745
+
746
+ ### tcToken — Error 463 Defense
747
+
748
+ > [!IMPORTANT]
749
+ > This section is informational. The entire tcToken lifecycle is **fully automatic**.
750
+ > You do not need to write any code for it.
751
+
752
+ WhatsApp counts every outbound DM sent **without** a `<tctoken>` node as an anonymous "reach-out" event. Once enough such events accumulate the server enforces a time-based **Reach-out Time-lock** and returns error `463` (`NackCallerReachoutTimelocked`), blocking all outbound messages and calls for a period.
753
+
754
+ whalibmob implements the full lifecycle to prevent this:
755
+
756
+ | Step | What the library does automatically |
757
+ |---|---|
758
+ | **History seed** | On every history sync chunk, `tcToken` bytes are extracted from each conversation in the protobuf and loaded into `TcTokenStore` in memory. The first send after reconnect already has a valid token ready — no 463 risk on cold start. |
759
+ | **Attach on send** | Before dispatching any DM, `MessageSender` looks up the token for the recipient JID, checks it has not expired (28-day rolling window), and pushes a `<tctoken>` child node into the message stanza. |
760
+ | **Proactive issuance** | After each successful DM send, the library fires a `<iq type='set' xmlns='privacy'>` requesting a fresh token for that JID from the server — once per 7-day bucket, deduplicated in-flight. |
761
+ | **Incoming notification** | When a contact starts a new conversation, WhatsApp pushes a `<notification type='privacy_token'>`. The library catches it in `_handlePrivacyTokenNotification` and stores the token immediately. |
762
+ | **Identity change re-issue** | When decrypting a `pkmsg` (new Signal session from peer), the library calls `_reissueTcTokenAfterIdentityChange` to re-issue the token for the new session. |
763
+ | **Error 463 recovery** | If a send fails with error 463, the library issues a fresh token, waits for the server response, and automatically retries the same message with the new token attached. |
764
+ | **Expiry** | Tokens use a 4-bucket rolling window (4 × 7 days = 28-day TTL). Expired tokens are cleared before the send and a fresh one is requested proactively. |
765
+
766
+ Token storage uses the **LID JID** of the contact (e.g. `112345678901234@lid`) as the key — never the phone-number JID — matching WhatsApp's internal convention.
767
+
768
+ ### What Is Automatic vs What You Need to Do
769
+
770
+ **Everything in the "Automatic" column requires zero code from you.**
771
+
772
+ | Feature | Automatic | Notes |
773
+ |---|---|---|
774
+ | Download history blob from CDN | ✅ | |
775
+ | Decrypt history blob (AES-256-CBC + HKDF) | ✅ | |
776
+ | Decompress zlib | ✅ | |
777
+ | Decode protobuf (WAProto v2.3000.x) | ✅ | Field numbers verified against official proto |
778
+ | Persist chats / contacts / push names | ✅ | Written to `<phone>.history.json` |
779
+ | Persist message metadata | ✅ | Written to `<phone>.messages.json` |
780
+ | Persist app-state sync keys | ✅ | Written to `<phone>.appStateKeys.json` |
781
+ | Seed tcTokens into memory on connect | ✅ | Prevents error 463 on first send after reconnect |
782
+ | Attach tcToken to every outbound DM | ✅ | |
783
+ | Issue fresh tcTokens after each send | ✅ | Once per 7-day bucket per contact |
784
+ | Handle incoming `privacy_token` notifications | ✅ | |
785
+ | Re-issue tcToken after peer identity change | ✅ | |
786
+ | Recover from error 463 with automatic retry | ✅ | |
787
+ | Populate in-memory LID↔PN maps | ✅ | |
788
+ | Listen to `history_sync` event | 🔵 Optional | Only if your app needs to react to history data |
789
+ | Read `<phone>.history.json` | 🔵 Optional | Only if your app needs chat/contact data at rest |
790
+ | Read `<phone>.messages.json` | 🔵 Optional | Only if your app indexes messages |
791
+
792
+ **Minimum working integration — history sync, tcTokens, and error-463 defense all active with zero extra code:**
793
+
794
+ ```js
795
+ const { WhalibmobClient } = require('whalibmob')
796
+ const path = require('path')
797
+
798
+ const client = new WhalibmobClient({
799
+ sessionDir: path.join(process.env.HOME, '.waSession')
800
+ })
801
+
802
+ // History sync, tcToken seeding, error-463 defense, and all disk persistence
803
+ // happen automatically. Add the listeners below only if your app needs the data.
804
+
805
+ client.on('history_sync', result => {
806
+ // Optional — fires once per chunk (multiple times on first connect)
807
+ console.log('[', result.syncTypeName, ']',
808
+ result.chats.length, 'chats,',
809
+ result.contacts.length, 'contacts')
810
+ })
811
+
812
+ client.on('history_sync_error', ({ err }) => {
813
+ // Optional — log failures (library continues working even if a chunk fails)
814
+ console.error('History chunk failed:', err.message)
815
+ })
816
+
817
+ await client.init('919634847671')
818
+ ```
819
+
820
+
424
821
  ## Receiving Media
425
822
 
426
823
  When a media message arrives, `msg.decoded` contains a CDN `url` and a `mediaKey`.
@@ -2317,43 +2714,6 @@ These variables override individual fields on top of the selected profile:
2317
2714
  | `WA_VERSION` | Pin the WhatsApp version (e.g. `2.24.13.80`). Skips the live store fetch. |
2318
2715
  | `WA_STATIC_TOKEN` | Override the static token used in registration token computation. |
2319
2716
 
2320
- ### SIM & Locale Overrides
2321
-
2322
- During registration the library derives `sim_mcc`, `sim_mnc`, `lg` and `lc` from the
2323
- country code of the number being registered, so the request looks like a SIM from
2324
- that country. Override them when the guessed operator is wrong for your number
2325
- (MVNOs, ported numbers, VoIP ranges):
2326
-
2327
- | Variable | Description |
2328
- |---|---|
2329
- | `WA_SIM_MCC` | Mobile Country Code (e.g. `262` for Germany) |
2330
- | `WA_SIM_MNC` | Mobile Network Code (e.g. `01` for Telekom) |
2331
- | `WA_LG` | Language tag, ISO 639-1 (e.g. `de`) |
2332
- | `WA_LC` | Country tag, ISO 3166-1 alpha-2 (e.g. `DE`) |
2333
- | `WA_PUSH_TOKEN` | APNS/FCM push token to include on the `/code` request. Only sent when set. |
2334
- | `WA_PUSH_CODE` | Silent-push verification code to include on the `/code` request. Only sent when set. |
2335
-
2336
- ### Proxy (SOCKS5 / Tor)
2337
-
2338
- WhatsApp routinely refuses registration from datacenter and VPS IPs — that is the
2339
- most common cause of `reason: "no_routes"` and of security blocks. Route
2340
- registration traffic through a residential or mobile proxy:
2341
-
2342
- ```sh
2343
- npm install socks
2344
- export TOR_PROXY=socks5://127.0.0.1:9050
2345
- # with credentials:
2346
- export TOR_PROXY=socks5://user:pass@proxy.example.com:1080
2347
- ```
2348
-
2349
- | Variable | Description |
2350
- |---|---|
2351
- | `TOR_PROXY` | SOCKS4/SOCKS5 proxy URL for registration traffic. Supports `user:pass@`. |
2352
- | `SOCKS_PROXY` | Alias for `TOR_PROXY`. |
2353
-
2354
- `socks` is an optional dependency: it is only required when one of these variables
2355
- is set, so installs without a proxy are unaffected.
2356
-
2357
2717
  ## License
2358
2718
 
2359
2719
  MIT