@badzz88/baileys 8.5.4 → 8.5.5

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 (107) hide show
  1. package/README.md +1 -1
  2. package/WAProto/WAProto.proto +75 -1256
  3. package/WAProto/index.d.ts +19729 -36122
  4. package/WAProto/index.js +132193 -178538
  5. package/engine-requirements.js +15 -7
  6. package/lib/Defaults/index.js +14 -22
  7. package/lib/Signal/Group/group_cipher.js +5 -5
  8. package/lib/Signal/Group/keyhelper.js +4 -4
  9. package/lib/Signal/Group/sender-chain-key.js +2 -2
  10. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  11. package/lib/Signal/Group/sender-key-message.js +3 -3
  12. package/lib/Signal/Group/sender-key-record.js +1 -1
  13. package/lib/Signal/Group/sender-message-key.js +5 -5
  14. package/lib/Signal/libsignal.js +28 -39
  15. package/lib/Signal/lid-mapping.js +8 -28
  16. package/lib/Socket/aigroups.js +5 -5
  17. package/lib/Socket/business.js +11 -3
  18. package/lib/Socket/chats.js +319 -668
  19. package/lib/Socket/communities.js +89 -85
  20. package/lib/Socket/graphql.js +228 -435
  21. package/lib/Socket/groups.js +211 -132
  22. package/lib/Socket/index.js +7 -9
  23. package/lib/Socket/interactive-handler.js +60 -117
  24. package/lib/Socket/interop.js +175 -56
  25. package/lib/Socket/luxu.js +387 -0
  26. package/lib/Socket/managed-account.js +48 -79
  27. package/lib/Socket/messages-recv.js +340 -898
  28. package/lib/Socket/messages-send.js +160 -462
  29. package/lib/Socket/mex.js +53 -40
  30. package/lib/Socket/newsletter.js +295 -291
  31. package/lib/Socket/privacy.js +265 -77
  32. package/lib/Socket/registration.js +157 -164
  33. package/lib/Socket/socket.js +199 -181
  34. package/lib/Socket/text-router.js +83 -0
  35. package/lib/Socket/username.js +227 -144
  36. package/lib/Store/keyed-db.js +108 -0
  37. package/lib/Store/make-cache-manager-store.js +2 -2
  38. package/lib/Store/make-in-memory-store.js +141 -25
  39. package/lib/Types/Auth.js +3 -3
  40. package/lib/Types/Label.js +1 -1
  41. package/lib/Types/LabelAssociation.js +1 -1
  42. package/lib/Types/Mex.js +112 -0
  43. package/lib/Types/Newsletter.js +65 -96
  44. package/lib/Types/State.js +4 -4
  45. package/lib/Types/index.js +1 -1
  46. package/lib/Utils/auth-utils.js +57 -52
  47. package/lib/Utils/browser-utils.js +1 -1
  48. package/lib/Utils/business.js +6 -2
  49. package/lib/Utils/chat-utils.js +38 -102
  50. package/lib/Utils/command-loader.d.ts +31 -0
  51. package/lib/Utils/command-loader.js +100 -0
  52. package/lib/Utils/companion-reg-client-utils.js +42 -0
  53. package/lib/Utils/crypto.js +84 -54
  54. package/lib/Utils/curve25519-js.js +275 -0
  55. package/lib/Utils/decode-wa-message.js +83 -316
  56. package/lib/Utils/event-buffer.js +61 -37
  57. package/lib/Utils/generics.js +103 -157
  58. package/lib/Utils/group-history.js +12 -3
  59. package/lib/Utils/history.js +15 -15
  60. package/lib/Utils/index.js +5 -1
  61. package/lib/Utils/link-preview.js +8 -3
  62. package/lib/Utils/lt-hash.js +35 -2
  63. package/lib/Utils/make-mutex.js +1 -1
  64. package/lib/Utils/message-retry-manager.js +92 -27
  65. package/lib/Utils/messages-media.js +130 -159
  66. package/lib/Utils/messages.js +566 -144
  67. package/lib/Utils/meta-ai-msmsg.js +202 -62
  68. package/lib/Utils/native-bridge.js +82 -0
  69. package/lib/Utils/noise-handler.js +21 -19
  70. package/lib/Utils/offline-node-processor.js +8 -3
  71. package/lib/Utils/pre-key-manager.js +23 -13
  72. package/lib/Utils/process-message.js +106 -197
  73. package/lib/Utils/reporting-utils.js +2 -2
  74. package/lib/Utils/session-pool.d.ts +16 -0
  75. package/lib/Utils/session-pool.js +94 -0
  76. package/lib/Utils/signal.js +63 -28
  77. package/lib/Utils/stanza-ack.js +12 -39
  78. package/lib/Utils/sticker.d.ts +17 -0
  79. package/lib/Utils/sticker.js +145 -0
  80. package/lib/Utils/sync-action-utils.js +7 -4
  81. package/lib/Utils/tc-token-utils.js +25 -20
  82. package/lib/Utils/use-multi-file-auth-state.js +14 -30
  83. package/lib/Utils/validate-connection.js +19 -59
  84. package/lib/Utils/view-once-cache.d.ts +9 -0
  85. package/lib/Utils/view-once-cache.js +79 -0
  86. package/lib/WABinary/decode.js +16 -50
  87. package/lib/WABinary/encode.js +224 -54
  88. package/lib/WABinary/generic-utils.js +97 -7
  89. package/lib/WABinary/jid-utils.js +14 -27
  90. package/lib/WAM/encode.js +7 -7
  91. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
  92. package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
  93. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
  94. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
  95. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
  96. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
  97. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
  98. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
  99. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
  100. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
  101. package/lib/WAUSync/Protocols/index.js +0 -1
  102. package/lib/WAUSync/USyncQuery.js +6 -77
  103. package/lib/WAUSync/USyncUser.js +8 -16
  104. package/lib/WAUSync/index.js +0 -1
  105. package/lib/antiban.js +854 -608
  106. package/lib/index.js +1 -1
  107. package/package.json +20 -16
