vialeys 0.0.1 → 0.0.2

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/README.md +11 -5
  2. package/WAProto/AICommon/AICommon.js +13991 -9169
  3. package/WAProto/AICommon/AICommon.proto +110 -3
  4. package/WAProto/CompanionReg/CompanionReg.js +114 -0
  5. package/WAProto/CompanionReg/CompanionReg.proto +3 -0
  6. package/WAProto/DeviceCapabilities/DeviceCapabilities.js +652 -0
  7. package/WAProto/DeviceCapabilities/DeviceCapabilities.proto +19 -0
  8. package/WAProto/E2E/E2E.js +77193 -51248
  9. package/WAProto/E2E/E2E.proto +68 -12
  10. package/WAProto/HistorySync/HistorySync.js +3375 -178
  11. package/WAProto/HistorySync/HistorySync.proto +3 -3
  12. package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.js +98 -49
  13. package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.proto +2 -2
  14. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.js +75226 -48422
  15. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.proto +6 -0
  16. package/WAProto/StatusAttributions/StatusAttributions.js +21 -0
  17. package/WAProto/StatusAttributions/StatusAttributions.proto +3 -0
  18. package/WAProto/SyncAction/SyncAction.js +9423 -2023
  19. package/WAProto/SyncAction/SyncAction.proto +208 -15
  20. package/WAProto/Wa6/Wa6.js +83 -0
  21. package/WAProto/Wa6/Wa6.proto +3 -0
  22. package/WAProto/Web/Web.js +92664 -63403
  23. package/WAProto/Web/Web.proto +31 -13
  24. package/engine-requirements.js +9 -7
  25. package/lib/Defaults/baileys-version.json +2 -2
  26. package/lib/Defaults/connection.js +51 -0
  27. package/lib/Defaults/constants.js +62 -0
  28. package/lib/Defaults/history.js +17 -0
  29. package/lib/Defaults/index.js +36 -142
  30. package/lib/Defaults/media.js +48 -0
  31. package/lib/Defaults/prefix.js +18 -0
  32. package/lib/Signal/Group/group-session-builder.js +10 -42
  33. package/lib/Signal/Group/group_cipher.js +9 -6
  34. package/lib/Signal/Group/index.js +39 -53
  35. package/lib/Signal/Group/keyhelper.js +8 -41
  36. package/lib/Signal/Group/sender-chain-key.js +4 -4
  37. package/lib/Signal/Group/sender-key-distribution-message.js +5 -5
  38. package/lib/Signal/Group/sender-key-message.js +12 -8
  39. package/lib/Signal/Group/sender-key-state.js +4 -4
  40. package/lib/Signal/Group/sender-message-key.js +2 -2
  41. package/lib/Signal/libsignal.js +45 -69
  42. package/lib/Signal/lid-mapping.js +15 -11
  43. package/lib/Socket/Client/types.js +2 -2
  44. package/lib/Socket/Client/websocket.js +16 -14
  45. package/lib/Socket/business.js +41 -32
  46. package/lib/Socket/chats.js +123 -98
  47. package/lib/Socket/community.js +50 -40
  48. package/lib/Socket/groups.js +59 -47
  49. package/lib/Socket/index.js +4 -4
  50. package/lib/Socket/messages-recv.js +226 -171
  51. package/lib/Socket/messages-send.js +187 -143
  52. package/lib/Socket/newsletter.js +61 -47
  53. package/lib/Socket/socket.js +133 -90
  54. package/lib/Socket/usync.js +6 -6
  55. package/lib/Store/index.js +27 -11
  56. package/lib/Store/make-cache-manager-store.js +14 -15
  57. package/lib/Store/make-in-memory-store.js +28 -24
  58. package/lib/Types/LabelAssociation.js +2 -2
  59. package/lib/Types/Message.js +6 -6
  60. package/lib/Types/MexUpdates.js +5 -4
  61. package/lib/Types/State.js +4 -4
  62. package/lib/Types/index.js +28 -12
  63. package/lib/Utils/auth-utils.js +28 -26
  64. package/lib/Utils/baileys-event-stream.js +68 -69
  65. package/lib/Utils/business.js +63 -53
  66. package/lib/Utils/chat-utils.js +81 -71
  67. package/lib/Utils/crypto.js +25 -45
  68. package/lib/Utils/decode-wa-message.js +319 -311
  69. package/lib/Utils/event-buffer.js +21 -22
  70. package/lib/Utils/generics.js +103 -73
  71. package/lib/Utils/history.js +21 -21
  72. package/lib/Utils/index.js +27 -13
  73. package/lib/Utils/link-preview.js +7 -30
  74. package/lib/Utils/logger.js +5 -5
  75. package/lib/Utils/lt-hash.js +3 -3
  76. package/lib/Utils/message-retry-manager.js +4 -4
  77. package/lib/Utils/messages-media.js +104 -109
  78. package/lib/Utils/messages.js +203 -171
  79. package/lib/Utils/noise-handler.js +28 -19
  80. package/lib/Utils/process-message.js +370 -136
  81. package/lib/Utils/signal.js +36 -25
  82. package/lib/Utils/use-multi-file-auth-state.js +18 -22
  83. package/lib/Utils/validate-connection.js +52 -45
  84. package/lib/WABinary/decode.js +6 -32
  85. package/lib/WABinary/encode.js +3 -29
  86. package/lib/WABinary/generic-utils.js +4 -4
  87. package/lib/WABinary/index.js +27 -11
  88. package/lib/WAM/encode.js +16 -8
  89. package/lib/WAM/index.js +27 -11
  90. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
  91. package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
  92. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
  93. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
  94. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
  95. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
  96. package/lib/WAUSync/Protocols/index.js +27 -11
  97. package/lib/WAUSync/USyncQuery.js +17 -10
  98. package/lib/WAUSync/index.js +27 -11
  99. package/lib/index.js +62 -37
  100. package/package.json +1 -1
  101. package/WAProto/index.d.ts +0 -55
  102. package/lib/index.d.ts +0 -13
