@badzz88/baileys 8.5.2 → 8.5.4
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.
- package/README.md +1 -1
- package/WAProto/WAProto.proto +1256 -75
- package/WAProto/index.d.ts +36042 -19649
- package/WAProto/index.js +181990 -135645
- package/engine-requirements.js +7 -15
- package/lib/Defaults/index.js +22 -17
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +39 -59
- package/lib/Signal/lid-mapping.js +28 -10
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +3 -11
- package/lib/Socket/chats.js +668 -319
- package/lib/Socket/communities.js +85 -89
- package/lib/Socket/graphql.js +435 -228
- package/lib/Socket/groups.js +132 -211
- package/lib/Socket/index.js +9 -7
- package/lib/Socket/interactive-handler.js +117 -60
- package/lib/Socket/interop.js +56 -175
- package/lib/Socket/managed-account.js +79 -48
- package/lib/Socket/messages-recv.js +898 -340
- package/lib/Socket/messages-send.js +462 -160
- package/lib/Socket/mex.js +40 -53
- package/lib/Socket/newsletter.js +291 -291
- package/lib/Socket/privacy.js +77 -265
- package/lib/Socket/registration.js +164 -157
- package/lib/Socket/socket.js +181 -199
- package/lib/Socket/username.js +144 -227
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +25 -141
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Newsletter.js +96 -65
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +52 -57
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +2 -6
- package/lib/Utils/chat-utils.js +102 -38
- package/lib/Utils/crypto.js +54 -84
- package/lib/Utils/decode-wa-message.js +316 -83
- package/lib/Utils/event-buffer.js +37 -61
- package/lib/Utils/generics.js +157 -103
- package/lib/Utils/group-history.js +3 -12
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +1 -5
- package/lib/Utils/link-preview.js +3 -8
- package/lib/Utils/lt-hash.js +2 -35
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-inspect.js +393 -0
- package/lib/Utils/message-retry-manager.js +27 -92
- package/lib/Utils/messages-media.js +159 -130
- package/lib/Utils/messages.js +144 -566
- package/lib/Utils/meta-ai-msmsg.js +62 -202
- package/lib/Utils/noise-handler.js +19 -21
- package/lib/Utils/offline-node-processor.js +3 -8
- package/lib/Utils/pre-key-manager.js +13 -23
- package/lib/Utils/process-message.js +197 -106
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/signal.js +28 -63
- package/lib/Utils/stanza-ack.js +39 -12
- package/lib/Utils/sync-action-utils.js +4 -7
- package/lib/Utils/tc-token-utils.js +20 -25
- package/lib/Utils/use-multi-file-auth-state.js +30 -14
- package/lib/Utils/validate-connection.js +59 -19
- package/lib/WABinary/decode.js +50 -16
- package/lib/WABinary/encode.js +54 -224
- package/lib/WABinary/generic-utils.js +7 -97
- package/lib/WABinary/jid-utils.js +27 -14
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +58 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +13 -5
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -4
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +20 -7
- package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +865 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -2
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +2 -1
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncBackoff.js +26 -0
- package/lib/WAUSync/USyncQuery.js +77 -6
- package/lib/WAUSync/USyncUser.js +16 -8
- package/lib/WAUSync/index.js +1 -0
- package/lib/antiban.js +608 -854
- package/lib/index.js +3 -3
- package/package.json +16 -19
- package/lib/Socket/luxu.js +0 -387
- package/lib/Socket/text-router.js +0 -83
- package/lib/Store/keyed-db.js +0 -108
- package/lib/Types/Mex.js +0 -112
- package/lib/Utils/command-loader.d.ts +0 -31
- package/lib/Utils/command-loader.js +0 -100
- package/lib/Utils/companion-reg-client-utils.js +0 -42
- package/lib/Utils/curve25519-js.js +0 -275
- package/lib/Utils/native-bridge.js +0 -82
- package/lib/Utils/session-pool.d.ts +0 -16
- package/lib/Utils/session-pool.js +0 -94
- package/lib/Utils/sticker.d.ts +0 -17
- package/lib/Utils/sticker.js +0 -145
- package/lib/Utils/view-once-cache.d.ts +0 -9
- package/lib/Utils/view-once-cache.js +0 -79
|
@@ -37,8 +37,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
37
37
|
signalRepository,
|
|
38
38
|
query,
|
|
39
39
|
upsertMessage,
|
|
40
|
-
getUSyncDevices,
|
|
41
|
-
createParticipantNodes,
|
|
42
40
|
resyncAppState,
|
|
43
41
|
onUnexpectedError,
|
|
44
42
|
assertSessions,
|
|
@@ -48,33 +46,36 @@ const makeMessagesRecvSocket = config => {
|
|
|
48
46
|
uploadPreKeys,
|
|
49
47
|
sendPeerDataOperationMessage,
|
|
50
48
|
messageRetryManager,
|
|
51
|
-
issuePrivacyTokens
|
|
49
|
+
issuePrivacyTokens,
|
|
50
|
+
getUSyncDevices,
|
|
51
|
+
createParticipantNodes,
|
|
52
|
+
newsletterServerIdCache
|
|
52
53
|
} = sock
|
|
53
54
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
const socketCreatedAt = Math.floor(Date.now() / 1000)
|
|
56
57
|
let isConnected = false
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
const retryMutex = (0, make_mutex_1.makeMutex)()
|
|
59
60
|
const msgRetryCache =
|
|
60
61
|
config.msgRetryCounterCache ||
|
|
61
62
|
new node_cache_1.default({
|
|
62
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
63
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
63
64
|
useClones: false
|
|
64
65
|
})
|
|
65
66
|
const callOfferCache =
|
|
66
67
|
config.callOfferCache ||
|
|
67
68
|
new node_cache_1.default({
|
|
68
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER,
|
|
69
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER,
|
|
69
70
|
useClones: false
|
|
70
71
|
})
|
|
71
72
|
const placeholderResendCache =
|
|
72
73
|
config.placeholderResendCache ||
|
|
73
74
|
new node_cache_1.default({
|
|
74
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
75
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
75
76
|
useClones: false
|
|
76
77
|
})
|
|
77
|
-
|
|
78
|
+
|
|
78
79
|
const identityAssertDebounce = new node_cache_1.default({ stdTTL: 5, useClones: false })
|
|
79
80
|
let sendActiveReceipts = false
|
|
80
81
|
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
@@ -101,8 +102,8 @@ const makeMessagesRecvSocket = config => {
|
|
|
101
102
|
logger.debug({ messageKey }, 'already requested resend')
|
|
102
103
|
return
|
|
103
104
|
} else {
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
|
|
106
|
+
|
|
106
107
|
await placeholderResendCache.set(messageKey?.id, msgData || true)
|
|
107
108
|
}
|
|
108
109
|
await (0, Utils_1.delay)(2000)
|
|
@@ -126,11 +127,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
126
127
|
}, 8000)
|
|
127
128
|
return sendPeerDataOperationMessage(pdoMessage)
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
* Request a Waffle (Meta-account) linking nonce from the paired phone.
|
|
131
|
-
* The phone responds via a PeerDataOperationRequestResponseMessage containing
|
|
132
|
-
* a WaffleNonceFetchResponse with the nonce needed for Meta account linking.
|
|
133
|
-
*/
|
|
130
|
+
|
|
134
131
|
const requestWaffleNonce = async () => {
|
|
135
132
|
if (!authState.creds.me?.id) {
|
|
136
133
|
throw new boom_1.Boom('Not authenticated')
|
|
@@ -139,27 +136,18 @@ const makeMessagesRecvSocket = config => {
|
|
|
139
136
|
peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.WAFFLE_LINKING_NONCE_FETCH
|
|
140
137
|
})
|
|
141
138
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* Used during companion linking to canonicalize the user identity across devices.
|
|
145
|
-
* The phone responds with a CompanionCanonicalUserNonceFetchResponse (nonce + waFbid).
|
|
146
|
-
*
|
|
147
|
-
* @param {string} [registrationTraceId] - Optional trace ID for this registration attempt.
|
|
148
|
-
*/
|
|
149
|
-
const requestCompanionCanonicalNonce = async (registrationTraceId) => {
|
|
139
|
+
|
|
140
|
+
const requestCompanionCanonicalNonce = async registrationTraceId => {
|
|
150
141
|
if (!authState.creds.me?.id) {
|
|
151
142
|
throw new boom_1.Boom('Not authenticated')
|
|
152
143
|
}
|
|
153
144
|
return sendPeerDataOperationMessage({
|
|
154
145
|
companionCanonicalUserNonceFetchRequest: registrationTraceId ? { registrationTraceId } : {},
|
|
155
|
-
peerDataOperationRequestType:
|
|
146
|
+
peerDataOperationRequestType:
|
|
147
|
+
index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_CANONICAL_USER_NONCE_FETCH
|
|
156
148
|
})
|
|
157
149
|
}
|
|
158
|
-
|
|
159
|
-
* Request a Companion Meta nonce from the paired phone.
|
|
160
|
-
* Used during Meta-account companion linking flow.
|
|
161
|
-
* The phone responds with a CompanionMetaNonceFetchResponse (nonce).
|
|
162
|
-
*/
|
|
150
|
+
|
|
163
151
|
const requestCompanionMetaNonce = async () => {
|
|
164
152
|
if (!authState.creds.me?.id) {
|
|
165
153
|
throw new boom_1.Boom('Not authenticated')
|
|
@@ -168,57 +156,138 @@ const makeMessagesRecvSocket = config => {
|
|
|
168
156
|
peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_META_NONCE_FETCH
|
|
169
157
|
})
|
|
170
158
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
let data
|
|
179
|
-
try {
|
|
180
|
-
data = JSON.parse(mexNode.content.toString())
|
|
181
|
-
} catch (error) {
|
|
182
|
-
logger.error({ err: error, node }, 'Failed to parse mex newsletter notification')
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
const operation = data?.operation
|
|
186
|
-
const updates = data?.updates
|
|
187
|
-
if (!updates || !operation) {
|
|
188
|
-
logger.warn({ data }, 'Invalid mex newsletter notification content')
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
const handleMexNotification = async node => {
|
|
162
|
+
const updateNodes = (0, WABinary_1.getBinaryNodeChildren)(node, 'update')
|
|
163
|
+
if (!updateNodes.length) {
|
|
164
|
+
logger.debug({ node }, 'mex notification with no update children')
|
|
189
165
|
return
|
|
190
166
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
167
|
+
for (const updateNode of updateNodes) {
|
|
168
|
+
const opName = updateNode.attrs?.op_name
|
|
169
|
+
if (!opName) continue
|
|
170
|
+
let payload
|
|
171
|
+
try {
|
|
172
|
+
const raw = updateNode.content?.text ?? updateNode.content?.toString?.()
|
|
173
|
+
payload = raw ? JSON.parse(raw) : null
|
|
174
|
+
} catch (e) {
|
|
175
|
+
logger.error({ err: e, opName }, 'failed to parse mex update payload')
|
|
176
|
+
continue
|
|
177
|
+
}
|
|
178
|
+
if (!payload?.data) {
|
|
179
|
+
logger.debug({ opName }, 'mex update with no data field')
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
const d = payload.data
|
|
183
|
+
switch (opName) {
|
|
184
|
+
case 'NotificationNewsletterUpdate': {
|
|
185
|
+
|
|
186
|
+
const upd = d.xwa2_notify_newsletter_on_metadata_update
|
|
187
|
+
if (upd?.id) {
|
|
196
188
|
ev.emit('newsletter-settings.update', {
|
|
197
|
-
id:
|
|
198
|
-
update:
|
|
189
|
+
id: upd.id,
|
|
190
|
+
update: upd.thread_metadata?.settings ?? {}
|
|
199
191
|
})
|
|
200
192
|
}
|
|
193
|
+
break
|
|
201
194
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
195
|
+
case 'NotificationNewsletterJoin': {
|
|
196
|
+
|
|
197
|
+
const upd = d.xwa2_notify_newsletter_on_join
|
|
198
|
+
if (upd?.id) {
|
|
206
199
|
ev.emit('newsletter-participants.update', {
|
|
207
|
-
id:
|
|
200
|
+
id: upd.id,
|
|
208
201
|
author: node.attrs.from,
|
|
209
|
-
user:
|
|
202
|
+
user: (0, WABinary_1.jidNormalizedUser)(node.attrs.from),
|
|
203
|
+
new_role: upd.viewer_metadata?.role ?? 'SUBSCRIBER',
|
|
204
|
+
action: 'join',
|
|
205
|
+
metadata: upd.thread_metadata
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
case 'NotificationNewsletterMuteChange': {
|
|
211
|
+
|
|
212
|
+
const upd = d.xwa2_notify_newsletter_on_mute_change
|
|
213
|
+
if (upd?.id) {
|
|
214
|
+
ev.emit('newsletter-settings.update', {
|
|
215
|
+
id: upd.id,
|
|
216
|
+
update: { mute: upd.mute }
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
break
|
|
220
|
+
}
|
|
221
|
+
case 'NotificationNewsletterUserSettingChange': {
|
|
222
|
+
|
|
223
|
+
const upd = d.xwa2_notify_newsletter_on_user_setting_change
|
|
224
|
+
if (upd?.id && upd.setting) {
|
|
225
|
+
ev.emit('newsletter-settings.update', {
|
|
226
|
+
id: upd.id,
|
|
227
|
+
update: { userSetting: upd.setting }
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
break
|
|
231
|
+
}
|
|
232
|
+
case 'NotificationNewsletterAdminPromote': {
|
|
233
|
+
|
|
234
|
+
const upd = d.xwa2_notify_newsletter_on_admin_promote
|
|
235
|
+
if (upd?.id) {
|
|
236
|
+
ev.emit('newsletter-participants.update', {
|
|
237
|
+
id: upd.id,
|
|
238
|
+
author: node.attrs.from,
|
|
239
|
+
user: upd.user,
|
|
210
240
|
new_role: 'ADMIN',
|
|
211
241
|
action: 'promote'
|
|
212
242
|
})
|
|
213
243
|
}
|
|
244
|
+
break
|
|
214
245
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
246
|
+
case 'NotificationGroupMemberLinkPropertyUpdate': {
|
|
247
|
+
|
|
248
|
+
const upd = d.xwa2_notify_group_on_prop_change
|
|
249
|
+
if (upd?.id && upd.properties?.member_link_mode !== undefined) {
|
|
250
|
+
ev.emit('groups.update', [
|
|
251
|
+
{
|
|
252
|
+
id: upd.id,
|
|
253
|
+
memberAddMode: upd.properties.member_link_mode
|
|
254
|
+
}
|
|
255
|
+
])
|
|
256
|
+
}
|
|
257
|
+
break
|
|
258
|
+
}
|
|
259
|
+
case 'NotificationGroupLimitSharingPropertyUpdate': {
|
|
260
|
+
|
|
261
|
+
const upd = d.xwa2_notify_group_on_prop_change
|
|
262
|
+
if (upd?.id && upd.properties?.limit_sharing !== undefined) {
|
|
263
|
+
ev.emit('groups.update', [
|
|
264
|
+
{
|
|
265
|
+
id: upd.id,
|
|
266
|
+
limitSharing: upd.properties.limit_sharing
|
|
267
|
+
}
|
|
268
|
+
])
|
|
269
|
+
}
|
|
270
|
+
break
|
|
271
|
+
}
|
|
272
|
+
case 'NotificationGroupMemberShareGroupHistoryModePropertyUpdate': {
|
|
273
|
+
|
|
274
|
+
const upd = d.xwa2_notify_group_on_prop_change
|
|
275
|
+
if (upd?.id && upd.properties?.member_share_group_history_mode !== undefined) {
|
|
276
|
+
ev.emit('groups.update', [
|
|
277
|
+
{
|
|
278
|
+
id: upd.id,
|
|
279
|
+
memberShareHistoryMode: upd.properties.member_share_group_history_mode
|
|
280
|
+
}
|
|
281
|
+
])
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
default:
|
|
286
|
+
logger.debug({ opName, from: node.attrs.from }, 'unhandled mex op')
|
|
287
|
+
}
|
|
219
288
|
}
|
|
220
289
|
}
|
|
221
|
-
|
|
290
|
+
|
|
222
291
|
const handleNewsletterNotification = async node => {
|
|
223
292
|
const from = node.attrs.from
|
|
224
293
|
const child = (0, WABinary_1.getAllBinaryNodeChildren)(node)[0]
|
|
@@ -268,7 +337,9 @@ const makeMessagesRecvSocket = config => {
|
|
|
268
337
|
})
|
|
269
338
|
}
|
|
270
339
|
break
|
|
271
|
-
case 'message':
|
|
340
|
+
case 'message': {
|
|
341
|
+
const viewCount = child.attrs.view_count !== undefined ? +child.attrs.view_count : undefined
|
|
342
|
+
const impressionCount = child.attrs.impression_count !== undefined ? +child.attrs.impression_count : undefined
|
|
272
343
|
const plaintextNode = (0, WABinary_1.getBinaryNodeChild)(child, 'plaintext')
|
|
273
344
|
if (plaintextNode?.content) {
|
|
274
345
|
try {
|
|
@@ -281,11 +352,16 @@ const makeMessagesRecvSocket = config => {
|
|
|
281
352
|
key: {
|
|
282
353
|
remoteJid: from,
|
|
283
354
|
id: child.attrs.message_id || child.attrs.server_id,
|
|
284
|
-
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
fromMe: false
|
|
285
358
|
},
|
|
286
359
|
message: messageProto,
|
|
287
360
|
messageTimestamp: +child.attrs.t
|
|
288
361
|
}).toJSON()
|
|
362
|
+
|
|
363
|
+
if (viewCount !== undefined) fullMessage.views = viewCount
|
|
364
|
+
if (impressionCount !== undefined) fullMessage.impressions = impressionCount
|
|
289
365
|
await upsertMessage(fullMessage, 'append')
|
|
290
366
|
logger.info('Processed plaintext newsletter message')
|
|
291
367
|
} catch (error) {
|
|
@@ -293,23 +369,44 @@ const makeMessagesRecvSocket = config => {
|
|
|
293
369
|
}
|
|
294
370
|
}
|
|
295
371
|
break
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
372
|
+
}
|
|
373
|
+
case 'live_updates': {
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
const messagesNode = (0, WABinary_1.getBinaryNodeChild)(child, 'messages')
|
|
378
|
+
const msgTs = messagesNode?.attrs?.t ? +messagesNode.attrs.t : undefined
|
|
379
|
+
for (const msgNode of (0, WABinary_1.getBinaryNodeChildren)(messagesNode ?? child, 'message')) {
|
|
380
|
+
const serverId = msgNode.attrs.server_id
|
|
381
|
+
const fwdNode = (0, WABinary_1.getBinaryNodeChild)(msgNode, 'forwards_count')
|
|
382
|
+
const forwardsCount = fwdNode?.attrs?.count !== undefined ? +fwdNode.attrs.count : undefined
|
|
383
|
+
const reactionsNode = (0, WABinary_1.getBinaryNodeChild)(msgNode, 'reactions')
|
|
384
|
+
const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionsNode ?? msgNode, 'reaction').map(r => ({
|
|
385
|
+
code: r.attrs.code,
|
|
386
|
+
count: +r.attrs.count
|
|
387
|
+
}))
|
|
388
|
+
ev.emit('newsletter.live-update', {
|
|
389
|
+
id: from,
|
|
390
|
+
server_id: serverId,
|
|
391
|
+
timestamp: msgTs,
|
|
392
|
+
forwardsCount,
|
|
393
|
+
reactions
|
|
394
|
+
})
|
|
395
|
+
}
|
|
305
396
|
break
|
|
306
|
-
|
|
397
|
+
}
|
|
398
|
+
case 'pin': {
|
|
399
|
+
const pinnedServerId = child.attrs.message_id || child.attrs.server_id
|
|
400
|
+
const isPinned = child.attrs.action !== 'unpin'
|
|
307
401
|
ev.emit('newsletter.pin', {
|
|
308
402
|
id: from,
|
|
309
|
-
server_id:
|
|
310
|
-
pinned:
|
|
403
|
+
server_id: pinnedServerId,
|
|
404
|
+
pinned: isPinned
|
|
311
405
|
})
|
|
406
|
+
|
|
407
|
+
ev.emit('newsletters.update', [{ id: from, pinnedMessage: isPinned ? pinnedServerId : null }])
|
|
312
408
|
break
|
|
409
|
+
}
|
|
313
410
|
case 'category':
|
|
314
411
|
ev.emit('newsletter-settings.update', {
|
|
315
412
|
id: from,
|
|
@@ -329,8 +426,92 @@ const makeMessagesRecvSocket = config => {
|
|
|
329
426
|
break
|
|
330
427
|
}
|
|
331
428
|
}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
const handleNewsletterStatus = async node => {
|
|
432
|
+
const { id, from, server_id, t, is_sender, offline, type, edit } = node.attrs
|
|
433
|
+
const serverId = server_id ? +server_id : undefined
|
|
434
|
+
const timestamp = t ? +t : undefined
|
|
435
|
+
const isSender = is_sender === 'true'
|
|
436
|
+
const offlineIndex = offline !== undefined ? +offline : undefined
|
|
437
|
+
|
|
438
|
+
if (id && serverId != null) {
|
|
439
|
+
newsletterServerIdCache?.set(id, serverId)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
const metaNode = (0, WABinary_1.getBinaryNodeChild)(node, 'meta')
|
|
444
|
+
const meta = metaNode
|
|
445
|
+
? {
|
|
446
|
+
...(metaNode.attrs.msg_edit_t ? { editedAt: +metaNode.attrs.msg_edit_t } : {}),
|
|
447
|
+
...(metaNode.attrs.original_msg_t ? { originalTimestamp: +metaNode.attrs.original_msg_t } : {}),
|
|
448
|
+
...(metaNode.attrs.interaction_type ? { interactionType: metaNode.attrs.interaction_type } : {}),
|
|
449
|
+
...(metaNode.attrs.parent_server_id ? { parentServerId: +metaNode.attrs.parent_server_id } : {}),
|
|
450
|
+
...(metaNode.attrs.response_server_id ? { responseServerId: +metaNode.attrs.response_server_id } : {})
|
|
451
|
+
}
|
|
452
|
+
: undefined
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
const viewsNode = (0, WABinary_1.getBinaryNodeChild)(node, 'views_count')
|
|
456
|
+
const viewsCount = viewsNode?.attrs?.count !== undefined ? +viewsNode.attrs.count : undefined
|
|
457
|
+
const responsesNode = (0, WABinary_1.getBinaryNodeChild)(node, 'responses_count')
|
|
458
|
+
const responsesCount = responsesNode?.attrs?.count !== undefined ? +responsesNode.attrs.count : undefined
|
|
459
|
+
const reactionsNode = (0, WABinary_1.getBinaryNodeChild)(node, 'reactions')
|
|
460
|
+
const reactionCounts = (0, WABinary_1.getBinaryNodeChildren)(reactionsNode ?? { content: [] }, 'reaction').map(
|
|
461
|
+
r => ({ code: r.attrs.code, count: +r.attrs.count })
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
let content = null
|
|
465
|
+
let mediaType = undefined
|
|
466
|
+
|
|
467
|
+
if (type === 'reaction') {
|
|
468
|
+
const reactionNode = (0, WABinary_1.getBinaryNodeChild)(node, 'reaction')
|
|
469
|
+
content = { type: 'reaction', code: reactionNode?.attrs?.code }
|
|
470
|
+
} else if (type === 'text' || type === 'media') {
|
|
471
|
+
const plaintextNode = (0, WABinary_1.getBinaryNodeChild)(node, 'plaintext')
|
|
472
|
+
if (type === 'media') mediaType = plaintextNode?.attrs?.mediatype
|
|
473
|
+
if (edit === '7' || edit === '8') {
|
|
474
|
+
content = { type: 'revoke', edit }
|
|
475
|
+
} else if (plaintextNode?.content) {
|
|
476
|
+
try {
|
|
477
|
+
const buf = Buffer.isBuffer(plaintextNode.content)
|
|
478
|
+
? plaintextNode.content
|
|
479
|
+
: Buffer.from(plaintextNode.content)
|
|
480
|
+
const message = index_js_1.proto.Message.decode(buf).toJSON()
|
|
481
|
+
content = { type, message, ...(mediaType ? { mediaType } : {}) }
|
|
482
|
+
} catch (err) {
|
|
483
|
+
logger.error({ err }, 'Failed to decode newsletter status plaintext')
|
|
484
|
+
content = { type, raw: true, ...(mediaType ? { mediaType } : {}) }
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
const ackType = type === 'reaction' ? 'reaction' : edit ? 'revoke' : type
|
|
491
|
+
await sendNode({
|
|
492
|
+
tag: 'ack',
|
|
493
|
+
attrs: { id, to: from, class: 'status', type: ackType || 'text' },
|
|
494
|
+
content: undefined
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
ev.emit('newsletter.status', {
|
|
498
|
+
id: from,
|
|
499
|
+
messageId: id,
|
|
500
|
+
serverId,
|
|
501
|
+
timestamp,
|
|
502
|
+
isSender,
|
|
503
|
+
...(offlineIndex !== undefined ? { offlineIndex } : {}),
|
|
504
|
+
...(meta ? { meta } : {}),
|
|
505
|
+
...(viewsCount !== undefined ? { viewsCount } : {}),
|
|
506
|
+
...(responsesCount !== undefined ? { responsesCount } : {}),
|
|
507
|
+
...(reactionCounts.length ? { reactionCounts } : {}),
|
|
508
|
+
content
|
|
509
|
+
})
|
|
510
|
+
}
|
|
332
511
|
const sendMessageAck = async (node, errorCode) => {
|
|
333
|
-
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
const stanza = (0, stanza_ack_1.buildAckStanza)(node, errorCode, authState.creds.me?.id)
|
|
334
515
|
logger.debug({ recv: { tag: node.tag, attrs: node.attrs }, sent: stanza.attrs }, 'sent ack')
|
|
335
516
|
await sendNode(stanza)
|
|
336
517
|
}
|
|
@@ -497,15 +678,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
497
678
|
await query(stanza)
|
|
498
679
|
}
|
|
499
680
|
|
|
500
|
-
|
|
501
|
-
* Re-encrypt call key for a device that reconnected mid-call.
|
|
502
|
-
* Source: OutgoingSignalingHandler.java enc_rekey / rekeyEncryptionTask
|
|
503
|
-
*
|
|
504
|
-
* @param {string} callId - Active call ID
|
|
505
|
-
* @param {string} callFrom - JID of the call creator
|
|
506
|
-
* @param {Buffer} encryptedKeyBytes - Re-encrypted call session key bytes
|
|
507
|
-
* @param {number} [count=0] - Retry counter (0–4)
|
|
508
|
-
*/
|
|
681
|
+
|
|
509
682
|
const rekeyCall = async (callId, callFrom, encryptedKeyBytes, count = 0) => {
|
|
510
683
|
const stanza = {
|
|
511
684
|
tag: 'call',
|
|
@@ -534,14 +707,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
534
707
|
await query(stanza)
|
|
535
708
|
}
|
|
536
709
|
|
|
537
|
-
|
|
538
|
-
* Join a call via an invite link.
|
|
539
|
-
* Source: OutgoingSignalingHandler.java link_join tag
|
|
540
|
-
*
|
|
541
|
-
* @param {string} callId - Call ID from the link
|
|
542
|
-
* @param {string} callCreator - JID of the call creator
|
|
543
|
-
* @param {string} linkToken - Token from the call link
|
|
544
|
-
*/
|
|
710
|
+
|
|
545
711
|
const joinCallLink = async (callId, callCreator, linkToken) => {
|
|
546
712
|
const stanza = {
|
|
547
713
|
tag: 'call',
|
|
@@ -564,13 +730,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
564
730
|
await query(stanza)
|
|
565
731
|
}
|
|
566
732
|
|
|
567
|
-
|
|
568
|
-
* Query info about a call link before joining.
|
|
569
|
-
* Source: OutgoingSignalingHandler.java link_query tag
|
|
570
|
-
*
|
|
571
|
-
* @param {string} callLinkCode - The call link code to query
|
|
572
|
-
* @param {string} to - JID to send the query to
|
|
573
|
-
*/
|
|
733
|
+
|
|
574
734
|
const queryCallLink = async (callLinkCode, to) => {
|
|
575
735
|
const stanza = {
|
|
576
736
|
tag: 'call',
|
|
@@ -593,19 +753,19 @@ const makeMessagesRecvSocket = config => {
|
|
|
593
753
|
const { key: msgKey } = fullMessage
|
|
594
754
|
const msgId = msgKey.id
|
|
595
755
|
if (messageRetryManager) {
|
|
596
|
-
|
|
756
|
+
|
|
597
757
|
if (messageRetryManager.hasExceededMaxRetries(msgId)) {
|
|
598
758
|
logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing')
|
|
599
759
|
messageRetryManager.markRetryFailed(msgId)
|
|
600
760
|
return
|
|
601
761
|
}
|
|
602
|
-
|
|
762
|
+
|
|
603
763
|
const retryCount = messageRetryManager.incrementRetryCount(msgId)
|
|
604
|
-
|
|
764
|
+
|
|
605
765
|
const key = `${msgId}:${msgKey?.participant}`
|
|
606
766
|
await msgRetryCache.set(key, retryCount)
|
|
607
767
|
} else {
|
|
608
|
-
|
|
768
|
+
|
|
609
769
|
const key = `${msgId}:${msgKey?.participant}`
|
|
610
770
|
let retryCount = (await msgRetryCache.get(key)) || 0
|
|
611
771
|
if (retryCount >= maxMsgRetryCount) {
|
|
@@ -620,12 +780,12 @@ const makeMessagesRecvSocket = config => {
|
|
|
620
780
|
const retryCount = (await msgRetryCache.get(key)) || 1
|
|
621
781
|
const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds
|
|
622
782
|
const fromJid = node.attrs.from
|
|
623
|
-
|
|
783
|
+
|
|
624
784
|
let shouldRecreateSession = false
|
|
625
785
|
let recreateReason = ''
|
|
626
786
|
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
|
|
627
787
|
try {
|
|
628
|
-
|
|
788
|
+
|
|
629
789
|
const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid)
|
|
630
790
|
const hasSession = await signalRepository.validateSession(fromJid)
|
|
631
791
|
const result = messageRetryManager.shouldRecreateSession(fromJid, hasSession.exists)
|
|
@@ -633,7 +793,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
633
793
|
recreateReason = result.reason
|
|
634
794
|
if (shouldRecreateSession) {
|
|
635
795
|
logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry')
|
|
636
|
-
|
|
796
|
+
|
|
637
797
|
await authState.keys.set({ session: { [sessionId]: null } })
|
|
638
798
|
forceIncludeKeys = true
|
|
639
799
|
}
|
|
@@ -642,9 +802,9 @@ const makeMessagesRecvSocket = config => {
|
|
|
642
802
|
}
|
|
643
803
|
}
|
|
644
804
|
if (retryCount <= 2) {
|
|
645
|
-
|
|
805
|
+
|
|
646
806
|
if (messageRetryManager) {
|
|
647
|
-
|
|
807
|
+
|
|
648
808
|
messageRetryManager.schedulePhoneRequest(msgId, async () => {
|
|
649
809
|
try {
|
|
650
810
|
const requestId = await requestPlaceholderResend(msgKey)
|
|
@@ -656,7 +816,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
656
816
|
}
|
|
657
817
|
})
|
|
658
818
|
} else {
|
|
659
|
-
|
|
819
|
+
|
|
660
820
|
const msgId = await requestPlaceholderResend(msgKey)
|
|
661
821
|
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`)
|
|
662
822
|
}
|
|
@@ -678,8 +838,9 @@ const makeMessagesRecvSocket = config => {
|
|
|
678
838
|
id: node.attrs.id,
|
|
679
839
|
t: node.attrs.t,
|
|
680
840
|
v: '1',
|
|
681
|
-
|
|
682
|
-
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
error: '1'
|
|
683
844
|
}
|
|
684
845
|
},
|
|
685
846
|
{
|
|
@@ -717,17 +878,14 @@ const makeMessagesRecvSocket = config => {
|
|
|
717
878
|
logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt')
|
|
718
879
|
}, authState?.creds?.me?.id || 'sendRetryRequest')
|
|
719
880
|
}
|
|
720
|
-
|
|
721
|
-
* Fire-and-forget tctoken re-issuance after a peer's device identity changed.
|
|
722
|
-
* Runs in parallel with the session refresh (not after it).
|
|
723
|
-
*/
|
|
881
|
+
|
|
724
882
|
const reissueTcTokenAfterIdentityChange = from => {
|
|
725
883
|
void (async () => {
|
|
726
884
|
const normalizedJid = (0, WABinary_1.jidNormalizedUser)(from)
|
|
727
885
|
const tcJid = await (0, tc_token_utils_1.resolveTcTokenJid)(normalizedJid, getLIDForPN)
|
|
728
886
|
const tcTokenData = await authState.keys.get('tctoken', [tcJid])
|
|
729
887
|
const senderTs = tcTokenData?.[tcJid]?.senderTimestamp
|
|
730
|
-
if (senderTs
|
|
888
|
+
if (senderTs == null || (0, tc_token_utils_1.isTcTokenExpired)(senderTs)) {
|
|
731
889
|
return
|
|
732
890
|
}
|
|
733
891
|
logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken')
|
|
@@ -775,8 +933,21 @@ const makeMessagesRecvSocket = config => {
|
|
|
775
933
|
}
|
|
776
934
|
}
|
|
777
935
|
}
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
const storeParticipantLidPairs = participants => {
|
|
939
|
+
const pairs = []
|
|
940
|
+
for (const p of participants || []) {
|
|
941
|
+
if (p.phoneNumber) pairs.push({ lid: p.id, pn: p.phoneNumber })
|
|
942
|
+
else if (p.lid) pairs.push({ lid: p.lid, pn: p.id })
|
|
943
|
+
}
|
|
944
|
+
if (!pairs.length) return
|
|
945
|
+
signalRepository.lidMapping
|
|
946
|
+
.storeLIDPNMappings(pairs)
|
|
947
|
+
.catch(error => logger.debug({ error }, 'failed to store LID/PN mappings from group notification'))
|
|
948
|
+
}
|
|
778
949
|
const handleGroupNotification = (fullNode, child, msg) => {
|
|
779
|
-
|
|
950
|
+
|
|
780
951
|
const actingParticipantLid = fullNode.attrs.participant
|
|
781
952
|
const actingParticipantPn = fullNode.attrs.participant_pn
|
|
782
953
|
const actingParticipantUsername = fullNode.attrs.participant_username
|
|
@@ -790,6 +961,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
790
961
|
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE
|
|
791
962
|
msg.messageStubParameters = [metadata.subject]
|
|
792
963
|
msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn }
|
|
964
|
+
storeParticipantLidPairs(metadata.participants)
|
|
793
965
|
ev.emit('chats.upsert', [
|
|
794
966
|
{
|
|
795
967
|
id: metadata.id,
|
|
@@ -828,7 +1000,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
828
1000
|
const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
|
|
829
1001
|
msg.messageStubType = Types_1.WAMessageStubType[stubType]
|
|
830
1002
|
const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(({ attrs }) => {
|
|
831
|
-
// TODO: Store LID MAPPINGS
|
|
832
1003
|
return {
|
|
833
1004
|
id: attrs.jid,
|
|
834
1005
|
phoneNumber:
|
|
@@ -837,19 +1008,21 @@ const makeMessagesRecvSocket = config => {
|
|
|
837
1008
|
: undefined,
|
|
838
1009
|
lid: (0, WABinary_1.isPnUser)(attrs.jid) && (0, WABinary_1.isLidUser)(attrs.lid) ? attrs.lid : undefined,
|
|
839
1010
|
username: attrs.participant_username || attrs.username || undefined,
|
|
840
|
-
admin: attrs.type || null
|
|
1011
|
+
admin: attrs.type || null,
|
|
1012
|
+
uuid: attrs.uuid || attrs.participant_uuid || undefined
|
|
841
1013
|
}
|
|
842
1014
|
})
|
|
843
1015
|
if (
|
|
844
1016
|
participants.length === 1 &&
|
|
845
|
-
|
|
846
|
-
|
|
1017
|
+
|
|
1018
|
+
|
|
847
1019
|
((0, WABinary_1.areJidsSameUser)(participants[0].id, actingParticipantLid) ||
|
|
848
1020
|
(0, WABinary_1.areJidsSameUser)(participants[0].id, actingParticipantPn)) &&
|
|
849
1021
|
child.tag === 'remove'
|
|
850
1022
|
) {
|
|
851
1023
|
msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE
|
|
852
1024
|
}
|
|
1025
|
+
storeParticipantLidPairs(participants)
|
|
853
1026
|
msg.messageStubParameters = participants.map(a => JSON.stringify(a))
|
|
854
1027
|
break
|
|
855
1028
|
case 'subject':
|
|
@@ -899,13 +1072,51 @@ const makeMessagesRecvSocket = config => {
|
|
|
899
1072
|
break
|
|
900
1073
|
case 'revoked_membership_requests':
|
|
901
1074
|
const isDenied = (0, WABinary_1.areJidsSameUser)(affectedParticipantLid, actingParticipantLid)
|
|
902
|
-
|
|
1075
|
+
if ((0, WABinary_1.isLidUser)(affectedParticipantLid) && (0, WABinary_1.isPnUser)(affectedParticipantPn)) {
|
|
1076
|
+
storeParticipantLidPairs([{ id: affectedParticipantLid, phoneNumber: affectedParticipantPn }])
|
|
1077
|
+
}
|
|
903
1078
|
msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
|
|
904
1079
|
msg.messageStubParameters = [
|
|
905
1080
|
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
|
|
906
1081
|
isDenied ? 'revoked' : 'rejected'
|
|
907
1082
|
]
|
|
908
1083
|
break
|
|
1084
|
+
case 'sibling_link': {
|
|
1085
|
+
const linkedGroupJid = (0, WABinary_1.getBinaryNodeChild)(child, 'group')?.attrs?.jid || child.attrs?.jid
|
|
1086
|
+
ev.emit('groups.update', [
|
|
1087
|
+
{
|
|
1088
|
+
id: fullNode.attrs.from,
|
|
1089
|
+
siblingGroupLinked: linkedGroupJid || true,
|
|
1090
|
+
author: actingParticipantLid,
|
|
1091
|
+
authorPn: actingParticipantPn
|
|
1092
|
+
}
|
|
1093
|
+
])
|
|
1094
|
+
break
|
|
1095
|
+
}
|
|
1096
|
+
case 'sibling_unlink': {
|
|
1097
|
+
const unlinkedGroupJid = (0, WABinary_1.getBinaryNodeChild)(child, 'group')?.attrs?.jid || child.attrs?.jid
|
|
1098
|
+
ev.emit('groups.update', [
|
|
1099
|
+
{
|
|
1100
|
+
id: fullNode.attrs.from,
|
|
1101
|
+
siblingGroupUnlinked: unlinkedGroupJid || true,
|
|
1102
|
+
author: actingParticipantLid,
|
|
1103
|
+
authorPn: actingParticipantPn
|
|
1104
|
+
}
|
|
1105
|
+
])
|
|
1106
|
+
break
|
|
1107
|
+
}
|
|
1108
|
+
case 'clear_history': {
|
|
1109
|
+
const historyClearTimestamp = child.attrs?.t ? +child.attrs.t : (0, Date)()
|
|
1110
|
+
ev.emit('groups.update', [
|
|
1111
|
+
{
|
|
1112
|
+
id: fullNode.attrs.from,
|
|
1113
|
+
historyClearTimestamp,
|
|
1114
|
+
author: actingParticipantLid,
|
|
1115
|
+
authorPn: actingParticipantPn
|
|
1116
|
+
}
|
|
1117
|
+
])
|
|
1118
|
+
break
|
|
1119
|
+
}
|
|
909
1120
|
}
|
|
910
1121
|
}
|
|
911
1122
|
const normalizeNotificationParticipant = async (jid, groupData) => {
|
|
@@ -1039,21 +1250,12 @@ const makeMessagesRecvSocket = config => {
|
|
|
1039
1250
|
}
|
|
1040
1251
|
}
|
|
1041
1252
|
|
|
1042
|
-
|
|
1043
|
-
* Handle incoming interop notifications (type="interop").
|
|
1044
|
-
*
|
|
1045
|
-
* The APK emits these for:
|
|
1046
|
-
* - stella_interop_enabled / stella_ios_enabled → feature-flag toggles
|
|
1047
|
-
* - ig_professional / ig_handle / followers → Instagram profile data updates
|
|
1048
|
-
* - fbid:thread / fbid:devices → Meta thread/device association
|
|
1049
|
-
* - peer_device_presence → interop contact online/offline
|
|
1050
|
-
* - group membership changes in interop groups → add/remove/promote events
|
|
1051
|
-
*/
|
|
1253
|
+
|
|
1052
1254
|
const handleInteropNotification = (node, child) => {
|
|
1053
1255
|
const childTag = child?.tag
|
|
1054
1256
|
const attrs = child?.attrs || {}
|
|
1055
1257
|
|
|
1056
|
-
|
|
1258
|
+
|
|
1057
1259
|
if (childTag === 'feature') {
|
|
1058
1260
|
const feature = attrs.name
|
|
1059
1261
|
const enabled = attrs.value === 'true' || attrs.value === '1'
|
|
@@ -1062,7 +1264,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1062
1264
|
return
|
|
1063
1265
|
}
|
|
1064
1266
|
|
|
1065
|
-
|
|
1267
|
+
|
|
1066
1268
|
if (childTag === 'ig_profile') {
|
|
1067
1269
|
const contactUpdate = {
|
|
1068
1270
|
id: (0, WABinary_1.jidNormalizedUser)(node.attrs.from),
|
|
@@ -1075,26 +1277,28 @@ const makeMessagesRecvSocket = config => {
|
|
|
1075
1277
|
return
|
|
1076
1278
|
}
|
|
1077
1279
|
|
|
1078
|
-
|
|
1280
|
+
|
|
1079
1281
|
if (childTag === 'peer_device_presence') {
|
|
1080
1282
|
const jid = attrs.jid || (0, WABinary_1.jidNormalizedUser)(node.attrs.from)
|
|
1081
1283
|
const presence = attrs.type === 'unavailable' ? 'unavailable' : 'available'
|
|
1082
|
-
|
|
1083
|
-
|
|
1284
|
+
const isIosInterop = !!authState.creds.interopIosEnabled
|
|
1285
|
+
logger.debug({ jid, presence, isIosInterop }, '[interop] peer_device_presence')
|
|
1286
|
+
ev.emit('presence.update', { id: jid, presences: { [jid]: { lastKnownPresence: presence } }, isIosInterop })
|
|
1084
1287
|
return
|
|
1085
1288
|
}
|
|
1086
1289
|
|
|
1087
|
-
|
|
1290
|
+
|
|
1088
1291
|
if (childTag === 'fbid_thread' || childTag === 'fbid_devices') {
|
|
1089
|
-
|
|
1090
|
-
|
|
1292
|
+
const isIosInterop = !!authState.creds.interopIosEnabled
|
|
1293
|
+
logger.debug({ childTag, attrs, from: node.attrs.from, isIosInterop }, '[interop] fbid association update')
|
|
1294
|
+
ev.emit('interop.fbid-update', { type: childTag, jid: node.attrs.from, attrs, isIosInterop })
|
|
1091
1295
|
return
|
|
1092
1296
|
}
|
|
1093
1297
|
|
|
1094
|
-
|
|
1298
|
+
|
|
1095
1299
|
if (childTag === 'participants') {
|
|
1096
1300
|
const groupJid = node.attrs.from
|
|
1097
|
-
const action = attrs.type
|
|
1301
|
+
const action = attrs.type
|
|
1098
1302
|
const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid)
|
|
1099
1303
|
logger.info({ groupJid, action, participants }, '[interop] group participants update')
|
|
1100
1304
|
ev.emit('group-participants.update', { id: groupJid, participants, action })
|
|
@@ -1114,10 +1318,24 @@ const makeMessagesRecvSocket = config => {
|
|
|
1114
1318
|
await handleNewsletterNotification(node)
|
|
1115
1319
|
break
|
|
1116
1320
|
case 'mex':
|
|
1117
|
-
await
|
|
1321
|
+
await handleMexNotification(node)
|
|
1118
1322
|
break
|
|
1119
1323
|
case 'w:gp2':
|
|
1120
|
-
|
|
1324
|
+
if (child?.tag === 'groups_dirty') {
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
const dirtyGroupJids = (0, WABinary_1.getBinaryNodeChildren)(child, 'group')
|
|
1328
|
+
.map(g => g.attrs.jid)
|
|
1329
|
+
.filter(Boolean)
|
|
1330
|
+
if (dirtyGroupJids.length) {
|
|
1331
|
+
ev.emit(
|
|
1332
|
+
'groups.update',
|
|
1333
|
+
dirtyGroupJids.map(jid => ({ id: jid }))
|
|
1334
|
+
)
|
|
1335
|
+
}
|
|
1336
|
+
break
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1121
1339
|
const groupData = await getNotificationGroupData(node)
|
|
1122
1340
|
handleGroupNotification(node, child, result)
|
|
1123
1341
|
|
|
@@ -1130,26 +1348,39 @@ const makeMessagesRecvSocket = config => {
|
|
|
1130
1348
|
case 'encrypt':
|
|
1131
1349
|
await handleEncryptNotification(node)
|
|
1132
1350
|
break
|
|
1133
|
-
case 'devices':
|
|
1134
|
-
|
|
1135
|
-
|
|
1351
|
+
case 'devices': {
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
const addNode = (0, WABinary_1.getBinaryNodeChild)(node, 'add')
|
|
1356
|
+
const removeNode = (0, WABinary_1.getBinaryNodeChild)(node, 'remove')
|
|
1357
|
+
const changedNode = addNode || removeNode
|
|
1358
|
+
if (!changedNode) {
|
|
1359
|
+
logger.debug({ node }, 'devices hash refresh, no add/remove list to report')
|
|
1360
|
+
break
|
|
1361
|
+
}
|
|
1362
|
+
const isAdded = !!addNode
|
|
1363
|
+
const devices = (0, WABinary_1.getBinaryNodeChildren)(changedNode, 'device')
|
|
1364
|
+
const deviceOwnerJid = from
|
|
1136
1365
|
const deviceData = devices.map(d => ({
|
|
1137
1366
|
id: d.attrs.jid,
|
|
1138
1367
|
lid: d.attrs.lid,
|
|
1139
|
-
|
|
1368
|
+
|
|
1369
|
+
keyIndex: d.attrs['key-index'] ? +d.attrs['key-index'] : undefined,
|
|
1140
1370
|
platform: d.attrs.platform || undefined,
|
|
1141
1371
|
isCompanion: d.attrs.companion === 'true' || undefined
|
|
1142
1372
|
}))
|
|
1143
|
-
|
|
1144
|
-
(0, WABinary_1.areJidsSameUser)(
|
|
1145
|
-
(0, WABinary_1.areJidsSameUser)(
|
|
1146
|
-
) {
|
|
1147
|
-
logger.info({ deviceData }, 'my own devices changed')
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
ev.emit('devices.update', { id: deviceOwnerJid, devices: deviceData, isSelf:
|
|
1373
|
+
const isSelf =
|
|
1374
|
+
(0, WABinary_1.areJidsSameUser)(from, authState.creds.me?.id) ||
|
|
1375
|
+
(0, WABinary_1.areJidsSameUser)(from, authState.creds.me?.lid)
|
|
1376
|
+
if (isSelf) {
|
|
1377
|
+
logger.info({ deviceData, isAdded }, 'my own devices changed')
|
|
1378
|
+
}
|
|
1379
|
+
if (deviceOwnerJid) {
|
|
1380
|
+
ev.emit('devices.update', { id: deviceOwnerJid, devices: deviceData, isSelf, added: isAdded })
|
|
1151
1381
|
}
|
|
1152
1382
|
break
|
|
1383
|
+
}
|
|
1153
1384
|
case 'server_sync':
|
|
1154
1385
|
const update = (0, WABinary_1.getBinaryNodeChild)(node, 'collection')
|
|
1155
1386
|
if (update) {
|
|
@@ -1157,13 +1388,18 @@ const makeMessagesRecvSocket = config => {
|
|
|
1157
1388
|
await resyncAppState([name], false)
|
|
1158
1389
|
}
|
|
1159
1390
|
break
|
|
1160
|
-
case 'picture':
|
|
1391
|
+
case 'picture': {
|
|
1161
1392
|
const setPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'set')
|
|
1162
1393
|
const delPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'delete')
|
|
1163
|
-
|
|
1394
|
+
const pictureOwnerJid = (0, WABinary_1.jidNormalizedUser)(node?.attrs?.from)
|
|
1395
|
+
if (!pictureOwnerJid) {
|
|
1396
|
+
|
|
1397
|
+
logger.debug({ node }, 'picture notification missing "from", skipping contacts.update')
|
|
1398
|
+
break
|
|
1399
|
+
}
|
|
1164
1400
|
ev.emit('contacts.update', [
|
|
1165
1401
|
{
|
|
1166
|
-
id:
|
|
1402
|
+
id: pictureOwnerJid,
|
|
1167
1403
|
imgUrl: setPicture ? 'changed' : 'removed'
|
|
1168
1404
|
}
|
|
1169
1405
|
])
|
|
@@ -1180,6 +1416,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1180
1416
|
}
|
|
1181
1417
|
}
|
|
1182
1418
|
break
|
|
1419
|
+
}
|
|
1183
1420
|
case 'account_sync':
|
|
1184
1421
|
if (child.tag === 'disappearing_mode') {
|
|
1185
1422
|
const newDuration = +child.attrs.duration
|
|
@@ -1195,18 +1432,60 @@ const makeMessagesRecvSocket = config => {
|
|
|
1195
1432
|
}
|
|
1196
1433
|
})
|
|
1197
1434
|
} else if (child.tag === 'blocklist') {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
sock.fetchBlocklist().catch(error => logger.warn({ error }, 'failed to refresh blocklist after account_sync'))
|
|
1439
|
+
} else if (child.tag === 'devices') {
|
|
1440
|
+
|
|
1441
|
+
|
|
1442
|
+
const dhash = child.attrs.dhash
|
|
1443
|
+
const deviceNodes = (0, WABinary_1.getBinaryNodeChildren)(child, 'device')
|
|
1444
|
+
const keyIndexListNode = (0, WABinary_1.getBinaryNodeChild)(child, 'key-index-list')
|
|
1445
|
+
const devices = deviceNodes.map(d => ({
|
|
1446
|
+
jid: d.attrs.jid ? String(d.attrs.jid) : undefined,
|
|
1447
|
+
keyIndex: d.attrs['key-index'] ? +d.attrs['key-index'] : undefined
|
|
1448
|
+
}))
|
|
1449
|
+
logger.info({ dhash, deviceCount: devices.length }, 'account devices list synced')
|
|
1450
|
+
ev.emit('account.devices-synced', {
|
|
1451
|
+
dhash,
|
|
1452
|
+
devices,
|
|
1453
|
+
keyIndexListTimestamp: keyIndexListNode?.attrs?.ts ? +keyIndexListNode.attrs.ts : undefined,
|
|
1454
|
+
keyIndexList: keyIndexListNode?.content ? Buffer.from(keyIndexListNode.content) : undefined
|
|
1455
|
+
})
|
|
1456
|
+
}
|
|
1457
|
+
break
|
|
1458
|
+
case 'disappearing_mode': {
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
const newDuration = +child.attrs.duration
|
|
1462
|
+
const timestamp = +child.attrs.t
|
|
1463
|
+
logger.info({ newDuration }, 'updated account disappearing mode')
|
|
1464
|
+
ev.emit('creds.update', {
|
|
1465
|
+
accountSettings: {
|
|
1466
|
+
...authState.creds.accountSettings,
|
|
1467
|
+
defaultDisappearingMode: {
|
|
1468
|
+
ephemeralExpiration: newDuration,
|
|
1469
|
+
ephemeralSettingTimestamp: timestamp
|
|
1470
|
+
}
|
|
1203
1471
|
}
|
|
1472
|
+
})
|
|
1473
|
+
break
|
|
1474
|
+
}
|
|
1475
|
+
case 'contacts': {
|
|
1476
|
+
|
|
1477
|
+
|
|
1478
|
+
const updateChild = (0, WABinary_1.getBinaryNodeChild)(node, 'update')
|
|
1479
|
+
if (updateChild?.attrs?.jid) {
|
|
1480
|
+
ev.emit('contacts.update', [{ id: (0, WABinary_1.jidNormalizedUser)(updateChild.attrs.jid) }])
|
|
1481
|
+
} else {
|
|
1482
|
+
logger.debug({ node }, 'contacts list changed (hash/sync signal), no per-contact jid to update')
|
|
1204
1483
|
}
|
|
1205
1484
|
break
|
|
1485
|
+
}
|
|
1206
1486
|
case 'business':
|
|
1207
|
-
// SMB privacy / data-sharing settings sync push
|
|
1208
|
-
// (WhatsApp Web: WASmaxInBizSettingsSyncPrivacySettingRequest)
|
|
1209
1487
|
if (child?.tag === 'privacy') {
|
|
1488
|
+
|
|
1210
1489
|
ev.emit('business.privacy-settings-sync', {
|
|
1211
1490
|
jid: from,
|
|
1212
1491
|
categories: (0, WABinary_1.getBinaryNodeChildren)(child, 'category').map(c => ({
|
|
@@ -1215,11 +1494,37 @@ const makeMessagesRecvSocket = config => {
|
|
|
1215
1494
|
})),
|
|
1216
1495
|
attrs: child.attrs
|
|
1217
1496
|
})
|
|
1497
|
+
} else if (child?.tag === 'profile') {
|
|
1498
|
+
|
|
1499
|
+
ev.emit('contacts.update', [
|
|
1500
|
+
{
|
|
1501
|
+
id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid || from),
|
|
1502
|
+
businessProfileTag: child.attrs.tag
|
|
1503
|
+
}
|
|
1504
|
+
])
|
|
1505
|
+
} else if (child?.tag === 'verified_name') {
|
|
1506
|
+
|
|
1507
|
+
ev.emit('contacts.update', [
|
|
1508
|
+
{
|
|
1509
|
+
id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid || from),
|
|
1510
|
+
verifiedName: {
|
|
1511
|
+
verifiedLevel: child.attrs.verified_level,
|
|
1512
|
+
serial: child.attrs.serial,
|
|
1513
|
+
version: child.attrs.v
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
])
|
|
1517
|
+
} else if (child?.tag === 'remove') {
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
if (child.attrs.jid) {
|
|
1521
|
+
ev.emit('contacts.update', [{ id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid), isBusiness: false }])
|
|
1522
|
+
}
|
|
1218
1523
|
}
|
|
1219
1524
|
break
|
|
1220
1525
|
case 'hosted':
|
|
1221
|
-
|
|
1222
|
-
|
|
1526
|
+
|
|
1527
|
+
|
|
1223
1528
|
if (child?.tag === 'onboarding_status') {
|
|
1224
1529
|
ev.emit('coexistence.update', {
|
|
1225
1530
|
jid: from,
|
|
@@ -1237,18 +1542,28 @@ const makeMessagesRecvSocket = config => {
|
|
|
1237
1542
|
break
|
|
1238
1543
|
case 'link_code_companion_reg':
|
|
1239
1544
|
const linkCodeCompanionReg = (0, WABinary_1.getBinaryNodeChild)(node, 'link_code_companion_reg')
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1545
|
+
const refBuffer = (0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_ref')
|
|
1546
|
+
const primaryIdentityPublicKeyBuffer = (0, WABinary_1.getBinaryNodeChildBuffer)(
|
|
1547
|
+
linkCodeCompanionReg,
|
|
1548
|
+
'primary_identity_pub'
|
|
1242
1549
|
)
|
|
1243
|
-
const
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(
|
|
1247
|
-
(0, WABinary_1.getBinaryNodeChildBuffer)(
|
|
1248
|
-
linkCodeCompanionReg,
|
|
1249
|
-
'link_code_pairing_wrapped_primary_ephemeral_pub'
|
|
1250
|
-
)
|
|
1550
|
+
const primaryEphemeralPublicKeyWrappedBuffer = (0, WABinary_1.getBinaryNodeChildBuffer)(
|
|
1551
|
+
linkCodeCompanionReg,
|
|
1552
|
+
'link_code_pairing_wrapped_primary_ephemeral_pub'
|
|
1251
1553
|
)
|
|
1554
|
+
if (
|
|
1555
|
+
refBuffer === undefined ||
|
|
1556
|
+
primaryIdentityPublicKeyBuffer === undefined ||
|
|
1557
|
+
primaryEphemeralPublicKeyWrappedBuffer === undefined
|
|
1558
|
+
) {
|
|
1559
|
+
|
|
1560
|
+
|
|
1561
|
+
logger.debug({ id: node.attrs.id, type: node.attrs.type }, 'skipping empty link code companion registration')
|
|
1562
|
+
break
|
|
1563
|
+
}
|
|
1564
|
+
const ref = toRequiredBuffer(refBuffer)
|
|
1565
|
+
const primaryIdentityPublicKey = toRequiredBuffer(primaryIdentityPublicKeyBuffer)
|
|
1566
|
+
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(primaryEphemeralPublicKeyWrappedBuffer)
|
|
1252
1567
|
const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped)
|
|
1253
1568
|
const companionSharedKey = Utils_1.Curve.sharedKey(
|
|
1254
1569
|
authState.creds.pairingEphemeralKeyPair.private,
|
|
@@ -1323,7 +1638,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1323
1638
|
await handlePrivacyTokenNotification(node)
|
|
1324
1639
|
break
|
|
1325
1640
|
case 'security':
|
|
1326
|
-
|
|
1641
|
+
|
|
1327
1642
|
const securityType = child?.tag || node.attrs.type
|
|
1328
1643
|
const securityData = {
|
|
1329
1644
|
type: securityType,
|
|
@@ -1335,7 +1650,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1335
1650
|
ev.emit('security.alert', securityData)
|
|
1336
1651
|
break
|
|
1337
1652
|
case 'identity':
|
|
1338
|
-
|
|
1653
|
+
|
|
1339
1654
|
const identityJid = node.attrs.from
|
|
1340
1655
|
const identityNewKey = child?.content ? Buffer.from(child.content) : undefined
|
|
1341
1656
|
ev.emit('identity.update', {
|
|
@@ -1345,7 +1660,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1345
1660
|
})
|
|
1346
1661
|
break
|
|
1347
1662
|
case 'server':
|
|
1348
|
-
|
|
1663
|
+
|
|
1349
1664
|
const serverTag = child?.tag
|
|
1350
1665
|
if (serverTag === 'config') {
|
|
1351
1666
|
const configData = {}
|
|
@@ -1354,7 +1669,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1354
1669
|
}
|
|
1355
1670
|
ev.emit('server.config', configData)
|
|
1356
1671
|
} else if (serverTag === 'app_state_key') {
|
|
1357
|
-
|
|
1672
|
+
|
|
1358
1673
|
logger.info('server pushed app state key update')
|
|
1359
1674
|
await resyncAppState(['critical_block', 'regular_high', 'regular_low'], false)
|
|
1360
1675
|
} else {
|
|
@@ -1362,7 +1677,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1362
1677
|
}
|
|
1363
1678
|
break
|
|
1364
1679
|
case 'status':
|
|
1365
|
-
|
|
1680
|
+
|
|
1366
1681
|
const statusOwner = node.attrs.from
|
|
1367
1682
|
const statusText = child?.content ? child.content.toString() : undefined
|
|
1368
1683
|
if (statusOwner && statusText !== undefined) {
|
|
@@ -1375,7 +1690,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1375
1690
|
}
|
|
1376
1691
|
break
|
|
1377
1692
|
case 'usync':
|
|
1378
|
-
|
|
1693
|
+
|
|
1379
1694
|
const usyncResults = (0, WABinary_1.getBinaryNodeChildren)(child || node, 'user')
|
|
1380
1695
|
if (usyncResults.length) {
|
|
1381
1696
|
const updates = usyncResults
|
|
@@ -1392,12 +1707,12 @@ const makeMessagesRecvSocket = config => {
|
|
|
1392
1707
|
}
|
|
1393
1708
|
break
|
|
1394
1709
|
case 'interop':
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1710
|
+
|
|
1711
|
+
|
|
1712
|
+
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
|
|
1401
1716
|
handleInteropNotification(node, child)
|
|
1402
1717
|
break
|
|
1403
1718
|
}
|
|
@@ -1405,10 +1720,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1405
1720
|
return result
|
|
1406
1721
|
}
|
|
1407
1722
|
}
|
|
1408
|
-
|
|
1409
|
-
* In-memory cache of storage JIDs with stored tctokens, seeded from the persisted index.
|
|
1410
|
-
* Used to coalesce writes during a session; pruning always re-reads the persisted index.
|
|
1411
|
-
*/
|
|
1723
|
+
|
|
1412
1724
|
const tcTokenKnownJids = new Set()
|
|
1413
1725
|
const tcTokenIndexLoaded = (async () => {
|
|
1414
1726
|
try {
|
|
@@ -1449,7 +1761,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1449
1761
|
const tokensNode = (0, WABinary_1.getBinaryNodeChild)(node, 'tokens')
|
|
1450
1762
|
if (!tokensNode) return
|
|
1451
1763
|
const from = (0, WABinary_1.jidNormalizedUser)(node.attrs.from)
|
|
1452
|
-
|
|
1764
|
+
|
|
1453
1765
|
const senderLid =
|
|
1454
1766
|
node.attrs.sender_lid && (0, WABinary_1.isLidUser)((0, WABinary_1.jidNormalizedUser)(node.attrs.sender_lid))
|
|
1455
1767
|
? (0, WABinary_1.jidNormalizedUser)(node.attrs.sender_lid)
|
|
@@ -1488,31 +1800,30 @@ const makeMessagesRecvSocket = config => {
|
|
|
1488
1800
|
const newValue = ((await msgRetryCache.get(key)) || 0) + 1
|
|
1489
1801
|
await msgRetryCache.set(key, newValue)
|
|
1490
1802
|
}
|
|
1491
|
-
const sendMessagesAgain = async (key, ids, retryNode
|
|
1803
|
+
const sendMessagesAgain = async (key, ids, retryNode) => {
|
|
1492
1804
|
const remoteJid = key.remoteJid
|
|
1493
1805
|
const participant = key.participant || remoteJid
|
|
1494
1806
|
const retryCount = +retryNode.attrs.count || 1
|
|
1495
|
-
|
|
1496
|
-
// Try to get messages from cache first, then fallback to getMessage
|
|
1807
|
+
|
|
1497
1808
|
const msgs = []
|
|
1498
1809
|
for (const id of ids) {
|
|
1499
1810
|
let msg
|
|
1500
|
-
|
|
1811
|
+
|
|
1501
1812
|
if (messageRetryManager) {
|
|
1502
1813
|
const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id)
|
|
1503
1814
|
if (cachedMsg) {
|
|
1504
1815
|
msg = cachedMsg.message
|
|
1505
1816
|
logger.debug({ jid: remoteJid, id }, 'found message in retry cache')
|
|
1506
|
-
|
|
1817
|
+
|
|
1507
1818
|
messageRetryManager.markRetrySuccess(id)
|
|
1508
1819
|
}
|
|
1509
1820
|
}
|
|
1510
|
-
|
|
1821
|
+
|
|
1511
1822
|
if (!msg) {
|
|
1512
1823
|
msg = await getMessage({ ...key, id })
|
|
1513
1824
|
if (msg) {
|
|
1514
1825
|
logger.debug({ jid: remoteJid, id }, 'found message via getMessage')
|
|
1515
|
-
|
|
1826
|
+
|
|
1516
1827
|
if (messageRetryManager) {
|
|
1517
1828
|
messageRetryManager.markRetrySuccess(id)
|
|
1518
1829
|
}
|
|
@@ -1520,57 +1831,16 @@ const makeMessagesRecvSocket = config => {
|
|
|
1520
1831
|
}
|
|
1521
1832
|
msgs.push(msg)
|
|
1522
1833
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1834
|
+
|
|
1835
|
+
|
|
1836
|
+
|
|
1526
1837
|
const sendToAll = !(0, WABinary_1.jidDecode)(participant)?.device
|
|
1527
|
-
|
|
1528
|
-
let injectedFromBundle = false
|
|
1529
|
-
if (typeof signalRepository.injectE2ESession === 'function' && typeof Utils_1.extractE2ESessionFromRetryReceipt === 'function') {
|
|
1530
|
-
const bundle = Utils_1.extractE2ESessionFromRetryReceipt(receiptNode)
|
|
1531
|
-
if (bundle) {
|
|
1532
|
-
try {
|
|
1533
|
-
await signalRepository.injectE2ESession({ jid: participant, session: bundle })
|
|
1534
|
-
injectedFromBundle = true
|
|
1535
|
-
logger.debug({ participant, retryCount }, 'injected session from retry receipt key bundle')
|
|
1536
|
-
} catch (error) {
|
|
1537
|
-
logger.warn({ error, participant }, 'failed to inject session from retry receipt')
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
}
|
|
1541
|
-
if (!injectedFromBundle && signalRepository.getSessionInfo) {
|
|
1542
|
-
const receivedRegId = (0, WABinary_1.getBinaryNodeChildUInt)(receiptNode, 'registration', 4)
|
|
1543
|
-
if (typeof receivedRegId === 'number' && Number.isInteger(receivedRegId)) {
|
|
1544
|
-
const info = await signalRepository.getSessionInfo(participant)
|
|
1545
|
-
if (info && info.registrationId !== 0 && info.registrationId !== receivedRegId) {
|
|
1546
|
-
logger.info(
|
|
1547
|
-
{ participant, stored: info.registrationId, received: receivedRegId },
|
|
1548
|
-
'reg id mismatch on retry without bundle, deleting session'
|
|
1549
|
-
)
|
|
1550
|
-
await authState.keys.set({ session: { [sessionId]: null } })
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
const BASE_KEY_CHECK_RETRY = 2
|
|
1555
|
-
if (msgId && messageRetryManager && signalRepository.getSessionInfo) {
|
|
1556
|
-
const info = await signalRepository.getSessionInfo(participant)
|
|
1557
|
-
if (info) {
|
|
1558
|
-
if (retryCount === BASE_KEY_CHECK_RETRY) {
|
|
1559
|
-
messageRetryManager.saveBaseKey(sessionId, msgId, info.baseKey)
|
|
1560
|
-
} else if (retryCount > BASE_KEY_CHECK_RETRY) {
|
|
1561
|
-
if (messageRetryManager.hasSameBaseKey(sessionId, msgId, info.baseKey)) {
|
|
1562
|
-
logger.warn({ participant, retryCount }, 'base key collision on retry, forcing fresh session')
|
|
1563
|
-
await authState.keys.set({ session: { [sessionId]: null } })
|
|
1564
|
-
}
|
|
1565
|
-
messageRetryManager.deleteBaseKey(sessionId, msgId)
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
// Check if we should recreate session for this retry
|
|
1838
|
+
|
|
1570
1839
|
let shouldRecreateSession = false
|
|
1571
1840
|
let recreateReason = ''
|
|
1572
|
-
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1
|
|
1841
|
+
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
|
|
1573
1842
|
try {
|
|
1843
|
+
const sessionId = signalRepository.jidToSignalProtocolAddress(participant)
|
|
1574
1844
|
const hasSession = await signalRepository.validateSession(participant)
|
|
1575
1845
|
const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists)
|
|
1576
1846
|
shouldRecreateSession = result.recreate
|
|
@@ -1583,9 +1853,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1583
1853
|
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry')
|
|
1584
1854
|
}
|
|
1585
1855
|
}
|
|
1586
|
-
|
|
1587
|
-
await assertSessions([participant], true)
|
|
1588
|
-
}
|
|
1856
|
+
await assertSessions([participant], true)
|
|
1589
1857
|
if ((0, WABinary_1.isJidGroup)(remoteJid)) {
|
|
1590
1858
|
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } })
|
|
1591
1859
|
}
|
|
@@ -1634,14 +1902,59 @@ const makeMessagesRecvSocket = config => {
|
|
|
1634
1902
|
const items = (0, WABinary_1.getBinaryNodeChildren)(content[0], 'item')
|
|
1635
1903
|
ids.push(...items.map(i => i.attrs.id))
|
|
1636
1904
|
}
|
|
1905
|
+
|
|
1906
|
+
|
|
1907
|
+
if (attrs.type === 'media-retry') {
|
|
1908
|
+
const mediaRetryNode =
|
|
1909
|
+
(0, WABinary_1.getBinaryNodeChild)(node, 'media-retry') ||
|
|
1910
|
+
(0, WABinary_1.getBinaryNodeChild)(node, 'media_retry')
|
|
1911
|
+
ev.emit('messages.media-retry', {
|
|
1912
|
+
ids,
|
|
1913
|
+
from: attrs.from,
|
|
1914
|
+
participant: attrs.participant,
|
|
1915
|
+
t: attrs.t,
|
|
1916
|
+
retryAttrs: mediaRetryNode?.attrs
|
|
1917
|
+
})
|
|
1918
|
+
await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack media-retry receipt'))
|
|
1919
|
+
return
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
|
|
1923
|
+
if (attrs.type === 'server-error') {
|
|
1924
|
+
const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error')
|
|
1925
|
+
ev.emit('messages.server-error', {
|
|
1926
|
+
ids,
|
|
1927
|
+
from: attrs.from,
|
|
1928
|
+
participant: attrs.participant,
|
|
1929
|
+
t: attrs.t,
|
|
1930
|
+
errorCode: errorNode?.attrs?.code || attrs.error_code,
|
|
1931
|
+
errorText: errorNode?.attrs?.text || errorNode?.content?.toString?.()
|
|
1932
|
+
})
|
|
1933
|
+
await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack server-error receipt'))
|
|
1934
|
+
return
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
if (attrs.receipt_agg) {
|
|
1940
|
+
ev.emit('receipt.batched', {
|
|
1941
|
+
ids,
|
|
1942
|
+
from: attrs.from,
|
|
1943
|
+
participant: attrs.participant,
|
|
1944
|
+
type: attrs.type,
|
|
1945
|
+
t: attrs.t,
|
|
1946
|
+
receiptAgg: attrs.receipt_agg
|
|
1947
|
+
})
|
|
1948
|
+
|
|
1949
|
+
}
|
|
1637
1950
|
try {
|
|
1638
1951
|
await Promise.all([
|
|
1639
1952
|
receiptMutex.mutex(async () => {
|
|
1640
1953
|
const status = (0, Utils_1.getStatusFromReceiptType)(attrs.type)
|
|
1641
1954
|
if (
|
|
1642
1955
|
typeof status !== 'undefined' &&
|
|
1643
|
-
|
|
1644
|
-
|
|
1956
|
+
|
|
1957
|
+
|
|
1645
1958
|
(status >= index_js_1.proto.WebMessageInfo.Status.SERVER_ACK || !isNodeFromMe)
|
|
1646
1959
|
) {
|
|
1647
1960
|
if ((0, WABinary_1.isJidGroup)(remoteJid) || (0, WABinary_1.isJidStatusBroadcast)(remoteJid)) {
|
|
@@ -1670,7 +1983,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1670
1983
|
}
|
|
1671
1984
|
}
|
|
1672
1985
|
if (attrs.type === 'retry') {
|
|
1673
|
-
|
|
1986
|
+
|
|
1674
1987
|
key.participant = key.participant || attrs.from
|
|
1675
1988
|
const retryNode = (0, WABinary_1.getBinaryNodeChild)(node, 'retry')
|
|
1676
1989
|
if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
|
|
@@ -1678,7 +1991,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1678
1991
|
try {
|
|
1679
1992
|
await updateSendMessageAgainCount(ids[0], key.participant)
|
|
1680
1993
|
logger.debug({ attrs, key }, 'recv retry request')
|
|
1681
|
-
await sendMessagesAgain(key, ids, retryNode
|
|
1994
|
+
await sendMessagesAgain(key, ids, retryNode)
|
|
1682
1995
|
} catch (error) {
|
|
1683
1996
|
logger.error(
|
|
1684
1997
|
{ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' },
|
|
@@ -1777,11 +2090,11 @@ const makeMessagesRecvSocket = config => {
|
|
|
1777
2090
|
const groupJid = node.attrs.from
|
|
1778
2091
|
const communityJid = linkedParentMap[groupJid]
|
|
1779
2092
|
const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc')
|
|
1780
|
-
|
|
2093
|
+
|
|
1781
2094
|
if (encNode?.attrs.type === 'msmsg') {
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
2095
|
+
|
|
2096
|
+
|
|
2097
|
+
|
|
1785
2098
|
if (getMessage) {
|
|
1786
2099
|
const metaNode = (0, WABinary_1.getBinaryNodeChild)(node, 'meta')
|
|
1787
2100
|
const targetId = metaNode?.attrs?.target_id
|
|
@@ -1819,7 +2132,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1819
2132
|
)
|
|
1820
2133
|
}
|
|
1821
2134
|
const alt = msg.key.participantAlt || msg.key.remoteJidAlt
|
|
1822
|
-
|
|
2135
|
+
|
|
1823
2136
|
if (!!alt) {
|
|
1824
2137
|
const altServer = (0, WABinary_1.jidDecode)(alt)?.server
|
|
1825
2138
|
const primaryJid = msg.key.participant || msg.key.remoteJid
|
|
@@ -1832,6 +2145,32 @@ const makeMessagesRecvSocket = config => {
|
|
|
1832
2145
|
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }])
|
|
1833
2146
|
await signalRepository.migrateSession(alt, primaryJid)
|
|
1834
2147
|
}
|
|
2148
|
+
} else if (msg.key.addressingMode === 'lid') {
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
|
|
2153
|
+
const primaryJid = msg.key.participant || msg.key.remoteJid
|
|
2154
|
+
if ((0, WABinary_1.isLidUser)(primaryJid)) {
|
|
2155
|
+
try {
|
|
2156
|
+
const pn = await signalRepository.lidMapping.getPNForLID(primaryJid)
|
|
2157
|
+
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
const pnJid = pn ? (0, WABinary_1.jidNormalizedUser)(pn) : ''
|
|
2161
|
+
if (pnJid) {
|
|
2162
|
+
if ((0, WABinary_1.isJidGroup)(msg.key.remoteJid)) {
|
|
2163
|
+
msg.key.participantAlt = pnJid
|
|
2164
|
+
} else {
|
|
2165
|
+
msg.key.remoteJidAlt = pnJid
|
|
2166
|
+
}
|
|
2167
|
+
logger.debug({ lid: primaryJid, pn: pnJid }, 'recovered alt JID from LID mapping store')
|
|
2168
|
+
}
|
|
2169
|
+
} catch (err) {
|
|
2170
|
+
|
|
2171
|
+
logger.warn({ err, lid: primaryJid }, 'failed to recover alt JID from LID mapping store')
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
1835
2174
|
}
|
|
1836
2175
|
await messageMutex.mutex(async () => {
|
|
1837
2176
|
await decrypt()
|
|
@@ -1852,7 +2191,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1852
2191
|
if (msg.key?.remoteJid && msg.key?.id && msg.message && messageRetryManager) {
|
|
1853
2192
|
messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message)
|
|
1854
2193
|
}
|
|
1855
|
-
|
|
2194
|
+
|
|
1856
2195
|
if (msg.messageStubType === index_js_1.proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
|
|
1857
2196
|
if (msg?.messageStubParameters?.[0] === Utils_1.MISSING_KEYS_ERROR_TEXT) {
|
|
1858
2197
|
if (isInteropNode) logger.warn({ id: msg.key.id }, '[interop] decrypt failed: MISSING_KEYS')
|
|
@@ -1860,8 +2199,8 @@ const makeMessagesRecvSocket = config => {
|
|
|
1860
2199
|
return sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError)
|
|
1861
2200
|
}
|
|
1862
2201
|
if (msg.messageStubParameters?.[0] === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
1863
|
-
|
|
1864
|
-
|
|
2202
|
+
|
|
2203
|
+
|
|
1865
2204
|
const unavailableNode = (0, WABinary_1.getBinaryNodeChild)(node, 'unavailable')
|
|
1866
2205
|
const unavailableType = unavailableNode?.attrs?.type
|
|
1867
2206
|
if (
|
|
@@ -1882,18 +2221,18 @@ const makeMessagesRecvSocket = config => {
|
|
|
1882
2221
|
acked = true
|
|
1883
2222
|
return sendMessageAck(node)
|
|
1884
2223
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2224
|
+
|
|
2225
|
+
|
|
2226
|
+
|
|
2227
|
+
|
|
1889
2228
|
const cleanKey = {
|
|
1890
2229
|
remoteJid: msg.key.remoteJid,
|
|
1891
2230
|
fromMe: msg.key.fromMe,
|
|
1892
2231
|
id: msg.key.id,
|
|
1893
2232
|
participant: msg.key.participant
|
|
1894
2233
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
2234
|
+
|
|
2235
|
+
|
|
1897
2236
|
const msgData = {
|
|
1898
2237
|
key: msg.key,
|
|
1899
2238
|
messageTimestamp: msg.messageTimestamp,
|
|
@@ -1918,9 +2257,9 @@ const makeMessagesRecvSocket = config => {
|
|
|
1918
2257
|
})
|
|
1919
2258
|
acked = true
|
|
1920
2259
|
await sendMessageAck(node)
|
|
1921
|
-
|
|
2260
|
+
|
|
1922
2261
|
} else {
|
|
1923
|
-
|
|
2262
|
+
|
|
1924
2263
|
if ((0, WABinary_1.isJidStatusBroadcast)(msg.key.remoteJid)) {
|
|
1925
2264
|
const messageAge = (0, Utils_1.unixTimestampSeconds)() - (0, Utils_1.toNumber)(msg.messageTimestamp)
|
|
1926
2265
|
if (messageAge > Defaults_1.STATUS_EXPIRY_SECONDS) {
|
|
@@ -1935,14 +2274,14 @@ const makeMessagesRecvSocket = config => {
|
|
|
1935
2274
|
const errorMessage = msg?.messageStubParameters?.[0] || ''
|
|
1936
2275
|
const isPreKeyError = errorMessage.includes('PreKey')
|
|
1937
2276
|
logger.debug(`[handleMessage] Attempting retry request for failed decryption`)
|
|
1938
|
-
|
|
2277
|
+
|
|
1939
2278
|
await retryMutex.mutex(async () => {
|
|
1940
2279
|
try {
|
|
1941
2280
|
if (!ws.isOpen) {
|
|
1942
2281
|
logger.debug({ node }, 'Connection closed, skipping retry')
|
|
1943
2282
|
return
|
|
1944
2283
|
}
|
|
1945
|
-
|
|
2284
|
+
|
|
1946
2285
|
if (isPreKeyError) {
|
|
1947
2286
|
logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying')
|
|
1948
2287
|
try {
|
|
@@ -1961,7 +2300,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1961
2300
|
}
|
|
1962
2301
|
} catch (err) {
|
|
1963
2302
|
logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry')
|
|
1964
|
-
|
|
2303
|
+
|
|
1965
2304
|
try {
|
|
1966
2305
|
const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc')
|
|
1967
2306
|
await sendRetryRequest(node, !encNode)
|
|
@@ -1979,34 +2318,34 @@ const makeMessagesRecvSocket = config => {
|
|
|
1979
2318
|
}
|
|
1980
2319
|
const isNewsletter = (0, WABinary_1.isJidNewsletter)(msg.key.remoteJid)
|
|
1981
2320
|
if (!isNewsletter) {
|
|
1982
|
-
|
|
2321
|
+
|
|
1983
2322
|
let type = undefined
|
|
1984
2323
|
let participant = msg.key.participant
|
|
1985
2324
|
if (communityJid) {
|
|
1986
2325
|
msg.communityJid = communityJid
|
|
1987
2326
|
}
|
|
1988
2327
|
if (category === 'peer') {
|
|
1989
|
-
|
|
2328
|
+
|
|
1990
2329
|
type = 'peer_msg'
|
|
1991
2330
|
} else if (msg.key.fromMe) {
|
|
1992
|
-
|
|
2331
|
+
|
|
1993
2332
|
type = 'sender'
|
|
1994
|
-
|
|
2333
|
+
|
|
1995
2334
|
if ((0, WABinary_1.isLidUser)(msg.key.remoteJid) || (0, WABinary_1.isLidUser)(msg.key.remoteJidAlt)) {
|
|
1996
|
-
participant = author
|
|
2335
|
+
participant = author
|
|
1997
2336
|
}
|
|
1998
2337
|
} else if (!sendActiveReceipts) {
|
|
1999
2338
|
type = 'inactive'
|
|
2000
2339
|
}
|
|
2001
2340
|
acked = true
|
|
2002
|
-
|
|
2341
|
+
|
|
2003
2342
|
const interopSts = (0, WABinary_1.isInteropUser)(msg.key.remoteJid) ? node.attrs.sts : undefined
|
|
2004
2343
|
await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type, interopSts)
|
|
2005
|
-
|
|
2344
|
+
|
|
2006
2345
|
const isAnyHistoryMsg = (0, Utils_1.getHistoryMsg)(msg.message)
|
|
2007
2346
|
if (isAnyHistoryMsg) {
|
|
2008
2347
|
const jid = (0, WABinary_1.jidNormalizedUser)(msg.key.remoteJid)
|
|
2009
|
-
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync')
|
|
2348
|
+
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync')
|
|
2010
2349
|
}
|
|
2011
2350
|
} else {
|
|
2012
2351
|
acked = true
|
|
@@ -2068,6 +2407,19 @@ const makeMessagesRecvSocket = config => {
|
|
|
2068
2407
|
}
|
|
2069
2408
|
}
|
|
2070
2409
|
}
|
|
2410
|
+
|
|
2411
|
+
|
|
2412
|
+
|
|
2413
|
+
const parseGroupCallRoster = groupInfoNode =>
|
|
2414
|
+
(0, WABinary_1.getBinaryNodeChildren)(groupInfoNode, 'user').map(userNode => ({
|
|
2415
|
+
jid: userNode.attrs.jid,
|
|
2416
|
+
state: userNode.attrs.state,
|
|
2417
|
+
userPn: userNode.attrs.user_pn,
|
|
2418
|
+
devices: (0, WABinary_1.getBinaryNodeChildren)(userNode, 'device').map(d => ({
|
|
2419
|
+
jid: d.attrs.jid,
|
|
2420
|
+
platform: d.attrs.platform
|
|
2421
|
+
}))
|
|
2422
|
+
}))
|
|
2071
2423
|
const handleCall = async node => {
|
|
2072
2424
|
try {
|
|
2073
2425
|
const { attrs } = node
|
|
@@ -2095,20 +2447,88 @@ const makeMessagesRecvSocket = config => {
|
|
|
2095
2447
|
}
|
|
2096
2448
|
}
|
|
2097
2449
|
if (status === 'offer') {
|
|
2098
|
-
|
|
2450
|
+
const videoNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'video')
|
|
2451
|
+
const audioNodes = (0, WABinary_1.getBinaryNodeChildren)(infoChild, 'audio')
|
|
2452
|
+
const silenceNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'silence')
|
|
2453
|
+
call.isVideo = !!videoNode
|
|
2099
2454
|
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid']
|
|
2100
2455
|
call.groupJid = infoChild.attrs['group-jid']
|
|
2456
|
+
|
|
2457
|
+
call.isLightweight = infoChild.attrs.lightweight === '1'
|
|
2458
|
+
if (silenceNode?.attrs?.reason) {
|
|
2459
|
+
call.silenceReason = silenceNode.attrs.reason
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
if (audioNodes.length) {
|
|
2464
|
+
call.audioCodecs = audioNodes.map(n => ({ enc: n.attrs.enc, rate: n.attrs.rate ? +n.attrs.rate : undefined }))
|
|
2465
|
+
call.audioCodec = audioNodes[0].attrs.enc
|
|
2466
|
+
}
|
|
2467
|
+
if (videoNode?.attrs?.dec) {
|
|
2468
|
+
call.videoCodec = videoNode.attrs.dec
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
const encNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc')
|
|
2472
|
+
if (encNode?.content) {
|
|
2473
|
+
try {
|
|
2474
|
+
const encType = encNode.attrs.type || 'msg'
|
|
2475
|
+
const ciphertext = Buffer.isBuffer(encNode.content) ? encNode.content : Buffer.from(encNode.content)
|
|
2476
|
+
const decrypted = await signalRepository.decryptMessage({ jid: attrs.from, type: encType, ciphertext })
|
|
2477
|
+
const unpadded = (0, Utils_1.unpadRandomMax16)(decrypted)
|
|
2478
|
+
const callMsg = index_js_1.proto.Message.decode(unpadded)
|
|
2479
|
+
if (callMsg?.call?.callKey?.length) {
|
|
2480
|
+
call.callKey = Buffer.from(callMsg.call.callKey)
|
|
2481
|
+
}
|
|
2482
|
+
} catch (e) {
|
|
2483
|
+
logger.debug({ e }, 'failed to decrypt call enc')
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2101
2486
|
await callOfferCache.set(call.id, call)
|
|
2487
|
+
} else if (status === 'waiting_room_request') {
|
|
2488
|
+
call.peerJid = infoChild.attrs.from || infoChild.attrs['peer-jid'] || infoChild.attrs['user-jid']
|
|
2102
2489
|
}
|
|
2103
2490
|
const existingCall = await callOfferCache.get(call.id)
|
|
2104
|
-
|
|
2491
|
+
|
|
2105
2492
|
if (existingCall) {
|
|
2106
2493
|
call.isVideo = existingCall.isVideo
|
|
2107
2494
|
call.isGroup = existingCall.isGroup
|
|
2108
2495
|
call.callerPn = call.callerPn || existingCall.callerPn
|
|
2109
2496
|
}
|
|
2110
|
-
|
|
2111
|
-
if (status === '
|
|
2497
|
+
|
|
2498
|
+
if (status === 'peer_state') {
|
|
2499
|
+
const stateChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'peer_state')
|
|
2500
|
+
call.state = stateChild?.attrs?.state || infoChild.attrs?.state
|
|
2501
|
+
} else if (status === 'group_info') {
|
|
2502
|
+
call.payload = infoChild.attrs
|
|
2503
|
+
call.participants = parseGroupCallRoster(infoChild)
|
|
2504
|
+
} else if (status === 'video_state') {
|
|
2505
|
+
const vsChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'video_state')
|
|
2506
|
+
const enabledRaw = vsChild?.attrs?.enabled ?? infoChild.attrs?.enabled
|
|
2507
|
+
call.enabled = enabledRaw === 'true' || enabledRaw === '1'
|
|
2508
|
+
} else if (status === 'enc_rekey') {
|
|
2509
|
+
const rekeyChild =
|
|
2510
|
+
(0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc-rekey') ||
|
|
2511
|
+
(0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc_rekey')
|
|
2512
|
+
if (rekeyChild?.content && Buffer.isBuffer(rekeyChild.content)) {
|
|
2513
|
+
try {
|
|
2514
|
+
call.rekeyPayload = (0, Utils_1.decodeE2eRekeyPayload)(rekeyChild.content)
|
|
2515
|
+
} catch (e) {
|
|
2516
|
+
logger.debug({ e }, 'failed to decode enc-rekey payload in handleCall')
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
if (
|
|
2522
|
+
status === 'reject' ||
|
|
2523
|
+
status === 'accept' ||
|
|
2524
|
+
status === 'timeout' ||
|
|
2525
|
+
status === 'terminate' ||
|
|
2526
|
+
status === 'reject_do_not_disturb' ||
|
|
2527
|
+
status === 'mic_permission_denied' ||
|
|
2528
|
+
status === 'camera_permission_denied' ||
|
|
2529
|
+
status === 'remote_busy' ||
|
|
2530
|
+
status === 'remote_offline'
|
|
2531
|
+
) {
|
|
2112
2532
|
await callOfferCache.del(call.id)
|
|
2113
2533
|
}
|
|
2114
2534
|
await normalizeCallEventJids(call, infoChild)
|
|
@@ -2119,15 +2539,32 @@ const makeMessagesRecvSocket = config => {
|
|
|
2119
2539
|
await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack call'))
|
|
2120
2540
|
}
|
|
2121
2541
|
}
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
const CALL_STATE_TAGS = new Set([
|
|
2542
|
+
|
|
2543
|
+
|
|
2544
|
+
|
|
2545
|
+
|
|
2546
|
+
const CALL_STATE_TAGS = new Set([
|
|
2547
|
+
'offer',
|
|
2548
|
+
'offer_notice',
|
|
2549
|
+
'terminate',
|
|
2550
|
+
'accept',
|
|
2551
|
+
'reject',
|
|
2552
|
+
'preaccept',
|
|
2553
|
+
'accept_ack',
|
|
2554
|
+
'enc-rekey',
|
|
2555
|
+
'enc_rekey',
|
|
2556
|
+
'peer_state',
|
|
2557
|
+
'group_info',
|
|
2558
|
+
'video_state',
|
|
2559
|
+
'video_state_ack',
|
|
2560
|
+
'flow_control',
|
|
2561
|
+
'mute_v2',
|
|
2562
|
+
'waiting_room_request'
|
|
2563
|
+
])
|
|
2127
2564
|
const handleStandaloneCallStanza = async node => {
|
|
2128
2565
|
try {
|
|
2129
2566
|
if (!CALL_STATE_TAGS.has(node.tag)) {
|
|
2130
|
-
return
|
|
2567
|
+
return
|
|
2131
2568
|
}
|
|
2132
2569
|
const { attrs } = node
|
|
2133
2570
|
const status = (0, Utils_1.getCallStatusFromNode)(node)
|
|
@@ -2143,9 +2580,24 @@ const makeMessagesRecvSocket = config => {
|
|
|
2143
2580
|
status
|
|
2144
2581
|
}
|
|
2145
2582
|
if (status === 'offer') {
|
|
2146
|
-
|
|
2583
|
+
const videoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'video')
|
|
2584
|
+
const audioNodes = (0, WABinary_1.getBinaryNodeChildren)(node, 'audio')
|
|
2585
|
+
const silenceNode = (0, WABinary_1.getBinaryNodeChild)(node, 'silence')
|
|
2586
|
+
call.isVideo = !!videoNode
|
|
2147
2587
|
call.isGroup = attrs.type === 'group' || !!attrs['group-jid']
|
|
2148
2588
|
call.groupJid = attrs['group-jid']
|
|
2589
|
+
call.isLightweight = attrs.lightweight === '1'
|
|
2590
|
+
if (silenceNode?.attrs?.reason) {
|
|
2591
|
+
call.silenceReason = silenceNode.attrs.reason
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
if (audioNodes.length) {
|
|
2595
|
+
call.audioCodecs = audioNodes.map(n => ({ enc: n.attrs.enc, rate: n.attrs.rate ? +n.attrs.rate : undefined }))
|
|
2596
|
+
call.audioCodec = audioNodes[0].attrs.enc
|
|
2597
|
+
}
|
|
2598
|
+
if (videoNode?.attrs?.dec) {
|
|
2599
|
+
call.videoCodec = videoNode.attrs.dec
|
|
2600
|
+
}
|
|
2149
2601
|
if (callId) {
|
|
2150
2602
|
await callOfferCache.set(callId, call)
|
|
2151
2603
|
}
|
|
@@ -2156,7 +2608,45 @@ const makeMessagesRecvSocket = config => {
|
|
|
2156
2608
|
call.isGroup = existingCall.isGroup
|
|
2157
2609
|
call.callerPn = call.callerPn || existingCall.callerPn
|
|
2158
2610
|
}
|
|
2159
|
-
|
|
2611
|
+
|
|
2612
|
+
if (status === 'peer_state') {
|
|
2613
|
+
const stateChild = (0, WABinary_1.getBinaryNodeChild)(node, 'peer_state')
|
|
2614
|
+
call.state = stateChild?.attrs?.state || attrs?.state
|
|
2615
|
+
} else if (status === 'group_info') {
|
|
2616
|
+
call.payload = attrs
|
|
2617
|
+
call.participants = parseGroupCallRoster(node)
|
|
2618
|
+
} else if (status === 'video_state') {
|
|
2619
|
+
const vsChild = (0, WABinary_1.getBinaryNodeChild)(node, 'video_state')
|
|
2620
|
+
const enabledRaw = vsChild?.attrs?.enabled ?? attrs?.enabled
|
|
2621
|
+
call.enabled = enabledRaw === 'true' || enabledRaw === '1'
|
|
2622
|
+
} else if (status === 'enc_rekey') {
|
|
2623
|
+
const rekeyChild =
|
|
2624
|
+
(0, WABinary_1.getBinaryNodeChild)(node, 'enc-rekey') || (0, WABinary_1.getBinaryNodeChild)(node, 'enc_rekey')
|
|
2625
|
+
if (rekeyChild?.content && Buffer.isBuffer(rekeyChild.content)) {
|
|
2626
|
+
try {
|
|
2627
|
+
call.rekeyPayload = (0, Utils_1.decodeE2eRekeyPayload)(rekeyChild.content)
|
|
2628
|
+
} catch (e) {
|
|
2629
|
+
logger.debug({ e }, 'failed to decode enc-rekey payload in standalone call stanza')
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
} else if (status === 'mute') {
|
|
2633
|
+
|
|
2634
|
+
|
|
2635
|
+
|
|
2636
|
+
call.muted = node.attrs['mute-state'] === '1'
|
|
2637
|
+
}
|
|
2638
|
+
if (
|
|
2639
|
+
callId &&
|
|
2640
|
+
(status === 'reject' ||
|
|
2641
|
+
status === 'accept' ||
|
|
2642
|
+
status === 'timeout' ||
|
|
2643
|
+
status === 'terminate' ||
|
|
2644
|
+
status === 'reject_do_not_disturb' ||
|
|
2645
|
+
status === 'mic_permission_denied' ||
|
|
2646
|
+
status === 'camera_permission_denied' ||
|
|
2647
|
+
status === 'remote_busy' ||
|
|
2648
|
+
status === 'remote_offline')
|
|
2649
|
+
) {
|
|
2160
2650
|
await callOfferCache.del(callId)
|
|
2161
2651
|
}
|
|
2162
2652
|
await normalizeCallEventJids(call, node)
|
|
@@ -2169,26 +2659,26 @@ const makeMessagesRecvSocket = config => {
|
|
|
2169
2659
|
}
|
|
2170
2660
|
const handleBadAck = async ({ attrs }) => {
|
|
2171
2661
|
const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id }
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2662
|
+
|
|
2663
|
+
|
|
2664
|
+
|
|
2665
|
+
|
|
2666
|
+
|
|
2667
|
+
|
|
2668
|
+
|
|
2669
|
+
|
|
2670
|
+
|
|
2671
|
+
|
|
2672
|
+
|
|
2673
|
+
|
|
2674
|
+
|
|
2675
|
+
|
|
2676
|
+
|
|
2187
2677
|
if (attrs.error) {
|
|
2188
2678
|
if (attrs.error === Utils_1.SERVER_ERROR_CODES.MissingTcToken) {
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2679
|
+
|
|
2680
|
+
|
|
2681
|
+
|
|
2192
2682
|
logger.warn(
|
|
2193
2683
|
{ msgId: attrs.id, from: attrs.from },
|
|
2194
2684
|
'error 463: account restricted or missing tctoken for contact'
|
|
@@ -2210,23 +2700,54 @@ const makeMessagesRecvSocket = config => {
|
|
|
2210
2700
|
}
|
|
2211
2701
|
}
|
|
2212
2702
|
])
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2703
|
+
|
|
2704
|
+
|
|
2705
|
+
|
|
2706
|
+
|
|
2707
|
+
|
|
2708
|
+
|
|
2709
|
+
|
|
2710
|
+
|
|
2711
|
+
|
|
2712
|
+
|
|
2713
|
+
|
|
2714
|
+
|
|
2715
|
+
|
|
2226
2716
|
}
|
|
2227
2717
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2718
|
+
const handleChatstate = async node => {
|
|
2719
|
+
const { from, state } = node.attrs
|
|
2720
|
+
if (!from) return
|
|
2721
|
+
const isTyping = state === 'typing'
|
|
2722
|
+
ev.emit('presence.update', [
|
|
2723
|
+
{
|
|
2724
|
+
id: from,
|
|
2725
|
+
presences: {
|
|
2726
|
+
[from]: isTyping ? 'composing' : 'paused'
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
])
|
|
2730
|
+
}
|
|
2731
|
+
const handlePresence = async node => {
|
|
2732
|
+
const { from, type } = node.attrs
|
|
2733
|
+
if (!from || !type) return
|
|
2734
|
+
const presenceMap = {
|
|
2735
|
+
available: 'available',
|
|
2736
|
+
unavailable: 'unavailable'
|
|
2737
|
+
}
|
|
2738
|
+
const presence = presenceMap[type] || type
|
|
2739
|
+
ev.emit('presence.update', [
|
|
2740
|
+
{
|
|
2741
|
+
id: from,
|
|
2742
|
+
presences: {
|
|
2743
|
+
[from]: presence
|
|
2744
|
+
},
|
|
2745
|
+
lastSeen: node.attrs.t ? Number(node.attrs.t) * 1000 : undefined
|
|
2746
|
+
}
|
|
2747
|
+
])
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
|
|
2230
2751
|
const processNodeWithBuffer = async (node, identifier, exec) => {
|
|
2231
2752
|
ev.buffer()
|
|
2232
2753
|
await execTask()
|
|
@@ -2265,7 +2786,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
2265
2786
|
const setNodeLoggerListener = () => {
|
|
2266
2787
|
return latestNodeInMemory
|
|
2267
2788
|
}
|
|
2268
|
-
|
|
2789
|
+
|
|
2269
2790
|
ws.on('CB:message', async node => {
|
|
2270
2791
|
nodelogger(node)
|
|
2271
2792
|
await processNode('message', node, 'processing message', handleMessage)
|
|
@@ -2274,10 +2795,35 @@ const makeMessagesRecvSocket = config => {
|
|
|
2274
2795
|
nodelogger(node)
|
|
2275
2796
|
await processNode('call', node, 'handling call', handleCall)
|
|
2276
2797
|
})
|
|
2277
|
-
|
|
2798
|
+
|
|
2278
2799
|
for (const callTag of [
|
|
2279
|
-
'offer',
|
|
2280
|
-
'
|
|
2800
|
+
'offer',
|
|
2801
|
+
'offer_notice',
|
|
2802
|
+
'terminate',
|
|
2803
|
+
'accept',
|
|
2804
|
+
'reject',
|
|
2805
|
+
'preaccept',
|
|
2806
|
+
'accept_ack',
|
|
2807
|
+
'enc-rekey',
|
|
2808
|
+
'enc_rekey',
|
|
2809
|
+
'peer_state',
|
|
2810
|
+
'group_info',
|
|
2811
|
+
'video_state',
|
|
2812
|
+
'video_state_ack',
|
|
2813
|
+
'flow_control',
|
|
2814
|
+
'transport',
|
|
2815
|
+
'video',
|
|
2816
|
+
'duration',
|
|
2817
|
+
'mute_v2',
|
|
2818
|
+
'lobby',
|
|
2819
|
+
'heartbeat',
|
|
2820
|
+
'relaylatency',
|
|
2821
|
+
'link_query',
|
|
2822
|
+
'waiting_room_request',
|
|
2823
|
+
|
|
2824
|
+
|
|
2825
|
+
|
|
2826
|
+
'group_update'
|
|
2281
2827
|
]) {
|
|
2282
2828
|
ws.on('CB:' + callTag, node => {
|
|
2283
2829
|
nodelogger(node)
|
|
@@ -2292,6 +2838,18 @@ const makeMessagesRecvSocket = config => {
|
|
|
2292
2838
|
nodelogger(node)
|
|
2293
2839
|
await processNode('notification', node, 'handling notification', handleNotification)
|
|
2294
2840
|
})
|
|
2841
|
+
ws.on('CB:status', async node => {
|
|
2842
|
+
nodelogger(node)
|
|
2843
|
+
await handleNewsletterStatus(node).catch(error => onUnexpectedError(error, 'handling newsletter status'))
|
|
2844
|
+
})
|
|
2845
|
+
ws.on('CB:chatstate', async node => {
|
|
2846
|
+
nodelogger(node)
|
|
2847
|
+
await handleChatstate(node).catch(error => onUnexpectedError(error, 'handling chatstate'))
|
|
2848
|
+
})
|
|
2849
|
+
ws.on('CB:presence', async node => {
|
|
2850
|
+
nodelogger(node)
|
|
2851
|
+
await handlePresence(node).catch(error => onUnexpectedError(error, 'handling presence'))
|
|
2852
|
+
})
|
|
2295
2853
|
ws.on('CB:ack,class:message', node => {
|
|
2296
2854
|
nodelogger(node)
|
|
2297
2855
|
handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'))
|
|
@@ -2321,7 +2879,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
2321
2879
|
return
|
|
2322
2880
|
}
|
|
2323
2881
|
nodelogger(call)
|
|
2324
|
-
|
|
2882
|
+
|
|
2325
2883
|
if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
|
|
2326
2884
|
const msg = {
|
|
2327
2885
|
key: {
|
|
@@ -2357,7 +2915,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
2357
2915
|
sendActiveReceipts = isOnline
|
|
2358
2916
|
logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`)
|
|
2359
2917
|
}
|
|
2360
|
-
|
|
2918
|
+
|
|
2361
2919
|
if (isOnline) {
|
|
2362
2920
|
const now = Date.now()
|
|
2363
2921
|
const DAY_MS = 24 * 60 * 60 * 1000
|