@@ -59,9 +59,9 @@ const REAL_MSG_STUB_TYPES = new Set([
59
59
  Types_1.WAMessageStubType.CALL_MISSED_VOICE
60
60
  ])
61
61
  const REAL_MSG_REQ_ME_STUB_TYPES = new Set([Types_1.WAMessageStubType.GROUP_PARTICIPANT_ADD])
62
-
62
+ /** Cleans a received message to further processing */
63
63
  const cleanMessage = (message, meId, meLid) => {
64
-
64
+ // ensure remoteJid and participant doesn't have device or agent in it
65
65
  if ((0, WABinary_1.isHostedPnUser)(message.key.remoteJid) || (0, WABinary_1.isHostedLidUser)(message.key.remoteJid)) {
66
66
  message.key.remoteJid = (0, WABinary_1.jidEncode)(
67
67
  (0, WABinary_1.jidDecode)(message.key?.remoteJid)?.user,
@@ -82,7 +82,7 @@ const cleanMessage = (message, meId, meLid) => {
82
82
  message.key.participant = (0, WABinary_1.jidNormalizedUser)(message.key.participant)
83
83
  }
84
84
  const content = (0, messages_1.normalizeMessageContent)(message.message)
85
-
85
+ // if the message has a reaction, ensure fromMe & remoteJid are from our perspective
86
86
  if (content?.reactionMessage) {
87
87
  normaliseKey(content.reactionMessage.key)
88
88
  }
@@ -90,48 +90,47 @@ const cleanMessage = (message, meId, meLid) => {
90
90
  normaliseKey(content.pollUpdateMessage.pollCreationMessageKey)
91
91
  }
92
92
  function normaliseKey(msgKey) {
93
-
94
-
93
+ // if the reaction is from another user
94
+ // we've to correctly map the key to this user's perspective
95
95
  if (!message.key.fromMe) {
96
-
97
-
96
+ // if the sender believed the message being reacted to is not from them
97
+ // we've to correct the key to be from them, or some other participant
98
98
  msgKey.fromMe = !msgKey.fromMe
99
99
  ? (0, WABinary_1.areJidsSameUser)(msgKey.participant || msgKey.remoteJid, meId) ||
100
100
  (0, WABinary_1.areJidsSameUser)(msgKey.participant || msgKey.remoteJid, meLid)
101
- :
102
-
101
+ : // if the message being reacted to, was from them
102
+ // fromMe automatically becomes false
103
103
  false
104
-
105
-
104
+ // set the remoteJid to being the same as the chat the message came from
105
+ // TODO: investigate inconsistencies
106
106
  msgKey.remoteJid = message.key.remoteJid
107
-
107
+ // set participant of the message
108
108
  msgKey.participant = msgKey.participant || message.key.participant
109
109
  }
110
110
  }
111
111
  }
112
112
  exports.cleanMessage = cleanMessage
113
+ // TODO: target:audit AUDIT THIS FUNCTION AGAIN
113
114
  const isRealMessage = message => {
114
-
115
-
116
-
117
- if (REAL_MSG_STUB_TYPES.has(message.messageStubType) || REAL_MSG_REQ_ME_STUB_TYPES.has(message.messageStubType)) {
118
- return true
119
- }
120
115
  const normalizedContent = (0, messages_1.normalizeMessageContent)(message.message)
121
116
  const hasSomeContent = !!(0, messages_1.getContentType)(normalizedContent)
122
117
  return (
123
- !!normalizedContent &&
118
+ (!!normalizedContent ||
119
+ REAL_MSG_STUB_TYPES.has(message.messageStubType) ||
120
+ REAL_MSG_REQ_ME_STUB_TYPES.has(message.messageStubType)) &&
124
121
  hasSomeContent &&
125
122
  !normalizedContent?.protocolMessage &&
126
123
  !normalizedContent?.reactionMessage &&
127
- !normalizedContent?.pollUpdateMessage &&
128
- !normalizedContent?.secretEncryptedMessage
124
+ !normalizedContent?.pollUpdateMessage
129
125
  )
130
126
  }
131
127
  exports.isRealMessage = isRealMessage
132
128
  const shouldIncrementChatUnread = message => !message.key.fromMe && !message.messageStubType
133
129
  exports.shouldIncrementChatUnread = shouldIncrementChatUnread
134
-
130
+ /**
131
+ * Get the ID of the chat from the given key.
132
+ * Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
133
+ */
135
134
  const getChatId = ({ remoteJid, participant, fromMe }) => {
136
135
  if ((0, WABinary_1.isJidBroadcast)(remoteJid) && !(0, WABinary_1.isJidStatusBroadcast)(remoteJid) && !fromMe) {
137
136
  return participant
@@ -139,7 +138,12 @@ const getChatId = ({ remoteJid, participant, fromMe }) => {
139
138
  return remoteJid
140
139
  }
141
140
  exports.getChatId = getChatId
142
-
141
+ /**
142
+ * Decrypt a poll vote
143
+ * @param vote encrypted vote
144
+ * @param ctx additional info about the poll required for decryption
145
+ * @returns list of SHA256 options
146
+ */
143
147
  function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pollEncKey, voterJid }) {
144
148
  const sign = Buffer.concat([
145
149
  toBinary(pollMsgId),
@@ -157,7 +161,12 @@ function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pol
157
161
  return Buffer.from(txt)
158
162
  }
159
163
  }
160
-
164
+ /**
165
+ * Decrypt an event response
166
+ * @param response encrypted event response
167
+ * @param ctx additional info about the event required for decryption
168
+ * @returns event response message
169
+ */
161
170
  function decryptEventResponse({ encPayload, encIv }, { eventCreatorJid, eventMsgId, eventEncKey, responderJid }) {
162
171
  const sign = Buffer.concat([
163
172
  toBinary(eventMsgId),
@@ -175,108 +184,6 @@ function decryptEventResponse({ encPayload, encIv }, { eventCreatorJid, eventMsg
175
184
  return Buffer.from(txt)
176
185
  }
177
186
  }
178
-
179
- function decryptMessageEdit({ encPayload, encIv }, { origMsgId, origMsgSenderJid, editorJid, msgEncKey }) {
180
- const sign = Buffer.concat([
181
- toBinary(origMsgId),
182
- toBinary(origMsgSenderJid),
183
- toBinary(editorJid),
184
- toBinary('Message Edit'),
185
- new Uint8Array([1])
186
- ])
187
- const key0 = (0, crypto_1.hmacSign)(msgEncKey, new Uint8Array(32), 'sha256')
188
- const decKey = (0, crypto_1.hmacSign)(sign, key0, 'sha256')
189
-
190
- const decrypted = (0, crypto_1.aesDecryptGCM)(encPayload, decKey, encIv, new Uint8Array(0))
191
- return index_js_1.proto.Message.decode(decrypted)
192
- function toBinary(txt) {
193
- return Buffer.from(txt)
194
- }
195
- }
196
- exports.decryptMessageEdit = decryptMessageEdit
197
-
198
- const unwrapSecretEncryptedMessage = async (message, { creds, getMessage, logger }) => {
199
- const content = (0, messages_1.normalizeMessageContent)(message.message)
200
- const secretEnc = content?.secretEncryptedMessage
201
- if (
202
- !secretEnc?.encPayload ||
203
- !secretEnc.encIv ||
204
- secretEnc.secretEncType !== index_js_1.proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT
205
- ) {
206
- return
207
- }
208
- const targetKey = secretEnc.targetMessageKey
209
- if (!targetKey?.id) {
210
- return
211
- }
212
- try {
213
-
214
-
215
- const origMsg = await getMessage({
216
- ...targetKey,
217
- remoteJid: message.key.remoteJid,
218
- fromMe: message.key.fromMe,
219
- participant: message.key.participant
220
- })
221
- let msgEncKey = origMsg?.messageContextInfo?.messageSecret
222
- if (typeof msgEncKey === 'string') {
223
-
224
- msgEncKey = Buffer.from(msgEncKey, 'base64')
225
- }
226
- if (!msgEncKey?.length) {
227
- logger?.warn({ targetKey }, 'message edit: missing messageSecret for decryption')
228
- return
229
- }
230
-
231
-
232
- const meIdNormalised = (0, WABinary_1.jidNormalizedUser)(creds.me.id)
233
- const candidates = [
234
- ...(message.key.fromMe
235
- ? [meIdNormalised, creds.me?.lid]
236
- : [message.key.participant || message.key.remoteJid, message.key.participantAlt || message.key.remoteJidAlt]),
237
- targetKey.remoteJid
238
- ]
239
- .filter(jid => !!jid)
240
- .map(jid => (0, WABinary_1.jidNormalizedUser)(jid))
241
- .filter((jid, i, arr) => arr.indexOf(jid) === i)
242
- let decoded
243
- let lastError
244
- for (const authorJid of candidates) {
245
- try {
246
- decoded = decryptMessageEdit(secretEnc, {
247
- origMsgId: targetKey.id,
248
- origMsgSenderJid: authorJid,
249
- editorJid: authorJid,
250
- msgEncKey
251
- })
252
- break
253
- } catch (err) {
254
- lastError = err
255
- }
256
- }
257
- if (!decoded) {
258
- throw lastError
259
- }
260
- if (!decoded.protocolMessage) {
261
-
262
- decoded = index_js_1.proto.Message.fromObject({
263
- protocolMessage: {
264
- key: targetKey,
265
- type: index_js_1.proto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
266
- editedMessage: decoded
267
- }
268
- })
269
- }
270
- if (!decoded.messageContextInfo && content?.messageContextInfo) {
271
- decoded.messageContextInfo = content.messageContextInfo
272
- }
273
- message.message = decoded
274
- logger?.debug({ targetKey }, 'decrypted secretEncryptedMessage edit')
275
- } catch (err) {
276
- logger?.warn({ err, targetKey }, 'failed to decrypt secretEncryptedMessage edit')
277
- }
278
- }
279
- exports.unwrapSecretEncryptedMessage = unwrapSecretEncryptedMessage
280
187
  const processMessage = async (
281
188
  message,
282
189
  {
@@ -298,7 +205,7 @@ const processMessage = async (
298
205
  if (isRealMsg) {
299
206
  chat.messages = [{ message }]
300
207
  chat.conversationTimestamp = (0, generics_1.toNumber)(message.messageTimestamp)
301
-
208
+ // only increment unread count if not CIPHERTEXT and from another person
302
209
  if ((0, exports.shouldIncrementChatUnread)(message)) {
303
210
  chat.unreadCount = (chat.unreadCount || 0) + 1
304
211
  }
@@ -329,8 +236,8 @@ const processMessage = async (
329
236
  })
330
237
  }
331
238
  const content = (0, messages_1.normalizeMessageContent)(message.message)
332
-
333
-
239
+ // unarchive chat if it's a real message, or someone reacted to our message
240
+ // and we've the unarchive chats setting on
334
241
  if ((isRealMsg || content?.reactionMessage?.key?.fromMe) && accountSettings?.unarchiveChats) {
335
242
  chat.archived = false
336
243
  chat.readOnly = false
@@ -352,7 +259,7 @@ const processMessage = async (
352
259
  'got history notification'
353
260
  )
354
261
  if (process) {
355
-
262
+ // TODO: investigate
356
263
  if (histNotification.syncType !== index_js_1.proto.HistorySync.HistorySyncType.ON_DEMAND) {
357
264
  ev.emit('creds.update', {
358
265
  processedHistoryMessages: [
@@ -418,31 +325,31 @@ const processMessage = async (
418
325
  case index_js_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE:
419
326
  const response = protocolMsg.peerDataOperationRequestResponseMessage
420
327
  if (response) {
421
-
328
+ // TODO: IMPLEMENT HISTORY SYNC ETC (sticker uploads etc.).
422
329
  const peerDataOperationResult = response.peerDataOperationResult || []
423
330
  for (const result of peerDataOperationResult) {
424
331
  const retryResponse = result?.placeholderMessageResendResponse
425
-
332
+ //eslint-disable-next-line max-depth
426
333
  if (!retryResponse?.webMessageInfoBytes) {
427
334
  continue
428
335
  }
429
-
336
+ //eslint-disable-next-line max-depth
430
337
  try {
431
338
  const webMessageInfo = index_js_1.proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes)
432
339
  const msgId = webMessageInfo.key?.id
433
-
434
-
340
+ // Retrieve cached original message data (preserves LID details,
341
+ // timestamps, etc. that the phone may omit in its PDO response)
435
342
  const cachedData = msgId ? await placeholderResendCache?.get(msgId) : undefined
436
-
343
+ //eslint-disable-next-line max-depth
437
344
  if (msgId) {
438
345
  await placeholderResendCache?.del(msgId)
439
346
  }
440
347
  let finalMsg
441
-
348
+ //eslint-disable-next-line max-depth
442
349
  if (cachedData && typeof cachedData === 'object') {
443
-
350
+ // Apply decoded message content onto cached metadata (preserves LID etc.)
444
351
  cachedData.message = webMessageInfo.message
445
-
352
+ //eslint-disable-next-line max-depth
446
353
  if (webMessageInfo.messageTimestamp) {
447
354
  cachedData.messageTimestamp = webMessageInfo.messageTimestamp
448
355
  }
@@ -465,7 +372,7 @@ const processMessage = async (
465
372
  case index_js_1.proto.Message.ProtocolMessage.Type.MESSAGE_EDIT:
466
373
  ev.emit('messages.update', [
467
374
  {
468
-
375
+ // flip the sender / fromMe properties because they're in the perspective of the sender
469
376
  key: { ...message.key, id: protocolMsg.key?.id },
470
377
  update: {
471
378
  message: {
@@ -482,12 +389,10 @@ const processMessage = async (
482
389
  break
483
390
  case index_js_1.proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE:
484
391
  const labelAssociationMsg = protocolMsg.memberLabel
485
- if (labelAssociationMsg) {
486
-
487
-
392
+ if (labelAssociationMsg?.label) {
488
393
  ev.emit('group.member-tag.update', {
489
394
  groupId: chat.id,
490
- label: labelAssociationMsg.label || '',
395
+ label: labelAssociationMsg.label,
491
396
  participant: message.key.participant,
492
397
  participantAlt: message.key.participantAlt,
493
398
  messageTimestamp: Number(message.messageTimestamp)
@@ -711,12 +616,12 @@ const processMessage = async (
711
616
  } else if (content?.encEventResponseMessage) {
712
617
  const encEventResponse = content.encEventResponseMessage
713
618
  const creationMsgKey = encEventResponse.eventCreationMessageKey
714
-
619
+ // we need to fetch the event creation message to get the event enc key
715
620
  const eventMsg = await getMessage(creationMsgKey)
716
621
  if (eventMsg) {
717
622
  try {
718
623
  const meIdNormalised = (0, WABinary_1.jidNormalizedUser)(meId)
719
-
624
+ // all jids need to be PN
720
625
  const eventCreatorKey = creationMsgKey.participant || creationMsgKey.remoteJid
721
626
  const eventCreatorPn = (0, WABinary_1.isLidUser)(eventCreatorKey)
722
627
  ? await signalRepository.lidMapping.getPNForLID(eventCreatorKey)
@@ -758,7 +663,7 @@ const processMessage = async (
758
663
  }
759
664
  } else if (message.messageStubType) {
760
665
  const jid = message.key?.remoteJid
761
-
666
+ //let actor = whatsappID (message.participant)
762
667
  let participants
763
668
  const emitParticipantsUpdate = action =>
764
669
  ev.emit('group-participants.update', {
@@ -774,7 +679,7 @@ const processMessage = async (
774
679
  {
775
680
  id: jid,
776
681
  ...update,
777
- author: message.key.participant,
682
+ author: message.key.participant ?? undefined,
778
683
  authorPn: message.key.participantAlt,
779
684
  authorUsername: message.key.participantUsername
780
685
  }
@@ -793,7 +698,7 @@ const processMessage = async (
793
698
  })
794
699
  }
795
700
  const participantsIncludesMe = () =>
796
- participants.find(jid => (0, WABinary_1.areJidsSameUser)(meId, jid.phoneNumber))
701
+ participants.find(jid => (0, WABinary_1.areJidsSameUser)(meId, jid.phoneNumber)) // ADD SUPPORT FOR LID
797
702
  switch (message.messageStubType) {
798
703
  case Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER:
799
704
  participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
@@ -803,7 +708,7 @@ const processMessage = async (
803
708
  case Types_1.WAMessageStubType.GROUP_PARTICIPANT_REMOVE:
804
709
  participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
805
710
  emitParticipantsUpdate('remove')
806
-
711
+ // mark the chat read only if you left the group
807
712
  if (participantsIncludesMe()) {
808
713
  chat.readOnly = true
809
714
  }
@@ -855,7 +760,7 @@ const processMessage = async (
855
760
  const approvalMode = message.messageStubParameters?.[0]
856
761
  emitGroupUpdate({ joinApprovalMode: approvalMode === 'on' })
857
762
  break
858
- case Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD:
763
+ case Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD: // TODO: Add other events
859
764
  const participant = JSON.parse(message.messageStubParameters?.[0])
860
765
  const action = message.messageStubParameters?.[1]
861
766
  const method = message.messageStubParameters?.[2]
@@ -945,7 +850,7 @@ const processMessage = async (
945
850
  ev.emit('chats.update', [{ id: jid, psa: { urlParams: message.messageStubParameters } }])
946
851
  break
947
852
  case Types_1.WAMessageStubType.CHAT_POLL_CREATION_MESSAGE:
948
-
853
+ // poll was created, no special action needed beyond message processing
949
854
  break
950
855
  case Types_1.WAMessageStubType.BIZ_CHAT_ASSIGNMENT:
951
856
  ev.emit('chats.update', [
@@ -1087,51 +992,55 @@ const processMessage = async (
1087
992
  emitGroupUpdate({ hidden: true })
1088
993
  break
1089
994
  }
1090
- } else if (content?.pollUpdateMessage) {
1091
- const creationMsgKey = content.pollUpdateMessage.pollCreationMessageKey
1092
- const pollMsg = await getMessage(creationMsgKey)
1093
- if (pollMsg) {
1094
-
1095
- const inner = pollMsg.viewOnceMessage?.message || pollMsg.botInvokeMessage?.message || pollMsg
1096
- const pollEncKey = inner.messageContextInfo?.messageSecret || pollMsg.messageContextInfo?.messageSecret || null
1097
- if (!pollEncKey) {
1098
- logger?.warn({ creationMsgKey }, 'poll: messageSecret missing, cannot decrypt vote')
1099
- } else {
1100
-
1101
- const rawLid = creds.me?.lid
1102
- const meLidNorm = rawLid ? `${rawLid.split(':')[0]}@lid` : ''
1103
- const pollCreatorJid = meLidNorm || (0, WABinary_1.jidNormalizedUser)(meId)
1104
-
1105
- const voterJid = message.key.fromMe ? pollCreatorJid : message.key.participant || message.key.remoteJid
1106
- try {
1107
- const voteMsg = decryptPollVote(content.pollUpdateMessage.vote, {
1108
- pollEncKey,
1109
- pollCreatorJid,
1110
- pollMsgId: creationMsgKey.id,
1111
- voterJid
1112
- })
1113
- ev.emit('messages.update', [
1114
- {
1115
- key: creationMsgKey,
1116
- update: {
1117
- pollUpdates: [
1118
- {
1119
- pollUpdateMessageKey: message.key,
1120
- vote: voteMsg,
1121
- senderTimestampMs: (0, generics_1.toNumber)(content.pollUpdateMessage.senderTimestampMs)
1122
- }
1123
- ]
1124
- }
1125
- }
1126
- ])
1127
- } catch (err) {
1128
- logger?.warn({ err, creationMsgKey, pollCreatorJid, voterJid }, 'failed to decrypt poll vote')
1129
- }
1130
- }
1131
- } else {
1132
- logger?.warn({ creationMsgKey }, 'poll: creation message not found, cannot decrypt vote')
1133
- }
1134
- }
995
+ } /* else if(content?.pollUpdateMessage) {
996
+ const creationMsgKey = content.pollUpdateMessage.pollCreationMessageKey!
997
+ // we need to fetch the poll creation message to get the poll enc key
998
+ // TODO: make standalone, remove getMessage reference
999
+ // TODO: Remove entirely
1000
+ const pollMsg = await getMessage(creationMsgKey)
1001
+ if(pollMsg) {
1002
+ const meIdNormalised = jidNormalizedUser(meId)
1003
+ const pollCreatorJid = getKeyAuthor(creationMsgKey, meIdNormalised)
1004
+ const voterJid = getKeyAuthor(message.key, meIdNormalised)
1005
+ const pollEncKey = pollMsg.messageContextInfo?.messageSecret!
1006
+
1007
+ try {
1008
+ const voteMsg = decryptPollVote(
1009
+ content.pollUpdateMessage.vote!,
1010
+ {
1011
+ pollEncKey,
1012
+ pollCreatorJid,
1013
+ pollMsgId: creationMsgKey.id!,
1014
+ voterJid,
1015
+ }
1016
+ )
1017
+ ev.emit('messages.update', [
1018
+ {
1019
+ key: creationMsgKey,
1020
+ update: {
1021
+ pollUpdates: [
1022
+ {
1023
+ pollUpdateMessageKey: message.key,
1024
+ vote: voteMsg,
1025
+ senderTimestampMs: (content.pollUpdateMessage.senderTimestampMs! as Long).toNumber(),
1026
+ }
1027
+ ]
1028
+ }
1029
+ }
1030
+ ])
1031
+ } catch(err) {
1032
+ logger?.warn(
1033
+ { err, creationMsgKey },
1034
+ 'failed to decrypt poll vote'
1035
+ )
1036
+ }
1037
+ } else {
1038
+ logger?.warn(
1039
+ { creationMsgKey },
1040
+ 'poll creation message not found, cannot decrypt update'
1041
+ )
1042
+ }
1043
+ } */
1135
1044
  if (Object.keys(chat).length > 1) {
1136
1045
  ev.emit('chats.update', [chat])
1137
1046
  }
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
  Object.defineProperty(exports, '__esModule', { value: true })
3
3
  exports.getMessageReportingToken = exports.shouldIncludeReportingToken = void 0
4
- const rb = require('whatsapp-rust-bridge')
4
+ const Crypto_1 = require('./crypto')
5
5
  const crypto_2 = require('./crypto')
6
6
  const reportingFields = [
7
7
  { f: 1 },
@@ -246,7 +246,7 @@ const getMessageReportingToken = async (msgProtobuf, message, key) => {
246
246
  if (!content || content.length === 0) {
247
247
  return null
248
248
  }
249
- const reportingToken = Buffer.from(rb.hmacSign(content, reportingSecret)).subarray(0, 16)
249
+ const reportingToken = Buffer.from(Crypto_1.hmacSign(content, reportingSecret)).subarray(0, 16)
250
250
  return {
251
251
  tag: 'reporting',
252
252
  attrs: {},
@@ -0,0 +1,16 @@
1
+ export interface SessionPoolOptions {
2
+ makeSocket: (sessionId: string) => Promise<any> | any
3
+ logger?: any
4
+ maxBackoffMs?: number
5
+ onSessionUpdate?: (sessionId: string, sock: any) => void
6
+ onLoggedOut?: (sessionId: string) => void
7
+ }
8
+
9
+ export interface SessionPool {
10
+ add: (sessionId: string) => Promise<void>
11
+ remove: (sessionId: string) => void
12
+ get: (sessionId: string) => any
13
+ list: () => string[]
14
+ }
15
+
16
+ export function createSessionPool(options: SessionPoolOptions): SessionPool
@@ -0,0 +1,94 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.createSessionPool = void 0
4
+
5
+ const DISCONNECT_REASON_LOGGED_OUT = 401
6
+
7
+ /**
8
+ * Manages multiple Baileys socket instances (one per account/session), automatically
9
+ * restarting any session that disconnects -- except when it was logged out -- using
10
+ * exponential backoff with jitter, instead of every project re-implementing its own
11
+ * reconnect loop.
12
+ *
13
+ * @param {{
14
+ * makeSocket: (sessionId: string) => Promise<any> | any,
15
+ * logger?: any,
16
+ * maxBackoffMs?: number,
17
+ * onSessionUpdate?: (sessionId: string, sock: any) => void,
18
+ * onLoggedOut?: (sessionId: string) => void
19
+ * }} options
20
+ * @returns {{
21
+ * add: (sessionId: string) => Promise<void>,
22
+ * remove: (sessionId: string) => void,
23
+ * get: (sessionId: string) => any,
24
+ * list: () => string[]
25
+ * }}
26
+ */
27
+ const createSessionPool = options => {
28
+ const logger = options.logger
29
+ const maxBackoffMs = options.maxBackoffMs ?? 60000
30
+ const sessions = new Map() // sessionId -> { sock, attempts, stopped }
31
+
32
+ const computeBackoffMs = attempts => {
33
+ const base = Math.min(1000 * 2 ** attempts, maxBackoffMs)
34
+ return base + Math.floor(Math.random() * (base * 0.2))
35
+ }
36
+
37
+ const start = async sessionId => {
38
+ const entry = sessions.get(sessionId) || { attempts: 0, stopped: false }
39
+ sessions.set(sessionId, entry)
40
+ if (entry.stopped) return
41
+ try {
42
+ const sock = await options.makeSocket(sessionId)
43
+ entry.sock = sock
44
+ options.onSessionUpdate?.(sessionId, sock)
45
+ sock.ev.on('connection.update', update => {
46
+ if (update.connection === 'open') {
47
+ entry.attempts = 0
48
+ }
49
+ if (update.connection === 'close') {
50
+ const statusCode = update.lastDisconnect?.error?.output?.statusCode
51
+ if (statusCode === DISCONNECT_REASON_LOGGED_OUT) {
52
+ logger?.info?.({ sessionId }, 'session logged out, removing from pool')
53
+ sessions.delete(sessionId)
54
+ options.onLoggedOut?.(sessionId)
55
+ return
56
+ }
57
+ if (entry.stopped) return
58
+ entry.attempts += 1
59
+ const backoffMs = computeBackoffMs(entry.attempts)
60
+ logger?.warn?.(
61
+ { sessionId, attempt: entry.attempts, backoffMs },
62
+ 'session disconnected, reconnecting with backoff'
63
+ )
64
+ setTimeout(() => start(sessionId), backoffMs)
65
+ }
66
+ })
67
+ } catch (error) {
68
+ entry.attempts += 1
69
+ const backoffMs = computeBackoffMs(entry.attempts)
70
+ logger?.error?.({ error, sessionId, backoffMs }, 'failed to start session, retrying with backoff')
71
+ if (!entry.stopped) {
72
+ setTimeout(() => start(sessionId), backoffMs)
73
+ }
74
+ }
75
+ }
76
+
77
+ const add = sessionId => start(sessionId)
78
+ const remove = sessionId => {
79
+ const entry = sessions.get(sessionId)
80
+ if (!entry) return
81
+ entry.stopped = true
82
+ if (entry.sock?.end) {
83
+ try {
84
+ entry.sock.end(undefined)
85
+ } catch {}
86
+ }
87
+ sessions.delete(sessionId)
88
+ }
89
+ const get = sessionId => sessions.get(sessionId)?.sock
90
+ const list = () => [...sessions.keys()]
91
+
92
+ return { add, remove, get, list }
93
+ }
94
+ exports.createSessionPool = createSessionPool