@neelegirl/baileys 1.5.7 → 1.5.9

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
@@ -5,7 +5,7 @@
5
5
 
6
6
  <h1 align="center">🌸 Willkommen bei <code>@neelegirl/baileys</code> 🌸</h1>
7
7
  <p align="center"><i>Eine charmante WhatsApp Web API für TypeScript/JavaScript – von einer Prinzessin für alle, die Magie lieben.</i></p>
8
- <p align="center"><strong>✨ Version 1.5.7 – Aktualisiert mit neuesten Features von @whiskeysockets/baileys ✨</strong></p>
8
+ <p align="center"><strong>✨ Version 1.5.9 – Aktualisiert mit neuesten Features von @whiskeysockets/baileys ✨</strong></p>
9
9
 
10
10
  <div align="center">
11
11
 
@@ -45,7 +45,10 @@ Die Entwickler:innen übernehmen keine Verantwortung für den Gebrauch. Lies die
45
45
  - 💖 Vollständige **Multi‑Device** Unterstützung
46
46
  - 🧩 **LID‑kompatibel** (Linked ID Erkennung & Nutzung)
47
47
  - 🧷 Saubere TypeScript‑Typen, DX zum Verlieben
48
- - 🆕 **Version 1.5.7** – Aktualisiert mit neuesten Features von @whiskeysockets/baileys
48
+ - 🆕 **Version 1.5.9** – Aktualisiert mit neuesten Features von @whiskeysockets/baileys
49
+ - ✨ **Neue Features**: `onWhatsApp()`, `executeUSyncQuery()`, `digestKeyBundle()`, `rotateSignedPreKey()`
50
+ - 🚀 **Verbesserte Pre-Key-Verwaltung** mit automatischer Validierung
51
+ - 💎 **WAM Buffer Support** für erweiterte Statistiken
49
52
 
50
53
  ---
51
54
 
@@ -11,6 +11,9 @@ const Defaults_1 = require("../Defaults")
11
11
  const Types_1 = require("../Types")
12
12
  const Utils_1 = require("../Utils")
13
13
  const WABinary_1 = require("../WABinary")
14
+ const BinaryInfo_1 = require("../WAM/BinaryInfo")
15
+ const USyncQuery_1 = require("../WAUSync/USyncQuery")
16
+ const USyncUser_1 = require("../WAUSync/USyncUser")
14
17
  const Client_1 = require("./Client")
15
18
  const package_json_1 = require("../../package.json")
16
19
  const CURRENT_VERSION = package_json_1.version
