whalibmob 5.29.2 → 5.29.4

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
@@ -2473,6 +2473,7 @@ Run it once, type the code into the phone, and it is linked. Run it again and it
2473
2473
  | `client.connectWeb(phone, opts?)` | Open the companion connection. Resolves as soon as the encrypted channel is up when unlinked, or after `<success>` when already linked. |
2474
2474
  | `client.requestPairingCode(phone?, customCode?)` | Ask for an 8-character code. Returns it immediately; the link completes later. Throws if the session is already linked. |
2475
2475
  | `client.disconnect()` | Close the connection. The link survives — reconnect with `connectWeb()`. |
2476
+ | `client.logout()` | Unlink this device from the account, the way the Linked Devices screen does, then disconnect. The link does **not** survive — the next `connectWeb()` pairs afresh. Nothing on disk is deleted; remove the session directory yourself if you want the keys gone. |
2476
2477
 
2477
2478
  `connectWeb(phone, opts)` options:
2478
2479
 
@@ -3778,13 +3779,36 @@ The whole message object is accepted as well as its `decoded` half, so
3778
3779
 
3779
3780
  **Verifying the file**
3780
3781
 
3781
- Pass `{ verify: true }` to check the download against the message's
3782
- `fileEncSha256` before decrypting it. The MAC already proves the plaintext was
3783
- not tampered with; this catches a truncated or substituted download earlier, and
3784
- names that failure separately from a decryption one.
3782
+ Three things are checked, and all three by default — the message says what the
3783
+ file should be, so there is no reason to take the CDN's word for it:
3784
+
3785
+ | check | what it covers |
3786
+ |---|---|
3787
+ | `fileEncSha256` | the blob as it arrived, before any work is spent decrypting it |
3788
+ | the MAC | the ciphertext, under a key derived from `mediaKey` — this is the one that authenticates |
3789
+ | `fileSha256` | the decrypted file |
3790
+
3791
+ ```js
3792
+ const bytes = await client.downloadMedia(d) // all three
3793
+ const raw = await client.downloadMedia(d, { verify: false }) // MAC only
3794
+ ```
3795
+
3796
+ `verify: false` skips the two digests for a caller that wants the bytes whatever
3797
+ they are. The MAC is checked either way and is never optional.
3798
+
3799
+ **Link preview thumbnails**
3800
+
3801
+ A text message that quoted a link carries the preview the sender's client built.
3802
+ The small inline image is already on the message as `jpegThumbnail` and needs no
3803
+ download. The full-size one is a media file of its own — its own `directPath`,
3804
+ its own `mediaKey`, encrypted under a key name of its own — so `downloadMedia`
3805
+ cannot reach it:
3785
3806
 
3786
3807
  ```js
3787
- const bytes = await client.downloadMedia(d, { verify: true })
3808
+ if (d.thumbnail) {
3809
+ const preview = await client.downloadThumbnail(d)
3810
+ // d.title, d.description and d.matchedText are the rest of the preview
3811
+ }
3788
3812
  ```
3789
3813
 
3790
3814
  **What happens underneath**
@@ -3991,10 +4015,36 @@ const { id, encKey } = await client.sendPoll(
3991
4015
  ['JavaScript', 'Python', 'Rust'],
3992
4016
  1 // voters may pick 1 option (0 = unlimited)
3993
4017
  )
3994
- // encKey (32-byte Buffer) is needed to decrypt incoming poll votes
4018
+ // encKey (32-byte Buffer) is what reads the votes — keep it
3995
4019
  // (also returned as `messageSecret`, which is the name the protocol uses)
