@badzz88/baileys 8.5.4 → 8.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/WAProto/WAProto.proto +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- 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 +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- 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 +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/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/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
|
@@ -14,7 +14,7 @@ const {
|
|
|
14
14
|
} = require('../Utils/index.js')
|
|
15
15
|
const { isJidGroup, isPnUser, jidNormalizedUser, STORIES_JID } = require('../WABinary/index.js')
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class InteractiveMessageHandler {
|
|
18
18
|
constructor(waUploadToServer, relayMessageFn, config, sock) {
|
|
19
19
|
this.relayMessage = relayMessageFn
|
|
20
20
|
this.waUploadToServer = waUploadToServer
|
|
@@ -24,12 +24,13 @@ class badzz88 {
|
|
|
24
24
|
detectType(content) {
|
|
25
25
|
if (content.requestPaymentMessage) return 'PAYMENT'
|
|
26
26
|
if (content.productMessage) return 'PRODUCT'
|
|
27
|
-
if (content.interactiveButtons) return 'INTERACTIVE_BUTTONS'
|
|
28
27
|
if (content.interactiveMessage) return 'INTERACTIVE'
|
|
29
28
|
if (content.albumMessage || content.album) return 'ALBUM'
|
|
30
29
|
if (content.eventMessage) return 'EVENT'
|
|
31
30
|
if (content.pollResultMessage) return 'POLL_RESULT'
|
|
32
31
|
if (content.groupStatusMessage) return 'GROUP_STORY'
|
|
32
|
+
if (content.orderMessage) return 'ORDER'
|
|
33
|
+
if (content.groupLabel) return 'GROUP_LABEL'
|
|
33
34
|
return null
|
|
34
35
|
}
|
|
35
36
|
async handlePayment(content, quoted) {
|
|
@@ -222,114 +223,6 @@ class badzz88 {
|
|
|
222
223
|
}
|
|
223
224
|
return { interactiveMessage }
|
|
224
225
|
}
|
|
225
|
-
async handleInteractiveButtons(content, _jid, _quoted) {
|
|
226
|
-
const {
|
|
227
|
-
text,
|
|
228
|
-
caption,
|
|
229
|
-
title,
|
|
230
|
-
subtitle,
|
|
231
|
-
footer,
|
|
232
|
-
interactiveButtons,
|
|
233
|
-
hasMediaAttachment,
|
|
234
|
-
image,
|
|
235
|
-
video,
|
|
236
|
-
document,
|
|
237
|
-
mimetype,
|
|
238
|
-
jpegThumbnail,
|
|
239
|
-
location,
|
|
240
|
-
product,
|
|
241
|
-
businessOwnerJid
|
|
242
|
-
} = content
|
|
243
|
-
const bodyText = text || caption || ''
|
|
244
|
-
const buttons = interactiveButtons.map(btn => ({
|
|
245
|
-
name: btn.name,
|
|
246
|
-
buttonParamsJson:
|
|
247
|
-
typeof btn.buttonParamsJson === 'string' ? btn.buttonParamsJson : JSON.stringify(btn.buttonParamsJson)
|
|
248
|
-
}))
|
|
249
|
-
let headerContent = {}
|
|
250
|
-
let mediaAttached = typeof hasMediaAttachment === 'boolean' ? hasMediaAttachment : false
|
|
251
|
-
if (image) {
|
|
252
|
-
const src = typeof image === 'object' && image.url ? { image: { url: image.url } } : { image }
|
|
253
|
-
const uploaded = await prepareWAMessageMedia(src, { upload: this.waUploadToServer })
|
|
254
|
-
headerContent = { ...uploaded }
|
|
255
|
-
mediaAttached = typeof hasMediaAttachment === 'boolean' ? hasMediaAttachment : true
|
|
256
|
-
} else if (video) {
|
|
257
|
-
const src = typeof video === 'object' && video.url ? { video: { url: video.url } } : { video }
|
|
258
|
-
const uploaded = await prepareWAMessageMedia(src, { upload: this.waUploadToServer })
|
|
259
|
-
headerContent = { ...uploaded }
|
|
260
|
-
mediaAttached = typeof hasMediaAttachment === 'boolean' ? hasMediaAttachment : true
|
|
261
|
-
} else if (document) {
|
|
262
|
-
const docPayload =
|
|
263
|
-
typeof document === 'object' && document.url ? { document: { url: document.url } } : { document }
|
|
264
|
-
if (mimetype) docPayload.mimetype = mimetype
|
|
265
|
-
const uploaded = await prepareWAMessageMedia(docPayload, { upload: this.waUploadToServer })
|
|
266
|
-
if (jpegThumbnail) {
|
|
267
|
-
uploaded.documentMessage.jpegThumbnail =
|
|
268
|
-
typeof jpegThumbnail === 'string' ? Buffer.from(jpegThumbnail, 'base64') : jpegThumbnail
|
|
269
|
-
}
|
|
270
|
-
headerContent = { ...uploaded }
|
|
271
|
-
mediaAttached = typeof hasMediaAttachment === 'boolean' ? hasMediaAttachment : true
|
|
272
|
-
} else if (location) {
|
|
273
|
-
headerContent = {
|
|
274
|
-
locationMessage: {
|
|
275
|
-
degreesLatitude: location.degressLatitude || location.degreesLatitude || 0,
|
|
276
|
-
degreesLongitude: location.degressLongitude || location.degreesLongitude || 0,
|
|
277
|
-
name: location.name || ''
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
mediaAttached = typeof hasMediaAttachment === 'boolean' ? hasMediaAttachment : true
|
|
281
|
-
} else if (product) {
|
|
282
|
-
let productImage
|
|
283
|
-
if (product.productImage) {
|
|
284
|
-
const imgSrc =
|
|
285
|
-
typeof product.productImage === 'object' && product.productImage.url
|
|
286
|
-
? { image: { url: product.productImage.url } }
|
|
287
|
-
: { image: product.productImage }
|
|
288
|
-
const uploaded = await prepareWAMessageMedia(imgSrc, { upload: this.waUploadToServer })
|
|
289
|
-
productImage = uploaded.imageMessage
|
|
290
|
-
}
|
|
291
|
-
headerContent = {
|
|
292
|
-
productMessage: {
|
|
293
|
-
product: {
|
|
294
|
-
productImage,
|
|
295
|
-
productId: product.productId,
|
|
296
|
-
title: product.title,
|
|
297
|
-
description: product.description,
|
|
298
|
-
currencyCode: product.currencyCode || 'IDR',
|
|
299
|
-
priceAmount1000: product.priceAmount1000,
|
|
300
|
-
retailerId: product.retailerId,
|
|
301
|
-
url: product.url,
|
|
302
|
-
productImageCount: product.productImageCount || 1
|
|
303
|
-
},
|
|
304
|
-
businessOwnerJid: businessOwnerJid || '0@s.whatsapp.net'
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
mediaAttached = typeof hasMediaAttachment === 'boolean' ? hasMediaAttachment : true
|
|
308
|
-
}
|
|
309
|
-
const interactiveMessage = {
|
|
310
|
-
body: { text: bodyText },
|
|
311
|
-
footer: { text: footer || '' },
|
|
312
|
-
header: {
|
|
313
|
-
title: title || '',
|
|
314
|
-
subtitle: subtitle || '',
|
|
315
|
-
hasMediaAttachment: mediaAttached,
|
|
316
|
-
...headerContent
|
|
317
|
-
},
|
|
318
|
-
nativeFlowMessage: { buttons }
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
viewOnceMessage: {
|
|
322
|
-
message: {
|
|
323
|
-
messageContextInfo: {
|
|
324
|
-
deviceListMetadata: {},
|
|
325
|
-
deviceListMetadataVersion: 2,
|
|
326
|
-
messageSecret: crypto.randomBytes(32)
|
|
327
|
-
},
|
|
328
|
-
interactiveMessage
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
226
|
async handleAlbum(content, jid, quoted) {
|
|
334
227
|
const array = content.albumMessage || content.album
|
|
335
228
|
const ctxInfo = content.contextInfo || {}
|
|
@@ -344,7 +237,7 @@ class badzz88 {
|
|
|
344
237
|
expectedVideoCount: array.filter(a => 'video' in a).length
|
|
345
238
|
}
|
|
346
239
|
},
|
|
347
|
-
{ userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || '') }
|
|
240
|
+
{ userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || ''), browser: this.config?.browser }
|
|
348
241
|
)
|
|
349
242
|
await this.relayMessage(jid, album.message, {
|
|
350
243
|
messageId: album.key.id
|
|
@@ -394,18 +287,18 @@ class badzz88 {
|
|
|
394
287
|
joinLink: eventData.joinLink || '',
|
|
395
288
|
startTime:
|
|
396
289
|
typeof eventData.startTime === 'string'
|
|
397
|
-
? parseInt(eventData.startTime
|
|
290
|
+
? parseInt(eventData.startTime)
|
|
398
291
|
: eventData.startTime || Date.now(),
|
|
399
292
|
endTime:
|
|
400
293
|
typeof eventData.endTime === 'string'
|
|
401
|
-
? parseInt(eventData.endTime
|
|
294
|
+
? parseInt(eventData.endTime)
|
|
402
295
|
: eventData.endTime || Date.now() + 3600000,
|
|
403
296
|
extraGuestsAllowed: eventData.extraGuestsAllowed !== false
|
|
404
297
|
}
|
|
405
298
|
}
|
|
406
299
|
}
|
|
407
300
|
},
|
|
408
|
-
{ quoted, userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || '') }
|
|
301
|
+
{ quoted, userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || ''), browser: this.config?.browser }
|
|
409
302
|
)
|
|
410
303
|
await this.relayMessage(jid, msg.message, {
|
|
411
304
|
messageId: msg.key.id
|
|
@@ -426,7 +319,7 @@ class badzz88 {
|
|
|
426
319
|
}))
|
|
427
320
|
}
|
|
428
321
|
},
|
|
429
|
-
{ quoted, userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || '') }
|
|
322
|
+
{ quoted, userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || ''), browser: this.config?.browser }
|
|
430
323
|
)
|
|
431
324
|
await this.relayMessage(jid, msg.message, {
|
|
432
325
|
messageId: msg.key.id
|
|
@@ -454,6 +347,55 @@ class badzz88 {
|
|
|
454
347
|
messageId: generateMessageID()
|
|
455
348
|
})
|
|
456
349
|
}
|
|
350
|
+
async handleOrderMessage(content, jid, quoted) {
|
|
351
|
+
const orderData = content.orderMessage
|
|
352
|
+
const orderMsg = await generateWAMessageFromContent(jid, {
|
|
353
|
+
orderMessage: {
|
|
354
|
+
orderId: orderData.orderId || 'ORDER' + generateMessageID(),
|
|
355
|
+
thumbnail: orderData.thumbnail || null,
|
|
356
|
+
itemCount: orderData.itemCount || 0,
|
|
357
|
+
status: orderData.status || 'ACCEPTED',
|
|
358
|
+
surface: orderData.surface || 'CATALOG',
|
|
359
|
+
message: orderData.message,
|
|
360
|
+
orderTitle: orderData.orderTitle,
|
|
361
|
+
sellerJid: orderData.sellerJid || jid,
|
|
362
|
+
token: orderData.token || generateMessageID(),
|
|
363
|
+
totalAmount1000: orderData.totalAmount1000 || 0,
|
|
364
|
+
totalCurrencyCode: orderData.totalCurrencyCode || 'IDR',
|
|
365
|
+
messageVersion: 2
|
|
366
|
+
}
|
|
367
|
+
}, { quoted, browser: this.config?.browser })
|
|
368
|
+
await this.relayMessage(jid, orderMsg.message, {
|
|
369
|
+
messageId: orderMsg.key.id
|
|
370
|
+
})
|
|
371
|
+
return orderMsg
|
|
372
|
+
}
|
|
373
|
+
async handleGbLabel(content, jid) {
|
|
374
|
+
const labelData = content.groupLabel
|
|
375
|
+
if (!isJidGroup(jid)) {
|
|
376
|
+
throw new Error('handleGbLabel requires a group jid')
|
|
377
|
+
}
|
|
378
|
+
const msg = await generateWAMessageFromContent(jid, {
|
|
379
|
+
protocolMessage: {
|
|
380
|
+
type: 'GROUP_MEMBER_LABEL_CHANGE',
|
|
381
|
+
memberLabel: {
|
|
382
|
+
label: labelData.labelText.slice(0, 30)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}, { browser: this.config?.browser })
|
|
386
|
+
return await this.relayMessage(jid, msg.message, {
|
|
387
|
+
additionalNodes: [
|
|
388
|
+
{
|
|
389
|
+
tag: 'meta',
|
|
390
|
+
attrs: {
|
|
391
|
+
tag_reason: 'user_update',
|
|
392
|
+
appdata: 'member_tag'
|
|
393
|
+
},
|
|
394
|
+
content: undefined
|
|
395
|
+
}
|
|
396
|
+
]
|
|
397
|
+
})
|
|
398
|
+
}
|
|
457
399
|
async sendStatusWhatsApp(content, jids = []) {
|
|
458
400
|
const userJid = jidNormalizedUser(this.sock.authState.creds.me.id)
|
|
459
401
|
const allUsers = new Set()
|
|
@@ -557,7 +499,8 @@ class badzz88 {
|
|
|
557
499
|
}
|
|
558
500
|
}
|
|
559
501
|
const statusMsg = await generateWAMessageFromContent(normalizedId, protocolMessage, {
|
|
560
|
-
userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || '')
|
|
502
|
+
userJid: jidNormalizedUser(this.sock.authState?.creds?.me?.id || ''),
|
|
503
|
+
browser: this.config?.browser
|
|
561
504
|
})
|
|
562
505
|
await this.relayMessage(normalizedId, statusMsg.message, {
|
|
563
506
|
additionalNodes: [
|
|
@@ -576,4 +519,4 @@ class badzz88 {
|
|
|
576
519
|
}
|
|
577
520
|
}
|
|
578
521
|
|
|
579
|
-
module.exports = {
|
|
522
|
+
module.exports = { InteractiveMessageHandler }
|
package/lib/Socket/interop.js
CHANGED
|
@@ -5,36 +5,47 @@ exports.makeInteropSocket = void 0
|
|
|
5
5
|
const { getBinaryNodeChild, getBinaryNodeChildren, S_WHATSAPP_NET } = require('../WABinary')
|
|
6
6
|
const { executeWMexQuery } = require('./mex')
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
const INTEROP_MEX_QUERY_IDS = {
|
|
10
|
-
CREATE_GROUP: '
|
|
11
|
-
LEAVE_GROUP: '
|
|
12
|
-
ADD_PARTICIPANTS: '
|
|
13
|
-
QUERY_GROUP_INFO: '
|
|
14
|
-
PRIVACY_SETTINGS_QUERY: '
|
|
15
|
-
PRIVACY_SETTINGS_UPDATE: '
|
|
16
|
-
PRIVACY_SETTINGS_WITH_CONTACT_LIST: '
|
|
9
|
+
CREATE_GROUP: '25726817620301612', // GroupsCreateInteropGroup
|
|
10
|
+
LEAVE_GROUP: '25346167795013270', // LeaveInteropGroup
|
|
11
|
+
ADD_PARTICIPANTS: '25732168276369452', // AddParticipantsToInteropGroup
|
|
12
|
+
QUERY_GROUP_INFO: '32734144032867936', // QueryInteropGroupInfo
|
|
13
|
+
PRIVACY_SETTINGS_QUERY: '24849123668112656', // InteropPrivacySettingsQuery
|
|
14
|
+
PRIVACY_SETTINGS_UPDATE: '25421856497452764', // InteropPrivacySettingsUpdate
|
|
15
|
+
PRIVACY_SETTINGS_WITH_CONTACT_LIST: '24913399124998600' // InteropPrivacySettingWithContactListUpdate
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Known integrator IDs (assigned by WhatsApp).
|
|
20
|
+
* BirdyChat → identifier_type="email"
|
|
21
|
+
* Haiket → identifier_type="pn"
|
|
22
|
+
*/
|
|
20
23
|
const INTEGRATOR_BIRDYCHAT = 12
|
|
21
24
|
const INTEGRATOR_HAIKET = 13
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* TOS trackable results sent by WA on first interop opt-in.
|
|
28
|
+
* 105 = TOS shown, 160 = TOS accepted.
|
|
29
|
+
*/
|
|
24
30
|
const TOS_TRACKABLE_ID = '20240306'
|
|
25
31
|
const TOS_RESULT_SHOWN = '105'
|
|
26
32
|
const TOS_RESULT_ACCEPTED = '160'
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
/** Maximum users per batch lookup (enforced server-side too). */
|
|
29
35
|
const INTEROP_BATCH_MAX = 256
|
|
30
36
|
|
|
31
37
|
const makeInteropSocket = sock => {
|
|
32
|
-
const { query, generateMessageTag, logger, signalRepository
|
|
38
|
+
const { query, generateMessageTag, logger, signalRepository } = sock
|
|
33
39
|
|
|
34
40
|
const mexQuery = (variables, queryId, dataPath) =>
|
|
35
41
|
executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Fetch all available interop integrators from the server.
|
|
45
|
+
* Each integrator carries: id, name, status, icon, identifierType,
|
|
46
|
+
* optedIn, features.groupMessaging.
|
|
47
|
+
* status can be "active" | "onboarding" | "removed".
|
|
48
|
+
*/
|
|
38
49
|
const fetchIntegrators = async () => {
|
|
39
50
|
const result = await query({
|
|
40
51
|
tag: 'iq',
|
|
@@ -51,14 +62,14 @@ const makeInteropSocket = sock => {
|
|
|
51
62
|
return getBinaryNodeChildren(listNode, 'integrator').map(node => {
|
|
52
63
|
const featuresNode = getBinaryNodeChild(node, 'features')
|
|
53
64
|
return {
|
|
54
|
-
id: parseInt(node.attrs.id
|
|
65
|
+
id: parseInt(node.attrs.id),
|
|
55
66
|
name: node.attrs.name,
|
|
56
|
-
|
|
67
|
+
// "active" | "onboarding" | "removed"
|
|
57
68
|
status: node.attrs.status,
|
|
58
69
|
icon: node.attrs.icon,
|
|
59
|
-
|
|
70
|
+
// "email" | "pn" | "username"
|
|
60
71
|
identifierType: node.attrs.identifier_type,
|
|
61
|
-
|
|
72
|
+
// Whether we are already opted-in to this integrator
|
|
62
73
|
optedIn: node.attrs.opted_in === 'true' || globalOptedIn,
|
|
63
74
|
features: {
|
|
64
75
|
groupMessaging: featuresNode?.attrs?.group_messaging === 'true'
|
|
@@ -67,7 +78,7 @@ const makeInteropSocket = sock => {
|
|
|
67
78
|
})
|
|
68
79
|
}
|
|
69
80
|
|
|
70
|
-
|
|
81
|
+
/** Send a single TOS trackable item (shown or accepted). */
|
|
71
82
|
const sendTOSTrackable = async (id, result) => {
|
|
72
83
|
await query({
|
|
73
84
|
tag: 'iq',
|
|
@@ -76,13 +87,20 @@ const makeInteropSocket = sock => {
|
|
|
76
87
|
})
|
|
77
88
|
}
|
|
78
89
|
|
|
79
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Accept TOS for interop.
|
|
92
|
+
* Sends two trackable items: shown (105) then accepted (160).
|
|
93
|
+
* Must be called before opting in.
|
|
94
|
+
*/
|
|
80
95
|
const acceptInteropTOS = async () => {
|
|
81
96
|
await sendTOSTrackable(TOS_TRACKABLE_ID, TOS_RESULT_SHOWN)
|
|
82
97
|
await sendTOSTrackable(TOS_TRACKABLE_ID, TOS_RESULT_ACCEPTED)
|
|
83
98
|
}
|
|
84
99
|
|
|
85
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Opt in to a list of integrators (by numeric ID).
|
|
102
|
+
* Defaults to both known integrators (BirdyChat + Haiket).
|
|
103
|
+
*/
|
|
86
104
|
const optInIntegrators = async (integratorIds = [INTEGRATOR_BIRDYCHAT, INTEGRATOR_HAIKET]) => {
|
|
87
105
|
await query({
|
|
88
106
|
tag: 'iq',
|
|
@@ -106,7 +124,10 @@ const makeInteropSocket = sock => {
|
|
|
106
124
|
})
|
|
107
125
|
}
|
|
108
126
|
|
|
109
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Opt out of a list of integrators (by numeric ID).
|
|
129
|
+
* Mirror of optInIntegrators — uses <opt_out_integrators>.
|
|
130
|
+
*/
|
|
110
131
|
const optOutIntegrators = async (integratorIds = [INTEGRATOR_BIRDYCHAT, INTEGRATOR_HAIKET]) => {
|
|
111
132
|
await query({
|
|
112
133
|
tag: 'iq',
|
|
@@ -130,7 +151,18 @@ const makeInteropSocket = sock => {
|
|
|
130
151
|
})
|
|
131
152
|
}
|
|
132
153
|
|
|
133
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Resolve one or more interop users by external ID in a single IQ.
|
|
156
|
+
* Each entry: { externalId, integratorId }
|
|
157
|
+
* - BirdyChat (12): externalId = email address
|
|
158
|
+
* - Haiket (13): externalId = phone number string (e.g. "19146088152")
|
|
159
|
+
*
|
|
160
|
+
* Max 256 entries per call (server limit).
|
|
161
|
+
*
|
|
162
|
+
* Returns an array of results:
|
|
163
|
+
* { jid, externalId, normalizedExternalId, integratorId } on success
|
|
164
|
+
* { externalId, integratorId, error: { code, text } } on failure
|
|
165
|
+
*/
|
|
134
166
|
const resolveInteropUsers = async users => {
|
|
135
167
|
if (!users || users.length === 0) return []
|
|
136
168
|
if (users.length > INTEROP_BATCH_MAX) {
|
|
@@ -160,9 +192,9 @@ const makeInteropSocket = sock => {
|
|
|
160
192
|
if (errorNode) {
|
|
161
193
|
return {
|
|
162
194
|
externalId: userNode.attrs.external_id,
|
|
163
|
-
integratorId: parseInt(userNode.attrs.integrator_id
|
|
195
|
+
integratorId: parseInt(userNode.attrs.integrator_id),
|
|
164
196
|
error: {
|
|
165
|
-
code: parseInt(errorNode.attrs.code
|
|
197
|
+
code: parseInt(errorNode.attrs.code),
|
|
166
198
|
text: errorNode.attrs.text
|
|
167
199
|
}
|
|
168
200
|
}
|
|
@@ -171,18 +203,25 @@ const makeInteropSocket = sock => {
|
|
|
171
203
|
jid: userNode.attrs.jid,
|
|
172
204
|
externalId: userNode.attrs.external_id,
|
|
173
205
|
normalizedExternalId: userNode.attrs.normalized_external_id,
|
|
174
|
-
integratorId: parseInt(userNode.attrs.integrator_id
|
|
206
|
+
integratorId: parseInt(userNode.attrs.integrator_id)
|
|
175
207
|
}
|
|
176
208
|
})
|
|
177
209
|
}
|
|
178
210
|
|
|
179
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Convenience wrapper: resolve a single interop user.
|
|
213
|
+
* Returns the result object or null.
|
|
214
|
+
*/
|
|
180
215
|
const resolveInteropUser = async (externalId, integratorId) => {
|
|
181
216
|
const results = await resolveInteropUsers([{ externalId, integratorId }])
|
|
182
217
|
return results[0] ?? null
|
|
183
218
|
}
|
|
184
219
|
|
|
185
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Get reachability settings for interop contacts.
|
|
222
|
+
* Returns the raw <reachability_settings> node content.
|
|
223
|
+
* WA uses this as the interop presence/subscription mechanism.
|
|
224
|
+
*/
|
|
186
225
|
const getReachabilitySettings = async () => {
|
|
187
226
|
const result = await query({
|
|
188
227
|
tag: 'iq',
|
|
@@ -195,13 +234,17 @@ const makeInteropSocket = sock => {
|
|
|
195
234
|
enabled: settingsNode.attrs?.enabled,
|
|
196
235
|
users: getBinaryNodeChildren(settingsNode, 'user').map(n => ({
|
|
197
236
|
externalId: n.attrs.external_id,
|
|
198
|
-
integratorId: parseInt(n.attrs.integrator_id
|
|
237
|
+
integratorId: parseInt(n.attrs.integrator_id),
|
|
199
238
|
jid: n.attrs.jid
|
|
200
239
|
}))
|
|
201
240
|
}
|
|
202
241
|
}
|
|
203
242
|
|
|
204
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Set reachability settings (subscribe to presence) for interop contacts.
|
|
245
|
+
* users: array of { externalId, integratorId }
|
|
246
|
+
* enabled: "true" | "false"
|
|
247
|
+
*/
|
|
205
248
|
const setReachabilitySettings = async (users, enabled = 'true') => {
|
|
206
249
|
await query({
|
|
207
250
|
tag: 'iq',
|
|
@@ -222,7 +265,10 @@ const makeInteropSocket = sock => {
|
|
|
222
265
|
})
|
|
223
266
|
}
|
|
224
267
|
|
|
225
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Block or unblock an interop user via the w:interop blocklist.
|
|
270
|
+
* Different from regular WA block (xmlns=blocklist).
|
|
271
|
+
*/
|
|
226
272
|
const updateInteropBlockStatus = async (jid, action) => {
|
|
227
273
|
await query({
|
|
228
274
|
tag: 'iq',
|
|
@@ -240,7 +286,10 @@ const makeInteropSocket = sock => {
|
|
|
240
286
|
const blockInteropUser = jid => updateInteropBlockStatus(jid, 'block')
|
|
241
287
|
const unblockInteropUser = jid => updateInteropBlockStatus(jid, 'unblock')
|
|
242
288
|
|
|
243
|
-
|
|
289
|
+
/**
|
|
290
|
+
* Report an interop contact as spam.
|
|
291
|
+
* spam_flow="account_info_block" = block + report (WA Web default).
|
|
292
|
+
*/
|
|
244
293
|
const reportInteropSpam = async (jid, spamFlow = 'account_info_block') => {
|
|
245
294
|
await query({
|
|
246
295
|
tag: 'iq',
|
|
@@ -249,7 +298,10 @@ const makeInteropSocket = sock => {
|
|
|
249
298
|
})
|
|
250
299
|
}
|
|
251
300
|
|
|
252
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Mark an interop JID as trusted_contact in privacy tokens.
|
|
303
|
+
* WA calls this automatically after the first outgoing message.
|
|
304
|
+
*/
|
|
253
305
|
const trustInteropContact = async jid => {
|
|
254
306
|
const t = Math.floor(Date.now() / 1000).toString()
|
|
255
307
|
await query({
|
|
@@ -265,7 +317,15 @@ const makeInteropSocket = sock => {
|
|
|
265
317
|
})
|
|
266
318
|
}
|
|
267
319
|
|
|
268
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Full interop initialization sequence matching WA Web:
|
|
322
|
+
* 1. Fetch available integrators
|
|
323
|
+
* 2. Accept TOS (shown 105 + accepted 160)
|
|
324
|
+
* 3. Opt-in to all active/onboarding integrators
|
|
325
|
+
*
|
|
326
|
+
* Silently tolerates TOS/opt-in errors.
|
|
327
|
+
* Returns the full integrator list.
|
|
328
|
+
*/
|
|
269
329
|
const initInterop = async () => {
|
|
270
330
|
let integrators
|
|
271
331
|
try {
|
|
@@ -286,23 +346,30 @@ const makeInteropSocket = sock => {
|
|
|
286
346
|
} catch (err) {
|
|
287
347
|
logger.warn({ err }, 'interop: failed to opt-in integrators')
|
|
288
348
|
}
|
|
289
|
-
logger.info({ integrators: toOptIn.map(i => i.name) }, 'interop:
|
|
290
|
-
logger.warn(
|
|
291
|
-
{ integrators: toOptIn.map(i => i.name) },
|
|
292
|
-
'interop: receiving messages from interop contacts (BirdyChat/Haiket) is NOT YET SUPPORTED. ' +
|
|
293
|
-
'The interop bridge only delivers to device 0 (primary phone) via a single Signal session — ' +
|
|
294
|
-
'no fan-out to companion devices. Opt-in and sending may work, but inbound messages will not arrive.'
|
|
295
|
-
)
|
|
349
|
+
logger.info({ integrators: toOptIn.map(i => i.name) }, 'interop: initialized')
|
|
296
350
|
return integrators
|
|
297
351
|
}
|
|
298
352
|
|
|
299
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Reset the Signal session with an interop contact and force a fresh pkmsg handshake.
|
|
355
|
+
* Call this when incoming messages from an interop contact are not arriving or
|
|
356
|
+
* failing to decrypt — it clears the stale session so the next sendMessage
|
|
357
|
+
* generates a new pre-key message and re-establishes both directions.
|
|
358
|
+
*/
|
|
300
359
|
const resetInteropSession = async jid => {
|
|
301
360
|
await signalRepository.deleteSession([jid])
|
|
302
361
|
logger.info({ jid }, '[interop] session reset — next send will use pkmsg')
|
|
303
362
|
}
|
|
304
363
|
|
|
305
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Create an interop group via MEX.
|
|
366
|
+
* Confirmed from C80913Xf.java opcode 15:
|
|
367
|
+
* input: { participants: [{ jid }] }
|
|
368
|
+
* dataPath: xwa2_interop_group_create
|
|
369
|
+
*
|
|
370
|
+
* @param {string[]} participants - Interop JIDs to add
|
|
371
|
+
* @returns {{ gid, creationTime, creator, participants }}
|
|
372
|
+
*/
|
|
306
373
|
const createInteropGroup = async participants => {
|
|
307
374
|
const result = await mexQuery(
|
|
308
375
|
{ input: { participants: participants.map(jid => ({ jid })) } },
|
|
@@ -313,7 +380,15 @@ const makeInteropSocket = sock => {
|
|
|
313
380
|
return result
|
|
314
381
|
}
|
|
315
382
|
|
|
316
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Leave one or more interop groups via MEX.
|
|
385
|
+
* Confirmed from C80913Xf.java opcode 28:
|
|
386
|
+
* input: { groups_to_leave: [{ gid }] }
|
|
387
|
+
* dataPath: xwa2_interop_group_leave
|
|
388
|
+
*
|
|
389
|
+
* gid is the raw interop group ID (without @g.us suffix).
|
|
390
|
+
* @param {string|string[]} jids - Interop group JID(s)
|
|
391
|
+
*/
|
|
317
392
|
const leaveInteropGroup = async jids => {
|
|
318
393
|
const ids = Array.isArray(jids) ? jids : [jids]
|
|
319
394
|
const result = await mexQuery(
|
|
@@ -325,7 +400,15 @@ const makeInteropSocket = sock => {
|
|
|
325
400
|
return result
|
|
326
401
|
}
|
|
327
402
|
|
|
328
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Add participants to an existing interop group via MEX.
|
|
405
|
+
* Confirmed from C3Wt.java opcode 9:
|
|
406
|
+
* input: { gid, participants: [{ jid }] }
|
|
407
|
+
* dataPath: xwa2_interop_add_participants_to_group
|
|
408
|
+
*
|
|
409
|
+
* @param {string} groupJid - Interop group JID
|
|
410
|
+
* @param {string[]} participants - Interop JIDs to add
|
|
411
|
+
*/
|
|
329
412
|
const addParticipantsToInteropGroup = async (groupJid, participants) => {
|
|
330
413
|
const gid = groupJid.split('@')[0]
|
|
331
414
|
return mexQuery(
|
|
@@ -335,13 +418,35 @@ const makeInteropSocket = sock => {
|
|
|
335
418
|
)
|
|
336
419
|
}
|
|
337
420
|
|
|
338
|
-
|
|
421
|
+
/**
|
|
422
|
+
* Query info about an interop group via MEX.
|
|
423
|
+
* Confirmed from C3Wt.java opcode 10:
|
|
424
|
+
* group_input: { gid }
|
|
425
|
+
* dataPath: xwa2_interop_group_query_by_id
|
|
426
|
+
*
|
|
427
|
+
* @param {string} groupJid - Interop group JID
|
|
428
|
+
*/
|
|
339
429
|
const queryInteropGroupInfo = async groupJid => {
|
|
340
430
|
const gid = groupJid.split('@')[0]
|
|
341
|
-
return mexQuery(
|
|
431
|
+
return mexQuery(
|
|
432
|
+
{ group_input: { gid } },
|
|
433
|
+
INTEROP_MEX_QUERY_IDS.QUERY_GROUP_INFO,
|
|
434
|
+
'xwa2_interop_group_query_by_id'
|
|
435
|
+
)
|
|
342
436
|
}
|
|
343
437
|
|
|
344
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Update an interop privacy setting via MEX.
|
|
440
|
+
* Confirmed from C24385Ak0.java (InteropPrivacySettingsUpdate):
|
|
441
|
+
* feature, setting
|
|
442
|
+
* dataPath: xwa2_interop_privacy_setting_update
|
|
443
|
+
*
|
|
444
|
+
* Known features: "GROUPADD"
|
|
445
|
+
* Known settings: "ALL" | "CONTACTS" | "NONE"
|
|
446
|
+
*
|
|
447
|
+
* @param {string} feature - Privacy feature name
|
|
448
|
+
* @param {string} setting - New value
|
|
449
|
+
*/
|
|
345
450
|
const updateInteropPrivacySetting = async (feature, setting) => {
|
|
346
451
|
return mexQuery(
|
|
347
452
|
{ feature, setting },
|
|
@@ -350,7 +455,20 @@ const makeInteropSocket = sock => {
|
|
|
350
455
|
)
|
|
351
456
|
}
|
|
352
457
|
|
|
353
|
-
|
|
458
|
+
/**
|
|
459
|
+
* Update an interop privacy setting with an explicit contact list via MEX.
|
|
460
|
+
* Confirmed from C24388Ak8.java (InteropPrivacySettingWithContactListUpdate):
|
|
461
|
+
* feature, setting, contacts[], contact_list_type, dhash
|
|
462
|
+
* dataPath: xwa2_interop_privacy_setting_with_contact_list_update
|
|
463
|
+
*
|
|
464
|
+
* Used when setting is "CONTACTS" and you want to specify exact allowed contacts.
|
|
465
|
+
*
|
|
466
|
+
* @param {string} feature - Privacy feature (e.g. "GROUPADD")
|
|
467
|
+
* @param {string} setting - "CONTACTS"
|
|
468
|
+
* @param {string[]} contacts - List of JIDs to allow
|
|
469
|
+
* @param {string} contactListType - Contact list type string
|
|
470
|
+
* @param {string} [dhash] - Hash of the current contact list (or null/"none")
|
|
471
|
+
*/
|
|
354
472
|
const updateInteropPrivacySettingWithContactList = async (
|
|
355
473
|
feature,
|
|
356
474
|
setting,
|
|
@@ -365,11 +483,13 @@ const makeInteropSocket = sock => {
|
|
|
365
483
|
)
|
|
366
484
|
}
|
|
367
485
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
486
|
+
/**
|
|
487
|
+
* Check whether an interop user allows being added to groups (GROUPADD privacy).
|
|
488
|
+
* Returns true if the user can be added, false if blocked by their privacy settings.
|
|
489
|
+
*
|
|
490
|
+
* Mirrors InteropPrivacySettingsManager.A01() in the APK which queries
|
|
491
|
+
* the "GROUPADD" feature for the given account.
|
|
492
|
+
*/
|
|
373
493
|
const getInteropGroupAddPrivacy = async (jid, integratorId) => {
|
|
374
494
|
const result = await query({
|
|
375
495
|
tag: 'iq',
|
|
@@ -392,8 +512,8 @@ const makeInteropSocket = sock => {
|
|
|
392
512
|
})
|
|
393
513
|
const privacyNode = getBinaryNodeChild(result, 'privacy')
|
|
394
514
|
const userNode = getBinaryNodeChild(privacyNode, 'user')
|
|
395
|
-
|
|
396
|
-
|
|
515
|
+
// "allowed" means the server confirmed the user can be added; any other value or
|
|
516
|
+
// absence of the attribute means the privacy setting blocks the add.
|
|
397
517
|
return userNode?.attrs?.result === 'allowed'
|
|
398
518
|
}
|
|
399
519
|
|
|
@@ -417,7 +537,6 @@ const makeInteropSocket = sock => {
|
|
|
417
537
|
leaveInteropGroup,
|
|
418
538
|
addParticipantsToInteropGroup,
|
|
419
539
|
queryInteropGroupInfo,
|
|
420
|
-
queryInteropPrivacySettings,
|
|
421
540
|
updateInteropPrivacySetting,
|
|
422
541
|
updateInteropPrivacySettingWithContactList,
|
|
423
542
|
getInteropGroupAddPrivacy,
|