@@ -38,6 +41,7 @@ const makeSocket = (config) => {
38
41
 
39
42
  ws.connect()
40
43
  const ev = Utils_1.makeEventBuffer(logger)
44
+ const publicWAMBuffer = new BinaryInfo_1.BinaryInfo()
41
45
 
42
46
  /** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
43
47
  const ephemeralKeyPair = Utils_1.Curve.generateKeyPair()
@@ -54,7 +58,11 @@ const makeSocket = (config) => {
54
58
 
55
59
  // add transaction capability
56
60
  const keys = Utils_1.addTransactionCapability(authState.keys, logger, transactionOpts)
57
- const signalRepository = makeSignalRepository({ creds, keys })
61
+ // pnFromLIDUSync wird später definiert, daher temporär undefined
62
+ let signalRepository
63
+ const initSignalRepository = () => {
64
+ signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync)
65
+ }
58
66
 
59
67
  let lastDateRecv
60
68
  let epoch = 1
@@ -139,21 +147,38 @@ const makeSocket = (config) => {
139
147
  let onRecv
140
148
  let onErr
141
149
  try {
142
- return await Utils_1.promiseTimeout(timeoutMs, (resolve, reject) => {
143
- onRecv = resolve
150
+ const result = await Utils_1.promiseTimeout(timeoutMs, (resolve, reject) => {
151
+ onRecv = data => {
152
+ resolve(data)
153
+ }
144
154
  onErr = err => {
145
- reject(err || new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed }))
155
+ reject(err ||
156
+ new boom_1.Boom('Connection Closed', {
157
+ statusCode: Types_1.DisconnectReason.connectionClosed
158
+ }))
146
159
  }
147
160
  ws.on(`TAG:${msgId}`, onRecv)
148
- ws.on('close', onErr) // if the socket closes, you'll never receive the message
149
- ws.off('error', onErr)
161
+ ws.on('close', onErr)
162
+ ws.on('error', onErr)
163
+ return () => reject(new boom_1.Boom('Query Cancelled'))
150
164
  })
165
+ return result
166
+ }
167
+ catch (error) {
168
+ // Catch timeout and return undefined instead of throwing
169
+ if (error instanceof boom_1.Boom && error.output?.statusCode === Types_1.DisconnectReason.timedOut) {
170
+ logger?.warn?.({ msgId }, 'timed out waiting for message')
171
+ return undefined
172
+ }
173
+ throw error
151
174
  }
152
-
153
175
  finally {
154
- ws.off(`TAG:${msgId}`, onRecv)
155
- ws.off('close', onErr) // if the socket closes, you'll never receive the message
156
- ws.off('error', onErr)
176
+ if (onRecv)
177
+ ws.off(`TAG:${msgId}`, onRecv)
178
+ if (onErr) {
179
+ ws.off('close', onErr)
180
+ ws.off('error', onErr)
181
+ }
157
182
  }
158
183
  }
159
184
 
@@ -162,20 +187,146 @@ const makeSocket = (config) => {
162
187
  if (!node.attrs.id) {
163
188
  node.attrs.id = generateMessageTag()
164
189
  }
165
-
166
190
  const msgId = node.attrs.id
167
- const wait = waitForMessage(msgId, timeoutMs)
168
-
169
- await sendNode(node)
170
-
171
- const result = await wait
172
-
173
- if ('tag' in result) {
191
+ const result = await Utils_1.promiseTimeout(timeoutMs, async (resolve, reject) => {
192
+ const result = waitForMessage(msgId, timeoutMs).catch(reject)
193
+ sendNode(node)
194
+ .then(async () => resolve(await result))
195
+ .catch(reject)
196
+ })
197
+ if (result && 'tag' in result) {
174
198
  WABinary_1.assertNodeErrorFree(result)
175
199
  }
176
-
177
200
  return result
178
201
  }
202
+ // Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
203
+ const digestKeyBundle = async () => {
204
+ const res = await query({
205
+ tag: 'iq',
206
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'encrypt' },
207
+ content: [{ tag: 'digest', attrs: {} }]
208
+ })
209
+ const digestNode = WABinary_1.getBinaryNodeChild(res, 'digest')
210
+ if (!digestNode) {
211
+ await uploadPreKeys()
212
+ throw new Error('encrypt/get digest returned no digest node')
213
+ }
214
+ }
215
+ // Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
216
+ const rotateSignedPreKey = async () => {
217
+ const newId = (creds.signedPreKey.keyId || 0) + 1
218
+ const skey = await Utils_1.signedKeyPair(creds.signedIdentityKey, newId)
219
+ await query({
220
+ tag: 'iq',
221
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'encrypt' },
222
+ content: [
223
+ {
224
+ tag: 'rotate',
225
+ attrs: {},
226
+ content: [Utils_1.xmppSignedPreKey(skey)]
227
+ }
228
+ ]
229
+ })
230
+ // Persist new signed pre-key in creds
231
+ ev.emit('creds.update', { signedPreKey: skey })
232
+ }
233
+ const executeUSyncQuery = async (usyncQuery) => {
234
+ if (usyncQuery.protocols.length === 0) {
235
+ throw new boom_1.Boom('USyncQuery must have at least one protocol')
236
+ }
237
+ // todo: validate users, throw WARNING on no valid users
238
+ // variable below has only validated users
239
+ const validUsers = usyncQuery.users
240
+ const userNodes = validUsers.map(user => {
241
+ return {
242
+ tag: 'user',
243
+ attrs: {
244
+ jid: !user.phone ? user.id : undefined
245
+ },
246
+ content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
247
+ }
248
+ })
249
+ const listNode = {
250
+ tag: 'list',
251
+ attrs: {},
252
+ content: userNodes
253
+ }
254
+ const queryNode = {
255
+ tag: 'query',
256
+ attrs: {},
257
+ content: usyncQuery.protocols.map(a => a.getQueryElement())
258
+ }
259
+ const iq = {
260
+ tag: 'iq',
261
+ attrs: {
262
+ to: WABinary_1.S_WHATSAPP_NET,
263
+ type: 'get',
264
+ xmlns: 'usync'
265
+ },
266
+ content: [
267
+ {
268
+ tag: 'usync',
269
+ attrs: {
270
+ context: usyncQuery.context,
271
+ mode: usyncQuery.mode,
272
+ sid: generateMessageTag(),
273
+ last: 'true',
274
+ index: '0'
275
+ },
276
+ content: [queryNode, listNode]
277
+ }
278
+ ]
279
+ }
280
+ const result = await query(iq)
281
+ return usyncQuery.parseUSyncQueryResult(result)
282
+ }
283
+ const onWhatsApp = async (...phoneNumber) => {
284
+ let usyncQuery = new USyncQuery_1.USyncQuery()
285
+ let contactEnabled = false
286
+ for (const jid of phoneNumber) {
287
+ if (WABinary_1.isLidUser(jid)) {
288
+ logger?.warn('LIDs are not supported with onWhatsApp')
289
+ continue
290
+ }
291
+ else {
292
+ if (!contactEnabled) {
293
+ contactEnabled = true
294
+ usyncQuery = usyncQuery.withContactProtocol()
295
+ }
296
+ const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`
297
+ usyncQuery.withUser(new USyncUser_1.USyncUser().withPhone(phone))
298
+ }
299
+ }
300
+ if (usyncQuery.users.length === 0) {
301
+ return [] // return early without forcing an empty query
302
+ }
303
+ const results = await executeUSyncQuery(usyncQuery)
304
+ if (results) {
305
+ return results.list.filter(a => !!a.contact).map(({ contact, id }) => ({ jid: id, exists: contact }))
306
+ }
307
+ }
308
+ const pnFromLIDUSync = async (jids) => {
309
+ const usyncQuery = new USyncQuery_1.USyncQuery().withLIDProtocol().withContext('background')
310
+ for (const jid of jids) {
311
+ if (WABinary_1.isLidUser(jid)) {
312
+ logger?.warn('LID user found in LID fetch call')
313
+ continue
314
+ }
315
+ else {
316
+ usyncQuery.withUser(new USyncUser_1.USyncUser().withId(jid))
317
+ }
318
+ }
319
+ if (usyncQuery.users.length === 0) {
320
+ return [] // return early without forcing an empty query
321
+ }
322
+ const results = await executeUSyncQuery(usyncQuery)
323
+ if (results) {
324
+ return results.list.filter(a => !!a.lid).map(({ lid, id }) => ({ pn: id, lid: lid }))
325
+ }
326
+ return []
327
+ }
328
+ // Initialize signalRepository after pnFromLIDUSync is defined
329
+ initSignalRepository()
179
330
  /** connection handshake */
180
331
  const validateConnection = async () => {
181
332
  let helloMsg = {
@@ -254,8 +405,8 @@ const makeSocket = (config) => {
254
405
  }
255
406
  }
256
407
 
257
- const onMessageReceived = (data) => {
258
- noise.decodeFrame(data, frame => {
408
+ const onMessageReceived = async (data) => {
409
+ await noise.decodeFrame(data, frame => {
259
410
  // reset ping timeout
260
411
  lastDateRecv = new Date()
261
412
  let anyTriggered = false
@@ -509,7 +660,7 @@ const makeSocket = (config) => {
509
660
  content: [
510
661
  {
511
662
  tag: 'add',
512
- attrs: {},
663
+ attrs: { t: Math.round(Date.now() / 1000) + '' },
513
664
  content: wamBuffer
514
665
  }
515
666
  ]
@@ -619,27 +770,52 @@ const makeSocket = (config) => {
619
770
  ws.on('CB:success', async (node) => {
620
771
  try {
621
772
  await uploadPreKeysToServerIfRequired()
622
-
623
773
  await sendPassiveIq('active')
624
-
625
- logger.info('opened connection to WA')
626
-
627
- clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
628
- ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
629
- ev.emit('connection.update', { connection: 'open' })
774
+ // After successful login, validate our key-bundle against server
775
+ try {
776
+ await digestKeyBundle()
777
+ }
778
+ catch (e) {
779
+ logger.warn({ e }, 'failed to run digest after login')
780
+ }
630
781
  }
631
-
632
782
  catch (err) {
633
- logger.error({ err }, 'error opening connection')
634
- end(err)
783
+ logger.warn({ err }, 'failed to send initial passive iq')
784
+ }
785
+ logger.info('opened connection to WA')
786
+ clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
787
+ ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
788
+ ev.emit('connection.update', { connection: 'open' })
789
+ if (node.attrs.lid && authState.creds.me?.id) {
790
+ const myLID = node.attrs.lid
791
+ process.nextTick(async () => {
792
+ try {
793
+ const myPN = authState.creds.me.id
794
+ // Store our own LID-PN mapping
795
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }])
796
+ // Create device list for our own user (needed for bulk migration)
797
+ const { user, device } = WABinary_1.jidDecode(myPN)
798
+ await authState.keys.set({
799
+ 'device-list': {
800
+ [user]: [device?.toString() || '0']
801
+ }
802
+ })
803
+ // migrate our own session
804
+ await signalRepository.migrateSession(myPN, myLID)
805
+ logger.info({ myPN, myLID }, 'Own LID session created successfully')
806
+ }
807
+ catch (error) {
808
+ logger.error({ error, lid: myLID }, 'Failed to create own LID session')
809
+ }
810
+ })
635
811
  }
636
812
  })
637
813
 
638
814
  ws.on('CB:stream:error', (node) => {
639
- logger.error({ node }, 'stream errored out')
815
+ const [reasonNode] = WABinary_1.getAllBinaryNodeChildren(node)
816
+ logger.error({ reasonNode, fullErrorNode: node }, 'stream errored out')
640
817
  const { reason, statusCode } = Utils_1.getErrorCodeFromStreamError(node)
641
-
642
- end(new boom_1.Boom(`Stream Errored (${reason})`, { statusCode, data: node }))
818
+ end(new boom_1.Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }))
643
819
  })
644
820
 
645
821
  // stream fail, possible logout
@@ -653,10 +829,9 @@ const makeSocket = (config) => {
653
829
  end(new boom_1.Boom('Multi-device beta not joined', { statusCode: Types_1.DisconnectReason.multideviceMismatch }))
654
830
  })
655
831
 
656
- ws.on('CB:ib,,offline_preview', (node) => {
832
+ ws.on('CB:ib,,offline_preview', async (node) => {
657
833
  logger.info('offline preview received', JSON.stringify(node))
658
-
659
- sendNode({
834
+ await sendNode({
660
835
  tag: 'ib',
661
836
  attrs: {},
662
837
  content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
@@ -744,10 +919,15 @@ const makeSocket = (config) => {
744
919
  onUnexpectedError,
745
920
  uploadPreKeys,
746
921
  uploadPreKeysToServerIfRequired,
922
+ digestKeyBundle,
923
+ rotateSignedPreKey,
747
924
  requestPairingCode,
925
+ wamBuffer: publicWAMBuffer,
748
926
  /** Waits for the connection to WA to reach a state */
749
927
  waitForConnectionUpdate: Utils_1.bindWaitForConnectionUpdate(ev),
750
928
  sendWAMBuffer,
929
+ executeUSyncQuery,
930
+ onWhatsApp
751
931
  }
752
932
  }
753
933
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neelegirl/baileys",
3
- "version": "1.5.7",
3
+ "version": "1.5.9",
4
4
  "description": "WhatsApp API for wa-api, a multi-device WhatsApp Web API client",
5
5
  "keywords": [
6
6
  "whatsapp",