3996
4020
  ```
3997
4021
 
4022
+ **Reading the votes**
4023
+
4024
+ A vote arrives as its own message and the ballot inside it is encrypted, so
4025
+ nothing about who chose what is visible in the stanza. `decryptPollVote()` opens
4026
+ it with the poll's `encKey`:
4027
+
4028
+ ```js
4029
+ const options = ['JavaScript', 'Python', 'Rust']
4030
+
4031
+ client.on('message', (msg) => {
4032
+ if (msg.decoded?.type !== 'pollVote') return
4033
+ if (msg.decoded.pollKey.id !== id) return // a vote on some other poll
4034
+
4035
+ const { selected, votedAt } = client.decryptPollVote(msg, { encKey, options })
4036
+ console.log(msg.participant, 'voted for', selected) // [ 'Rust' ]
4037
+ })
4038
+ ```
4039
+
4040
+ Pass `options` and the choices come back as text. Without them you get
4041
+ `selectedHashes` — the SHA-256 of each chosen option, which is how the ballot
4042
+ names them on the wire — and match them yourself.
4043
+
4044
+ The key is derived from the poll id, the poll's sender and the voter, so a vote
4045
+ on someone else's poll needs their `encKey`, which only arrives if they sent the
4046
+ poll to you: read it off `msg.decoded.encKey` on the poll message itself.
4047
+
3998
4048
  ### Quoted Reply
3999
4049
 
4000
4050
  Send a text message that quotes (replies to) a specific earlier message. The recipient sees the original message highlighted above your reply.
@@ -5457,6 +5507,30 @@ These variables override individual fields on top of the selected profile:
5457
5507
  | `WA_DEVICE_OS_VERSION` | OS version string (e.g. `14`) |
5458
5508
  | `WA_DEVICE_BUILD` | Build fingerprint (e.g. `UP1A.231005.007`) |
5459
5509
  | `WA_DEVICE_MODEL_ID` | Model ID slug (e.g. `samsung-sm-s928b`) |
5510
+ | `WA_DEVICE_RAM` | Usable RAM in GiB, as Android reports it (e.g. `7.53`). Each named profile carries its own; set this only for a different memory variant of the same model. |
5511
+
5512
+ ### Declaring your SIM
5513
+
5514
+ Registration sends `sim_mcc` / `sim_mnc` to say which network the SIM belongs
5515
+ to. The built-in table can only name **one operator per country** — Romania is
5516
+ always Orange, Brazil always Vivo — and it cannot do better, because number
5517
+ portability broke the prefix→operator link years ago. Guessing from the number
5518
+ would be wrong more often than the table is.
5519
+
5520
+ So say which SIM is in the phone:
5521
+
5522
+ ```js
5523
+ const store = createNewStore('40712345678', { simMcc: '226', simMnc: '01' })
5524
+ ```
5525
+
5526
+ | Variable | Description |
5527
+ |---|---|
5528
+ | `WA_SIM_MCC` | Mobile country code of the SIM (e.g. `226`) |
5529
+ | `WA_SIM_MNC` | Mobile network code. **Width matters** — `10` and `010` are different networks to the server, so write it exactly as your operator does. |
5530
+
5531
+ Either half can be given on its own; the other keeps the table's value, and the
5532
+ country's language and locale are unaffected either way. Leave both unset and
5533
+ nothing changes from before.
5460
5534
 
5461
5535
  ### Version & Token Overrides
5462
5536
 
@@ -5641,6 +5715,24 @@ registered, no code is requested, and the session is never written to.
5641
5715
  the problem.** The difference is then in what the CLI reads and this tool does
5642
5716
  not — `WA_VERSION`, from `.env`. Go back to the top of this section.
5643
5717
 
5718
+ ## Support this project
5719
+
5720
+ If whalibmob saves you time, you can support its development.
5721
+
5722
+ **USDT — TRC-20 (Tron network only)**
5723
+
5724
+ ```
5725
+ TNxxWvAc5m5YS89uz5uSrbXC9EKPuS27aP
5726
+ ```
5727
+
5728
+ > [!WARNING]
5729
+ > This address is **TRC-20 on the Tron network**. Send **only USDT on TRC-20** to
5730
+ > it. USDT sent on any other network — ERC-20 (Ethereum), BEP-20 (BSC), Polygon,
5731
+ > or anything else — will be **lost and cannot be recovered**. Always double-check
5732
+ > the network in your wallet before sending, and copy the address in full.
5733
+
5734
+ The **Sponsor** button at the top of the repository leads back here.
5735
+
5644
5736
  ## License
5645
5737
 
5646
5738
  MIT
package/index.d.ts CHANGED
@@ -271,6 +271,11 @@ export interface DecodedBase {
271
271
  ephemeral?: boolean;
272
272
  /** The message is the new text of an edit. */
273
273
  edited?: boolean;
274
+ /**
275
+ * On a text message that quoted a link, the downloadable preview image.
276
+ * Fetch it with `downloadThumbnail()`.
277
+ */
278
+ thumbnail?: LinkThumbnail;
274
279
  /**
275
280
  * Present only on a message this account sent from one of its other devices.
276
281
  * The stanza names us as the sender, so the chat it belongs to is written
@@ -280,6 +285,34 @@ export interface DecodedBase {
280
285
  [key: string]: any;
281
286
  }
282
287
 
288
+ /** The downloadable preview image on a message that quoted a link. */
289
+ export interface LinkThumbnail {
290
+ directPath: string;
291
+ mediaKey: Buffer;
292
+ thumbnailSha256: Buffer | null;
293
+ thumbnailEncSha256: Buffer | null;
294
+ }
295
+
296
+ export interface PollVoteOptions {
297
+ /** The poll's 32-byte message secret — what `sendPoll` returned. */
298
+ encKey: Buffer;
299
+ /** Who cast the vote. Defaults to the message's `participant`, then `from`. */
300
+ voter?: Jid;
301
+ /** Who sent the poll. Defaults to what the vote's message key names. */
302
+ pollSender?: Jid;
303
+ /** The poll's options, so the choices come back as text rather than hashes. */
304
+ options?: string[];
305
+ }
306
+
307
+ export interface PollVoteResult {
308
+ /** The chosen options as text. Empty unless `options` was passed. */
309
+ selected: string[];
310
+ /** SHA-256 of each chosen option, always present. */
311
+ selectedHashes: Buffer[];
312
+ /** When the vote was cast, in ms, when the sender said. */
313
+ votedAt: number | null;
314
+ }
315
+
283
316
  /** What a `DeviceSentMessage` envelope said about the message inside it. */
284
317
  export interface DeviceSentMeta {
285
318
  /** The chat the message was originally addressed to. */
@@ -856,6 +889,15 @@ export declare class WhalibmobClient extends EventEmitter {
856
889
  /** Ask for an 8-character pairing code. Throws if the session is already linked. */
857
890
  requestPairingCode(phoneNumber?: string, customCode?: string): Promise<string>;
858
891
  disconnect(opts?: DisconnectOptions): void;
892
+ /**
893
+ * Unlink this device from the account, the way the Linked Devices screen on
894
+ * the phone does, then close the connection.
895
+ *
896
+ * Web only. Nothing on disk is deleted — the session it describes no longer
897
+ * exists, so the next `connectWeb()` pairs afresh. Resolves to whether the
898
+ * server accepted the removal; the connection drops either way.
899
+ */
900
+ logout(): Promise<boolean>;
859
901
 
860
902
  /** Ask the server whether this session still logs in, and under which number. */
861
903
  checkSessionAlive(): Promise<SessionAliveProbe>;
@@ -882,8 +924,30 @@ export declare class WhalibmobClient extends EventEmitter {
882
924
  createCallLink(type?: 'audio' | 'video', opts?: { startTime?: number }): Promise<CallLink>;
883
925
 
884
926
  // ─── Receiving ───────────────────────────────────────────────────────────
885
- /** Download and decrypt a media message. Accepts `msg` or `msg.decoded`. */
927
+ /**
928
+ * Download and decrypt a media message. Accepts `msg` or `msg.decoded`.
929
+ *
930
+ * Both digests the message carried are checked by default, on top of the MAC
931
+ * that always is. `verify: false` skips the digests only.
932
+ */
886
933
  downloadMedia(decoded: DecodedMessage | IncomingMessage, opts?: { verify?: boolean }): Promise<Buffer>;
934
+ /**
935
+ * Download the full-size preview image of a message that quoted a link.
936
+ *
937
+ * A link preview's thumbnail is a media file of its own, encrypted under its
938
+ * own key name, so `downloadMedia` cannot reach it. The small inline preview
939
+ * needs no download — it is already on the message as `jpegThumbnail`.
940
+ */
941
+ downloadThumbnail(decoded: DecodedMessage | IncomingMessage, opts?: { verify?: boolean }): Promise<Buffer>;
942
+ /**
943
+ * Read a vote on a poll.
944
+ *
945
+ * The ballot is encrypted under a key derived from the poll's message secret:
946
+ * the `encKey` `sendPoll` returned, or `decoded.encKey` on a poll somebody
947
+ * else sent. Pass the poll's `options` to get the choices back as text —
948
+ * without them only the SHA-256 of each choice is available.
949
+ */
950
+ decryptPollVote(vote: DecodedMessage | IncomingMessage, opts: PollVoteOptions): PollVoteResult;
887
951
  /** Ask the sender's phone to re-upload a file the CDN no longer has. */
888
952
  requestMediaRetry(info: { id: string; chatJid: Jid; fromMe: boolean; participant?: Jid }, mediaKey: Buffer): Promise<void>;
889
953
  decryptMediaRetry(notification: MediaRetryNotification, mediaKey: Buffer): MediaRetryResult;
@@ -980,6 +1044,15 @@ export declare class WhalibmobClient extends EventEmitter {
980
1044
 
981
1045
  /** Pull pins/archives/mutes/stars/contact names changed elsewhere. */
982
1046
  syncAppState(names?: string[] | null, opts?: { snapshot?: boolean }): Promise<AppStateSyncResult>;
1047
+ /**
1048
+ * Acknowledge a dirty bit so the server stops announcing it.
1049
+ *
1050
+ * Done for you on every `<ib><dirty>` the server sends; this is here for a
1051
+ * bit you want to clear yourself. Pass the `timestamp` the announcement
1052
+ * carried — without it nothing in particular is acknowledged and the server
1053
+ * re-announces on the next connection.
1054
+ */
1055
+ markNotDirty(type: string, timestamp?: number | string): boolean;
983
1056
  /** Whether an app-state key is available — `false` on an SMS session with no companion. */
984
1057
  canSyncAppState(): boolean;
985
1058
  /**
@@ -1155,7 +1228,17 @@ export declare function fetchWaWebVersion(): Promise<{ version: [number, number,
1155
1228
  // Stores
1156
1229
  // ────────────────────────────────────────────────────────────────────────────
1157
1230
 
1158
- export declare function createNewStore(phoneNumber: string, opts?: { name?: string }): WhalibmobStore;
1231
+ export declare function createNewStore(phoneNumber: string, opts?: {
1232
+ name?: string;
1233
+ /**
1234
+ * The MCC of the SIM actually in the phone. Without it the country table's
1235
+ * one-operator-per-country guess stands — number portability means the
1236
+ * number's prefix cannot tell you. Also settable as `WA_SIM_MCC`.
1237
+ */
1238
+ simMcc?: string;
1239
+ /** The matching MNC. Width matters: `10` and `010` are different networks. */
1240
+ simMnc?: string;
1241
+ }): WhalibmobStore;
1159
1242
  /** What the CLI uses for every new SMS session; prefer it over `createNewStore`. */
1160
1243
  export declare function initAuthCreds(phoneNumber: string, opts?: { name?: string }): WhalibmobStore;
1161
1244
  export declare function saveStore(store: WhalibmobStore, filePath: string): void;