@neykoor/baileys 7.0.5-rc15 → 7.0.7-rc15
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/lib/Defaults/index.js +1 -1
- package/lib/Signal/lid-mapping.d.ts.map +1 -1
- package/lib/Signal/lid-mapping.js.map +1 -1
- package/lib/Socket/business.d.ts +33 -0
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +9 -2
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +33 -0
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +7 -2
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +33 -0
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/messages-recv.d.ts +33 -0
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-send.d.ts +34 -1
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +139 -65
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/smgss.d.ts +203 -0
- package/lib/Socket/smgss.d.ts.map +1 -0
- package/lib/Socket/smgss.js +461 -0
- package/lib/Socket/smgss.js.map +1 -0
- package/lib/Store/make-in-memory-store.d.ts.map +1 -1
- package/lib/Store/make-in-memory-store.js +12 -10
- package/lib/Store/make-in-memory-store.js.map +1 -1
- package/lib/Types/Message.d.ts +94 -2
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js +4 -0
- package/lib/Types/Message.js.map +1 -1
- package/lib/Utils/connection-watchdog.d.ts +30 -0
- package/lib/Utils/connection-watchdog.d.ts.map +1 -0
- package/lib/Utils/connection-watchdog.js +66 -0
- package/lib/Utils/connection-watchdog.js.map +1 -0
- package/lib/Utils/generics.js +1 -1
- package/lib/Utils/group-status-detection.d.ts +7 -0
- package/lib/Utils/group-status-detection.d.ts.map +1 -0
- package/lib/Utils/group-status-detection.js +72 -0
- package/lib/Utils/group-status-detection.js.map +1 -0
- package/lib/Utils/index.d.ts +3 -0
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +3 -0
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/message-retry-manager.d.ts.map +1 -1
- package/lib/Utils/message-retry-manager.js.map +1 -1
- package/lib/Utils/messages-media.d.ts +1 -0
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +92 -0
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages-newsletter.d.ts +34 -0
- package/lib/Utils/messages-newsletter.d.ts.map +1 -1
- package/lib/Utils/messages-newsletter.js +67 -0
- package/lib/Utils/messages-newsletter.js.map +1 -1
- package/lib/Utils/messages.d.ts +3 -46
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +290 -84
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/scheduling.d.ts +46 -0
- package/lib/Utils/scheduling.d.ts.map +1 -0
- package/lib/Utils/scheduling.js +117 -0
- package/lib/Utils/scheduling.js.map +1 -0
- package/lib/Utils/signal.d.ts +0 -4
- package/lib/Utils/signal.d.ts.map +1 -1
- package/lib/Utils/signal.js +7 -14
- package/lib/Utils/signal.js.map +1 -1
- package/lib/Utils/use-sqlite-auth-state.d.ts +1 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts.map +1 -1
- package/lib/Utils/use-sqlite-auth-state.js +18 -3
- package/lib/Utils/use-sqlite-auth-state.js.map +1 -1
- package/lib/Utils/validate-connection.d.ts.map +1 -1
- package/lib/Utils/validate-connection.js +1 -4
- package/lib/Utils/validate-connection.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { Boom } from '@neykoor/boom';
|
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import { proto } from '../../WAProto/index.js';
|
|
5
5
|
import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
6
|
-
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, captureUnifiedResponse, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateCodeBlockContent, generateLatexContent, generateListContent, generateMessageIDV2, generateParticipantHashV2, generateRichMessageContent, generateTableContent, generateUnifiedResponseContent, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
|
|
6
|
+
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, compressMedia, convertMedia, captureUnifiedResponse, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateCodeBlockContent, generateLatexContent, generateLatexImageContent, generateLatexInlineImageContent, generateListContent, generateMessageIDV2, generateParticipantHashV2, generateRichMessageContent, generateTableContent, generateUnifiedResponseContent, generateWAMessage, generateWAMessageFromContent, getMediaMetadata, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, imageToWebpSticker, MessageRetryManager, MessageScheduler, normalizeMessageContent, parseAndInjectE2ESessions, resizeImage, unixTimestampSeconds } from '../Utils/index.js';
|
|
7
7
|
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
8
8
|
import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
|
|
9
9
|
import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
|
|
@@ -11,30 +11,22 @@ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, res
|
|
|
11
11
|
import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET, STORIES_JID } from '../WABinary/index.js';
|
|
12
12
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
13
|
import { makeNewsletterSocket } from './newsletter.js';
|
|
14
|
+
import { Smgss } from './smgss.js';
|
|
14
15
|
export const makeMessagesSocket = (config) => {
|
|
15
16
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
|
|
16
17
|
const sock = makeNewsletterSocket(config);
|
|
17
18
|
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
|
|
18
19
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
19
|
-
/**
|
|
20
|
-
* Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
|
|
21
|
-
* Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
|
|
22
|
-
* Entries are always removed in `.finally()`, so the set is bounded by concurrency.
|
|
23
|
-
*/
|
|
24
20
|
const inFlightTcTokenIssuance = new Set();
|
|
25
21
|
const userDevicesCache = config.userDevicesCache ||
|
|
26
22
|
new NodeCache({
|
|
27
|
-
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
23
|
+
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
28
24
|
useClones: false
|
|
29
25
|
});
|
|
30
|
-
/** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
|
|
31
26
|
const devicesMutex = makeMutex();
|
|
32
|
-
// Initialize message retry manager if enabled
|
|
33
27
|
const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
|
|
34
|
-
// Prevent race conditions in Signal session encryption by user
|
|
35
28
|
const encryptionMutex = makeKeyedMutex();
|
|
36
29
|
let mediaConn;
|
|
37
|
-
/** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
|
|
38
30
|
let mediaHost = DEF_MEDIA_HOST;
|
|
39
31
|
const refreshMediaConn = async (forceGet = false) => {
|
|
40
32
|
const media = await mediaConn;
|
|
@@ -50,7 +42,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
50
42
|
content: [{ tag: 'media_conn', attrs: {} }]
|
|
51
43
|
});
|
|
52
44
|
const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
|
|
53
|
-
// TODO: explore full length of data that whatsapp provides
|
|
54
45
|
const node = {
|
|
55
46
|
hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
|
|
56
47
|
hostname: attrs.hostname,
|
|
@@ -69,10 +60,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
69
60
|
}
|
|
70
61
|
return mediaConn;
|
|
71
62
|
};
|
|
72
|
-
/**
|
|
73
|
-
* generic send receipt function
|
|
74
|
-
* used for receipts of phone call, read, delivery etc.
|
|
75
|
-
* */
|
|
76
63
|
const sendReceipt = async (jid, participant, messageIds, type) => {
|
|
77
64
|
if (!messageIds || messageIds.length === 0) {
|
|
78
65
|
throw new Boom('missing ids in receipt');
|
|
@@ -116,21 +103,17 @@ export const makeMessagesSocket = (config) => {
|
|
|
116
103
|
logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages');
|
|
117
104
|
await sendNode(node);
|
|
118
105
|
};
|
|
119
|
-
/** Correctly bulk send receipts to multiple chats, participants */
|
|
120
106
|
const sendReceipts = async (keys, type) => {
|
|
121
107
|
const recps = aggregateMessageKeysNotFromMe(keys);
|
|
122
108
|
for (const { jid, participant, messageIds } of recps) {
|
|
123
109
|
await sendReceipt(jid, participant, messageIds, type);
|
|
124
110
|
}
|
|
125
111
|
};
|
|
126
|
-
/** Bulk read messages. Keys can be from different chats & participants */
|
|
127
112
|
const readMessages = async (keys) => {
|
|
128
113
|
const privacySettings = await fetchPrivacySettings();
|
|
129
|
-
// based on privacy settings, we have to change the read type
|
|
130
114
|
const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
|
|
131
115
|
await sendReceipts(keys, readType);
|
|
132
116
|
};
|
|
133
|
-
/** Fetch all the devices we've to send a message to */
|
|
134
117
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
135
118
|
const deviceResults = [];
|
|
136
119
|
if (!useCache) {
|
|
@@ -193,16 +176,14 @@ export const makeMessagesSocket = (config) => {
|
|
|
193
176
|
}
|
|
194
177
|
const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
|
|
195
178
|
for (const jid of toFetch) {
|
|
196
|
-
query.withUser(new USyncUser().withId(jid));
|
|
179
|
+
query.withUser(new USyncUser().withId(jid));
|
|
197
180
|
}
|
|
198
181
|
const result = await sock.executeUSyncQuery(query);
|
|
199
182
|
if (result) {
|
|
200
|
-
// TODO: LID MAP this stuff (lid protocol will now return lid with devices)
|
|
201
183
|
const lidResults = result.list.filter(a => !!a.lid);
|
|
202
184
|
if (lidResults.length > 0) {
|
|
203
185
|
logger.trace('Storing LID maps from device call');
|
|
204
186
|
await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
|
|
205
|
-
// Force-refresh sessions for newly mapped LIDs to align identity addressing
|
|
206
187
|
try {
|
|
207
188
|
const lids = lidResults.map(a => a.lid);
|
|
208
189
|
if (lids.length) {
|
|
@@ -219,10 +200,8 @@ export const makeMessagesSocket = (config) => {
|
|
|
219
200
|
deviceMap[item.user] = deviceMap[item.user] || [];
|
|
220
201
|
deviceMap[item.user]?.push(item);
|
|
221
202
|
}
|
|
222
|
-
// Process each user's devices as a group for bulk LID migration
|
|
223
203
|
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
224
204
|
const isLidUser = requestedLidUsers.has(user);
|
|
225
|
-
// Process all devices for this user
|
|
226
205
|
for (const item of userDevices) {
|
|
227
206
|
const finalJid = isLidUser
|
|
228
207
|
? jidEncode(user, item.server, item.device)
|
|
@@ -241,7 +220,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
241
220
|
}
|
|
242
221
|
await devicesMutex.mutex(async () => {
|
|
243
222
|
if (userDevicesCache.mset) {
|
|
244
|
-
// if the cache supports mset, we can set all devices in one go
|
|
245
223
|
await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
|
|
246
224
|
}
|
|
247
225
|
else {
|
|
@@ -269,9 +247,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
269
247
|
}
|
|
270
248
|
return deviceResults;
|
|
271
249
|
};
|
|
272
|
-
/**
|
|
273
|
-
* Update Member Label
|
|
274
|
-
*/
|
|
275
250
|
const updateMemberLabel = (jid, memberLabel) => {
|
|
276
251
|
return relayMessage(jid, {
|
|
277
252
|
protocolMessage: {
|
|
@@ -309,7 +284,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
309
284
|
jidsRequiringFetch.push(jid);
|
|
310
285
|
}
|
|
311
286
|
if (jidsRequiringFetch.length) {
|
|
312
|
-
// LID if mapped, otherwise original
|
|
313
287
|
const wireJids = [
|
|
314
288
|
...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
|
|
315
289
|
...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
|
|
@@ -341,7 +315,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
341
315
|
return didFetchNewSession;
|
|
342
316
|
};
|
|
343
317
|
const sendPeerDataOperationMessage = async (pdoMessage) => {
|
|
344
|
-
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
345
318
|
if (!authState.creds.me?.id) {
|
|
346
319
|
throw new Boom('Not authenticated');
|
|
347
320
|
}
|
|
@@ -494,25 +467,23 @@ export const makeMessagesSocket = (config) => {
|
|
|
494
467
|
return;
|
|
495
468
|
}
|
|
496
469
|
if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
|
|
497
|
-
extraAttrs['decrypt-fail'] = 'hide';
|
|
470
|
+
extraAttrs['decrypt-fail'] = 'hide';
|
|
498
471
|
}
|
|
499
472
|
if (isGroupOrStatus && !isRetryResend) {
|
|
500
473
|
const [groupData, senderKeyMap] = await Promise.all([
|
|
501
474
|
(async () => {
|
|
502
|
-
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
|
|
475
|
+
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
|
|
503
476
|
if (groupData && Array.isArray(groupData?.participants)) {
|
|
504
477
|
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
|
|
505
478
|
}
|
|
506
479
|
else if (!isStatus) {
|
|
507
|
-
groupData = await groupMetadata(jid);
|
|
480
|
+
groupData = await groupMetadata(jid);
|
|
508
481
|
}
|
|
509
482
|
return groupData;
|
|
510
483
|
})(),
|
|
511
484
|
(async () => {
|
|
512
485
|
if (!participant && !isStatus) {
|
|
513
|
-
|
|
514
|
-
// on participant change in group, we should do sender memory manipulation
|
|
515
|
-
const result = await authState.keys.get('sender-key-memory', [jid]); // TODO: check out what if the sender key memory doesn't include the LID stuff now?
|
|
486
|
+
const result = await authState.keys.get('sender-key-memory', [jid]);
|
|
516
487
|
return result[jid] || {};
|
|
517
488
|
}
|
|
518
489
|
return {};
|
|
@@ -557,8 +528,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
557
528
|
!isHostedLidUser(deviceJid) &&
|
|
558
529
|
!isHostedPnUser(deviceJid) &&
|
|
559
530
|
device.device !== 99) {
|
|
560
|
-
//todo: revamp all this logic
|
|
561
|
-
// the goal is to follow with what I said above for each group, and instead of a true false map of ids, we can set an array full of those the app has already sent pkmsgs
|
|
562
531
|
senderKeyRecipients.push(deviceJid);
|
|
563
532
|
senderKeyMap[deviceJid] = true;
|
|
564
533
|
}
|
|
@@ -585,8 +554,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
585
554
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
|
|
586
555
|
}
|
|
587
556
|
else {
|
|
588
|
-
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
589
|
-
// TODO: investigate if this is true
|
|
590
557
|
let ownId = meId;
|
|
591
558
|
if (isLid && meLid) {
|
|
592
559
|
ownId = meLid;
|
|
@@ -607,7 +574,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
607
574
|
devices.push({
|
|
608
575
|
user,
|
|
609
576
|
device: 0,
|
|
610
|
-
jid: jidEncode(user, targetUserServer, 0)
|
|
577
|
+
jid: jidEncode(user, targetUserServer, 0)
|
|
611
578
|
});
|
|
612
579
|
if (user !== ownUser) {
|
|
613
580
|
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
@@ -619,13 +586,10 @@ export const makeMessagesSocket = (config) => {
|
|
|
619
586
|
});
|
|
620
587
|
}
|
|
621
588
|
if (additionalAttributes?.['category'] !== 'peer') {
|
|
622
|
-
// Clear placeholders and enumerate actual devices
|
|
623
589
|
devices.length = 0;
|
|
624
|
-
// Use conversation-appropriate sender identity
|
|
625
590
|
const senderIdentity = isLid && meLid
|
|
626
591
|
? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
|
|
627
592
|
: jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
|
|
628
|
-
// Enumerate devices for sender and target with consistent addressing
|
|
629
593
|
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
|
|
630
594
|
devices.push(...sessionDevices);
|
|
631
595
|
logger.debug({
|
|
@@ -645,7 +609,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
645
609
|
logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
|
|
646
610
|
continue;
|
|
647
611
|
}
|
|
648
|
-
// Check if this is our device (could match either PN or LID user)
|
|
649
612
|
const isMe = user === mePnUser || user === meLidUser;
|
|
650
613
|
if (isMe) {
|
|
651
614
|
meRecipients.push(jid);
|
|
@@ -657,7 +620,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
657
620
|
}
|
|
658
621
|
await assertSessions(allRecipients);
|
|
659
622
|
const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
|
|
660
|
-
// For own devices: use DSM if available (1:1 chats only)
|
|
661
623
|
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
|
662
624
|
createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
|
|
663
625
|
]);
|
|
@@ -725,7 +687,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
725
687
|
if (additionalAttributes?.['category'] === 'peer') {
|
|
726
688
|
const peerNode = participants[0]?.content?.[0];
|
|
727
689
|
if (peerNode) {
|
|
728
|
-
binaryNodeContent.push(peerNode);
|
|
690
|
+
binaryNodeContent.push(peerNode);
|
|
729
691
|
}
|
|
730
692
|
}
|
|
731
693
|
else {
|
|
@@ -746,9 +708,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
746
708
|
},
|
|
747
709
|
content: binaryNodeContent
|
|
748
710
|
};
|
|
749
|
-
// if the participant to send to is explicitly specified (generally retry recp)
|
|
750
|
-
// ensure the message is only sent to that person
|
|
751
|
-
// if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
|
|
752
711
|
if (participant) {
|
|
753
712
|
if (isJidGroup(destinationJid)) {
|
|
754
713
|
stanza.attrs.to = destinationJid;
|
|
@@ -797,19 +756,15 @@ export const makeMessagesSocket = (config) => {
|
|
|
797
756
|
logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
|
|
798
757
|
}
|
|
799
758
|
}
|
|
800
|
-
// WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
|
|
801
759
|
const isPeerMessage = additionalAttributes?.['category'] === 'peer';
|
|
802
760
|
const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
|
|
803
|
-
// Resolve destination to LID for tctoken storage — matches Signal session key pattern
|
|
804
761
|
const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
|
|
805
762
|
const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
|
|
806
763
|
const existingTokenEntry = contactTcTokenData[tcTokenJid];
|
|
807
764
|
let tcTokenBuffer = existingTokenEntry?.token;
|
|
808
|
-
// Treat expired tokens the same as missing — clear from cache
|
|
809
765
|
if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
|
|
810
766
|
logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
|
|
811
767
|
tcTokenBuffer = undefined;
|
|
812
|
-
// Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
|
|
813
768
|
const cleared = existingTokenEntry?.senderTimestamp !== undefined
|
|
814
769
|
? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
|
|
815
770
|
: null;
|
|
@@ -834,8 +789,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
834
789
|
}
|
|
835
790
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`);
|
|
836
791
|
await sendNode(stanza);
|
|
837
|
-
// Fire-and-forget: issue our token to the contact AFTER message send.
|
|
838
|
-
// WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
|
|
839
792
|
const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
|
|
840
793
|
const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
|
|
841
794
|
if (is1on1Send &&
|
|
@@ -876,7 +829,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
876
829
|
inFlightTcTokenIssuance.delete(tcTokenJid);
|
|
877
830
|
});
|
|
878
831
|
}
|
|
879
|
-
// Add message to retry cache if enabled
|
|
880
832
|
if (messageRetryManager && !participant) {
|
|
881
833
|
messageRetryManager.addRecentMessage(destinationJid, msgId, message);
|
|
882
834
|
}
|
|
@@ -938,12 +890,18 @@ export const makeMessagesSocket = (config) => {
|
|
|
938
890
|
else if (message.listResponseMessage) {
|
|
939
891
|
return 'list_response';
|
|
940
892
|
}
|
|
893
|
+
else if (message.buttonsResponseMessage) {
|
|
894
|
+
return 'buttons_response';
|
|
895
|
+
}
|
|
941
896
|
else if (message.orderMessage) {
|
|
942
897
|
return 'order';
|
|
943
898
|
}
|
|
944
899
|
else if (message.productMessage) {
|
|
945
900
|
return 'product';
|
|
946
901
|
}
|
|
902
|
+
else if (message.interactiveResponseMessage) {
|
|
903
|
+
return 'native_flow_response';
|
|
904
|
+
}
|
|
947
905
|
else if (message.groupInviteMessage) {
|
|
948
906
|
return 'url';
|
|
949
907
|
}
|
|
@@ -976,6 +934,83 @@ export const makeMessagesSocket = (config) => {
|
|
|
976
934
|
return result;
|
|
977
935
|
};
|
|
978
936
|
const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
|
|
937
|
+
const smgss = new Smgss(waUploadToServer, relayMessage, config, sock);
|
|
938
|
+
const getButtonType = (message) => {
|
|
939
|
+
if (message.listMessage)
|
|
940
|
+
return 'list';
|
|
941
|
+
if (message.buttonsMessage)
|
|
942
|
+
return 'buttons';
|
|
943
|
+
if (message.templateMessage)
|
|
944
|
+
return 'template';
|
|
945
|
+
if (message.interactiveMessage?.nativeFlowMessage)
|
|
946
|
+
return 'native_flow';
|
|
947
|
+
if (message.interactiveMessage?.shopStorefrontMessage)
|
|
948
|
+
return 'shop';
|
|
949
|
+
if (message.interactiveMessage?.collectionMessage)
|
|
950
|
+
return 'collection';
|
|
951
|
+
if (message.interactiveMessage?.carouselMessage)
|
|
952
|
+
return 'carousel';
|
|
953
|
+
if (message.interactiveMessage)
|
|
954
|
+
return 'interactive';
|
|
955
|
+
return null;
|
|
956
|
+
};
|
|
957
|
+
const getButtonArgs = (message) => {
|
|
958
|
+
const nativeFlow = message.interactiveMessage?.nativeFlowMessage;
|
|
959
|
+
const firstButtonName = nativeFlow?.buttons?.[0]?.name;
|
|
960
|
+
const nativeFlowSpecials = [
|
|
961
|
+
'mpm',
|
|
962
|
+
'cta_catalog',
|
|
963
|
+
'send_location',
|
|
964
|
+
'call_permission_request',
|
|
965
|
+
'wa_payment_transaction_details',
|
|
966
|
+
'automated_greeting_message_view_catalog'
|
|
967
|
+
];
|
|
968
|
+
const ts = unixTimestampSeconds().toString();
|
|
969
|
+
const bizBase = { actual_actors: '2', host_storage: '2', privacy_mode_ts: ts };
|
|
970
|
+
const qualityControl = { tag: 'quality_control', attrs: { source_type: 'third_party' } };
|
|
971
|
+
if (nativeFlow && (firstButtonName === 'review_and_pay' || firstButtonName === 'payment_info')) {
|
|
972
|
+
return {
|
|
973
|
+
tag: 'biz',
|
|
974
|
+
attrs: { native_flow_name: firstButtonName === 'review_and_pay' ? 'order_details' : firstButtonName }
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
|
|
978
|
+
return {
|
|
979
|
+
tag: 'biz',
|
|
980
|
+
attrs: bizBase,
|
|
981
|
+
content: [
|
|
982
|
+
{
|
|
983
|
+
tag: 'interactive',
|
|
984
|
+
attrs: { type: 'native_flow', v: '1' },
|
|
985
|
+
content: [{ tag: 'native_flow', attrs: { v: '2', name: firstButtonName } }]
|
|
986
|
+
},
|
|
987
|
+
qualityControl
|
|
988
|
+
]
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
else if (nativeFlow || message.buttonsMessage || message.templateMessage || message.interactiveMessage) {
|
|
992
|
+
return {
|
|
993
|
+
tag: 'biz',
|
|
994
|
+
attrs: bizBase,
|
|
995
|
+
content: [
|
|
996
|
+
{
|
|
997
|
+
tag: 'interactive',
|
|
998
|
+
attrs: { type: 'native_flow', v: '1' },
|
|
999
|
+
content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }]
|
|
1000
|
+
},
|
|
1001
|
+
qualityControl
|
|
1002
|
+
]
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
else if (message.listMessage) {
|
|
1006
|
+
return {
|
|
1007
|
+
tag: 'biz',
|
|
1008
|
+
attrs: bizBase,
|
|
1009
|
+
content: [{ tag: 'list', attrs: { v: '2', type: 'product_list' } }, qualityControl]
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
return { tag: 'biz', attrs: bizBase };
|
|
1013
|
+
};
|
|
979
1014
|
const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
|
|
980
1015
|
registerSocketEndHandler(() => {
|
|
981
1016
|
if (!config.userDevicesCache && userDevicesCache.close) {
|
|
@@ -1101,7 +1136,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1101
1136
|
}
|
|
1102
1137
|
return msg;
|
|
1103
1138
|
};
|
|
1104
|
-
|
|
1139
|
+
const messagesSock = {
|
|
1105
1140
|
...sock,
|
|
1106
1141
|
userDevicesCache,
|
|
1107
1142
|
devicesMutex,
|
|
@@ -1113,7 +1148,11 @@ export const makeMessagesSocket = (config) => {
|
|
|
1113
1148
|
sendReceipts,
|
|
1114
1149
|
readMessages,
|
|
1115
1150
|
refreshMediaConn,
|
|
1116
|
-
|
|
1151
|
+
resize: resizeImage,
|
|
1152
|
+
convert: convertMedia,
|
|
1153
|
+
toSticker: imageToWebpSticker,
|
|
1154
|
+
compress: compressMedia,
|
|
1155
|
+
metadata: getMediaMetadata,
|
|
1117
1156
|
getMediaHost: () => mediaHost,
|
|
1118
1157
|
waUploadToServer,
|
|
1119
1158
|
fetchPrivacySettings,
|
|
@@ -1185,6 +1224,16 @@ export const makeMessagesSocket = (config) => {
|
|
|
1185
1224
|
await relayMessage(jid, message, { messageId });
|
|
1186
1225
|
return { message, messageId };
|
|
1187
1226
|
},
|
|
1227
|
+
sendLatexImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
|
|
1228
|
+
const { message, messageId } = await generateLatexImageContent(quoted, options, uploadFn, renderLatexToPng);
|
|
1229
|
+
await relayMessage(jid, message, { messageId });
|
|
1230
|
+
return { message, messageId };
|
|
1231
|
+
},
|
|
1232
|
+
sendLatexInlineImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
|
|
1233
|
+
const { message, messageId } = await generateLatexInlineImageContent(quoted, options, uploadFn, renderLatexToPng);
|
|
1234
|
+
await relayMessage(jid, message, { messageId });
|
|
1235
|
+
return { message, messageId };
|
|
1236
|
+
},
|
|
1188
1237
|
sendUnifiedResponse: async (jid, quoted, captured) => {
|
|
1189
1238
|
const { message, messageId } = generateUnifiedResponseContent(quoted, captured);
|
|
1190
1239
|
await relayMessage(jid, message, { messageId });
|
|
@@ -1197,6 +1246,15 @@ export const makeMessagesSocket = (config) => {
|
|
|
1197
1246
|
},
|
|
1198
1247
|
sendMessage: async (jid, content, options = {}) => {
|
|
1199
1248
|
const userJid = authState.creds.me.id;
|
|
1249
|
+
const smgssType = smgss.detectType(content);
|
|
1250
|
+
if (smgssType) {
|
|
1251
|
+
const quoted = options?.quoted;
|
|
1252
|
+
const built = await smgss.handle(smgssType, content, jid, quoted);
|
|
1253
|
+
if (smgssType === 'ALBUM' || smgssType === 'EVENT' || smgssType === 'POLL_RESULT' || smgssType === 'GROUP_STORY') {
|
|
1254
|
+
return built;
|
|
1255
|
+
}
|
|
1256
|
+
content = built;
|
|
1257
|
+
}
|
|
1200
1258
|
if (typeof content === 'object' &&
|
|
1201
1259
|
'disappearingMessagesInChat' in content &&
|
|
1202
1260
|
typeof content['disappearingMessagesInChat'] !== 'undefined' &&
|
|
@@ -1222,7 +1280,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
1222
1280
|
logger,
|
|
1223
1281
|
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1224
1282
|
}),
|
|
1225
|
-
//TODO: CACHE
|
|
1226
1283
|
getProfilePicUrl: sock.profilePictureUrl,
|
|
1227
1284
|
getCallLink: sock.createCallLink,
|
|
1228
1285
|
upload: waUploadToServer,
|
|
@@ -1232,17 +1289,16 @@ export const makeMessagesSocket = (config) => {
|
|
|
1232
1289
|
...options
|
|
1233
1290
|
});
|
|
1234
1291
|
const isEventMsg = 'event' in content && !!content.event;
|
|
1235
|
-
const
|
|
1292
|
+
const deleteKey = 'delete' in content ? content.delete : undefined;
|
|
1293
|
+
const isDeleteMsg = !!deleteKey;
|
|
1236
1294
|
const isEditMsg = 'edit' in content && !!content.edit;
|
|
1237
1295
|
const isPinMsg = 'pin' in content && !!content.pin;
|
|
1238
1296
|
const isPollMessage = 'poll' in content && !!content.poll;
|
|
1239
1297
|
const isAiMsg = 'ai' in content && !!content.ai;
|
|
1240
1298
|
const additionalAttributes = {};
|
|
1241
1299
|
const additionalNodes = [];
|
|
1242
|
-
// required for delete
|
|
1243
1300
|
if (isDeleteMsg) {
|
|
1244
|
-
|
|
1245
|
-
if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
|
|
1301
|
+
if (isJidGroup(deleteKey?.remoteJid) && !deleteKey?.fromMe) {
|
|
1246
1302
|
additionalAttributes.edit = '8';
|
|
1247
1303
|
}
|
|
1248
1304
|
else {
|
|
@@ -1289,6 +1345,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
1289
1345
|
});
|
|
1290
1346
|
delete content.ai;
|
|
1291
1347
|
}
|
|
1348
|
+
const buttonType = getButtonType(fullMsg.message);
|
|
1349
|
+
if (buttonType) {
|
|
1350
|
+
const btnNode = getButtonArgs(fullMsg.message);
|
|
1351
|
+
if (btnNode)
|
|
1352
|
+
additionalNodes.push(btnNode);
|
|
1353
|
+
}
|
|
1292
1354
|
await relayMessage(jid, fullMsg.message, {
|
|
1293
1355
|
messageId: fullMsg.key.id,
|
|
1294
1356
|
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
@@ -1305,5 +1367,17 @@ export const makeMessagesSocket = (config) => {
|
|
|
1305
1367
|
}
|
|
1306
1368
|
}
|
|
1307
1369
|
};
|
|
1370
|
+
const messageScheduler = new MessageScheduler(messagesSock.sendMessage, {
|
|
1371
|
+
logger: config.logger
|
|
1372
|
+
});
|
|
1373
|
+
return {
|
|
1374
|
+
...messagesSock,
|
|
1375
|
+
messageScheduler,
|
|
1376
|
+
scheduleMessage: messageScheduler.schedule.bind(messageScheduler),
|
|
1377
|
+
scheduleMessageDelay: messageScheduler.scheduleDelay.bind(messageScheduler),
|
|
1378
|
+
cancelScheduledMessage: messageScheduler.cancel.bind(messageScheduler),
|
|
1379
|
+
cancelScheduledMessagesForJid: messageScheduler.cancelForJid.bind(messageScheduler),
|
|
1380
|
+
getPendingScheduledMessages: messageScheduler.getPending.bind(messageScheduler)
|
|
1381
|
+
};
|
|
1308
1382
|
};
|
|
1309
1383
|
//# sourceMappingURL=messages-send.js.map
|