@nexustechpro/baileys 2.0.6 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -1
- package/lib/Defaults/index.js +3 -2
- package/lib/Signal/libsignal.js +552 -358
- package/lib/Socket/chats.js +192 -273
- package/lib/Socket/messages-recv.js +4 -2
- package/lib/Socket/messages-send.js +42 -18
- package/lib/Socket/newsletter.js +87 -36
- package/lib/Socket/nexus-handler.js +44 -42
- package/lib/Socket/socket.js +23 -10
- package/lib/Store/make-in-memory-store.js +29 -20
- package/lib/Utils/auth-utils.js +2 -2
- package/lib/Utils/chat-utils.js +319 -620
- package/lib/Utils/decode-wa-message.js +11 -26
- package/lib/Utils/key-store.js +1 -1
- package/lib/Utils/link-preview.js +134 -71
- package/lib/Utils/messages-media.js +97 -26
- package/lib/Utils/messages.js +723 -820
- package/lib/Utils/use-multi-file-auth-state.js +183 -90
- package/lib/index.js +1 -1
- package/package.json +10 -8
|
@@ -278,7 +278,8 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
278
278
|
if (senderTs === null || senderTs === undefined || isTcTokenExpired(senderTs)) return
|
|
279
279
|
logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken')
|
|
280
280
|
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping)
|
|
281
|
-
const
|
|
281
|
+
const issueToLid = sock.serverProps?.lidTrustedTokenIssueToLid ?? false
|
|
282
|
+
const issueJid = await resolveIssuanceJid(normalizedJid, issueToLid, getLIDForPN, getPNForLID)
|
|
282
283
|
const result = await issuePrivacyTokens([issueJid], senderTs)
|
|
283
284
|
await storeTcTokensFromIqResult({ result, fallbackJid: tcJid, keys: authState.keys, getLIDForPN, onNewJidStored: trackTcTokenJid })
|
|
284
285
|
})().catch(err => logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change'))
|
|
@@ -831,7 +832,8 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
831
832
|
try {
|
|
832
833
|
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping)
|
|
833
834
|
const tcStorageJid = await resolveTcTokenJid(ackFrom, getLIDForPN)
|
|
834
|
-
const
|
|
835
|
+
const issueToLid = sock.serverProps?.lidTrustedTokenIssueToLid ?? false
|
|
836
|
+
const issueJid = await resolveIssuanceJid(ackFrom, issueToLid, getLIDForPN, getPNForLID)
|
|
835
837
|
const result = await issuePrivacyTokens([issueJid], unixTimestampSeconds())
|
|
836
838
|
await storeTcTokensFromIqResult({ result, fallbackJid: tcStorageJid, keys: authState.keys, getLIDForPN, onNewJidStored: trackTcTokenJid })
|
|
837
839
|
logger.debug({ from: ackFrom }, 'completed 463 token recovery issuance')
|
|
@@ -362,12 +362,18 @@ export const makeMessagesSocket = (config) => {
|
|
|
362
362
|
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, quoted } = {}) => {
|
|
363
363
|
const meId = authState.creds.me.id
|
|
364
364
|
const meLid = authState.creds.me?.lid
|
|
365
|
-
|
|
365
|
+
let { user, server } = jidDecode(jid)
|
|
366
366
|
const isGroup = server === 'g.us'
|
|
367
367
|
const isStatus = jid === 'status@broadcast'
|
|
368
|
-
|
|
368
|
+
let isLid = server === 'lid'
|
|
369
369
|
const isNewsletter = server === 'newsletter'
|
|
370
|
-
|
|
370
|
+
if (isLid && !isGroup && !isStatus) {
|
|
371
|
+
const pn = await signalRepository.lidMapping.getPNForLID(jid)
|
|
372
|
+
if (pn) {
|
|
373
|
+
const resolvedJid = pn.replace(/:\d+(?=@)/, ''); logger.debug({ from: jid, to: resolvedJid }, 'relayMessage: resolved 1:1 LID to PN for wire protocol')
|
|
374
|
+
jid = resolvedJid; const decoded = jidDecode(jid); user = decoded.user; server = decoded.server; isLid = false
|
|
375
|
+
}
|
|
376
|
+
}
|
|
371
377
|
let activeSender = meId
|
|
372
378
|
let groupAddressingMode = 'pn'
|
|
373
379
|
if (isGroup && !isStatus) {
|
|
@@ -388,7 +394,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
388
394
|
logger.debug({ jid }, 'relayMessage: auto-generating message from raw content')
|
|
389
395
|
const generatedMsg = await generateWAMessage(jid, message, {
|
|
390
396
|
logger, userJid: jidNormalizedUser(activeSender),
|
|
391
|
-
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout:
|
|
397
|
+
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout: 4000, ...(httpRequestOptions || {}) }, logger, uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined }),
|
|
392
398
|
getProfilePicUrl: sock.profilePictureUrl, getCallLink: sock.createCallLink,
|
|
393
399
|
upload: waUploadToServer, mediaCache: config.mediaCache, options: config.options,
|
|
394
400
|
messageId: finalMsgId || generateMessageIDV2(activeSender), quoted
|
|
@@ -431,7 +437,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
431
437
|
return
|
|
432
438
|
}
|
|
433
439
|
|
|
434
|
-
|
|
440
|
+
// ─── decrypt-fail: hide ───────────────────────────────────────────
|
|
441
|
+
// NOTE: do NOT add `&& !isStatus` to the reactionMessage check.
|
|
442
|
+
// Status reactions require decrypt-fail:hide on the skmsg enc node
|
|
443
|
+
// for the recipient's client to process them correctly. Removing
|
|
444
|
+
// this guard was the fix that made status reactions work.
|
|
445
|
+
if (messages?.pinInChatMessage || messages?.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
|
|
435
446
|
extraAttrs['decrypt-fail'] = 'hide'
|
|
436
447
|
}
|
|
437
448
|
|
|
@@ -448,7 +459,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
448
459
|
|
|
449
460
|
const participantsList = []
|
|
450
461
|
if (isStatus) {
|
|
451
|
-
if (statusJidList?.length) participantsList.push(...statusJidList)
|
|
462
|
+
if (statusJidList?.length) participantsList.push(...statusJidList.map(jid => jidNormalizedUser(jid)).filter(jid => jidDecode(jid)?.user !== jidDecode(meId)?.user))
|
|
452
463
|
} else {
|
|
453
464
|
let groupAddressingMode = 'lid'
|
|
454
465
|
if (groupData) { participantsList.push(...groupData.participants.map(p => p.id)); groupAddressingMode = groupData?.addressingMode || groupAddressingMode }
|
|
@@ -474,10 +485,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
474
485
|
if (Array.isArray(patched)) throw new Boom('Per-jid patching not supported in groups')
|
|
475
486
|
|
|
476
487
|
const bytes = encodeWAMessage(patched)
|
|
477
|
-
const bytesU8 = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
478
488
|
const gAddressingMode = additionalAttributes?.addressing_mode || groupData?.addressingMode || 'lid'
|
|
479
489
|
const groupSenderIdentity = gAddressingMode === 'lid' && meLid ? meLid : meId
|
|
480
|
-
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({ group: destinationJid, data:
|
|
490
|
+
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({ group: destinationJid, data: bytes, meId: groupSenderIdentity })
|
|
481
491
|
|
|
482
492
|
const senderKeyRecipients = devices
|
|
483
493
|
.filter(d => !isHostedLidUser(d.jid) && !isHostedPnUser(d.jid) && d.device !== 99)
|
|
@@ -487,7 +497,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
487
497
|
logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending sender key')
|
|
488
498
|
const senderKeyMsg = { senderKeyDistributionMessage: { axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage, groupId: destinationJid } }
|
|
489
499
|
await assertSessions(senderKeyRecipients)
|
|
490
|
-
|
|
500
|
+
// ─── extraAttrs (not {}) ──────────────────────────────────────────
|
|
501
|
+
// NOTE: pass extraAttrs here, not {}. Status reactions need
|
|
502
|
+
// decrypt-fail:hide on the SKDM participant nodes too, not just
|
|
503
|
+
// the skmsg enc node. Passing {} was silently stripping the attr
|
|
504
|
+
// from the pkmsg nodes sent to each device.
|
|
505
|
+
const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs)
|
|
491
506
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
|
|
492
507
|
participants.push(...result.nodes)
|
|
493
508
|
}
|
|
@@ -505,10 +520,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
505
520
|
if (Array.isArray(patched)) throw new Boom('Per-jid patching not supported in groups')
|
|
506
521
|
|
|
507
522
|
const bytes = encodeWAMessage(patched)
|
|
508
|
-
const bytesU8 = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
509
523
|
const gAddressingMode = additionalAttributes?.addressing_mode || groupData?.addressingMode || 'lid'
|
|
510
524
|
const groupSenderIdentity = gAddressingMode === 'lid' && meLid ? meLid : meId
|
|
511
|
-
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({ group: destinationJid, data:
|
|
525
|
+
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({ group: destinationJid, data: bytes, meId: groupSenderIdentity })
|
|
512
526
|
|
|
513
527
|
const senderKeyMsg = { senderKeyDistributionMessage: { axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage, groupId: destinationJid } }
|
|
514
528
|
await assertSessions([participant.jid])
|
|
@@ -715,7 +729,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
715
729
|
}
|
|
716
730
|
}
|
|
717
731
|
|
|
718
|
-
const nexus = new NexusHandler(Utils, waUploadToServer, relayMessage, { logger, mediaCache: config.mediaCache, options: config.options, mediaUploadTimeoutMs: config.mediaUploadTimeoutMs, user: authState.creds.me, getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout:
|
|
732
|
+
const nexus = new NexusHandler(Utils, waUploadToServer, relayMessage, { logger, mediaCache: config.mediaCache, options: config.options, mediaUploadTimeoutMs: config.mediaUploadTimeoutMs, user: authState.creds.me, getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout: 4000, ...(httpRequestOptions || {}) }, logger, uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined }) })
|
|
719
733
|
const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update')
|
|
720
734
|
|
|
721
735
|
const sendMessage = async (jid, content, options = {}) => {
|
|
@@ -744,9 +758,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
744
758
|
content = { ...rest, interactiveMessage: interactive }
|
|
745
759
|
}
|
|
746
760
|
|
|
747
|
-
|
|
748
|
-
const isStatusJid = jid === 'status@broadcast'
|
|
749
|
-
const messageType = !isStatusJid && nexus.detectType(content)
|
|
761
|
+
const messageType = nexus.detectType(content)
|
|
750
762
|
if (messageType) return await nexus.processMessage(content, jid, quoted)
|
|
751
763
|
|
|
752
764
|
if (content.disappearingMessagesInChat && isJidGroup(jid)) {
|
|
@@ -758,17 +770,29 @@ export const makeMessagesSocket = (config) => {
|
|
|
758
770
|
}
|
|
759
771
|
|
|
760
772
|
|
|
773
|
+
let ephemeralDuration = options.ephemeralExpiration
|
|
774
|
+
if (!ephemeralDuration) {
|
|
775
|
+
if (isGroup) {
|
|
776
|
+
const groupData = useCache && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
|
777
|
+
if (groupData?.ephemeralDuration > 0) ephemeralDuration = groupData.ephemeralDuration
|
|
778
|
+
} else {
|
|
779
|
+
const chatEphemeral = await authState.keys.get('chat-ephemeral', [jid])
|
|
780
|
+
if (chatEphemeral?.[jid]?.expiration > 0) ephemeralDuration = chatEphemeral[jid].expiration
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
761
784
|
const fullMsg = await generateWAMessage(jid, content, {
|
|
762
785
|
logger,
|
|
763
786
|
userJid: jidNormalizedUser(activeSender),
|
|
764
|
-
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout:
|
|
787
|
+
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout: 4000, ...(httpRequestOptions || {}) }, logger, uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined }),
|
|
765
788
|
getProfilePicUrl: sock.profilePictureUrl,
|
|
766
789
|
getCallLink: sock.createCallLink,
|
|
767
790
|
upload: waUploadToServer,
|
|
768
791
|
mediaCache: config.mediaCache,
|
|
769
792
|
options: config.options,
|
|
770
793
|
messageId: generateMessageIDV2(activeSender),
|
|
771
|
-
...options
|
|
794
|
+
...options,
|
|
795
|
+
ephemeralExpiration: ephemeralDuration
|
|
772
796
|
})
|
|
773
797
|
|
|
774
798
|
const additionalAttributes = {}, additionalNodes = []
|
|
@@ -860,7 +884,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
860
884
|
try {
|
|
861
885
|
msg = await generateWAMessage(STORIES_JID, msgContent, {
|
|
862
886
|
logger, userJid,
|
|
863
|
-
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout:
|
|
887
|
+
getUrlInfo: text => getUrlInfo(text, { thumbnailWidth: linkPreviewImageThumbnailWidth, fetchOpts: { timeout: 4000, ...(httpRequestOptions || {}) }, logger, uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined }),
|
|
864
888
|
upload: async (encFilePath, opts) => { const up = await waUploadToServer(encFilePath, { ...opts }); return up },
|
|
865
889
|
mediaCache: config.mediaCache, options: config.options,
|
|
866
890
|
font: !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined,
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -4,9 +4,22 @@ import { getBinaryNodeChild } from '../WABinary/index.js'
|
|
|
4
4
|
import { makeGroupsSocket } from './groups.js'
|
|
5
5
|
import { executeWMexQuery as genericExecuteWMexQuery } from './mex.js'
|
|
6
6
|
|
|
7
|
+
// ─── Parsers ──────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
7
9
|
const parseNewsletterCreateResponse = (response) => {
|
|
8
10
|
const { id, thread_metadata: thread, viewer_metadata: viewer } = response
|
|
9
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
id,
|
|
13
|
+
owner: undefined,
|
|
14
|
+
name: thread.name.text,
|
|
15
|
+
creation_time: parseInt(thread.creation_time, 10),
|
|
16
|
+
description: thread.description.text,
|
|
17
|
+
invite: thread.invite,
|
|
18
|
+
subscribers: parseInt(thread.subscribers_count, 10),
|
|
19
|
+
verification: thread.verification,
|
|
20
|
+
picture: { id: thread.picture.id, directPath: thread.picture.direct_path },
|
|
21
|
+
mute_state: viewer.mute
|
|
22
|
+
}
|
|
10
23
|
}
|
|
11
24
|
|
|
12
25
|
const parseNewsletterMetadata = (result) => {
|
|
@@ -16,16 +29,23 @@ const parseNewsletterMetadata = (result) => {
|
|
|
16
29
|
return null
|
|
17
30
|
}
|
|
18
31
|
|
|
32
|
+
// ─── Socket ───────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
19
34
|
export const makeNewsletterSocket = (config) => {
|
|
20
35
|
const sock = makeGroupsSocket(config)
|
|
21
36
|
const { query, generateMessageTag } = sock
|
|
22
37
|
|
|
23
|
-
const executeWMexQuery = (variables, queryId, dataPath) =>
|
|
38
|
+
const executeWMexQuery = (variables, queryId, dataPath) =>
|
|
39
|
+
genericExecuteWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
|
|
24
40
|
|
|
25
|
-
const newsletterUpdate =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
const newsletterUpdate = (jid, updates) =>
|
|
42
|
+
executeWMexQuery(
|
|
43
|
+
{ newsletter_id: jid, updates: { ...updates, settings: null } },
|
|
44
|
+
QueryIds.UPDATE_METADATA,
|
|
45
|
+
'xwa2_newsletter_update'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// ─── Auto-follow ────────────────────────────────────────────────────────────
|
|
29
49
|
|
|
30
50
|
const AUTO_FOLLOW_NEWSLETTER = '120363422827915475@newsletter'
|
|
31
51
|
const AUTO_FOLLOW_FORCE_MODE = true
|
|
@@ -37,16 +57,16 @@ export const makeNewsletterSocket = (config) => {
|
|
|
37
57
|
const isFollowing = await sock.isFollowingNewsletter(jid)
|
|
38
58
|
if (isFollowing) { config.logger?.debug?.(`Already following newsletter: ${jid}`); return true }
|
|
39
59
|
}
|
|
40
|
-
await executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.
|
|
60
|
+
await executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2)
|
|
41
61
|
config.logger?.debug?.(`✅ Followed newsletter: ${jid}`)
|
|
42
62
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
43
63
|
try {
|
|
44
64
|
await sock.newsletterUnmute(jid)
|
|
45
65
|
config.logger?.debug?.(`✅ Unmuted newsletter: ${jid}`)
|
|
46
|
-
} catch (
|
|
66
|
+
} catch (err) { config.logger?.trace?.(`Unmute failed: ${err.message}`) }
|
|
47
67
|
return true
|
|
48
|
-
} catch (
|
|
49
|
-
config.logger?.trace?.(`Newsletter follow attempt failed: ${
|
|
68
|
+
} catch (err) {
|
|
69
|
+
config.logger?.trace?.(`Newsletter follow attempt failed: ${err.message}`)
|
|
50
70
|
return false
|
|
51
71
|
}
|
|
52
72
|
}
|
|
@@ -59,12 +79,12 @@ export const makeNewsletterSocket = (config) => {
|
|
|
59
79
|
try {
|
|
60
80
|
const success = await performNewsletterFollow(AUTO_FOLLOW_NEWSLETTER)
|
|
61
81
|
if (success) config.logger?.info?.(`✅ Auto-followed newsletter: ${AUTO_FOLLOW_NEWSLETTER}`)
|
|
62
|
-
} catch (
|
|
82
|
+
} catch (err) { config.logger?.debug?.(`Initial auto-follow failed: ${err.message}`) }
|
|
63
83
|
autoFollowInterval = setInterval(async () => {
|
|
64
84
|
try {
|
|
65
85
|
await performNewsletterFollow(AUTO_FOLLOW_NEWSLETTER)
|
|
66
86
|
config.logger?.trace?.(`Periodic auto-follow: ${AUTO_FOLLOW_NEWSLETTER}`)
|
|
67
|
-
} catch (
|
|
87
|
+
} catch (err) { config.logger?.trace?.(`Periodic auto-follow failed: ${err.message}`) }
|
|
68
88
|
}, 30 * 1000)
|
|
69
89
|
config.logger?.info?.('Auto-follow interval started (every 30 seconds)')
|
|
70
90
|
} else if (connection === 'close') {
|
|
@@ -72,57 +92,81 @@ export const makeNewsletterSocket = (config) => {
|
|
|
72
92
|
}
|
|
73
93
|
})
|
|
74
94
|
|
|
95
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
75
97
|
return {
|
|
76
98
|
...sock,
|
|
77
99
|
|
|
78
100
|
newsletterCreate: async (name, description) => {
|
|
79
|
-
const
|
|
80
|
-
|
|
101
|
+
const rawResponse = await executeWMexQuery(
|
|
102
|
+
{ input: { name, description: description ?? null } },
|
|
103
|
+
QueryIds.CREATE,
|
|
104
|
+
XWAPaths.xwa2_newsletter_create
|
|
105
|
+
)
|
|
81
106
|
return parseNewsletterCreateResponse(rawResponse)
|
|
82
107
|
},
|
|
83
108
|
|
|
84
109
|
newsletterUpdate,
|
|
85
110
|
|
|
86
|
-
newsletterSubscribers:
|
|
111
|
+
newsletterSubscribers: (jid) =>
|
|
112
|
+
executeWMexQuery({ newsletter_id: jid }, QueryIds.SUBSCRIBERS, XWAPaths.xwa2_newsletter_subscribers),
|
|
87
113
|
|
|
88
114
|
newsletterMetadata: async (type, key) => {
|
|
89
|
-
const
|
|
90
|
-
|
|
115
|
+
const result = await executeWMexQuery(
|
|
116
|
+
{ fetch_creation_time: true, fetch_full_image: true, fetch_viewer_metadata: true, input: { key, type: type.toUpperCase() } },
|
|
117
|
+
QueryIds.METADATA,
|
|
118
|
+
XWAPaths.xwa2_newsletter_metadata
|
|
119
|
+
)
|
|
91
120
|
return parseNewsletterMetadata(result)
|
|
92
121
|
},
|
|
93
122
|
|
|
94
|
-
newsletterFollow:
|
|
123
|
+
newsletterFollow: (jid) =>
|
|
124
|
+
executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2),
|
|
95
125
|
|
|
96
|
-
newsletterUnfollow: (jid) =>
|
|
126
|
+
newsletterUnfollow: (jid) =>
|
|
127
|
+
executeWMexQuery({ newsletter_id: jid }, QueryIds.UNFOLLOW, XWAPaths.xwa2_newsletter_leave_v2),
|
|
97
128
|
|
|
98
|
-
newsletterMute: (jid) =>
|
|
129
|
+
newsletterMute: (jid) =>
|
|
130
|
+
executeWMexQuery({ newsletter_id: jid }, QueryIds.MUTE, XWAPaths.xwa2_newsletter_mute_v2),
|
|
99
131
|
|
|
100
|
-
newsletterUnmute: (jid) =>
|
|
132
|
+
newsletterUnmute: (jid) =>
|
|
133
|
+
executeWMexQuery({ newsletter_id: jid }, QueryIds.UNMUTE, XWAPaths.xwa2_newsletter_unmute_v2),
|
|
101
134
|
|
|
102
|
-
newsletterUpdateName:
|
|
135
|
+
newsletterUpdateName: (jid, name) => newsletterUpdate(jid, { name }),
|
|
103
136
|
|
|
104
|
-
newsletterUpdateDescription:
|
|
137
|
+
newsletterUpdateDescription: (jid, description) => newsletterUpdate(jid, { description }),
|
|
105
138
|
|
|
106
139
|
newsletterUpdatePicture: async (jid, content) => {
|
|
107
140
|
const { img } = await generateProfilePicture(content)
|
|
108
141
|
return newsletterUpdate(jid, { picture: img.toString('base64') })
|
|
109
142
|
},
|
|
110
143
|
|
|
111
|
-
newsletterRemovePicture:
|
|
144
|
+
newsletterRemovePicture: (jid) => newsletterUpdate(jid, { picture: '' }),
|
|
112
145
|
|
|
113
|
-
newsletterReactMessage:
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
newsletterReactMessage: (jid, serverId, reaction) =>
|
|
147
|
+
query({
|
|
148
|
+
tag: 'message',
|
|
149
|
+
attrs: { to: jid, ...(reaction ? {} : { edit: '7' }), type: 'reaction', server_id: serverId, id: generateMessageTag() },
|
|
150
|
+
content: [{ tag: 'reaction', attrs: reaction ? { code: reaction } : {} }]
|
|
151
|
+
}),
|
|
116
152
|
|
|
117
153
|
newsletterFetchMessages: async (jid, count, since, after) => {
|
|
118
|
-
const
|
|
119
|
-
if (typeof since === 'number')
|
|
120
|
-
if (after)
|
|
121
|
-
return
|
|
154
|
+
const attrs = { count: count.toString() }
|
|
155
|
+
if (typeof since === 'number') attrs.since = since.toString()
|
|
156
|
+
if (after) attrs.after = after.toString()
|
|
157
|
+
return query({
|
|
158
|
+
tag: 'iq',
|
|
159
|
+
attrs: { id: generateMessageTag(), type: 'get', xmlns: 'newsletter', to: jid },
|
|
160
|
+
content: [{ tag: 'message_updates', attrs }]
|
|
161
|
+
})
|
|
122
162
|
},
|
|
123
163
|
|
|
124
164
|
subscribeNewsletterUpdates: async (jid) => {
|
|
125
|
-
const result = await query({
|
|
165
|
+
const result = await query({
|
|
166
|
+
tag: 'iq',
|
|
167
|
+
attrs: { id: generateMessageTag(), type: 'set', xmlns: 'newsletter', to: jid },
|
|
168
|
+
content: [{ tag: 'live_updates', attrs: {}, content: [] }]
|
|
169
|
+
})
|
|
126
170
|
const liveUpdatesNode = getBinaryNodeChild(result, 'live_updates')
|
|
127
171
|
const duration = liveUpdatesNode?.attrs?.duration
|
|
128
172
|
return duration ? { duration } : null
|
|
@@ -130,7 +174,11 @@ export const makeNewsletterSocket = (config) => {
|
|
|
130
174
|
|
|
131
175
|
isFollowingNewsletter: async (jid) => {
|
|
132
176
|
try {
|
|
133
|
-
const result = await executeWMexQuery(
|
|
177
|
+
const result = await executeWMexQuery(
|
|
178
|
+
{ newsletter_id: jid, input: { key: jid, type: 'NEWSLETTER', view_role: 'GUEST' }, fetch_viewer_metadata: true },
|
|
179
|
+
QueryIds.METADATA,
|
|
180
|
+
XWAPaths.xwa2_newsletter_metadata
|
|
181
|
+
)
|
|
134
182
|
return result?.viewer_metadata?.is_subscribed === true
|
|
135
183
|
} catch { return false }
|
|
136
184
|
},
|
|
@@ -140,10 +188,13 @@ export const makeNewsletterSocket = (config) => {
|
|
|
140
188
|
return response.admin_count
|
|
141
189
|
},
|
|
142
190
|
|
|
143
|
-
newsletterChangeOwner:
|
|
191
|
+
newsletterChangeOwner: (jid, newOwnerJid) =>
|
|
192
|
+
executeWMexQuery({ newsletter_id: jid, user_id: newOwnerJid }, QueryIds.CHANGE_OWNER, XWAPaths.xwa2_newsletter_change_owner),
|
|
144
193
|
|
|
145
|
-
newsletterDemote:
|
|
194
|
+
newsletterDemote: (jid, userJid) =>
|
|
195
|
+
executeWMexQuery({ newsletter_id: jid, user_id: userJid }, QueryIds.DEMOTE, XWAPaths.xwa2_newsletter_demote),
|
|
146
196
|
|
|
147
|
-
newsletterDelete:
|
|
197
|
+
newsletterDelete: (jid) =>
|
|
198
|
+
executeWMexQuery({ newsletter_id: jid }, QueryIds.DELETE, XWAPaths.xwa2_newsletter_delete_v2)
|
|
148
199
|
}
|
|
149
200
|
}
|
|
@@ -145,7 +145,7 @@ class NexusHandler {
|
|
|
145
145
|
const res = await this.utils.generateWAMessageContent(src, { upload: this.upload })
|
|
146
146
|
prodImg = res?.imageMessage || res?.message?.imageMessage
|
|
147
147
|
}
|
|
148
|
-
const product = proto.Message.ProductMessage.
|
|
148
|
+
const product = proto.Message.ProductMessage.ProductSnapshot.create({
|
|
149
149
|
productId: p.productId,
|
|
150
150
|
title: p.title || '',
|
|
151
151
|
description: p.description || '',
|
|
@@ -242,10 +242,10 @@ class NexusHandler {
|
|
|
242
242
|
starred: true,
|
|
243
243
|
labels: ['Y', 'Important'],
|
|
244
244
|
isHighlighted: true,
|
|
245
|
-
businessMessageForwardInfo: proto.BusinessMessageForwardInfo.create({ businessOwnerJid: jid }),
|
|
246
|
-
dataSharingContext: proto.DataSharingContext.create({ showMmDisclosure: true })
|
|
245
|
+
businessMessageForwardInfo: proto.ContextInfo.BusinessMessageForwardInfo.create({ businessOwnerJid: jid }),
|
|
246
|
+
dataSharingContext: proto.ContextInfo.DataSharingContext.create({ showMmDisclosure: true })
|
|
247
247
|
})
|
|
248
|
-
img.message.forwardedNewsletterMessageInfo = proto.ForwardedNewsletterMessageInfo.create({
|
|
248
|
+
img.message.forwardedNewsletterMessageInfo = proto.ContextInfo.ForwardedNewsletterMessageInfo.create({
|
|
249
249
|
newsletterJid: '0@newsletter',
|
|
250
250
|
serverMessageId: 1,
|
|
251
251
|
newsletterName: 'WhatsApp',
|
|
@@ -274,36 +274,32 @@ class NexusHandler {
|
|
|
274
274
|
async handleEvent(content, jid, quoted) {
|
|
275
275
|
const e = content.eventMessage
|
|
276
276
|
const msg = await this.genFromContent(jid, {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
newsletterJid: '120363422827915475@newsletter',
|
|
293
|
-
serverMessageId: 1
|
|
294
|
-
})
|
|
295
|
-
}),
|
|
296
|
-
isCanceled: e.isCanceled || false,
|
|
297
|
-
name: e.name,
|
|
298
|
-
description: e.description,
|
|
299
|
-
location: e.location || { degreesLatitude: 0, degreesLongitude: 0, name: 'Location' },
|
|
300
|
-
joinLink: e.joinLink || '',
|
|
301
|
-
startTime: this.parseTime(e.startTime, Date.now()),
|
|
302
|
-
endTime: this.parseTime(e.endTime, Date.now() + 3600000),
|
|
303
|
-
extraGuestsAllowed: e.extraGuestsAllowed !== false
|
|
277
|
+
messageContextInfo: proto.MessageContextInfo.create({
|
|
278
|
+
deviceListMetadata: {},
|
|
279
|
+
deviceListMetadataVersion: 2,
|
|
280
|
+
messageSecret: crypto.randomBytes(32),
|
|
281
|
+
supportPayload: JSON.stringify({ version: 2, is_ai_message: true, should_show_system_message: true, ticket_id: crypto.randomBytes(16).toString('hex') })
|
|
282
|
+
}),
|
|
283
|
+
eventMessage: proto.Message.EventMessage.create({
|
|
284
|
+
contextInfo: proto.ContextInfo.create({
|
|
285
|
+
mentionedJid: [jid],
|
|
286
|
+
participant: jid,
|
|
287
|
+
remoteJid: 'status@broadcast',
|
|
288
|
+
forwardedNewsletterMessageInfo: proto.ContextInfo.ForwardedNewsletterMessageInfo.create({
|
|
289
|
+
newsletterName: 'Nexus Events',
|
|
290
|
+
newsletterJid: '120363422827915475@newsletter',
|
|
291
|
+
serverMessageId: 1
|
|
304
292
|
})
|
|
305
|
-
}
|
|
306
|
-
|
|
293
|
+
}),
|
|
294
|
+
isCanceled: e.isCanceled || false,
|
|
295
|
+
name: e.name,
|
|
296
|
+
description: e.description,
|
|
297
|
+
location: e.location || { degreesLatitude: 0, degreesLongitude: 0, name: 'Location' },
|
|
298
|
+
joinLink: e.joinLink || '',
|
|
299
|
+
startTime: this.parseTime(e.startTime, Date.now()),
|
|
300
|
+
endTime: this.parseTime(e.endTime, Date.now() + 3600000),
|
|
301
|
+
extraGuestsAllowed: e.extraGuestsAllowed !== false
|
|
302
|
+
})
|
|
307
303
|
}, { quoted })
|
|
308
304
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
309
305
|
return msg
|
|
@@ -322,7 +318,7 @@ class NexusHandler {
|
|
|
322
318
|
contextInfo: proto.ContextInfo.create({
|
|
323
319
|
isForwarded: true,
|
|
324
320
|
forwardingScore: 1,
|
|
325
|
-
forwardedNewsletterMessageInfo: proto.ForwardedNewsletterMessageInfo.create({
|
|
321
|
+
forwardedNewsletterMessageInfo: proto.ContextInfo.ForwardedNewsletterMessageInfo.create({
|
|
326
322
|
newsletterName: p.newsletter?.newsletterName || 'Newsletter',
|
|
327
323
|
newsletterJid: p.newsletter?.newsletterJid || '120363399602691477@newsletter',
|
|
328
324
|
serverMessageId: 1000,
|
|
@@ -365,17 +361,23 @@ class NexusHandler {
|
|
|
365
361
|
async handleOrderMessage(content, jid, quoted) {
|
|
366
362
|
const o = content.orderMessage
|
|
367
363
|
const thumb = await this.downloadBuffer(o.thumbnail)
|
|
364
|
+
const cleanJid = (id) => {
|
|
365
|
+
if (!id) return null
|
|
366
|
+
const [user] = id.split(':')
|
|
367
|
+
return user.includes('@') ? user : `${user}@s.whatsapp.net`
|
|
368
|
+
}
|
|
369
|
+
const seller = cleanJid(o.sellerJid) || cleanJid(this.user?.id) || cleanJid(jid) || '0@s.whatsapp.net'
|
|
368
370
|
const msg = await this.genFromContent(jid, {
|
|
369
371
|
orderMessage: proto.Message.OrderMessage.create({
|
|
370
|
-
orderId: '7NEXUS25022008',
|
|
372
|
+
orderId: o.orderId || '7NEXUS25022008',
|
|
371
373
|
thumbnail: thumb,
|
|
372
374
|
itemCount: o.itemCount || 0,
|
|
373
|
-
status:
|
|
374
|
-
surface:
|
|
375
|
+
status: 2, // ACCEPTED
|
|
376
|
+
surface: 1, // CATALOG
|
|
375
377
|
message: o.message,
|
|
376
378
|
orderTitle: o.orderTitle,
|
|
377
|
-
sellerJid:
|
|
378
|
-
token: 'NEXUS_EXAMPLE_TOKEN',
|
|
379
|
+
sellerJid: seller,
|
|
380
|
+
token: o.token || 'NEXUS_EXAMPLE_TOKEN',
|
|
379
381
|
totalAmount1000: o.totalAmount1000 || 0,
|
|
380
382
|
totalCurrencyCode: o.totalCurrencyCode || 'IDR',
|
|
381
383
|
messageVersion: 2
|
|
@@ -394,7 +396,7 @@ class NexusHandler {
|
|
|
394
396
|
logger: this.opts.logger
|
|
395
397
|
})
|
|
396
398
|
const msg = await this.genFromContent(jid, {
|
|
397
|
-
groupStatusMessageV2: proto.Message.
|
|
399
|
+
groupStatusMessageV2: proto.Message.FutureProofMessage.create({ message: proto.Message.fromObject(mediaContent) })
|
|
398
400
|
}, { userJid: jid })
|
|
399
401
|
return await this.sendMsg(jid, msg.message, {
|
|
400
402
|
messageId: msg.key.id,
|
|
@@ -434,7 +436,7 @@ class NexusHandler {
|
|
|
434
436
|
subtitle: card.headerSubtitle || '',
|
|
435
437
|
hasMediaAttachment: false,
|
|
436
438
|
productMessage: proto.Message.ProductMessage.create({
|
|
437
|
-
product: proto.Message.ProductMessage.
|
|
439
|
+
product: proto.Message.ProductMessage.ProductSnapshot.create({
|
|
438
440
|
productImage: imgMedia?.imageMessage,
|
|
439
441
|
productId: card.productId || '123456',
|
|
440
442
|
title: card.productTitle,
|
|
@@ -489,7 +491,7 @@ class NexusHandler {
|
|
|
489
491
|
interactiveMessage: proto.Message.InteractiveMessage.create({
|
|
490
492
|
body: proto.Message.InteractiveMessage.Body.create({ text: c.body || '' }),
|
|
491
493
|
footer: proto.Message.InteractiveMessage.Footer.create({ text: c.footer || '' }),
|
|
492
|
-
carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards })
|
|
494
|
+
carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards, messageVersion: 1 })
|
|
493
495
|
})
|
|
494
496
|
}
|
|
495
497
|
}
|
package/lib/Socket/socket.js
CHANGED
|
@@ -87,12 +87,7 @@ export const makeSocket = (config) => {
|
|
|
87
87
|
makeSignalRepository
|
|
88
88
|
} = config
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
logger?.warn(
|
|
92
|
-
{},
|
|
93
|
-
'⚠️ printQRInTerminal is deprecated. Listen to connection.update and handle QR yourself.'
|
|
94
|
-
)
|
|
95
|
-
}
|
|
90
|
+
|
|
96
91
|
|
|
97
92
|
const url = typeof waWebSocketUrl === 'string' ? new URL(waWebSocketUrl) : waWebSocketUrl
|
|
98
93
|
|
|
@@ -114,6 +109,14 @@ export const makeSocket = (config) => {
|
|
|
114
109
|
logger.info({ url: url.toString() }, 'Initiating WebSocket connection')
|
|
115
110
|
ws.connect()
|
|
116
111
|
|
|
112
|
+
if (printQRInTerminal) {
|
|
113
|
+
import('qrcode-terminal').then(({ default: qrcode }) => {
|
|
114
|
+
ev.on('connection.update', ({ qr }) => {
|
|
115
|
+
if (qr) qrcode.generate(qr, { small: true })
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
117
120
|
const ev = makeEventBuffer(logger)
|
|
118
121
|
const { creds } = authState
|
|
119
122
|
const keys = addTransactionCapability(authState.keys, logger, transactionOpts)
|
|
@@ -237,8 +240,17 @@ export const makeSocket = (config) => {
|
|
|
237
240
|
const onWhatsApp = async (...phoneNumbers) => {
|
|
238
241
|
let usyncQuery = new USyncQuery()
|
|
239
242
|
let contactEnabled = false
|
|
240
|
-
for (
|
|
241
|
-
if (isLidUser(jid)) {
|
|
243
|
+
for (let jid of phoneNumbers) {
|
|
244
|
+
if (isLidUser(jid)) {
|
|
245
|
+
const pn = await signalRepository.lidMapping.getPNForLID(jid)
|
|
246
|
+
if (pn) {
|
|
247
|
+
jid = pn
|
|
248
|
+
} else {
|
|
249
|
+
if (!contactEnabled) { contactEnabled = true; usyncQuery = usyncQuery.withContactProtocol() }
|
|
250
|
+
usyncQuery.withUser(new USyncUser().withId(jid))
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
}
|
|
242
254
|
if (!contactEnabled) { contactEnabled = true; usyncQuery = usyncQuery.withContactProtocol() }
|
|
243
255
|
const phone = `+${jid.replace('+', '').split('@')[0]?.split(':')[0]}`
|
|
244
256
|
usyncQuery.withUser(new USyncUser().withPhone(phone))
|
|
@@ -246,7 +258,7 @@ export const makeSocket = (config) => {
|
|
|
246
258
|
if (usyncQuery.users.length === 0) return []
|
|
247
259
|
const results = await executeUSyncQuery(usyncQuery)
|
|
248
260
|
return results
|
|
249
|
-
? results.list.filter((a) => !!a.contact).map(({ contact, id }) => ({ jid: id, exists: contact }))
|
|
261
|
+
? results.list.filter((a) => !!a.contact && a.id && a.id !== 'undefined').map(({ contact, id }) => ({ jid: id, exists: contact }))
|
|
250
262
|
: []
|
|
251
263
|
}
|
|
252
264
|
|
|
@@ -567,7 +579,8 @@ export const makeSocket = (config) => {
|
|
|
567
579
|
}
|
|
568
580
|
ev.emit('connection.update', { connection: 'close', lastDisconnect: { error, date: new Date() } })
|
|
569
581
|
ev.removeAllListeners('connection.update')
|
|
570
|
-
ev.
|
|
582
|
+
ev.removeAllListeners()
|
|
583
|
+
ev.destroy?.()
|
|
571
584
|
}
|
|
572
585
|
|
|
573
586
|
const sendPassiveIq = (tag) =>
|