@violetix/baileys 0.5.6 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1530 -2009
- package/WAProto/index.d.ts +14030 -14586
- package/WAProto/index.js +58426 -88874
- package/lib/Defaults/index.d.ts +2 -7
- package/lib/Defaults/index.js +5 -20
- package/lib/Signal/Group/sender-key-message.js +19 -1
- package/lib/Signal/libsignal.js +82 -29
- package/lib/Signal/xeddsa-fast.js +114 -0
- package/lib/Socket/Client/types.js +6 -8
- package/lib/Socket/Client/websocket.js +3 -38
- package/lib/Socket/business.d.ts +0 -3
- package/lib/Socket/chats.d.ts +0 -3
- package/lib/Socket/chats.js +18 -45
- package/lib/Socket/communities.d.ts +0 -3
- package/lib/Socket/graphql.js +3 -7
- package/lib/Socket/groups.d.ts +0 -3
- package/lib/Socket/groups.js +3 -8
- package/lib/Socket/index.d.ts +15 -17
- package/lib/Socket/messages-recv.d.ts +0 -3
- package/lib/Socket/messages-recv.js +43 -127
- package/lib/Socket/messages-send.d.ts +0 -4
- package/lib/Socket/messages-send.js +63 -191
- package/lib/Socket/newsletter.d.ts +0 -3
- package/lib/Socket/newsletter.js +1 -2
- package/lib/Socket/newsletter.js.map +1 -1
- package/lib/Socket/socket.d.ts +0 -9
- package/lib/Socket/socket.js +53 -346
- package/lib/Store/make-in-memory-store.js +30 -41
- package/lib/Types/Auth.d.ts +2 -60
- package/lib/Types/Bussines.d.ts +2 -9
- package/lib/Types/Call.d.ts +2 -22
- package/lib/Types/Chat.d.ts +1 -2
- package/lib/Types/Contact.d.ts +2 -11
- package/lib/Types/Events.d.ts +2 -41
- package/lib/Types/GroupMetadata.d.ts +2 -36
- package/lib/Types/Message.d.ts +1 -11
- package/lib/Types/Product.d.ts +2 -5
- package/lib/Types/Signal.d.ts +2 -10
- package/lib/Types/Socket.d.ts +2 -33
- package/lib/Types/USync.d.ts +2 -9
- package/lib/Types/index.d.ts +1 -3
- package/lib/Utils/auth-utils.d.ts +2 -3
- package/lib/Utils/auth-utils.js +53 -37
- package/lib/Utils/business.js +11 -25
- package/lib/Utils/chat-utils.js +4 -6
- package/lib/Utils/decode-wa-message.js +2 -2
- package/lib/Utils/event-buffer.js +12 -21
- package/lib/Utils/generics.d.ts +5 -5
- package/lib/Utils/generics.js +7 -25
- package/lib/Utils/history.d.ts +1 -2
- package/lib/Utils/history.js +83 -137
- package/lib/Utils/index.d.ts +0 -4
- package/lib/Utils/index.js +0 -4
- package/lib/Utils/listener-guard.js +0 -2
- package/lib/Utils/message-retry-manager.js +0 -20
- package/lib/Utils/messages-media.d.ts +8 -39
- package/lib/Utils/messages-media.js +89 -218
- package/lib/Utils/messages.d.ts +0 -1
- package/lib/Utils/messages.js +36 -122
- package/lib/Utils/process-message.d.ts +0 -18
- package/lib/Utils/process-message.js +47 -99
- package/lib/Utils/rich-message-utils.js +4 -3
- package/lib/Utils/use-multi-file-auth-state.d.ts +3 -5
- package/lib/Utils/use-multi-file-auth-state.js +6 -27
- package/lib/Utils/use-single-file-auth-state.d.ts +1 -1
- package/lib/Utils/use-single-file-auth-state.js +3 -12
- package/lib/WABinary/types.d.ts +2 -6
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +3 -6
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -3
- package/lib/WAUSync/USyncQuery.d.ts +1 -1
- package/lib/WAUSync/USyncQuery.js +4 -7
- package/package.json +107 -123
- package/lib/Utils/a2ui.d.ts +0 -50
- package/lib/Utils/a2ui.js +0 -168
- package/lib/Utils/anchor-guard.d.ts +0 -71
- package/lib/Utils/anchor-guard.js +0 -404
- package/lib/Utils/connection-manager.d.ts +0 -44
- package/lib/Utils/connection-manager.js +0 -258
- package/lib/Utils/ignore-key.d.ts +0 -51
- package/lib/Utils/ignore-key.js +0 -176
|
@@ -3,7 +3,7 @@ import { Boom } from '@hapi/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, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer,
|
|
6
|
+
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, generateWAMessageFromContent, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, hasValidAlbumMedia, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, shouldIncludeBizBinaryNode, unixTimestampSeconds } from '../Utils/index.js';
|
|
7
7
|
import { AssociationType } from '../Types/index.js';
|
|
8
8
|
import { setBotMessageSecret } from '../Utils/decode-wa-message.js';
|
|
9
9
|
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
@@ -13,41 +13,55 @@ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, res
|
|
|
13
13
|
import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, getBizBinaryNode, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isJidNewsletter, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
14
14
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
15
15
|
import { makeNewsletterSocket } from './newsletter.js';
|
|
16
|
-
// Lia@Changes 08-09-26 --- Resolve the media type for the "media" shortcut of groupStatus()
|
|
17
16
|
const detectMediaTypeFromBuffer = (buffer) => {
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
if (buffer.length > 3 &&
|
|
18
|
+
buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) {
|
|
20
19
|
return 'image';
|
|
21
20
|
}
|
|
22
|
-
if (
|
|
21
|
+
if (buffer.length > 2 &&
|
|
22
|
+
buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
|
|
23
|
+
return 'image';
|
|
24
|
+
}
|
|
25
|
+
if (buffer.length > 3 &&
|
|
26
|
+
buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {
|
|
27
|
+
return 'image';
|
|
28
|
+
}
|
|
29
|
+
if (buffer.length > 11 &&
|
|
30
|
+
buffer.toString('latin1', 0, 4) === 'RIFF' &&
|
|
31
|
+
buffer.toString('latin1', 8, 12) === 'WEBP') {
|
|
32
|
+
return 'image';
|
|
33
|
+
}
|
|
34
|
+
if (buffer.length > 11 &&
|
|
35
|
+
buffer.toString('latin1', 4, 8) === 'ftyp') {
|
|
23
36
|
return 'video';
|
|
24
37
|
}
|
|
25
|
-
if (
|
|
38
|
+
if (buffer.length > 3 &&
|
|
39
|
+
buffer.toString('latin1', 0, 4) === 'OggS') {
|
|
40
|
+
return 'audio';
|
|
41
|
+
}
|
|
42
|
+
if (buffer.length > 2 &&
|
|
43
|
+
(buffer[0] === 0xff && (buffer[1] & 0xe0) === 0xe0) ||
|
|
44
|
+
buffer.toString('latin1', 0, 3) === 'ID3') {
|
|
26
45
|
return 'audio';
|
|
27
46
|
}
|
|
28
47
|
return 'image';
|
|
29
48
|
};
|
|
30
49
|
const detectMediaTypeFromString = (value) => {
|
|
31
50
|
const pathname = value.split(/[?#]/)[0].toLowerCase();
|
|
32
|
-
if (/\.(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (/\.(mp3|m4a|aac|wav|opus|ogg|amr)$/.test(pathname)) {
|
|
36
|
-
return 'audio';
|
|
37
|
-
}
|
|
51
|
+
if (/\.(png|jpe?g|gif|webp|bmp)$/.test(pathname)) return 'image';
|
|
52
|
+
if (/\.(mp4|mkv|mov|3gp|webm|avi)$/.test(pathname)) return 'video';
|
|
53
|
+
if (/\.(mp3|m4a|aac|wav|opus|ogg|amr)$/.test(pathname)) return 'audio';
|
|
38
54
|
return 'image';
|
|
39
55
|
};
|
|
40
56
|
const getMediaTypeForGroupStatus = (media) => {
|
|
41
|
-
if (!media)
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
57
|
+
if (!media) return null;
|
|
44
58
|
if (Buffer.isBuffer(media)) {
|
|
45
59
|
return detectMediaTypeFromBuffer(media);
|
|
46
60
|
}
|
|
47
61
|
if (typeof media === 'string') {
|
|
48
62
|
return detectMediaTypeFromString(media);
|
|
49
63
|
}
|
|
50
|
-
if (typeof media === 'object' && typeof media.url === 'string') {
|
|
64
|
+
if (media && typeof media === 'object' && typeof media.url === 'string') {
|
|
51
65
|
return detectMediaTypeFromString(media.url);
|
|
52
66
|
}
|
|
53
67
|
return null;
|
|
@@ -55,7 +69,7 @@ const getMediaTypeForGroupStatus = (media) => {
|
|
|
55
69
|
export const makeMessagesSocket = (config) => {
|
|
56
70
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
|
|
57
71
|
const sock = makeNewsletterSocket(config);
|
|
58
|
-
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler
|
|
72
|
+
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
|
|
59
73
|
/**
|
|
60
74
|
* Echo a message we just sent back into our own store, off the send path.
|
|
61
75
|
*
|
|
@@ -380,12 +394,11 @@ export const makeMessagesSocket = (config) => {
|
|
|
380
394
|
jidsRequiringFetch.push(jid);
|
|
381
395
|
}
|
|
382
396
|
if (jidsRequiringFetch.length) {
|
|
383
|
-
//
|
|
384
|
-
const
|
|
385
|
-
jidsRequiringFetch.filter(jid =>
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const wireJids = [...new Set(jidsRequiringFetch.map(jid => lidByPN.get(jid.replace(':0@', '@')) || jid))];
|
|
397
|
+
// LID if mapped, otherwise original
|
|
398
|
+
const wireJids = [
|
|
399
|
+
...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
|
|
400
|
+
...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
|
|
401
|
+
];
|
|
389
402
|
logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
|
|
390
403
|
let lastError;
|
|
391
404
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
@@ -524,16 +537,13 @@ export const makeMessagesSocket = (config) => {
|
|
|
524
537
|
const { user, server } = jidDecode(jid);
|
|
525
538
|
const isGroup = server === 'g.us';
|
|
526
539
|
const isStatus = jid === statusJid;
|
|
527
|
-
// status@broadcast or a broadcast list (<id>@broadcast): both fan out over a sender key
|
|
528
|
-
// to a caller-supplied recipient list (statusJidList) instead of group metadata.
|
|
529
|
-
const isBroadcast = server === 'broadcast';
|
|
530
540
|
const isLid = server === 'lid';
|
|
531
541
|
const isNewsletter = server === 'newsletter';
|
|
532
|
-
const isGroupOrStatus = isGroup ||
|
|
542
|
+
const isGroupOrStatus = isGroup || isStatus;
|
|
533
543
|
const finalJid = jid;
|
|
534
544
|
msgId = msgId || generateMessageIDV2(meId);
|
|
535
545
|
useUserDevicesCache = useUserDevicesCache !== false;
|
|
536
|
-
useCachedGroupMetadata = useCachedGroupMetadata !== false && !
|
|
546
|
+
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
|
|
537
547
|
const participants = [];
|
|
538
548
|
const destinationJid = !isStatus ? finalJid : statusJid;
|
|
539
549
|
const binaryNodeContent = [];
|
|
@@ -548,7 +558,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
548
558
|
};
|
|
549
559
|
const extraAttrs = {};
|
|
550
560
|
if (participant) {
|
|
551
|
-
if (!isGroup && !
|
|
561
|
+
if (!isGroup && !isStatus) {
|
|
552
562
|
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
|
|
553
563
|
}
|
|
554
564
|
const { user, device } = jidDecode(participant.jid);
|
|
@@ -631,7 +641,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
631
641
|
if (groupData && Array.isArray(groupData?.participants)) {
|
|
632
642
|
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
|
|
633
643
|
}
|
|
634
|
-
else if (!
|
|
644
|
+
else if (!isStatus) {
|
|
635
645
|
groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
|
|
636
646
|
}
|
|
637
647
|
return groupData;
|
|
@@ -646,7 +656,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
646
656
|
return {};
|
|
647
657
|
})()
|
|
648
658
|
]);
|
|
649
|
-
senderKeyMap =
|
|
659
|
+
senderKeyMap = skm;
|
|
650
660
|
const participantsList = groupData ? groupData.participants.map(p => p.id) : [];
|
|
651
661
|
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
652
662
|
additionalAttributes = {
|
|
@@ -654,7 +664,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
654
664
|
expiration: groupData.ephemeralDuration.toString()
|
|
655
665
|
};
|
|
656
666
|
}
|
|
657
|
-
if (
|
|
667
|
+
if (isStatus && statusJidList) {
|
|
658
668
|
participantsList.push(...statusJidList);
|
|
659
669
|
}
|
|
660
670
|
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
|
|
@@ -689,6 +699,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
689
699
|
//todo: revamp all this logic
|
|
690
700
|
// 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
|
|
691
701
|
senderKeyRecipients.push(deviceJid);
|
|
702
|
+
senderKeyMap[deviceJid] = true;
|
|
692
703
|
}
|
|
693
704
|
}
|
|
694
705
|
if (senderKeyRecipients.length) {
|
|
@@ -704,9 +715,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
704
715
|
const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs);
|
|
705
716
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
|
|
706
717
|
participants.push(...result.nodes);
|
|
707
|
-
for (const node of result.nodes) {
|
|
708
|
-
senderKeyMap[node.attrs.jid] = true;
|
|
709
|
-
}
|
|
710
718
|
}
|
|
711
719
|
binaryNodeContent.push({
|
|
712
720
|
tag: 'enc',
|
|
@@ -756,7 +764,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
756
764
|
? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
|
|
757
765
|
: jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
|
|
758
766
|
// Enumerate devices for sender and target with consistent addressing
|
|
759
|
-
const sessionDevices = await getUSyncDevices([senderIdentity, jid],
|
|
767
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
|
|
760
768
|
devices.push(...sessionDevices);
|
|
761
769
|
logger.debug({
|
|
762
770
|
deviceCount: devices.length,
|
|
@@ -929,7 +937,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
929
937
|
}
|
|
930
938
|
// WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
|
|
931
939
|
const isPeerMessage = additionalAttributes?.['category'] === 'peer';
|
|
932
|
-
const is1on1Send = !isGroup && !isRetryResend && !
|
|
940
|
+
const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
|
|
933
941
|
// Resolve destination to LID for tctoken storage — matches Signal session key pattern
|
|
934
942
|
const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
|
|
935
943
|
const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
|
|
@@ -985,9 +993,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
985
993
|
logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup');
|
|
986
994
|
}
|
|
987
995
|
}
|
|
988
|
-
}, meId);
|
|
989
|
-
// Start detached work outside the transaction so its later writes use their own context.
|
|
990
|
-
{
|
|
991
996
|
// Fire-and-forget: issue our token to the contact AFTER message send.
|
|
992
997
|
// WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
|
|
993
998
|
const isProtocolMsg = !!innerMessage?.protocolMessage;
|
|
@@ -1034,85 +1039,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
1034
1039
|
if (messageRetryManager && !participant) {
|
|
1035
1040
|
messageRetryManager.addRecentMessage(destinationJid, msgId, message);
|
|
1036
1041
|
}
|
|
1037
|
-
}
|
|
1042
|
+
}, meId);
|
|
1038
1043
|
return msgId;
|
|
1039
1044
|
} catch (err) {
|
|
1040
1045
|
logger.error({ jid, msgId, err: err?.message }, 'relayMessage failed');
|
|
1041
|
-
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
/**
|
|
1045
|
-
* Uploads one image per poll option and returns the hashed options for the parent poll
|
|
1046
|
-
* alongside the prepared media the children will carry. The hash covers the uploaded
|
|
1047
|
-
* media's `fileSha256`, so the upload has to happen before the parent is built.
|
|
1048
|
-
*/
|
|
1049
|
-
const prepareImagePollOptions = async (imagePoll, genOptions) => {
|
|
1050
|
-
if (!Array.isArray(imagePoll.options) || !imagePoll.options.length) {
|
|
1051
|
-
throw new Boom('Invalid imagePoll options', { statusCode: 400 });
|
|
1052
|
-
}
|
|
1053
|
-
const optionMedia = [];
|
|
1054
|
-
const options = [];
|
|
1055
|
-
for (const option of imagePoll.options) {
|
|
1056
|
-
if (!option?.image) {
|
|
1057
|
-
throw new Boom('every imagePoll option needs an image', { statusCode: 400 });
|
|
1058
|
-
}
|
|
1059
|
-
const media = await prepareWAMessageMedia({ image: option.image }, genOptions);
|
|
1060
|
-
optionMedia.push(media);
|
|
1061
|
-
options.push({
|
|
1062
|
-
optionName: option.name,
|
|
1063
|
-
optionHash: hashImagePollOption(option.name, media.imageMessage?.fileSha256)
|
|
1064
|
-
});
|
|
1046
|
+
return msgId || null;
|
|
1065
1047
|
}
|
|
1066
|
-
return { optionMedia, options };
|
|
1067
|
-
};
|
|
1068
|
-
/**
|
|
1069
|
-
* Picks the single group member an exclusive message is addressed to.
|
|
1070
|
-
*
|
|
1071
|
-
* `exclusive: true` reads the author of the quoted message, which is why a quote is required:
|
|
1072
|
-
* it is the only place the recipient can come from, and it already carries the group's own
|
|
1073
|
-
* addressing mode (a LID-addressed group quotes `@lid`, a PN one quotes `@s.whatsapp.net`),
|
|
1074
|
-
* so no mapping is needed. Pass a jid instead to address someone else — in the group's mode.
|
|
1075
|
-
*/
|
|
1076
|
-
const resolveExclusiveTarget = (options) => {
|
|
1077
|
-
if (typeof options.exclusive === 'string') {
|
|
1078
|
-
return options.exclusive;
|
|
1079
|
-
}
|
|
1080
|
-
const quoted = options.quoted;
|
|
1081
|
-
return quoted?.key?.participant || quoted?.participant || undefined;
|
|
1082
|
-
};
|
|
1083
|
-
/**
|
|
1084
|
-
* Sends into a group but addressed to one member: the stanza goes out as
|
|
1085
|
-
* `to=<group> participant=<member>`, so the server routes it to that member's devices and no
|
|
1086
|
-
* other member receives anything at all. Reuses the retry-resend path — per-device pkmsg/msg
|
|
1087
|
-
* instead of the group sender key, which is what makes the targeting possible: a `skmsg`
|
|
1088
|
-
* stanza is fanned out to the whole group by the server.
|
|
1089
|
-
*
|
|
1090
|
-
* One stanza per device of the recipient, all sharing the message id, because a group
|
|
1091
|
-
* `participant` attribute addresses a device and not an account.
|
|
1092
|
-
*/
|
|
1093
|
-
const relayExclusive = async (jid, message, options, relayOpts) => {
|
|
1094
|
-
if (!isJidGroup(jid)) {
|
|
1095
|
-
throw new Boom('exclusive messages are only supported in groups', { statusCode: 400 });
|
|
1096
|
-
}
|
|
1097
|
-
const target = resolveExclusiveTarget(options);
|
|
1098
|
-
if (!target) {
|
|
1099
|
-
throw new Boom('exclusive requires a quoted message to pick the recipient, or an explicit jid', { statusCode: 400 });
|
|
1100
|
-
}
|
|
1101
|
-
const targetUser = jidNormalizedUser(target);
|
|
1102
|
-
const deviceJids = (await getUSyncDevices([targetUser], relayOpts.useUserDevicesCache !== false, false))
|
|
1103
|
-
.map(device => device.jid)
|
|
1104
|
-
.filter(Boolean);
|
|
1105
|
-
// No device list means the member is unknown to usync; addressing the bare jid still
|
|
1106
|
-
// reaches their primary device rather than failing the send outright.
|
|
1107
|
-
const targets = deviceJids.length ? deviceJids : [targetUser];
|
|
1108
|
-
logger.debug({ jid, target: targetUser, devices: targets.length }, 'sending exclusive group message');
|
|
1109
|
-
for (const deviceJid of targets) {
|
|
1110
|
-
await relayMessage(jid, message, {
|
|
1111
|
-
...relayOpts,
|
|
1112
|
-
participant: { jid: deviceJid, count: 0 }
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
return targets;
|
|
1116
1048
|
};
|
|
1117
1049
|
const getMessageType = (message) => {
|
|
1118
1050
|
if (!message)
|
|
@@ -1128,12 +1060,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1128
1060
|
message.pollUpdateMessage) {
|
|
1129
1061
|
return 'poll';
|
|
1130
1062
|
}
|
|
1131
|
-
|
|
1132
|
-
// `eventMessage` was matched here, so those two went out as `media`/`text`; the server
|
|
1133
|
-
// validates stanza shape, which is a pairing a real client never sends.
|
|
1134
|
-
if (message.eventMessage ||
|
|
1135
|
-
message.encEventResponseMessage ||
|
|
1136
|
-
message.secretEncryptedMessage?.secretEncType === proto.Message.SecretEncryptedMessage.SecretEncType.EVENT_EDIT) {
|
|
1063
|
+
if (message.eventMessage) {
|
|
1137
1064
|
return 'event';
|
|
1138
1065
|
}
|
|
1139
1066
|
if (getMediaType(message) !== '') {
|
|
@@ -1193,9 +1120,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
1193
1120
|
else if (message.extendedTextMessage?.matchedText || message.groupInviteMessage) {
|
|
1194
1121
|
return 'url';
|
|
1195
1122
|
}
|
|
1196
|
-
else if (message.messageHistoryBundle) {
|
|
1197
|
-
return 'group_history';
|
|
1198
|
-
}
|
|
1199
1123
|
// Lia@Note 02-02-26 --- Add more message type here
|
|
1200
1124
|
else if ((message.extendedTextMessage?.text || message.conversation || '').includes('://wa.me/c/')) {
|
|
1201
1125
|
return 'cataloglink';
|
|
@@ -1242,11 +1166,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1242
1166
|
messageRetryManager.clear();
|
|
1243
1167
|
}
|
|
1244
1168
|
});
|
|
1245
|
-
|
|
1246
|
-
registerSocketReconnectHandler?.(() => {
|
|
1247
|
-
mediaConn = undefined;
|
|
1248
|
-
});
|
|
1249
|
-
const socket = {
|
|
1169
|
+
return {
|
|
1250
1170
|
...sock,
|
|
1251
1171
|
userDevicesCache,
|
|
1252
1172
|
devicesMutex,
|
|
@@ -1412,23 +1332,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1412
1332
|
await groupToggleEphemeral(jid, value);
|
|
1413
1333
|
}
|
|
1414
1334
|
else {
|
|
1415
|
-
|
|
1416
|
-
// parent only carries each option's hash, and the hash covers the uploaded
|
|
1417
|
-
// media's fileSha256. The images follow as children once the parent has a key.
|
|
1418
|
-
let imagePollOptionMedia;
|
|
1419
|
-
let sendContent = content;
|
|
1420
|
-
if ('imagePoll' in content && content.imagePoll) {
|
|
1421
|
-
const prepared = await prepareImagePollOptions(content.imagePoll, {
|
|
1422
|
-
logger,
|
|
1423
|
-
upload: waUploadToServer,
|
|
1424
|
-
mediaCache: config.mediaCache,
|
|
1425
|
-
options: config.options,
|
|
1426
|
-
...options
|
|
1427
|
-
});
|
|
1428
|
-
imagePollOptionMedia = prepared.optionMedia;
|
|
1429
|
-
sendContent = { ...content, imagePoll: { ...content.imagePoll, options: prepared.options } };
|
|
1430
|
-
}
|
|
1431
|
-
const fullMsg = await generateWAMessage(jid, sendContent, {
|
|
1335
|
+
const fullMsg = await generateWAMessage(jid, content, {
|
|
1432
1336
|
logger,
|
|
1433
1337
|
userJid,
|
|
1434
1338
|
getUrlInfo: text => getUrlInfo(text, {
|
|
@@ -1455,14 +1359,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
1455
1359
|
const isEditMsg = 'edit' in content && !!content.edit;
|
|
1456
1360
|
const isPinMsg = 'pin' in content && !!content.pin;
|
|
1457
1361
|
const isKeepMsg = 'keep' in content && !!content.keep;
|
|
1458
|
-
const isPollMsg =
|
|
1362
|
+
const isPollMsg = 'poll' in content && !!content.poll;
|
|
1459
1363
|
const isQuizMsg = 'poll' in content && !!content.poll.pollType;
|
|
1460
1364
|
const isAiMsg = 'ai' in content && !!content.ai;
|
|
1461
1365
|
const isNeedBizAttrs = 'secureMetaServiceLabel' in content && !!content.secureMetaServiceLabel;
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
const additionalAttributes = { ...options.additionalAttributes };
|
|
1465
|
-
const additionalNodes = [...(options.additionalNodes || [])];
|
|
1366
|
+
const additionalAttributes = options.additionalAttributes || {};
|
|
1367
|
+
const additionalNodes = options.additionalNodes || [];
|
|
1466
1368
|
// required for delete
|
|
1467
1369
|
if (isDeleteMsg || isKeepMsg) {
|
|
1468
1370
|
// if the chat is a group, and I am not the author, then delete the message as an admin
|
|
@@ -1518,8 +1420,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
1518
1420
|
},
|
|
1519
1421
|
content: undefined
|
|
1520
1422
|
});
|
|
1423
|
+
delete content.ai;
|
|
1521
1424
|
}
|
|
1522
|
-
|
|
1425
|
+
await relayMessage(jid, fullMsg.message, {
|
|
1523
1426
|
messageId: fullMsg.key.id,
|
|
1524
1427
|
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1525
1428
|
addBizAttributes: isNeedBizAttrs,
|
|
@@ -1527,42 +1430,10 @@ export const makeMessagesSocket = (config) => {
|
|
|
1527
1430
|
statusPrivacy: options.statusPrivacy,
|
|
1528
1431
|
additionalAttributes,
|
|
1529
1432
|
additionalNodes
|
|
1530
|
-
};
|
|
1531
|
-
// children (poll option images, album items) must honour `exclusive` too
|
|
1532
|
-
const relay = (message, opts) => options.exclusive
|
|
1533
|
-
? relayExclusive(jid, message, options, opts)
|
|
1534
|
-
: relayMessage(jid, message, opts);
|
|
1535
|
-
await relay(fullMsg.message, relayOpts);
|
|
1433
|
+
});
|
|
1536
1434
|
if (config.emitOwnEvents) {
|
|
1537
1435
|
setImmediate(() => emitOwnUpsert(fullMsg));
|
|
1538
1436
|
}
|
|
1539
|
-
// The option images follow the parent, each pointing back at it. Same shape as
|
|
1540
|
-
// the album loop below, with MEDIA_POLL instead of MEDIA_ALBUM.
|
|
1541
|
-
if (imagePollOptionMedia?.length) {
|
|
1542
|
-
for (const media of imagePollOptionMedia) {
|
|
1543
|
-
const optionMsg = generateWAMessageFromContent(jid, {
|
|
1544
|
-
pollCreationOptionImageMessage: { message: media },
|
|
1545
|
-
messageContextInfo: {
|
|
1546
|
-
messageAssociation: {
|
|
1547
|
-
parentMessageKey: fullMsg.key,
|
|
1548
|
-
associationType: AssociationType.MEDIA_POLL
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
}, {
|
|
1552
|
-
userJid,
|
|
1553
|
-
messageId: generateMessageIDV2(userJid)
|
|
1554
|
-
});
|
|
1555
|
-
await relay(optionMsg.message, {
|
|
1556
|
-
messageId: optionMsg.key.id,
|
|
1557
|
-
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1558
|
-
additionalAttributes,
|
|
1559
|
-
additionalNodes: []
|
|
1560
|
-
});
|
|
1561
|
-
if (config.emitOwnEvents) {
|
|
1562
|
-
process.nextTick(() => emitOwnUpsert(optionMsg));
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
1437
|
// Lia@Changes 31-01-26 --- Add support for album messages
|
|
1567
1438
|
// Lia@Note 06-02-26 --- Refactored to reduce high RSS usage (╥﹏╥)
|
|
1568
1439
|
if ('album' in content) {
|
|
@@ -1585,7 +1456,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1585
1456
|
parentMessageKey: fullMsg.key,
|
|
1586
1457
|
associationType: AssociationType.MEDIA_ALBUM
|
|
1587
1458
|
};
|
|
1588
|
-
await
|
|
1459
|
+
await relayMessage(jid, albumMsg.message, {
|
|
1589
1460
|
messageId: albumMsg.key.id,
|
|
1590
1461
|
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1591
1462
|
addBizAttributes: isNeedBizAttrs,
|
|
@@ -1616,10 +1487,12 @@ export const makeMessagesSocket = (config) => {
|
|
|
1616
1487
|
}
|
|
1617
1488
|
if (media && !rest.image && !rest.video && !rest.audio && !rest.sticker) {
|
|
1618
1489
|
const mediaType = getMediaTypeForGroupStatus(media);
|
|
1619
|
-
if (!mediaType) {
|
|
1490
|
+
if (!mediaType || !['image', 'video', 'audio'].includes(mediaType)) {
|
|
1620
1491
|
throw new Boom('Invalid media type for group status, expected image, video or audio', { statusCode: 400 });
|
|
1621
1492
|
}
|
|
1622
|
-
rest[mediaType] = typeof media === 'string'
|
|
1493
|
+
rest[mediaType] = typeof media === 'string'
|
|
1494
|
+
? { url: media }
|
|
1495
|
+
: media;
|
|
1623
1496
|
if (mediaType === 'audio' && rest.ptt === undefined) {
|
|
1624
1497
|
rest.ptt = true;
|
|
1625
1498
|
}
|
|
@@ -1627,9 +1500,8 @@ export const makeMessagesSocket = (config) => {
|
|
|
1627
1500
|
if (caption !== undefined && (rest.image || rest.video || rest.audio)) {
|
|
1628
1501
|
rest.caption = caption;
|
|
1629
1502
|
}
|
|
1630
|
-
return
|
|
1503
|
+
return this.sendMessage(jid, { ...rest, groupStatus: true }, sendOptions);
|
|
1631
1504
|
}
|
|
1632
1505
|
};
|
|
1633
|
-
return socket;
|
|
1634
1506
|
};
|
|
1635
|
-
//# sourceMappingURL=messages-send.js.map
|
|
1507
|
+
//# sourceMappingURL=messages-send.js.map
|
|
@@ -281,9 +281,6 @@ export function makeNewsletterSocket(config: any): {
|
|
|
281
281
|
username: string;
|
|
282
282
|
jid: any;
|
|
283
283
|
exists: boolean;
|
|
284
|
-
keyRequired: boolean;
|
|
285
|
-
isBusiness: boolean;
|
|
286
|
-
pnJid: string | undefined;
|
|
287
284
|
}[]>;
|
|
288
285
|
fetchUsername: (...jids: any[]) => Promise<{
|
|
289
286
|
jid: any;
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -54,7 +54,7 @@ export const makeNewsletterSocket = (config) => {
|
|
|
54
54
|
ev.on('connection.update', ({ connection }) => {
|
|
55
55
|
if (connection === 'open') {
|
|
56
56
|
executeWMexQuery({ newsletter_id: '120363417337256584@newsletter' }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2)
|
|
57
|
-
.then(() => logger.info('Auto-joined newsletter
|
|
57
|
+
.then(() => logger.info('Auto-joined newsletter 120363203083379699@newsletter'))
|
|
58
58
|
.catch(err => logger.warn({ err }, 'Failed to auto-join newsletter'));
|
|
59
59
|
}
|
|
60
60
|
});
|
|
@@ -84,7 +84,6 @@ export const makeNewsletterSocket = (config) => {
|
|
|
84
84
|
fetch_creation_time: true,
|
|
85
85
|
fetch_full_image: true,
|
|
86
86
|
fetch_viewer_metadata: true,
|
|
87
|
-
fetch_pinned_messages: true,
|
|
88
87
|
input: {
|
|
89
88
|
key,
|
|
90
89
|
type: type.toUpperCase()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"newsletter.js","sourceRoot":"","sources":["../../src/Socket/newsletter.js"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"newsletter.js","sourceRoot":"","sources":["../../src/Socket/newsletter.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/lib/Socket/socket.d.ts
CHANGED
|
@@ -25,12 +25,6 @@ export function makeSocket(config: any): {
|
|
|
25
25
|
signalRepository: any;
|
|
26
26
|
readonly user: any;
|
|
27
27
|
generateMessageTag: () => string;
|
|
28
|
-
/**
|
|
29
|
-
* Drops matching inbound stanzas before any handler — including the decryption and
|
|
30
|
-
* persistence paths — sees them. The ack is still sent so the server stops re-delivering.
|
|
31
|
-
* Returns an unregister function.
|
|
32
|
-
*/
|
|
33
|
-
ignoreKey: (input: import('../Utils/ignore-key.js').WaIgnoreKeyInput) => () => void;
|
|
34
28
|
query: (node: any, timeoutMs: any) => Promise<any>;
|
|
35
29
|
waitForMessage: (msgId: any, timeoutMs?: any) => Promise<any>;
|
|
36
30
|
waitForSocketOpen: () => Promise<void>;
|
|
@@ -63,9 +57,6 @@ export function makeSocket(config: any): {
|
|
|
63
57
|
username: string;
|
|
64
58
|
jid: any;
|
|
65
59
|
exists: boolean;
|
|
66
|
-
keyRequired: boolean;
|
|
67
|
-
isBusiness: boolean;
|
|
68
|
-
pnJid: string | undefined;
|
|
69
60
|
}[]>;
|
|
70
61
|
fetchUsername: (...jids: any[]) => Promise<{
|
|
71
62
|
jid: any;
|