@nexustechpro/baileys 2.0.2 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +924 -1299
  3. package/lib/Defaults/baileys-version.json +6 -2
  4. package/lib/Defaults/index.js +172 -172
  5. package/lib/Signal/libsignal.js +380 -292
  6. package/lib/Signal/lid-mapping.js +264 -171
  7. package/lib/Socket/Client/index.js +2 -2
  8. package/lib/Socket/Client/types.js +10 -10
  9. package/lib/Socket/Client/websocket.js +45 -310
  10. package/lib/Socket/business.js +375 -375
  11. package/lib/Socket/chats.js +909 -963
  12. package/lib/Socket/communities.js +430 -430
  13. package/lib/Socket/groups.js +342 -342
  14. package/lib/Socket/index.js +22 -22
  15. package/lib/Socket/messages-recv.js +777 -743
  16. package/lib/Socket/messages-send.js +295 -305
  17. package/lib/Socket/mex.js +50 -50
  18. package/lib/Socket/newsletter.js +148 -148
  19. package/lib/Socket/nexus-handler.js +75 -261
  20. package/lib/Socket/socket.js +709 -1201
  21. package/lib/Store/index.js +5 -5
  22. package/lib/Store/make-cache-manager-store.js +81 -81
  23. package/lib/Store/make-in-memory-store.js +416 -416
  24. package/lib/Store/make-ordered-dictionary.js +81 -81
  25. package/lib/Store/object-repository.js +30 -30
  26. package/lib/Types/Auth.js +1 -1
  27. package/lib/Types/Bussines.js +1 -1
  28. package/lib/Types/Call.js +1 -1
  29. package/lib/Types/Chat.js +7 -7
  30. package/lib/Types/Contact.js +1 -1
  31. package/lib/Types/Events.js +1 -1
  32. package/lib/Types/GroupMetadata.js +1 -1
  33. package/lib/Types/Label.js +24 -24
  34. package/lib/Types/LabelAssociation.js +6 -6
  35. package/lib/Types/Message.js +10 -10
  36. package/lib/Types/Newsletter.js +28 -28
  37. package/lib/Types/Product.js +1 -1
  38. package/lib/Types/Signal.js +1 -1
  39. package/lib/Types/Socket.js +2 -2
  40. package/lib/Types/State.js +12 -12
  41. package/lib/Types/USync.js +1 -1
  42. package/lib/Types/index.js +25 -25
  43. package/lib/Utils/auth-utils.js +264 -256
  44. package/lib/Utils/baileys-event-stream.js +55 -55
  45. package/lib/Utils/browser-utils.js +27 -27
  46. package/lib/Utils/business.js +228 -230
  47. package/lib/Utils/chat-utils.js +694 -764
  48. package/lib/Utils/crypto.js +109 -135
  49. package/lib/Utils/decode-wa-message.js +310 -314
  50. package/lib/Utils/event-buffer.js +547 -547
  51. package/lib/Utils/generics.js +297 -297
  52. package/lib/Utils/history.js +91 -83
  53. package/lib/Utils/index.js +21 -20
  54. package/lib/Utils/key-store.js +17 -0
  55. package/lib/Utils/link-preview.js +97 -98
  56. package/lib/Utils/logger.js +2 -2
  57. package/lib/Utils/lt-hash.js +47 -47
  58. package/lib/Utils/make-mutex.js +39 -39
  59. package/lib/Utils/message-retry-manager.js +148 -148
  60. package/lib/Utils/messages-media.js +534 -534
  61. package/lib/Utils/messages.js +705 -705
  62. package/lib/Utils/noise-handler.js +255 -255
  63. package/lib/Utils/pre-key-manager.js +105 -105
  64. package/lib/Utils/process-message.js +412 -412
  65. package/lib/Utils/signal.js +160 -158
  66. package/lib/Utils/use-multi-file-auth-state.js +120 -120
  67. package/lib/Utils/validate-connection.js +194 -194
  68. package/lib/WABinary/constants.js +1300 -1300
  69. package/lib/WABinary/decode.js +237 -237
  70. package/lib/WABinary/encode.js +232 -232
  71. package/lib/WABinary/generic-utils.js +252 -211
  72. package/lib/WABinary/index.js +5 -5
  73. package/lib/WABinary/jid-utils.js +279 -95
  74. package/lib/WABinary/types.js +1 -1
  75. package/lib/WAM/BinaryInfo.js +9 -9
  76. package/lib/WAM/constants.js +22852 -22852
  77. package/lib/WAM/encode.js +149 -149
  78. package/lib/WAM/index.js +3 -3
  79. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -28
  80. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -53
  81. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -26
  82. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -37
  83. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
  84. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -28
  85. package/lib/WAUSync/Protocols/index.js +4 -4
  86. package/lib/WAUSync/USyncQuery.js +93 -93
  87. package/lib/WAUSync/USyncUser.js +22 -22
  88. package/lib/WAUSync/index.js +3 -3
  89. package/lib/index.js +66 -66
  90. package/package.json +171 -144
  91. package/lib/Signal/Group/ciphertext-message.js +0 -12
  92. package/lib/Signal/Group/group-session-builder.js +0 -30
  93. package/lib/Signal/Group/group_cipher.js +0 -100
  94. package/lib/Signal/Group/index.js +0 -12
  95. package/lib/Signal/Group/keyhelper.js +0 -18
  96. package/lib/Signal/Group/sender-chain-key.js +0 -26
  97. package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
  98. package/lib/Signal/Group/sender-key-message.js +0 -66
  99. package/lib/Signal/Group/sender-key-name.js +0 -48
  100. package/lib/Signal/Group/sender-key-record.js +0 -41
  101. package/lib/Signal/Group/sender-key-state.js +0 -84
  102. package/lib/Signal/Group/sender-message-key.js +0 -26
