@neelegirl/baileys 1.4.3

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