@neelegirly/baileys 2.2.17 → 2.2.19

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 (41) hide show
  1. package/README.md +109 -27
  2. package/lib/Socket/business.d.ts +6 -1
  3. package/lib/Socket/chats.d.ts +6 -1
  4. package/lib/Socket/chats.js +121 -28
  5. package/lib/Socket/communities.d.ts +5 -0
  6. package/lib/Socket/groups.d.ts +6 -1
  7. package/lib/Socket/groups.js +15 -1
  8. package/lib/Socket/index.d.ts +6 -1
  9. package/lib/Socket/messages-recv.d.ts +5 -0
  10. package/lib/Socket/messages-recv.js +26 -8
  11. package/lib/Socket/messages-send.d.ts +6 -1
  12. package/lib/Socket/newsletter.d.ts +6 -1
  13. package/lib/Socket/registration.d.ts +5 -0
  14. package/lib/Socket/socket.js +55 -29
  15. package/lib/Types/Call.d.ts +3 -2
  16. package/lib/Types/Contact.d.ts +3 -1
  17. package/lib/Types/Events.d.ts +14 -1
  18. package/lib/Types/GroupMetadata.d.ts +9 -1
  19. package/lib/Types/Message.d.ts +21 -3
  20. package/lib/Types/Signal.d.ts +17 -1
  21. package/lib/Utils/branding.js +47 -3
  22. package/lib/Utils/chat-utils.d.ts +21 -1
  23. package/lib/Utils/chat-utils.js +27 -8
  24. package/lib/Utils/decode-wa-message.js +5 -1
  25. package/lib/Utils/event-buffer.js +3 -1
  26. package/lib/Utils/generics.js +9 -0
  27. package/lib/Utils/history.js +10 -11
  28. package/lib/Utils/messages-media.js +2 -2
  29. package/lib/Utils/messages.js +19 -2
  30. package/lib/Utils/process-message.js +5 -4
  31. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +1 -1
  32. package/lib/WAUSync/Protocols/USyncContactProtocol.js +27 -4
  33. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
  34. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +31 -0
  35. package/lib/WAUSync/Protocols/index.d.ts +2 -1
  36. package/lib/WAUSync/Protocols/index.js +2 -1
  37. package/lib/WAUSync/USyncQuery.d.ts +2 -1
  38. package/lib/WAUSync/USyncQuery.js +5 -1
  39. package/lib/WAUSync/USyncUser.d.ts +4 -0
  40. package/lib/WAUSync/USyncUser.js +9 -1
  41. package/package.json +1 -1
package/README.md CHANGED
@@ -2,49 +2,90 @@
2
2
 
3
3
  # 🌸 @neelegirly/baileys 🌸
4
4
 
5
- ### *The cutest WhatsApp Web API glow-up for Node.js*
6
- ### *QR Branding · LID Support · Stable Sessions · Update Checks*
5
+ ### *Die WhatsApp Web API mit sauberem Neelegirly-Glow-up*
6
+ ### *QR Branding · Wrapper-Aware Update Notify · LID · Smart Queue*
7
7
 