@@ -1,312 +1,320 @@
1
- "use strict"
2
-
3
- Object.defineProperty(exports, "__esModule", { value: true })
4
-
5
- const boom_1 = require("@hapi/boom")
6
- const WAProto_1 = require("../../WAProto")
7
- const WABinary_1 = require("../WABinary")
8
- const generics_1 = require("./generics")
9
- const messages_1 = require("./messages")
10
-
11
- const NO_MESSAGE_FOUND_ERROR_TEXT = 'Message absent from node'
12
-
13
- const MISSING_KEYS_ERROR_TEXT = 'Key used already or never filled'
14
-
15
- // Retry configuration for failed decryption
16
- const DECRYPTION_RETRY_CONFIG = {
17
- maxRetries: 3,
18
- baseDelayMs: 100,
19
- sessionRecordErrors: ['No session record', 'SessionError: No session record']
20
- }
21
-
22
- const NACK_REASONS = {
23
- ParsingError: 487,
24
- UnrecognizedStanza: 488,
25
- UnrecognizedStanzaClass: 489,
26
- UnrecognizedStanzaType: 490,
27
- InvalidProtobuf: 491,
28
- InvalidHostedCompanionStanza: 493,
29
- MissingMessageSecret: 495,
30
- SignalErrorOldCounter: 496,
31
- MessageDeletedOnPeer: 499,
32
- UnhandledError: 500,
33
- UnsupportedAdminRevoke: 550,
34
- UnsupportedLIDGroup: 551,
35
- DBOperationFailed: 552
36
- }
37
-
38
- const extractAddressingContext = (stanza) => {
39
- let senderAlt
40
- let recipientAlt
41
-
42
- const sender = stanza.attrs.participant || stanza.attrs.from
43
- const addressingMode = stanza.attrs.addressing_mode || (sender?.endsWith('lid') ? 'lid' : 'pn')
44
-
45
- if (WABinary_1.isLidUser(sender)) {
46
- // Message is LID-addressed: sender is LID, extract corresponding PN
47
- // without device data
48
- senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn
49
- recipientAlt = stanza.attrs.recipient_pn
50
- // with device data
51
- if (sender && senderAlt)
52
- senderAlt = WABinary_1.transferDevice(sender, senderAlt)
53
- }
54
-
55
- else if (WABinary_1.isJidUser(sender)) {
56
- // Message is PN-addressed: sender is PN, extract corresponding LID
57
- // without device data
58
- senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid || stanza.attrs.peer_recipient_lid
59
- recipientAlt = stanza.attrs.recipient_lid
60
- //with device data
61
- if (sender && senderAlt)
62
- senderAlt = WABinary_1.transferDevice(sender, senderAlt)
63
- }
64
- return {
65
- addressingMode,
66
- senderAlt,
67
- recipientAlt
68
- }
69
- }
70
-
71
- const getDecryptionJid = async (sender, repository) => {
72
- if (!sender.includes('@s.whatsapp.net')) {
73
- return sender
74
- }
75
- return (await repository.lidMapping.getLIDForPN(sender))
76
- }
77
-
78
- const storeMappingFromEnvelope = async (stanza, sender, decryptionJid, repository, logger) => {
79
- const { senderAlt } = extractAddressingContext(stanza)
80
-
81
- if (senderAlt && WABinary_1.isLidUser(senderAlt) && WABinary_1.isJidUser(sender) && decryptionJid === sender) {
82
- try {
83
- await repository.lidMapping.storeLIDPNMappings([{ lid: senderAlt, pn: sender }])
84
- logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope')
85
- }
86
- catch (error) {
87
- logger.warn({ sender, senderAlt, error }, 'Failed to store LID mapping')
88
- }
89
- }
90
- }
91
-
92
- /**
93
- * Decode the received node as a message.
94
- * @note this will only parse the message, not decrypt it
95
- */
96
- function decodeMessageNode(stanza, meId, meLid) {
97
- let msgType
98
- let chatId
99
- let author
100
- let fromMe = false
101
- const msgId = stanza.attrs.id
102
- const from = stanza.attrs.from
103
- const participant = stanza.attrs.participant
104
- const recipient = stanza.attrs.recipient
105
- const addressingContext = extractAddressingContext(stanza)
106
- const isMe = (jid) => WABinary_1.areJidsSameUser(jid, meId)
107
- const isMeLid = (jid) => WABinary_1.areJidsSameUser(jid, meLid)
108
- if (WABinary_1.isJidUser(from) || WABinary_1.isLidUser(from)) {
109
- if (recipient && !WABinary_1.isJidMetaAI(recipient)) {
110
- if (!isMe(from) && !isMeLid(from)) {
111
- throw new boom_1.Boom('receipient present, but msg not from me', { data: stanza })
112
- }
113
- if (isMe(from) || isMeLid(from)) {
114
- fromMe = true
115
- }
116
- chatId = recipient
117
- }
118
- else {
119
- chatId = from
120
- }
121
- msgType = 'chat'
122
- author = from
123
- }
124
- else if (WABinary_1.isJidGroup(from)) {
125
- if (!participant) {
126
- throw new boom_1.Boom('No participant in group message')
127
- }
128
- if (isMe(participant) || isMeLid(participant)) {
129
- fromMe = true;
130
- }
131
- msgType = 'group'
132
- author = participant
133
- chatId = from
134
- }
135
- else if (WABinary_1.isJidBroadcast(from)) {
136
- if (!participant) {
137
- throw new boom_1.Boom('No participant in group message')
138
- }
139
- const isParticipantMe = isMe(participant)
140
- if (WABinary_1.isJidStatusBroadcast(from)) {
141
- msgType = isParticipantMe ? 'direct_peer_status' : 'other_status'
142
- }
143
- else {
144
- msgType = isParticipantMe ? 'peer_broadcast' : 'other_broadcast'
145
- }
146
- fromMe = isParticipantMe
147
- chatId = from
148
- author = participant
149
- }
150
- else if (WABinary_1.isJidNewsletter(from)) {
151
- msgType = 'newsletter'
152
- chatId = from
153
- author = from
154
- if (isMe(from) || isMeLid(from)) {
155
- fromMe = true;
156
- }
157
- }
158
- else {
159
- throw new boom_1.Boom('Unknown message type', { data: stanza })
160
- }
161
-
162
- const pushname = stanza?.attrs?.notify
163
- const key = {
164
- remoteJid: chatId,
165
- remoteJidAlt: !WABinary_1.isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
166
- fromMe,
167
- id: msgId,
168
- participant,
169
- participantAlt: WABinary_1.isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
170
- }
171
- const fullMessage = {
172
- key,
173
- messageTimestamp: +stanza.attrs.t,
174
- pushName: pushname,
175
- broadcast: WABinary_1.isJidBroadcast(from),
176
- newsletter: WABinary_1.isJidNewsletter(from)
177
- }
178
- if (msgType === 'newsletter') {
179
- fullMessage.newsletter_server_id = +stanza.attrs?.server_id
180
- }
181
- if (key.fromMe) {
182
- fullMessage.status = WAProto_1.proto.WebMessageInfo.Status.SERVER_ACK
183
- }
184
- if (!key.fromMe) {
185
- fullMessage.platform = messages_1.getDevice(key.id)
186
- }
187
- return {
188
- fullMessage,
189
- author,
190
- sender: msgType === 'chat' ? author : chatId
191
- }
192
- }
193
-
194
- 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 = WAProto_1.proto.VerifiedNameCertificate.decode(content)
206
- const details = WAProto_1.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 user = WABinary_1.isJidUser(sender) ? sender : author // TODO: flaky logic
221
- const decryptionJid = await getDecryptionJid(user, repository)
222
-
223
- if (tag !== 'plaintext') {
224
- await storeMappingFromEnvelope(stanza, user, decryptionJid, repository, logger)
225
- }
226
- try {
227
- const e2eType = tag === 'plaintext' ? 'plaintext' : attrs.type
228
- switch (e2eType) {
229
- case 'skmsg':
230
- msgBuffer = await repository.decryptGroupMessage({
231
- group: sender,
232
- authorJid: author,
233
- msg: content
234
- })
235
- break
236
- case 'pkmsg':
237
- case 'msg':
238
- msgBuffer = await repository.decryptMessage({
239
- jid: decryptionJid,
240
- type: e2eType,
241
- ciphertext: content
242
- })
243
- break
244
- case 'plaintext':
245
- msgBuffer = content
246
- break
247
- default:
248
- throw new Error(`Unknown e2e type: ${e2eType}`)
249
- }
250
- let msg = WAProto_1.proto.Message.decode(e2eType !== 'plaintext' ? generics_1.unpadRandomMax16(msgBuffer) : msgBuffer)
251
- msg = msg.deviceSentMessage?.message || msg
252
- if (msg.senderKeyDistributionMessage) {
253
- //eslint-disable-next-line max-depth
254
- try {
255
- await repository.processSenderKeyDistributionMessage({
256
- authorJid: author,
257
- item: msg.senderKeyDistributionMessage
258
- })
259
- }
260
- catch (err) {
261
- logger.error({ key: fullMessage.key, err }, 'failed to decrypt message')
262
- }
263
- }
264
- if (fullMessage.message) {
265
- Object.assign(fullMessage.message, msg)
266
- }
267
- else {
268
- fullMessage.message = msg
269
- }
270
- }
271
- catch (err) {
272
- const errorContext = {
273
- key: fullMessage.key,
274
- err,
275
- messageType: tag === 'plaintext' ? 'plaintext' : attrs.type,
276
- sender,
277
- author,
278
- isSessionRecordError: isSessionRecordError(err)
279
- }
280
- logger.error(errorContext, 'failed to process sender key distribution message')
281
- fullMessage.messageStubType = WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT
282
- fullMessage.messageStubParameters = [err.message.toString()]
283
- }
284
- }
285
- }
286
-
287
- // if nothing was found to decrypt
288
- if (!decryptables) {
289
- fullMessage.messageStubType = WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT
290
- fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT]
291
- }
292
- }
293
- }
294
- }
295
-
296
- /**
297
- * Utility function to check if an error is related to missing session record
298
- */
299
- function isSessionRecordError(error) {
300
- const errorMessage = error?.message || error?.toString() || ''
301
- return DECRYPTION_RETRY_CONFIG.sessionRecordErrors.some(errorPattern => errorMessage.includes(errorPattern))
302
- }
303
-
304
- module.exports = {
305
- NACK_REASONS,
306
- decodeMessageNode,
307
- decryptMessageNode,
308
- extractAddressingContext,
309
- MISSING_KEYS_ERROR_TEXT,
310
- DECRYPTION_RETRY_CONFIG,
311
- NO_MESSAGE_FOUND_ERROR_TEXT
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { Boom } = require("@hapi/boom")
6
+ const { proto } = require("../../WAProto")
7
+ const {
8
+ isJidUser,
9
+ isLidUser,
10
+ isJidGroup,
11
+ isJidMetaAI,
12
+ isJidBroadcast,
13
+ isJidNewsletter,
14
+ transferDevice,
15
+ areJidsSameUser,
16
+ isJidStatusBroadcast
17
+ } = require("../WABinary")
18
+ const { unpadRandomMax16 } = require("./generics")
19
+ const { getDevice } = require("./messages")
20
+
21
+ const NO_MESSAGE_FOUND_ERROR_TEXT = 'Message absent from node'
22
+
23
+ const MISSING_KEYS_ERROR_TEXT = 'Key used already or never filled'
24
+
25
+ // Retry configuration for failed decryption
26
+ const DECRYPTION_RETRY_CONFIG = {
27
+ maxRetries: 3,
28
+ baseDelayMs: 100,
29
+ sessionRecordErrors: ['No session record', 'SessionError: No session record']
30
+ }
31
+
32
+ const NACK_REASONS = {
33
+ ParsingError: 487,
34
+ UnrecognizedStanza: 488,
35
+ UnrecognizedStanzaClass: 489,
36
+ UnrecognizedStanzaType: 490,
37
+ InvalidProtobuf: 491,
38
+ InvalidHostedCompanionStanza: 493,
39
+ MissingMessageSecret: 495,
40
+ SignalErrorOldCounter: 496,
41
+ MessageDeletedOnPeer: 499,
42
+ UnhandledError: 500,
43
+ UnsupportedAdminRevoke: 550,
44
+ UnsupportedLIDGroup: 551,
45
+ DBOperationFailed: 552
46
+ }
47
+
48
+ const extractAddressingContext = (stanza) => {
49
+ let senderAlt
50
+ let recipientAlt
51
+
52
+ const sender = stanza.attrs.participant || stanza.attrs.from
53
+ const addressingMode = stanza.attrs.addressing_mode || (sender?.endsWith('lid') ? 'lid' : 'pn')
54
+
55
+ if (isLidUser(sender)) {
56
+ // Message is LID-addressed: sender is LID, extract corresponding PN
57
+ // without device data
58
+ senderAlt = stanza.attrs.participant_pn || stanza.attrs.sender_pn || stanza.attrs.peer_recipient_pn
59
+ recipientAlt = stanza.attrs.recipient_pn
60
+ // with device data
61
+ if (sender && senderAlt)
62
+ senderAlt = transferDevice(sender, senderAlt)
63
+ }
64
+
65
+ else if (isJidUser(sender)) {
66
+ // Message is PN-addressed: sender is PN, extract corresponding LID
67
+ // without device data
68
+ senderAlt = stanza.attrs.participant_lid || stanza.attrs.sender_lid || stanza.attrs.peer_recipient_lid
69
+ recipientAlt = stanza.attrs.recipient_lid
70
+ //with device data
71
+ if (sender && senderAlt)
72
+ senderAlt = transferDevice(sender, senderAlt)
73
+ }
74
+ return {
75
+ addressingMode,
76
+ senderAlt,
77
+ recipientAlt
78
+ }
79
+ }
80
+
81
+ const getDecryptionJid = async (sender, repository) => {
82
+ if (!sender.includes('@s.whatsapp.net')) {
83
+ return sender
84
+ }
85
+ return (await repository.lidMapping.getLIDForPN(sender))
86
+ }
87
+
88
+ const storeMappingFromEnvelope = async (stanza, sender, decryptionJid, repository, logger) => {
89
+ const { senderAlt } = extractAddressingContext(stanza)
90
+
91
+ if (senderAlt && isLidUser(senderAlt) && isJidUser(sender) && decryptionJid === sender) {
92
+ try {
93
+ await repository.lidMapping.storeLIDPNMappings([{ lid: senderAlt, pn: sender }])
94
+ logger.debug({ sender, senderAlt }, 'Stored LID mapping from envelope')
95
+ }
96
+ catch (error) {
97
+ logger.warn({ sender, senderAlt, error }, 'Failed to store LID mapping')
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Decode the received node as a message.
104
+ * @note this will only parse the message, not decrypt it
105
+ */
106
+ function decodeMessageNode(stanza, meId, meLid) {
107
+ let msgType
108
+ let chatId
109
+ let author
110
+ let fromMe = false
111
+ const msgId = stanza.attrs.id
112
+ const from = stanza.attrs.from
113
+ const participant = stanza.attrs.participant
114
+ const recipient = stanza.attrs.recipient
115
+ const addressingContext = extractAddressingContext(stanza)
116
+ const isMe = (jid) => areJidsSameUser(jid, meId)
117
+ const isMeLid = (jid) => areJidsSameUser(jid, meLid)
118
+ if (isJidUser(from) || isLidUser(from)) {
119
+ if (recipient && !isJidMetaAI(recipient)) {
120
+ if (!isMe(from) && !isMeLid(from)) {
121
+ throw new Boom('receipient present, but msg not from me', { data: stanza })
122
+ }
123
+ if (isMe(from) || isMeLid(from)) {
124
+ fromMe = true
125
+ }
126
+ chatId = recipient
127
+ }
128
+ else {
129
+ chatId = from
130
+ }
131
+ msgType = 'chat'
132
+ author = from
133
+ }
134
+ else if (isJidGroup(from)) {
135
+ if (!participant) {
136
+ throw new Boom('No participant in group message')
137
+ }
138
+ if (isMe(participant) || isMeLid(participant)) {
139
+ fromMe = true;
140
+ }
141
+ msgType = 'group'
142
+ author = participant
143
+ chatId = from
144
+ }
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
+ }
153
+ else {
154
+ msgType = isParticipantMe ? 'peer_broadcast' : 'other_broadcast'
155
+ }
156
+ fromMe = isParticipantMe
157
+ chatId = from
158
+ author = participant
159
+ }
160
+ else if (isJidNewsletter(from)) {
161
+ msgType = 'newsletter'
162
+ chatId = from
163
+ author = from
164
+ if (isMe(from) || isMeLid(from)) {
165
+ fromMe = true;
166
+ }
167
+ }
168
+ else {
169
+ throw new Boom('Unknown message type', { data: stanza })
170
+ }
171
+
172
+ const pushname = stanza?.attrs?.notify
173
+ const key = {
174
+ remoteJid: chatId,
175
+ remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
176
+ fromMe,
177
+ id: msgId,
178
+ participant,
179
+ participantAlt: isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
180
+ }
181
+ const fullMessage = {
182
+ key,
183
+ messageTimestamp: +stanza.attrs.t,
184
+ pushName: pushname,
185
+ broadcast: isJidBroadcast(from),
186
+ newsletter: isJidNewsletter(from),
187
+ platform: getDevice(key.id)
188
+ }
189
+ if (msgType === 'newsletter') {
190
+ fullMessage.newsletter_server_id = +stanza.attrs?.server_id
191
+ }
192
+ if (key.fromMe) {
193
+ fullMessage.status = proto.WebMessageInfo.Status.SERVER_ACK
194
+ }
195
+ return {
196
+ fullMessage,
197
+ author,
198
+ sender: msgType === 'chat' ? author : chatId
199
+ }
200
+ }
201
+
202
+ const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
203
+ const { fullMessage, author, sender } = decodeMessageNode(stanza, meId, meLid)
204
+ return {
205
+ fullMessage,
206
+ category: stanza.attrs.category,
207
+ author,
208
+ async decrypt() {
209
+ let decryptables = 0
210
+ if (Array.isArray(stanza.content)) {
211
+ for (const { tag, attrs, content } of stanza.content) {
212
+ if (tag === 'verified_name' && content instanceof Uint8Array) {
213
+ const cert = proto.VerifiedNameCertificate.decode(content)
214
+ const details = proto.VerifiedNameCertificate.Details.decode(cert.details)
215
+ fullMessage.verifiedBizName = details.verifiedName
216
+ }
217
+ if (tag === 'unavailable' && attrs.type === 'view_once') {
218
+ fullMessage.key.isViewOnce = true; // TODO: remove from here and add a STUB TYPE
219
+ }
220
+ if (tag !== 'enc' && tag !== 'plaintext') {
221
+ continue
222
+ }
223
+ if (!(content instanceof Uint8Array)) {
224
+ continue
225
+ }
226
+ decryptables += 1
227
+ let msgBuffer
228
+ const user = isJidUser(sender) ? sender : author // TODO: flaky logic
229
+ const decryptionJid = await getDecryptionJid(user, repository)
230
+
231
+ if (tag !== 'plaintext') {
232
+ await storeMappingFromEnvelope(stanza, user, decryptionJid, repository, logger)
233
+ }
234
+ try {
235
+ const e2eType = tag === 'plaintext' ? 'plaintext' : attrs.type
236
+ switch (e2eType) {
237
+ case 'skmsg':
238
+ msgBuffer = await repository.decryptGroupMessage({
239
+ group: sender,
240
+ authorJid: author,
241
+ msg: content
242
+ })
243
+ break
244
+ case 'pkmsg':
245
+ case 'msg':
246
+ msgBuffer = await repository.decryptMessage({
247
+ jid: decryptionJid,
248
+ type: e2eType,
249
+ ciphertext: content
250
+ })
251
+ break
252
+ case 'plaintext':
253
+ msgBuffer = content
254
+ break
255
+ default:
256
+ throw new Error(`Unknown e2e type: ${e2eType}`)
257
+ }
258
+ let msg = proto.Message.decode(e2eType !== 'plaintext' ? unpadRandomMax16(msgBuffer) : msgBuffer)
259
+ msg = msg.deviceSentMessage?.message || msg
260
+ if (msg.senderKeyDistributionMessage) {
261
+ //eslint-disable-next-line max-depth
262
+ try {
263
+ await repository.processSenderKeyDistributionMessage({
264
+ authorJid: author,
265
+ item: msg.senderKeyDistributionMessage
266
+ })
267
+ }
268
+ catch (err) {
269
+ logger.error({ key: fullMessage.key, err }, 'failed to decrypt message')
270
+ }
271
+ }
272
+ if (fullMessage.message) {
273
+ Object.assign(fullMessage.message, msg)
274
+ }
275
+ else {
276
+ fullMessage.message = msg
277
+ }
278
+ }
279
+ catch (err) {
280
+ const errorContext = {
281
+ key: fullMessage.key,
282
+ err,
283
+ messageType: tag === 'plaintext' ? 'plaintext' : attrs.type,
284
+ sender,
285
+ author,
286
+ isSessionRecordError: isSessionRecordError(err)
287
+ }
288
+ logger.error(errorContext, 'failed to process sender key distribution message')
289
+ fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
290
+ fullMessage.messageStubParameters = [err.message.toString()]
291
+ }
292
+ }
293
+ }
294
+
295
+ // if nothing was found to decrypt
296
+ if (!decryptables) {
297
+ fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
298
+ fullMessage.messageStubParameters = [NO_MESSAGE_FOUND_ERROR_TEXT]
299
+ }
300
+ }
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Utility function to check if an error is related to missing session record
306
+ */
307
+ function isSessionRecordError(error) {
308
+ const errorMessage = error?.message || error?.toString() || ''
309
+ return DECRYPTION_RETRY_CONFIG.sessionRecordErrors.some(errorPattern => errorMessage.includes(errorPattern))
310
+ }
311
+
312
+ module.exports = {
313
+ NACK_REASONS,
314
+ decodeMessageNode,
315
+ decryptMessageNode,
316
+ extractAddressingContext,
317
+ MISSING_KEYS_ERROR_TEXT,
318
+ DECRYPTION_RETRY_CONFIG,
319
+ NO_MESSAGE_FOUND_ERROR_TEXT
312
320
  }