@badzz88/baileys 8.4.7 → 8.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1055 -337
  3. package/WAProto/WAProto.proto +347 -6
  4. package/WAProto/fix-import.js +38 -0
  5. package/WAProto/fix-imports.js +86 -85
  6. package/WAProto/index.d.ts +4913 -25
  7. package/WAProto/index.js +14074 -98
  8. package/package.json +50 -90
  9. package/src/Defaults/index.js +201 -0
  10. package/src/Defaults/phonenumber-mcc.json +223 -0
  11. package/src/Signal/Group/ciphertext-message.js +15 -0
  12. package/src/Signal/Group/group-session-builder.js +92 -0
  13. package/src/Signal/Group/group_cipher.js +89 -0
  14. package/src/Signal/Group/index.js +136 -0
  15. package/src/Signal/Group/keyhelper.js +73 -0
  16. package/src/Signal/Group/sender-chain-key.js +32 -0
  17. package/src/Signal/Group/sender-key-distribution-message.js +66 -0
  18. package/src/Signal/Group/sender-key-message.js +69 -0
  19. package/src/Signal/Group/sender-key-name.js +50 -0
  20. package/src/Signal/Group/sender-key-record.js +44 -0
  21. package/src/Signal/Group/sender-key-state.js +97 -0
  22. package/src/Signal/Group/sender-message-key.js +30 -0
  23. package/src/Signal/libsignal.js +470 -0
  24. package/src/Signal/lid-mapping.js +262 -0
  25. package/src/Socket/Client/index.js +30 -0
  26. package/src/Socket/Client/types.js +13 -0
  27. package/src/Socket/Client/websocket.js +62 -0
  28. package/src/Socket/aigroups.js +240 -0
  29. package/src/Socket/business.js +422 -0
  30. package/src/Socket/chats.js +2374 -0
  31. package/src/Socket/communities.js +580 -0
  32. package/src/Socket/graphql.js +915 -0
  33. package/src/Socket/groups.js +812 -0
  34. package/src/Socket/index.js +37 -0
  35. package/src/Socket/interactive-handler.js +579 -0
  36. package/src/Socket/interop.js +566 -0
  37. package/src/Socket/managed-account.js +214 -0
  38. package/src/Socket/messages-recv.js +3012 -0
  39. package/src/Socket/messages-send.js +2163 -0
  40. package/{lib → src}/Socket/mex.js +11 -5
  41. package/src/Socket/newsletter.js +1057 -0
  42. package/src/Socket/privacy.js +452 -0
  43. package/src/Socket/registration.js +434 -0
  44. package/src/Socket/socket.js +1079 -0
  45. package/src/Socket/text-router.js +67 -0
  46. package/src/Socket/username.js +234 -0
  47. package/src/Store/index.js +36 -0
  48. package/src/Store/make-cache-manager-store.js +90 -0
  49. package/src/Store/make-in-memory-store.js +506 -0
  50. package/src/Store/make-ordered-dictionary.js +81 -0
  51. package/src/Store/object-repository.js +29 -0
  52. package/src/Types/Auth.js +38 -0
  53. package/src/Types/Bussines.js +2 -0
  54. package/src/Types/Call.js +2 -0
  55. package/src/Types/Chat.js +4 -0
  56. package/src/Types/Contact.js +2 -0
  57. package/src/Types/Events.js +2 -0
  58. package/src/Types/GroupMetadata.js +2 -0
  59. package/src/Types/Label.js +27 -0
  60. package/src/Types/LabelAssociation.js +9 -0
  61. package/src/Types/Message.js +95 -0
  62. package/src/Types/Newsletter.js +152 -0
  63. package/src/Types/Product.js +2 -0
  64. package/src/Types/Signal.js +2 -0
  65. package/src/Types/Socket.js +2 -0
  66. package/src/Types/State.js +70 -0
  67. package/src/Types/USync.js +2 -0
  68. package/src/Types/index.js +54 -0
  69. package/src/Utils/auth-utils.js +306 -0
  70. package/src/Utils/browser-utils.js +114 -0
  71. package/src/Utils/business.js +247 -0
  72. package/src/Utils/chat-utils.js +1272 -0
  73. package/src/Utils/consumer-application.js +107 -0
  74. package/src/Utils/crypto.js +125 -0
  75. package/src/Utils/decode-wa-message.js +808 -0
  76. package/src/Utils/event-buffer.js +586 -0
  77. package/src/Utils/generics.js +640 -0
  78. package/src/Utils/group-history.js +60 -0
  79. package/src/Utils/history.js +244 -0
  80. package/src/Utils/identity-change-handler.js +52 -0
  81. package/src/Utils/index.js +53 -0
  82. package/src/Utils/jid-display-normalization.js +218 -0
  83. package/src/Utils/link-preview.js +143 -0
  84. package/src/Utils/logger.js +9 -0
  85. package/src/Utils/lt-hash.js +10 -0
  86. package/src/Utils/make-mutex.js +36 -0
  87. package/src/Utils/message-composer.js +479 -0
  88. package/src/Utils/message-inspect.js +400 -0
  89. package/src/Utils/message-retry-manager.js +231 -0
  90. package/src/Utils/messages-media.js +943 -0
  91. package/src/Utils/messages.js +2490 -0
  92. package/src/Utils/meta-ai-msmsg.js +133 -0
  93. package/src/Utils/noise-handler.js +194 -0
  94. package/src/Utils/offline-node-processor.js +42 -0
  95. package/src/Utils/pre-key-manager.js +107 -0
  96. package/src/Utils/process-message.js +1047 -0
  97. package/src/Utils/reporting-utils.js +262 -0
  98. package/src/Utils/signal.js +192 -0
  99. package/src/Utils/stanza-ack.js +74 -0
  100. package/src/Utils/sync-action-utils.js +54 -0
  101. package/src/Utils/tc-token-utils.js +161 -0
  102. package/src/Utils/use-multi-file-auth-state.js +121 -0
  103. package/src/Utils/validate-connection.js +248 -0
  104. package/src/Utils/voip-rekey.js +22 -0
  105. package/src/WABinary/constants.js +1304 -0
  106. package/src/WABinary/decode.js +377 -0
  107. package/src/WABinary/encode.js +58 -0
  108. package/src/WABinary/generic-utils.js +148 -0
  109. package/src/WABinary/index.js +33 -0
  110. package/src/WABinary/jid-utils.js +374 -0
  111. package/src/WABinary/types.js +2 -0
  112. package/src/WAM/BinaryInfo.js +13 -0
  113. package/src/WAM/constants.js +39486 -0
  114. package/src/WAM/encode.js +142 -0
  115. package/src/WAM/index.js +31 -0
  116. package/src/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
  117. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +100 -0
  118. package/src/WAUSync/Protocols/USyncContactProtocol.js +60 -0
  119. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +65 -0
  120. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -0
  121. package/src/WAUSync/Protocols/USyncFeatureProtocol.js +74 -0
  122. package/src/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
  123. package/src/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  124. package/src/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  125. package/src/WAUSync/Protocols/USyncStatusProtocol.js +44 -0
  126. package/src/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  127. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +28 -0
  128. package/src/WAUSync/Protocols/index.js +40 -0
  129. package/src/WAUSync/USyncBackoff.js +31 -0
  130. package/src/WAUSync/USyncQuery.js +204 -0
  131. package/src/WAUSync/USyncUser.js +58 -0
  132. package/src/WAUSync/index.js +32 -0
  133. package/src/antiban.js +4726 -0
  134. package/{lib → src}/index.js +48 -15
  135. package/lib/Defaults/index.js +0 -130
  136. package/lib/Signal/Group/ciphertext-message.js +0 -12
  137. package/lib/Signal/Group/group-session-builder.js +0 -30
  138. package/lib/Signal/Group/group_cipher.js +0 -82
  139. package/lib/Signal/Group/index.js +0 -12
  140. package/lib/Signal/Group/keyhelper.js +0 -18
  141. package/lib/Signal/Group/sender-chain-key.js +0 -26
  142. package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
  143. package/lib/Signal/Group/sender-key-message.js +0 -66
  144. package/lib/Signal/Group/sender-key-name.js +0 -48
  145. package/lib/Signal/Group/sender-key-record.js +0 -41
  146. package/lib/Signal/Group/sender-key-state.js +0 -84
  147. package/lib/Signal/Group/sender-message-key.js +0 -26
  148. package/lib/Signal/libsignal.js +0 -431
  149. package/lib/Signal/lid-mapping.js +0 -277
  150. package/lib/Socket/Client/index.js +0 -3
  151. package/lib/Socket/Client/types.js +0 -11
  152. package/lib/Socket/Client/websocket.js +0 -54
  153. package/lib/Socket/business.js +0 -379
  154. package/lib/Socket/chats.js +0 -1193
  155. package/lib/Socket/communities.js +0 -431
  156. package/lib/Socket/groups.js +0 -374
  157. package/lib/Socket/index.js +0 -12
  158. package/lib/Socket/luxu.js +0 -386
  159. package/lib/Socket/messages-recv.js +0 -1916
  160. package/lib/Socket/messages-send.js +0 -1453
  161. package/lib/Socket/newsletter.js +0 -251
  162. package/lib/Socket/socket.js +0 -980
  163. package/lib/Socket/username.js +0 -146
  164. package/lib/Store/index.js +0 -10
  165. package/lib/Store/keyed-db.js +0 -108
  166. package/lib/Store/make-cache-manager-store.js +0 -85
  167. package/lib/Store/make-in-memory-store.js +0 -198
  168. package/lib/Store/make-ordered-dictionary.js +0 -75
  169. package/lib/Store/object-repository.js +0 -32
  170. package/lib/Types/Auth.js +0 -2
  171. package/lib/Types/Bussines.js +0 -2
  172. package/lib/Types/Call.js +0 -2
  173. package/lib/Types/Chat.js +0 -8
  174. package/lib/Types/Contact.js +0 -2
  175. package/lib/Types/Events.js +0 -2
  176. package/lib/Types/GroupMetadata.js +0 -2
  177. package/lib/Types/Label.js +0 -25
  178. package/lib/Types/LabelAssociation.js +0 -7
  179. package/lib/Types/Message.js +0 -11
  180. package/lib/Types/Mex.js +0 -37
  181. package/lib/Types/Product.js +0 -2
  182. package/lib/Types/Signal.js +0 -2
  183. package/lib/Types/Socket.js +0 -3
  184. package/lib/Types/State.js +0 -56
  185. package/lib/Types/USync.js +0 -2
  186. package/lib/Types/index.js +0 -26
  187. package/lib/Utils/auth-utils.js +0 -302
  188. package/lib/Utils/browser-utils.js +0 -49
  189. package/lib/Utils/business.js +0 -231
  190. package/lib/Utils/chat-utils.js +0 -872
  191. package/lib/Utils/companion-reg-client-utils.js +0 -35
  192. package/lib/Utils/crypto.js +0 -118
  193. package/lib/Utils/decode-wa-message.js +0 -350
  194. package/lib/Utils/event-buffer.js +0 -622
  195. package/lib/Utils/generics.js +0 -403
  196. package/lib/Utils/history.js +0 -134
  197. package/lib/Utils/identity-change-handler.js +0 -50
  198. package/lib/Utils/index.js +0 -23
  199. package/lib/Utils/link-preview.js +0 -85
  200. package/lib/Utils/logger.js +0 -3
  201. package/lib/Utils/lt-hash.js +0 -8
  202. package/lib/Utils/make-mutex.js +0 -33
  203. package/lib/Utils/message-composer.js +0 -273
  204. package/lib/Utils/message-retry-manager.js +0 -265
  205. package/lib/Utils/messages-media.js +0 -788
  206. package/lib/Utils/messages.js +0 -1260
  207. package/lib/Utils/noise-handler.js +0 -201
  208. package/lib/Utils/offline-node-processor.js +0 -40
  209. package/lib/Utils/pre-key-manager.js +0 -106
  210. package/lib/Utils/process-message.js +0 -630
  211. package/lib/Utils/reporting-utils.js +0 -258
  212. package/lib/Utils/signal.js +0 -201
  213. package/lib/Utils/stanza-ack.js +0 -38
  214. package/lib/Utils/sync-action-utils.js +0 -49
  215. package/lib/Utils/tc-token-utils.js +0 -163
  216. package/lib/Utils/use-multi-file-auth-state.js +0 -121
  217. package/lib/Utils/validate-connection.js +0 -203
  218. package/lib/WABinary/constants.js +0 -1301
  219. package/lib/WABinary/decode.js +0 -262
  220. package/lib/WABinary/encode.js +0 -220
  221. package/lib/WABinary/generic-utils.js +0 -204
  222. package/lib/WABinary/index.js +0 -6
  223. package/lib/WABinary/jid-utils.js +0 -98
  224. package/lib/WABinary/types.js +0 -2
  225. package/lib/WAM/BinaryInfo.js +0 -10
  226. package/lib/WAM/constants.js +0 -22853
  227. package/lib/WAM/encode.js +0 -150
  228. package/lib/WAM/index.js +0 -4
  229. package/lib/WAUSync/Protocols/USyncContactProtocol.js +0 -52
  230. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +0 -54
  231. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +0 -27
  232. package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -263
  233. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +0 -38
  234. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -25
  235. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -51
  236. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -29
  237. package/lib/WAUSync/Protocols/index.js +0 -6
  238. package/lib/WAUSync/USyncQuery.js +0 -98
  239. package/lib/WAUSync/USyncUser.js +0 -31
  240. package/lib/WAUSync/index.js +0 -4
