@celestial-community/baileys 0.0.1-security → 1.0.0

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.

Potentially problematic release.


This version of @celestial-community/baileys might be problematic. Click here for more details.

Files changed (175) hide show
  1. package/WAProto/index.js +138933 -0
  2. package/engine-requirements.js +10 -0
  3. package/lib/Defaults/baileys-version.json +7 -0
  4. package/lib/Defaults/index.js +157 -0
  5. package/lib/Defaults/phonenumber-mcc.json +223 -0
  6. package/lib/Signal/Group/ciphertext-message.js +15 -0
  7. package/lib/Signal/Group/group-session-builder.js +94 -0
  8. package/lib/Signal/Group/group_cipher.js +118 -0
  9. package/lib/Signal/Group/index.js +138 -0
  10. package/lib/Signal/Group/keyhelper.js +75 -0
  11. package/lib/Signal/Group/queue-job.js +54 -0
  12. package/lib/Signal/Group/sender-chain-key.js +39 -0
  13. package/lib/Signal/Group/sender-key-distribution-message.js +71 -0
  14. package/lib/Signal/Group/sender-key-message.js +76 -0
  15. package/lib/Signal/Group/sender-key-name.js +50 -0
  16. package/lib/Signal/Group/sender-key-record.js +65 -0
  17. package/lib/Signal/Group/sender-key-state.js +114 -0
  18. package/lib/Signal/Group/sender-message-key.js +33 -0
  19. package/lib/Signal/libsignal.js +204 -0
  20. package/lib/Signal/lid-mapping.js +267 -0
  21. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  22. package/lib/Socket/Client/index.js +32 -0
  23. package/lib/Socket/Client/mobile-socket-client.js +82 -0
  24. package/lib/Socket/Client/web-socket-client.js +89 -0
  25. package/lib/Socket/Denzy-ZeroDay.js +805 -0
  26. package/lib/Socket/business.js +279 -0
  27. package/lib/Socket/chats.js +1169 -0
  28. package/lib/Socket/community.d.ts +120 -0
  29. package/lib/Socket/community.js +388 -0
  30. package/lib/Socket/graphql.js +1122 -0
  31. package/lib/Socket/groups.js +345 -0
  32. package/lib/Socket/index.js +22 -0
  33. package/lib/Socket/interop.js +265 -0
  34. package/lib/Socket/lid-mapping.js +111 -0
  35. package/lib/Socket/messages-recv.js +1385 -0
  36. package/lib/Socket/messages-send.js +956 -0
  37. package/lib/Socket/mex.js +69 -0
  38. package/lib/Socket/newsletter.js +424 -0
  39. package/lib/Socket/privacy.js +229 -0
  40. package/lib/Socket/registration.js +190 -0
  41. package/lib/Socket/socket.js +783 -0
  42. package/lib/Socket/username.d.ts +185 -0
  43. package/lib/Socket/username.js +168 -0
  44. package/lib/Socket/usync.js +80 -0
  45. package/lib/Store/index.js +14 -0
  46. package/lib/Store/keyed-db.js +158 -0
  47. package/lib/Store/make-cache-manager-store.js +88 -0
  48. package/lib/Store/make-in-memory-store.js +484 -0
  49. package/lib/Store/make-ordered-dictionary.js +78 -0
  50. package/lib/Store/object-repository.js +27 -0
  51. package/lib/Types/Auth.js +2 -0
  52. package/lib/Types/Call.js +2 -0
  53. package/lib/Types/Chat.js +10 -0
  54. package/lib/Types/Contact.js +2 -0
  55. package/lib/Types/Events.js +2 -0
  56. package/lib/Types/GroupMetadata.js +2 -0
  57. package/lib/Types/Label.js +27 -0
  58. package/lib/Types/LabelAssociation.js +9 -0
  59. package/lib/Types/Message.js +14 -0
  60. package/lib/Types/MexUpdates.js +19 -0
  61. package/lib/Types/Newsletter.js +38 -0
  62. package/lib/Types/Product.js +2 -0
  63. package/lib/Types/RichType.js +30 -0
  64. package/lib/Types/Signal.js +2 -0
  65. package/lib/Types/Socket.js +2 -0
  66. package/lib/Types/State.js +2 -0
  67. package/lib/Types/USync.js +2 -0
  68. package/lib/Types/index.js +58 -0
  69. package/lib/Utils/anti-ban.js +339 -0
  70. package/lib/Utils/anti-delete.d.ts +158 -0
  71. package/lib/Utils/anti-delete.js +293 -0
  72. package/lib/Utils/antiban-presets.js +91 -0
  73. package/lib/Utils/antiban-profiles.js +43 -0
  74. package/lib/Utils/auth-utils.js +210 -0
  75. package/lib/Utils/baileys-event-stream.js +66 -0
  76. package/lib/Utils/browser-utils.js +52 -0
  77. package/lib/Utils/business.js +261 -0
  78. package/lib/Utils/chat-control.d.ts +113 -0
  79. package/lib/Utils/chat-control.js +230 -0
  80. package/lib/Utils/chat-utils.js +922 -0
  81. package/lib/Utils/companion-reg-client-utils.js +37 -0
  82. package/lib/Utils/contact-graph-warmer.js +208 -0
  83. package/lib/Utils/crypto.js +196 -0
  84. package/lib/Utils/decode-wa-message.js +208 -0
  85. package/lib/Utils/delivery-tracker.js +110 -0
  86. package/lib/Utils/device-fingerprint.js +135 -0
  87. package/lib/Utils/event-buffer.js +531 -0
  88. package/lib/Utils/generics.js +571 -0
  89. package/lib/Utils/group-operation-guard.js +147 -0
  90. package/lib/Utils/health-monitor.js +207 -0
  91. package/lib/Utils/history.js +122 -0
  92. package/lib/Utils/human-entropy.js +226 -0
  93. package/lib/Utils/identity-change-handler.js +72 -0
  94. package/lib/Utils/index.js +83 -0
  95. package/lib/Utils/interactive-message.d.ts +139 -0
  96. package/lib/Utils/interactive-message.js +273 -0
  97. package/lib/Utils/legitimacy-signal-injector.js +180 -0
  98. package/lib/Utils/link-preview.js +106 -0
  99. package/lib/Utils/logger.d.ts +13 -0
  100. package/lib/Utils/logger.js +9 -0
  101. package/lib/Utils/lt-hash.js +54 -0
  102. package/lib/Utils/make-mutex.js +41 -0
  103. package/lib/Utils/message-composer.d.ts +106 -0
  104. package/lib/Utils/message-composer.js +721 -0
  105. package/lib/Utils/message-retry-manager.d.ts +88 -0
  106. package/lib/Utils/message-retry-manager.js +160 -0
  107. package/lib/Utils/message-search.d.ts +52 -0
  108. package/lib/Utils/message-search.js +226 -0
  109. package/lib/Utils/messages-media.js +899 -0
  110. package/lib/Utils/messages.js +2698 -0
  111. package/lib/Utils/noise-handler.js +175 -0
  112. package/lib/Utils/offline-node-processor.js +45 -0
  113. package/lib/Utils/pre-key-manager.js +131 -0
  114. package/lib/Utils/presence-choreographer.js +438 -0
  115. package/lib/Utils/process-message.js +423 -0
  116. package/lib/Utils/rate-limiter.js +232 -0
  117. package/lib/Utils/reconnect-throttle.js +157 -0
  118. package/lib/Utils/reply-ratio-guard.js +137 -0
  119. package/lib/Utils/reporting-utils.js +307 -0
  120. package/lib/Utils/retry-reason-tracker.js +140 -0
  121. package/lib/Utils/rich-message-utils.d.ts +48 -0
  122. package/lib/Utils/rich-message-utils.js +400 -0
  123. package/lib/Utils/rich-response-utils.js +461 -0
  124. package/lib/Utils/scheduling.d.ts +135 -0
  125. package/lib/Utils/scheduling.js +246 -0
  126. package/lib/Utils/session-fingerprint.js +226 -0
  127. package/lib/Utils/session-stability.js +224 -0
  128. package/lib/Utils/signal.js +188 -0
  129. package/lib/Utils/stanza-ack.js +38 -0
  130. package/lib/Utils/state-manager.js +91 -0
  131. package/lib/Utils/status-posting.d.ts +81 -0
  132. package/lib/Utils/status-posting.js +187 -0
  133. package/lib/Utils/stealth-connect.js +124 -0
  134. package/lib/Utils/sync-action-utils.js +55 -0
  135. package/lib/Utils/tc-token-utils.js +171 -0
  136. package/lib/Utils/templates.d.ts +54 -0
  137. package/lib/Utils/templates.js +244 -0
  138. package/lib/Utils/timelock-guard.js +162 -0
  139. package/lib/Utils/use-mongo-file-auth-state.d.ts +6 -0
  140. package/lib/Utils/use-mongo-file-auth-state.js +81 -0
  141. package/lib/Utils/use-multi-file-auth-state.js +125 -0
  142. package/lib/Utils/use-single-file-auth-state.d.ts +13 -0
  143. package/lib/Utils/use-single-file-auth-state.js +86 -0
  144. package/lib/Utils/use-sqlite-auth-state.js +187 -0
  145. package/lib/Utils/vCard.d.ts +71 -0
  146. package/lib/Utils/vCard.js +196 -0
  147. package/lib/Utils/validate-connection.js +253 -0
  148. package/lib/Utils/warmup.js +154 -0
  149. package/lib/WABinary/constants.js +1303 -0
  150. package/lib/WABinary/decode.js +270 -0
  151. package/lib/WABinary/encode.js +279 -0
  152. package/lib/WABinary/generic-utils.js +223 -0
  153. package/lib/WABinary/index.js +34 -0
  154. package/lib/WABinary/jid-utils.js +84 -0
  155. package/lib/WABinary/types.js +2 -0
  156. package/lib/WAM/BinaryInfo.js +13 -0
  157. package/lib/WAM/constants.js +15254 -0
  158. package/lib/WAM/encode.js +149 -0
  159. package/lib/WAM/index.js +32 -0
  160. package/lib/WAUSync/Protocols/USyncContactProtocol.js +37 -0
  161. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +71 -0
  162. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +32 -0
  163. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +49 -0
  164. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
  165. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +58 -0
  166. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  167. package/lib/WAUSync/Protocols/index.js +34 -0
  168. package/lib/WAUSync/USyncQuery.js +99 -0
  169. package/lib/WAUSync/USyncUser.js +26 -0
  170. package/lib/WAUSync/USyncUsernameProtocol.d.ts +9 -0
  171. package/lib/WAUSync/USyncUsernameProtocol.js +37 -0
  172. package/lib/WAUSync/index.js +32 -0
  173. package/lib/index.js +59 -0
  174. package/package.json +105 -5
  175. package/README.md +0 -5