@@ -1,314 +1,310 @@
1
- import { Boom } from "@hapi/boom"
2
- import { proto } from "../../WAProto/index.js"
3
- import {
4
- areJidsSameUser,
5
- isHostedLidUser,
6
- isHostedPnUser,
7
- isJidBroadcast,
8
- isJidGroup,
9
- isJidMetaAI,
10
- isJidNewsletter,
11
- isJidStatusBroadcast,
12
- isLidUser,
13
- isPnUser,
14
- // transferDevice
15
- } from "../WABinary/index.js"
16
- import { unpadRandomMax16 } from "./generics.js"
17
- export const getDecryptionJid = async (sender, repository, logger) => {
18
- // Skip LID mapping for newsletters, status broadcasts, and meta AI - they don't use it
19
- if (isJidNewsletter(sender) || isJidStatusBroadcast(sender) || isJidMetaAI(sender)) {
20
- return sender
21
- }
22
-
23
- if (isLidUser(sender) || isHostedLidUser(sender)) {
24
- return sender
25
- }
26
- const mapped = await repository.lidMapping.getLIDForPN(sender)
27
- //AUTO-RECOVERY: If mapping is missing, try to fetch it
28
- if (!mapped) {
29
- logger.warn({ sender }, "Lid mapping missing, attempting recovery")
30
- mapped = await repository.lidMapping.recoverMissingMapping(sender, logger)
31
- }
32
- return mapped || sender
33
- }
34
- const storeMappingFromEnvelope = async (stanza, sender, repository, decryptionJid, logger) => {
35
- // TODO: Handle hosted IDs
36
- const { senderAlt } = extractAddressingContext(stanza)
37
- if (senderAlt && isLidUser(senderAlt) && isPnUser(sender) && decryptionJid === sender) {
38
- try {
39
- await repository.lidMapping.storeLIDPNMappings([{ lid: senderAlt, pn: sender }])
40
- await repository.migrateSession(sender, senderAlt)
41
- logger.debug({ sender, senderAlt }, "Stored LID mapping from envelope")
42
- } catch (error) {
43
- logger.warn({ sender, senderAlt, error }, "Failed to store LID mapping")
44
- }
45
- }
46
- }
47
- export const NO_MESSAGE_FOUND_ERROR_TEXT = "Message absent from node"
48
- export const MISSING_KEYS_ERROR_TEXT = "Key used already or never filled"
49
- // Retry configuration for failed decryption
50
- export const DECRYPTION_RETRY_CONFIG = {
51
- maxRetries: 3,
52
- baseDelayMs: 100,
53
- sessionRecordErrors: ["No session record", "SessionError: No session record"],
54
- }
55
- export const NACK_REASONS = {
56
- ParsingError: 487,
57
- UnrecognizedStanza: 488,
58
- UnrecognizedStanzaClass: 489,
59
- UnrecognizedStanzaType: 490,
60
- InvalidProtobuf: 491,
61
- InvalidHostedCompanionStanza: 493,
62
- MissingMessageSecret: 495,
63
- SignalErrorOldCounter: 496,
64
- MessageDeletedOnPeer: 499,
65
- UnhandledError: 500,
66
- UnsupportedAdminRevoke: 550,
67
- UnsupportedLIDGroup: 551,
68
- DBOperationFailed: 552,
69
- }
70
- export const extractAddressingContext = (stanza) => {
71
- let senderAlt
72
- let recipientAlt
73
- const sender = stanza.attrs.participant || stanza.attrs.from
74
- const addressingMode = stanza.attrs.addressing_mode || (sender?.endsWith("lid") ? "lid" : "pn")
75
- if (addressingMode === "lid") {
76
- // Message is LID-addressed: sender is LID, extract corresponding PN
77
- // without device data
78
- senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn
79
- recipientAlt = stanza.attrs.recipient_pn
80
- // with device data
81
- //if (sender && senderAlt) senderAlt = transferDevice(sender, senderAlt)
82
- } else {
83
- // Message is PN-addressed: sender is PN, extract corresponding LID
84
- // without device data
85
- senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid || stanza.attrs.peer_recipient_lid
86
- recipientAlt = stanza.attrs.recipient_lid
87
- //with device data
88
- //if (sender && senderAlt) senderAlt = transferDevice(sender, senderAlt)
89
- }
90
- return {
91
- addressingMode,
92
- senderAlt,
93
- recipientAlt,
94
- }
95
- }
96
- /**
97
- * Decode the received node as a message.
98
- * @note this will only parse the message, not decrypt it
99
- */
100
- export function decodeMessageNode(stanza, meId, meLid) {
101
- let msgType
102
- let chatId
103
- let author
104
- let fromMe = false
105
- const msgId = stanza.attrs.id
106
- const from = stanza.attrs.from
107
- const participant = stanza.attrs.participant
108
- const recipient = stanza.attrs.recipient
109
- const addressingContext = extractAddressingContext(stanza)
110
- const isMe = (jid) => areJidsSameUser(jid, meId)
111
- const isMeLid = (jid) => areJidsSameUser(jid, meLid)
112
- if (isPnUser(from) || isLidUser(from) || isHostedLidUser(from) || isHostedPnUser(from)) {
113
- if (recipient && !isJidMetaAI(recipient)) {
114
- if (!isMe(from) && !isMeLid(from)) {
115
- throw new Boom("receipient present, but msg not from me", { data: stanza })
116
- }
117
- if (isMe(from) || isMeLid(from)) {
118
- fromMe = true
119
- }
120
- chatId = recipient
121
- } else {
122
- chatId = from
123
- }
124
- msgType = "chat"
125
- author = from
126
- } else if (isJidGroup(from)) {
127
- if (!participant) {
128
- throw new Boom("No participant in group message")
129
- }
130
- if (isMe(participant) || isMeLid(participant)) {
131
- fromMe = true
132
- }
133
- msgType = "group"
134
- author = participant
135
- chatId = from
136
- } else if (isJidBroadcast(from)) {
137
- if (!participant) {
138
- throw new Boom("No participant in group message")
139
- }
140
- const isParticipantMe = isMe(participant)
141
- if (isJidStatusBroadcast(from)) {
142
- msgType = isParticipantMe ? "direct_peer_status" : "other_status"
143
- } else {
144
- msgType = isParticipantMe ? "peer_broadcast" : "other_broadcast"
145
- }
146
- fromMe = isParticipantMe
147
- chatId = from
148
- author = participant
149
- } else if (isJidNewsletter(from)) {
150
- msgType = "newsletter"
151
- chatId = from
152
- author = from
153
- if (isMe(from) || isMeLid(from)) {
154
- fromMe = true
155
- }
156
- } else {
157
- throw new Boom("Unknown message type", { data: stanza })
158
- }
159
- const pushname = stanza?.attrs?.notify
160
- const key = {
161
- remoteJid: chatId,
162
- remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
163
- fromMe,
164
- id: msgId,
165
- participant,
166
- participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
167
- addressingMode: addressingContext.addressingMode,
168
- ...(msgType === "newsletter" && stanza.attrs.server_id ? { server_id: stanza.attrs.server_id } : {}),
169
- }
170
- const fullMessage = {
171
- key,
172
- messageTimestamp: +stanza.attrs.t,
173
- pushName: pushname,
174
- broadcast: isJidBroadcast(from),
175
- }
176
- if (key.fromMe) {
177
- fullMessage.status = proto.WebMessageInfo.Status.SERVER_ACK
178
- }
179
- return {
180
- fullMessage,
181
- author,
182
- sender: msgType === "chat" ? author : chatId,
183
- }
184
- }
185
- export const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
186
- const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid)
187
- return {
188
- fullMessage,
189
- category: stanza.attrs.category,
190
- author,
191
- async decrypt() {
192
- let decryptables = 0
193
- if (Array.isArray(stanza.content)) {
194
- for (const { tag, attrs, content } of stanza.content) {
195
- if (tag === "verified_name" && content instanceof Uint8Array) {
196
- const cert = proto.VerifiedNameCertificate.decode(content)
197
- const details = proto.VerifiedNameCertificate.Details.decode(cert.details)
198
- fullMessage.verifiedBizName = details.verifiedName
199
- }
200
- if (tag === "unavailable" && attrs.type === "view_once") {
201
- fullMessage.key.isViewOnce = true // TODO: remove from here and add a STUB TYPE
202
- }
203
- if (tag !== "enc" && tag !== "plaintext") {
204
- continue
205
- }
206
- if (!(content instanceof Uint8Array)) {
207
- continue
208
- }
209
- decryptables += 1
210
- let msgBuffer
211
- const decryptionJid = await getDecryptionJid(author, repository, logger)
212
- let decryptionAltJid = null
213
- const { senderAlt } = extractAddressingContext(stanza)
214
- if (senderAlt) {
215
- decryptionAltJid = await getDecryptionJid(senderAlt, repository, logger)
216
- }
217
- if (tag !== "plaintext") {
218
- // TODO: Handle hosted devices
219
- await storeMappingFromEnvelope(stanza, author, repository, decryptionJid, logger)
220
- }
221
- try {
222
- const e2eType = tag === "plaintext" ? "plaintext" : attrs.type
223
- switch (e2eType) {
224
- case "skmsg":
225
- msgBuffer = await repository.decryptGroupMessage({
226
- group: sender,
227
- authorJid: author,
228
- msg: content,
229
- })
230
- break
231
- case "pkmsg":
232
- case "msg":
233
- try {
234
- msgBuffer = await repository.decryptMessage({
235
- jid: decryptionJid,
236
- alternateJid: decryptionAltJid,
237
- type: e2eType,
238
- ciphertext: content,
239
- })
240
- } catch (decryptErr) {
241
- const cleanError = new Error(decryptErr?.message || decryptErr?.toString() || "Decryption failed")
242
- cleanError.stack = decryptErr?.stack
243
- throw cleanError
244
- }
245
- break
246
- case "plaintext":
247
- msgBuffer = content
248
- break
249
- default:
250
- throw new Error(`Unknown e2e type: ${e2eType}`)
251
- }
252
- let msg = proto.Message.decode(e2eType !== "plaintext" ? unpadRandomMax16(msgBuffer) : msgBuffer)
253
- msg = msg.deviceSentMessage?.message || msg
254
- if (msg.senderKeyDistributionMessage) {
255
- //eslint-disable-next-line max-depth
256
- try {
257
- await repository.processSenderKeyDistributionMessage({
258
- authorJid: author,
259
- item: msg.senderKeyDistributionMessage,
260
- })
261
- } catch (err) {
262
- logger.error({ key: fullMessage.key, err }, "failed to process sender key distribution message")
263
- }
264
- }
265
- if (fullMessage.message) {
266
- Object.assign(fullMessage.message, msg)
267
- } else {
268
- fullMessage.message = msg
269
- }
270
- } catch (err) {
271
- const errorMessage = err?.message || err?.toString() || ""
272
- const isBadMac = errorMessage.includes("Bad MAC")
273
- const isMessageCounter = errorMessage.includes("Key used already or never filled")
274
-
275
- const errorContext = {
276
- key: fullMessage.key,
277
- err,
278
- errorMessage,
279
- messageType: tag === "plaintext" ? "plaintext" : attrs.type,
280
- sender,
281
- author,
282
- isSessionRecordError: isSessionRecordError(err),
283
- isBadMac,
284
- isMessageCounter,
285
- }
286
-
287
- if (isBadMac || isMessageCounter) {
288
- errorContext.isSignalSessionCorrupted = true
289
- errorContext.requiresSessionReset = true
290
- logger.error(
291
- { ...errorContext, jid: decryptionJid },
292
- `Signal session corrupted - ${isBadMac ? 'BAD_MAC' : 'MESSAGE_COUNTER'} error, session reset required`,
293
- )
294
- }
295
-
296
- logger.error(errorContext, "failed to decrypt message")
297
- fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
298
- fullMessage.messageStubParameters = [errorMessage]
299
- }
300
- }
301
- }
302
- // if nothing was found to decrypt
303
- if (!decryptables) {
304
- fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
305
- fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT]
306
- }
307
- },
308
- }
309
- }
310
- function isSessionRecordError(error) {
311
- const errorMessage = error?.message || error?.toString() || ""
312
- return DECRYPTION_RETRY_CONFIG.sessionRecordErrors.some((errorPattern) => errorMessage.includes(errorPattern))
313
- }
314
- //# sourceMappingURL=decode-wa-message.js.map
1
+ import { Boom } from "@hapi/boom"
2
+ import { proto } from "../../WAProto/index.js"
3
+ import {
4
+ areJidsSameUser,
5
+ isHostedLidUser,
6
+ isHostedPnUser,
7
+ isJidBroadcast,
8
+ isJidGroup,
9
+ isJidMetaAI,
10
+ isJidNewsletter,
11
+ isJidStatusBroadcast,
12
+ isLidUser,
13
+ isPnUser,
14
+ // transferDevice
15
+ } from "../WABinary/index.js"
16
+ import { unpadRandomMax16 } from "./generics.js"
17
+ export const getDecryptionJid = async (sender, repository, logger) => {
18
+ // Skip LID mapping for newsletters, status broadcasts, and meta AI - they don't use it
19
+ if (isJidNewsletter(sender) || isJidStatusBroadcast(sender) || isJidMetaAI(sender)) {
20
+ return sender
21
+ }
22
+
23
+ if (isLidUser(sender) || isHostedLidUser(sender)) {
24
+ return sender
25
+ }
26
+ const mapped = await repository.lidMapping.getLIDForPN(sender)
27
+ return mapped || sender
28
+ }
29
+ const storeMappingFromEnvelope = async (stanza, sender, repository, decryptionJid, logger, meId, meLid) => {
30
+ const { senderAlt } = extractAddressingContext(stanza)
31
+ if (!senderAlt) return
32
+
33
+ // Case 1: PN-addressed message — sender is PN, senderAlt is LID
34
+ if (isLidUser(senderAlt) && isPnUser(sender) && decryptionJid === sender) {
35
+ if (areJidsSameUser(sender, meId) || areJidsSameUser(senderAlt, meLid)) return // never remap own identity
36
+ try {
37
+ await repository.lidMapping.storeLIDPNMappings([{ lid: senderAlt, pn: sender }])
38
+ repository.migrateSession(sender, senderAlt).catch(err => logger?.warn?.({ sender, senderAlt, err }, 'Failed to migrate session (PN→LID)'))
39
+ logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope (PN→LID)')
40
+ } catch (error) {
41
+ logger.warn({ sender, senderAlt, error }, 'Failed to store LID mapping (PN→LID)')
42
+ }
43
+ }
44
+ // Case 2: LID-addressed message — sender is LID, senderAlt is PN
45
+ else if (isPnUser(senderAlt) && isLidUser(sender)) {
46
+ if (areJidsSameUser(sender, meLid) || areJidsSameUser(senderAlt, meId)) return // never remap own identity
47
+ try {
48
+ await repository.lidMapping.storeLIDPNMappings([{ lid: sender, pn: senderAlt }])
49
+ repository.migrateSession(senderAlt, sender).catch(err => logger?.warn?.({ sender, senderAlt, err }, 'Failed to migrate session (LID→PN)'))
50
+ logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope (LID→PN)')
51
+ } catch (error) {
52
+ logger.warn({ sender, senderAlt, error }, 'Failed to store LID mapping (LID→PN)')
53
+ }
54
+ }
55
+ }
56
+ export const NO_MESSAGE_FOUND_ERROR_TEXT = "Message absent from node"
57
+ export const MISSING_KEYS_ERROR_TEXT = "Key used already or never filled"
58
+ // Retry configuration for failed decryption
59
+ export const DECRYPTION_RETRY_CONFIG = {
60
+ maxRetries: 3,
61
+ baseDelayMs: 100,
62
+ sessionRecordErrors: ["No session record", "SessionError: No session record"],
63
+ }
64
+ export const NACK_REASONS = {
65
+ ParsingError: 487,
66
+ UnrecognizedStanza: 488,
67
+ UnrecognizedStanzaClass: 489,
68
+ UnrecognizedStanzaType: 490,
69
+ InvalidProtobuf: 491,
70
+ InvalidHostedCompanionStanza: 493,
71
+ MissingMessageSecret: 495,
72
+ SignalErrorOldCounter: 496,
73
+ MessageDeletedOnPeer: 499,
74
+ UnhandledError: 500,
75
+ UnsupportedAdminRevoke: 550,
76
+ UnsupportedLIDGroup: 551,
77
+ DBOperationFailed: 552,
78
+ }
79
+ export const extractAddressingContext = (stanza) => {
80
+ let senderAlt
81
+ let recipientAlt
82
+ const sender = stanza.attrs.participant || stanza.attrs.from
83
+ const addressingMode = stanza.attrs.addressing_mode || (sender?.endsWith("lid") ? "lid" : "pn")
84
+ if (addressingMode === "lid") {
85
+ // Message is LID-addressed: sender is LID, extract corresponding PN
86
+ // without device data
87
+ senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn
88
+ recipientAlt = stanza.attrs.recipient_pn
89
+ // with device data
90
+ //if (sender && senderAlt) senderAlt = transferDevice(sender, senderAlt)
91
+ } else {
92
+ // Message is PN-addressed: sender is PN, extract corresponding LID
93
+ // without device data
94
+ senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid || stanza.attrs.peer_recipient_lid
95
+ recipientAlt = stanza.attrs.recipient_lid
96
+ //with device data
97
+ //if (sender && senderAlt) senderAlt = transferDevice(sender, senderAlt)
98
+ }
99
+ return {
100
+ addressingMode,
101
+ senderAlt,
102
+ recipientAlt,
103
+ }
104
+ }
105
+ /**
106
+ * Decode the received node as a message.
107
+ * @note this will only parse the message, not decrypt it
108
+ */
109
+ export function decodeMessageNode(stanza, meId, meLid) {
110
+ let msgType
111
+ let chatId
112
+ let author
113
+ let fromMe = false
114
+ const msgId = stanza.attrs.id
115
+ const from = stanza.attrs.from
116
+ const participant = stanza.attrs.participant
117
+ const recipient = stanza.attrs.recipient
118
+ const addressingContext = extractAddressingContext(stanza)
119
+ const isMe = (jid) => areJidsSameUser(jid, meId)
120
+ const isMeLid = (jid) => areJidsSameUser(jid, meLid)
121
+ if (isPnUser(from) || isLidUser(from) || isHostedLidUser(from) || isHostedPnUser(from)) {
122
+ if (recipient && !isJidMetaAI(recipient)) {
123
+ if (!isMe(from) && !isMeLid(from)) {
124
+ throw new Boom("receipient present, but msg not from me", { data: stanza })
125
+ }
126
+ if (isMe(from) || isMeLid(from)) {
127
+ fromMe = true
128
+ }
129
+ chatId = recipient
130
+ } else {
131
+ chatId = from
132
+ }
133
+ msgType = "chat"
134
+ author = from
135
+ } else if (isJidGroup(from)) {
136
+ if (!participant) {
137
+ throw new Boom("No participant in group message")
138
+ }
139
+ if (isMe(participant) || isMeLid(participant)) {
140
+ fromMe = true
141
+ }
142
+ msgType = "group"
143
+ author = participant
144
+ chatId = from
145
+ } else if (isJidBroadcast(from)) {
146
+ if (!participant) {
147
+ throw new Boom("No participant in group message")
148
+ }
149
+ const isParticipantMe = isMe(participant)
150
+ if (isJidStatusBroadcast(from)) {
151
+ msgType = isParticipantMe ? "direct_peer_status" : "other_status"
152
+ } else {
153
+ msgType = isParticipantMe ? "peer_broadcast" : "other_broadcast"
154
+ }
155
+ fromMe = isParticipantMe
156
+ chatId = from
157
+ author = participant
158
+ } else if (isJidNewsletter(from)) {
159
+ msgType = "newsletter"
160
+ chatId = from
161
+ author = from
162
+ if (isMe(from) || isMeLid(from)) {
163
+ fromMe = true
164
+ }
165
+ } else {
166
+ throw new Boom("Unknown message type", { data: stanza })
167
+ }
168
+ const pushname = stanza?.attrs?.notify
169
+ const key = {
170
+ remoteJid: chatId,
171
+ remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
172
+ fromMe,
173
+ id: msgId,
174
+ participant,
175
+ participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
176
+ addressingMode: addressingContext.addressingMode,
177
+ ...(msgType === "newsletter" && stanza.attrs.server_id ? { server_id: stanza.attrs.server_id } : {}),
178
+ }
179
+ const fullMessage = {
180
+ key,
181
+ messageTimestamp: +stanza.attrs.t,
182
+ pushName: pushname,
183
+ broadcast: isJidBroadcast(from),
184
+ }
185
+ if (key.fromMe) {
186
+ fullMessage.status = proto.WebMessageInfo.Status.SERVER_ACK
187
+ }
188
+ return {
189
+ fullMessage,
190
+ author,
191
+ sender: msgType === "chat" ? author : chatId,
192
+ }
193
+ }
194
+ export const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
195
+ const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid)
196
+ return {
197
+ fullMessage,
198
+ category: stanza.attrs.category,
199
+ author,
200
+ async decrypt() {
201
+ let decryptables = 0
202
+ if (Array.isArray(stanza.content)) {
203
+ for (const { tag, attrs, content } of stanza.content) {
204
+ if (tag === "verified_name" && content instanceof Uint8Array) {
205
+ const cert = proto.VerifiedNameCertificate.decode(content)
206
+ const details = proto.VerifiedNameCertificate.Details.decode(cert.details)
207
+ fullMessage.verifiedBizName = details.verifiedName
208
+ }
209
+ if (tag === "unavailable" && attrs.type === "view_once") {
210
+ fullMessage.key.isViewOnce = true // TODO: remove from here and add a STUB TYPE
211
+ }
212
+ if (tag !== "enc" && tag !== "plaintext") {
213
+ continue
214
+ }
215
+ if (!(content instanceof Uint8Array)) {
216
+ continue
217
+ }
218
+ decryptables += 1
219
+ let msgBuffer
220
+ const decryptionJid = await getDecryptionJid(author, repository, logger)
221
+ let decryptionAltJid = null
222
+ const { senderAlt } = extractAddressingContext(stanza)
223
+ if (senderAlt) {
224
+ decryptionAltJid = await getDecryptionJid(senderAlt, repository, logger)
225
+ }
226
+ if (tag !== 'plaintext') {
227
+ storeMappingFromEnvelope(stanza, author, repository, decryptionJid, logger, meId, meLid).catch(err => logger?.warn?.({ err }, 'storeMappingFromEnvelope failed'))
228
+ }
229
+ try {
230
+ const e2eType = tag === "plaintext" ? "plaintext" : attrs.type
231
+ switch (e2eType) {
232
+ case "skmsg":
233
+ msgBuffer = await repository.decryptGroupMessage({
234
+ group: sender,
235
+ authorJid: author,
236
+ msg: content,
237
+ })
238
+ break
239
+ case "pkmsg":
240
+ case "msg":
241
+ try {
242
+ msgBuffer = await repository.decryptMessage({ jid: decryptionJid, type: e2eType, ciphertext: content })
243
+ } catch (decryptErr) {
244
+ const errMsg = decryptErr?.message || decryptErr?.toString() || ''
245
+ if (errMsg.includes('InvalidMessage') && e2eType === 'msg') {
246
+ logger?.debug?.({ jid: decryptionJid }, '[Signal] InvalidMessage — stale session, deleting')
247
+ try {
248
+ const toDelete = [decryptionJid]
249
+ await repository.deleteSession(toDelete)
250
+ } catch { }
251
+ }
252
+ const cleanError = new Error(errMsg || 'Decryption failed')
253
+ cleanError.stack = decryptErr?.stack
254
+ throw cleanError
255
+ }
256
+ break
257
+ case "plaintext":
258
+ msgBuffer = content
259
+ break
260
+ default:
261
+ throw new Error(`Unknown e2e type: ${e2eType}`)
262
+ }
263
+ let msg = proto.Message.decode(e2eType !== "plaintext" ? unpadRandomMax16(msgBuffer) : msgBuffer)
264
+ msg = msg.deviceSentMessage?.message || msg
265
+ if (msg.senderKeyDistributionMessage) {
266
+ //eslint-disable-next-line max-depth
267
+ try {
268
+ await repository.processSenderKeyDistributionMessage({
269
+ authorJid: author,
270
+ item: msg.senderKeyDistributionMessage,
271
+ })
272
+ } catch (err) {
273
+ logger.error({ key: fullMessage.key, err }, "failed to process sender key distribution message")
274
+ }
275
+ }
276
+ if (fullMessage.message) {
277
+ Object.assign(fullMessage.message, msg)
278
+ } else {
279
+ fullMessage.message = msg
280
+ }
281
+ const viewOnceInner =
282
+ msg?.viewOnceMessage?.message ||
283
+ msg?.viewOnceMessageV2?.message ||
284
+ msg?.viewOnceMessageV2Extension?.message
285
+ if (
286
+ viewOnceInner?.imageMessage?.viewOnce ||
287
+ viewOnceInner?.videoMessage?.viewOnce ||
288
+ viewOnceInner?.audioMessage?.viewOnce
289
+ ) {
290
+ fullMessage.key.isViewOnce = true
291
+ }
292
+ } catch (err) {
293
+ const errorMessage = err?.message || err?.toString() || ""
294
+ const errStr = err?.message || (typeof err === "string" ? err : "") || ""
295
+ const isExpectedDecryptErr = errStr.includes("InvalidPreKeyId") || errStr.includes("SessionNotFound") || errStr.includes("InvalidMessage") || errStr.includes("no sender key state") || errStr.includes("old counter")
296
+ ; (isExpectedDecryptErr ? logger?.debug?.bind(logger) : logger?.error?.bind(logger))?.({ key: fullMessage.key, err, errorMessage, messageType: tag === "plaintext" ? "plaintext" : attrs.type, sender, author }, "failed to decrypt message")
297
+ fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
298
+ fullMessage.messageStubParameters = [errorMessage]
299
+ }
300
+ }
301
+ }
302
+ // if nothing was found to decrypt
303
+ if (!decryptables) {
304
+ fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
305
+ fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT]
306
+ }
307
+ },
308
+ }
309
+ }
310
+ //# sourceMappingURL=decode-wa-message.js.map