@kelvdra/baileys 1.0.4 → 1.0.5-rc.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/LICENSE +21 -0
- package/WAProto/index.js +65472 -137440
- package/lib/Defaults/index.d.ts +1 -1
- package/lib/Defaults/index.js +22 -3
- package/lib/Socket/chats.js +12 -13
- package/lib/Socket/groups.js +321 -18
- package/lib/Socket/hydra.js +521 -0
- package/lib/Socket/messages-recv.js +736 -324
- package/lib/Socket/messages-send.js +526 -113
- package/lib/Socket/mex.js +44 -6
- package/lib/Socket/newsletter.d.ts +16 -9
- package/lib/Socket/newsletter.js +259 -70
- package/lib/Types/GroupMetadata.d.ts +16 -0
- package/lib/Types/Mex.d.ts +141 -0
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/State.js +54 -1
- package/lib/Utils/auth-utils.js +12 -1
- package/lib/Utils/chat-utils.js +36 -2
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/decode-wa-message.js +23 -4
- package/lib/Utils/generics.js +4 -1
- package/lib/Utils/identity-change-handler.d.ts +44 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.js +1 -1
- package/lib/Utils/message-retry-manager.js +25 -1
- package/lib/Utils/messages-media.js +162 -43
- package/lib/Utils/messages.d.ts +1 -1
- package/lib/Utils/messages.js +230 -9
- package/lib/Utils/offline-node-processor.d.ts +17 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.js +45 -1
- package/lib/Utils/stanza-ack.d.ts +11 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/sync-action-utils.d.ts +19 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/tc-token-utils.d.ts +37 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/index.d.ts +1 -0
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncQuery.js +5 -1
- package/package.json +3 -1
|
@@ -2,17 +2,20 @@ import NodeCache from '@cacheable/node-cache';
|
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { proto } from '../../WAProto/index.js';
|
|
4
4
|
import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
5
|
-
import
|
|
5
|
+
import * as Utils_1 from "../Utils/index.js"
|
|
6
|
+
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
|
|
6
7
|
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
7
8
|
import { makeKeyedMutex } from '../Utils/make-mutex.js';
|
|
8
|
-
import
|
|
9
|
+
import * as WABinary_1 from "../WABinary/index.js"
|
|
9
10
|
import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidGroup, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
10
11
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
11
12
|
import { makeNewsletterSocket } from './newsletter.js';
|
|
13
|
+
import hydra from './hydra.js'
|
|
14
|
+
import { randomBytes } from "crypto"
|
|
12
15
|
export const makeMessagesSocket = (config) => {
|
|
13
16
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
|
|
14
17
|
const sock = makeNewsletterSocket(config);
|
|
15
|
-
const { ev, authState,
|
|
18
|
+
const { ev, authState, processingMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
|
|
16
19
|
const userDevicesCache = config.userDevicesCache ||
|
|
17
20
|
new NodeCache({
|
|
18
21
|
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
@@ -255,31 +258,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
255
258
|
}
|
|
256
259
|
return deviceResults;
|
|
257
260
|
};
|
|
258
|
-
/**
|
|
259
|
-
* Update Member Label
|
|
260
|
-
*/
|
|
261
|
-
const updateMemberLabel = (jid, memberLabel) => {
|
|
262
|
-
return relayMessage(jid, {
|
|
263
|
-
protocolMessage: {
|
|
264
|
-
type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
|
|
265
|
-
memberLabel: {
|
|
266
|
-
label: memberLabel?.slice(0, 30),
|
|
267
|
-
labelTimestamp: unixTimestampSeconds()
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}, {
|
|
271
|
-
additionalNodes: [
|
|
272
|
-
{
|
|
273
|
-
tag: 'meta',
|
|
274
|
-
attrs: {
|
|
275
|
-
tag_reason: 'user_update',
|
|
276
|
-
appdata: 'member_tag'
|
|
277
|
-
},
|
|
278
|
-
content: undefined
|
|
279
|
-
}
|
|
280
|
-
]
|
|
281
|
-
});
|
|
282
|
-
};
|
|
283
261
|
const assertSessions = async (jids, force) => {
|
|
284
262
|
let didFetchNewSession = false;
|
|
285
263
|
const uniqueJids = [...new Set(jids)]; // Deduplicate JIDs
|
|
@@ -376,59 +354,57 @@ export const makeMessagesSocket = (config) => {
|
|
|
376
354
|
? patched
|
|
377
355
|
: recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
|
|
378
356
|
let shouldIncludeDeviceIdentity = false;
|
|
379
|
-
const meId = authState.creds
|
|
357
|
+
const meId = assertMeId(authState.creds);
|
|
380
358
|
const meLid = authState.creds.me?.lid;
|
|
381
359
|
const meLidUser = meLid ? jidDecode(meLid)?.user : null;
|
|
382
360
|
const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
|
|
383
|
-
|
|
384
|
-
if (!jid)
|
|
385
|
-
return null;
|
|
386
|
-
let msgToEncrypt = patchedMessage;
|
|
387
|
-
if (dsmMessage) {
|
|
388
|
-
const { user: targetUser } = jidDecode(jid);
|
|
389
|
-
const { user: ownPnUser } = jidDecode(meId);
|
|
390
|
-
const ownLidUser = meLidUser;
|
|
391
|
-
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
|
|
392
|
-
const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
|
|
393
|
-
if (isOwnUser && !isExactSenderDevice) {
|
|
394
|
-
msgToEncrypt = dsmMessage;
|
|
395
|
-
logger.debug({ jid, targetUser }, 'Using DSM for own device');
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
const bytes = encodeWAMessage(msgToEncrypt);
|
|
399
|
-
const mutexKey = jid;
|
|
400
|
-
const node = await encryptionMutex.mutex(mutexKey, async () => {
|
|
401
|
-
const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
|
|
402
|
-
if (type === 'pkmsg') {
|
|
403
|
-
shouldIncludeDeviceIdentity = true;
|
|
404
|
-
}
|
|
405
|
-
return {
|
|
406
|
-
tag: 'to',
|
|
407
|
-
attrs: { jid },
|
|
408
|
-
content: [
|
|
409
|
-
{
|
|
410
|
-
tag: 'enc',
|
|
411
|
-
attrs: { v: '2', type, ...(extraAttrs || {}) },
|
|
412
|
-
content: ciphertext
|
|
413
|
-
}
|
|
414
|
-
]
|
|
415
|
-
};
|
|
416
|
-
});
|
|
417
|
-
return node;
|
|
418
|
-
}
|
|
419
|
-
catch (err) {
|
|
420
|
-
logger.error({ jid, err }, 'Failed to encrypt for recipient');
|
|
361
|
+
if (!jid)
|
|
421
362
|
return null;
|
|
363
|
+
let msgToEncrypt = patchedMessage;
|
|
364
|
+
if (dsmMessage) {
|
|
365
|
+
const { user: targetUser } = jidDecode(jid);
|
|
366
|
+
const { user: ownPnUser } = jidDecode(meId);
|
|
367
|
+
const ownLidUser = meLidUser;
|
|
368
|
+
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
|
|
369
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
|
|
370
|
+
if (isOwnUser && !isExactSenderDevice) {
|
|
371
|
+
msgToEncrypt = dsmMessage;
|
|
372
|
+
logger.debug({ jid, targetUser }, 'Using DSM for own device');
|
|
373
|
+
}
|
|
422
374
|
}
|
|
375
|
+
const bytes = encodeWAMessage(msgToEncrypt);
|
|
376
|
+
const mutexKey = jid;
|
|
377
|
+
const node = await encryptionMutex.mutex(mutexKey, async () => {
|
|
378
|
+
const { type, ciphertext } = await signalRepository.encryptMessage({
|
|
379
|
+
jid,
|
|
380
|
+
data: bytes
|
|
381
|
+
});
|
|
382
|
+
if (type === 'pkmsg') {
|
|
383
|
+
shouldIncludeDeviceIdentity = true;
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
tag: 'to',
|
|
387
|
+
attrs: { jid },
|
|
388
|
+
content: [
|
|
389
|
+
{
|
|
390
|
+
tag: 'enc',
|
|
391
|
+
attrs: {
|
|
392
|
+
v: '2',
|
|
393
|
+
type,
|
|
394
|
+
...(extraAttrs || {})
|
|
395
|
+
},
|
|
396
|
+
content: ciphertext
|
|
397
|
+
}
|
|
398
|
+
]
|
|
399
|
+
};
|
|
400
|
+
});
|
|
401
|
+
return node;
|
|
423
402
|
});
|
|
424
403
|
const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
|
|
425
|
-
if (recipientJids.length > 0 && nodes.length === 0) {
|
|
426
|
-
throw new Boom('All encryptions failed', { statusCode: 500 });
|
|
427
|
-
}
|
|
428
404
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
429
405
|
};
|
|
430
|
-
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
|
|
431
|
-
const meId = authState.creds
|
|
406
|
+
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList } = {}) => {
|
|
407
|
+
const meId = assertMeId(authState.creds);
|
|
432
408
|
const meLid = authState.creds.me?.lid;
|
|
433
409
|
const isRetryResend = Boolean(participant?.jid);
|
|
434
410
|
let shouldIncludeDeviceIdentity = isRetryResend;
|
|
@@ -441,13 +417,19 @@ export const makeMessagesSocket = (config) => {
|
|
|
441
417
|
const isGroupOrStatus = isGroup || isStatus;
|
|
442
418
|
const finalJid = jid;
|
|
443
419
|
msgId = msgId || generateMessageIDV2(meId);
|
|
444
|
-
|
|
445
|
-
|
|
420
|
+
// Anti waiting-message fix: default to fresh device/group lookup.
|
|
421
|
+
// Stale device cache/sender-key metadata can make recipient clients show
|
|
422
|
+
// "menunggu pesan" until a retry happens. Existing callers can still
|
|
423
|
+
// opt in to cache with useUserDevicesCache: true / useCachedGroupMetadata: true.
|
|
424
|
+
useUserDevicesCache = useUserDevicesCache === true;
|
|
425
|
+
useCachedGroupMetadata = useCachedGroupMetadata === true && !isStatus;
|
|
446
426
|
const participants = [];
|
|
427
|
+
let additionalAlready = false
|
|
447
428
|
const destinationJid = !isStatus ? finalJid : statusJid;
|
|
448
429
|
const binaryNodeContent = [];
|
|
449
430
|
const devices = [];
|
|
450
|
-
|
|
431
|
+
const messages = Utils_1.normalizeMessageContent(message)
|
|
432
|
+
const buttonType = getButtonType(messages)
|
|
451
433
|
const meMsg = {
|
|
452
434
|
deviceSentMessage: {
|
|
453
435
|
destinationJid,
|
|
@@ -542,7 +524,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
542
524
|
throw new Boom('Per-jid patching is not supported in groups');
|
|
543
525
|
}
|
|
544
526
|
const bytes = encodeWAMessage(patched);
|
|
545
|
-
reportingMessage = patched;
|
|
546
527
|
const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
|
|
547
528
|
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
|
|
548
529
|
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
@@ -597,12 +578,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
597
578
|
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
|
|
598
579
|
}
|
|
599
580
|
const { user: ownUser } = jidDecode(ownId);
|
|
600
|
-
if (!participant) {
|
|
601
|
-
const patchedForReporting = await patchMessageBeforeSending(message, [jid]);
|
|
602
|
-
reportingMessage = Array.isArray(patchedForReporting)
|
|
603
|
-
? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
|
|
604
|
-
: patchedForReporting;
|
|
605
|
-
}
|
|
606
581
|
if (!isRetryResend) {
|
|
607
582
|
const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
608
583
|
devices.push({
|
|
@@ -627,7 +602,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
627
602
|
? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
|
|
628
603
|
: jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
|
|
629
604
|
// Enumerate devices for sender and target with consistent addressing
|
|
630
|
-
const sessionDevices = await getUSyncDevices([senderIdentity, jid],
|
|
605
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], !!useUserDevicesCache, false);
|
|
631
606
|
devices.push(...sessionDevices);
|
|
632
607
|
logger.debug({
|
|
633
608
|
deviceCount: devices.length,
|
|
@@ -738,6 +713,16 @@ export const makeMessagesSocket = (config) => {
|
|
|
738
713
|
else {
|
|
739
714
|
stanza.attrs.to = destinationJid;
|
|
740
715
|
}
|
|
716
|
+
if (!isNewsletter && buttonType) {
|
|
717
|
+
const buttonsNode = getButtonArgs(messages)
|
|
718
|
+
const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
|
|
719
|
+
if (filteredButtons) {
|
|
720
|
+
stanza.content.push(...additionalNodes)
|
|
721
|
+
additionalAlready = true
|
|
722
|
+
} else {
|
|
723
|
+
stanza.content.push(buttonsNode)
|
|
724
|
+
}
|
|
725
|
+
}
|
|
741
726
|
if (shouldIncludeDeviceIdentity) {
|
|
742
727
|
;
|
|
743
728
|
stanza.content.push({
|
|
@@ -747,29 +732,6 @@ export const makeMessagesSocket = (config) => {
|
|
|
747
732
|
});
|
|
748
733
|
logger.debug({ jid }, 'adding device identity');
|
|
749
734
|
}
|
|
750
|
-
if (!isNewsletter &&
|
|
751
|
-
!isRetryResend &&
|
|
752
|
-
reportingMessage?.messageContextInfo?.messageSecret &&
|
|
753
|
-
shouldIncludeReportingToken(reportingMessage)) {
|
|
754
|
-
try {
|
|
755
|
-
const encoded = encodeWAMessage(reportingMessage);
|
|
756
|
-
const reportingKey = {
|
|
757
|
-
id: msgId,
|
|
758
|
-
fromMe: true,
|
|
759
|
-
remoteJid: destinationJid,
|
|
760
|
-
participant: participant?.jid
|
|
761
|
-
};
|
|
762
|
-
const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey);
|
|
763
|
-
if (reportingNode) {
|
|
764
|
-
;
|
|
765
|
-
stanza.content.push(reportingNode);
|
|
766
|
-
logger.trace({ jid }, 'added reporting token to message');
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
catch (error) {
|
|
770
|
-
logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
735
|
const contactTcTokenData = !isGroup && !isRetryResend && !isStatus ? await authState.keys.get('tctoken', [destinationJid]) : {};
|
|
774
736
|
const tcTokenBuffer = contactTcTokenData[destinationJid]?.token;
|
|
775
737
|
if (tcTokenBuffer) {
|
|
@@ -780,7 +742,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
780
742
|
content: tcTokenBuffer
|
|
781
743
|
});
|
|
782
744
|
}
|
|
783
|
-
if (additionalNodes && additionalNodes.length > 0) {
|
|
745
|
+
if (!additionalAlready && additionalNodes && additionalNodes.length > 0) {
|
|
784
746
|
;
|
|
785
747
|
stanza.content.push(...additionalNodes);
|
|
786
748
|
}
|
|
@@ -794,9 +756,26 @@ export const makeMessagesSocket = (config) => {
|
|
|
794
756
|
return msgId;
|
|
795
757
|
};
|
|
796
758
|
const getMessageType = (message) => {
|
|
797
|
-
|
|
759
|
+
// Keep group status wrappers as text stanza like 1.0.4.
|
|
760
|
+
// If this wrapper is normalized first, inner image/video content can be
|
|
761
|
+
// detected as media and status-group relay may fail on WA clients.
|
|
762
|
+
if (message?.groupStatusMessageV2 || message?.groupStatusMessage) {
|
|
763
|
+
return 'text';
|
|
764
|
+
}
|
|
765
|
+
// stickerPackMessage/media must be sent as media, especially in groups.
|
|
766
|
+
const normalizedMessage = Utils_1.normalizeMessageContent(message) || message;
|
|
767
|
+
if (normalizedMessage?.reactionMessage || normalizedMessage?.encReactionMessage) {
|
|
768
|
+
return 'reaction';
|
|
769
|
+
}
|
|
770
|
+
if (normalizedMessage?.pollCreationMessage || normalizedMessage?.pollCreationMessageV2 || normalizedMessage?.pollCreationMessageV3 || normalizedMessage?.pollUpdateMessage) {
|
|
798
771
|
return 'poll';
|
|
799
772
|
}
|
|
773
|
+
if (normalizedMessage?.eventMessage) {
|
|
774
|
+
return 'event';
|
|
775
|
+
}
|
|
776
|
+
if (getMediaType(normalizedMessage) !== '') {
|
|
777
|
+
return 'media';
|
|
778
|
+
}
|
|
800
779
|
return 'text';
|
|
801
780
|
};
|
|
802
781
|
const getMediaType = (message) => {
|
|
@@ -824,6 +803,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
824
803
|
else if (message.stickerMessage) {
|
|
825
804
|
return 'sticker';
|
|
826
805
|
}
|
|
806
|
+
else if (message.stickerPackMessage) {
|
|
807
|
+
return 'sticker_pack';
|
|
808
|
+
}
|
|
827
809
|
else if (message.listMessage) {
|
|
828
810
|
return 'list';
|
|
829
811
|
}
|
|
@@ -847,6 +829,114 @@ export const makeMessagesSocket = (config) => {
|
|
|
847
829
|
}
|
|
848
830
|
return '';
|
|
849
831
|
};
|
|
832
|
+
|
|
833
|
+
const getButtonType = (message) => {
|
|
834
|
+
if (message.listMessage) {
|
|
835
|
+
return 'list'
|
|
836
|
+
}
|
|
837
|
+
else if (message.buttonsMessage) {
|
|
838
|
+
return 'buttons'
|
|
839
|
+
}
|
|
840
|
+
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_and_pay') {
|
|
841
|
+
return 'review_and_pay'
|
|
842
|
+
}
|
|
843
|
+
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_order') {
|
|
844
|
+
return 'review_order'
|
|
845
|
+
}
|
|
846
|
+
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
|
|
847
|
+
return 'payment_info'
|
|
848
|
+
}
|
|
849
|
+
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
|
|
850
|
+
return 'payment_status'
|
|
851
|
+
}
|
|
852
|
+
else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
|
|
853
|
+
return 'payment_method'
|
|
854
|
+
}
|
|
855
|
+
else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
|
|
856
|
+
return 'interactive'
|
|
857
|
+
}
|
|
858
|
+
else if (message.interactiveMessage?.nativeFlowMessage) {
|
|
859
|
+
return 'native_flow'
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const getButtonArgs = (message) => {
|
|
864
|
+
const message_content = message.viewOnceMessage?.message || message;
|
|
865
|
+
const message_flow = message_content.interactiveMessage?.nativeFlowMessage;
|
|
866
|
+
const flow_buttons_first = message_flow?.buttons?.[0]?.name;
|
|
867
|
+
const flow_buttons_special = [ 'mpm', 'cta_catalog', 'send_location', 'call_permission_request', 'wa_payment_transaction_details', 'automated_greeting_message_view_catalog' ];
|
|
868
|
+
const baseArgs = {
|
|
869
|
+
tag: 'biz',
|
|
870
|
+
attrs: {
|
|
871
|
+
actual_actors: '2',
|
|
872
|
+
host_storage: '2',
|
|
873
|
+
privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
if (message_flow && (flow_buttons_first === 'review_and_pay' || flow_buttons_first === 'payment_info')) {
|
|
877
|
+
return {
|
|
878
|
+
tag: 'biz',
|
|
879
|
+
attrs: {
|
|
880
|
+
native_flow_name: flow_buttons_first === 'review_and_pay' ? 'order_details' : flow_buttons_first
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
if (message_flow && flow_buttons_special.includes(flow_buttons_first)) {
|
|
885
|
+
return {
|
|
886
|
+
...baseArgs,
|
|
887
|
+
content: [
|
|
888
|
+
{
|
|
889
|
+
tag: 'interactive',
|
|
890
|
+
attrs: { type: 'native_flow', v: '1' },
|
|
891
|
+
content: [{
|
|
892
|
+
tag: 'native_flow',
|
|
893
|
+
attrs: { v: '2', name: flow_buttons_first }
|
|
894
|
+
}]
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
tag: 'quality_control',
|
|
898
|
+
attrs: { source_type: 'third_party' }
|
|
899
|
+
}
|
|
900
|
+
]
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
if (message_flow || message_content.buttonsMessage) {
|
|
904
|
+
return {
|
|
905
|
+
...baseArgs,
|
|
906
|
+
content: [
|
|
907
|
+
{
|
|
908
|
+
tag: 'interactive',
|
|
909
|
+
attrs: { type: 'native_flow', v: '1' },
|
|
910
|
+
content: [{
|
|
911
|
+
tag: 'native_flow',
|
|
912
|
+
attrs: { v: '9', name: 'mixed' }
|
|
913
|
+
}]
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
tag: 'quality_control',
|
|
917
|
+
attrs: { source_type: 'third_party' }
|
|
918
|
+
}
|
|
919
|
+
]
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
if (message_content.listMessage) {
|
|
923
|
+
return {
|
|
924
|
+
...baseArgs,
|
|
925
|
+
content: [
|
|
926
|
+
{
|
|
927
|
+
tag: 'list',
|
|
928
|
+
attrs: { v: '2', type: 'product_list' }
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
tag: 'quality_control',
|
|
932
|
+
attrs: { source_type: 'third_party' }
|
|
933
|
+
}
|
|
934
|
+
]
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
return baseArgs;
|
|
938
|
+
};
|
|
939
|
+
|
|
850
940
|
const getPrivacyTokens = async (jids) => {
|
|
851
941
|
const t = unixTimestampSeconds().toString();
|
|
852
942
|
const result = await query({
|
|
@@ -873,7 +963,22 @@ export const makeMessagesSocket = (config) => {
|
|
|
873
963
|
});
|
|
874
964
|
return result;
|
|
875
965
|
};
|
|
966
|
+
|
|
967
|
+
const getEphemeralGroup = (jid) => {
|
|
968
|
+
if (!WABinary_1.isJidGroup(jid)) throw new TypeError("Jid should originate from a group!")
|
|
969
|
+
|
|
970
|
+
return groupQuery(jid, 'get', [{
|
|
971
|
+
tag: 'query',
|
|
972
|
+
attrs: {
|
|
973
|
+
request: 'interactive'
|
|
974
|
+
}
|
|
975
|
+
}])
|
|
976
|
+
.then((groups) => WABinary_1.getBinaryNodeChild(groups, 'group'))
|
|
977
|
+
.then((metadata) => WABinary_1.getBinaryNodeChild(metadata, 'ephemeral')?.attrs?.expiration || 0)
|
|
978
|
+
}
|
|
979
|
+
|
|
876
980
|
const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
|
|
981
|
+
const kelvdra = new hydra(Utils_1, waUploadToServer, relayMessage)
|
|
877
982
|
const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
|
|
878
983
|
return {
|
|
879
984
|
...sock,
|
|
@@ -885,17 +990,17 @@ export const makeMessagesSocket = (config) => {
|
|
|
885
990
|
readMessages,
|
|
886
991
|
refreshMediaConn,
|
|
887
992
|
waUploadToServer,
|
|
993
|
+
kelvdra,
|
|
888
994
|
fetchPrivacySettings,
|
|
889
995
|
sendPeerDataOperationMessage,
|
|
890
996
|
createParticipantNodes,
|
|
891
997
|
getUSyncDevices,
|
|
892
998
|
messageRetryManager,
|
|
893
|
-
updateMemberLabel,
|
|
894
999
|
updateMediaMessage: async (message) => {
|
|
895
1000
|
const content = assertMediaContent(message.message);
|
|
896
1001
|
const mediaKey = content.mediaKey;
|
|
897
|
-
const meId = authState.creds
|
|
898
|
-
const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
|
|
1002
|
+
const meId = assertMeId(authState.creds);
|
|
1003
|
+
const node = await encryptMediaRetryRequest(message.key, mediaKey, meId);
|
|
899
1004
|
let error = undefined;
|
|
900
1005
|
await Promise.all([
|
|
901
1006
|
sendNode(node),
|
|
@@ -907,7 +1012,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
907
1012
|
}
|
|
908
1013
|
else {
|
|
909
1014
|
try {
|
|
910
|
-
const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
|
|
1015
|
+
const media = await decryptMediaRetryData(result.media, mediaKey, result.key.id);
|
|
911
1016
|
if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
|
|
912
1017
|
const resultStr = proto.MediaRetryNotification.ResultType[media.result];
|
|
913
1018
|
throw new Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
@@ -933,8 +1038,316 @@ export const makeMessagesSocket = (config) => {
|
|
|
933
1038
|
ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
|
|
934
1039
|
return message;
|
|
935
1040
|
},
|
|
1041
|
+
sendStatusMentions: async (content, jids = []) => {
|
|
1042
|
+
const userJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
|
|
1043
|
+
let allUsers = new Set()
|
|
1044
|
+
allUsers.add(userJid)
|
|
1045
|
+
|
|
1046
|
+
for (const id of jids) {
|
|
1047
|
+
const isGroup = WABinary_1.isJidGroup(id)
|
|
1048
|
+
const isPrivate = WABinary_1.isPnUser(id)
|
|
1049
|
+
|
|
1050
|
+
if (isGroup) {
|
|
1051
|
+
try {
|
|
1052
|
+
const metadata = await cachedGroupMetadata(id) || await global.groupMetadataCache(id)
|
|
1053
|
+
const participants = metadata.participants.map(p => WABinary_1.jidNormalizedUser(p.id))
|
|
1054
|
+
participants.forEach(jid => allUsers.add(jid))
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
logger.error(`Error getting metadata for group ${id}: ${error}`)
|
|
1057
|
+
}
|
|
1058
|
+
} else if (isPrivate) {
|
|
1059
|
+
allUsers.add(WABinary_1.jidNormalizedUser(id))
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const uniqueUsers = Array.from(allUsers)
|
|
1064
|
+
const getRandomHexColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")
|
|
1065
|
+
|
|
1066
|
+
const isMedia = content.image || content.video || content.audio
|
|
1067
|
+
const isAudio = !!content.audio
|
|
1068
|
+
|
|
1069
|
+
const messageContent = {
|
|
1070
|
+
...content
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
if (isMedia && !isAudio) {
|
|
1074
|
+
if (messageContent.text) {
|
|
1075
|
+
messageContent.caption = messageContent.text
|
|
1076
|
+
|
|
1077
|
+
delete messageContent.text
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
delete messageContent.ptt
|
|
1081
|
+
delete messageContent.font
|
|
1082
|
+
delete messageContent.backgroundColor
|
|
1083
|
+
delete messageContent.textColor
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
if (isAudio) {
|
|
1087
|
+
delete messageContent.text
|
|
1088
|
+
delete messageContent.caption
|
|
1089
|
+
delete messageContent.font
|
|
1090
|
+
delete messageContent.textColor
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const font = !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined
|
|
1094
|
+
const textColor = !isMedia ? (content.textColor || getRandomHexColor()) : undefined
|
|
1095
|
+
const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor || getRandomHexColor()) : undefined
|
|
1096
|
+
const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined
|
|
1097
|
+
|
|
1098
|
+
let msg
|
|
1099
|
+
let mediaHandle
|
|
1100
|
+
try {
|
|
1101
|
+
msg = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, messageContent, {
|
|
1102
|
+
logger,
|
|
1103
|
+
userJid,
|
|
1104
|
+
getUrlInfo: text => link_preview_1.getUrlInfo(text, {
|
|
1105
|
+
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1106
|
+
fetchOpts: {
|
|
1107
|
+
timeout: 3000,
|
|
1108
|
+
...axiosOptions || {}
|
|
1109
|
+
},
|
|
1110
|
+
logger,
|
|
1111
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1112
|
+
}),
|
|
1113
|
+
upload: async (encFilePath, opts) => {
|
|
1114
|
+
const up = await waUploadToServer(encFilePath, {
|
|
1115
|
+
...opts
|
|
1116
|
+
})
|
|
1117
|
+
mediaHandle = up.handle
|
|
1118
|
+
return up
|
|
1119
|
+
},
|
|
1120
|
+
mediaCache: config.mediaCache,
|
|
1121
|
+
options: config.options,
|
|
1122
|
+
font,
|
|
1123
|
+
textColor,
|
|
1124
|
+
backgroundColor,
|
|
1125
|
+
ptt
|
|
1126
|
+
})
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
logger.error(`Error generating message: ${error}`)
|
|
1129
|
+
throw error
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
await relayMessage(WABinary_1.STORIES_JID, msg.message, {
|
|
1133
|
+
messageId: msg.key.id,
|
|
1134
|
+
statusJidList: uniqueUsers,
|
|
1135
|
+
additionalNodes: [{
|
|
1136
|
+
tag: 'meta',
|
|
1137
|
+
attrs: {},
|
|
1138
|
+
content: [{
|
|
1139
|
+
tag: 'mentioned_users',
|
|
1140
|
+
attrs: {},
|
|
1141
|
+
content: jids.map(jid => ({
|
|
1142
|
+
tag: 'to',
|
|
1143
|
+
attrs: {
|
|
1144
|
+
jid: WABinary_1.jidNormalizedUser(jid)
|
|
1145
|
+
}
|
|
1146
|
+
}))
|
|
1147
|
+
}]
|
|
1148
|
+
}]
|
|
1149
|
+
})
|
|
1150
|
+
|
|
1151
|
+
for (const id of jids) {
|
|
1152
|
+
try {
|
|
1153
|
+
const normalizedId = WABinary_1.jidNormalizedUser(id)
|
|
1154
|
+
const isPrivate = WABinary_1.isJidUser(normalizedId)
|
|
1155
|
+
const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
|
|
1156
|
+
|
|
1157
|
+
const protocolMessage = {
|
|
1158
|
+
[type]: {
|
|
1159
|
+
message: {
|
|
1160
|
+
protocolMessage: {
|
|
1161
|
+
key: msg.key,
|
|
1162
|
+
type: 25
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
messageContextInfo: {
|
|
1167
|
+
messageSecret: randomBytes(32)
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
const statusMsg = await Utils_1.generateWAMessageFromContent(normalizedId,
|
|
1172
|
+
protocolMessage, {}
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
await relayMessage(
|
|
1176
|
+
normalizedId,
|
|
1177
|
+
statusMsg.message, {
|
|
1178
|
+
additionalNodes: [{
|
|
1179
|
+
tag: 'meta',
|
|
1180
|
+
attrs: isPrivate ? {
|
|
1181
|
+
is_status_mention: 'true'
|
|
1182
|
+
} : {
|
|
1183
|
+
is_group_status_mention: 'true'
|
|
1184
|
+
}
|
|
1185
|
+
}]
|
|
1186
|
+
}
|
|
1187
|
+
)
|
|
1188
|
+
|
|
1189
|
+
await Utils_1.delay(2000)
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
logger.error(`Error sending to ${id}: ${error}`)
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
return msg
|
|
1196
|
+
},
|
|
1197
|
+
sendAlbumMessage: async (jid, medias, options = {}) => {
|
|
1198
|
+
const userJid = authState.creds.me.id
|
|
1199
|
+
for (const media of medias) {
|
|
1200
|
+
if (!media.image && !media.video) throw new TypeError(`medias[i] must have image or video property`)
|
|
1201
|
+
}
|
|
1202
|
+
if (medias.length < 2) throw new RangeError("Minimum 2 media")
|
|
1203
|
+
const time = options.delay || 500
|
|
1204
|
+
delete options.delay
|
|
1205
|
+
const album = await Utils_1.generateWAMessageFromContent(jid, {
|
|
1206
|
+
albumMessage: {
|
|
1207
|
+
expectedImageCount: medias.filter(media => media.image).length,
|
|
1208
|
+
expectedVideoCount: medias.filter(media => media.video).length,
|
|
1209
|
+
...options
|
|
1210
|
+
}
|
|
1211
|
+
}, {
|
|
1212
|
+
userJid,
|
|
1213
|
+
...options
|
|
1214
|
+
})
|
|
1215
|
+
await relayMessage(jid, album.message, {
|
|
1216
|
+
messageId: album.key.id
|
|
1217
|
+
})
|
|
1218
|
+
let mediaHandle
|
|
1219
|
+
let msg
|
|
1220
|
+
for (const i in medias) {
|
|
1221
|
+
const media = medias[i]
|
|
1222
|
+
if (media.image) {
|
|
1223
|
+
msg = await Utils_1.generateWAMessage(jid, {
|
|
1224
|
+
image: media.image,
|
|
1225
|
+
...media,
|
|
1226
|
+
...options
|
|
1227
|
+
}, {
|
|
1228
|
+
userJid,
|
|
1229
|
+
upload: async (readStream, opts) => {
|
|
1230
|
+
const up = await waUploadToServer(readStream, {
|
|
1231
|
+
...opts,
|
|
1232
|
+
newsletter: WABinary_1.isJidNewsletter(jid)
|
|
1233
|
+
})
|
|
1234
|
+
mediaHandle = up.handle
|
|
1235
|
+
return up
|
|
1236
|
+
},
|
|
1237
|
+
...options
|
|
1238
|
+
})
|
|
1239
|
+
} else if (media.video) {
|
|
1240
|
+
msg = await Utils_1.generateWAMessage(jid, {
|
|
1241
|
+
video: media.video,
|
|
1242
|
+
...media,
|
|
1243
|
+
...options
|
|
1244
|
+
}, {
|
|
1245
|
+
userJid,
|
|
1246
|
+
upload: async (readStream, opts) => {
|
|
1247
|
+
const up = await waUploadToServer(readStream, {
|
|
1248
|
+
...opts,
|
|
1249
|
+
newsletter: WABinary_1.isJidNewsletter(jid)
|
|
1250
|
+
})
|
|
1251
|
+
mediaHandle = up.handle
|
|
1252
|
+
return up
|
|
1253
|
+
},
|
|
1254
|
+
...options,
|
|
1255
|
+
})
|
|
1256
|
+
}
|
|
1257
|
+
if (msg) {
|
|
1258
|
+
msg.message.messageContextInfo = {
|
|
1259
|
+
messageSecret: randomBytes(32),
|
|
1260
|
+
messageAssociation: {
|
|
1261
|
+
associationType: 1,
|
|
1262
|
+
parentMessageKey: album.key
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
await relayMessage(jid, msg.message, {
|
|
1267
|
+
messageId: msg.key.id
|
|
1268
|
+
})
|
|
1269
|
+
await Utils_1.delay(time)
|
|
1270
|
+
}
|
|
1271
|
+
return album
|
|
1272
|
+
},
|
|
1273
|
+
sendAIRich: async (jid, content = {}, options = {}) => {
|
|
1274
|
+
const { quoted, filter = false } = options;
|
|
1275
|
+
const getParticipantAttr = () => filter ? { participant: { jid } } : {};
|
|
1276
|
+
return await kelvdra.handleAIRich({ aiRich: content }, jid, quoted, {
|
|
1277
|
+
...getParticipantAttr()
|
|
1278
|
+
});
|
|
1279
|
+
},
|
|
936
1280
|
sendMessage: async (jid, content, options = {}) => {
|
|
937
1281
|
const userJid = authState.creds.me.id;
|
|
1282
|
+
const normalizeDeleteKey = async (key = {}) => {
|
|
1283
|
+
const normalized = { ...key };
|
|
1284
|
+
const resolveLid = async (value) => {
|
|
1285
|
+
if (!value || !isLidUser(value)) return value;
|
|
1286
|
+
try {
|
|
1287
|
+
return (await signalRepository?.lidMapping?.getPNForLID(value)) || value;
|
|
1288
|
+
} catch {
|
|
1289
|
+
return value;
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
normalized.remoteJid = await resolveLid(normalized.remoteJid || jid);
|
|
1293
|
+
normalized.participant = await resolveLid(normalized.participant);
|
|
1294
|
+
if (isJidGroup(normalized.remoteJid || jid) && !normalized.participant) {
|
|
1295
|
+
normalized.participant = normalized.fromMe ? userJid : undefined;
|
|
1296
|
+
}
|
|
1297
|
+
return normalized;
|
|
1298
|
+
};
|
|
1299
|
+
if (content?.delete) {
|
|
1300
|
+
content = { ...content, delete: await normalizeDeleteKey(content.delete) };
|
|
1301
|
+
}
|
|
1302
|
+
const additionalAttributes = {}
|
|
1303
|
+
const { filter = false, quoted } = options;
|
|
1304
|
+
const getParticipantAttr = () => filter ? { participant: { jid } } : {};
|
|
1305
|
+
const messageType = kelvdra.detectType(content);
|
|
1306
|
+
|
|
1307
|
+
if (messageType) {
|
|
1308
|
+
switch (messageType) {
|
|
1309
|
+
case 'PAYMENT':
|
|
1310
|
+
const paymentContent = await kelvdra.handlePayment(content, quoted);
|
|
1311
|
+
return await relayMessage(jid, paymentContent, {
|
|
1312
|
+
messageId: Utils_1.generateMessageID(),
|
|
1313
|
+
...getParticipantAttr()
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
case 'PRODUCT':
|
|
1317
|
+
const productContent = await kelvdra.handleProduct(content, jid, quoted);
|
|
1318
|
+
const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
|
|
1319
|
+
return await relayMessage(jid, productMsg.message, {
|
|
1320
|
+
messageId: productMsg.key.id,
|
|
1321
|
+
...getParticipantAttr()
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
case 'INTERACTIVE':
|
|
1325
|
+
const interactiveContent = await kelvdra.handleInteractive(content, jid, quoted);
|
|
1326
|
+
const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
|
|
1327
|
+
return await relayMessage(jid, interactiveMsg.message, {
|
|
1328
|
+
messageId: interactiveMsg.key.id,
|
|
1329
|
+
...getParticipantAttr()
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
case 'ALBUM':
|
|
1333
|
+
const albumContent = await kelvdra.handleAlbum(content, jid, quoted);
|
|
1334
|
+
return albumContent;
|
|
1335
|
+
|
|
1336
|
+
case 'EVENT':
|
|
1337
|
+
return await kelvdra.handleEvent(content, jid, quoted);
|
|
1338
|
+
|
|
1339
|
+
case 'POLL_RESULT':
|
|
1340
|
+
return await kelvdra.handlePollResult(content, jid, quoted);
|
|
1341
|
+
|
|
1342
|
+
case 'CAROUSEL':
|
|
1343
|
+
return await kelvdra.handleCarousel(content, jid, quoted);
|
|
1344
|
+
|
|
1345
|
+
case 'AI_RICH':
|
|
1346
|
+
return await kelvdra.handleAIRich(content, jid, quoted, {
|
|
1347
|
+
...getParticipantAttr()
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
938
1351
|
if (typeof content === 'object' &&
|
|
939
1352
|
'disappearingMessagesInChat' in content &&
|
|
940
1353
|
typeof content['disappearingMessagesInChat'] !== 'undefined' &&
|
|
@@ -1017,7 +1430,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1017
1430
|
});
|
|
1018
1431
|
if (config.emitOwnEvents) {
|
|
1019
1432
|
process.nextTick(async () => {
|
|
1020
|
-
await
|
|
1433
|
+
await processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1021
1434
|
});
|
|
1022
1435
|
}
|
|
1023
1436
|
return fullMsg;
|