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