@@ -0,0 +1,400 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+
4
+ const boom_1 = require('@hapi/boom')
5
+ const { proto } = require('../../WAProto/index.js')
6
+ const WABinary_1 = require('../WABinary')
7
+ const crypto_2 = require('./crypto')
8
+ const generics_1 = require('./generics')
9
+ const messages_media_1 = require('./messages-media')
10
+
11
+ /** Get the key to access the true type of content */
12
+ const getContentType = content => {
13
+ if (content) {
14
+ const keys = Object.keys(content)
15
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage')
16
+ return key
17
+ }
18
+ }
19
+ exports.getContentType = getContentType
20
+
21
+ /**
22
+ * Normalizes ephemeral, view once messages to regular message content
23
+ * Eg. image messages in ephemeral messages, in view once messages etc.
24
+ */
25
+ const normalizeMessageContent = content => {
26
+ if (!content) {
27
+ return undefined
28
+ }
29
+ // set max iterations to prevent an infinite loop
30
+ for (let i = 0; i < 5; i++) {
31
+ const inner = getFutureProofMessage(content)
32
+ if (!inner) {
33
+ break
34
+ }
35
+ content = inner.message
36
+ }
37
+ return content
38
+ function getFutureProofMessage(message) {
39
+ return (
40
+ message.ephemeralMessage ||
41
+ message.viewOnceMessage ||
42
+ message.documentWithCaptionMessage ||
43
+ message.viewOnceMessageV2 ||
44
+ message.viewOnceMessageV2Extension ||
45
+ message.editedMessage ||
46
+ message.groupMentionedMessage ||
47
+ message.botInvokeMessage ||
48
+ message.lottieStickerMessage ||
49
+ message.eventCoverImage ||
50
+ message.statusMentionMessage ||
51
+ message.pollCreationOptionImageMessage ||
52
+ message.associatedChildMessage ||
53
+ message.groupStatusMentionMessage ||
54
+ message.pollCreationMessageV4 ||
55
+ message.pollCreationMessageV5 ||
56
+ message.statusAddYours ||
57
+ message.groupStatusMessage ||
58
+ message.limitSharingMessage ||
59
+ message.botTaskMessage ||
60
+ message.questionMessage ||
61
+ message.groupStatusMessageV2 ||
62
+ message.botForwardedMessage ||
63
+ message.questionReplyMessage ||
64
+ message.newsletterAdminProfileMessage ||
65
+ message.newsletterAdminProfileMessageV2 ||
66
+ message.newsletterAdminProfileStatusMessage ||
67
+ message.spoilerMessage ||
68
+ message.groupStatusV3Message ||
69
+ message.pollCreationMessageV6
70
+ )
71
+ }
72
+ }
73
+ exports.normalizeMessageContent = normalizeMessageContent
74
+
75
+ /**
76
+ * Extract the true message content from a message
77
+ * Eg. extracts the inner message from a disappearing message/view once message
78
+ */
79
+ const extractMessageContent = content => {
80
+ const extractFromTemplateMessage = msg => {
81
+ if (msg.imageMessage) {
82
+ return { imageMessage: msg.imageMessage }
83
+ } else if (msg.documentMessage) {
84
+ return { documentMessage: msg.documentMessage }
85
+ } else if (msg.videoMessage) {
86
+ return { videoMessage: msg.videoMessage }
87
+ } else if (msg.locationMessage) {
88
+ return { locationMessage: msg.locationMessage }
89
+ } else {
90
+ return {
91
+ conversation:
92
+ 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
93
+ }
94
+ }
95
+ }
96
+ content = normalizeMessageContent(content)
97
+ if (content?.buttonsMessage) {
98
+ return extractFromTemplateMessage(content.buttonsMessage)
99
+ }
100
+ if (content?.templateMessage?.hydratedFourRowTemplate) {
101
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate)
102
+ }
103
+ if (content?.templateMessage?.hydratedTemplate) {
104
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate)
105
+ }
106
+ if (content?.templateMessage?.fourRowTemplate) {
107
+ return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate)
108
+ }
109
+ return content
110
+ }
111
+ exports.extractMessageContent = extractMessageContent
112
+
113
+ /** Returns the device predicted by message ID */
114
+ const getDevice = id =>
115
+ /^3A.{18}$/.test(id)
116
+ ? 'ios'
117
+ : /^3E.{20}$/.test(id)
118
+ ? 'web'
119
+ : /^(.{21}|.{32})$/.test(id)
120
+ ? 'android'
121
+ : /^(3F|.{18}$)/.test(id)
122
+ ? 'desktop'
123
+ : 'api/baileys'
124
+ exports.getDevice = getDevice
125
+
126
+ /** Upserts a receipt in the message */
127
+ const updateMessageWithReceipt = (msg, receipt) => {
128
+ msg.userReceipt = msg.userReceipt || []
129
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
130
+ if (recp) {
131
+ Object.assign(recp, receipt)
132
+ } else {
133
+ msg.userReceipt.push(receipt)
134
+ }
135
+ }
136
+ exports.updateMessageWithReceipt = updateMessageWithReceipt
137
+
138
+ /** Update the message with a new reaction */
139
+ const updateMessageWithReaction = (msg, reaction) => {
140
+ const authorID = (0, generics_1.getKeyAuthor)(reaction.key)
141
+ const reactions = (msg.reactions || []).filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID)
142
+ reaction.text = reaction.text || ''
143
+ reactions.push(reaction)
144
+ msg.reactions = reactions
145
+ }
146
+ exports.updateMessageWithReaction = updateMessageWithReaction
147
+
148
+ /** Update the message with a new poll update */
149
+ const updateMessageWithPollUpdate = (msg, update) => {
150
+ const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey)
151
+ const reactions = (msg.pollUpdates || []).filter(
152
+ r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID
153
+ )
154
+ if (update.vote?.selectedOptions?.length) {
155
+ reactions.push(update)
156
+ }
157
+ msg.pollUpdates = reactions
158
+ }
159
+ exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate
160
+
161
+ /** Update the message with a new event response */
162
+ const updateMessageWithEventResponse = (msg, update) => {
163
+ const authorID = (0, generics_1.getKeyAuthor)(update.eventResponseMessageKey)
164
+ const responses = (msg.eventResponses || []).filter(
165
+ r => (0, generics_1.getKeyAuthor)(r.eventResponseMessageKey) !== authorID
166
+ )
167
+ responses.push(update)
168
+ msg.eventResponses = responses
169
+ }
170
+ exports.updateMessageWithEventResponse = updateMessageWithEventResponse
171
+
172
+ /**
173
+ * Aggregates all poll updates in a poll.
174
+ * @param msg the poll creation message
175
+ * @param meId your jid
176
+ * @returns A list of options & their voters
177
+ */
178
+ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
179
+ const opts =
180
+ message?.pollCreationMessage?.options ||
181
+ message?.pollCreationMessageV2?.options ||
182
+ message?.pollCreationMessageV3?.options ||
183
+ message?.pollCreationMessageV5?.options ||
184
+ message?.pollCreationMessageV6?.options ||
185
+ message?.pollCreationMessageV4?.message?.pollCreationMessage?.options ||
186
+ message?.pollCreationMessageV4?.message?.pollCreationMessageV3?.options ||
187
+ []
188
+ const voteHashMap = opts.reduce((acc, opt) => {
189
+ const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString()
190
+ acc[hash] = {
191
+ name: opt.optionName || '',
192
+ voters: []
193
+ }
194
+ return acc
195
+ }, {})
196
+ for (const update of pollUpdates || []) {
197
+ const { vote } = update
198
+ if (!vote) {
199
+ continue
200
+ }
201
+ for (const option of vote.selectedOptions || []) {
202
+ const hash = option.toString()
203
+ let data = voteHashMap[hash]
204
+ if (!data) {
205
+ voteHashMap[hash] = {
206
+ name: 'Unknown',
207
+ voters: []
208
+ }
209
+ data = voteHashMap[hash]
210
+ }
211
+ voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId))
212
+ }
213
+ }
214
+ return Object.values(voteHashMap)
215
+ }
216
+ exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage
217
+
218
+ /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
219
+ const aggregateMessageKeysNotFromMe = keys => {
220
+ const keyMap = {}
221
+ for (const { remoteJid, id, participant, fromMe, sts } of keys) {
222
+ if (!fromMe) {
223
+ const uqKey = `${remoteJid}:${participant || ''}`
224
+ if (!keyMap[uqKey]) {
225
+ keyMap[uqKey] = {
226
+ jid: remoteJid,
227
+ participant: participant,
228
+ messageIds: [],
229
+ // sts is per-message; kept only for single-message interop receipts
230
+ sts: sts
231
+ }
232
+ } else if (keyMap[uqKey].sts !== sts) {
233
+ // Multiple messages with different sts — can't aggregate a single sts
234
+ keyMap[uqKey].sts = undefined
235
+ }
236
+ keyMap[uqKey].messageIds.push(id)
237
+ }
238
+ }
239
+ return Object.values(keyMap)
240
+ }
241
+ exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe
242
+
243
+ /** Check if a WebMessageInfo has a scheduled reveal time (ConditionalRevealMessage) */
244
+ const isScheduledMessage = msg => !!msg?.scheduledMessageMetadata?.scheduledTime
245
+ exports.isScheduledMessage = isScheduledMessage
246
+
247
+ /** Get scheduled reveal time of a message as a Date, or null */
248
+ const getScheduledMessageTime = msg => {
249
+ const t = msg?.scheduledMessageMetadata?.scheduledTime
250
+ if (!t) return null
251
+ return new Date(Number(t) * 1000)
252
+ }
253
+ exports.getScheduledMessageTime = getScheduledMessageTime
254
+
255
+ /** Extract PaymentInfo from a WebMessageInfo (the payment status field, not the message content) */
256
+ const getMessagePaymentInfo = msg => msg?.paymentInfo || msg?.quotedPaymentInfo || null
257
+ exports.getMessagePaymentInfo = getMessagePaymentInfo
258
+
259
+ /** Get all comment metadata from a WebMessageInfo */
260
+ const getMessageCommentMetadata = msg => msg?.commentMetadata || null
261
+ exports.getMessageCommentMetadata = getMessageCommentMetadata
262
+
263
+ /** Get all message add-ons (reactions, poll updates, pins) from a WebMessageInfo */
264
+ const getMessageAddOns = msg => msg?.messageAddOns || []
265
+ exports.getMessageAddOns = getMessageAddOns
266
+
267
+ /** Get the quiz correct answer from a poll creation message, if it's a quiz */
268
+ const getPollCorrectAnswer = pollMsg => {
269
+ const poll =
270
+ pollMsg?.pollCreationMessage ||
271
+ pollMsg?.pollCreationMessageV2 ||
272
+ pollMsg?.pollCreationMessageV3 ||
273
+ pollMsg?.pollCreationMessageV5 ||
274
+ pollMsg?.pollCreationMessageV6
275
+ if (!poll) return null
276
+ const isQuiz = poll.pollType === proto.Message.PollType?.QUIZ || poll.pollType === 1
277
+ return isQuiz ? poll.correctAnswer?.optionName || null : null
278
+ }
279
+ exports.getPollCorrectAnswer = getPollCorrectAnswer
280
+
281
+ /**
282
+ * Aggregates all event responses in an event message.
283
+ * @param msg the event creation message
284
+ * @param meLid your lid
285
+ * @returns A list of response types & their responders
286
+ */
287
+ function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
288
+ const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE']
289
+ const responseMap = {}
290
+
291
+ for (const type of responseTypes) {
292
+ responseMap[type] = {
293
+ response: type,
294
+ responders: []
295
+ }
296
+ }
297
+ for (const update of eventResponses) {
298
+ const { response } = update.response || {}
299
+ const responseType = proto.Message.EventResponseMessage.EventResponseType[response]
300
+ if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
301
+ responseMap[responseType].responders.push(generics_1.getKeyAuthor(update.eventResponseMessageKey, meLid))
302
+ }
303
+ }
304
+
305
+ return Object.values(responseMap)
306
+ }
307
+ exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
308
+
309
+ const REUPLOAD_REQUIRED_STATUS = [410, 404]
310
+
311
+ /** Downloads the given message. Throws an error if it's not a media message */
312
+ const downloadMediaMessage = async (message, type, options, ctx) => {
313
+ const result = await downloadMsg().catch(async error => {
314
+ if (ctx && typeof error?.status === 'number' && REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
315
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...')
316
+ message = await ctx.reuploadRequest(message)
317
+ const result = await downloadMsg()
318
+ return result
319
+ }
320
+ throw error
321
+ })
322
+ return result
323
+ async function downloadMsg() {
324
+ const mContent = extractMessageContent(message.message)
325
+ if (!mContent) {
326
+ throw new boom_1.Boom('No message present', { statusCode: 400, data: message })
327
+ }
328
+ const contentType = getContentType(mContent)
329
+ let mediaType = contentType?.replace('Message', '')
330
+ const media = mContent[contentType]
331
+ if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
332
+ throw new boom_1.Boom(`"${contentType}" message is not a media message`)
333
+ }
334
+ let download
335
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
336
+ download = {
337
+ directPath: media.thumbnailDirectPath,
338
+ mediaKey: media.mediaKey
339
+ }
340
+ mediaType = 'thumbnail-link'
341
+ } else {
342
+ download = media
343
+ }
344
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options)
345
+ if (type === 'buffer') {
346
+ const bufferArray = []
347
+ for await (const chunk of stream) {
348
+ bufferArray.push(chunk)
349
+ }
350
+ return Buffer.concat(bufferArray)
351
+ }
352
+ return stream
353
+ }
354
+ }
355
+ exports.downloadMediaMessage = downloadMediaMessage
356
+
357
+ /** Checks whether the given message is a media message; if it is returns the inner content */
358
+ const assertMediaContent = content => {
359
+ content = extractMessageContent(content)
360
+ const mediaContent =
361
+ content?.documentMessage ||
362
+ content?.imageMessage ||
363
+ content?.videoMessage ||
364
+ content?.audioMessage ||
365
+ content?.stickerMessage
366
+ if (!mediaContent) {
367
+ throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content })
368
+ }
369
+ return mediaContent
370
+ }
371
+ exports.assertMediaContent = assertMediaContent
372
+
373
+ /** Normalizes a bare user id to @s.whatsapp.net. Does not convert LID↔PN; use lidMapping / PN in key.remoteJidAlt when needed. */
374
+ const toJid = id => {
375
+ if (!id) return ''
376
+ if (id.includes('@')) return id
377
+ return `${id}@s.whatsapp.net`
378
+ }
379
+ exports.toJid = toJid
380
+
381
+ /** Returns the peer LID JID when the key is LID-primary (decode sets remoteJid/participant to @lid when WA sends LID). */
382
+ const getSenderLid = message => {
383
+ const k = message.key
384
+ if (!k) {
385
+ return { jid: '', lid: '' }
386
+ }
387
+ const jid = k.participant || k.remoteJid || ''
388
+ if (jid.endsWith('@lid') || jid.endsWith('@hosted.lid')) {
389
+ return { jid, lid: jid }
390
+ }
391
+ if (k.lid && typeof k.lid === 'string') {
392
+ const lid = k.lid.includes('@') ? k.lid : (0, WABinary_1.jidEncode)(k.lid, 'lid')
393
+ return { jid, lid }
394
+ }
395
+ if (k.participantLid && (0, WABinary_1.isLidUser)(k.participantLid)) {
396
+ return { jid, lid: k.participantLid }
397
+ }
398
+ return { jid, lid: '' }
399
+ }
400
+ exports.getSenderLid = getSenderLid
@@ -0,0 +1,231 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.MessageRetryManager = exports.RetryReason = void 0
4
+ const lru_cache_1 = require('lru-cache')
5
+ /** Number of sent messages to cache in memory for handling retry receipts */
6
+ const RECENT_MESSAGES_SIZE = 512
7
+ const MESSAGE_KEY_SEPARATOR = '\u0000'
8
+ /** Timeout for session recreation - 1 hour */
9
+ const RECREATE_SESSION_TIMEOUT = 60 * 60 * 1000 // 1 hour in milliseconds
10
+ const PHONE_REQUEST_DELAY = 3000
11
+ // Retry reason codes matching WhatsApp Web's Signal error codes.
12
+ var RetryReason
13
+ ;(function (RetryReason) {
14
+ RetryReason[(RetryReason['UnknownError'] = 0)] = 'UnknownError'
15
+ RetryReason[(RetryReason['SignalErrorNoSession'] = 1)] = 'SignalErrorNoSession'
16
+ RetryReason[(RetryReason['SignalErrorInvalidKey'] = 2)] = 'SignalErrorInvalidKey'
17
+ RetryReason[(RetryReason['SignalErrorInvalidKeyId'] = 3)] = 'SignalErrorInvalidKeyId'
18
+ /** MAC verification failed - most common cause of decryption failures */
19
+ RetryReason[(RetryReason['SignalErrorInvalidMessage'] = 4)] = 'SignalErrorInvalidMessage'
20
+ RetryReason[(RetryReason['SignalErrorInvalidSignature'] = 5)] = 'SignalErrorInvalidSignature'
21
+ RetryReason[(RetryReason['SignalErrorFutureMessage'] = 6)] = 'SignalErrorFutureMessage'
22
+ /** Explicit MAC failure - session is definitely out of sync */
23
+ RetryReason[(RetryReason['SignalErrorBadMac'] = 7)] = 'SignalErrorBadMac'
24
+ RetryReason[(RetryReason['SignalErrorInvalidSession'] = 8)] = 'SignalErrorInvalidSession'
25
+ RetryReason[(RetryReason['SignalErrorInvalidMsgKey'] = 9)] = 'SignalErrorInvalidMsgKey'
26
+ RetryReason[(RetryReason['BadBroadcastEphemeralSetting'] = 10)] = 'BadBroadcastEphemeralSetting'
27
+ RetryReason[(RetryReason['UnknownCompanionNoPrekey'] = 11)] = 'UnknownCompanionNoPrekey'
28
+ RetryReason[(RetryReason['AdvFailure'] = 12)] = 'AdvFailure'
29
+ RetryReason[(RetryReason['StatusRevokeDelay'] = 13)] = 'StatusRevokeDelay'
30
+ })(RetryReason || (exports.RetryReason = RetryReason = {}))
31
+ /** Error codes that indicate a MAC failure and require immediate session recreation */
32
+ const MAC_ERROR_CODES = new Set([RetryReason.SignalErrorInvalidMessage, RetryReason.SignalErrorBadMac])
33
+ class MessageRetryManager {
34
+ constructor(logger, maxMsgRetryCount) {
35
+ this.logger = logger
36
+ this.recentMessagesMap = new lru_cache_1.LRUCache({
37
+ max: RECENT_MESSAGES_SIZE,
38
+ ttl: 5 * 60 * 1000,
39
+ ttlAutopurge: true,
40
+ dispose: (_value, key) => {
41
+ const separatorIndex = key.lastIndexOf(MESSAGE_KEY_SEPARATOR)
42
+ if (separatorIndex > -1) {
43
+ const messageId = key.slice(separatorIndex + MESSAGE_KEY_SEPARATOR.length)
44
+ this.messageKeyIndex.delete(messageId)
45
+ }
46
+ }
47
+ })
48
+ this.messageKeyIndex = new Map()
49
+ this.sessionRecreateHistory = new lru_cache_1.LRUCache({
50
+ ttl: RECREATE_SESSION_TIMEOUT * 2,
51
+ ttlAutopurge: true
52
+ })
53
+ this.retryCounters = new lru_cache_1.LRUCache({
54
+ ttl: 15 * 60 * 1000,
55
+ ttlAutopurge: true,
56
+ updateAgeOnGet: true
57
+ }) // 15 minutes TTL
58
+ this.pendingPhoneRequests = {}
59
+ this.maxMsgRetryCount = 5
60
+ this.statistics = {
61
+ totalRetries: 0,
62
+ successfulRetries: 0,
63
+ failedRetries: 0,
64
+ mediaRetries: 0,
65
+ sessionRecreations: 0,
66
+ phoneRequests: 0
67
+ }
68
+ this.maxMsgRetryCount = maxMsgRetryCount
69
+ }
70
+ /**
71
+ * Add a recent message to the cache for retry handling
72
+ */
73
+ addRecentMessage(to, id, message) {
74
+ const key = { to, id }
75
+ const keyStr = this.keyToString(key)
76
+ // Add new message
77
+ this.recentMessagesMap.set(keyStr, {
78
+ message,
79
+ timestamp: Date.now()
80
+ })
81
+ this.messageKeyIndex.set(id, keyStr)
82
+ this.logger.debug(`Added message to retry cache: ${to}/${id}`)
83
+ }
84
+ /**
85
+ * Get a recent message from the cache
86
+ */
87
+ getRecentMessage(to, id) {
88
+ const key = { to, id }
89
+ const keyStr = this.keyToString(key)
90
+ return this.recentMessagesMap.get(keyStr)
91
+ }
92
+ /**
93
+ * Check if a session should be recreated based on retry count, history, and error code.
94
+ * MAC errors (codes 4 and 7) trigger immediate session recreation regardless of timeout.
95
+ */
96
+ shouldRecreateSession(jid, hasSession, errorCode) {
97
+ // If we don't have a session, always recreate
98
+ if (!hasSession) {
99
+ this.sessionRecreateHistory.set(jid, Date.now())
100
+ this.statistics.sessionRecreations++
101
+ return {
102
+ reason: "we don't have a Signal session with them",
103
+ recreate: true
104
+ }
105
+ }
106
+ // IMMEDIATE recreation for MAC errors - session is definitely out of sync
107
+ if (errorCode !== undefined && MAC_ERROR_CODES.has(errorCode)) {
108
+ this.sessionRecreateHistory.set(jid, Date.now())
109
+ this.statistics.sessionRecreations++
110
+ this.logger.warn(
111
+ { jid, errorCode: RetryReason[errorCode] },
112
+ 'MAC error detected, forcing immediate session recreation'
113
+ )
114
+ return {
115
+ reason: `MAC error (code ${errorCode}: ${RetryReason[errorCode]}), immediate session recreation`,
116
+ recreate: true
117
+ }
118
+ }
119
+ const now = Date.now()
120
+ const prevTime = this.sessionRecreateHistory.get(jid)
121
+ // If no previous recreation or it's been more than an hour
122
+ if (!prevTime || now - prevTime > RECREATE_SESSION_TIMEOUT) {
123
+ this.sessionRecreateHistory.set(jid, now)
124
+ this.statistics.sessionRecreations++
125
+ return {
126
+ reason: 'retry count > 1 and over an hour since last recreation',
127
+ recreate: true
128
+ }
129
+ }
130
+ return { reason: '', recreate: false }
131
+ }
132
+ /**
133
+ * Parse error code from retry receipt's retry node.
134
+ * Returns undefined if no error code is present.
135
+ */
136
+ parseRetryErrorCode(errorAttr) {
137
+ if (errorAttr === undefined || errorAttr === '') {
138
+ return undefined
139
+ }
140
+ const code = parseInt(errorAttr, 10)
141
+ if (Number.isNaN(code)) {
142
+ return undefined
143
+ }
144
+ // Validate it's a known RetryReason
145
+ if (code >= RetryReason.UnknownError && code <= RetryReason.StatusRevokeDelay) {
146
+ return code
147
+ }
148
+ return RetryReason.UnknownError
149
+ }
150
+ /**
151
+ * Check if an error code indicates a MAC failure
152
+ */
153
+ isMacError(errorCode) {
154
+ return errorCode !== undefined && MAC_ERROR_CODES.has(errorCode)
155
+ }
156
+ /**
157
+ * Increment retry counter for a message
158
+ */
159
+ incrementRetryCount(messageId) {
160
+ this.retryCounters.set(messageId, (this.retryCounters.get(messageId) || 0) + 1)
161
+ this.statistics.totalRetries++
162
+ return this.retryCounters.get(messageId)
163
+ }
164
+ /**
165
+ * Get retry count for a message
166
+ */
167
+ getRetryCount(messageId) {
168
+ return this.retryCounters.get(messageId) || 0
169
+ }
170
+ /**
171
+ * Check if message has exceeded maximum retry attempts
172
+ */
173
+ hasExceededMaxRetries(messageId) {
174
+ return this.getRetryCount(messageId) >= this.maxMsgRetryCount
175
+ }
176
+ /**
177
+ * Mark retry as successful
178
+ */
179
+ markRetrySuccess(messageId) {
180
+ this.statistics.successfulRetries++
181
+ // Clean up retry counter for successful message
182
+ this.retryCounters.delete(messageId)
183
+ this.cancelPendingPhoneRequest(messageId)
184
+ this.removeRecentMessage(messageId)
185
+ }
186
+ /**
187
+ * Mark retry as failed
188
+ */
189
+ markRetryFailed(messageId) {
190
+ this.statistics.failedRetries++
191
+ this.retryCounters.delete(messageId)
192
+ this.cancelPendingPhoneRequest(messageId)
193
+ this.removeRecentMessage(messageId)
194
+ }
195
+ /**
196
+ * Schedule a phone request with delay
197
+ */
198
+ schedulePhoneRequest(messageId, callback, delay = PHONE_REQUEST_DELAY) {
199
+ // Cancel any existing request for this message
200
+ this.cancelPendingPhoneRequest(messageId)
201
+ this.pendingPhoneRequests[messageId] = setTimeout(() => {
202
+ delete this.pendingPhoneRequests[messageId]
203
+ this.statistics.phoneRequests++
204
+ callback()
205
+ }, delay)
206
+ this.logger.debug(`Scheduled phone request for message ${messageId} with ${delay}ms delay`)
207
+ }
208
+ /**
209
+ * Cancel pending phone request
210
+ */
211
+ cancelPendingPhoneRequest(messageId) {
212
+ const timeout = this.pendingPhoneRequests[messageId]
213
+ if (timeout) {
214
+ clearTimeout(timeout)
215
+ delete this.pendingPhoneRequests[messageId]
216
+ this.logger.debug(`Cancelled pending phone request for message ${messageId}`)
217
+ }
218
+ }
219
+ keyToString(key) {
220
+ return `${key.to}${MESSAGE_KEY_SEPARATOR}${key.id}`
221
+ }
222
+ removeRecentMessage(messageId) {
223
+ const keyStr = this.messageKeyIndex.get(messageId)
224
+ if (!keyStr) {
225
+ return
226
+ }
227
+ this.recentMessagesMap.delete(keyStr)
228
+ this.messageKeyIndex.delete(messageId)
229
+ }
230
+ }
231
+ exports.MessageRetryManager = MessageRetryManager