@badzz88/baileys 8.4.9 → 8.5.0
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/WAProto/WAProto.proto +6 -347
- package/WAProto/fix-imports.js +85 -86
- package/WAProto/index.d.ts +25 -4913
- package/WAProto/index.js +93 -14069
- package/{src → lib}/Defaults/index.js +2 -13
- package/{src → lib}/Signal/Group/group_cipher.js +5 -5
- package/{src → lib}/Signal/Group/keyhelper.js +4 -4
- package/{src → lib}/Signal/Group/sender-chain-key.js +2 -2
- package/{src → lib}/Signal/Group/sender-key-distribution-message.js +1 -1
- package/{src → lib}/Signal/Group/sender-key-message.js +3 -3
- package/{src → lib}/Signal/Group/sender-key-record.js +1 -1
- package/{src → lib}/Signal/Group/sender-message-key.js +2 -2
- package/{src → lib}/Signal/libsignal.js +26 -6
- package/{src → lib}/Socket/chats.js +58 -616
- package/{src → lib}/Socket/communities.js +28 -59
- package/{src → lib}/Socket/graphql.js +103 -362
- package/{src → lib}/Socket/groups.js +29 -77
- package/{src → lib}/Socket/index.js +5 -3
- package/{src → lib}/Socket/interactive-handler.js +60 -117
- package/{src → lib}/Socket/interop.js +19 -36
- package/lib/Socket/luxu.js +387 -0
- package/{src → lib}/Socket/managed-account.js +27 -58
- package/{src → lib}/Socket/messages-recv.js +143 -715
- package/{src → lib}/Socket/messages-send.js +102 -412
- package/lib/Socket/mex.js +60 -0
- package/{src → lib}/Socket/newsletter.js +23 -266
- package/{src → lib}/Socket/privacy.js +38 -41
- package/{src → lib}/Socket/registration.js +106 -113
- package/{src → lib}/Socket/socket.js +101 -88
- package/{src → lib}/Socket/text-router.js +17 -1
- package/lib/Socket/username.js +243 -0
- package/lib/Store/keyed-db.js +108 -0
- package/{src → lib}/Store/make-in-memory-store.js +104 -6
- package/lib/Types/Mex.js +112 -0
- package/{src → lib}/Types/Newsletter.js +41 -72
- package/{src → lib}/Types/index.js +1 -0
- package/{src → lib}/Utils/chat-utils.js +9 -73
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +42 -0
- package/lib/Utils/crypto.js +155 -0
- package/lib/Utils/curve25519-js.js +275 -0
- package/{src → lib}/Utils/decode-wa-message.js +36 -284
- package/{src → lib}/Utils/event-buffer.js +27 -6
- package/{src → lib}/Utils/generics.js +37 -114
- package/{src → lib}/Utils/index.js +5 -1
- package/lib/Utils/lt-hash.js +39 -0
- package/{src → lib}/Utils/message-retry-manager.js +39 -0
- package/{src → lib}/Utils/messages-media.js +58 -105
- package/{src → lib}/Utils/messages.js +529 -115
- package/lib/Utils/meta-ai-msmsg.js +262 -0
- package/lib/Utils/native-bridge.js +82 -0
- package/{src → lib}/Utils/noise-handler.js +15 -13
- package/{src → lib}/Utils/process-message.js +55 -54
- package/{src → lib}/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/{src → lib}/Utils/signal.js +51 -19
- package/{src → lib}/Utils/stanza-ack.js +2 -36
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/{src → lib}/Utils/tc-token-utils.js +1 -1
- package/{src → lib}/Utils/validate-connection.js +11 -40
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/{src → lib}/WABinary/decode.js +4 -38
- package/lib/WABinary/encode.js +228 -0
- package/{src → lib}/WABinary/generic-utils.js +93 -1
- package/{src → lib}/WABinary/jid-utils.js +3 -16
- package/{src → lib}/WAM/encode.js +1 -1
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
- package/{src → lib}/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/{src → lib}/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/{src → lib}/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/{src → lib}/WAUSync/Protocols/USyncFeatureProtocol.js +2 -17
- package/{src → lib}/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/{src → lib}/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/{src → lib}/WAUSync/Protocols/index.js +0 -1
- package/{src → lib}/WAUSync/USyncQuery.js +6 -77
- package/{src → lib}/WAUSync/USyncUser.js +8 -16
- package/{src → lib}/WAUSync/index.js +0 -1
- package/{src → lib}/antiban.js +266 -355
- package/{src → lib}/index.js +2 -2
- package/package.json +18 -15
- package/WAProto/fix-import.js +0 -38
- package/src/Socket/mex.js +0 -47
- package/src/Socket/username.js +0 -234
- package/src/Utils/crypto.js +0 -125
- package/src/Utils/lt-hash.js +0 -10
- package/src/Utils/message-inspect.js +0 -400
- package/src/Utils/meta-ai-msmsg.js +0 -133
- package/src/WABinary/encode.js +0 -58
- package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -100
- package/src/WAUSync/USyncBackoff.js +0 -31
- /package/{src → lib}/Defaults/phonenumber-mcc.json +0 -0
- /package/{src → lib}/Signal/Group/ciphertext-message.js +0 -0
- /package/{src → lib}/Signal/Group/group-session-builder.js +0 -0
- /package/{src → lib}/Signal/Group/index.js +0 -0
- /package/{src → lib}/Signal/Group/sender-key-name.js +0 -0
- /package/{src → lib}/Signal/Group/sender-key-state.js +0 -0
- /package/{src → lib}/Signal/lid-mapping.js +0 -0
- /package/{src → lib}/Socket/Client/index.js +0 -0
- /package/{src → lib}/Socket/Client/types.js +0 -0
- /package/{src → lib}/Socket/Client/websocket.js +0 -0
- /package/{src → lib}/Socket/aigroups.js +0 -0
- /package/{src → lib}/Socket/business.js +0 -0
- /package/{src → lib}/Store/index.js +0 -0
- /package/{src → lib}/Store/make-cache-manager-store.js +0 -0
- /package/{src → lib}/Store/make-ordered-dictionary.js +0 -0
- /package/{src → lib}/Store/object-repository.js +0 -0
- /package/{src → lib}/Types/Auth.js +0 -0
- /package/{src → lib}/Types/Bussines.js +0 -0
- /package/{src → lib}/Types/Call.js +0 -0
- /package/{src → lib}/Types/Chat.js +0 -0
- /package/{src → lib}/Types/Contact.js +0 -0
- /package/{src → lib}/Types/Events.js +0 -0
- /package/{src → lib}/Types/GroupMetadata.js +0 -0
- /package/{src → lib}/Types/Label.js +0 -0
- /package/{src → lib}/Types/LabelAssociation.js +0 -0
- /package/{src → lib}/Types/Message.js +0 -0
- /package/{src → lib}/Types/Product.js +0 -0
- /package/{src → lib}/Types/Signal.js +0 -0
- /package/{src → lib}/Types/Socket.js +0 -0
- /package/{src → lib}/Types/State.js +0 -0
- /package/{src → lib}/Types/USync.js +0 -0
- /package/{src → lib}/Utils/auth-utils.js +0 -0
- /package/{src → lib}/Utils/browser-utils.js +0 -0
- /package/{src → lib}/Utils/business.js +0 -0
- /package/{src → lib}/Utils/consumer-application.js +0 -0
- /package/{src → lib}/Utils/group-history.js +0 -0
- /package/{src → lib}/Utils/history.js +0 -0
- /package/{src → lib}/Utils/identity-change-handler.js +0 -0
- /package/{src → lib}/Utils/jid-display-normalization.js +0 -0
- /package/{src → lib}/Utils/link-preview.js +0 -0
- /package/{src → lib}/Utils/logger.js +0 -0
- /package/{src → lib}/Utils/make-mutex.js +0 -0
- /package/{src → lib}/Utils/message-composer.js +0 -0
- /package/{src → lib}/Utils/offline-node-processor.js +0 -0
- /package/{src → lib}/Utils/pre-key-manager.js +0 -0
- /package/{src → lib}/Utils/sync-action-utils.js +0 -0
- /package/{src → lib}/Utils/use-multi-file-auth-state.js +0 -0
- /package/{src → lib}/Utils/voip-rekey.js +0 -0
- /package/{src → lib}/WABinary/constants.js +0 -0
- /package/{src → lib}/WABinary/index.js +0 -0
- /package/{src → lib}/WABinary/types.js +0 -0
- /package/{src → lib}/WAM/BinaryInfo.js +0 -0
- /package/{src → lib}/WAM/constants.js +0 -0
- /package/{src → lib}/WAM/index.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncLIDProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncPictureProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncSidelistProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -0
|
@@ -46,10 +46,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
46
46
|
uploadPreKeys,
|
|
47
47
|
sendPeerDataOperationMessage,
|
|
48
48
|
messageRetryManager,
|
|
49
|
-
issuePrivacyTokens
|
|
50
|
-
getUSyncDevices,
|
|
51
|
-
createParticipantNodes,
|
|
52
|
-
newsletterServerIdCache
|
|
49
|
+
issuePrivacyTokens
|
|
53
50
|
} = sock
|
|
54
51
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
|
|
55
52
|
// Track when the socket fully opens so pending pre-connect messages are treated as history
|
|
@@ -147,14 +144,13 @@ const makeMessagesRecvSocket = config => {
|
|
|
147
144
|
*
|
|
148
145
|
* @param {string} [registrationTraceId] - Optional trace ID for this registration attempt.
|
|
149
146
|
*/
|
|
150
|
-
const requestCompanionCanonicalNonce = async registrationTraceId => {
|
|
147
|
+
const requestCompanionCanonicalNonce = async (registrationTraceId) => {
|
|
151
148
|
if (!authState.creds.me?.id) {
|
|
152
149
|
throw new boom_1.Boom('Not authenticated')
|
|
153
150
|
}
|
|
154
151
|
return sendPeerDataOperationMessage({
|
|
155
152
|
companionCanonicalUserNonceFetchRequest: registrationTraceId ? { registrationTraceId } : {},
|
|
156
|
-
peerDataOperationRequestType:
|
|
157
|
-
index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_CANONICAL_USER_NONCE_FETCH
|
|
153
|
+
peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_CANONICAL_USER_NONCE_FETCH
|
|
158
154
|
})
|
|
159
155
|
}
|
|
160
156
|
/**
|
|
@@ -170,135 +166,54 @@ const makeMessagesRecvSocket = config => {
|
|
|
170
166
|
peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_META_NONCE_FETCH
|
|
171
167
|
})
|
|
172
168
|
}
|
|
173
|
-
// Handles mex
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
logger.debug({ node }, 'mex notification with no update children')
|
|
169
|
+
// Handles mex newsletter notifications
|
|
170
|
+
const handleMexNewsletterNotification = async node => {
|
|
171
|
+
const mexNode = (0, WABinary_1.getBinaryNodeChild)(node, 'mex')
|
|
172
|
+
if (!mexNode?.content) {
|
|
173
|
+
logger.warn({ node }, 'Invalid mex newsletter notification')
|
|
179
174
|
return
|
|
180
175
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// d.xwa2_notify_newsletter_on_metadata_update: { id, thread_metadata: { settings } }
|
|
200
|
-
const upd = d.xwa2_notify_newsletter_on_metadata_update
|
|
201
|
-
if (upd?.id) {
|
|
202
|
-
ev.emit('newsletter-settings.update', {
|
|
203
|
-
id: upd.id,
|
|
204
|
-
update: upd.thread_metadata?.settings ?? {}
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
break
|
|
208
|
-
}
|
|
209
|
-
case 'NotificationNewsletterJoin': {
|
|
210
|
-
// d.xwa2_notify_newsletter_on_join: full newsletter metadata on subscribe
|
|
211
|
-
const upd = d.xwa2_notify_newsletter_on_join
|
|
212
|
-
if (upd?.id) {
|
|
213
|
-
ev.emit('newsletter-participants.update', {
|
|
214
|
-
id: upd.id,
|
|
215
|
-
author: node.attrs.from,
|
|
216
|
-
user: (0, WABinary_1.jidNormalizedUser)(node.attrs.from),
|
|
217
|
-
new_role: upd.viewer_metadata?.role ?? 'SUBSCRIBER',
|
|
218
|
-
action: 'join',
|
|
219
|
-
metadata: upd.thread_metadata
|
|
220
|
-
})
|
|
221
|
-
}
|
|
222
|
-
break
|
|
223
|
-
}
|
|
224
|
-
case 'NotificationNewsletterMuteChange': {
|
|
225
|
-
// d.xwa2_notify_newsletter_on_mute_change: { id, mute: "ON"|"OFF" }
|
|
226
|
-
const upd = d.xwa2_notify_newsletter_on_mute_change
|
|
227
|
-
if (upd?.id) {
|
|
228
|
-
ev.emit('newsletter-settings.update', {
|
|
229
|
-
id: upd.id,
|
|
230
|
-
update: { mute: upd.mute }
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
break
|
|
234
|
-
}
|
|
235
|
-
case 'NotificationNewsletterUserSettingChange': {
|
|
236
|
-
// d.xwa2_notify_newsletter_on_user_setting_change: { id, setting: { type, value } }
|
|
237
|
-
const upd = d.xwa2_notify_newsletter_on_user_setting_change
|
|
238
|
-
if (upd?.id && upd.setting) {
|
|
176
|
+
let data
|
|
177
|
+
try {
|
|
178
|
+
data = JSON.parse(mexNode.content.toString())
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.error({ err: error, node }, 'Failed to parse mex newsletter notification')
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
const operation = data?.operation
|
|
184
|
+
const updates = data?.updates
|
|
185
|
+
if (!updates || !operation) {
|
|
186
|
+
logger.warn({ data }, 'Invalid mex newsletter notification content')
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
logger.info({ operation, updates }, 'got mex newsletter notification')
|
|
190
|
+
switch (operation) {
|
|
191
|
+
case 'NotificationNewsletterUpdate':
|
|
192
|
+
for (const update of updates) {
|
|
193
|
+
if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
|
|
239
194
|
ev.emit('newsletter-settings.update', {
|
|
240
|
-
id:
|
|
241
|
-
update:
|
|
195
|
+
id: update.jid,
|
|
196
|
+
update: update.settings
|
|
242
197
|
})
|
|
243
198
|
}
|
|
244
|
-
break
|
|
245
199
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (
|
|
200
|
+
break
|
|
201
|
+
case 'NotificationNewsletterAdminPromote':
|
|
202
|
+
for (const update of updates) {
|
|
203
|
+
if (update.jid && update.user) {
|
|
250
204
|
ev.emit('newsletter-participants.update', {
|
|
251
|
-
id:
|
|
205
|
+
id: update.jid,
|
|
252
206
|
author: node.attrs.from,
|
|
253
|
-
user:
|
|
207
|
+
user: update.user,
|
|
254
208
|
new_role: 'ADMIN',
|
|
255
209
|
action: 'promote'
|
|
256
210
|
})
|
|
257
211
|
}
|
|
258
|
-
break
|
|
259
|
-
}
|
|
260
|
-
case 'NotificationGroupMemberLinkPropertyUpdate': {
|
|
261
|
-
// d.xwa2_notify_group_on_prop_change: { id, properties: { member_link_mode } }
|
|
262
|
-
const upd = d.xwa2_notify_group_on_prop_change
|
|
263
|
-
if (upd?.id && upd.properties?.member_link_mode !== undefined) {
|
|
264
|
-
ev.emit('groups.update', [
|
|
265
|
-
{
|
|
266
|
-
id: upd.id,
|
|
267
|
-
memberAddMode: upd.properties.member_link_mode
|
|
268
|
-
}
|
|
269
|
-
])
|
|
270
|
-
}
|
|
271
|
-
break
|
|
272
212
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
ev.emit('groups.update', [
|
|
278
|
-
{
|
|
279
|
-
id: upd.id,
|
|
280
|
-
limitSharing: upd.properties.limit_sharing
|
|
281
|
-
}
|
|
282
|
-
])
|
|
283
|
-
}
|
|
284
|
-
break
|
|
285
|
-
}
|
|
286
|
-
case 'NotificationGroupMemberShareGroupHistoryModePropertyUpdate': {
|
|
287
|
-
// d.xwa2_notify_group_on_prop_change: { id, properties: { member_share_group_history_mode } }
|
|
288
|
-
const upd = d.xwa2_notify_group_on_prop_change
|
|
289
|
-
if (upd?.id && upd.properties?.member_share_group_history_mode !== undefined) {
|
|
290
|
-
ev.emit('groups.update', [
|
|
291
|
-
{
|
|
292
|
-
id: upd.id,
|
|
293
|
-
memberShareHistoryMode: upd.properties.member_share_group_history_mode
|
|
294
|
-
}
|
|
295
|
-
])
|
|
296
|
-
}
|
|
297
|
-
break
|
|
298
|
-
}
|
|
299
|
-
default:
|
|
300
|
-
logger.debug({ opName, from: node.attrs.from }, 'unhandled mex op')
|
|
301
|
-
}
|
|
213
|
+
break
|
|
214
|
+
default:
|
|
215
|
+
logger.info({ operation, data }, 'Unhandled mex newsletter notification')
|
|
216
|
+
break
|
|
302
217
|
}
|
|
303
218
|
}
|
|
304
219
|
// Handles newsletter notifications
|
|
@@ -351,9 +266,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
351
266
|
})
|
|
352
267
|
}
|
|
353
268
|
break
|
|
354
|
-
case 'message':
|
|
355
|
-
const viewCount = child.attrs.view_count !== undefined ? +child.attrs.view_count : undefined
|
|
356
|
-
const impressionCount = child.attrs.impression_count !== undefined ? +child.attrs.impression_count : undefined
|
|
269
|
+
case 'message':
|
|
357
270
|
const plaintextNode = (0, WABinary_1.getBinaryNodeChild)(child, 'plaintext')
|
|
358
271
|
if (plaintextNode?.content) {
|
|
359
272
|
try {
|
|
@@ -366,16 +279,11 @@ const makeMessagesRecvSocket = config => {
|
|
|
366
279
|
key: {
|
|
367
280
|
remoteJid: from,
|
|
368
281
|
id: child.attrs.message_id || child.attrs.server_id,
|
|
369
|
-
|
|
370
|
-
// which always originate from the channel, never from us
|
|
371
|
-
fromMe: false
|
|
282
|
+
fromMe: false // TODO: is this really true though
|
|
372
283
|
},
|
|
373
284
|
message: messageProto,
|
|
374
285
|
messageTimestamp: +child.attrs.t
|
|
375
286
|
}).toJSON()
|
|
376
|
-
// Attach insight counters when the server includes them
|
|
377
|
-
if (viewCount !== undefined) fullMessage.views = viewCount
|
|
378
|
-
if (impressionCount !== undefined) fullMessage.impressions = impressionCount
|
|
379
287
|
await upsertMessage(fullMessage, 'append')
|
|
380
288
|
logger.info('Processed plaintext newsletter message')
|
|
381
289
|
} catch (error) {
|
|
@@ -383,44 +291,23 @@ const makeMessagesRecvSocket = config => {
|
|
|
383
291
|
}
|
|
384
292
|
}
|
|
385
293
|
break
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const fwdNode = (0, WABinary_1.getBinaryNodeChild)(msgNode, 'forwards_count')
|
|
396
|
-
const forwardsCount = fwdNode?.attrs?.count !== undefined ? +fwdNode.attrs.count : undefined
|
|
397
|
-
const reactionsNode = (0, WABinary_1.getBinaryNodeChild)(msgNode, 'reactions')
|
|
398
|
-
const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionsNode ?? msgNode, 'reaction').map(r => ({
|
|
399
|
-
code: r.attrs.code,
|
|
400
|
-
count: +r.attrs.count
|
|
401
|
-
}))
|
|
402
|
-
ev.emit('newsletter.live-update', {
|
|
403
|
-
id: from,
|
|
404
|
-
server_id: serverId,
|
|
405
|
-
timestamp: msgTs,
|
|
406
|
-
forwardsCount,
|
|
407
|
-
reactions
|
|
408
|
-
})
|
|
409
|
-
}
|
|
294
|
+
case 'live_updates':
|
|
295
|
+
// Live view count / engagement update
|
|
296
|
+
const liveCount = parseInt(child.attrs.count || child.content?.toString() || '0', 10)
|
|
297
|
+
ev.emit('newsletter.live-update', {
|
|
298
|
+
id: from,
|
|
299
|
+
server_id: child.attrs.message_id || child.attrs.server_id,
|
|
300
|
+
liveViewers: liveCount,
|
|
301
|
+
timestamp: child.attrs.t ? +child.attrs.t : undefined
|
|
302
|
+
})
|
|
410
303
|
break
|
|
411
|
-
|
|
412
|
-
case 'pin': {
|
|
413
|
-
const pinnedServerId = child.attrs.message_id || child.attrs.server_id
|
|
414
|
-
const isPinned = child.attrs.action !== 'unpin'
|
|
304
|
+
case 'pin':
|
|
415
305
|
ev.emit('newsletter.pin', {
|
|
416
306
|
id: from,
|
|
417
|
-
server_id:
|
|
418
|
-
pinned:
|
|
307
|
+
server_id: child.attrs.message_id || child.attrs.server_id,
|
|
308
|
+
pinned: child.attrs.action !== 'unpin'
|
|
419
309
|
})
|
|
420
|
-
// Sync the pinnedMessage field on the newsletter metadata object
|
|
421
|
-
ev.emit('newsletters.update', [{ id: from, pinnedMessage: isPinned ? pinnedServerId : null }])
|
|
422
310
|
break
|
|
423
|
-
}
|
|
424
311
|
case 'category':
|
|
425
312
|
ev.emit('newsletter-settings.update', {
|
|
426
313
|
id: from,
|
|
@@ -440,88 +327,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
440
327
|
break
|
|
441
328
|
}
|
|
442
329
|
}
|
|
443
|
-
// Handles incoming <status> stanzas pushed by the server for newsletter posts.
|
|
444
|
-
// These arrive on the CB:status channel (AB-gated by status_e2ee_recv_over_status_stanza).
|
|
445
|
-
const handleNewsletterStatus = async node => {
|
|
446
|
-
const { id, from, server_id, t, is_sender, offline, type, edit } = node.attrs
|
|
447
|
-
const serverId = server_id ? +server_id : undefined
|
|
448
|
-
const timestamp = t ? +t : undefined
|
|
449
|
-
const isSender = is_sender === 'true'
|
|
450
|
-
const offlineIndex = offline !== undefined ? +offline : undefined
|
|
451
|
-
// Cache messageId → serverId so the bot can look up server_id when a message is quoted
|
|
452
|
-
if (id && serverId != null) {
|
|
453
|
-
newsletterServerIdCache?.set(id, serverId)
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Parse optional <meta> child: edit timestamps, interaction type, admin profile
|
|
457
|
-
const metaNode = (0, WABinary_1.getBinaryNodeChild)(node, 'meta')
|
|
458
|
-
const meta = metaNode
|
|
459
|
-
? {
|
|
460
|
-
...(metaNode.attrs.msg_edit_t ? { editedAt: +metaNode.attrs.msg_edit_t } : {}),
|
|
461
|
-
...(metaNode.attrs.original_msg_t ? { originalTimestamp: +metaNode.attrs.original_msg_t } : {}),
|
|
462
|
-
...(metaNode.attrs.interaction_type ? { interactionType: metaNode.attrs.interaction_type } : {}),
|
|
463
|
-
...(metaNode.attrs.parent_server_id ? { parentServerId: +metaNode.attrs.parent_server_id } : {}),
|
|
464
|
-
...(metaNode.attrs.response_server_id ? { responseServerId: +metaNode.attrs.response_server_id } : {})
|
|
465
|
-
}
|
|
466
|
-
: undefined
|
|
467
|
-
|
|
468
|
-
// Parse engagement counters
|
|
469
|
-
const viewsNode = (0, WABinary_1.getBinaryNodeChild)(node, 'views_count')
|
|
470
|
-
const viewsCount = viewsNode?.attrs?.count !== undefined ? +viewsNode.attrs.count : undefined
|
|
471
|
-
const responsesNode = (0, WABinary_1.getBinaryNodeChild)(node, 'responses_count')
|
|
472
|
-
const responsesCount = responsesNode?.attrs?.count !== undefined ? +responsesNode.attrs.count : undefined
|
|
473
|
-
const reactionsNode = (0, WABinary_1.getBinaryNodeChild)(node, 'reactions')
|
|
474
|
-
const reactionCounts = (0, WABinary_1.getBinaryNodeChildren)(reactionsNode ?? { content: [] }, 'reaction').map(
|
|
475
|
-
r => ({ code: r.attrs.code, count: +r.attrs.count })
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
let content = null
|
|
479
|
-
let mediaType = undefined
|
|
480
|
-
|
|
481
|
-
if (type === 'reaction') {
|
|
482
|
-
const reactionNode = (0, WABinary_1.getBinaryNodeChild)(node, 'reaction')
|
|
483
|
-
content = { type: 'reaction', code: reactionNode?.attrs?.code }
|
|
484
|
-
} else if (type === 'text' || type === 'media') {
|
|
485
|
-
const plaintextNode = (0, WABinary_1.getBinaryNodeChild)(node, 'plaintext')
|
|
486
|
-
if (type === 'media') mediaType = plaintextNode?.attrs?.mediatype
|
|
487
|
-
if (edit === '7' || edit === '8') {
|
|
488
|
-
content = { type: 'revoke', edit }
|
|
489
|
-
} else if (plaintextNode?.content) {
|
|
490
|
-
try {
|
|
491
|
-
const buf = Buffer.isBuffer(plaintextNode.content)
|
|
492
|
-
? plaintextNode.content
|
|
493
|
-
: Buffer.from(plaintextNode.content)
|
|
494
|
-
const message = index_js_1.proto.Message.decode(buf).toJSON()
|
|
495
|
-
content = { type, message, ...(mediaType ? { mediaType } : {}) }
|
|
496
|
-
} catch (err) {
|
|
497
|
-
logger.error({ err }, 'Failed to decode newsletter status plaintext')
|
|
498
|
-
content = { type, raw: true, ...(mediaType ? { mediaType } : {}) }
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Send ACK back to server
|
|
504
|
-
const ackType = type === 'reaction' ? 'reaction' : edit ? 'revoke' : type
|
|
505
|
-
await sendNode({
|
|
506
|
-
tag: 'ack',
|
|
507
|
-
attrs: { id, to: from, class: 'status', type: ackType || 'text' },
|
|
508
|
-
content: undefined
|
|
509
|
-
})
|
|
510
|
-
|
|
511
|
-
ev.emit('newsletter.status', {
|
|
512
|
-
id: from,
|
|
513
|
-
messageId: id,
|
|
514
|
-
serverId,
|
|
515
|
-
timestamp,
|
|
516
|
-
isSender,
|
|
517
|
-
...(offlineIndex !== undefined ? { offlineIndex } : {}),
|
|
518
|
-
...(meta ? { meta } : {}),
|
|
519
|
-
...(viewsCount !== undefined ? { viewsCount } : {}),
|
|
520
|
-
...(responsesCount !== undefined ? { responsesCount } : {}),
|
|
521
|
-
...(reactionCounts.length ? { reactionCounts } : {}),
|
|
522
|
-
content
|
|
523
|
-
})
|
|
524
|
-
}
|
|
525
330
|
const sendMessageAck = async (node, errorCode) => {
|
|
526
331
|
const stanza = (0, stanza_ack_1.buildAckStanza)(node, errorCode, authState.creds.me.id)
|
|
527
332
|
logger.debug({ recv: { tag: node.tag, attrs: node.attrs }, sent: stanza.attrs }, 'sent ack')
|
|
@@ -871,9 +676,8 @@ const makeMessagesRecvSocket = config => {
|
|
|
871
676
|
id: node.attrs.id,
|
|
872
677
|
t: node.attrs.t,
|
|
873
678
|
v: '1',
|
|
874
|
-
//
|
|
875
|
-
|
|
876
|
-
error: '1'
|
|
679
|
+
// ADD ERROR FIELD
|
|
680
|
+
error: '0'
|
|
877
681
|
}
|
|
878
682
|
},
|
|
879
683
|
{
|
|
@@ -921,7 +725,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
921
725
|
const tcJid = await (0, tc_token_utils_1.resolveTcTokenJid)(normalizedJid, getLIDForPN)
|
|
922
726
|
const tcTokenData = await authState.keys.get('tctoken', [tcJid])
|
|
923
727
|
const senderTs = tcTokenData?.[tcJid]?.senderTimestamp
|
|
924
|
-
if (senderTs
|
|
728
|
+
if (senderTs === null || senderTs === undefined || (0, tc_token_utils_1.isTcTokenExpired)(senderTs)) {
|
|
925
729
|
return
|
|
926
730
|
}
|
|
927
731
|
logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken')
|
|
@@ -969,21 +773,8 @@ const makeMessagesRecvSocket = config => {
|
|
|
969
773
|
}
|
|
970
774
|
}
|
|
971
775
|
}
|
|
972
|
-
// Group participant nodes carry authoritative LID<->PN pairs (jid + phone_number/lid
|
|
973
|
-
// attrs). Persist them so PN<->LID resolution elsewhere doesn't need a usync round trip.
|
|
974
|
-
const storeParticipantLidPairs = participants => {
|
|
975
|
-
const pairs = []
|
|
976
|
-
for (const p of participants || []) {
|
|
977
|
-
if (p.phoneNumber) pairs.push({ lid: p.id, pn: p.phoneNumber })
|
|
978
|
-
else if (p.lid) pairs.push({ lid: p.lid, pn: p.id })
|
|
979
|
-
}
|
|
980
|
-
if (!pairs.length) return
|
|
981
|
-
signalRepository.lidMapping
|
|
982
|
-
.storeLIDPNMappings(pairs)
|
|
983
|
-
.catch(error => logger.debug({ error }, 'failed to store LID/PN mappings from group notification'))
|
|
984
|
-
}
|
|
985
776
|
const handleGroupNotification = (fullNode, child, msg) => {
|
|
986
|
-
// PN/LID
|
|
777
|
+
// TODO: Support PN/LID (Here is only LID now)
|
|
987
778
|
const actingParticipantLid = fullNode.attrs.participant
|
|
988
779
|
const actingParticipantPn = fullNode.attrs.participant_pn
|
|
989
780
|
const actingParticipantUsername = fullNode.attrs.participant_username
|
|
@@ -997,7 +788,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
997
788
|
msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE
|
|
998
789
|
msg.messageStubParameters = [metadata.subject]
|
|
999
790
|
msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn }
|
|
1000
|
-
storeParticipantLidPairs(metadata.participants)
|
|
1001
791
|
ev.emit('chats.upsert', [
|
|
1002
792
|
{
|
|
1003
793
|
id: metadata.id,
|
|
@@ -1036,6 +826,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1036
826
|
const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
|
|
1037
827
|
msg.messageStubType = Types_1.WAMessageStubType[stubType]
|
|
1038
828
|
const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(({ attrs }) => {
|
|
829
|
+
// TODO: Store LID MAPPINGS
|
|
1039
830
|
return {
|
|
1040
831
|
id: attrs.jid,
|
|
1041
832
|
phoneNumber:
|
|
@@ -1044,8 +835,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
1044
835
|
: undefined,
|
|
1045
836
|
lid: (0, WABinary_1.isPnUser)(attrs.jid) && (0, WABinary_1.isLidUser)(attrs.lid) ? attrs.lid : undefined,
|
|
1046
837
|
username: attrs.participant_username || attrs.username || undefined,
|
|
1047
|
-
admin: attrs.type || null
|
|
1048
|
-
uuid: attrs.uuid || attrs.participant_uuid || undefined
|
|
838
|
+
admin: attrs.type || null
|
|
1049
839
|
}
|
|
1050
840
|
})
|
|
1051
841
|
if (
|
|
@@ -1058,7 +848,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
1058
848
|
) {
|
|
1059
849
|
msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE
|
|
1060
850
|
}
|
|
1061
|
-
storeParticipantLidPairs(participants)
|
|
1062
851
|
msg.messageStubParameters = participants.map(a => JSON.stringify(a))
|
|
1063
852
|
break
|
|
1064
853
|
case 'subject':
|
|
@@ -1108,51 +897,13 @@ const makeMessagesRecvSocket = config => {
|
|
|
1108
897
|
break
|
|
1109
898
|
case 'revoked_membership_requests':
|
|
1110
899
|
const isDenied = (0, WABinary_1.areJidsSameUser)(affectedParticipantLid, actingParticipantLid)
|
|
1111
|
-
|
|
1112
|
-
storeParticipantLidPairs([{ id: affectedParticipantLid, phoneNumber: affectedParticipantPn }])
|
|
1113
|
-
}
|
|
900
|
+
// TODO: LIDMAPPING SUPPORT
|
|
1114
901
|
msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
|
|
1115
902
|
msg.messageStubParameters = [
|
|
1116
903
|
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
|
|
1117
904
|
isDenied ? 'revoked' : 'rejected'
|
|
1118
905
|
]
|
|
1119
906
|
break
|
|
1120
|
-
case 'sibling_link': {
|
|
1121
|
-
const linkedGroupJid = (0, WABinary_1.getBinaryNodeChild)(child, 'group')?.attrs?.jid || child.attrs?.jid
|
|
1122
|
-
ev.emit('groups.update', [
|
|
1123
|
-
{
|
|
1124
|
-
id: fullNode.attrs.from,
|
|
1125
|
-
siblingGroupLinked: linkedGroupJid || true,
|
|
1126
|
-
author: actingParticipantLid,
|
|
1127
|
-
authorPn: actingParticipantPn
|
|
1128
|
-
}
|
|
1129
|
-
])
|
|
1130
|
-
break
|
|
1131
|
-
}
|
|
1132
|
-
case 'sibling_unlink': {
|
|
1133
|
-
const unlinkedGroupJid = (0, WABinary_1.getBinaryNodeChild)(child, 'group')?.attrs?.jid || child.attrs?.jid
|
|
1134
|
-
ev.emit('groups.update', [
|
|
1135
|
-
{
|
|
1136
|
-
id: fullNode.attrs.from,
|
|
1137
|
-
siblingGroupUnlinked: unlinkedGroupJid || true,
|
|
1138
|
-
author: actingParticipantLid,
|
|
1139
|
-
authorPn: actingParticipantPn
|
|
1140
|
-
}
|
|
1141
|
-
])
|
|
1142
|
-
break
|
|
1143
|
-
}
|
|
1144
|
-
case 'clear_history': {
|
|
1145
|
-
const historyClearTimestamp = child.attrs?.t ? +child.attrs.t : (0, Date)()
|
|
1146
|
-
ev.emit('groups.update', [
|
|
1147
|
-
{
|
|
1148
|
-
id: fullNode.attrs.from,
|
|
1149
|
-
historyClearTimestamp,
|
|
1150
|
-
author: actingParticipantLid,
|
|
1151
|
-
authorPn: actingParticipantPn
|
|
1152
|
-
}
|
|
1153
|
-
])
|
|
1154
|
-
break
|
|
1155
|
-
}
|
|
1156
907
|
}
|
|
1157
908
|
}
|
|
1158
909
|
const normalizeNotificationParticipant = async (jid, groupData) => {
|
|
@@ -1326,17 +1077,15 @@ const makeMessagesRecvSocket = config => {
|
|
|
1326
1077
|
if (childTag === 'peer_device_presence') {
|
|
1327
1078
|
const jid = attrs.jid || (0, WABinary_1.jidNormalizedUser)(node.attrs.from)
|
|
1328
1079
|
const presence = attrs.type === 'unavailable' ? 'unavailable' : 'available'
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
ev.emit('presence.update', { id: jid, presences: { [jid]: { lastKnownPresence: presence } }, isIosInterop })
|
|
1080
|
+
logger.debug({ jid, presence }, '[interop] peer_device_presence')
|
|
1081
|
+
ev.emit('presence.update', { id: jid, presences: { [jid]: { lastKnownPresence: presence } } })
|
|
1332
1082
|
return
|
|
1333
1083
|
}
|
|
1334
1084
|
|
|
1335
1085
|
// fbid:thread / fbid:devices — Meta thread or device list association
|
|
1336
1086
|
if (childTag === 'fbid_thread' || childTag === 'fbid_devices') {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
ev.emit('interop.fbid-update', { type: childTag, jid: node.attrs.from, attrs, isIosInterop })
|
|
1087
|
+
logger.debug({ childTag, attrs, from: node.attrs.from }, '[interop] fbid association update')
|
|
1088
|
+
ev.emit('interop.fbid-update', { type: childTag, jid: node.attrs.from, attrs })
|
|
1340
1089
|
return
|
|
1341
1090
|
}
|
|
1342
1091
|
|
|
@@ -1363,24 +1112,10 @@ const makeMessagesRecvSocket = config => {
|
|
|
1363
1112
|
await handleNewsletterNotification(node)
|
|
1364
1113
|
break
|
|
1365
1114
|
case 'mex':
|
|
1366
|
-
await
|
|
1115
|
+
await handleMexNewsletterNotification(node)
|
|
1367
1116
|
break
|
|
1368
1117
|
case 'w:gp2':
|
|
1369
|
-
|
|
1370
|
-
// Bulk "these groups are stale, refetch" signal — not a per-group stub message,
|
|
1371
|
-
// so it doesn't go through handleGroupNotification (which assumes a single group).
|
|
1372
|
-
const dirtyGroupJids = (0, WABinary_1.getBinaryNodeChildren)(child, 'group')
|
|
1373
|
-
.map(g => g.attrs.jid)
|
|
1374
|
-
.filter(Boolean)
|
|
1375
|
-
if (dirtyGroupJids.length) {
|
|
1376
|
-
ev.emit(
|
|
1377
|
-
'groups.update',
|
|
1378
|
-
dirtyGroupJids.map(jid => ({ id: jid }))
|
|
1379
|
-
)
|
|
1380
|
-
}
|
|
1381
|
-
break
|
|
1382
|
-
}
|
|
1383
|
-
// PN/LID resolution for participant fields happens below via normalizeNotificationResult
|
|
1118
|
+
// TODO: HANDLE PARTICIPANT_PN
|
|
1384
1119
|
const groupData = await getNotificationGroupData(node)
|
|
1385
1120
|
handleGroupNotification(node, child, result)
|
|
1386
1121
|
|
|
@@ -1393,39 +1128,26 @@ const makeMessagesRecvSocket = config => {
|
|
|
1393
1128
|
case 'encrypt':
|
|
1394
1129
|
await handleEncryptNotification(node)
|
|
1395
1130
|
break
|
|
1396
|
-
case 'devices':
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
// device-list hash refresh) — don't misreport it as "removed nothing".
|
|
1400
|
-
const addNode = (0, WABinary_1.getBinaryNodeChild)(node, 'add')
|
|
1401
|
-
const removeNode = (0, WABinary_1.getBinaryNodeChild)(node, 'remove')
|
|
1402
|
-
const changedNode = addNode || removeNode
|
|
1403
|
-
if (!changedNode) {
|
|
1404
|
-
logger.debug({ node }, 'devices hash refresh, no add/remove list to report')
|
|
1405
|
-
break
|
|
1406
|
-
}
|
|
1407
|
-
const isAdded = !!addNode
|
|
1408
|
-
const devices = (0, WABinary_1.getBinaryNodeChildren)(changedNode, 'device')
|
|
1409
|
-
const deviceOwnerJid = from
|
|
1131
|
+
case 'devices':
|
|
1132
|
+
const devices = (0, WABinary_1.getBinaryNodeChildren)(child, 'device')
|
|
1133
|
+
const deviceOwnerJid = child.attrs.jid || child.attrs.lid
|
|
1410
1134
|
const deviceData = devices.map(d => ({
|
|
1411
1135
|
id: d.attrs.jid,
|
|
1412
1136
|
lid: d.attrs.lid,
|
|
1413
|
-
|
|
1414
|
-
keyIndex: d.attrs['key-index'] ? +d.attrs['key-index'] : undefined,
|
|
1137
|
+
keyIndex: d.attrs.key_index ? +d.attrs.key_index : undefined,
|
|
1415
1138
|
platform: d.attrs.platform || undefined,
|
|
1416
1139
|
isCompanion: d.attrs.companion === 'true' || undefined
|
|
1417
1140
|
}))
|
|
1418
|
-
|
|
1419
|
-
(0, WABinary_1.areJidsSameUser)(
|
|
1420
|
-
(0, WABinary_1.areJidsSameUser)(
|
|
1421
|
-
|
|
1422
|
-
logger.info({ deviceData
|
|
1423
|
-
|
|
1424
|
-
if (deviceOwnerJid) {
|
|
1425
|
-
ev.emit('devices.update', { id: deviceOwnerJid, devices: deviceData, isSelf
|
|
1141
|
+
if (
|
|
1142
|
+
(0, WABinary_1.areJidsSameUser)(child.attrs.jid, authState.creds.me.id) ||
|
|
1143
|
+
(0, WABinary_1.areJidsSameUser)(child.attrs.lid, authState.creds.me.lid)
|
|
1144
|
+
) {
|
|
1145
|
+
logger.info({ deviceData }, 'my own devices changed')
|
|
1146
|
+
ev.emit('devices.update', { id: deviceOwnerJid, devices: deviceData, isSelf: true })
|
|
1147
|
+
} else if (deviceOwnerJid) {
|
|
1148
|
+
ev.emit('devices.update', { id: deviceOwnerJid, devices: deviceData, isSelf: false })
|
|
1426
1149
|
}
|
|
1427
1150
|
break
|
|
1428
|
-
}
|
|
1429
1151
|
case 'server_sync':
|
|
1430
1152
|
const update = (0, WABinary_1.getBinaryNodeChild)(node, 'collection')
|
|
1431
1153
|
if (update) {
|
|
@@ -1433,18 +1155,13 @@ const makeMessagesRecvSocket = config => {
|
|
|
1433
1155
|
await resyncAppState([name], false)
|
|
1434
1156
|
}
|
|
1435
1157
|
break
|
|
1436
|
-
case 'picture':
|
|
1158
|
+
case 'picture':
|
|
1437
1159
|
const setPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'set')
|
|
1438
1160
|
const delPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'delete')
|
|
1439
|
-
|
|
1440
|
-
if (!pictureOwnerJid) {
|
|
1441
|
-
// a picture content hash is not a valid contact id — nothing usable to emit without "from"
|
|
1442
|
-
logger.debug({ node }, 'picture notification missing "from", skipping contacts.update')
|
|
1443
|
-
break
|
|
1444
|
-
}
|
|
1161
|
+
// TODO: WAJIDHASH stuff proper support inhouse
|
|
1445
1162
|
ev.emit('contacts.update', [
|
|
1446
1163
|
{
|
|
1447
|
-
id:
|
|
1164
|
+
id: (0, WABinary_1.jidNormalizedUser)(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '',
|
|
1448
1165
|
imgUrl: setPicture ? 'changed' : 'removed'
|
|
1449
1166
|
}
|
|
1450
1167
|
])
|
|
@@ -1461,7 +1178,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
1461
1178
|
}
|
|
1462
1179
|
}
|
|
1463
1180
|
break
|
|
1464
|
-
}
|
|
1465
1181
|
case 'account_sync':
|
|
1466
1182
|
if (child.tag === 'disappearing_mode') {
|
|
1467
1183
|
const newDuration = +child.attrs.duration
|
|
@@ -1477,60 +1193,18 @@ const makeMessagesRecvSocket = config => {
|
|
|
1477
1193
|
}
|
|
1478
1194
|
})
|
|
1479
1195
|
} else if (child.tag === 'blocklist') {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
// Full device-list sync: all linked devices + signed key-index-list.
|
|
1486
|
-
// Sent when a device is added, removed, or key index changes.
|
|
1487
|
-
const dhash = child.attrs.dhash
|
|
1488
|
-
const deviceNodes = (0, WABinary_1.getBinaryNodeChildren)(child, 'device')
|
|
1489
|
-
const keyIndexListNode = (0, WABinary_1.getBinaryNodeChild)(child, 'key-index-list')
|
|
1490
|
-
const devices = deviceNodes.map(d => ({
|
|
1491
|
-
jid: d.attrs.jid ? String(d.attrs.jid) : undefined,
|
|
1492
|
-
keyIndex: d.attrs['key-index'] ? +d.attrs['key-index'] : undefined
|
|
1493
|
-
}))
|
|
1494
|
-
logger.info({ dhash, deviceCount: devices.length }, 'account devices list synced')
|
|
1495
|
-
ev.emit('account.devices-synced', {
|
|
1496
|
-
dhash,
|
|
1497
|
-
devices,
|
|
1498
|
-
keyIndexListTimestamp: keyIndexListNode?.attrs?.ts ? +keyIndexListNode.attrs.ts : undefined,
|
|
1499
|
-
keyIndexList: keyIndexListNode?.content ? Buffer.from(keyIndexListNode.content) : undefined
|
|
1500
|
-
})
|
|
1501
|
-
}
|
|
1502
|
-
break
|
|
1503
|
-
case 'disappearing_mode': {
|
|
1504
|
-
// Same payload as the account_sync-nested variant above, but WhatsApp also
|
|
1505
|
-
// pushes it as its own top-level notification type on some accounts.
|
|
1506
|
-
const newDuration = +child.attrs.duration
|
|
1507
|
-
const timestamp = +child.attrs.t
|
|
1508
|
-
logger.info({ newDuration }, 'updated account disappearing mode')
|
|
1509
|
-
ev.emit('creds.update', {
|
|
1510
|
-
accountSettings: {
|
|
1511
|
-
...authState.creds.accountSettings,
|
|
1512
|
-
defaultDisappearingMode: {
|
|
1513
|
-
ephemeralExpiration: newDuration,
|
|
1514
|
-
ephemeralSettingTimestamp: timestamp
|
|
1515
|
-
}
|
|
1196
|
+
const blocklists = (0, WABinary_1.getBinaryNodeChildren)(child, 'item')
|
|
1197
|
+
for (const { attrs } of blocklists) {
|
|
1198
|
+
const blocklist = [attrs.jid]
|
|
1199
|
+
const type = attrs.action === 'block' ? 'add' : 'remove'
|
|
1200
|
+
ev.emit('blocklist.update', { blocklist, type })
|
|
1516
1201
|
}
|
|
1517
|
-
})
|
|
1518
|
-
break
|
|
1519
|
-
}
|
|
1520
|
-
case 'contacts': {
|
|
1521
|
-
// <update jid="..."/> = single contact touched; <update hash="..."/> or a
|
|
1522
|
-
// sibling <sync after="..."/> = list-level signal with no actionable jid.
|
|
1523
|
-
const updateChild = (0, WABinary_1.getBinaryNodeChild)(node, 'update')
|
|
1524
|
-
if (updateChild?.attrs?.jid) {
|
|
1525
|
-
ev.emit('contacts.update', [{ id: (0, WABinary_1.jidNormalizedUser)(updateChild.attrs.jid) }])
|
|
1526
|
-
} else {
|
|
1527
|
-
logger.debug({ node }, 'contacts list changed (hash/sync signal), no per-contact jid to update')
|
|
1528
1202
|
}
|
|
1529
1203
|
break
|
|
1530
|
-
}
|
|
1531
1204
|
case 'business':
|
|
1205
|
+
// SMB privacy / data-sharing settings sync push
|
|
1206
|
+
// (WhatsApp Web: WASmaxInBizSettingsSyncPrivacySettingRequest)
|
|
1532
1207
|
if (child?.tag === 'privacy') {
|
|
1533
|
-
// SMB privacy / data-sharing settings sync push
|
|
1534
1208
|
ev.emit('business.privacy-settings-sync', {
|
|
1535
1209
|
jid: from,
|
|
1536
1210
|
categories: (0, WABinary_1.getBinaryNodeChildren)(child, 'category').map(c => ({
|
|
@@ -1539,32 +1213,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
1539
1213
|
})),
|
|
1540
1214
|
attrs: child.attrs
|
|
1541
1215
|
})
|
|
1542
|
-
} else if (child?.tag === 'profile') {
|
|
1543
|
-
// Business profile updated for a contact (tag = profile version hash)
|
|
1544
|
-
ev.emit('contacts.update', [
|
|
1545
|
-
{
|
|
1546
|
-
id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid || from),
|
|
1547
|
-
businessProfileTag: child.attrs.tag
|
|
1548
|
-
}
|
|
1549
|
-
])
|
|
1550
|
-
} else if (child?.tag === 'verified_name') {
|
|
1551
|
-
// Verified business name changed — content is a proto payload
|
|
1552
|
-
ev.emit('contacts.update', [
|
|
1553
|
-
{
|
|
1554
|
-
id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid || from),
|
|
1555
|
-
verifiedName: {
|
|
1556
|
-
verifiedLevel: child.attrs.verified_level,
|
|
1557
|
-
serial: child.attrs.serial,
|
|
1558
|
-
version: child.attrs.v
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
])
|
|
1562
|
-
} else if (child?.tag === 'remove') {
|
|
1563
|
-
// Business account downgraded/removed. <remove jid="..."/> names the account;
|
|
1564
|
-
// <remove hash="..."/> is a list-level signal with no actionable jid.
|
|
1565
|
-
if (child.attrs.jid) {
|
|
1566
|
-
ev.emit('contacts.update', [{ id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid), isBusiness: false }])
|
|
1567
|
-
}
|
|
1568
1216
|
}
|
|
1569
1217
|
break
|
|
1570
1218
|
case 'hosted':
|
|
@@ -1838,10 +1486,11 @@ const makeMessagesRecvSocket = config => {
|
|
|
1838
1486
|
const newValue = ((await msgRetryCache.get(key)) || 0) + 1
|
|
1839
1487
|
await msgRetryCache.set(key, newValue)
|
|
1840
1488
|
}
|
|
1841
|
-
const sendMessagesAgain = async (key, ids, retryNode) => {
|
|
1489
|
+
const sendMessagesAgain = async (key, ids, retryNode, receiptNode) => {
|
|
1842
1490
|
const remoteJid = key.remoteJid
|
|
1843
1491
|
const participant = key.participant || remoteJid
|
|
1844
1492
|
const retryCount = +retryNode.attrs.count || 1
|
|
1493
|
+
const msgId = ids[0]
|
|
1845
1494
|
// Try to get messages from cache first, then fallback to getMessage
|
|
1846
1495
|
const msgs = []
|
|
1847
1496
|
for (const id of ids) {
|
|
@@ -1873,12 +1522,53 @@ const makeMessagesRecvSocket = config => {
|
|
|
1873
1522
|
// just re-send the message to everyone
|
|
1874
1523
|
// prevents the first message decryption failure
|
|
1875
1524
|
const sendToAll = !(0, WABinary_1.jidDecode)(participant)?.device
|
|
1525
|
+
const sessionId = signalRepository.jidToSignalProtocolAddress(participant)
|
|
1526
|
+
let injectedFromBundle = false
|
|
1527
|
+
if (typeof signalRepository.injectE2ESession === 'function' && typeof Utils_1.extractE2ESessionFromRetryReceipt === 'function') {
|
|
1528
|
+
const bundle = Utils_1.extractE2ESessionFromRetryReceipt(receiptNode)
|
|
1529
|
+
if (bundle) {
|
|
1530
|
+
try {
|
|
1531
|
+
await signalRepository.injectE2ESession({ jid: participant, session: bundle })
|
|
1532
|
+
injectedFromBundle = true
|
|
1533
|
+
logger.debug({ participant, retryCount }, 'injected session from retry receipt key bundle')
|
|
1534
|
+
} catch (error) {
|
|
1535
|
+
logger.warn({ error, participant }, 'failed to inject session from retry receipt')
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (!injectedFromBundle && signalRepository.getSessionInfo) {
|
|
1540
|
+
const receivedRegId = (0, WABinary_1.getBinaryNodeChildUInt)(receiptNode, 'registration', 4)
|
|
1541
|
+
if (typeof receivedRegId === 'number' && Number.isInteger(receivedRegId)) {
|
|
1542
|
+
const info = await signalRepository.getSessionInfo(participant)
|
|
1543
|
+
if (info && info.registrationId !== 0 && info.registrationId !== receivedRegId) {
|
|
1544
|
+
logger.info(
|
|
1545
|
+
{ participant, stored: info.registrationId, received: receivedRegId },
|
|
1546
|
+
'reg id mismatch on retry without bundle, deleting session'
|
|
1547
|
+
)
|
|
1548
|
+
await authState.keys.set({ session: { [sessionId]: null } })
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
const BASE_KEY_CHECK_RETRY = 2
|
|
1553
|
+
if (msgId && messageRetryManager && signalRepository.getSessionInfo) {
|
|
1554
|
+
const info = await signalRepository.getSessionInfo(participant)
|
|
1555
|
+
if (info) {
|
|
1556
|
+
if (retryCount === BASE_KEY_CHECK_RETRY) {
|
|
1557
|
+
messageRetryManager.saveBaseKey(sessionId, msgId, info.baseKey)
|
|
1558
|
+
} else if (retryCount > BASE_KEY_CHECK_RETRY) {
|
|
1559
|
+
if (messageRetryManager.hasSameBaseKey(sessionId, msgId, info.baseKey)) {
|
|
1560
|
+
logger.warn({ participant, retryCount }, 'base key collision on retry, forcing fresh session')
|
|
1561
|
+
await authState.keys.set({ session: { [sessionId]: null } })
|
|
1562
|
+
}
|
|
1563
|
+
messageRetryManager.deleteBaseKey(sessionId, msgId)
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1876
1567
|
// Check if we should recreate session for this retry
|
|
1877
1568
|
let shouldRecreateSession = false
|
|
1878
1569
|
let recreateReason = ''
|
|
1879
|
-
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
|
|
1570
|
+
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1 && !injectedFromBundle) {
|
|
1880
1571
|
try {
|
|
1881
|
-
const sessionId = signalRepository.jidToSignalProtocolAddress(participant)
|
|
1882
1572
|
const hasSession = await signalRepository.validateSession(participant)
|
|
1883
1573
|
const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists)
|
|
1884
1574
|
shouldRecreateSession = result.recreate
|
|
@@ -1891,7 +1581,9 @@ const makeMessagesRecvSocket = config => {
|
|
|
1891
1581
|
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry')
|
|
1892
1582
|
}
|
|
1893
1583
|
}
|
|
1894
|
-
|
|
1584
|
+
if (!injectedFromBundle) {
|
|
1585
|
+
await assertSessions([participant], true)
|
|
1586
|
+
}
|
|
1895
1587
|
if ((0, WABinary_1.isJidGroup)(remoteJid)) {
|
|
1896
1588
|
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } })
|
|
1897
1589
|
}
|
|
@@ -1940,51 +1632,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
1940
1632
|
const items = (0, WABinary_1.getBinaryNodeChildren)(content[0], 'item')
|
|
1941
1633
|
ids.push(...items.map(i => i.attrs.id))
|
|
1942
1634
|
}
|
|
1943
|
-
// E. Media Retry Notification Receipt
|
|
1944
|
-
// Server tells us a specific message's media should be re-fetched
|
|
1945
|
-
if (attrs.type === 'media-retry') {
|
|
1946
|
-
const mediaRetryNode =
|
|
1947
|
-
(0, WABinary_1.getBinaryNodeChild)(node, 'media-retry') ||
|
|
1948
|
-
(0, WABinary_1.getBinaryNodeChild)(node, 'media_retry')
|
|
1949
|
-
ev.emit('messages.media-retry', {
|
|
1950
|
-
ids,
|
|
1951
|
-
from: attrs.from,
|
|
1952
|
-
participant: attrs.participant,
|
|
1953
|
-
t: attrs.t,
|
|
1954
|
-
retryAttrs: mediaRetryNode?.attrs
|
|
1955
|
-
})
|
|
1956
|
-
await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack media-retry receipt'))
|
|
1957
|
-
return
|
|
1958
|
-
}
|
|
1959
|
-
// F. Server Error Receipt
|
|
1960
|
-
// Server signals a delivery error for one or more messages
|
|
1961
|
-
if (attrs.type === 'server-error') {
|
|
1962
|
-
const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error')
|
|
1963
|
-
ev.emit('messages.server-error', {
|
|
1964
|
-
ids,
|
|
1965
|
-
from: attrs.from,
|
|
1966
|
-
participant: attrs.participant,
|
|
1967
|
-
t: attrs.t,
|
|
1968
|
-
errorCode: errorNode?.attrs?.code || attrs.error_code,
|
|
1969
|
-
errorText: errorNode?.attrs?.text || errorNode?.content?.toString?.()
|
|
1970
|
-
})
|
|
1971
|
-
await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack server-error receipt'))
|
|
1972
|
-
return
|
|
1973
|
-
}
|
|
1974
|
-
// D. Receipt aggregation
|
|
1975
|
-
// Server sent a batched-receipt (receipt_agg attr) — emit dedicated event AND fall through
|
|
1976
|
-
// to the normal status-update pipeline so all IDs in the batch get their status updated.
|
|
1977
|
-
if (attrs.receipt_agg) {
|
|
1978
|
-
ev.emit('receipt.batched', {
|
|
1979
|
-
ids,
|
|
1980
|
-
from: attrs.from,
|
|
1981
|
-
participant: attrs.participant,
|
|
1982
|
-
type: attrs.type,
|
|
1983
|
-
t: attrs.t,
|
|
1984
|
-
receiptAgg: attrs.receipt_agg
|
|
1985
|
-
})
|
|
1986
|
-
// Do not return — let all IDs be processed by the normal receipt path below.
|
|
1987
|
-
}
|
|
1988
1635
|
try {
|
|
1989
1636
|
await Promise.all([
|
|
1990
1637
|
receiptMutex.mutex(async () => {
|
|
@@ -2029,7 +1676,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
2029
1676
|
try {
|
|
2030
1677
|
await updateSendMessageAgainCount(ids[0], key.participant)
|
|
2031
1678
|
logger.debug({ attrs, key }, 'recv retry request')
|
|
2032
|
-
await sendMessagesAgain(key, ids, retryNode)
|
|
1679
|
+
await sendMessagesAgain(key, ids, retryNode, node)
|
|
2033
1680
|
} catch (error) {
|
|
2034
1681
|
logger.error(
|
|
2035
1682
|
{ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' },
|
|
@@ -2419,19 +2066,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
2419
2066
|
}
|
|
2420
2067
|
}
|
|
2421
2068
|
}
|
|
2422
|
-
// group_info roster: <group_info><user jid=.. state=.. user_pn=..><device jid=..
|
|
2423
|
-
// platform=..><capability ver=../></device></user>...</group_info>. Baileys previously
|
|
2424
|
-
// only kept the flat group_info-level attrs (call.payload), dropping the roster entirely.
|
|
2425
|
-
const parseGroupCallRoster = groupInfoNode =>
|
|
2426
|
-
(0, WABinary_1.getBinaryNodeChildren)(groupInfoNode, 'user').map(userNode => ({
|
|
2427
|
-
jid: userNode.attrs.jid,
|
|
2428
|
-
state: userNode.attrs.state,
|
|
2429
|
-
userPn: userNode.attrs.user_pn,
|
|
2430
|
-
devices: (0, WABinary_1.getBinaryNodeChildren)(userNode, 'device').map(d => ({
|
|
2431
|
-
jid: d.attrs.jid,
|
|
2432
|
-
platform: d.attrs.platform
|
|
2433
|
-
}))
|
|
2434
|
-
}))
|
|
2435
2069
|
const handleCall = async node => {
|
|
2436
2070
|
try {
|
|
2437
2071
|
const { attrs } = node
|
|
@@ -2459,45 +2093,10 @@ const makeMessagesRecvSocket = config => {
|
|
|
2459
2093
|
}
|
|
2460
2094
|
}
|
|
2461
2095
|
if (status === 'offer') {
|
|
2462
|
-
|
|
2463
|
-
const audioNodes = (0, WABinary_1.getBinaryNodeChildren)(infoChild, 'audio')
|
|
2464
|
-
const silenceNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'silence')
|
|
2465
|
-
call.isVideo = !!videoNode
|
|
2096
|
+
call.isVideo = !!(0, WABinary_1.getBinaryNodeChild)(infoChild, 'video')
|
|
2466
2097
|
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid']
|
|
2467
2098
|
call.groupJid = infoChild.attrs['group-jid']
|
|
2468
|
-
// lightweight=1 marks a silent/wave-style group ring (no full ringing UX expected)
|
|
2469
|
-
call.isLightweight = infoChild.attrs.lightweight === '1'
|
|
2470
|
-
if (silenceNode?.attrs?.reason) {
|
|
2471
|
-
call.silenceReason = silenceNode.attrs.reason
|
|
2472
|
-
}
|
|
2473
|
-
// Wire attrs are "enc"/"rate" (audio) and "dec" (video) — not "codec".
|
|
2474
|
-
// <audio> can repeat once per supported sample rate.
|
|
2475
|
-
if (audioNodes.length) {
|
|
2476
|
-
call.audioCodecs = audioNodes.map(n => ({ enc: n.attrs.enc, rate: n.attrs.rate ? +n.attrs.rate : undefined }))
|
|
2477
|
-
call.audioCodec = audioNodes[0].attrs.enc
|
|
2478
|
-
}
|
|
2479
|
-
if (videoNode?.attrs?.dec) {
|
|
2480
|
-
call.videoCodec = videoNode.attrs.dec
|
|
2481
|
-
}
|
|
2482
|
-
// Decrypt Signal-encrypted callKey from <enc> child
|
|
2483
|
-
const encNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc')
|
|
2484
|
-
if (encNode?.content) {
|
|
2485
|
-
try {
|
|
2486
|
-
const encType = encNode.attrs.type || 'msg'
|
|
2487
|
-
const ciphertext = Buffer.isBuffer(encNode.content) ? encNode.content : Buffer.from(encNode.content)
|
|
2488
|
-
const decrypted = await signalRepository.decryptMessage({ jid: attrs.from, type: encType, ciphertext })
|
|
2489
|
-
const unpadded = (0, Utils_1.unpadRandomMax16)(decrypted)
|
|
2490
|
-
const callMsg = index_js_1.proto.Message.decode(unpadded)
|
|
2491
|
-
if (callMsg?.call?.callKey?.length) {
|
|
2492
|
-
call.callKey = Buffer.from(callMsg.call.callKey)
|
|
2493
|
-
}
|
|
2494
|
-
} catch (e) {
|
|
2495
|
-
logger.debug({ e }, 'failed to decrypt call enc')
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
2099
|
await callOfferCache.set(call.id, call)
|
|
2499
|
-
} else if (status === 'waiting_room_request') {
|
|
2500
|
-
call.peerJid = infoChild.attrs.from || infoChild.attrs['peer-jid'] || infoChild.attrs['user-jid']
|
|
2501
2100
|
}
|
|
2502
2101
|
const existingCall = await callOfferCache.get(call.id)
|
|
2503
2102
|
// use existing call info to populate this event
|
|
@@ -2506,41 +2105,8 @@ const makeMessagesRecvSocket = config => {
|
|
|
2506
2105
|
call.isGroup = existingCall.isGroup
|
|
2507
2106
|
call.callerPn = call.callerPn || existingCall.callerPn
|
|
2508
2107
|
}
|
|
2509
|
-
// Enrich event payload for signalling-only statuses
|
|
2510
|
-
if (status === 'peer_state') {
|
|
2511
|
-
const stateChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'peer_state')
|
|
2512
|
-
call.state = stateChild?.attrs?.state || infoChild.attrs?.state
|
|
2513
|
-
} else if (status === 'group_info') {
|
|
2514
|
-
call.payload = infoChild.attrs
|
|
2515
|
-
call.participants = parseGroupCallRoster(infoChild)
|
|
2516
|
-
} else if (status === 'video_state') {
|
|
2517
|
-
const vsChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'video_state')
|
|
2518
|
-
const enabledRaw = vsChild?.attrs?.enabled ?? infoChild.attrs?.enabled
|
|
2519
|
-
call.enabled = enabledRaw === 'true' || enabledRaw === '1'
|
|
2520
|
-
} else if (status === 'enc_rekey') {
|
|
2521
|
-
const rekeyChild =
|
|
2522
|
-
(0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc-rekey') ||
|
|
2523
|
-
(0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc_rekey')
|
|
2524
|
-
if (rekeyChild?.content && Buffer.isBuffer(rekeyChild.content)) {
|
|
2525
|
-
try {
|
|
2526
|
-
call.rekeyPayload = (0, Utils_1.decodeE2eRekeyPayload)(rekeyChild.content)
|
|
2527
|
-
} catch (e) {
|
|
2528
|
-
logger.debug({ e }, 'failed to decode enc-rekey payload in handleCall')
|
|
2529
|
-
}
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
2108
|
// delete data once call has ended
|
|
2533
|
-
if (
|
|
2534
|
-
status === 'reject' ||
|
|
2535
|
-
status === 'accept' ||
|
|
2536
|
-
status === 'timeout' ||
|
|
2537
|
-
status === 'terminate' ||
|
|
2538
|
-
status === 'reject_do_not_disturb' ||
|
|
2539
|
-
status === 'mic_permission_denied' ||
|
|
2540
|
-
status === 'camera_permission_denied' ||
|
|
2541
|
-
status === 'remote_busy' ||
|
|
2542
|
-
status === 'remote_offline'
|
|
2543
|
-
) {
|
|
2109
|
+
if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
|
|
2544
2110
|
await callOfferCache.del(call.id)
|
|
2545
2111
|
}
|
|
2546
2112
|
await normalizeCallEventJids(call, infoChild)
|
|
@@ -2555,24 +2121,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
2555
2121
|
// <mute_v2>, <transport>, … each with a call-id) instead of wrapped in <call>.
|
|
2556
2122
|
// This additively handles those: emit a 'call' event for state stanzas and ack ALL
|
|
2557
2123
|
// of them (otherwise WhatsApp keeps redelivering). The <call> path above is untouched.
|
|
2558
|
-
const CALL_STATE_TAGS = new Set([
|
|
2559
|
-
'offer',
|
|
2560
|
-
'offer_notice',
|
|
2561
|
-
'terminate',
|
|
2562
|
-
'accept',
|
|
2563
|
-
'reject',
|
|
2564
|
-
'preaccept',
|
|
2565
|
-
'accept_ack',
|
|
2566
|
-
'enc-rekey',
|
|
2567
|
-
'enc_rekey',
|
|
2568
|
-
'peer_state',
|
|
2569
|
-
'group_info',
|
|
2570
|
-
'video_state',
|
|
2571
|
-
'video_state_ack',
|
|
2572
|
-
'flow_control',
|
|
2573
|
-
'mute_v2',
|
|
2574
|
-
'waiting_room_request'
|
|
2575
|
-
])
|
|
2124
|
+
const CALL_STATE_TAGS = new Set(['offer', 'offer_notice', 'terminate', 'accept', 'reject', 'preaccept'])
|
|
2576
2125
|
const handleStandaloneCallStanza = async node => {
|
|
2577
2126
|
try {
|
|
2578
2127
|
if (!CALL_STATE_TAGS.has(node.tag)) {
|
|
@@ -2592,24 +2141,9 @@ const makeMessagesRecvSocket = config => {
|
|
|
2592
2141
|
status
|
|
2593
2142
|
}
|
|
2594
2143
|
if (status === 'offer') {
|
|
2595
|
-
|
|
2596
|
-
const audioNodes = (0, WABinary_1.getBinaryNodeChildren)(node, 'audio')
|
|
2597
|
-
const silenceNode = (0, WABinary_1.getBinaryNodeChild)(node, 'silence')
|
|
2598
|
-
call.isVideo = !!videoNode
|
|
2144
|
+
call.isVideo = !!(0, WABinary_1.getBinaryNodeChild)(node, 'video')
|
|
2599
2145
|
call.isGroup = attrs.type === 'group' || !!attrs['group-jid']
|
|
2600
2146
|
call.groupJid = attrs['group-jid']
|
|
2601
|
-
call.isLightweight = attrs.lightweight === '1'
|
|
2602
|
-
if (silenceNode?.attrs?.reason) {
|
|
2603
|
-
call.silenceReason = silenceNode.attrs.reason
|
|
2604
|
-
}
|
|
2605
|
-
// Wire attrs are "enc"/"rate" (audio) and "dec" (video) — not "codec".
|
|
2606
|
-
if (audioNodes.length) {
|
|
2607
|
-
call.audioCodecs = audioNodes.map(n => ({ enc: n.attrs.enc, rate: n.attrs.rate ? +n.attrs.rate : undefined }))
|
|
2608
|
-
call.audioCodec = audioNodes[0].attrs.enc
|
|
2609
|
-
}
|
|
2610
|
-
if (videoNode?.attrs?.dec) {
|
|
2611
|
-
call.videoCodec = videoNode.attrs.dec
|
|
2612
|
-
}
|
|
2613
2147
|
if (callId) {
|
|
2614
2148
|
await callOfferCache.set(callId, call)
|
|
2615
2149
|
}
|
|
@@ -2620,45 +2154,7 @@ const makeMessagesRecvSocket = config => {
|
|
|
2620
2154
|
call.isGroup = existingCall.isGroup
|
|
2621
2155
|
call.callerPn = call.callerPn || existingCall.callerPn
|
|
2622
2156
|
}
|
|
2623
|
-
|
|
2624
|
-
if (status === 'peer_state') {
|
|
2625
|
-
const stateChild = (0, WABinary_1.getBinaryNodeChild)(node, 'peer_state')
|
|
2626
|
-
call.state = stateChild?.attrs?.state || attrs?.state
|
|
2627
|
-
} else if (status === 'group_info') {
|
|
2628
|
-
call.payload = attrs
|
|
2629
|
-
call.participants = parseGroupCallRoster(node)
|
|
2630
|
-
} else if (status === 'video_state') {
|
|
2631
|
-
const vsChild = (0, WABinary_1.getBinaryNodeChild)(node, 'video_state')
|
|
2632
|
-
const enabledRaw = vsChild?.attrs?.enabled ?? attrs?.enabled
|
|
2633
|
-
call.enabled = enabledRaw === 'true' || enabledRaw === '1'
|
|
2634
|
-
} else if (status === 'enc_rekey') {
|
|
2635
|
-
const rekeyChild =
|
|
2636
|
-
(0, WABinary_1.getBinaryNodeChild)(node, 'enc-rekey') || (0, WABinary_1.getBinaryNodeChild)(node, 'enc_rekey')
|
|
2637
|
-
if (rekeyChild?.content && Buffer.isBuffer(rekeyChild.content)) {
|
|
2638
|
-
try {
|
|
2639
|
-
call.rekeyPayload = (0, Utils_1.decodeE2eRekeyPayload)(rekeyChild.content)
|
|
2640
|
-
} catch (e) {
|
|
2641
|
-
logger.debug({ e }, 'failed to decode enc-rekey payload in standalone call stanza')
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
} else if (status === 'mute') {
|
|
2645
|
-
// mute_v2 IS the top-level tag on real traffic (no wrapping/nested child) —
|
|
2646
|
-
// attrs are { call-id, call-creator, mute-state: "0"|"1" }, never
|
|
2647
|
-
// muted/audio_muted, and there's no nested <audio> child.
|
|
2648
|
-
call.muted = node.attrs['mute-state'] === '1'
|
|
2649
|
-
}
|
|
2650
|
-
if (
|
|
2651
|
-
callId &&
|
|
2652
|
-
(status === 'reject' ||
|
|
2653
|
-
status === 'accept' ||
|
|
2654
|
-
status === 'timeout' ||
|
|
2655
|
-
status === 'terminate' ||
|
|
2656
|
-
status === 'reject_do_not_disturb' ||
|
|
2657
|
-
status === 'mic_permission_denied' ||
|
|
2658
|
-
status === 'camera_permission_denied' ||
|
|
2659
|
-
status === 'remote_busy' ||
|
|
2660
|
-
status === 'remote_offline')
|
|
2661
|
-
) {
|
|
2157
|
+
if (callId && (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate')) {
|
|
2662
2158
|
await callOfferCache.del(callId)
|
|
2663
2159
|
}
|
|
2664
2160
|
await normalizeCallEventJids(call, node)
|
|
@@ -2727,37 +2223,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
2727
2223
|
// }
|
|
2728
2224
|
}
|
|
2729
2225
|
}
|
|
2730
|
-
const handleChatstate = async node => {
|
|
2731
|
-
const { from, state } = node.attrs
|
|
2732
|
-
if (!from) return
|
|
2733
|
-
const isTyping = state === 'typing'
|
|
2734
|
-
ev.emit('presence.update', [
|
|
2735
|
-
{
|
|
2736
|
-
id: from,
|
|
2737
|
-
presences: {
|
|
2738
|
-
[from]: isTyping ? 'composing' : 'paused'
|
|
2739
|
-
}
|
|
2740
|
-
}
|
|
2741
|
-
])
|
|
2742
|
-
}
|
|
2743
|
-
const handlePresence = async node => {
|
|
2744
|
-
const { from, type } = node.attrs
|
|
2745
|
-
if (!from || !type) return
|
|
2746
|
-
const presenceMap = {
|
|
2747
|
-
available: 'available',
|
|
2748
|
-
unavailable: 'unavailable'
|
|
2749
|
-
}
|
|
2750
|
-
const presence = presenceMap[type] || type
|
|
2751
|
-
ev.emit('presence.update', [
|
|
2752
|
-
{
|
|
2753
|
-
id: from,
|
|
2754
|
-
presences: {
|
|
2755
|
-
[from]: presence
|
|
2756
|
-
},
|
|
2757
|
-
lastSeen: node.attrs.t ? Number(node.attrs.t) * 1000 : undefined
|
|
2758
|
-
}
|
|
2759
|
-
])
|
|
2760
|
-
}
|
|
2761
2226
|
/// processes a node with the given function
|
|
2762
2227
|
/// and adds the task to the existing buffer if we're buffering events
|
|
2763
2228
|
const processNodeWithBuffer = async (node, identifier, exec) => {
|
|
@@ -2809,33 +2274,8 @@ const makeMessagesRecvSocket = config => {
|
|
|
2809
2274
|
})
|
|
2810
2275
|
// additive: top-level call-signalling stanzas (some accounts send these instead of <call>)
|
|
2811
2276
|
for (const callTag of [
|
|
2812
|
-
'offer',
|
|
2813
|
-
'
|
|
2814
|
-
'terminate',
|
|
2815
|
-
'accept',
|
|
2816
|
-
'reject',
|
|
2817
|
-
'preaccept',
|
|
2818
|
-
'accept_ack',
|
|
2819
|
-
'enc-rekey',
|
|
2820
|
-
'enc_rekey',
|
|
2821
|
-
'peer_state',
|
|
2822
|
-
'group_info',
|
|
2823
|
-
'video_state',
|
|
2824
|
-
'video_state_ack',
|
|
2825
|
-
'flow_control',
|
|
2826
|
-
'transport',
|
|
2827
|
-
'video',
|
|
2828
|
-
'duration',
|
|
2829
|
-
'mute_v2',
|
|
2830
|
-
'lobby',
|
|
2831
|
-
'heartbeat',
|
|
2832
|
-
'relaylatency',
|
|
2833
|
-
'link_query',
|
|
2834
|
-
'waiting_room_request',
|
|
2835
|
-
// Present in Utils/stanza-ack.js's CALL_STANZA_TAGS (ack builder already treats it as a
|
|
2836
|
-
// call stanza) but was missing here — meant it was never routed/acked, so real traffic
|
|
2837
|
-
// (confirmed via live capture) would just get redelivered forever.
|
|
2838
|
-
'group_update'
|
|
2277
|
+
'offer', 'offer_notice', 'terminate', 'accept', 'reject', 'preaccept',
|
|
2278
|
+
'transport', 'video', 'duration', 'mute_v2', 'lobby', 'heartbeat', 'relaylatency', 'link_query'
|
|
2839
2279
|
]) {
|
|
2840
2280
|
ws.on('CB:' + callTag, node => {
|
|
2841
2281
|
nodelogger(node)
|
|
@@ -2850,18 +2290,6 @@ const makeMessagesRecvSocket = config => {
|
|
|
2850
2290
|
nodelogger(node)
|
|
2851
2291
|
await processNode('notification', node, 'handling notification', handleNotification)
|
|
2852
2292
|
})
|
|
2853
|
-
ws.on('CB:status', async node => {
|
|
2854
|
-
nodelogger(node)
|
|
2855
|
-
await handleNewsletterStatus(node).catch(error => onUnexpectedError(error, 'handling newsletter status'))
|
|
2856
|
-
})
|
|
2857
|
-
ws.on('CB:chatstate', async node => {
|
|
2858
|
-
nodelogger(node)
|
|
2859
|
-
await handleChatstate(node).catch(error => onUnexpectedError(error, 'handling chatstate'))
|
|
2860
|
-
})
|
|
2861
|
-
ws.on('CB:presence', async node => {
|
|
2862
|
-
nodelogger(node)
|
|
2863
|
-
await handlePresence(node).catch(error => onUnexpectedError(error, 'handling presence'))
|
|
2864
|
-
})
|
|
2865
2293
|
ws.on('CB:ack,class:message', node => {
|
|
2866
2294
|
nodelogger(node)
|
|
2867
2295
|
handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'))
|