8
- [![npm](https://img.shields.io/npm/v/@neelegirly/baileys?style=for-the-badge&color=ff69b4&logo=npm)](https://www.npmjs.com/package/@neelegirly/baileys)
9
- [![wa-api](https://img.shields.io/badge/wa--api-1.7.16-c77dff?style=for-the-badge)](https://www.npmjs.com/package/@neelegirly/wa-api)
8
+ [![Version](https://img.shields.io/badge/Version-2.2.19-ff69b4?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@neelegirly/baileys)
9
+ [![wa-api](https://img.shields.io/badge/wa--api-1.8.4-c77dff?style=for-the-badge)](https://www.npmjs.com/package/@neelegirly/wa-api)
10
10
  [![libsignal](https://img.shields.io/badge/libsignal-1.0.28-f4a261?style=for-the-badge)](https://www.npmjs.com/package/@neelegirly/libsignal)
11
+ [![Node](https://img.shields.io/badge/Node-16+-4caf50?style=for-the-badge&logo=node.js)](https://nodejs.org)
12
+ [![npm](https://img.shields.io/npm/v/%40neelegirly%2Fbaileys?style=for-the-badge&color=ff69b4&logo=npm)](https://www.npmjs.com/package/@neelegirly/baileys)
11
13
 
12
14
  <p align="center">
13
- <img src="https://files.catbox.moe/5bqumy.jpeg" width="420" alt="Neelegirly baileys hero" />
15
+ <img src="https://files.catbox.moe/5bqumy.jpeg" width="780" alt="@neelegirly/baileys Hero" />
14
16
  </p>
15
17
 
16
- **🎀 Release-Stack:** `@neelegirly/baileys 2.2.17` · `@neelegirly/libsignal 1.0.28` · `@neelegirly/wa-api 1.7.16`
18
+ <p align="center"><sub>2026 Glow-Up Edition · frische WA-API- und USync-Patches · Companion-Stack auf <strong>2.2.19 / 1.8.4 / 1.0.28</strong></sub></p>
19
+
20
+ [**Installation**](#-installation) · [**Quickstart**](#-quickstart) · [**Highlights**](#-highlights) · [**QR Branding**](#-qr-branding--update-status) · [**Migration**](#-namespace-migration) · [**Release Notes**](#-release-notes-2219)
17
21
 
18
22
  </div>
19
23
 
20
- Neelegirly-Variante von Baileys als WhatsApp-Web-Basis fuer Node.js — gemacht fuer stabile Bots, schoene QR-Flows und einen sauber gepinnten Multi-Session-Stack.
24
+ ---
25
+
26
+ Neelegirlys Variante von Baileys ist die WhatsApp-Web-Basis für stabile Bots, schöne QR-Flows und einen sauber gepinnten Multi-Session-Stack. Die Library bleibt nah an Baileys, aber mit fokussiertem Branding, Update-Notify und Begleit-Ökosystem für `@neelegirly/wa-api`.
21
27
 
22
28
  > Hinweis: Dieses Projekt ist nicht offiziell mit WhatsApp, Meta oder Baileys-Upstream verbunden.
23
29
 
30
+ ---
31
+
24
32
  ## ✨ Highlights
25
33
 
26
34
  | Feature | Beschreibung | Status |
27
35
  | --- | --- | --- |
28
36
  | 💖 Multi-Device API | WhatsApp Web auf Node.js mit Event-Flow | ✅ |
29
37
  | 📷 QR Branding | Header/Footer + Wrapper-/Versionskontext direkt im QR-Flow | ✅ |
30
- | 🔔 Update-Checks | npm zuerst, GitHub-Fallback, semver-sicher | ✅ |
31
- | 🧷 LID / Session-Fokus | passend fuer moderne Multi-Device-/Linked-ID-Flows | ✅ |
38
+ | 🔔 Wrapper-Aware Update Notify | Status für Baileys **und** optionalen Wrapper-Kontext | ✅ |
39
+ | 🚀 Smart Queue | Retries, Prioritäten und Bulk-Sending für produktive Setups | ✅ |
40
+ | 🧷 LID / Session-Fokus | passend für moderne Multi-Device-/Linked-ID-Flows | ✅ |
32
41
  | 🎀 Neelegirly Scope | sauber auf `@neelegirly/*` ausgerichtet | ✅ |
33
42
 
34
- ## 📦 Kompatibilitaet
43
+ ---
44
+
45
+ ## 🆕 Was sich in `v2.2.19` geändert hat
46
+
47
+ - ✅ Aktuelle Baileys-Upstream-Fixes vom 24./25. April 2026 sauber übernommen
48
+ - ✅ AB-Props-Query nutzt wieder den aktuellen `abt`/Protocol-1-Flow
49
+ - ✅ Username-Felder in Contacts, Gruppenmetadaten, Message Keys und USync ergänzt
50
+ - ✅ App-State-Sync robuster bei fehlenden Keys, Snapshot-Retry und Reconnects
51
+ - ✅ Call-Events, Album-Messages und History/Media-Streaming erweitert
52
+
53
+ ---
54
+
55
+ ## 📦 Kompatibilität
35
56
 
36
57
  | Paket | Empfohlene Version |
37
58
  | --- | --- |
38
- | `@neelegirly/baileys` | `2.2.17` |
39
- | `@neelegirly/wa-api` | `1.7.16` |
59
+ | `@neelegirly/baileys` | `2.2.19` |
60
+ | `@neelegirly/wa-api` | `1.8.4` |
40
61
  | `@neelegirly/libsignal` | `1.0.28` |
41
62
 
63
+ ---
64
+
42
65
  ## 🚀 Installation
43
66
 
67
+ ### npm
68
+
44
69
  ```bash
45
- npm install @neelegirly/baileys@2.2.17 @neelegirly/libsignal@1.0.28 --save-exact
70
+ npm install @neelegirly/baileys@2.2.19 @neelegirly/libsignal@1.0.28 --save-exact
46
71
  ```
47
72
 
73
+ ### yarn
74
+
75
+ ```bash
76
+ yarn add @neelegirly/baileys@2.2.19 @neelegirly/libsignal@1.0.28 --exact
77
+ ```
78
+
79
+ ### pnpm
80
+
81
+ ```bash
82
+ pnpm add @neelegirly/baileys@2.2.19 @neelegirly/libsignal@1.0.28 --save-exact
83
+ ```
84
+
85
+ > Für den kompletten Neelegirly-Stack passt dazu `@neelegirly/wa-api@1.8.4`.
86
+
87
+ ---
88
+
48
89
  ## ⚡ Quickstart
49
90
 
50
91
  ```ts
@@ -77,6 +118,7 @@ async function start() {
77
118
  if (connection === 'close') {
78
119
  const statusCode = lastDisconnect?.error?.output?.statusCode
79
120
  const isLoggedOut = statusCode === DisconnectReason.loggedOut
121
+
80
122
  if (!isLoggedOut) start().catch(console.error)
81
123
  }
82
124
  })
@@ -85,21 +127,33 @@ async function start() {
85
127
  start().catch(console.error)
86
128
  ```
87
129
 
88
- > Direktes `sock.ev.on('creds.update', saveCreds)` ist fuer kleine Demos okay. In produktiven Multi-Session-Setups sollten Credential-Saves gebuendelt/debounced werden, damit Session-Dateien bei vielen Updates stabil bleiben.
130
+ > Direktes `sock.ev.on('creds.update', saveCreds)` ist für kleine Demos okay. In produktiven Multi-Session-Setups sollten Credential-Saves gebündelt oder debounced werden.
89
131
 
90
- ## 🌐 Namespace-Migration
132
+ ---
91
133
 
92
- Wenn du vom Upstream kommst, nutze den Import-Scope von Neelegirly:
134
+ ## 🧩 Companion Stack
93
135
 
94
- ```diff
95
- - import makeWASocket from '@whiskeysockets/baileys'
96
- + import makeWASocket from '@neelegirly/baileys'
97
- ```
136
+ | Paket | Rolle |
137
+ |------|-------|
138
+ | `@neelegirly/baileys 2.2.19` | Socket, Events, Messaging |
139
+ | `@neelegirly/libsignal 1.0.28` | Signal-Protokoll-Komponente |
140
+ | `@neelegirly/wa-api 1.8.4` | Lifecycle-, Session- und Update-Wrapper |
141
+
142
+ ---
143
+
144
+ ## 🩷 QR Branding & Update Status
145
+
146
+ Beim QR-Scan werden automatisch Markenzeilen oberhalb und unterhalb des QR-Codes ausgegeben. Die Ausgabe liest Versionen dynamisch aus `package.json`, verarbeitet Update-Status robust und kann zusätzlich Wrapper-Kontext aus `@neelegirly/wa-api` darstellen.
147
+
148
+ Typische Anzeige:
98
149
 
99
- ## 🩷 QR-Branding und Versionsanzeige
150
+ - `Baileys Update-Status: up to date (2.2.19)`
151
+ - `Wrapper Update-Status: up to date (1.8.4)`
152
+ - Bei echten Updates werden kompakte Hinweise auf `latest` eingeblendet
100
153
 
101
- Beim QR-Scan werden automatisch Markenzeilen oberhalb und unterhalb des QR-Codes ausgegeben.
102
- Die Anzeige liest Versionen dynamisch aus `package.json`, erkennt Wrapper-Kontexte und zeigt bei Bedarf einen kompakten Update-Hinweis an.
154
+ Wenn kein Wrapper-Kontext vorhanden ist, bleibt die Anzeige sauber bei Baileys. Kein unnötiges Drama, nur QR.
155
+
156
+ ---
103
157
 
104
158
  ## 🔄 Update-Check
105
159
 
@@ -107,9 +161,37 @@ Die Anzeige liest Versionen dynamisch aus `package.json`, erkennt Wrapper-Kontex
107
161
  - Quelle 2: GitHub Releases (`neelegirly/baileys`) als Fallback
108
162
  - Fehler und Timeouts werden abgefangen, ohne den Prozess zu stoppen
109
163
  - Semver-Vergleich wird numerisch ausgewertet
164
+ - Wrapper-Status kann über die von `@neelegirly/wa-api` gesetzten Umgebungsvariablen übernommen werden
165
+
166
+ Wenn du direkt prüfen willst:
167
+
168
+ ```ts
169
+ import { checkNpmVersion } from '@neelegirly/baileys'
170
+
171
+ const info = await checkNpmVersion('@neelegirly/baileys', '2.2.19', {
172
+ githubRepo: 'neelegirly/baileys'
173
+ })
174
+
175
+ console.log(info)
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 🌐 Namespace-Migration
181
+
182
+ Wenn du vom Upstream kommst, nutze den Neelegirly-Scope:
183
+
184
+ ```diff
185
+ - import makeWASocket from '@whiskeysockets/baileys'
186
+ + import makeWASocket from '@neelegirly/baileys'
187
+ ```
188
+
189
+ ---
110
190
 
111
- ## 🎉 Release-Notizen
191
+ ## 📝 Release Notes `2.2.19`
112
192
 
113
- - README im Hero-/Glow-up-Stil komplett erneuert
114
- - Stack auf `2.2.17 / 1.7.16 / 1.0.28` aktualisiert
115
- - QR-/Versionsbranding bleibt zentral im Package verankert
193
+ - 🔧 Upstream-Änderungen vom 24./25. April 2026 portiert, ohne QR-/Update-Notify- oder Message-ID-Handling umzubauen
194
+ - 🧩 Username-USync und Inbound-Username-Felder für Kontakte, Gruppen und Message Keys ergänzt
195
+ - 🛡️ App-State-Sync und libsignal-LID/PN-Typen robuster gemacht
196
+ - 🖼️ Album-Message-Sending und erweiterte Call-Event-Typen ergänzt
197
+ - 🔗 Companion-Stack auf `2.2.19 / 1.8.4 / 1.0.28` aktualisiert
@@ -144,6 +144,11 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
144
144
  resyncAppState: (collections: readonly ("critical_block" | "critical_unblock_low" | "regular_high" | "regular_low" | "regular")[], isInitialSync: boolean) => Promise<void>
145
145
  chatModify: (mod: import("../Types").ChatModification, jid: string) => Promise<void>
146
146
  cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: string | number | undefined) => Promise<void>
147
+ serverProps: {
148
+ privacyTokenOn1to1: boolean
149
+ profilePicPrivacyToken: boolean
150
+ lidTrustedTokenIssueToLid: boolean
151
+ }
147
152
  addLabel: (jid: string, labels: import("../Types/Label").LabelActionBody) => Promise<void>
148
153
  addChatLabel: (jid: string, labelId: string) => Promise<void>
149
154
  removeChatLabel: (jid: string, labelId: string) => Promise<void>
@@ -184,4 +189,4 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
184
189
  requestPairingCode: (phoneNumber: string, code?: string) => Promise<string>
185
190
  waitForConnectionUpdate: (check: (u: Partial<import("../Types").ConnectionState>) => boolean | undefined, timeoutMs?: number | undefined) => Promise<void>
186
191
  sendWAMBuffer: (wamBuffer: Buffer) => Promise<BinaryNode>
187
- }
192
+ }
@@ -48,6 +48,11 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
48
48
  resyncAppState: (collections: readonly ("critical_block" | "critical_unblock_low" | "regular_high" | "regular_low" | "regular")[], isInitialSync: boolean) => Promise<void>
49
49
  chatModify: (mod: ChatModification, jid: string) => Promise<void>
50
50
  cleanDirtyBits: (type: 'account_sync' | 'groups', fromTimestamp?: number | string) => Promise<void>
51
+ serverProps: {
52
+ privacyTokenOn1to1: boolean
53
+ profilePicPrivacyToken: boolean
54
+ lidTrustedTokenIssueToLid: boolean
55
+ }
51
56
  addLabel: (jid: string, labels: LabelActionBody) => Promise<void>
52
57
  addChatLabel: (jid: string, labelId: string) => Promise<void>
53
58
  removeChatLabel: (jid: string, labelId: string) => Promise<void>
@@ -95,4 +100,4 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
95
100
  * requires the last messages till the last message received required for archive & unread
96
101
  */
97
102
  sendWAMBuffer: (wamBuffer: Buffer) => Promise<BinaryNode>
98
- }
103
+ }
@@ -21,9 +21,16 @@ const MAX_SYNC_ATTEMPTS = 2
21
21
  const makeChatsSocket = (config) => {
22
22
  const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config
23
23
  const Neele = usync_1.makeUSyncSocket(config)
24
- const { ev, ws, authState, generateMessageTag, sendNode, query, onUnexpectedError, groupFetchAllParticipating } = Neele
24
+ const { ev, ws, authState, generateMessageTag, sendNode, query, onUnexpectedError, groupFetchAllParticipating, signalRepository } = Neele
25
+ const getLIDForPN = signalRepository?.lidMapping?.getLIDForPN?.bind(signalRepository.lidMapping)
26
+ const getPNForLID = signalRepository?.lidMapping?.getPNForLID?.bind(signalRepository.lidMapping)
25
27
 
26
28
  let privacySettings
29
+ const serverProps = {
30
+ privacyTokenOn1to1: true,
31
+ profilePicPrivacyToken: true,
32
+ lidTrustedTokenIssueToLid: false
33
+ }
27
34
  let syncState = Types_1.SyncState.Connecting
28
35
 
29
36
  /** this mutex ensures that the notifications (receipts, messages etc.) are processed in order */
@@ -31,6 +38,7 @@ const makeChatsSocket = (config) => {
31
38
 
32
39
  // Timeout for AwaitingInitialSync State
33
40
  let awaitingSyncTimeout
41
+ const blockedCollections = new Set()
34
42
 
35
43
  const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
36
44
  stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
@@ -337,6 +345,40 @@ const makeChatsSocket = (config) => {
337
345
  }
338
346
 
339
347
  const updateBlockStatus = async (jid, action) => {
348
+ const normalizedJid = WABinary_1.jidNormalizedUser(jid)
349
+ let lid
350
+ let pn_jid
351
+ if (WABinary_1.isLidUser(normalizedJid) || WABinary_1.isHostedLidUser(normalizedJid)) {
352
+ lid = normalizedJid
353
+ if (action === 'block') {
354
+ const pn = getPNForLID ? await getPNForLID(normalizedJid) : undefined
355
+ if (!pn) {
356
+ throw new boom_1.Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 })
357
+ }
358
+ pn_jid = WABinary_1.jidNormalizedUser(pn)
359
+ }
360
+ } else if (WABinary_1.isJidUser(normalizedJid) || WABinary_1.isHostedPnUser(normalizedJid)) {
361
+ const mapped = getLIDForPN ? await getLIDForPN(normalizedJid) : undefined
362
+ if (!mapped) {
363
+ throw new boom_1.Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 })
364
+ }
365
+ lid = WABinary_1.jidNormalizedUser(mapped)
366
+ if (action === 'block') {
367
+ pn_jid = normalizedJid
368
+ }
369
+ } else {
370
+ throw new boom_1.Boom(`Invalid jid: ${jid}`, { statusCode: 400 })
371
+ }
372
+ const itemAttrs = {
373
+ action,
374
+ jid: lid
375
+ }
376
+ if (action === 'block') {
377
+ if (!pn_jid) {
378
+ throw new boom_1.Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 })
379
+ }
380
+ itemAttrs.pn_jid = pn_jid
381
+ }
340
382
  await query({
341
383
  tag: 'iq',
342
384
  attrs: {
@@ -347,10 +389,7 @@ const makeChatsSocket = (config) => {
347
389
  content: [
348
390
  {
349
391
  tag: 'item',
350
- attrs: {
351
- action,
352
- jid
353
- }
392
+ attrs: itemAttrs
354
393
  }
355
394
  ]
356
395
  })
@@ -444,6 +483,7 @@ const makeChatsSocket = (config) => {
444
483
  const collectionsToHandle = new Set(collections)
445
484
  // in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
446
485
  const attemptsMap = {}
486
+ const forceSnapshotCollections = new Set()
447
487
  // keep executing till all collections are done
448
488
  // sometimes a single patch request will not return all the patches (God knows why)
449
489
  // so we fetch till they're all done (this is determined by the "has_more_patches" flag)
@@ -455,6 +495,7 @@ const makeChatsSocket = (config) => {
455
495
  const result = await authState.keys.get('app-state-sync-version', [name])
456
496
  let state = result[name]
457
497
  if (state) {
498
+ state = Utils_1.ensureLTHashStateVersion(state)
458
499
  if (typeof initialVersionMap[name] === 'undefined') {
459
500
  initialVersionMap[name] = state.version
460
501
  }
@@ -463,14 +504,18 @@ const makeChatsSocket = (config) => {
463
504
  state = Utils_1.newLTHashState()
464
505
  }
465
506
  states[name] = state
466
- logger.info(`resyncing ${name} from v${state.version}`)
507
+ const shouldForceSnapshot = forceSnapshotCollections.has(name)
508
+ if (shouldForceSnapshot) {
509
+ forceSnapshotCollections.delete(name)
510
+ }
511
+ logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`)
467
512
  nodes.push({
468
513
  tag: 'collection',
469
514
  attrs: {
470
515
  name,
471
516
  version: state.version.toString(),
472
- // return snapshot if being synced from scratch
473
- 'return_snapshot': (!state.version).toString()
517
+ // return snapshot if syncing from scratch or forcing after a failed attempt
518
+ 'return_snapshot': (shouldForceSnapshot || !state.version).toString()
474
519
  }
475
520
  })
476
521
  }
@@ -523,19 +568,28 @@ const makeChatsSocket = (config) => {
523
568
  }
524
569
  }
525
570
  catch (error) {
526
- // if retry attempts overshoot
527
- // or key not found
528
- const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS
529
- || error.output?.statusCode === 404
530
- || error.name === 'TypeError'
531
- logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`)
532
- await authState.keys.set({ 'app-state-sync-version': { [name]: null } })
533
- // increment number of retries
534
571
  attemptsMap[name] = (attemptsMap[name] || 0) + 1
535
-
536
- if (isIrrecoverableError) {
537
- // stop retrying
572
+ const logData = {
573
+ name,
574
+ attempt: attemptsMap[name],
575
+ version: states[name].version,
576
+ statusCode: error.output?.statusCode,
577
+ errorType: error.name,
578
+ error: error.stack
579
+ }
580
+ if (Utils_1.isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
581
+ logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`)
582
+ blockedCollections.add(name)
583
+ collectionsToHandle.delete(name)
584
+ } else if (Utils_1.isMissingKeyError(error)) {
585
+ logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`)
586
+ forceSnapshotCollections.add(name)
587
+ } else if (Utils_1.isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
588
+ logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`)
538
589
  collectionsToHandle.delete(name)
590
+ } else {
591
+ logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`)
592
+ forceSnapshotCollections.add(name)
539
593
  }
540
594
  }
541
595
  }
@@ -698,7 +752,7 @@ const makeChatsSocket = (config) => {
698
752
  logger.debug({ patch: patchCreate }, 'applying app patch')
699
753
  await resyncAppState([name], false)
700
754
  const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name])
701
- initial = currentSyncVersion || Utils_1.newLTHashState()
755
+ initial = currentSyncVersion ? Utils_1.ensureLTHashStateVersion(currentSyncVersion) : Utils_1.newLTHashState()
702
756
  encodeResult = await Utils_1.encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey)
703
757
  const { patch, state } = encodeResult
704
758
 
@@ -748,19 +802,19 @@ const makeChatsSocket = (config) => {
748
802
  }
749
803
  }
750
804
 
751
- /** sending non-abt props may fix QR scan fail if server expects */
805
+ /** fetch AB props */
752
806
  const fetchProps = async () => {
753
807
  const resultNode = await query({
754
808
  tag: 'iq',
755
809
  attrs: {
756
810
  to: WABinary_1.S_WHATSAPP_NET,
757
- xmlns: 'w',
811
+ xmlns: 'abt',
758
812
  type: 'get',
759
813
  },
760
814
  content: [
761
815
  { tag: 'props', attrs: {
762
- protocol: '2',
763
- hash: authState?.creds?.lastPropHash || ''
816
+ protocol: '1',
817
+ ...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
764
818
  } }
765
819
  ]
766
820
  })
@@ -776,7 +830,19 @@ const makeChatsSocket = (config) => {
776
830
 
777
831
  props = WABinary_1.reduceBinaryNodeToDictionary(propsNode, 'prop')
778
832
  }
779
- logger.debug('fetched props')
833
+ const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages']
834
+ if (privacyTokenProp !== undefined) {
835
+ serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1'
836
+ }
837
+ const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq']
838
+ if (profilePicProp !== undefined) {
839
+ serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1'
840
+ }
841
+ const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid']
842
+ if (lidIssueProp !== undefined) {
843
+ serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1'
844
+ }
845
+ logger.debug({ serverProps }, 'fetched props')
780
846
  return props
781
847
  }
782
848
 
@@ -1037,6 +1103,9 @@ const makeChatsSocket = (config) => {
1037
1103
  })
1038
1104
 
1039
1105
  ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
1106
+ if (connection === 'close') {
1107
+ blockedCollections.clear()
1108
+ }
1040
1109
  if (connection === 'open') {
1041
1110
  if (fireInitQueries) {
1042
1111
  executeInitQueries()
@@ -1050,7 +1119,7 @@ const makeChatsSocket = (config) => {
1050
1119
  return
1051
1120
  }
1052
1121
 
1053
- syncState = Types_1.AwaitingInitialSync
1122
+ syncState = Types_1.SyncState.AwaitingInitialSync
1054
1123
  logger.info('Connection is now AwaitingInitialSync, buffering events')
1055
1124
  ev.buffer()
1056
1125
 
@@ -1065,7 +1134,14 @@ const makeChatsSocket = (config) => {
1065
1134
  return
1066
1135
  }
1067
1136
 
1068
- logger.info('History sync is enabled, awaiting notification with a 20s timeout.')
1137
+ if (authState.creds.accountSyncCounter > 0) {
1138
+ logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.')
1139
+ syncState = Types_1.SyncState.Online
1140
+ setTimeout(() => ev.flush(), 0)
1141
+ return
1142
+ }
1143
+
1144
+ logger.info('First connection, awaiting history sync notification with a 20s timeout.')
1069
1145
 
1070
1146
  if (awaitingSyncTimeout) {
1071
1147
  clearTimeout(awaitingSyncTimeout)
@@ -1076,15 +1152,32 @@ const makeChatsSocket = (config) => {
1076
1152
  logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer')
1077
1153
  syncState = Types_1.SyncState.Online
1078
1154
  ev.flush()
1155
+ const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1
1156
+ ev.emit('creds.update', { accountSyncCounter })
1079
1157
  }
1080
1158
  }, 20_000)
1081
1159
  })
1082
-
1160
+
1161
+ ev.on('creds.update', ({ myAppStateKeyId }) => {
1162
+ if (!myAppStateKeyId || blockedCollections.size === 0) {
1163
+ return
1164
+ }
1165
+ if (syncState === Types_1.SyncState.Syncing) {
1166
+ blockedCollections.clear()
1167
+ return
1168
+ }
1169
+ const collections = [...blockedCollections]
1170
+ blockedCollections.clear()
1171
+ logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections')
1172
+ resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'))
1173
+ })
1174
+
1083
1175
  return {
1084
1176
  ...Neele,
1085
1177
  star,
1086
1178
  addOrEditContact,
1087
1179
  removeContact,
1180
+ serverProps,
1088
1181
  processingMutex,
1089
1182
  fetchPrivacySettings,
1090
1183
  upsertMessage,
@@ -178,6 +178,11 @@ export declare const makeCommunitiesSocket: (config: SocketConfig) => {
178
178
  resyncAppState: (collections: readonly ("critical_unblock_low" | "regular_high" | "regular_low" | "critical_block" | "regular")[], isInitialSync: boolean) => Promise<void>;
179
179
  chatModify: (mod: import("../index.js").ChatModification, jid: string) => Promise<void>;
180
180
  cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: number | string) => Promise<void>;
181
+ serverProps: {
182
+ privacyTokenOn1to1: boolean;
183
+ profilePicPrivacyToken: boolean;
184
+ lidTrustedTokenIssueToLid: boolean;
185
+ };
181
186
  addOrEditContact: (jid: string, contact: proto.SyncActionValue.IContactAction) => Promise<void>;
182
187
  removeContact: (jid: string) => Promise<void>;
183
188
  addLabel: (jid: string, labels: import("../Types/Label.js").LabelActionBody) => Promise<void>;
@@ -84,6 +84,11 @@ export declare const makeGroupsSocket: (config: SocketConfig) => {
84
84
  resyncAppState: (collections: readonly ("critical_block" | "critical_unblock_low" | "regular_high" | "regular_low" | "regular")[], isInitialSync: boolean) => Promise<void>
85
85
  chatModify: (mod: import("../Types").ChatModification, jid: string) => Promise<void>
86
86
  cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: string | number | undefined) => Promise<void>
87
+ serverProps: {
88
+ privacyTokenOn1to1: boolean
89
+ profilePicPrivacyToken: boolean
90
+ lidTrustedTokenIssueToLid: boolean
91
+ }
87
92
  addLabel: (jid: string, labels: import("../Types/Label").LabelActionBody) => Promise<void>
88
93
  addChatLabel: (jid: string, labelId: string) => Promise<void>
89
94
  removeChatLabel: (jid: string, labelId: string) => Promise<void>
@@ -128,4 +133,4 @@ export declare const makeGroupsSocket: (config: SocketConfig) => {
128
133
  sendWAMBuffer: (wamBuffer: Buffer) => Promise<BinaryNode>
129
134
  }
130
135
 
131
- export declare const extractGroupMetadata: (result: BinaryNode) => GroupMetadata
136
+ export declare const extractGroupMetadata: (result: BinaryNode) => GroupMetadata
@@ -302,9 +302,15 @@ const extractGroupMetadata = (result) => {
302
302
 
303
303
  let desc
304
304
  let descId
305
+ let descOwner
306
+ let descOwnerPn
307
+ let descOwnerUsername
305
308
 
306
309
  if (descChild) {
307
310
  desc = WABinary_1.getBinaryNodeChildString(descChild, 'body')
311
+ descOwner = descChild.attrs.participant ? WABinary_1.jidNormalizedUser(descChild.attrs.participant) : undefined
312
+ descOwnerPn = descChild.attrs.participant_pn ? WABinary_1.jidNormalizedUser(descChild.attrs.participant_pn) : undefined
313
+ descOwnerUsername = descChild.attrs.participant_username || undefined
308
314
  descId = descChild.attrs.id
309
315
  }
310
316
 
@@ -318,13 +324,20 @@ const extractGroupMetadata = (result) => {
318
324
  addressingMode: mode,
319
325
  subject: group.attrs.subject,
320
326
  subjectOwner: mode === 'lid' ? group.attrs.s_o_pn : group.attrs.s_o,
327
+ subjectOwnerPn: group.attrs.s_o_pn,
328
+ subjectOwnerUsername: group.attrs.s_o_username,
321
329
  subjectTime: +group.attrs.s_t,
322
330
  size: group.attrs?.size ? +group.attrs.size : WABinary_1.getBinaryNodeChildren(group, 'participant').length,
323
331
  creation: +group.attrs.creation,
324
332
  owner: group.attrs.creator ? WABinary_1.jidNormalizedUser(mode === 'lid' ? group.attrs.creator_pn : group.attrs.creator) : undefined,
333
+ ownerPn: group.attrs.creator_pn ? WABinary_1.jidNormalizedUser(group.attrs.creator_pn) : undefined,
334
+ ownerUsername: group.attrs.creator_username || undefined,
325
335
  ownerCountry: group.attrs.creator_country_code,
326
336
  desc,
327
337
  descId,
338
+ descOwner,
339
+ descOwnerPn,
340
+ descOwnerUsername,
328
341
  linkedParent: WABinary_1.getBinaryNodeChild(group, 'linked_parent')?.attrs.jid || undefined,
329
342
  restrict: !!WABinary_1.getBinaryNodeChild(group, 'locked'),
330
343
  announce: !!WABinary_1.getBinaryNodeChild(group, 'announcement'),
@@ -337,6 +350,7 @@ const extractGroupMetadata = (result) => {
337
350
  id: (0, WABinary_1.isJidUser)(attrs.jid) ? attrs.jid : (0, WABinary_1.jidNormalizedUser)(attrs.phone_number),
338
351
  jid: (0, WABinary_1.isJidUser)(attrs.jid) ? attrs.jid : (0, WABinary_1.jidNormalizedUser)(attrs.phone_number),
339
352
  lid: (0, WABinary_1.isLidUser)(attrs.jid) ? attrs.jid : attrs.lid,
353
+ username: attrs.participant_username || attrs.username || undefined,
340
354
  admin: (attrs.type || null)
341
355
  };
342
356
  }),
@@ -349,4 +363,4 @@ const extractGroupMetadata = (result) => {
349
363
  module.exports = {
350
364
  makeGroupsSocket,
351
365
  extractGroupMetadata
352
- }
366
+ }
@@ -144,6 +144,11 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
144
144
  resyncAppState: (collections: readonly ("critical_block" | "critical_unblock_low" | "regular_high" | "regular_low" | "regular")[], isInitialSync: boolean) => Promise<void>
145
145
  chatModify: (mod: import("../Types").ChatModification, jid: string) => Promise<void>
146
146
  cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: string | number | undefined) => Promise<void>
147
+ serverProps: {
148
+ privacyTokenOn1to1: boolean
149
+ profilePicPrivacyToken: boolean
150
+ lidTrustedTokenIssueToLid: boolean
151
+ }
147
152
  addLabel: (jid: string, labels: import("../Types/Label").LabelActionBody) => Promise<void>
148
153
  addChatLabel: (jid: string, labelId: string) => Promise<void>
149
154
  removeChatLabel: (jid: string, labelId: string) => Promise<void>
@@ -188,4 +193,4 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
188
193
  sendWAMBuffer: (wamBuffer: Buffer) => Promise<import("..").BinaryNode>
189
194
  }
190
195
 
191
- export default makeWASocket
196
+ export default makeWASocket
@@ -132,6 +132,11 @@ export declare const makeMessagesRecvSocket: (config: SocketConfig) => {
132
132
  resyncAppState: (collections: readonly ("critical_block" | "critical_unblock_low" | "regular_high" | "regular_low" | "regular")[], isInitialSync: boolean) => Promise<void>
133
133
  chatModify: (mod: import("../Types").ChatModification, jid: string) => Promise<void>
134
134
  cleanDirtyBits: (type: "account_sync" | "groups", fromTimestamp?: string | number | undefined) => Promise<void>
135
+ serverProps: {
136
+ privacyTokenOn1to1: boolean
137
+ profilePicPrivacyToken: boolean
138
+ lidTrustedTokenIssueToLid: boolean
139
+ }
135
140
  addLabel: (jid: string, labels: import("../Types/Label").LabelActionBody) => Promise<void>
136
141
  addChatLabel: (jid: string, labelId: string) => Promise<void>
137
142
  removeChatLabel: (jid: string, labelId: string) => Promise<void>