@alannxd/baileys 6.0.3 → 6.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/WAProto/GenerateStatics.sh +3 -0
- package/WAProto/WAProto.proto +5479 -0
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +14017 -0
- package/WAProto/index.js +201 -160
- package/engine-requirements.js +1 -1
- package/lib/Defaults/index.d.ts +37 -15
- package/lib/Defaults/index.js +119 -136
- package/lib/Signal/Group/ciphertext-message.d.ts +1 -0
- package/lib/Signal/Group/ciphertext-message.js +2 -5
- package/lib/Signal/Group/group-session-builder.d.ts +4 -3
- package/lib/Signal/Group/group-session-builder.js +7 -41
- package/lib/Signal/Group/group_cipher.d.ts +4 -4
- package/lib/Signal/Group/group_cipher.js +37 -51
- package/lib/Signal/Group/index.d.ts +12 -11
- package/lib/Signal/Group/index.js +12 -57
- package/lib/Signal/Group/keyhelper.d.ts +2 -1
- package/lib/Signal/Group/keyhelper.js +7 -44
- package/lib/Signal/Group/sender-chain-key.d.ts +3 -2
- package/lib/Signal/Group/sender-chain-key.js +7 -15
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +2 -1
- package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
- package/lib/Signal/Group/sender-key-message.d.ts +2 -1
- package/lib/Signal/Group/sender-key-message.js +9 -12
- package/lib/Signal/Group/sender-key-name.d.ts +1 -0
- package/lib/Signal/Group/sender-key-name.js +2 -5
- package/lib/Signal/Group/sender-key-record.d.ts +3 -2
- package/lib/Signal/Group/sender-key-record.js +9 -21
- package/lib/Signal/Group/sender-key-state.d.ts +7 -6
- package/lib/Signal/Group/sender-key-state.js +27 -42
- package/lib/Signal/Group/sender-message-key.d.ts +1 -0
- package/lib/Signal/Group/sender-message-key.js +4 -7
- package/lib/Signal/libsignal.d.ts +5 -3
- package/lib/Signal/libsignal.js +347 -90
- package/lib/Signal/lid-mapping.d.ts +23 -0
- package/lib/Signal/lid-mapping.js +277 -0
- package/lib/Socket/Client/index.d.ts +3 -3
- package/lib/Socket/Client/index.js +3 -19
- package/lib/Socket/Client/{abstract-socket-client.d.ts → types.d.ts} +4 -5
- package/lib/Socket/Client/types.js +11 -0
- package/lib/Socket/Client/{web-socket-client.d.ts → websocket.d.ts} +3 -2
- package/lib/Socket/Client/websocket.js +54 -0
- package/lib/Socket/business.d.ts +154 -108
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/chats.d.ts +96 -239
- package/lib/Socket/chats.js +627 -427
- package/lib/Socket/communities.d.ts +239 -146
- package/lib/Socket/communities.js +90 -80
- package/lib/Socket/groups.d.ts +104 -57
- package/lib/Socket/groups.js +154 -161
- package/lib/Socket/index.d.ts +202 -115
- package/lib/Socket/index.js +11 -10
- package/lib/Socket/luxu.d.ts +22 -266
- package/lib/Socket/luxu.js +422 -465
- package/lib/Socket/messages-recv.d.ts +136 -84
- package/lib/Socket/messages-recv.js +1421 -615
- package/lib/Socket/messages-send.d.ts +142 -126
- package/lib/Socket/messages-send.js +878 -671
- package/lib/Socket/mex.d.ts +3 -0
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/newsletter.d.ts +121 -85
- package/lib/Socket/newsletter.js +147 -272
- package/lib/Socket/socket.d.ts +34 -19
- package/lib/Socket/socket.js +544 -313
- package/lib/Store/index.d.ts +10 -3
- package/lib/Store/index.js +10 -10
- package/lib/Store/keyed-db.d.ts +22 -0
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.d.ts +17 -11
- package/lib/Store/make-cache-manager-store.js +43 -41
- package/lib/Store/make-in-memory-store.d.ts +39 -118
- package/lib/Store/make-in-memory-store.js +112 -341
- package/lib/Store/make-ordered-dictionary.d.ts +11 -10
- package/lib/Store/make-ordered-dictionary.js +14 -20
- package/lib/Store/object-repository.d.ts +10 -9
- package/lib/Store/object-repository.js +11 -6
- package/lib/Types/Auth.d.ts +19 -12
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.d.ts +3 -1
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.d.ts +35 -13
- package/lib/Types/Chat.js +8 -4
- package/lib/Types/Contact.d.ts +8 -1
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.d.ts +116 -17
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.d.ts +21 -5
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.d.ts +12 -0
- package/lib/Types/Label.js +3 -5
- package/lib/Types/LabelAssociation.d.ts +1 -0
- package/lib/Types/LabelAssociation.js +3 -5
- package/lib/Types/Message.d.ts +105 -58
- package/lib/Types/Message.js +11 -9
- package/lib/Types/Mex.d.ts +141 -0
- package/lib/Types/Mex.js +37 -0
- package/lib/Types/Product.d.ts +2 -1
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.d.ts +32 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.d.ts +50 -25
- package/lib/Types/Socket.js +3 -2
- package/lib/Types/State.d.ts +72 -2
- package/lib/Types/State.js +56 -2
- package/lib/Types/USync.d.ts +3 -2
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.d.ts +22 -14
- package/lib/Types/index.js +15 -31
- package/lib/Utils/auth-utils.d.ts +12 -6
- package/lib/Utils/auth-utils.js +239 -143
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/business.d.ts +3 -2
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/chat-utils.d.ts +52 -23
- package/lib/Utils/chat-utils.js +396 -253
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/crypto.d.ts +18 -22
- package/lib/Utils/crypto.js +57 -90
- package/lib/Utils/decode-wa-message.d.ts +55 -8
- package/lib/Utils/decode-wa-message.js +203 -84
- package/lib/Utils/event-buffer.d.ts +9 -8
- package/lib/Utils/event-buffer.js +185 -77
- package/lib/Utils/generics.d.ts +28 -29
- package/lib/Utils/generics.js +180 -210
- package/lib/Utils/history.d.ts +18 -9
- package/lib/Utils/history.js +93 -55
- package/lib/Utils/identity-change-handler.d.ts +44 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/index.d.ts +22 -17
- package/lib/Utils/index.js +22 -33
- package/lib/Utils/link-preview.d.ts +5 -5
- package/lib/Utils/link-preview.js +16 -24
- package/lib/Utils/logger.d.ts +11 -3
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/lt-hash.d.ts +8 -12
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/make-mutex.d.ts +4 -2
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/message-retry-manager.d.ts +115 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/messages-media.d.ts +61 -44
- package/lib/Utils/messages-media.js +451 -482
- package/lib/Utils/messages.d.ts +32 -18
- package/lib/Utils/messages.js +458 -369
- package/lib/Utils/noise-handler.d.ts +13 -14
- package/lib/Utils/noise-handler.js +145 -99
- package/lib/Utils/offline-node-processor.d.ts +17 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/process-message.d.ts +31 -12
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/signal.d.ts +20 -5
- package/lib/Utils/signal.js +120 -72
- 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/Utils/use-multi-file-auth-state.d.ts +2 -2
- package/lib/Utils/use-multi-file-auth-state.js +29 -27
- package/lib/Utils/validate-connection.d.ts +7 -7
- package/lib/Utils/validate-connection.js +73 -99
- package/lib/WABinary/constants.d.ts +25 -27
- package/lib/WABinary/constants.js +1281 -20
- package/lib/WABinary/decode.d.ts +5 -5
- package/lib/WABinary/decode.js +52 -42
- package/lib/WABinary/encode.d.ts +3 -3
- package/lib/WABinary/encode.js +110 -155
- package/lib/WABinary/generic-utils.d.ts +8 -7
- package/lib/WABinary/generic-utils.js +48 -49
- package/lib/WABinary/index.d.ts +6 -5
- package/lib/WABinary/index.js +6 -21
- package/lib/WABinary/jid-utils.d.ts +25 -8
- package/lib/WABinary/jid-utils.js +74 -40
- package/lib/WABinary/types.d.ts +2 -1
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.d.ts +3 -11
- package/lib/WAM/BinaryInfo.js +2 -5
- package/lib/WAM/constants.d.ts +5 -3
- package/lib/WAM/constants.js +19071 -11568
- package/lib/WAM/encode.d.ts +3 -3
- package/lib/WAM/encode.js +17 -22
- package/lib/WAM/index.d.ts +4 -3
- package/lib/WAM/index.js +4 -19
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +4 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +3 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +3 -2
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +3 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +4 -3
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +5 -3
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
- package/lib/WAUSync/Protocols/index.d.ts +6 -4
- package/lib/WAUSync/Protocols/index.js +6 -20
- package/lib/WAUSync/USyncQuery.d.ts +6 -4
- package/lib/WAUSync/USyncQuery.js +44 -35
- package/lib/WAUSync/USyncUser.d.ts +10 -5
- package/lib/WAUSync/USyncUser.js +10 -5
- package/lib/WAUSync/index.d.ts +4 -0
- package/lib/WAUSync/index.js +4 -19
- package/lib/index.d.ts +10 -9
- package/lib/index.js +12 -34
- package/package.json +84 -53
- package/WAProto/fix-import.js +0 -29
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Socket/Client/abstract-socket-client.js +0 -13
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/Client/web-socket-client.js +0 -62
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Socket/usync.js +0 -70
- package/lib/Types/Newsletter.d.ts +0 -103
- package/lib/Types/Newsletter.js +0 -38
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/baileys-event-stream.js +0 -63
|
@@ -1,75 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
fetchPrivacySettings,
|
|
38
|
-
generateMessageTag,
|
|
39
|
-
sendNode,
|
|
40
|
-
groupMetadata,
|
|
41
|
-
groupToggleEphemeral,
|
|
42
|
-
executeUSyncQuery
|
|
43
|
-
} = sock;
|
|
44
|
-
const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
45
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
46
|
-
useClones: false
|
|
47
|
-
});
|
|
1
|
+
import NodeCache from '@cacheable/node-cache';
|
|
2
|
+
import { Boom } from '@hapi/boom';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
5
|
+
import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
|
|
6
|
+
import { getUrlInfo } from '../Utils/link-preview.js';
|
|
7
|
+
import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
|
|
8
|
+
import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
|
|
9
|
+
import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, resolveTcTokenJid, shouldSendNewTcToken, storeTcTokensFromIqResult } from '../Utils/tc-token-utils.js';
|
|
10
|
+
import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET, getAdditionalNode, getBinaryNodeFilter } from '../WABinary/index.js';
|
|
11
|
+
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
12
|
+
import { makeNewsletterSocket } from './newsletter.js';
|
|
13
|
+
import imup from './luxu.js';
|
|
14
|
+
import * as Utils_1 from '../Utils/index.js';
|
|
15
|
+
export const makeMessagesSocket = (config) => {
|
|
16
|
+
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount, aiLabel } = config;
|
|
17
|
+
const sock = makeNewsletterSocket(config);
|
|
18
|
+
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
|
|
19
|
+
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
20
|
+
/**
|
|
21
|
+
* Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
|
|
22
|
+
* Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
|
|
23
|
+
* Entries are always removed in `.finally()`, so the set is bounded by concurrency.
|
|
24
|
+
*/
|
|
25
|
+
const inFlightTcTokenIssuance = new Set();
|
|
26
|
+
const userDevicesCache = config.userDevicesCache ||
|
|
27
|
+
new NodeCache({
|
|
28
|
+
stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
29
|
+
useClones: false
|
|
30
|
+
});
|
|
31
|
+
/** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
|
|
32
|
+
const devicesMutex = makeMutex();
|
|
33
|
+
// Initialize message retry manager if enabled
|
|
34
|
+
const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
|
|
35
|
+
// Prevent race conditions in Signal session encryption by user
|
|
36
|
+
const encryptionMutex = makeKeyedMutex();
|
|
48
37
|
let mediaConn;
|
|
38
|
+
/** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
|
|
39
|
+
let mediaHost = DEF_MEDIA_HOST;
|
|
49
40
|
const refreshMediaConn = async (forceGet = false) => {
|
|
50
41
|
const media = await mediaConn;
|
|
51
|
-
if (!media || forceGet ||
|
|
42
|
+
if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
|
|
52
43
|
mediaConn = (async () => {
|
|
53
44
|
const result = await query({
|
|
54
45
|
tag: 'iq',
|
|
55
46
|
attrs: {
|
|
56
47
|
type: 'set',
|
|
57
48
|
xmlns: 'w:m',
|
|
58
|
-
to:
|
|
49
|
+
to: S_WHATSAPP_NET
|
|
59
50
|
},
|
|
60
51
|
content: [{ tag: 'media_conn', attrs: {} }]
|
|
61
52
|
});
|
|
62
|
-
const mediaConnNode =
|
|
53
|
+
const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
|
|
54
|
+
// TODO: explore full length of data that whatsapp provides
|
|
63
55
|
const node = {
|
|
64
|
-
hosts:
|
|
56
|
+
hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
|
|
65
57
|
hostname: attrs.hostname,
|
|
66
|
-
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
58
|
+
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
67
59
|
})),
|
|
68
60
|
auth: mediaConnNode.attrs.auth,
|
|
69
61
|
ttl: +mediaConnNode.attrs.ttl,
|
|
70
62
|
fetchDate: new Date()
|
|
71
63
|
};
|
|
72
64
|
logger.debug('fetched media conn');
|
|
65
|
+
if (node.hosts[0]) {
|
|
66
|
+
mediaHost = node.hosts[0].hostname;
|
|
67
|
+
}
|
|
73
68
|
return node;
|
|
74
69
|
})();
|
|
75
70
|
}
|
|
@@ -80,17 +75,20 @@ const makeMessagesSocket = (config) => {
|
|
|
80
75
|
* used for receipts of phone call, read, delivery etc.
|
|
81
76
|
* */
|
|
82
77
|
const sendReceipt = async (jid, participant, messageIds, type) => {
|
|
78
|
+
if (!messageIds || messageIds.length === 0) {
|
|
79
|
+
throw new Boom('missing ids in receipt');
|
|
80
|
+
}
|
|
83
81
|
const node = {
|
|
84
82
|
tag: 'receipt',
|
|
85
83
|
attrs: {
|
|
86
|
-
id: messageIds[0]
|
|
87
|
-
}
|
|
84
|
+
id: messageIds[0]
|
|
85
|
+
}
|
|
88
86
|
};
|
|
89
87
|
const isReadReceipt = type === 'read' || type === 'read-self';
|
|
90
88
|
if (isReadReceipt) {
|
|
91
|
-
node.attrs.t =
|
|
89
|
+
node.attrs.t = unixTimestampSeconds().toString();
|
|
92
90
|
}
|
|
93
|
-
if (type === 'sender' &&
|
|
91
|
+
if (type === 'sender' && (isPnUser(jid) || isLidUser(jid))) {
|
|
94
92
|
node.attrs.recipient = jid;
|
|
95
93
|
node.attrs.to = participant;
|
|
96
94
|
}
|
|
@@ -101,7 +99,7 @@ const makeMessagesSocket = (config) => {
|
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
if (type) {
|
|
104
|
-
node.attrs.type =
|
|
102
|
+
node.attrs.type = type;
|
|
105
103
|
}
|
|
106
104
|
const remainingMessageIds = messageIds.slice(1);
|
|
107
105
|
if (remainingMessageIds.length) {
|
|
@@ -121,7 +119,7 @@ const makeMessagesSocket = (config) => {
|
|
|
121
119
|
};
|
|
122
120
|
/** Correctly bulk send receipts to multiple chats, participants */
|
|
123
121
|
const sendReceipts = async (keys, type) => {
|
|
124
|
-
const recps =
|
|
122
|
+
const recps = aggregateMessageKeysNotFromMe(keys);
|
|
125
123
|
for (const { jid, participant, messageIds } of recps) {
|
|
126
124
|
await sendReceipt(jid, participant, messageIds, type);
|
|
127
125
|
}
|
|
@@ -135,290 +133,450 @@ const makeMessagesSocket = (config) => {
|
|
|
135
133
|
};
|
|
136
134
|
/** Fetch all the devices we've to send a message to */
|
|
137
135
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
138
|
-
const deviceResults = []
|
|
139
|
-
|
|
136
|
+
const deviceResults = [];
|
|
140
137
|
if (!useCache) {
|
|
141
|
-
logger.debug('not using cache for devices')
|
|
138
|
+
logger.debug('not using cache for devices');
|
|
142
139
|
}
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
140
|
+
const toFetch = [];
|
|
141
|
+
const jidsWithUser = jids
|
|
142
|
+
.map(jid => {
|
|
143
|
+
const decoded = jidDecode(jid);
|
|
144
|
+
const user = decoded?.user;
|
|
145
|
+
const device = decoded?.device;
|
|
146
|
+
const isExplicitDevice = typeof device === 'number' && device >= 0;
|
|
147
|
+
if (isExplicitDevice && user) {
|
|
148
|
+
deviceResults.push({
|
|
149
|
+
user,
|
|
150
|
+
device,
|
|
151
|
+
jid
|
|
152
|
+
});
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
jid = jidNormalizedUser(jid);
|
|
156
|
+
return { jid, user };
|
|
157
|
+
})
|
|
158
|
+
.filter(jid => jid !== null);
|
|
159
|
+
let mgetDevices;
|
|
160
|
+
if (useCache && userDevicesCache.mget) {
|
|
161
|
+
const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean);
|
|
162
|
+
mgetDevices = await userDevicesCache.mget(usersToFetch);
|
|
163
|
+
}
|
|
164
|
+
for (const { jid, user } of jidsWithUser) {
|
|
153
165
|
if (useCache) {
|
|
154
|
-
const devices =
|
|
155
|
-
|
|
166
|
+
const devices = mgetDevices?.[user] ||
|
|
167
|
+
(userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)));
|
|
156
168
|
if (devices) {
|
|
157
|
-
|
|
158
|
-
|
|
169
|
+
const devicesWithJid = devices.map(d => ({
|
|
170
|
+
...d,
|
|
171
|
+
jid: jidEncode(d.user, d.server, d.device)
|
|
172
|
+
}));
|
|
173
|
+
deviceResults.push(...devicesWithJid);
|
|
174
|
+
logger.trace({ user }, 'using cache for devices');
|
|
159
175
|
}
|
|
160
|
-
|
|
161
176
|
else {
|
|
162
|
-
toFetch.push(jid)
|
|
177
|
+
toFetch.push(jid);
|
|
163
178
|
}
|
|
164
179
|
}
|
|
165
|
-
|
|
166
180
|
else {
|
|
167
|
-
toFetch.push(jid)
|
|
181
|
+
toFetch.push(jid);
|
|
168
182
|
}
|
|
169
183
|
}
|
|
170
|
-
|
|
171
184
|
if (!toFetch.length) {
|
|
172
|
-
return deviceResults
|
|
185
|
+
return deviceResults;
|
|
173
186
|
}
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
const requestedLidUsers = new Set();
|
|
188
|
+
for (const jid of toFetch) {
|
|
189
|
+
if (isLidUser(jid) || isHostedLidUser(jid)) {
|
|
190
|
+
const user = jidDecode(jid)?.user;
|
|
191
|
+
if (user)
|
|
192
|
+
requestedLidUsers.add(user);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
|
|
179
196
|
for (const jid of toFetch) {
|
|
180
|
-
query.withUser(new
|
|
197
|
+
query.withUser(new USyncUser().withId(jid)); // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
|
|
181
198
|
}
|
|
182
|
-
|
|
183
|
-
const result = await executeUSyncQuery(query)
|
|
184
|
-
|
|
199
|
+
const result = await sock.executeUSyncQuery(query);
|
|
185
200
|
if (result) {
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
201
|
+
// TODO: LID MAP this stuff (lid protocol will now return lid with devices)
|
|
202
|
+
const lidResults = result.list.filter(a => !!a.lid);
|
|
203
|
+
if (lidResults.length > 0) {
|
|
204
|
+
logger.trace('Storing LID maps from device call');
|
|
205
|
+
await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
|
|
206
|
+
// Force-refresh sessions for newly mapped LIDs to align identity addressing
|
|
207
|
+
try {
|
|
208
|
+
const lids = lidResults.map(a => a.lid);
|
|
209
|
+
if (lids.length) {
|
|
210
|
+
await assertSessions(lids, true);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (e) {
|
|
214
|
+
logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const extracted = extractDeviceJids(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices);
|
|
218
|
+
const deviceMap = {};
|
|
189
219
|
for (const item of extracted) {
|
|
190
|
-
deviceMap[item.user] = deviceMap[item.user] || []
|
|
191
|
-
deviceMap[item.user]
|
|
192
|
-
deviceResults.push(item)
|
|
220
|
+
deviceMap[item.user] = deviceMap[item.user] || [];
|
|
221
|
+
deviceMap[item.user]?.push(item);
|
|
193
222
|
}
|
|
194
|
-
|
|
195
|
-
for (const
|
|
196
|
-
|
|
223
|
+
// Process each user's devices as a group for bulk LID migration
|
|
224
|
+
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
225
|
+
const isLidUser = requestedLidUsers.has(user);
|
|
226
|
+
// Process all devices for this user
|
|
227
|
+
for (const item of userDevices) {
|
|
228
|
+
const finalJid = isLidUser
|
|
229
|
+
? jidEncode(user, item.server, item.device)
|
|
230
|
+
: jidEncode(item.user, item.server, item.device);
|
|
231
|
+
deviceResults.push({
|
|
232
|
+
...item,
|
|
233
|
+
jid: finalJid
|
|
234
|
+
});
|
|
235
|
+
logger.debug({
|
|
236
|
+
user: item.user,
|
|
237
|
+
device: item.device,
|
|
238
|
+
finalJid,
|
|
239
|
+
usedLid: isLidUser
|
|
240
|
+
}, 'Processed device with LID priority');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
await devicesMutex.mutex(async () => {
|
|
244
|
+
if (userDevicesCache.mset) {
|
|
245
|
+
// if the cache supports mset, we can set all devices in one go
|
|
246
|
+
await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
for (const key in deviceMap) {
|
|
250
|
+
if (deviceMap[key])
|
|
251
|
+
await userDevicesCache.set(key, deviceMap[key]);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
const userDeviceUpdates = {};
|
|
256
|
+
for (const [userId, devices] of Object.entries(deviceMap)) {
|
|
257
|
+
if (devices && devices.length > 0) {
|
|
258
|
+
userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (Object.keys(userDeviceUpdates).length > 0) {
|
|
262
|
+
try {
|
|
263
|
+
await authState.keys.set({ 'device-list': userDeviceUpdates });
|
|
264
|
+
logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
logger.warn({ error }, 'failed to store user device lists');
|
|
268
|
+
}
|
|
197
269
|
}
|
|
198
270
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
271
|
+
return deviceResults;
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* Update Member Label
|
|
275
|
+
*/
|
|
276
|
+
const updateMemberLabel = (jid, memberLabel) => {
|
|
277
|
+
return relayMessage(jid, {
|
|
278
|
+
protocolMessage: {
|
|
279
|
+
type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
|
|
280
|
+
memberLabel: {
|
|
281
|
+
label: memberLabel?.slice(0, 30),
|
|
282
|
+
labelTimestamp: unixTimestampSeconds()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}, {
|
|
286
|
+
additionalNodes: [
|
|
287
|
+
{
|
|
288
|
+
tag: 'meta',
|
|
289
|
+
attrs: {
|
|
290
|
+
tag_reason: 'user_update',
|
|
291
|
+
appdata: 'member_tag'
|
|
292
|
+
},
|
|
293
|
+
content: undefined
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
});
|
|
297
|
+
};
|
|
202
298
|
const assertSessions = async (jids, force) => {
|
|
203
299
|
let didFetchNewSession = false;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
for (const jid of jids) {
|
|
213
|
-
const signalId = signalRepository
|
|
214
|
-
.jidToSignalProtocolAddress(jid);
|
|
215
|
-
if (!sessions[signalId]) {
|
|
216
|
-
jidsRequiringFetch.push(jid);
|
|
300
|
+
const uniqueJids = [...new Set(jids)];
|
|
301
|
+
const jidsRequiringFetch = [];
|
|
302
|
+
logger.debug({ jids }, 'assertSessions call with jids');
|
|
303
|
+
for (const jid of uniqueJids) {
|
|
304
|
+
if (!force) {
|
|
305
|
+
const sessionValidation = await signalRepository.validateSession(jid);
|
|
306
|
+
if (sessionValidation.exists) {
|
|
307
|
+
continue;
|
|
217
308
|
}
|
|
218
309
|
}
|
|
310
|
+
jidsRequiringFetch.push(jid);
|
|
219
311
|
}
|
|
220
312
|
if (jidsRequiringFetch.length) {
|
|
221
|
-
|
|
313
|
+
// LID if mapped, otherwise original
|
|
314
|
+
const wireJids = [
|
|
315
|
+
...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
|
|
316
|
+
...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
|
|
317
|
+
];
|
|
318
|
+
logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
|
|
222
319
|
const result = await query({
|
|
223
320
|
tag: 'iq',
|
|
224
321
|
attrs: {
|
|
225
322
|
xmlns: 'encrypt',
|
|
226
323
|
type: 'get',
|
|
227
|
-
to:
|
|
324
|
+
to: S_WHATSAPP_NET
|
|
228
325
|
},
|
|
229
326
|
content: [
|
|
230
327
|
{
|
|
231
328
|
tag: 'key',
|
|
232
329
|
attrs: {},
|
|
233
|
-
content:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
330
|
+
content: wireJids.map(jid => {
|
|
331
|
+
const attrs = { jid };
|
|
332
|
+
if (force)
|
|
333
|
+
attrs.reason = 'identity';
|
|
334
|
+
return { tag: 'user', attrs };
|
|
335
|
+
})
|
|
237
336
|
}
|
|
238
337
|
]
|
|
239
338
|
});
|
|
240
|
-
await
|
|
339
|
+
await parseAndInjectE2ESessions(result, signalRepository);
|
|
241
340
|
didFetchNewSession = true;
|
|
242
341
|
}
|
|
243
342
|
return didFetchNewSession;
|
|
244
343
|
};
|
|
245
|
-
|
|
246
|
-
|
|
247
344
|
const sendPeerDataOperationMessage = async (pdoMessage) => {
|
|
345
|
+
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
248
346
|
if (!authState.creds.me?.id) {
|
|
249
|
-
throw new
|
|
347
|
+
throw new Boom('Not authenticated');
|
|
250
348
|
}
|
|
251
|
-
|
|
252
349
|
const protocolMessage = {
|
|
253
350
|
protocolMessage: {
|
|
254
351
|
peerDataOperationRequestMessage: pdoMessage,
|
|
255
|
-
type:
|
|
352
|
+
type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
256
353
|
}
|
|
257
354
|
};
|
|
258
|
-
const meJid =
|
|
355
|
+
const meJid = jidNormalizedUser(authState.creds.me.id);
|
|
259
356
|
const msgId = await relayMessage(meJid, protocolMessage, {
|
|
260
357
|
additionalAttributes: {
|
|
261
358
|
category: 'peer',
|
|
262
|
-
|
|
263
|
-
push_priority: 'high_force',
|
|
359
|
+
push_priority: 'high_force'
|
|
264
360
|
},
|
|
361
|
+
additionalNodes: [
|
|
362
|
+
{
|
|
363
|
+
tag: 'meta',
|
|
364
|
+
attrs: { appdata: 'default' }
|
|
365
|
+
}
|
|
366
|
+
]
|
|
265
367
|
});
|
|
266
368
|
return msgId;
|
|
267
369
|
};
|
|
268
|
-
const createParticipantNodes = async (
|
|
269
|
-
|
|
270
|
-
|
|
370
|
+
const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
|
|
371
|
+
if (!recipientJids.length) {
|
|
372
|
+
return { nodes: [], shouldIncludeDeviceIdentity: false };
|
|
373
|
+
}
|
|
374
|
+
const patched = await patchMessageBeforeSending(message, recipientJids);
|
|
375
|
+
const patchedMessages = Array.isArray(patched)
|
|
376
|
+
? patched
|
|
377
|
+
: recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
|
|
271
378
|
let shouldIncludeDeviceIdentity = false;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
379
|
+
const meId = authState.creds.me.id;
|
|
380
|
+
const meLid = authState.creds.me?.lid;
|
|
381
|
+
const meLidUser = meLid ? jidDecode(meLid)?.user : null;
|
|
382
|
+
const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
|
|
383
|
+
try {
|
|
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;
|
|
277
418
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
},
|
|
288
|
-
content: ciphertext
|
|
289
|
-
}]
|
|
290
|
-
};
|
|
291
|
-
return node;
|
|
292
|
-
}));
|
|
419
|
+
catch (err) {
|
|
420
|
+
logger.error({ jid, err }, 'Failed to encrypt for recipient');
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
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
|
+
}
|
|
293
428
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
294
429
|
};
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
let
|
|
300
|
-
const { user, server } = WABinary_1.jidDecode(jid);
|
|
430
|
+
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
|
|
431
|
+
const meId = assertMeId(authState.creds);
|
|
432
|
+
const meLid = authState.creds.me?.lid;
|
|
433
|
+
const isRetryResend = Boolean(participant?.jid);
|
|
434
|
+
let shouldIncludeDeviceIdentity = isRetryResend;
|
|
301
435
|
const statusJid = 'status@broadcast';
|
|
436
|
+
const { user, server } = jidDecode(jid);
|
|
302
437
|
const isGroup = server === 'g.us';
|
|
303
438
|
const isStatus = jid === statusJid;
|
|
304
439
|
const isLid = server === 'lid';
|
|
305
|
-
const isPrivate = server === 's.whatsapp.net'
|
|
306
440
|
const isNewsletter = server === 'newsletter';
|
|
307
|
-
|
|
441
|
+
const isGroupOrStatus = isGroup || isStatus;
|
|
442
|
+
const finalJid = jid;
|
|
443
|
+
msgId = msgId || generateMessageIDV2(meId);
|
|
308
444
|
useUserDevicesCache = useUserDevicesCache !== false;
|
|
309
|
-
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
|
|
445
|
+
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
|
|
310
446
|
const participants = [];
|
|
311
|
-
const destinationJid =
|
|
447
|
+
const destinationJid = !isStatus ? finalJid : statusJid;
|
|
312
448
|
const binaryNodeContent = [];
|
|
313
449
|
const devices = [];
|
|
450
|
+
const buttonType = getButtonType(message);
|
|
451
|
+
let didPushAdditional = false;
|
|
452
|
+
let reportingMessage;
|
|
314
453
|
const meMsg = {
|
|
315
454
|
deviceSentMessage: {
|
|
316
455
|
destinationJid,
|
|
317
456
|
message
|
|
318
|
-
}
|
|
457
|
+
},
|
|
458
|
+
messageContextInfo: message.messageContextInfo
|
|
319
459
|
};
|
|
320
|
-
const extraAttrs = {}
|
|
321
|
-
const messages = Utils_1.normalizeMessageContent(message)
|
|
322
|
-
const buttonType = getButtonType(messages);
|
|
460
|
+
const extraAttrs = {};
|
|
323
461
|
if (participant) {
|
|
324
|
-
// when the retry request is not for a group
|
|
325
|
-
// only send to the specific device that asked for a retry
|
|
326
|
-
// otherwise the message is sent out to every device that should be a recipient
|
|
327
462
|
if (!isGroup && !isStatus) {
|
|
328
|
-
additionalAttributes = { ...additionalAttributes,
|
|
463
|
+
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
|
|
329
464
|
}
|
|
330
|
-
const { user, device } =
|
|
331
|
-
devices.push({
|
|
465
|
+
const { user, device } = jidDecode(participant.jid);
|
|
466
|
+
devices.push({
|
|
467
|
+
user,
|
|
468
|
+
device,
|
|
469
|
+
jid: participant.jid
|
|
470
|
+
});
|
|
332
471
|
}
|
|
333
472
|
await authState.keys.transaction(async () => {
|
|
334
|
-
const mediaType = getMediaType(
|
|
335
|
-
|
|
473
|
+
const mediaType = getMediaType(message);
|
|
336
474
|
if (mediaType) {
|
|
337
|
-
extraAttrs['mediatype'] = mediaType
|
|
475
|
+
extraAttrs['mediatype'] = mediaType;
|
|
476
|
+
}
|
|
477
|
+
if (isNewsletter) {
|
|
478
|
+
const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
|
|
479
|
+
const bytes = encodeNewsletterMessage(patched);
|
|
480
|
+
binaryNodeContent.push({
|
|
481
|
+
tag: 'plaintext',
|
|
482
|
+
attrs: {},
|
|
483
|
+
content: bytes
|
|
484
|
+
});
|
|
485
|
+
const stanza = {
|
|
486
|
+
tag: 'message',
|
|
487
|
+
attrs: {
|
|
488
|
+
to: jid,
|
|
489
|
+
id: msgId,
|
|
490
|
+
type: getMessageType(message),
|
|
491
|
+
...(additionalAttributes || {})
|
|
492
|
+
},
|
|
493
|
+
content: binaryNodeContent
|
|
494
|
+
};
|
|
495
|
+
logger.debug({ msgId }, `sending newsletter message to ${jid}`);
|
|
496
|
+
await sendNode(stanza);
|
|
497
|
+
return;
|
|
338
498
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
extraAttrs['decrypt-fail'] = 'hide'
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (messages.interactiveResponseMessage?.nativeFlowResponseMessage) {
|
|
345
|
-
extraAttrs['native_flow_name'] = messages.interactiveResponseMessage?.nativeFlowResponseMessage.name
|
|
499
|
+
if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
|
|
500
|
+
extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
|
|
346
501
|
}
|
|
347
|
-
|
|
348
|
-
if (isGroup || isStatus) {
|
|
502
|
+
if (isGroupOrStatus && !isRetryResend) {
|
|
349
503
|
const [groupData, senderKeyMap] = await Promise.all([
|
|
350
504
|
(async () => {
|
|
351
|
-
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
|
|
352
|
-
if (groupData) {
|
|
505
|
+
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined; // todo: should we rely on the cache specially if the cache is outdated and the metadata has new fields?
|
|
506
|
+
if (groupData && Array.isArray(groupData?.participants)) {
|
|
353
507
|
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
|
|
354
508
|
}
|
|
355
|
-
|
|
356
509
|
else if (!isStatus) {
|
|
357
|
-
groupData = await groupMetadata(jid)
|
|
510
|
+
groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
|
|
358
511
|
}
|
|
359
|
-
|
|
360
512
|
return groupData;
|
|
361
513
|
})(),
|
|
362
514
|
(async () => {
|
|
363
515
|
if (!participant && !isStatus) {
|
|
364
|
-
|
|
365
|
-
|
|
516
|
+
// what if sender memory is less accurate than the cached metadata
|
|
517
|
+
// on participant change in group, we should do sender memory manipulation
|
|
518
|
+
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?
|
|
519
|
+
return result[jid] || {};
|
|
366
520
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
})()
|
|
521
|
+
return {};
|
|
522
|
+
})()
|
|
371
523
|
]);
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// if (!isStatus) {
|
|
380
|
-
// const expiration = await getEphemeralGroup(jid)
|
|
381
|
-
// additionalAttributes = {
|
|
382
|
-
// ...additionalAttributes,
|
|
383
|
-
// addressing_mode: 'pn',
|
|
384
|
-
// ...expiration ? { expiration: expiration.toString() } : null
|
|
385
|
-
// }
|
|
386
|
-
// }
|
|
387
|
-
|
|
388
|
-
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
|
|
389
|
-
devices.push(...additionalDevices)
|
|
524
|
+
const participantsList = groupData ? groupData.participants.map(p => p.id) : [];
|
|
525
|
+
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
526
|
+
additionalAttributes = {
|
|
527
|
+
...additionalAttributes,
|
|
528
|
+
expiration: groupData.ephemeralDuration.toString()
|
|
529
|
+
};
|
|
390
530
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
531
|
+
if (isStatus && statusJidList) {
|
|
532
|
+
participantsList.push(...statusJidList);
|
|
533
|
+
}
|
|
534
|
+
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
|
|
535
|
+
devices.push(...additionalDevices);
|
|
536
|
+
if (isGroup) {
|
|
537
|
+
additionalAttributes = {
|
|
538
|
+
...additionalAttributes,
|
|
539
|
+
addressing_mode: groupData?.addressingMode || 'lid'
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
const patched = await patchMessageBeforeSending(message);
|
|
543
|
+
if (Array.isArray(patched)) {
|
|
544
|
+
throw new Boom('Per-jid patching is not supported in groups');
|
|
545
|
+
}
|
|
546
|
+
const bytes = encodeWAMessage(patched);
|
|
547
|
+
reportingMessage = patched;
|
|
548
|
+
const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
|
|
549
|
+
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
|
|
395
550
|
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
396
551
|
group: destinationJid,
|
|
397
552
|
data: bytes,
|
|
398
|
-
meId
|
|
553
|
+
meId: groupSenderIdentity
|
|
399
554
|
});
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const
|
|
404
|
-
if (!
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
555
|
+
const senderKeyRecipients = [];
|
|
556
|
+
for (const device of devices) {
|
|
557
|
+
const deviceJid = device.jid;
|
|
558
|
+
const hasKey = !!senderKeyMap[deviceJid];
|
|
559
|
+
if ((!hasKey || !!participant) &&
|
|
560
|
+
!isHostedLidUser(deviceJid) &&
|
|
561
|
+
!isHostedPnUser(deviceJid) &&
|
|
562
|
+
device.device !== 99) {
|
|
563
|
+
//todo: revamp all this logic
|
|
564
|
+
// 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
|
|
565
|
+
senderKeyRecipients.push(deviceJid);
|
|
566
|
+
senderKeyMap[deviceJid] = true;
|
|
408
567
|
}
|
|
409
568
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (senderKeyJids.length) {
|
|
413
|
-
logger.debug({ senderKeyJids }, 'sending new sender key');
|
|
569
|
+
if (senderKeyRecipients.length) {
|
|
570
|
+
logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key');
|
|
414
571
|
const senderKeyMsg = {
|
|
415
572
|
senderKeyDistributionMessage: {
|
|
416
573
|
axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
|
|
417
574
|
groupId: destinationJid
|
|
418
575
|
}
|
|
419
576
|
};
|
|
420
|
-
|
|
421
|
-
|
|
577
|
+
const senderKeySessionTargets = senderKeyRecipients;
|
|
578
|
+
await assertSessions(senderKeySessionTargets);
|
|
579
|
+
const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs);
|
|
422
580
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
|
|
423
581
|
participants.push(...result.nodes);
|
|
424
582
|
}
|
|
@@ -429,104 +587,177 @@ const makeMessagesSocket = (config) => {
|
|
|
429
587
|
});
|
|
430
588
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
|
|
431
589
|
}
|
|
432
|
-
else
|
|
433
|
-
//
|
|
434
|
-
if
|
|
435
|
-
|
|
436
|
-
|
|
590
|
+
else {
|
|
591
|
+
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
592
|
+
// TODO: investigate if this is true
|
|
593
|
+
let ownId = meId;
|
|
594
|
+
if (isLid && meLid) {
|
|
595
|
+
ownId = meLid;
|
|
596
|
+
logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
|
|
437
597
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (message.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
|
|
441
|
-
msgId = message.protocolMessage.key?.id
|
|
442
|
-
message = {}
|
|
598
|
+
else {
|
|
599
|
+
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
|
|
443
600
|
}
|
|
444
|
-
|
|
445
|
-
const patched = await patchMessageBeforeSending(message, [])
|
|
446
|
-
const bytes = Utils_1.encodeNewsletterMessage(patched)
|
|
447
|
-
|
|
448
|
-
binaryNodeContent.push({
|
|
449
|
-
tag: 'plaintext',
|
|
450
|
-
attrs: extraAttrs ? extraAttrs : {},
|
|
451
|
-
content: bytes
|
|
452
|
-
})
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
const { user: meUser } = WABinary_1.jidDecode(meId);
|
|
601
|
+
const { user: ownUser } = jidDecode(ownId);
|
|
456
602
|
if (!participant) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
603
|
+
const patchedForReporting = await patchMessageBeforeSending(message, [jid]);
|
|
604
|
+
reportingMessage = Array.isArray(patchedForReporting)
|
|
605
|
+
? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
|
|
606
|
+
: patchedForReporting;
|
|
607
|
+
}
|
|
608
|
+
if (!isRetryResend) {
|
|
609
|
+
const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
610
|
+
devices.push({
|
|
611
|
+
user,
|
|
612
|
+
device: 0,
|
|
613
|
+
jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
|
|
614
|
+
});
|
|
615
|
+
if (user !== ownUser) {
|
|
616
|
+
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
617
|
+
const ownUserForAddressing = isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user;
|
|
618
|
+
devices.push({
|
|
619
|
+
user: ownUserForAddressing,
|
|
620
|
+
device: 0,
|
|
621
|
+
jid: jidEncode(ownUserForAddressing, ownUserServer, 0)
|
|
622
|
+
});
|
|
460
623
|
}
|
|
461
|
-
|
|
462
624
|
if (additionalAttributes?.['category'] !== 'peer') {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
625
|
+
// Clear placeholders and enumerate actual devices
|
|
626
|
+
devices.length = 0;
|
|
627
|
+
// Use conversation-appropriate sender identity
|
|
628
|
+
const senderIdentity = isLid && meLid
|
|
629
|
+
? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
|
|
630
|
+
: jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
|
|
631
|
+
// Enumerate devices for sender and target with consistent addressing
|
|
632
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
|
|
633
|
+
devices.push(...sessionDevices);
|
|
634
|
+
logger.debug({
|
|
635
|
+
deviceCount: devices.length,
|
|
636
|
+
devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`)
|
|
637
|
+
}, 'Device enumeration complete with unified addressing');
|
|
466
638
|
}
|
|
467
639
|
}
|
|
468
|
-
const
|
|
469
|
-
const
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
640
|
+
const allRecipients = [];
|
|
641
|
+
const meRecipients = [];
|
|
642
|
+
const otherRecipients = [];
|
|
643
|
+
const { user: mePnUser } = jidDecode(meId);
|
|
644
|
+
const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
|
|
645
|
+
for (const { user, jid } of devices) {
|
|
646
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
|
|
647
|
+
if (isExactSenderDevice) {
|
|
648
|
+
logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
// Check if this is our device (could match either PN or LID user)
|
|
652
|
+
const isMe = user === mePnUser || user === meLidUser;
|
|
475
653
|
if (isMe) {
|
|
476
|
-
|
|
654
|
+
meRecipients.push(jid);
|
|
477
655
|
}
|
|
478
|
-
|
|
479
656
|
else {
|
|
480
|
-
|
|
657
|
+
otherRecipients.push(jid);
|
|
481
658
|
}
|
|
482
|
-
|
|
483
|
-
allJids.push(jid)
|
|
659
|
+
allRecipients.push(jid);
|
|
484
660
|
}
|
|
485
|
-
await assertSessions(
|
|
661
|
+
await assertSessions(allRecipients);
|
|
486
662
|
const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
|
|
487
|
-
|
|
488
|
-
createParticipantNodes(
|
|
489
|
-
|
|
663
|
+
// For own devices: use DSM if available (1:1 chats only)
|
|
664
|
+
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
|
665
|
+
createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
|
|
666
|
+
]);
|
|
490
667
|
participants.push(...meNodes);
|
|
491
668
|
participants.push(...otherNodes);
|
|
669
|
+
if (meRecipients.length > 0 || otherRecipients.length > 0) {
|
|
670
|
+
extraAttrs['phash'] = generateParticipantHashV2([...meRecipients, ...otherRecipients]);
|
|
671
|
+
}
|
|
492
672
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
|
|
493
673
|
}
|
|
674
|
+
if (isRetryResend) {
|
|
675
|
+
const isParticipantLid = isLidUser(participant.jid);
|
|
676
|
+
const isMe = areJidsSameUser(participant.jid, isParticipantLid ? meLid : meId);
|
|
677
|
+
let messageToSend = message;
|
|
678
|
+
if (isGroupOrStatus) {
|
|
679
|
+
let groupSenderIdentity;
|
|
680
|
+
if (meLid && (await signalRepository.hasSenderKey({ group: destinationJid, meId: meLid }))) {
|
|
681
|
+
groupSenderIdentity = meLid;
|
|
682
|
+
}
|
|
683
|
+
else if (await signalRepository.hasSenderKey({ group: destinationJid, meId })) {
|
|
684
|
+
groupSenderIdentity = meId;
|
|
685
|
+
}
|
|
686
|
+
if (groupSenderIdentity) {
|
|
687
|
+
try {
|
|
688
|
+
const skdm = await signalRepository.getSenderKeyDistributionMessage({
|
|
689
|
+
group: destinationJid,
|
|
690
|
+
meId: groupSenderIdentity
|
|
691
|
+
});
|
|
692
|
+
messageToSend = {
|
|
693
|
+
...message,
|
|
694
|
+
senderKeyDistributionMessage: {
|
|
695
|
+
groupId: destinationJid,
|
|
696
|
+
axolotlSenderKeyDistributionMessage: skdm
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
catch (err) {
|
|
701
|
+
logger.warn({ err, jid: destinationJid }, 'failed to build SKDM for retry, sending without it');
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const encodedMessageToSend = isMe
|
|
706
|
+
? encodeWAMessage({
|
|
707
|
+
deviceSentMessage: {
|
|
708
|
+
destinationJid,
|
|
709
|
+
message: messageToSend
|
|
710
|
+
}
|
|
711
|
+
})
|
|
712
|
+
: encodeWAMessage(messageToSend);
|
|
713
|
+
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
|
|
714
|
+
data: encodedMessageToSend,
|
|
715
|
+
jid: participant.jid
|
|
716
|
+
});
|
|
717
|
+
binaryNodeContent.push({
|
|
718
|
+
tag: 'enc',
|
|
719
|
+
attrs: {
|
|
720
|
+
v: '2',
|
|
721
|
+
type,
|
|
722
|
+
count: participant.count.toString()
|
|
723
|
+
},
|
|
724
|
+
content: encryptedContent
|
|
725
|
+
});
|
|
726
|
+
}
|
|
494
727
|
if (participants.length) {
|
|
495
728
|
if (additionalAttributes?.['category'] === 'peer') {
|
|
496
|
-
const peerNode = participants[0]?.content?.[0]
|
|
497
|
-
|
|
729
|
+
const peerNode = participants[0]?.content?.[0];
|
|
498
730
|
if (peerNode) {
|
|
499
|
-
binaryNodeContent.push(peerNode) // push only enc
|
|
731
|
+
binaryNodeContent.push(peerNode); // push only enc
|
|
500
732
|
}
|
|
501
733
|
}
|
|
502
|
-
|
|
503
734
|
else {
|
|
504
735
|
binaryNodeContent.push({
|
|
505
736
|
tag: 'participants',
|
|
506
737
|
attrs: {},
|
|
507
738
|
content: participants
|
|
508
|
-
})
|
|
739
|
+
});
|
|
509
740
|
}
|
|
510
741
|
}
|
|
511
|
-
|
|
512
742
|
const stanza = {
|
|
513
743
|
tag: 'message',
|
|
514
744
|
attrs: {
|
|
515
745
|
id: msgId,
|
|
516
|
-
|
|
746
|
+
to: destinationJid,
|
|
747
|
+
type: getMessageType(message),
|
|
517
748
|
...(additionalAttributes || {})
|
|
518
749
|
},
|
|
519
750
|
content: binaryNodeContent
|
|
520
|
-
}
|
|
751
|
+
};
|
|
521
752
|
// if the participant to send to is explicitly specified (generally retry recp)
|
|
522
753
|
// ensure the message is only sent to that person
|
|
523
754
|
// if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
|
|
524
755
|
if (participant) {
|
|
525
|
-
if (
|
|
756
|
+
if (isJidGroup(destinationJid)) {
|
|
526
757
|
stanza.attrs.to = destinationJid;
|
|
527
758
|
stanza.attrs.participant = participant.jid;
|
|
528
759
|
}
|
|
529
|
-
else if (
|
|
760
|
+
else if (areJidsSameUser(participant.jid, meId)) {
|
|
530
761
|
stanza.attrs.to = participant.jid;
|
|
531
762
|
stanza.attrs.recipient = destinationJid;
|
|
532
763
|
}
|
|
@@ -538,170 +769,221 @@ const makeMessagesSocket = (config) => {
|
|
|
538
769
|
stanza.attrs.to = destinationJid;
|
|
539
770
|
}
|
|
540
771
|
if (shouldIncludeDeviceIdentity) {
|
|
772
|
+
;
|
|
541
773
|
stanza.content.push({
|
|
542
774
|
tag: 'device-identity',
|
|
543
775
|
attrs: {},
|
|
544
|
-
content:
|
|
776
|
+
content: encodeSignedDeviceIdentity(authState.creds.account, true)
|
|
545
777
|
});
|
|
546
778
|
logger.debug({ jid }, 'adding device identity');
|
|
547
779
|
}
|
|
548
|
-
|
|
549
|
-
|
|
780
|
+
if (!isNewsletter && buttonType && !isStatus) {
|
|
781
|
+
const content = getAdditionalNode(buttonType)
|
|
782
|
+
const filteredNode = getBinaryNodeFilter(additionalNodes)
|
|
783
|
+
|
|
784
|
+
if (filteredNode) {
|
|
785
|
+
didPushAdditional = true
|
|
786
|
+
stanza.content.push(...additionalNodes)
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
stanza.content.push(...content)
|
|
790
|
+
}
|
|
791
|
+
logger.debug({ jid }, 'adding business node')
|
|
792
|
+
}
|
|
793
|
+
if (!isNewsletter && !isGroupOrStatus && aiLabel) {
|
|
550
794
|
const botNode = {
|
|
551
795
|
tag: 'bot',
|
|
552
796
|
attrs: {
|
|
553
797
|
biz_bot: '1'
|
|
554
798
|
}
|
|
555
799
|
}
|
|
556
|
-
|
|
557
|
-
const filteredBizBot = WABinary_1.getBinaryNodeFilter(additionalNodes ? additionalNodes : [])
|
|
558
|
-
|
|
800
|
+
const filteredBizBot = getBinaryNodeFilter(additionalNodes ? additionalNodes : [])
|
|
559
801
|
if (filteredBizBot) {
|
|
560
802
|
stanza.content.push(...additionalNodes)
|
|
561
803
|
didPushAdditional = true
|
|
562
804
|
}
|
|
563
|
-
|
|
564
805
|
else {
|
|
565
806
|
stanza.content.push(botNode)
|
|
566
807
|
}
|
|
567
808
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
809
|
+
if (!isNewsletter &&
|
|
810
|
+
!isRetryResend &&
|
|
811
|
+
reportingMessage?.messageContextInfo?.messageSecret &&
|
|
812
|
+
shouldIncludeReportingToken(reportingMessage)) {
|
|
813
|
+
try {
|
|
814
|
+
const encoded = encodeWAMessage(reportingMessage);
|
|
815
|
+
const reportingKey = {
|
|
816
|
+
id: msgId,
|
|
817
|
+
fromMe: true,
|
|
818
|
+
remoteJid: destinationJid,
|
|
819
|
+
participant: participant?.jid
|
|
820
|
+
};
|
|
821
|
+
const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey);
|
|
822
|
+
if (reportingNode) {
|
|
823
|
+
;
|
|
824
|
+
stanza.content.push(reportingNode);
|
|
825
|
+
logger.trace({ jid }, 'added reporting token to message');
|
|
826
|
+
}
|
|
579
827
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
828
|
+
catch (error) {
|
|
829
|
+
logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
// WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
|
|
833
|
+
const isPeerMessage = additionalAttributes?.['category'] === 'peer';
|
|
834
|
+
const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
|
|
835
|
+
// Resolve destination to LID for tctoken storage — matches Signal session key pattern
|
|
836
|
+
const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
|
|
837
|
+
const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
|
|
838
|
+
const existingTokenEntry = contactTcTokenData[tcTokenJid];
|
|
839
|
+
let tcTokenBuffer = existingTokenEntry?.token;
|
|
840
|
+
// Treat expired tokens the same as missing — clear from cache
|
|
841
|
+
if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
|
|
842
|
+
logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
|
|
843
|
+
tcTokenBuffer = undefined;
|
|
844
|
+
// Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
|
|
845
|
+
const cleared = existingTokenEntry?.senderTimestamp !== undefined
|
|
846
|
+
? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
|
|
847
|
+
: null;
|
|
848
|
+
try {
|
|
849
|
+
await authState.keys.set({ tctoken: { [tcTokenJid]: cleared } });
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup');
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
|
|
856
|
+
;
|
|
857
|
+
stanza.content.push({
|
|
858
|
+
tag: 'tctoken',
|
|
859
|
+
attrs: {},
|
|
860
|
+
content: tcTokenBuffer
|
|
861
|
+
});
|
|
862
|
+
}
|
|
583
863
|
if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
|
|
864
|
+
;
|
|
584
865
|
stanza.content.push(...additionalNodes);
|
|
585
866
|
}
|
|
586
|
-
|
|
587
867
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`);
|
|
588
868
|
await sendNode(stanza);
|
|
589
|
-
|
|
869
|
+
// Fire-and-forget: issue our token to the contact AFTER message send.
|
|
870
|
+
// WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
|
|
871
|
+
const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
|
|
872
|
+
const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
|
|
873
|
+
if (is1on1Send &&
|
|
874
|
+
!isProtocolMsg &&
|
|
875
|
+
!isBotOrPSA &&
|
|
876
|
+
shouldSendNewTcToken(existingTokenEntry?.senderTimestamp) &&
|
|
877
|
+
!inFlightTcTokenIssuance.has(tcTokenJid)) {
|
|
878
|
+
inFlightTcTokenIssuance.add(tcTokenJid);
|
|
879
|
+
const issueTimestamp = unixTimestampSeconds();
|
|
880
|
+
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
|
|
881
|
+
resolveIssuanceJid(destinationJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID)
|
|
882
|
+
.then(issueJid => issuePrivacyTokens([issueJid], issueTimestamp))
|
|
883
|
+
.then(async (result) => {
|
|
884
|
+
await storeTcTokensFromIqResult({
|
|
885
|
+
result,
|
|
886
|
+
fallbackJid: tcTokenJid,
|
|
887
|
+
keys: authState.keys,
|
|
888
|
+
getLIDForPN
|
|
889
|
+
});
|
|
890
|
+
const currentData = await authState.keys.get('tctoken', [tcTokenJid]);
|
|
891
|
+
const currentEntry = currentData[tcTokenJid];
|
|
892
|
+
const indexWrite = await buildMergedTcTokenIndexWrite(authState.keys, [tcTokenJid]);
|
|
893
|
+
await authState.keys.set({
|
|
894
|
+
tctoken: {
|
|
895
|
+
[tcTokenJid]: {
|
|
896
|
+
token: Buffer.alloc(0),
|
|
897
|
+
...currentEntry,
|
|
898
|
+
senderTimestamp: issueTimestamp
|
|
899
|
+
},
|
|
900
|
+
...indexWrite
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
})
|
|
904
|
+
.catch(err => {
|
|
905
|
+
logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed');
|
|
906
|
+
})
|
|
907
|
+
.finally(() => {
|
|
908
|
+
inFlightTcTokenIssuance.delete(tcTokenJid);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
// Add message to retry cache if enabled
|
|
912
|
+
if (messageRetryManager && !participant) {
|
|
913
|
+
messageRetryManager.addRecentMessage(destinationJid, msgId, message);
|
|
914
|
+
}
|
|
915
|
+
}, meId);
|
|
590
916
|
return msgId;
|
|
591
917
|
};
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
useUserDevicesCache = true,
|
|
599
|
-
cachedGroupMetadata
|
|
600
|
-
} = options;
|
|
601
|
-
|
|
602
|
-
const { server } = WABinary_1.jidDecode(jid);
|
|
603
|
-
if (server !== "g.us") throw new Error("@g.us server required");
|
|
604
|
-
|
|
605
|
-
const meId = authState.creds.me.id;
|
|
606
|
-
const messages = Utils_1.normalizeMessageContent(message);
|
|
607
|
-
const groupData = cachedGroupMetadata? await cachedGroupMetadata(jid) : await groupMetadata(jid);
|
|
608
|
-
const isLid = groupData.addressingMode === "lid";
|
|
609
|
-
const participantJids = groupData.participants.map(p => p.id);
|
|
610
|
-
|
|
611
|
-
logger.info(`Sending message to ${participantJids.length} members from ${jid}`);
|
|
612
|
-
|
|
613
|
-
for (let i = 0; i < participantJids.length; i++) {
|
|
614
|
-
const jid = participantJids[i];
|
|
615
|
-
if (WABinary_1.areJidsSameUser(jid, meId)) continue;
|
|
616
|
-
|
|
617
|
-
try {
|
|
618
|
-
const msgId = `${idm || Utils_1.generateMessageID()}_${i}`;
|
|
619
|
-
const fullMsg = await Utils_1.generateWAMessageFromContent(jid, message, {
|
|
620
|
-
messageId: msgId,
|
|
621
|
-
quoted
|
|
622
|
-
})
|
|
623
|
-
await relayMessage(jid, fullMsg.message, {
|
|
624
|
-
messageId: fullMsg.key.id
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
logger.debug(`Message successfully sent to ${jid}`);
|
|
628
|
-
if (delayMs && i < participantJids.length - 1) {
|
|
629
|
-
await new Promise(z => setTimeout(z, delayMs));
|
|
630
|
-
}
|
|
631
|
-
} catch (e) {
|
|
632
|
-
logger.error({ jid, e }, "Error sending message to");
|
|
918
|
+
const getMessageType = (message) => {
|
|
919
|
+
const normalizedMessage = normalizeMessageContent(message);
|
|
920
|
+
if (!normalizedMessage)
|
|
921
|
+
return 'text';
|
|
922
|
+
if (normalizedMessage.reactionMessage || normalizedMessage.encReactionMessage) {
|
|
923
|
+
return 'reaction';
|
|
633
924
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
};
|
|
640
|
-
|
|
641
|
-
const getTypeMessage = (msg) => {
|
|
642
|
-
const message = Utils_1.normalizeMessageContent(msg)
|
|
643
|
-
if (message.reactionMessage) {
|
|
644
|
-
return 'reaction'
|
|
645
|
-
}
|
|
646
|
-
else if (getMediaType(message)) {
|
|
647
|
-
return 'media'
|
|
648
|
-
}
|
|
649
|
-
else {
|
|
650
|
-
return 'text'
|
|
925
|
+
if (normalizedMessage.pollCreationMessage ||
|
|
926
|
+
normalizedMessage.pollCreationMessageV2 ||
|
|
927
|
+
normalizedMessage.pollCreationMessageV3 ||
|
|
928
|
+
normalizedMessage.pollUpdateMessage) {
|
|
929
|
+
return 'poll';
|
|
651
930
|
}
|
|
652
|
-
|
|
653
|
-
|
|
931
|
+
if (normalizedMessage.eventMessage) {
|
|
932
|
+
return 'event';
|
|
933
|
+
}
|
|
934
|
+
if (getMediaType(normalizedMessage) !== '') {
|
|
935
|
+
return 'media';
|
|
936
|
+
}
|
|
937
|
+
return 'text';
|
|
938
|
+
};
|
|
654
939
|
const getMediaType = (message) => {
|
|
655
940
|
if (message.imageMessage) {
|
|
656
|
-
return 'image'
|
|
941
|
+
return 'image';
|
|
657
942
|
}
|
|
658
943
|
else if (message.videoMessage) {
|
|
659
|
-
return message.videoMessage.gifPlayback ? 'gif' : 'video'
|
|
944
|
+
return message.videoMessage.gifPlayback ? 'gif' : 'video';
|
|
660
945
|
}
|
|
661
946
|
else if (message.audioMessage) {
|
|
662
|
-
return message.audioMessage.ptt ? 'ptt' : 'audio'
|
|
947
|
+
return message.audioMessage.ptt ? 'ptt' : 'audio';
|
|
663
948
|
}
|
|
664
949
|
else if (message.contactMessage) {
|
|
665
|
-
return 'vcard'
|
|
950
|
+
return 'vcard';
|
|
666
951
|
}
|
|
667
952
|
else if (message.documentMessage) {
|
|
668
|
-
return 'document'
|
|
953
|
+
return 'document';
|
|
669
954
|
}
|
|
670
955
|
else if (message.contactsArrayMessage) {
|
|
671
|
-
return 'contact_array'
|
|
956
|
+
return 'contact_array';
|
|
672
957
|
}
|
|
673
958
|
else if (message.liveLocationMessage) {
|
|
674
|
-
return 'livelocation'
|
|
959
|
+
return 'livelocation';
|
|
675
960
|
}
|
|
676
961
|
else if (message.stickerMessage) {
|
|
677
|
-
return 'sticker'
|
|
962
|
+
return 'sticker';
|
|
678
963
|
}
|
|
679
964
|
else if (message.listMessage) {
|
|
680
|
-
return 'list'
|
|
965
|
+
return 'list';
|
|
681
966
|
}
|
|
682
967
|
else if (message.listResponseMessage) {
|
|
683
|
-
return 'list_response'
|
|
968
|
+
return 'list_response';
|
|
684
969
|
}
|
|
685
970
|
else if (message.buttonsResponseMessage) {
|
|
686
|
-
return 'buttons_response'
|
|
971
|
+
return 'buttons_response';
|
|
687
972
|
}
|
|
688
973
|
else if (message.orderMessage) {
|
|
689
|
-
return 'order'
|
|
974
|
+
return 'order';
|
|
690
975
|
}
|
|
691
976
|
else if (message.productMessage) {
|
|
692
|
-
return 'product'
|
|
977
|
+
return 'product';
|
|
693
978
|
}
|
|
694
979
|
else if (message.interactiveResponseMessage) {
|
|
695
|
-
return 'native_flow_response'
|
|
980
|
+
return 'native_flow_response';
|
|
696
981
|
}
|
|
697
982
|
else if (message.groupInviteMessage) {
|
|
698
|
-
return 'url'
|
|
983
|
+
return 'url';
|
|
699
984
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
985
|
+
return '';
|
|
986
|
+
};
|
|
705
987
|
const getButtonType = (message) => {
|
|
706
988
|
if (message.listMessage) {
|
|
707
989
|
return 'list'
|
|
@@ -731,13 +1013,13 @@ const makeMessagesSocket = (config) => {
|
|
|
731
1013
|
else if (message.interactiveMessage?.nativeFlowMessage) {
|
|
732
1014
|
return 'native_flow'
|
|
733
1015
|
}
|
|
734
|
-
}
|
|
735
|
-
const
|
|
736
|
-
const t =
|
|
1016
|
+
};
|
|
1017
|
+
const issuePrivacyTokens = async (jids, timestamp) => {
|
|
1018
|
+
const t = (timestamp ?? unixTimestampSeconds()).toString();
|
|
737
1019
|
const result = await query({
|
|
738
1020
|
tag: 'iq',
|
|
739
1021
|
attrs: {
|
|
740
|
-
to:
|
|
1022
|
+
to: S_WHATSAPP_NET,
|
|
741
1023
|
type: 'set',
|
|
742
1024
|
xmlns: 'privacy'
|
|
743
1025
|
},
|
|
@@ -748,7 +1030,7 @@ const makeMessagesSocket = (config) => {
|
|
|
748
1030
|
content: jids.map(jid => ({
|
|
749
1031
|
tag: 'token',
|
|
750
1032
|
attrs: {
|
|
751
|
-
jid:
|
|
1033
|
+
jid: jidNormalizedUser(jid),
|
|
752
1034
|
t,
|
|
753
1035
|
type: 'trusted_contact'
|
|
754
1036
|
}
|
|
@@ -757,158 +1039,47 @@ const makeMessagesSocket = (config) => {
|
|
|
757
1039
|
]
|
|
758
1040
|
});
|
|
759
1041
|
return result;
|
|
760
|
-
}
|
|
761
|
-
const waUploadToServer =
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
delete options.ephemeralExpiration
|
|
767
|
-
const { ptcp = false, quoted } = options;
|
|
768
|
-
const getParticipantAttr = () => ptcp ? { participant: { jid } } : {};
|
|
769
|
-
const messageType = luki.detectType(content);
|
|
770
|
-
if (typeof content === 'object' && 'disappearingMessagesInChat' in content &&
|
|
771
|
-
typeof content['disappearingMessagesInChat'] !== 'undefined' && WABinary_1.isJidGroup(jid)) {
|
|
772
|
-
const { disappearingMessagesInChat } = content
|
|
773
|
-
|
|
774
|
-
const value = typeof disappearingMessagesInChat === 'boolean' ?
|
|
775
|
-
(disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
|
|
776
|
-
disappearingMessagesInChat
|
|
777
|
-
|
|
778
|
-
await groupToggleEphemeral(jid, value)
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
else {
|
|
782
|
-
let mediaHandle
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (messageType) {
|
|
786
|
-
switch(messageType) {
|
|
787
|
-
case 'PAYMENT':
|
|
788
|
-
const paymentContent = await luki.handlePayment(content, quoted);
|
|
789
|
-
return await relayMessage(jid, paymentContent, {
|
|
790
|
-
messageId: Utils_1.generateMessageID(),
|
|
791
|
-
...getParticipantAttr()
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
case 'PRODUCT':
|
|
795
|
-
const productContent = await luki.handleProduct(content, jid, quoted);
|
|
796
|
-
const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
|
|
797
|
-
return await relayMessage(jid, productMsg.message, {
|
|
798
|
-
messageId: productMsg.key.id,
|
|
799
|
-
...getParticipantAttr()
|
|
800
|
-
});
|
|
801
|
-
|
|
802
|
-
case 'INTERACTIVE':
|
|
803
|
-
const interactiveContent = await luki.handleInteractive(content, jid, quoted);
|
|
804
|
-
const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
|
|
805
|
-
return await relayMessage(jid, interactiveMsg.message, {
|
|
806
|
-
messageId: interactiveMsg.key.id,
|
|
807
|
-
...getParticipantAttr()
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
case 'ALBUM':
|
|
811
|
-
return await luki.handleAlbum(content, jid, quoted)
|
|
812
|
-
case 'EVENT':
|
|
813
|
-
return await luki.handleEvent(content, jid, quoted)
|
|
814
|
-
case 'POLL_RESULT':
|
|
815
|
-
return await luki.handlePollResult(content, jid, quoted)
|
|
816
|
-
case 'ORDER':
|
|
817
|
-
return await luki.handleOrderMessage(content, jid, quoted)
|
|
818
|
-
case 'GROUP_STATUS':
|
|
819
|
-
return await luki.handleGroupStory(content, jid, quoted)
|
|
820
|
-
case 'GROUP_LABEL':
|
|
821
|
-
return await luki.handleGbLabel(content, jid)
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
const fullMsg = await Utils_1.generateWAMessage(jid, content, {
|
|
825
|
-
logger,
|
|
826
|
-
userJid,
|
|
827
|
-
quoted,
|
|
828
|
-
getUrlInfo: text => link_preview_1.getUrlInfo(text, {
|
|
829
|
-
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
830
|
-
fetchOpts: {
|
|
831
|
-
timeout: 3000,
|
|
832
|
-
...axiosOptions || {}
|
|
833
|
-
},
|
|
834
|
-
logger,
|
|
835
|
-
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
836
|
-
}),
|
|
837
|
-
upload: async (readStream, opts) => {
|
|
838
|
-
const up = await waUploadToServer(readStream, {
|
|
839
|
-
...opts,
|
|
840
|
-
newsletter: WABinary_1.isJidNewsLetter(jid)
|
|
841
|
-
});
|
|
842
|
-
return up;
|
|
843
|
-
},
|
|
844
|
-
mediaCache: config.mediaCache,
|
|
845
|
-
options: config.options,
|
|
846
|
-
...options
|
|
847
|
-
});
|
|
848
|
-
|
|
849
|
-
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
850
|
-
const isEditMsg = 'edit' in content && !!content.edit;
|
|
851
|
-
const isAiMsg = 'ai' in content && !!content.ai;
|
|
852
|
-
|
|
853
|
-
const additionalAttributes = {};
|
|
854
|
-
const additionalNodes = [];
|
|
855
|
-
|
|
856
|
-
if (isDeleteMsg) {
|
|
857
|
-
const fromMe = content.delete?.fromMe;
|
|
858
|
-
const isGroup = WABinary_1.isJidGroup(content.delete?.remoteJid);
|
|
859
|
-
additionalAttributes.edit = (isGroup && !fromMe) || WABinary_1.isJidNewsLetter(jid) ? '8' : '7';
|
|
860
|
-
} else if (isEditMsg) {
|
|
861
|
-
additionalAttributes.edit = WABinary_1.isJidNewsLetter(jid) ? '3' : '1';
|
|
862
|
-
} else if (isAiMsg) {
|
|
863
|
-
additionalNodes.push({
|
|
864
|
-
attrs: {
|
|
865
|
-
biz_bot: '1'
|
|
866
|
-
}, tag: "bot"
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
await relayMessage(jid, fullMsg.message, {
|
|
871
|
-
messageId: fullMsg.key.id,
|
|
872
|
-
cachedGroupMetadata: options.cachedGroupMetadata,
|
|
873
|
-
additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes,
|
|
874
|
-
additionalAttributes,
|
|
875
|
-
statusJidList: options.statusJidList
|
|
876
|
-
});
|
|
877
|
-
|
|
878
|
-
if (config.emitOwnEvents) {
|
|
879
|
-
process.nextTick(() => {
|
|
880
|
-
processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
881
|
-
});
|
|
1042
|
+
};
|
|
1043
|
+
const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
|
|
1044
|
+
const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
|
|
1045
|
+
registerSocketEndHandler(() => {
|
|
1046
|
+
if (!config.userDevicesCache && userDevicesCache.close) {
|
|
1047
|
+
userDevicesCache.close();
|
|
882
1048
|
}
|
|
883
|
-
|
|
1049
|
+
mediaConn = undefined;
|
|
1050
|
+
if (messageRetryManager) {
|
|
1051
|
+
messageRetryManager.clear();
|
|
884
1052
|
}
|
|
885
|
-
}
|
|
1053
|
+
});
|
|
886
1054
|
return {
|
|
887
1055
|
...sock,
|
|
888
|
-
|
|
1056
|
+
userDevicesCache,
|
|
1057
|
+
devicesMutex,
|
|
1058
|
+
issuePrivacyTokens,
|
|
889
1059
|
assertSessions,
|
|
890
1060
|
relayMessage,
|
|
891
|
-
sendMessageMembers,
|
|
892
1061
|
sendReceipt,
|
|
893
1062
|
sendReceipts,
|
|
894
|
-
luki,
|
|
895
|
-
sendMessage,
|
|
896
1063
|
readMessages,
|
|
897
1064
|
refreshMediaConn,
|
|
898
|
-
|
|
899
|
-
|
|
1065
|
+
// Function (not getter) so the spread in chats.ts preserves the live closure binding.
|
|
1066
|
+
getMediaHost: () => mediaHost,
|
|
900
1067
|
waUploadToServer,
|
|
901
|
-
sendPeerDataOperationMessage,
|
|
902
1068
|
fetchPrivacySettings,
|
|
1069
|
+
sendPeerDataOperationMessage,
|
|
1070
|
+
createParticipantNodes,
|
|
1071
|
+
getUSyncDevices,
|
|
1072
|
+
messageRetryManager,
|
|
1073
|
+
updateMemberLabel,
|
|
903
1074
|
updateMediaMessage: async (message) => {
|
|
904
|
-
const content =
|
|
1075
|
+
const content = assertMediaContent(message.message);
|
|
905
1076
|
const mediaKey = content.mediaKey;
|
|
906
1077
|
const meId = authState.creds.me.id;
|
|
907
|
-
const node =
|
|
1078
|
+
const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
|
|
908
1079
|
let error = undefined;
|
|
909
1080
|
await Promise.all([
|
|
910
1081
|
sendNode(node),
|
|
911
|
-
waitForMsgMediaUpdate(update => {
|
|
1082
|
+
waitForMsgMediaUpdate(async (update) => {
|
|
912
1083
|
const result = update.find(c => c.key.id === message.key.id);
|
|
913
1084
|
if (result) {
|
|
914
1085
|
if (result.error) {
|
|
@@ -916,13 +1087,16 @@ const makeMessagesSocket = (config) => {
|
|
|
916
1087
|
}
|
|
917
1088
|
else {
|
|
918
1089
|
try {
|
|
919
|
-
const media =
|
|
920
|
-
if (media.result !==
|
|
921
|
-
const resultStr =
|
|
922
|
-
throw new
|
|
1090
|
+
const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
|
|
1091
|
+
if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
|
|
1092
|
+
const resultStr = proto.MediaRetryNotification.ResultType[media.result];
|
|
1093
|
+
throw new Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
1094
|
+
data: media,
|
|
1095
|
+
statusCode: getStatusCodeForMediaRetry(media.result) || 404
|
|
1096
|
+
});
|
|
923
1097
|
}
|
|
924
1098
|
content.directPath = media.directPath;
|
|
925
|
-
content.url =
|
|
1099
|
+
content.url = getUrlFromDirectPath(content.directPath, mediaHost);
|
|
926
1100
|
logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
|
|
927
1101
|
}
|
|
928
1102
|
catch (err) {
|
|
@@ -936,149 +1110,182 @@ const makeMessagesSocket = (config) => {
|
|
|
936
1110
|
if (error) {
|
|
937
1111
|
throw error;
|
|
938
1112
|
}
|
|
939
|
-
ev.emit('messages.update', [
|
|
940
|
-
{
|
|
941
|
-
key: message.key,
|
|
942
|
-
update: {
|
|
943
|
-
message: message.message
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
]);
|
|
1113
|
+
ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
|
|
947
1114
|
return message;
|
|
948
1115
|
},
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
caption,
|
|
966
|
-
...options
|
|
967
|
-
}, { quoted })
|
|
968
|
-
},
|
|
969
|
-
sendDocument: async (jid, document, caption, options, quoted = null) => {
|
|
970
|
-
return sendMessage(jid, {
|
|
971
|
-
document,
|
|
972
|
-
caption,
|
|
973
|
-
...options
|
|
974
|
-
}, { quoted })
|
|
975
|
-
},
|
|
976
|
-
sendAudio: async (jid, audio, options, quoted = null) => {
|
|
977
|
-
return sendMessage(jid, {
|
|
978
|
-
audio,
|
|
979
|
-
...options
|
|
980
|
-
}, { quoted })
|
|
981
|
-
},
|
|
982
|
-
sendLocation: async (jid, name, degreesLongitude, degreesLatitude, url, address, options, quoted = null) => {
|
|
983
|
-
return sendMessage(jid, {
|
|
984
|
-
location: {
|
|
985
|
-
degreesLongitude,
|
|
986
|
-
degreesLatitude,
|
|
987
|
-
name,
|
|
988
|
-
url,
|
|
989
|
-
address
|
|
990
|
-
},
|
|
991
|
-
...options
|
|
992
|
-
}, { quoted })
|
|
993
|
-
},
|
|
994
|
-
sendPoll: async (jid, name, pollVote = [], multiSelect = false, options, quoted = null) => {
|
|
995
|
-
const selectableCount = multiSelect ? pollVote.length : 1;
|
|
996
|
-
|
|
997
|
-
return sendMessage(jid, {
|
|
998
|
-
poll: {
|
|
999
|
-
name,
|
|
1000
|
-
values: pollVote,
|
|
1001
|
-
selectableCount
|
|
1002
|
-
},
|
|
1003
|
-
...options
|
|
1004
|
-
}, { quoted });
|
|
1005
|
-
},
|
|
1006
|
-
sendQuiz: (
|
|
1007
|
-
jid,
|
|
1008
|
-
name,
|
|
1009
|
-
pollVote = [],
|
|
1010
|
-
answer,
|
|
1011
|
-
options,
|
|
1012
|
-
quoted
|
|
1013
|
-
) => {
|
|
1014
|
-
const poll = {
|
|
1015
|
-
name,
|
|
1016
|
-
values: pollVote,
|
|
1017
|
-
selectableCount: 1,
|
|
1018
|
-
type: "QUIZ",
|
|
1019
|
-
answer: { optionName: answer }
|
|
1116
|
+
sendMessage: async (jid, content, options = {}) => {
|
|
1117
|
+
const userJid = authState.creds.me.id;
|
|
1118
|
+
const luki = new imup(Utils_1, waUploadToServer, relayMessage)
|
|
1119
|
+
const { quoted } = options;
|
|
1120
|
+
const messageType = luki.detectType(content);
|
|
1121
|
+
if (typeof content === 'object' &&
|
|
1122
|
+
'disappearingMessagesInChat' in content &&
|
|
1123
|
+
typeof content['disappearingMessagesInChat'] !== 'undefined' &&
|
|
1124
|
+
isJidGroup(jid)) {
|
|
1125
|
+
const { disappearingMessagesInChat } = content;
|
|
1126
|
+
const value = typeof disappearingMessagesInChat === 'boolean'
|
|
1127
|
+
? disappearingMessagesInChat
|
|
1128
|
+
? WA_DEFAULT_EPHEMERAL
|
|
1129
|
+
: 0
|
|
1130
|
+
: disappearingMessagesInChat;
|
|
1131
|
+
await groupToggleEphemeral(jid, value);
|
|
1020
1132
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
]
|
|
1133
|
+
else {
|
|
1134
|
+
if (messageType) {
|
|
1135
|
+
switch(messageType) {
|
|
1136
|
+
case 'PAYMENT':
|
|
1137
|
+
const paymentContent = await luki.handlePayment(content, quoted);
|
|
1138
|
+
return await relayMessage(jid, paymentContent, {
|
|
1139
|
+
messageId: Utils_1.generateMessageID()
|
|
1140
|
+
});
|
|
1141
|
+
case 'PRODUCT':
|
|
1142
|
+
const productContent = await luki.handleProduct(content, jid, quoted);
|
|
1143
|
+
const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
|
|
1144
|
+
return await relayMessage(jid, productMsg.message, {
|
|
1145
|
+
messageId: productMsg.key.id,
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
case 'INTERACTIVE':
|
|
1149
|
+
const interactiveContent = await luki.handleInteractive(content, jid, quoted);
|
|
1150
|
+
const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
|
|
1151
|
+
return await relayMessage(jid, interactiveMsg.message, {
|
|
1152
|
+
messageId: interactiveMsg.key.id
|
|
1153
|
+
});
|
|
1154
|
+
case 'ALBUM':
|
|
1155
|
+
return await luki.handleAlbum(content, jid, quoted)
|
|
1156
|
+
case 'EVENT':
|
|
1157
|
+
return await luki.handleEvent(content, jid, quoted)
|
|
1158
|
+
case 'POLL_RESULT':
|
|
1159
|
+
return await luki.handlePollResult(content, jid, quoted)
|
|
1160
|
+
case 'ORDER':
|
|
1161
|
+
return await luki.handleOrderMessage(content, jid, quoted)
|
|
1162
|
+
case 'GROUP_STATUS':
|
|
1163
|
+
return await luki.handleGroupStory(content, jid, quoted)
|
|
1164
|
+
case 'GROUP_LABEL':
|
|
1165
|
+
return await luki.handleGbLabel(content, jid)
|
|
1055
1166
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1167
|
+
}
|
|
1168
|
+
const fullMsg = await generateWAMessage(jid, content, {
|
|
1169
|
+
logger,
|
|
1170
|
+
userJid,
|
|
1171
|
+
getUrlInfo: text => getUrlInfo(text, {
|
|
1172
|
+
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1173
|
+
fetchOpts: {
|
|
1174
|
+
timeout: 3000,
|
|
1175
|
+
...(httpRequestOptions || {})
|
|
1176
|
+
},
|
|
1177
|
+
logger,
|
|
1178
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1179
|
+
}),
|
|
1180
|
+
//TODO: CACHE
|
|
1181
|
+
getProfilePicUrl: sock.profilePictureUrl,
|
|
1182
|
+
getCallLink: sock.createCallLink,
|
|
1183
|
+
upload: waUploadToServer,
|
|
1184
|
+
mediaCache: config.mediaCache,
|
|
1185
|
+
options: config.options,
|
|
1186
|
+
messageId: generateMessageIDV2(sock.user?.id),
|
|
1187
|
+
...options
|
|
1188
|
+
});
|
|
1189
|
+
const isEventMsg = 'event' in content && !!content.event;
|
|
1190
|
+
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
1191
|
+
const isEditMsg = 'edit' in content && !!content.edit;
|
|
1192
|
+
const isPinMsg = 'pin' in content && !!content.pin;
|
|
1193
|
+
const isPollMessage = 'poll' in content && !!content.poll;
|
|
1194
|
+
const additionalAttributes = {};
|
|
1195
|
+
const additionalNodes = [];
|
|
1196
|
+
// required for delete
|
|
1197
|
+
if (isDeleteMsg) {
|
|
1198
|
+
// if the chat is a group, and I am not the author, then delete the message as an admin
|
|
1199
|
+
if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
|
|
1200
|
+
additionalAttributes.edit = '8';
|
|
1067
1201
|
}
|
|
1202
|
+
else {
|
|
1203
|
+
additionalAttributes.edit = '7';
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
else if (isEditMsg) {
|
|
1207
|
+
additionalAttributes.edit = '1';
|
|
1068
1208
|
}
|
|
1209
|
+
else if (isPinMsg) {
|
|
1210
|
+
additionalAttributes.edit = '2';
|
|
1211
|
+
}
|
|
1212
|
+
else if (isPollMessage) {
|
|
1213
|
+
additionalNodes.push({
|
|
1214
|
+
tag: 'meta',
|
|
1215
|
+
attrs: {
|
|
1216
|
+
polltype: 'creation'
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
else if (isEventMsg) {
|
|
1221
|
+
additionalNodes.push({
|
|
1222
|
+
tag: 'meta',
|
|
1223
|
+
attrs: {
|
|
1224
|
+
event_type: 'creation'
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
await relayMessage(jid, fullMsg.message, {
|
|
1229
|
+
messageId: fullMsg.key.id,
|
|
1230
|
+
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1231
|
+
additionalAttributes,
|
|
1232
|
+
statusJidList: options.statusJidList,
|
|
1233
|
+
additionalNodes: aiLabel ? additionalNodes : options.additionalNodes
|
|
1234
|
+
});
|
|
1235
|
+
if (config.emitOwnEvents) {
|
|
1236
|
+
process.nextTick(async () => {
|
|
1237
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
return fullMsg;
|
|
1069
1241
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
messageId:
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1242
|
+
},
|
|
1243
|
+
sendMessageMembers: async (jid, message, options = {}) => {
|
|
1244
|
+
const {
|
|
1245
|
+
messageId: idm,
|
|
1246
|
+
quoted,
|
|
1247
|
+
delayMs = 1500,
|
|
1248
|
+
useUserDevicesCache = true,
|
|
1249
|
+
cachedGroupMetadata,
|
|
1250
|
+
onlyMember = true
|
|
1251
|
+
} = options;
|
|
1252
|
+
const { server } = jidDecode(jid);
|
|
1253
|
+
if (server !== "g.us") throw new Error("@g.us server required");
|
|
1254
|
+
const meId = authState.creds.me.id;
|
|
1255
|
+
const messages = Utils_1.normalizeMessageContent(message);
|
|
1256
|
+
const groupData = cachedGroupMetadata? await cachedGroupMetadata(jid) : await groupMetadata(jid);
|
|
1257
|
+
const isLid = groupData.addressingMode === "lid";
|
|
1258
|
+
const isAdmin = groupData.participants.filter((x) => x.admin !== null).map((y) => y.id)
|
|
1259
|
+
let participantJids = groupData.participants.map(z => z.id);
|
|
1260
|
+
if (onlyMember) {
|
|
1261
|
+
participantJids = isAdmin ? isAdmin : participantJids;
|
|
1262
|
+
}
|
|
1263
|
+
logger.info(`Sending message to ${participantJids.length} members from ${jid}`);
|
|
1264
|
+
for (let i = 0; i < participantJids.length; i++) {
|
|
1265
|
+
const jid = participantJids[i];
|
|
1266
|
+
if (areJidsSameUser(jid, meId)) continue;
|
|
1267
|
+
try {
|
|
1268
|
+
const msgId = `${idm || Utils_1.generateMessageID()}_${i}`;
|
|
1269
|
+
const fullMsg = await Utils_1.generateWAMessageFromContent(jid, message, {
|
|
1270
|
+
messageId: msgId,
|
|
1271
|
+
quoted
|
|
1272
|
+
})
|
|
1273
|
+
await relayMessage(jid, fullMsg.message, {
|
|
1274
|
+
messageId: fullMsg.key.id
|
|
1275
|
+
});
|
|
1276
|
+
logger.debug(`Message successfully sent to ${jid}`);
|
|
1277
|
+
if (delayMs && i < participantJids.length - 1) {
|
|
1278
|
+
await new Promise(z => setTimeout(z, delayMs));
|
|
1078
1279
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1280
|
+
} catch (e) {
|
|
1281
|
+
logger.error({ jid, e }, "Error sending message to");
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return JSON.stringify({
|
|
1285
|
+
members_total: participantJids.length,
|
|
1286
|
+
message
|
|
1287
|
+
}, null, 4);
|
|
1081
1288
|
}
|
|
1082
|
-
}
|
|
1289
|
+
};
|
|
1083
1290
|
};
|
|
1084
|
-
|
|
1291
|
+
//# sourceMappingURL=messages-send.js.map
|