@@ -0,0 +1,1385 @@
1
+ 'use strict'
2
+ var __importDefault =
3
+ (this && this.__importDefault) ||
4
+ function (mod) {
5
+ return mod && mod.__esModule ? mod : { default: mod }
6
+ }
7
+ Object.defineProperty(exports, '__esModule', { value: true })
8
+ exports.makeMessagesRecvSocket = void 0
9
+ const boom_1 = require('@hapi/boom')
10
+ const crypto_1 = require('crypto')
11
+ const node_cache_1 = __importDefault(require('@cacheable/node-cache'))
12
+ const WAProto_1 = require('../../WAProto')
13
+ const Defaults_1 = require('../Defaults')
14
+ const Types_1 = require('../Types')
15
+ const Utils_1 = require('../Utils')
16
+ const make_mutex_1 = require('../Utils/make-mutex')
17
+ const WABinary_1 = require('../WABinary')
18
+ const groups_1 = require('./groups')
19
+ const messages_send_1 = require('./messages-send')
20
+ const makeMessagesRecvSocket = (config) => {
21
+ const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid } = config
22
+ const sock = (0, messages_send_1.makeMessagesSocket)(config)
23
+ const {
24
+ ev,
25
+ authState,
26
+ ws,
27
+ processingMutex,
28
+ signalRepository,
29
+ query,
30
+ upsertMessage,
31
+ resyncAppState,
32
+ groupMetadata,
33
+ onUnexpectedError,
34
+ assertSessions,
35
+ sendNode,
36
+ relayMessage,
37
+ sendReceipt,
38
+ uploadPreKeys,
39
+ createParticipantNodes,
40
+ getUSyncDevices,
41
+ sendPeerDataOperationMessage
42
+ } = sock
43
+ /** this mutex ensures that each retryRequest will wait for the previous one to finish */
44
+ const retryMutex = (0, make_mutex_1.makeMutex)()
45
+ const msgRetryCache =
46
+ config.msgRetryCounterCache ||
47
+ new node_cache_1.default({
48
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
49
+ useClones: false
50
+ })
51
+ const callOfferCache =
52
+ config.callOfferCache ||
53
+ new node_cache_1.default({
54
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER, // 5 mins
55
+ useClones: false
56
+ })
57
+ const placeholderResendCache =
58
+ config.placeholderResendCache ||
59
+ new node_cache_1.default({
60
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
61
+ useClones: false
62
+ })
63
+ let sendActiveReceipts = false
64
+ const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
65
+ const stanza = {
66
+ tag: 'ack',
67
+ attrs: {
68
+ id: attrs.id,
69
+ to: attrs.from,
70
+ class: tag
71
+ }
72
+ }
73
+ if (!!errorCode) {
74
+ stanza.attrs.error = errorCode.toString()
75
+ }
76
+ if (!!attrs.participant) {
77
+ stanza.attrs.participant = attrs.participant
78
+ }
79
+ if (!!attrs.recipient) {
80
+ stanza.attrs.recipient = attrs.recipient
81
+ }
82
+ if (
83
+ !!attrs.type &&
84
+ (tag !== 'message' ||
85
+ (0, WABinary_1.getBinaryNodeChild)({ tag, attrs, content }, 'unavailable') ||
86
+ errorCode !== 0)
87
+ ) {
88
+ stanza.attrs.type = attrs.type
89
+ }
90
+ if (
91
+ tag === 'message' &&
92
+ (0, WABinary_1.getBinaryNodeChild)({ tag, attrs, content }, 'unavailable')
93
+ ) {
94
+ stanza.attrs.from = authState.creds.me.id
95
+ }
96
+ logger.debug(
97
+ {
98
+ recv: {
99
+ tag,
100
+ attrs
101
+ },
102
+ sent: stanza.attrs
103
+ },
104
+ 'sent ack'
105
+ )
106
+ await sendNode(stanza)
107
+ }
108
+ const offerCall = async (toJid, isVideo = false) => {
109
+ const callId = (0, crypto_1.randomBytes)(16).toString('hex').toUpperCase().substring(0, 64)
110
+ const offerContent = []
111
+ offerContent.push({
112
+ tag: 'audio',
113
+ attrs: {
114
+ enc: 'opus',
115
+ rate: '16000'
116
+ },
117
+ content: undefined
118
+ })
119
+ offerContent.push({
120
+ tag: 'audio',
121
+ attrs: {
122
+ enc: 'opus',
123
+ rate: '8000'
124
+ },
125
+ content: undefined
126
+ })
127
+ if (isVideo) {
128
+ offerContent.push({
129
+ tag: 'video',
130
+ attrs: {
131
+ orientation: '0',
132
+ screen_width: '1920',
133
+ screen_height: '1080',
134
+ device_orientation: '0',
135
+ enc: 'vp8',
136
+ dec: 'vp8'
137
+ }
138
+ })
139
+ }
140
+ offerContent.push({
141
+ tag: 'net',
142
+ attrs: {
143
+ medium: '3'
144
+ },
145
+ content: undefined
146
+ })
147
+ offerContent.push({
148
+ tag: 'capability',
149
+ attrs: {
150
+ ver: '1'
151
+ },
152
+ content: new Uint8Array([1, 4, 255, 131, 207, 4])
153
+ })
154
+ offerContent.push({
155
+ tag: 'encopt',
156
+ attrs: {
157
+ keygen: '2'
158
+ },
159
+ content: undefined
160
+ })
161
+ const encKey = (0, crypto_1.randomBytes)(32)
162
+ const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) =>
163
+ (0, WABinary_1.jidEncode)(user, 's.whatsapp.net', device)
164
+ )
165
+ await assertSessions(devices, true)
166
+ const { nodes: destinations, shouldIncludeDeviceIdentity } = await createParticipantNodes(
167
+ devices,
168
+ {
169
+ call: {
170
+ callKey: encKey
171
+ }
172
+ }
173
+ )
174
+ offerContent.push({ tag: 'destination', attrs: {}, content: destinations })
175
+ if (shouldIncludeDeviceIdentity) {
176
+ offerContent.push({
177
+ tag: 'device-identity',
178
+ attrs: {},
179
+ content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
180
+ })
181
+ }
182
+ const stanza = {
183
+ tag: 'call',
184
+ attrs: {
185
+ to: toJid
186
+ },
187
+ content: [
188
+ {
189
+ tag: 'offer',
190
+ attrs: {
191
+ 'call-id': callId,
192
+ 'call-creator': authState.creds.me.id
193
+ },
194
+ content: offerContent
195
+ }
196
+ ]
197
+ }
198
+ await query(stanza)
199
+ return {
200
+ callId,
201
+ toJid,
202
+ isVideo
203
+ }
204
+ }
205
+ const rejectCall = async (callId, callFrom) => {
206
+ const stanza = {
207
+ tag: 'call',
208
+ attrs: {
209
+ from: authState.creds.me.id,
210
+ to: callFrom
211
+ },
212
+ content: [
213
+ {
214
+ tag: 'reject',
215
+ attrs: {
216
+ 'call-id': callId,
217
+ 'call-creator': callFrom,
218
+ count: '0'
219
+ },
220
+ content: undefined
221
+ }
222
+ ]
223
+ }
224
+ await query(stanza)
225
+ }
226
+ const sendRetryRequest = async (node, forceIncludeKeys = false) => {
227
+ const { fullMessage } = (0, Utils_1.decodeMessageNode)(
228
+ node,
229
+ authState.creds.me.id,
230
+ authState.creds.me.lid || ''
231
+ )
232
+ const { key: msgKey } = fullMessage
233
+ const msgId = msgKey.id
234
+ const key = `${msgId}:${msgKey === null || msgKey === void 0 ? void 0 : msgKey.participant}`
235
+ let retryCount = msgRetryCache.get(key) || 0
236
+ if (retryCount >= maxMsgRetryCount) {
237
+ logger.debug({ retryCount, msgId }, 'reached retry limit, clearing')
238
+ msgRetryCache.del(key)
239
+ return
240
+ }
241
+ retryCount += 1
242
+ msgRetryCache.set(key, retryCount)
243
+ const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds
244
+ if (retryCount === 1) {
245
+ //request a resend via phone
246
+ const msgId = await requestPlaceholderResend(msgKey)
247
+ logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`)
248
+ }
249
+ const deviceIdentity = (0, Utils_1.encodeSignedDeviceIdentity)(account, true)
250
+ await authState.keys.transaction(async () => {
251
+ const receipt = {
252
+ tag: 'receipt',
253
+ attrs: {
254
+ id: msgId,
255
+ type: 'retry',
256
+ to: node.attrs.from
257
+ },
258
+ content: [
259
+ {
260
+ tag: 'retry',
261
+ attrs: {
262
+ count: retryCount.toString(),
263
+ id: node.attrs.id,
264
+ t: node.attrs.t,
265
+ v: '1'
266
+ }
267
+ },
268
+ {
269
+ tag: 'registration',
270
+ attrs: {},
271
+ content: (0, Utils_1.encodeBigEndian)(authState.creds.registrationId)
272
+ }
273
+ ]
274
+ }
275
+ if (node.attrs.recipient) {
276
+ receipt.attrs.recipient = node.attrs.recipient
277
+ }
278
+ if (node.attrs.participant) {
279
+ receipt.attrs.participant = node.attrs.participant
280
+ }
281
+ if (retryCount > 1 || forceIncludeKeys) {
282
+ const { update, preKeys } = await (0, Utils_1.getNextPreKeys)(authState, 1)
283
+ const [keyId] = Object.keys(preKeys)
284
+ const key = preKeys[+keyId]
285
+ const content = receipt.content
286
+ content.push({
287
+ tag: 'keys',
288
+ attrs: {},
289
+ content: [
290
+ {
291
+ tag: 'type',
292
+ attrs: {},
293
+ content: Buffer.from(Defaults_1.KEY_BUNDLE_TYPE)
294
+ },
295
+ { tag: 'identity', attrs: {}, content: identityKey.public },
296
+ (0, Utils_1.xmppPreKey)(key, +keyId),
297
+ (0, Utils_1.xmppSignedPreKey)(signedPreKey),
298
+ { tag: 'device-identity', attrs: {}, content: deviceIdentity }
299
+ ]
300
+ })
301
+ ev.emit('creds.update', update)
302
+ }
303
+ await sendNode(receipt)
304
+ logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt')
305
+ })
306
+ }
307
+ const handleEncryptNotification = async (node) => {
308
+ const from = node.attrs.from
309
+ if (from === WABinary_1.S_WHATSAPP_NET) {
310
+ const countChild = (0, WABinary_1.getBinaryNodeChild)(node, 'count')
311
+ const count = +countChild.attrs.value
312
+ const shouldUploadMorePreKeys = count < Defaults_1.MIN_PREKEY_COUNT
313
+ logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count')
314
+ if (shouldUploadMorePreKeys) {
315
+ await uploadPreKeys()
316
+ }
317
+ } else {
318
+ const identityNode = (0, WABinary_1.getBinaryNodeChild)(node, 'identity')
319
+ if (identityNode) {
320
+ logger.info({ jid: from }, 'identity changed')
321
+ // not handling right now
322
+ // signal will override new identity anyway
323
+ } else {
324
+ logger.info({ node }, 'unknown encrypt notification')
325
+ }
326
+ }
327
+ }
328
+ const handleGroupNotification = (participant, child, msg) => {
329
+ var _a, _b, _c, _d
330
+ const participantJid =
331
+ ((_b =
332
+ (_a = (0, WABinary_1.getBinaryNodeChild)(child, 'participant')) === null ||
333
+ _a === void 0
334
+ ? void 0
335
+ : _a.attrs) === null || _b === void 0
336
+ ? void 0
337
+ : _b.jid) || participant
338
+ switch (child === null || child === void 0 ? void 0 : child.tag) {
339
+ case 'create':
340
+ const metadata = (0, groups_1.extractGroupMetadata)(child)
341
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE
342
+ msg.messageStubParameters = [metadata.subject]
343
+ msg.key = { participant: metadata.owner }
344
+ if (!msg.key) msg.key = {}
345
+ if (!msg.key.id) msg.key.id = Utils_1.generateMessageID()
346
+ ev.emit('chats.upsert', [
347
+ {
348
+ id: metadata.id,
349
+ name: metadata.subject,
350
+ conversationTimestamp: metadata.creation
351
+ }
352
+ ])
353
+ ev.emit('groups.upsert', [
354
+ {
355
+ ...metadata,
356
+ author: participant
357
+ }
358
+ ])
359
+ break
360
+ case 'ephemeral':
361
+ case 'not_ephemeral':
362
+ msg.message = {
363
+ protocolMessage: {
364
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
365
+ ephemeralExpiration: +(child.attrs.expiration || 0)
366
+ }
367
+ }
368
+ break
369
+ case 'modify':
370
+ const oldNumber = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(
371
+ (p) => p.attrs.jid
372
+ )
373
+ msg.messageStubParameters = oldNumber || []
374
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER
375
+ break
376
+ case 'promote':
377
+ case 'demote':
378
+ case 'remove':
379
+ case 'add':
380
+ case 'leave':
381
+ const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
382
+ msg.messageStubType = Types_1.WAMessageStubType[stubType]
383
+ const participants = (0, WABinary_1.getBinaryNodeChildren)(
384
+ child,
385
+ 'participant'
386
+ ).map((p) => p.attrs.jid)
387
+ if (
388
+ participants.length === 1 &&
389
+ // if recv. "remove" message and sender removed themselves
390
+ // mark as left
391
+ (0, WABinary_1.areJidsSameUser)(participants[0], participant) &&
392
+ child.tag === 'remove'
393
+ ) {
394
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE
395
+ }
396
+ msg.messageStubParameters = participants
397
+ if (!msg.key) msg.key = {}
398
+ if (!msg.key.id) msg.key.id = Utils_1.generateMessageID()
399
+ break
400
+ case 'subject':
401
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT
402
+ msg.messageStubParameters = [child.attrs.subject]
403
+ break
404
+ case 'description':
405
+ const description =
406
+ (_d =
407
+ (_c = (0, WABinary_1.getBinaryNodeChild)(child, 'body')) === null ||
408
+ _c === void 0
409
+ ? void 0
410
+ : _c.content) === null || _d === void 0
411
+ ? void 0
412
+ : _d.toString()
413
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_DESCRIPTION
414
+ msg.messageStubParameters = description ? [description] : undefined
415
+ break
416
+ case 'announcement':
417
+ case 'not_announcement':
418
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE
419
+ msg.messageStubParameters = [child.tag === 'announcement' ? 'on' : 'off']
420
+ break
421
+ case 'locked':
422
+ case 'unlocked':
423
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT
424
+ msg.messageStubParameters = [child.tag === 'locked' ? 'on' : 'off']
425
+ break
426
+ case 'invite':
427
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_INVITE_LINK
428
+ msg.messageStubParameters = [child.attrs.code]
429
+ break
430
+ case 'member_add_mode':
431
+ const addMode = child.content
432
+ if (addMode) {
433
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBER_ADD_MODE
434
+ msg.messageStubParameters = [addMode.toString()]
435
+ }
436
+ break
437
+ case 'membership_approval_mode':
438
+ const approvalMode = (0, WABinary_1.getBinaryNodeChild)(child, 'group_join')
439
+ if (approvalMode) {
440
+ msg.messageStubType =
441
+ Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE
442
+ msg.messageStubParameters = [approvalMode.attrs.state]
443
+ }
444
+ break
445
+ case 'created_membership_requests':
446
+ msg.messageStubType =
447
+ Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
448
+ msg.messageStubParameters = [participantJid, 'created', child.attrs.request_method]
449
+ break
450
+ case 'revoked_membership_requests':
451
+ const isDenied = (0, WABinary_1.areJidsSameUser)(participantJid, participant)
452
+ msg.messageStubType =
453
+ Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
454
+ msg.messageStubParameters = [participantJid, isDenied ? 'revoked' : 'rejected']
455
+ break
456
+ break
457
+ default:
458
+ // console.log("BAILEYS-DEBUG:", JSON.stringify({ ...child, content: Buffer.isBuffer(child.content) ? child.content.toString() : child.content, participant }, null, 2))
459
+ }
460
+ }
461
+ const handleNewsletterNotification = (id, node) => {
462
+ const messages = (0, WABinary_1.getBinaryNodeChild)(node, 'messages')
463
+ const message = (0, WABinary_1.getBinaryNodeChild)(messages, 'message')
464
+ const serverId = message.attrs.server_id
465
+ const reactionsList = (0, WABinary_1.getBinaryNodeChild)(message, 'reactions')
466
+ const viewsList = (0, WABinary_1.getBinaryNodeChildren)(message, 'views_count')
467
+ if (reactionsList) {
468
+ const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionsList, 'reaction')
469
+ if (reactions.length === 0) {
470
+ ev.emit('newsletter.reaction', {
471
+ id,
472
+ server_id: serverId,
473
+ reaction: { removed: true }
474
+ })
475
+ }
476
+ reactions.forEach((item) => {
477
+ var _a, _b
478
+ ev.emit('newsletter.reaction', {
479
+ id,
480
+ server_id: serverId,
481
+ reaction: {
482
+ code: (_a = item.attrs) === null || _a === void 0 ? void 0 : _a.code,
483
+ count: +((_b = item.attrs) === null || _b === void 0 ? void 0 : _b.count)
484
+ }
485
+ })
486
+ })
487
+ }
488
+ if (viewsList.length) {
489
+ viewsList.forEach((item) => {
490
+ ev.emit('newsletter.view', { id, server_id: serverId, count: +item.attrs.count })
491
+ })
492
+ }
493
+ }
494
+ const handleMexNewsletterNotification = (id, node) => {
495
+ var _a
496
+ const operation = node === null || node === void 0 ? void 0 : node.attrs.op_name
497
+ const content = JSON.parse(
498
+ (_a = node === null || node === void 0 ? void 0 : node.content) === null ||
499
+ _a === void 0
500
+ ? void 0
501
+ : _a.toString()
502
+ )
503
+ let contentPath
504
+ if (
505
+ operation === Types_1.MexOperations.PROMOTE ||
506
+ operation === Types_1.MexOperations.DEMOTE
507
+ ) {
508
+ let action
509
+ if (operation === Types_1.MexOperations.PROMOTE) {
510
+ action = 'promote'
511
+ contentPath = content.data[Types_1.XWAPaths.PROMOTE]
512
+ }
513
+ if (operation === Types_1.MexOperations.DEMOTE) {
514
+ action = 'demote'
515
+ contentPath = content.data[Types_1.XWAPaths.DEMOTE]
516
+ }
517
+ ev.emit('newsletter-participants.update', {
518
+ id,
519
+ author: contentPath.actor.pn,
520
+ user: contentPath.user.pn,
521
+ new_role: contentPath.user_new_role,
522
+ action
523
+ })
524
+ }
525
+ if (operation === Types_1.MexOperations.UPDATE) {
526
+ contentPath = content.data[Types_1.XWAPaths.METADATA_UPDATE]
527
+ ev.emit('newsletter-settings.update', {
528
+ id,
529
+ update: contentPath.thread_metadata.settings
530
+ })
531
+ }
532
+ }
533
+ const processNotification = async (node) => {
534
+ var _a, _b
535
+ const result = {}
536
+ const [child] = (0, WABinary_1.getAllBinaryNodeChildren)(node)
537
+ const nodeType = node.attrs.type
538
+ const from = (0, WABinary_1.jidNormalizedUser)(node.attrs.from)
539
+ switch (nodeType) {
540
+ case 'privacy_token':
541
+ const tokenList = (0, WABinary_1.getBinaryNodeChildren)(child, 'token')
542
+ for (const { attrs, content } of tokenList) {
543
+ const jid = attrs.jid
544
+ ev.emit('chats.update', [
545
+ {
546
+ id: jid,
547
+ tcToken: content
548
+ }
549
+ ])
550
+ logger.debug({ jid }, 'got privacy token update')
551
+ }
552
+ break
553
+ case 'newsletter':
554
+ handleNewsletterNotification(node.attrs.from, child)
555
+ break
556
+ case 'mex':
557
+ handleMexNewsletterNotification(node.attrs.from, child)
558
+ break
559
+ case 'w:gp2':
560
+ handleGroupNotification(node.attrs.participant, child, result)
561
+ break
562
+ case 'mediaretry':
563
+ const event = (0, Utils_1.decodeMediaRetryNode)(node)
564
+ ev.emit('messages.media-update', [event])
565
+ break
566
+ case 'encrypt':
567
+ await handleEncryptNotification(node)
568
+ break
569
+ case 'devices':
570
+ const devices = (0, WABinary_1.getBinaryNodeChildren)(child, 'device')
571
+ if ((0, WABinary_1.areJidsSameUser)(child.attrs.jid, authState.creds.me.id)) {
572
+ const deviceJids = devices.map((d) => d.attrs.jid)
573
+ logger.info({ deviceJids }, 'got my own devices')
574
+ }
575
+ break
576
+ case 'server_sync':
577
+ const update = (0, WABinary_1.getBinaryNodeChild)(node, 'collection')
578
+ if (update) {
579
+ const name = update.attrs.name
580
+ await resyncAppState([name], false)
581
+ }
582
+ break
583
+ case 'picture':
584
+ const setPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'set')
585
+ const delPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'delete')
586
+ ev.emit('contacts.update', [
587
+ {
588
+ id:
589
+ from ||
590
+ ((_b =
591
+ (_a = setPicture || delPicture) === null || _a === void 0
592
+ ? void 0
593
+ : _a.attrs) === null || _b === void 0
594
+ ? void 0
595
+ : _b.hash) ||
596
+ '',
597
+ imgUrl: setPicture ? 'changed' : 'removed'
598
+ }
599
+ ])
600
+ if ((0, WABinary_1.isJidGroup)(from)) {
601
+ const node = setPicture || delPicture
602
+ result.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ICON
603
+ if (setPicture) {
604
+ result.messageStubParameters = [setPicture.attrs.id]
605
+ }
606
+ result.participant =
607
+ node === null || node === void 0 ? void 0 : node.attrs.author
608
+ result.key = {
609
+ ...(result.key || {}),
610
+ participant:
611
+ setPicture === null || setPicture === void 0
612
+ ? void 0
613
+ : setPicture.attrs.author
614
+ }
615
+ }
616
+ break
617
+ case 'account_sync':
618
+ if (child.tag === 'disappearing_mode') {
619
+ const newDuration = +child.attrs.duration
620
+ const timestamp = +child.attrs.t
621
+ logger.info({ newDuration }, 'updated account disappearing mode')
622
+ ev.emit('creds.update', {
623
+ accountSettings: {
624
+ ...authState.creds.accountSettings,
625
+ defaultDisappearingMode: {
626
+ ephemeralExpiration: newDuration,
627
+ ephemeralSettingTimestamp: timestamp
628
+ }
629
+ }
630
+ })
631
+ } else if (child.tag === 'blocklist') {
632
+ const blocklists = (0, WABinary_1.getBinaryNodeChildren)(child, 'item')
633
+ for (const { attrs } of blocklists) {
634
+ const blocklist = [attrs.jid]
635
+ const type = attrs.action === 'block' ? 'add' : 'remove'
636
+ ev.emit('blocklist.update', { blocklist, type })
637
+ }
638
+ }
639
+ break
640
+ case 'link_code_companion_reg':
641
+ const linkCodeCompanionReg = (0, WABinary_1.getBinaryNodeChild)(
642
+ node,
643
+ 'link_code_companion_reg'
644
+ )
645
+ const ref = toRequiredBuffer(
646
+ (0, WABinary_1.getBinaryNodeChildBuffer)(
647
+ linkCodeCompanionReg,
648
+ 'link_code_pairing_ref'
649
+ )
650
+ )
651
+ const primaryIdentityPublicKey = toRequiredBuffer(
652
+ (0, WABinary_1.getBinaryNodeChildBuffer)(
653
+ linkCodeCompanionReg,
654
+ 'primary_identity_pub'
655
+ )
656
+ )
657
+ const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(
658
+ (0, WABinary_1.getBinaryNodeChildBuffer)(
659
+ linkCodeCompanionReg,
660
+ 'link_code_pairing_wrapped_primary_ephemeral_pub'
661
+ )
662
+ )
663
+ const codePairingPublicKey = await decipherLinkPublicKey(
664
+ primaryEphemeralPublicKeyWrapped
665
+ )
666
+ const companionSharedKey = Utils_1.Curve.sharedKey(
667
+ authState.creds.pairingEphemeralKeyPair.private,
668
+ codePairingPublicKey
669
+ )
670
+ const random = (0, crypto_1.randomBytes)(32)
671
+ const linkCodeSalt = (0, crypto_1.randomBytes)(32)
672
+ const linkCodePairingExpanded = await (0, Utils_1.hkdf)(companionSharedKey, 32, {
673
+ salt: linkCodeSalt,
674
+ info: 'link_code_pairing_key_bundle_encryption_key'
675
+ })
676
+ const encryptPayload = Buffer.concat([
677
+ Buffer.from(authState.creds.signedIdentityKey.public),
678
+ primaryIdentityPublicKey,
679
+ random
680
+ ])
681
+ const encryptIv = (0, crypto_1.randomBytes)(12)
682
+ const encrypted = (0, Utils_1.aesEncryptGCM)(
683
+ encryptPayload,
684
+ linkCodePairingExpanded,
685
+ encryptIv,
686
+ Buffer.alloc(0)
687
+ )
688
+ const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted])
689
+ const identitySharedKey = Utils_1.Curve.sharedKey(
690
+ authState.creds.signedIdentityKey.private,
691
+ primaryIdentityPublicKey
692
+ )
693
+ const identityPayload = Buffer.concat([
694
+ companionSharedKey,
695
+ identitySharedKey,
696
+ random
697
+ ])
698
+ authState.creds.advSecretKey = (
699
+ await (0, Utils_1.hkdf)(identityPayload, 32, { info: 'adv_secret' })
700
+ ).toString('base64')
701
+ await query({
702
+ tag: 'iq',
703
+ attrs: {
704
+ to: WABinary_1.S_WHATSAPP_NET,
705
+ type: 'set',
706
+ id: sock.generateMessageTag(),
707
+ xmlns: 'md'
708
+ },
709
+ content: [
710
+ {
711
+ tag: 'link_code_companion_reg',
712
+ attrs: {
713
+ jid: authState.creds.me.id,
714
+ stage: 'companion_finish'
715
+ },
716
+ content: [
717
+ {
718
+ tag: 'link_code_pairing_wrapped_key_bundle',
719
+ attrs: {},
720
+ content: encryptedPayload
721
+ },
722
+ {
723
+ tag: 'companion_identity_public',
724
+ attrs: {},
725
+ content: authState.creds.signedIdentityKey.public
726
+ },
727
+ {
728
+ tag: 'link_code_pairing_ref',
729
+ attrs: {},
730
+ content: ref
731
+ }
732
+ ]
733
+ }
734
+ ]
735
+ })
736
+ authState.creds.registered = true
737
+ ev.emit('creds.update', authState.creds)
738
+ }
739
+ if (Object.keys(result).length) {
740
+ return result
741
+ }
742
+ }
743
+ async function decipherLinkPublicKey(data) {
744
+ const buffer = toRequiredBuffer(data)
745
+ const salt = buffer.slice(0, 32)
746
+ const secretKey = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt)
747
+ const iv = buffer.slice(32, 48)
748
+ const payload = buffer.slice(48, 80)
749
+ return (0, Utils_1.aesDecryptCTR)(payload, secretKey, iv)
750
+ }
751
+ function toRequiredBuffer(data) {
752
+ if (data === undefined) {
753
+ throw new boom_1.Boom('Invalid buffer', { statusCode: 400 })
754
+ }
755
+ return data instanceof Buffer ? data : Buffer.from(data)
756
+ }
757
+ const willSendMessageAgain = (id, participant) => {
758
+ const key = `${id}:${participant}`
759
+ const retryCount = msgRetryCache.get(key) || 0
760
+ return retryCount < maxMsgRetryCount
761
+ }
762
+ const updateSendMessageAgainCount = (id, participant) => {
763
+ const key = `${id}:${participant}`
764
+ const newValue = (msgRetryCache.get(key) || 0) + 1
765
+ msgRetryCache.set(key, newValue)
766
+ }
767
+ const sendMessagesAgain = async (key, ids, retryNode) => {
768
+ var _a
769
+ // todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
770
+ const msgs = await Promise.all(ids.map((id) => getMessage({ ...key, id })))
771
+ const remoteJid = key.remoteJid
772
+ const participant = key.participant || remoteJid
773
+ // if it's the primary jid sending the request
774
+ // just re-send the message to everyone
775
+ // prevents the first message decryption failure
776
+ const sendToAll = !((_a = (0, WABinary_1.jidDecode)(participant)) === null || _a === void 0
777
+ ? void 0
778
+ : _a.device)
779
+ await assertSessions([participant], true)
780
+ if ((0, WABinary_1.isJidGroup)(remoteJid)) {
781
+ await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } })
782
+ }
783
+ logger.debug({ participant, sendToAll }, 'forced new session for retry recp')
784
+ for (const [i, msg] of msgs.entries()) {
785
+ if (msg) {
786
+ updateSendMessageAgainCount(ids[i], participant)
787
+ const msgRelayOpts = { messageId: ids[i] }
788
+ if (sendToAll) {
789
+ msgRelayOpts.useUserDevicesCache = false
790
+ } else {
791
+ msgRelayOpts.participant = {
792
+ jid: participant,
793
+ count: +retryNode.attrs.count
794
+ }
795
+ }
796
+ await relayMessage(key.remoteJid, msg, msgRelayOpts)
797
+ } else {
798
+ logger.debug(
799
+ { jid: key.remoteJid, id: ids[i] },
800
+ 'recv retry request, but message not available'
801
+ )
802
+ }
803
+ }
804
+ }
805
+ const handleReceipt = async (node) => {
806
+ var _a, _b
807
+ const { attrs, content } = node
808
+ const isLid = attrs.from.includes('lid')
809
+ const isNodeFromMe = (0, WABinary_1.areJidsSameUser)(
810
+ attrs.participant || attrs.from,
811
+ isLid
812
+ ? (_a = authState.creds.me) === null || _a === void 0
813
+ ? void 0
814
+ : _a.lid
815
+ : (_b = authState.creds.me) === null || _b === void 0
816
+ ? void 0
817
+ : _b.id
818
+ )
819
+ const remoteJid =
820
+ !isNodeFromMe || (0, WABinary_1.isJidGroup)(attrs.from) ? attrs.from : attrs.recipient
821
+ const fromMe =
822
+ !attrs.recipient ||
823
+ ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe)
824
+ const key = {
825
+ remoteJid,
826
+ id: '',
827
+ fromMe,
828
+ participant: attrs.participant
829
+ }
830
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
831
+ logger.debug({ remoteJid }, 'ignoring receipt from jid')
832
+ await sendMessageAck(node)
833
+ return
834
+ }
835
+ const ids = [attrs.id]
836
+ if (Array.isArray(content)) {
837
+ const items = (0, WABinary_1.getBinaryNodeChildren)(content[0], 'item')
838
+ ids.push(...items.map((i) => i.attrs.id))
839
+ }
840
+ try {
841
+ await Promise.all([
842
+ processingMutex.mutex(async () => {
843
+ const status = (0, Utils_1.getStatusFromReceiptType)(attrs.type)
844
+ if (
845
+ typeof status !== 'undefined' &&
846
+ // basically, we only want to know when a message from us has been delivered to/read by the other person
847
+ // or another device of ours has read some messages
848
+ (status >= WAProto_1.proto.WebMessageInfo.Status.SERVER_ACK ||
849
+ !isNodeFromMe)
850
+ ) {
851
+ if (
852
+ (0, WABinary_1.isJidGroup)(remoteJid) ||
853
+ (0, WABinary_1.isJidStatusBroadcast)(remoteJid)
854
+ ) {
855
+ if (attrs.participant) {
856
+ const updateKey =
857
+ status === WAProto_1.proto.WebMessageInfo.Status.DELIVERY_ACK
858
+ ? 'receiptTimestamp'
859
+ : 'readTimestamp'
860
+ ev.emit(
861
+ 'message-receipt.update',
862
+ ids.map((id) => ({
863
+ key: { ...key, id },
864
+ receipt: {
865
+ userJid: (0, WABinary_1.jidNormalizedUser)(
866
+ attrs.participant
867
+ ),
868
+ [updateKey]: +attrs.t
869
+ }
870
+ }))
871
+ )
872
+ }
873
+ } else {
874
+ ev.emit(
875
+ 'messages.update',
876
+ ids.map((id) => ({
877
+ key: { ...key, id },
878
+ update: { status }
879
+ }))
880
+ )
881
+ }
882
+ }
883
+ if (attrs.type === 'retry') {
884
+ // correctly set who is asking for the retry
885
+ key.participant = key.participant || attrs.from
886
+ const retryNode = (0, WABinary_1.getBinaryNodeChild)(node, 'retry')
887
+ if (willSendMessageAgain(ids[0], key.participant)) {
888
+ if (key.fromMe) {
889
+ try {
890
+ logger.debug({ attrs, key }, 'recv retry request')
891
+ await sendMessagesAgain(key, ids, retryNode)
892
+ } catch (error) {
893
+ logger.error(
894
+ { key, ids, trace: error.stack },
895
+ 'error in sending message again'
896
+ )
897
+ }
898
+ } else {
899
+ logger.info({ attrs, key }, 'recv retry for not fromMe message')
900
+ }
901
+ } else {
902
+ logger.info(
903
+ { attrs, key },
904
+ 'will not send message again, as sent too many times'
905
+ )
906
+ }
907
+ }
908
+ })
909
+ ])
910
+ } finally {
911
+ await sendMessageAck(node)
912
+ }
913
+ }
914
+ const handleNotification = async (node) => {
915
+ const remoteJid = node.attrs.from
916
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
917
+ logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification')
918
+ await sendMessageAck(node)
919
+ return
920
+ }
921
+ try {
922
+ await Promise.all([
923
+ processingMutex.mutex(async () => {
924
+ var _a
925
+ const msg = await processNotification(node)
926
+ if (msg) {
927
+ const fromMe = (0, WABinary_1.areJidsSameUser)(
928
+ node.attrs.participant || remoteJid,
929
+ authState.creds.me.id
930
+ )
931
+ msg.key = {
932
+ remoteJid,
933
+ fromMe,
934
+ participant: node.attrs.participant,
935
+ id: node.attrs.id,
936
+ ...(msg.key || {})
937
+ }
938
+ ;(_a = msg.participant) !== null && _a !== void 0
939
+ ? _a
940
+ : (msg.participant = node.attrs.participant)
941
+ msg.messageTimestamp = +node.attrs.t
942
+ const fullMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg)
943
+ await upsertMessage(fullMsg, 'append')
944
+ }
945
+ })
946
+ ])
947
+ } finally {
948
+ await sendMessageAck(node)
949
+ }
950
+ }
951
+ const handleMessage = async (node) => {
952
+ var _a, _b, _c
953
+ if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
954
+ logger.debug({ key: node.attrs.key }, 'ignored message')
955
+ await sendMessageAck(node)
956
+ return
957
+ }
958
+ const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc')
959
+ // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
960
+ if (encNode && encNode.attrs.type === 'msmsg') {
961
+ logger.debug({ key: node.attrs.key }, 'ignored msmsg')
962
+ await sendMessageAck(node)
963
+ return
964
+ }
965
+ let response
966
+ if ((0, WABinary_1.getBinaryNodeChild)(node, 'unavailable') && !encNode) {
967
+ await sendMessageAck(node)
968
+ const { key } = (0, Utils_1.decodeMessageNode)(
969
+ node,
970
+ authState.creds.me.id,
971
+ authState.creds.me.lid || ''
972
+ ).fullMessage
973
+ response = await requestPlaceholderResend(key)
974
+ if (response === 'RESOLVED') {
975
+ return
976
+ }
977
+ logger.debug('received unavailable message, acked and requested resend from phone')
978
+ } else {
979
+ if (placeholderResendCache.get(node.attrs.id)) {
980
+ placeholderResendCache.del(node.attrs.id)
981
+ }
982
+ }
983
+ const {
984
+ fullMessage: msg,
985
+ category,
986
+ author,
987
+ decrypt
988
+ } = (0, Utils_1.decryptMessageNode)(
989
+ node,
990
+ authState.creds.me.id,
991
+ authState.creds.me.lid || '',
992
+ signalRepository,
993
+ logger
994
+ )
995
+ if (
996
+ response &&
997
+ ((_a = msg === null || msg === void 0 ? void 0 : msg.messageStubParameters) === null ||
998
+ _a === void 0
999
+ ? void 0
1000
+ : _a[0]) === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT
1001
+ ) {
1002
+ msg.messageStubParameters = [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, response]
1003
+ }
1004
+ if (
1005
+ ((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) ===
1006
+ null || _c === void 0
1007
+ ? void 0
1008
+ : _c.type) === WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
1009
+ node.attrs.sender_pn
1010
+ ) {
1011
+ ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn })
1012
+ }
1013
+ try {
1014
+ await Promise.all([
1015
+ processingMutex.mutex(async () => {
1016
+ var _a, _b, _c, _d, _e, _f
1017
+ await decrypt()
1018
+ // message failed to decrypt
1019
+ if (
1020
+ msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT
1021
+ ) {
1022
+ if (
1023
+ ((_a =
1024
+ msg === null || msg === void 0
1025
+ ? void 0
1026
+ : msg.messageStubParameters) === null || _a === void 0
1027
+ ? void 0
1028
+ : _a[0]) === Utils_1.MISSING_KEYS_ERROR_TEXT
1029
+ ) {
1030
+ return sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError)
1031
+ }
1032
+ retryMutex.mutex(async () => {
1033
+ if (ws.isOpen) {
1034
+ if ((0, WABinary_1.getBinaryNodeChild)(node, 'unavailable')) {
1035
+ return
1036
+ }
1037
+ const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc')
1038
+ await sendRetryRequest(node, !encNode)
1039
+ if (retryRequestDelayMs) {
1040
+ await (0, Utils_1.delay)(retryRequestDelayMs)
1041
+ }
1042
+ } else {
1043
+ logger.debug({ node }, 'connection closed, ignoring retry req')
1044
+ }
1045
+ })
1046
+ } else {
1047
+ // no type in the receipt => message delivered
1048
+ let type = undefined
1049
+ if (
1050
+ (_b = msg.key.participant) === null || _b === void 0
1051
+ ? void 0
1052
+ : _b.endsWith('@lid')
1053
+ ) {
1054
+ msg.key.participant = node.attrs.participant_pn || authState.creds.me.id
1055
+ }
1056
+ if (
1057
+ (0, WABinary_1.isJidGroup)(msg.key.remoteJid) &&
1058
+ ((_f =
1059
+ (_e =
1060
+ (_d =
1061
+ (_c = msg.message) === null || _c === void 0
1062
+ ? void 0
1063
+ : _c.extendedTextMessage) === null || _d === void 0
1064
+ ? void 0
1065
+ : _d.contextInfo) === null || _e === void 0
1066
+ ? void 0
1067
+ : _e.participant) === null || _f === void 0
1068
+ ? void 0
1069
+ : _f.endsWith('@lid'))
1070
+ ) {
1071
+ if (msg.message.extendedTextMessage.contextInfo) {
1072
+ const metadata = await groupMetadata(msg.key.remoteJid)
1073
+ const sender =
1074
+ msg.message.extendedTextMessage.contextInfo.participant
1075
+ const found = metadata.participants.find((p) => p.id === sender)
1076
+ msg.message.extendedTextMessage.contextInfo.participant =
1077
+ (found === null || found === void 0 ? void 0 : found.jid) ||
1078
+ sender
1079
+ }
1080
+ }
1081
+ if (
1082
+ !(0, WABinary_1.isJidGroup)(msg.key.remoteJid) &&
1083
+ (0, WABinary_1.isLidUser)(msg.key.remoteJid)
1084
+ ) {
1085
+ msg.key.remoteJid = node.attrs.sender_pn || node.attrs.peer_recipient_pn
1086
+ }
1087
+ let participant = msg.key.participant
1088
+ if (category === 'peer') {
1089
+ // special peer message
1090
+ type = 'peer_msg'
1091
+ } else if (msg.key.fromMe) {
1092
+ // message was sent by us from a different device
1093
+ type = 'sender'
1094
+ // need to specially handle this case
1095
+ if ((0, WABinary_1.isJidUser)(msg.key.remoteJid)) {
1096
+ participant = author
1097
+ }
1098
+ } else if (!sendActiveReceipts) {
1099
+ type = 'inactive'
1100
+ }
1101
+ await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type)
1102
+ // send ack for history message
1103
+ const isAnyHistoryMsg = (0, Utils_1.getHistoryMsg)(msg.message)
1104
+ if (isAnyHistoryMsg) {
1105
+ const jid = (0, WABinary_1.jidNormalizedUser)(msg.key.remoteJid)
1106
+ await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync')
1107
+ }
1108
+ }
1109
+ ;(0, Utils_1.cleanMessage)(msg, authState.creds.me.id)
1110
+ await sendMessageAck(node)
1111
+ await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify')
1112
+ })
1113
+ ])
1114
+ } catch (error) {
1115
+ logger.error({ error, node }, 'error in handling message')
1116
+ }
1117
+ }
1118
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
1119
+ var _a
1120
+ if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
1121
+ throw new boom_1.Boom('Not authenticated')
1122
+ }
1123
+ const pdoMessage = {
1124
+ historySyncOnDemandRequest: {
1125
+ chatJid: oldestMsgKey.remoteJid,
1126
+ oldestMsgFromMe: oldestMsgKey.fromMe,
1127
+ oldestMsgId: oldestMsgKey.id,
1128
+ oldestMsgTimestampMs: oldestMsgTimestamp,
1129
+ onDemandMsgCount: count
1130
+ },
1131
+ peerDataOperationRequestType:
1132
+ WAProto_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
1133
+ }
1134
+ return sendPeerDataOperationMessage(pdoMessage)
1135
+ }
1136
+ const requestPlaceholderResend = async (messageKey) => {
1137
+ var _a
1138
+ if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
1139
+ throw new boom_1.Boom('Not authenticated')
1140
+ }
1141
+ if (
1142
+ placeholderResendCache.get(
1143
+ messageKey === null || messageKey === void 0 ? void 0 : messageKey.id
1144
+ )
1145
+ ) {
1146
+ logger.debug({ messageKey }, 'already requested resend')
1147
+ return
1148
+ } else {
1149
+ placeholderResendCache.set(
1150
+ messageKey === null || messageKey === void 0 ? void 0 : messageKey.id,
1151
+ true
1152
+ )
1153
+ }
1154
+ await (0, Utils_1.delay)(5000)
1155
+ if (
1156
+ !placeholderResendCache.get(
1157
+ messageKey === null || messageKey === void 0 ? void 0 : messageKey.id
1158
+ )
1159
+ ) {
1160
+ logger.debug({ messageKey }, 'message received while resend requested')
1161
+ return 'RESOLVED'
1162
+ }
1163
+ const pdoMessage = {
1164
+ placeholderMessageResendRequest: [
1165
+ {
1166
+ messageKey
1167
+ }
1168
+ ],
1169
+ peerDataOperationRequestType:
1170
+ WAProto_1.proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
1171
+ }
1172
+ setTimeout(() => {
1173
+ if (
1174
+ placeholderResendCache.get(
1175
+ messageKey === null || messageKey === void 0 ? void 0 : messageKey.id
1176
+ )
1177
+ ) {
1178
+ logger.debug(
1179
+ { messageKey },
1180
+ 'PDO message without response after 15 seconds. Phone possibly offline'
1181
+ )
1182
+ placeholderResendCache.del(
1183
+ messageKey === null || messageKey === void 0 ? void 0 : messageKey.id
1184
+ )
1185
+ }
1186
+ }, 15000)
1187
+ return sendPeerDataOperationMessage(pdoMessage)
1188
+ }
1189
+ const handleCall = async (node) => {
1190
+ const { attrs } = node
1191
+ const [infoChild] = (0, WABinary_1.getAllBinaryNodeChildren)(node)
1192
+ const callId = infoChild.attrs['call-id']
1193
+ const from = infoChild.attrs.from || infoChild.attrs['call-creator']
1194
+ const status = (0, Utils_1.getCallStatusFromNode)(infoChild)
1195
+ const call = {
1196
+ chatId: attrs.from,
1197
+ from,
1198
+ id: callId,
1199
+ date: new Date(+attrs.t * 1000),
1200
+ offline: !!attrs.offline,
1201
+ status
1202
+ }
1203
+ if (status === 'offer') {
1204
+ call.isVideo = !!(0, WABinary_1.getBinaryNodeChild)(infoChild, 'video')
1205
+ call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid']
1206
+ call.groupJid = infoChild.attrs['group-jid']
1207
+ callOfferCache.set(call.id, call)
1208
+ }
1209
+ const existingCall = callOfferCache.get(call.id)
1210
+ // use existing call info to populate this event
1211
+ if (existingCall) {
1212
+ call.isVideo = existingCall.isVideo
1213
+ call.isGroup = existingCall.isGroup
1214
+ }
1215
+ // delete data once call has ended
1216
+ if (
1217
+ status === 'reject' ||
1218
+ status === 'accept' ||
1219
+ status === 'timeout' ||
1220
+ status === 'terminate'
1221
+ ) {
1222
+ callOfferCache.del(call.id)
1223
+ }
1224
+ ev.emit('call', [call])
1225
+ await sendMessageAck(node)
1226
+ }
1227
+ const handleBadAck = async ({ attrs }) => {
1228
+ const key = {
1229
+ remoteJid: attrs.from,
1230
+ fromMe: true,
1231
+ id: attrs.id,
1232
+ server_id: attrs === null || attrs === void 0 ? void 0 : attrs.server_id
1233
+ }
1234
+ // current hypothesis is that if pash is sent in the ack
1235
+ // it means -- the message hasn't reached all devices yet
1236
+ // we'll retry sending the message here
1237
+ if (attrs.phash) {
1238
+ logger.info({ attrs }, 'received phash in ack, resending message...')
1239
+ const cacheKey = `${key.remoteJid}:${key.id}`
1240
+ if ((msgRetryCache.get(cacheKey) || 0) >= maxMsgRetryCount) {
1241
+ logger.warn({ attrs }, 'reached max retry count, not sending message again')
1242
+ msgRetryCache.del(cacheKey)
1243
+ return
1244
+ }
1245
+ const retryCount = msgRetryCache.get(cacheKey) || 0
1246
+ const msg = await getMessage(key)
1247
+ if (msg) {
1248
+ await relayMessage(key.remoteJid, msg, {
1249
+ messageId: key.id,
1250
+ useUserDevicesCache: false
1251
+ })
1252
+ msgRetryCache.set(cacheKey, retryCount + 1)
1253
+ } else {
1254
+ logger.warn({ attrs }, 'could not send message again, as it was not found')
1255
+ }
1256
+ }
1257
+ // error in acknowledgement,
1258
+ // device could not display the message
1259
+ if (attrs.error) {
1260
+ logger.warn({ attrs }, 'received error in ack')
1261
+ ev.emit('messages.update', [
1262
+ {
1263
+ key,
1264
+ update: {
1265
+ status: Types_1.WAMessageStatus.ERROR,
1266
+ messageStubParameters: [attrs.error]
1267
+ }
1268
+ }
1269
+ ])
1270
+ }
1271
+ }
1272
+ /// processes a node with the given function
1273
+ /// and adds the task to the existing buffer if we're buffering events
1274
+ const processNodeWithBuffer = async (node, identifier, exec) => {
1275
+ ev.buffer()
1276
+ await execTask()
1277
+ ev.flush()
1278
+ function execTask() {
1279
+ return exec(node, false).catch((err) => onUnexpectedError(err, identifier))
1280
+ }
1281
+ }
1282
+ const makeOfflineNodeProcessor = () => {
1283
+ const nodeProcessorMap = new Map([
1284
+ ['message', handleMessage],
1285
+ ['call', handleCall],
1286
+ ['receipt', handleReceipt],
1287
+ ['notification', handleNotification]
1288
+ ])
1289
+ const nodes = []
1290
+ let isProcessing = false
1291
+ const enqueue = (type, node) => {
1292
+ nodes.push({ type, node })
1293
+ if (isProcessing) {
1294
+ return
1295
+ }
1296
+ isProcessing = true
1297
+ const promise = async () => {
1298
+ while (nodes.length && ws.isOpen) {
1299
+ const { type, node } = nodes.shift()
1300
+ const nodeProcessor = nodeProcessorMap.get(type)
1301
+ if (!nodeProcessor) {
1302
+ onUnexpectedError(
1303
+ new Error(`unknown offline node type: ${type}`),
1304
+ 'processing offline node'
1305
+ )
1306
+ continue
1307
+ }
1308
+ await nodeProcessor(node)
1309
+ }
1310
+ isProcessing = false
1311
+ }
1312
+ promise().catch((error) => onUnexpectedError(error, 'processing offline nodes'))
1313
+ }
1314
+ return { enqueue }
1315
+ }
1316
+ const offlineNodeProcessor = makeOfflineNodeProcessor()
1317
+ const processNode = (type, node, identifier, exec) => {
1318
+ const isOffline = !!node.attrs.offline
1319
+ if (isOffline) {
1320
+ offlineNodeProcessor.enqueue(type, node)
1321
+ } else {
1322
+ processNodeWithBuffer(node, identifier, exec)
1323
+ }
1324
+ }
1325
+ // recv a message
1326
+ ws.on('CB:message', (node) => {
1327
+ processNode('message', node, 'processing message', handleMessage)
1328
+ })
1329
+ ws.on('CB:call', async (node) => {
1330
+ processNode('call', node, 'handling call', handleCall)
1331
+ })
1332
+ ws.on('CB:receipt', (node) => {
1333
+ processNode('receipt', node, 'handling receipt', handleReceipt)
1334
+ })
1335
+ ws.on('CB:notification', async (node) => {
1336
+ processNode('notification', node, 'handling notification', handleNotification)
1337
+ })
1338
+ ws.on('CB:ack,class:message', (node) => {
1339
+ handleBadAck(node).catch((error) => onUnexpectedError(error, 'handling bad ack'))
1340
+ })
1341
+ ev.on('call', ([call]) => {
1342
+ // missed call + group call notification message generation
1343
+ if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
1344
+ const msg = {
1345
+ key: {
1346
+ remoteJid: call.chatId,
1347
+ id: call.id,
1348
+ fromMe: false
1349
+ },
1350
+ messageTimestamp: (0, Utils_1.unixTimestampSeconds)(call.date)
1351
+ }
1352
+ if (call.status === 'timeout') {
1353
+ if (call.isGroup) {
1354
+ msg.messageStubType = call.isVideo
1355
+ ? Types_1.WAMessageStubType.CALL_MISSED_GROUP_VIDEO
1356
+ : Types_1.WAMessageStubType.CALL_MISSED_GROUP_VOICE
1357
+ } else {
1358
+ msg.messageStubType = call.isVideo
1359
+ ? Types_1.WAMessageStubType.CALL_MISSED_VIDEO
1360
+ : Types_1.WAMessageStubType.CALL_MISSED_VOICE
1361
+ }
1362
+ } else {
1363
+ msg.message = { call: { callKey: Buffer.from(call.id) } }
1364
+ }
1365
+ const protoMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg)
1366
+ upsertMessage(protoMsg, call.offline ? 'append' : 'notify')
1367
+ }
1368
+ })
1369
+ ev.on('connection.update', ({ isOnline }) => {
1370
+ if (typeof isOnline !== 'undefined') {
1371
+ sendActiveReceipts = isOnline
1372
+ logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`)
1373
+ }
1374
+ })
1375
+ return {
1376
+ ...sock,
1377
+ sendMessageAck,
1378
+ sendRetryRequest,
1379
+ rejectCall,
1380
+ offerCall,
1381
+ fetchMessageHistory,
1382
+ requestPlaceholderResend
1383
+ }
1384
+ }
1385
+ exports.makeMessagesRecvSocket = makeMessagesRecvSocket