@queenanya/baileys 9.5.4 → 9.5.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -1831
- package/WAProto/fix-imports.js +22 -18
- package/WAProto/index.js +22 -18
- package/lib/Defaults/index.d.ts +2 -0
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +3 -1
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Socket/business.d.ts +80 -3
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/chats.d.ts +9 -0
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +216 -58
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +80 -3
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/groups.d.ts +6 -0
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +6 -0
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +80 -3
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/messages-recv.d.ts +80 -3
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-recv.js +315 -60
- package/lib/Socket/messages-recv.js.map +1 -1
- package/lib/Socket/messages-send.d.ts +109 -3
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +170 -4
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/newsletter.d.ts +6 -0
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/newsletter.js +2 -2
- package/lib/Socket/newsletter.js.map +1 -1
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +3 -3
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Types/Auth.d.ts +1 -0
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Call.d.ts +1 -1
- package/lib/Types/Call.d.ts.map +1 -1
- package/lib/Types/Contact.d.ts +2 -0
- package/lib/Types/Contact.d.ts.map +1 -1
- package/lib/Types/Events.d.ts +18 -1
- package/lib/Types/Events.d.ts.map +1 -1
- package/lib/Types/GroupMetadata.d.ts +4 -0
- package/lib/Types/GroupMetadata.d.ts.map +1 -1
- package/lib/Types/Message.d.ts +360 -9
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js.map +1 -1
- package/lib/Types/Newsletter.d.ts +37 -42
- package/lib/Types/Newsletter.d.ts.map +1 -1
- package/lib/Types/Newsletter.js +18 -23
- package/lib/Types/Newsletter.js.map +1 -1
- package/lib/Types/State.d.ts +54 -0
- package/lib/Types/State.d.ts.map +1 -1
- package/lib/Types/State.js +42 -0
- package/lib/Types/State.js.map +1 -1
- package/lib/Utils/chat-utils.d.ts +30 -0
- package/lib/Utils/chat-utils.d.ts.map +1 -1
- package/lib/Utils/chat-utils.js +34 -8
- package/lib/Utils/chat-utils.js.map +1 -1
- package/lib/Utils/decode-wa-message.d.ts +12 -0
- package/lib/Utils/decode-wa-message.d.ts.map +1 -1
- package/lib/Utils/decode-wa-message.js +16 -0
- package/lib/Utils/decode-wa-message.js.map +1 -1
- package/lib/Utils/event-buffer.js +2 -0
- package/lib/Utils/event-buffer.js.map +1 -1
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +9 -0
- package/lib/Utils/generics.js.map +1 -1
- package/lib/Utils/history.d.ts.map +1 -1
- package/lib/Utils/history.js +12 -10
- package/lib/Utils/history.js.map +1 -1
- package/lib/Utils/identity-change-handler.d.ts +7 -0
- package/lib/Utils/identity-change-handler.d.ts.map +1 -1
- package/lib/Utils/identity-change-handler.js +1 -0
- package/lib/Utils/identity-change-handler.js.map +1 -1
- package/lib/Utils/index.d.ts +3 -0
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +3 -0
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/interactive-message.js.map +1 -1
- package/lib/Utils/message-composer.d.ts +5 -0
- package/lib/Utils/message-composer.d.ts.map +1 -0
- package/lib/Utils/message-composer.js +5 -0
- package/lib/Utils/message-composer.js.map +1 -0
- package/lib/Utils/message-retry-manager.js.map +1 -1
- package/lib/Utils/messages-media.d.ts +1 -1
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +2 -2
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +14 -5
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/noise-handler.js.map +1 -1
- package/lib/Utils/process-message.d.ts.map +1 -1
- package/lib/Utils/process-message.js +58 -2
- package/lib/Utils/process-message.js.map +1 -1
- package/lib/Utils/sync-action-utils.d.ts.map +1 -1
- package/lib/Utils/sync-action-utils.js +1 -0
- package/lib/Utils/sync-action-utils.js.map +1 -1
- package/lib/Utils/tc-token-utils.d.ts +26 -1
- package/lib/Utils/tc-token-utils.d.ts.map +1 -1
- package/lib/Utils/tc-token-utils.js +149 -4
- package/lib/Utils/tc-token-utils.js.map +1 -1
- package/lib/WABinary/jid-utils.js.map +1 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +1 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +1 -0
- package/lib/WAUSync/Protocols/index.d.ts +1 -0
- package/lib/WAUSync/Protocols/index.d.ts.map +1 -1
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/Protocols/index.js.map +1 -1
- package/lib/WAUSync/USyncQuery.d.ts +1 -0
- package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
- package/lib/WAUSync/USyncQuery.js +5 -1
- package/lib/WAUSync/USyncQuery.js.map +1 -1
- package/lib/WAUSync/USyncUser.d.ts +4 -0
- package/lib/WAUSync/USyncUser.d.ts.map +1 -1
- package/lib/WAUSync/USyncUser.js +8 -0
- package/lib/WAUSync/USyncUser.js.map +1 -1
- package/lib/addons/auto-reply.js.map +1 -1
- package/lib/addons/browser-presets.d.ts +16 -0
- package/lib/addons/browser-presets.d.ts.map +1 -0
- package/lib/addons/browser-presets.js +24 -0
- package/lib/addons/browser-presets.js.map +1 -0
- package/lib/addons/button-sender.d.ts +0 -2
- package/lib/addons/button-sender.d.ts.map +1 -1
- package/lib/addons/button-sender.js +21 -23
- package/lib/addons/button-sender.js.map +1 -1
- package/lib/addons/call-handler.d.ts.map +1 -1
- package/lib/addons/call-handler.js.map +1 -1
- package/lib/addons/from-messages-recv.d.ts.map +1 -1
- package/lib/addons/from-messages-recv.js.map +1 -1
- package/lib/addons/from-messages.js.map +1 -1
- package/lib/addons/index.d.ts +22 -5
- package/lib/addons/index.d.ts.map +1 -1
- package/lib/addons/index.js +29 -17
- package/lib/addons/index.js.map +1 -1
- package/lib/addons/interactive-message.js.map +1 -1
- package/lib/addons/jid-plot.d.ts +49 -0
- package/lib/addons/jid-plot.d.ts.map +1 -0
- package/lib/addons/jid-plot.js +84 -0
- package/lib/addons/jid-plot.js.map +1 -0
- package/lib/addons/lid-support.d.ts +41 -0
- package/lib/addons/lid-support.d.ts.map +1 -0
- package/lib/addons/lid-support.js +42 -0
- package/lib/addons/lid-support.js.map +1 -0
- package/lib/addons/message-composer.d.ts +142 -0
- package/lib/addons/message-composer.d.ts.map +1 -0
- package/lib/addons/message-composer.js +377 -0
- package/lib/addons/message-composer.js.map +1 -0
- package/lib/addons/message-scheduler.d.ts +77 -0
- package/lib/addons/message-scheduler.d.ts.map +1 -0
- package/lib/addons/message-scheduler.js +108 -0
- package/lib/addons/message-scheduler.js.map +1 -0
- package/lib/addons/message-utils.d.ts.map +1 -1
- package/lib/addons/message-utils.js.map +1 -1
- package/lib/addons/outgoing-calls.d.ts +64 -0
- package/lib/addons/outgoing-calls.d.ts.map +1 -0
- package/lib/addons/outgoing-calls.js +139 -0
- package/lib/addons/outgoing-calls.js.map +1 -0
- package/lib/addons/pairing-fix.d.ts +31 -0
- package/lib/addons/pairing-fix.d.ts.map +1 -0
- package/lib/addons/pairing-fix.js +74 -0
- package/lib/addons/pairing-fix.js.map +1 -0
- package/lib/addons/past-participants.d.ts +42 -0
- package/lib/addons/past-participants.d.ts.map +1 -0
- package/lib/addons/past-participants.js +41 -0
- package/lib/addons/past-participants.js.map +1 -0
- package/lib/addons/rich-response.d.ts +111 -0
- package/lib/addons/rich-response.d.ts.map +1 -0
- package/lib/addons/rich-response.js +152 -0
- package/lib/addons/rich-response.js.map +1 -0
- package/lib/addons/status-posting.d.ts.map +1 -1
- package/lib/addons/status-posting.js +1 -3
- package/lib/addons/status-posting.js.map +1 -1
- package/lib/addons/stickerpack.d.ts +37 -0
- package/lib/addons/stickerpack.d.ts.map +1 -0
- package/lib/addons/stickerpack.js +39 -0
- package/lib/addons/stickerpack.js.map +1 -0
- package/package.json +3 -3
|
@@ -4,16 +4,18 @@ import { randomBytes } from 'crypto';
|
|
|
4
4
|
import Long from 'long';
|
|
5
5
|
import { proto } from '../../WAProto/index.js';
|
|
6
6
|
import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT, PLACEHOLDER_MAX_AGE_SECONDS, STATUS_EXPIRY_SECONDS } from '../Defaults/index.js';
|
|
7
|
-
import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
|
|
8
|
-
import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, handleIdentityChange, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, toNumber, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
|
|
7
|
+
import { ReachoutTimelockEnforcementType, WAMessageStatus, WAMessageStubType } from '../Types/index.js';
|
|
8
|
+
import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, handleIdentityChange, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, SERVER_ERROR_CODES, toNumber, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
|
|
9
9
|
import { makeMutex } from '../Utils/make-mutex.js';
|
|
10
|
+
import { buildMergedTcTokenIndexWrite, isTcTokenExpired, readTcTokenIndex, resolveIssuanceJid, resolveTcTokenJid, storeTcTokensFromIqResult, TC_TOKEN_INDEX_KEY } from '../Utils/tc-token-utils.js';
|
|
10
11
|
import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
11
12
|
import { extractGroupMetadata } from './groups.js';
|
|
12
13
|
import { makeMessagesSocket } from './messages-send.js';
|
|
13
14
|
export const makeMessagesRecvSocket = (config) => {
|
|
14
15
|
const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
|
|
15
16
|
const sock = makeMessagesSocket(config);
|
|
16
|
-
const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, generateMessageTag, getUSyncDevices, createParticipantNodes, messageRetryManager } = sock;
|
|
17
|
+
const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, generateMessageTag, getUSyncDevices, createParticipantNodes, messageRetryManager, issuePrivacyTokens } = sock;
|
|
18
|
+
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
17
19
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
18
20
|
const retryMutex = makeMutex();
|
|
19
21
|
const msgRetryCache = config.msgRetryCounterCache ||
|
|
@@ -84,25 +86,145 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
84
86
|
}, 8000);
|
|
85
87
|
return sendPeerDataOperationMessage(pdoMessage);
|
|
86
88
|
};
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
const ENFORCEMENT_TYPE_VALUES = new Set(Object.values(ReachoutTimelockEnforcementType));
|
|
90
|
+
function isValidEnforcementType(value) {
|
|
91
|
+
return typeof value === 'string' && ENFORCEMENT_TYPE_VALUES.has(value);
|
|
92
|
+
}
|
|
93
|
+
// ── Top-level mex dispatcher (PR: feat-mex-notification-dispatch) ─────────
|
|
94
|
+
const handleMexNotification = async (node) => {
|
|
95
|
+
const updateNode = getBinaryNodeChild(node, 'update');
|
|
96
|
+
if (updateNode) {
|
|
97
|
+
const opName = updateNode.attrs?.op_name;
|
|
98
|
+
if (!opName) {
|
|
99
|
+
logger.warn({ node: binaryNodeToString(node) }, 'mex notification missing op_name');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let mexResponse;
|
|
103
|
+
try {
|
|
104
|
+
mexResponse = JSON.parse(updateNode.content.toString());
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
logger.error({ err: error, opName }, 'failed to parse mex notification JSON');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (mexResponse.errors?.length) {
|
|
111
|
+
logger.warn({ errors: mexResponse.errors, opName }, 'mex notification has GQL errors');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const data = mexResponse.data;
|
|
115
|
+
if (!data) {
|
|
116
|
+
logger.warn({ opName }, 'mex notification has null data');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
logger.debug({ opName }, 'processing mex notification');
|
|
120
|
+
switch (opName) {
|
|
121
|
+
case 'NotificationUserReachoutTimelockUpdate':
|
|
122
|
+
handleReachoutTimelockNotification(data);
|
|
123
|
+
break;
|
|
124
|
+
case 'MessageCappingInfoNotification':
|
|
125
|
+
handleMessageCappingNotification(data);
|
|
126
|
+
break;
|
|
127
|
+
case 'NotificationLinkedProfilesUpdates': {
|
|
128
|
+
// PR: fix-mex-linked-profiles
|
|
129
|
+
const linkedProfiles = data.xwa2_notify_linked_profiles;
|
|
130
|
+
if (!linkedProfiles)
|
|
131
|
+
break;
|
|
132
|
+
const lid = linkedProfiles.jid;
|
|
133
|
+
for (const profile of linkedProfiles.added_profiles ?? []) {
|
|
134
|
+
const pn = typeof profile === 'string' ? profile : (profile?.pn ?? profile?.jid ?? null);
|
|
135
|
+
if (lid && pn)
|
|
136
|
+
ev.emit('lid-mapping.update', { lid, pn });
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
// newsletter ops still use the legacy <mex> child structure
|
|
141
|
+
case 'NotificationNewsletterUpdate':
|
|
142
|
+
case 'NotificationNewsletterAdminPromote':
|
|
143
|
+
case 'NotificationNewsletterAdminDemote':
|
|
144
|
+
case 'NotificationNewsletterUserSettingChange':
|
|
145
|
+
case 'NotificationNewsletterJoin':
|
|
146
|
+
case 'NotificationNewsletterLeave':
|
|
147
|
+
case 'NotificationNewsletterStateChange':
|
|
148
|
+
case 'NotificationNewsletterAdminMetadataUpdate':
|
|
149
|
+
case 'NotificationNewsletterOwnerUpdate':
|
|
150
|
+
case 'NotificationNewsletterAdminInviteRevoke':
|
|
151
|
+
case 'NotificationNewsletterWamoSubStatusChange':
|
|
152
|
+
case 'NotificationNewsletterBlockUser':
|
|
153
|
+
case 'NotificationNewsletterPaidPartnership':
|
|
154
|
+
case 'NotificationNewsletterMilestone':
|
|
155
|
+
case 'NewsletterResponseStateUpdate':
|
|
156
|
+
await handleLegacyMexNewsletterNotification(node);
|
|
157
|
+
break;
|
|
158
|
+
default:
|
|
159
|
+
logger.debug({ opName }, 'unhandled mex notification');
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
await handleLegacyMexNewsletterNotification(node);
|
|
165
|
+
};
|
|
166
|
+
const handleReachoutTimelockNotification = (data) => {
|
|
167
|
+
const payload = data.xwa2_notify_account_reachout_timelock;
|
|
168
|
+
if (!payload) {
|
|
169
|
+
logger.warn('reachout timelock notification missing payload');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (!payload.is_active) {
|
|
173
|
+
logger.info('reachout timelock restriction lifted');
|
|
174
|
+
ev.emit('connection.update', {
|
|
175
|
+
reachoutTimeLock: {
|
|
176
|
+
isActive: false,
|
|
177
|
+
enforcementType: ReachoutTimelockEnforcementType.DEFAULT
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// WA Web defaults to now+60s when the server omits the expiry
|
|
183
|
+
const timeEnforcementEnds = payload.time_enforcement_ends
|
|
184
|
+
? new Date(parseInt(payload.time_enforcement_ends, 10) * 1000)
|
|
185
|
+
: new Date(Date.now() + 60000);
|
|
186
|
+
const enforcementType = isValidEnforcementType(payload.enforcement_type)
|
|
187
|
+
? payload.enforcement_type
|
|
188
|
+
: ReachoutTimelockEnforcementType.DEFAULT;
|
|
189
|
+
logger.info({ enforcementType, timeEnforcementEnds }, 'reachout timelock restriction set');
|
|
190
|
+
ev.emit('connection.update', {
|
|
191
|
+
reachoutTimeLock: {
|
|
192
|
+
isActive: true,
|
|
193
|
+
timeEnforcementEnds,
|
|
194
|
+
enforcementType
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
const handleMessageCappingNotification = (data) => {
|
|
199
|
+
const payload = data.xwa2_notify_new_chat_messages_capping_info_update;
|
|
200
|
+
if (!payload) {
|
|
201
|
+
logger.warn('message capping notification missing payload');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
logger.info({ payload }, 'received message capping update');
|
|
205
|
+
ev.emit('message-capping.update', payload);
|
|
206
|
+
};
|
|
207
|
+
// ── Legacy mex newsletter notification handler ────────────────────────────
|
|
208
|
+
const handleLegacyMexNewsletterNotification = async (node) => {
|
|
89
209
|
const mexNode = getBinaryNodeChild(node, 'mex');
|
|
90
210
|
if (!mexNode?.content) {
|
|
91
|
-
logger.warn({ node }, '
|
|
211
|
+
logger.warn({ node: binaryNodeToString(node) }, 'invalid mex newsletter notification');
|
|
92
212
|
return;
|
|
93
213
|
}
|
|
94
|
-
let
|
|
214
|
+
let parsed;
|
|
95
215
|
try {
|
|
96
|
-
|
|
216
|
+
// PR fix-mex-linked-profiles: handle binary-encoded content correctly
|
|
217
|
+
const payloadContent = mexNode.content;
|
|
218
|
+
const contentBuf = typeof payloadContent === 'string' ? Buffer.from(payloadContent, 'binary') : Buffer.from(payloadContent);
|
|
219
|
+
parsed = JSON.parse(contentBuf.toString());
|
|
97
220
|
}
|
|
98
221
|
catch (error) {
|
|
99
|
-
logger.error({ err: error, node }, '
|
|
222
|
+
logger.error({ err: error, node: binaryNodeToString(node) }, 'failed to parse mex newsletter notification');
|
|
100
223
|
return;
|
|
101
224
|
}
|
|
102
|
-
const operation =
|
|
103
|
-
const updates = data?.updates;
|
|
225
|
+
const { operation, updates } = parsed;
|
|
104
226
|
if (!updates || !operation) {
|
|
105
|
-
logger.warn({
|
|
227
|
+
logger.warn({ parsed }, 'invalid mex newsletter notification content');
|
|
106
228
|
return;
|
|
107
229
|
}
|
|
108
230
|
logger.info({ operation, updates }, 'got mex newsletter notification');
|
|
@@ -131,7 +253,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
131
253
|
}
|
|
132
254
|
break;
|
|
133
255
|
default:
|
|
134
|
-
logger.info({ operation,
|
|
256
|
+
logger.info({ operation, parsed }, 'unhandled mex newsletter notification');
|
|
135
257
|
break;
|
|
136
258
|
}
|
|
137
259
|
};
|
|
@@ -664,7 +786,8 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
664
786
|
validateSession: signalRepository.validateSession,
|
|
665
787
|
assertSessions,
|
|
666
788
|
debounceCache: identityAssertDebounce,
|
|
667
|
-
logger
|
|
789
|
+
logger,
|
|
790
|
+
onBeforeSessionRefresh: reissueTcTokenAfterIdentityChange
|
|
668
791
|
});
|
|
669
792
|
if (result.action === 'no_identity_node') {
|
|
670
793
|
logger.info({ node }, 'unknown encrypt notification');
|
|
@@ -675,6 +798,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
675
798
|
// TODO: Support PN/LID (Here is only LID now)
|
|
676
799
|
const actingParticipantLid = fullNode.attrs.participant;
|
|
677
800
|
const actingParticipantPn = fullNode.attrs.participant_pn;
|
|
801
|
+
const actingParticipantUsername = fullNode.attrs.participant_username;
|
|
678
802
|
const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
|
|
679
803
|
const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
|
|
680
804
|
switch (child?.tag) {
|
|
@@ -694,7 +818,8 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
694
818
|
{
|
|
695
819
|
...metadata,
|
|
696
820
|
author: actingParticipantLid,
|
|
697
|
-
authorPn: actingParticipantPn
|
|
821
|
+
authorPn: actingParticipantPn,
|
|
822
|
+
authorUsername: actingParticipantUsername
|
|
698
823
|
}
|
|
699
824
|
]);
|
|
700
825
|
break;
|
|
@@ -804,7 +929,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
804
929
|
await handleNewsletterNotification(node);
|
|
805
930
|
break;
|
|
806
931
|
case 'mex':
|
|
807
|
-
await
|
|
932
|
+
await handleMexNotification(node);
|
|
808
933
|
break;
|
|
809
934
|
case 'w:gp2':
|
|
810
935
|
// TODO: HANDLE PARTICIPANT_PN
|
|
@@ -950,27 +1075,91 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
950
1075
|
return result;
|
|
951
1076
|
}
|
|
952
1077
|
};
|
|
1078
|
+
/**
|
|
1079
|
+
* In-memory cache of storage JIDs with stored tctokens, seeded from the persisted index.
|
|
1080
|
+
*/
|
|
1081
|
+
const tcTokenKnownJids = new Set();
|
|
1082
|
+
const tcTokenIndexLoaded = (async () => {
|
|
1083
|
+
try {
|
|
1084
|
+
const jids = await readTcTokenIndex(authState.keys);
|
|
1085
|
+
for (const jid of jids)
|
|
1086
|
+
tcTokenKnownJids.add(jid);
|
|
1087
|
+
logger.debug({ count: tcTokenKnownJids.size }, 'loaded tctoken index');
|
|
1088
|
+
}
|
|
1089
|
+
catch (err) {
|
|
1090
|
+
logger.warn({ err: err?.message }, 'failed to load tctoken index');
|
|
1091
|
+
}
|
|
1092
|
+
})();
|
|
1093
|
+
let tcTokenIndexTimer;
|
|
1094
|
+
async function flushTcTokenIndex() {
|
|
1095
|
+
if (tcTokenIndexTimer) {
|
|
1096
|
+
clearTimeout(tcTokenIndexTimer);
|
|
1097
|
+
tcTokenIndexTimer = undefined;
|
|
1098
|
+
}
|
|
1099
|
+
const write = await buildMergedTcTokenIndexWrite(authState.keys, tcTokenKnownJids);
|
|
1100
|
+
return authState.keys.set({ tctoken: write });
|
|
1101
|
+
}
|
|
1102
|
+
function scheduleTcTokenIndexSave() {
|
|
1103
|
+
if (tcTokenIndexTimer) {
|
|
1104
|
+
clearTimeout(tcTokenIndexTimer);
|
|
1105
|
+
}
|
|
1106
|
+
tcTokenIndexTimer = setTimeout(() => {
|
|
1107
|
+
tcTokenIndexTimer = undefined;
|
|
1108
|
+
flushTcTokenIndex().catch(err => {
|
|
1109
|
+
logger.warn({ err: err?.message }, 'failed to save tctoken index');
|
|
1110
|
+
});
|
|
1111
|
+
}, 5000);
|
|
1112
|
+
}
|
|
1113
|
+
function trackTcTokenJid(jid) {
|
|
1114
|
+
if (jid && jid !== TC_TOKEN_INDEX_KEY && !tcTokenKnownJids.has(jid)) {
|
|
1115
|
+
tcTokenKnownJids.add(jid);
|
|
1116
|
+
scheduleTcTokenIndexSave();
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
953
1119
|
const handlePrivacyTokenNotification = async (node) => {
|
|
954
1120
|
const tokensNode = getBinaryNodeChild(node, 'tokens');
|
|
955
|
-
const from = jidNormalizedUser(node.attrs.from);
|
|
956
1121
|
if (!tokensNode)
|
|
957
1122
|
return;
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1123
|
+
const from = jidNormalizedUser(node.attrs.from);
|
|
1124
|
+
const senderLid = node.attrs.sender_lid && isLidUser(jidNormalizedUser(node.attrs.sender_lid))
|
|
1125
|
+
? jidNormalizedUser(node.attrs.sender_lid)
|
|
1126
|
+
: undefined;
|
|
1127
|
+
const fallbackJid = senderLid ?? (await resolveTcTokenJid(from, getLIDForPN));
|
|
1128
|
+
logger.debug({ from, storageJid: fallbackJid }, 'processing privacy token notification');
|
|
1129
|
+
await storeTcTokensFromIqResult({
|
|
1130
|
+
result: node,
|
|
1131
|
+
fallbackJid,
|
|
1132
|
+
keys: authState.keys,
|
|
1133
|
+
getLIDForPN,
|
|
1134
|
+
onNewJidStored: trackTcTokenJid
|
|
1135
|
+
});
|
|
1136
|
+
};
|
|
1137
|
+
/**
|
|
1138
|
+
* Fire-and-forget tctoken re-issuance after a peer's device identity changed.
|
|
1139
|
+
*/
|
|
1140
|
+
const reissueTcTokenAfterIdentityChange = (from) => {
|
|
1141
|
+
void (async () => {
|
|
1142
|
+
const normalizedJid = jidNormalizedUser(from);
|
|
1143
|
+
const tcJid = await resolveTcTokenJid(normalizedJid, getLIDForPN);
|
|
1144
|
+
const tcTokenData = await authState.keys.get('tctoken', [tcJid]);
|
|
1145
|
+
const senderTs = tcTokenData?.[tcJid]?.senderTimestamp;
|
|
1146
|
+
if (senderTs === null || senderTs === undefined || isTcTokenExpired(senderTs)) {
|
|
1147
|
+
return;
|
|
972
1148
|
}
|
|
973
|
-
|
|
1149
|
+
logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken');
|
|
1150
|
+
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
|
|
1151
|
+
const issueJid = await resolveIssuanceJid(normalizedJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
|
|
1152
|
+
const result = await issuePrivacyTokens([issueJid], senderTs);
|
|
1153
|
+
await storeTcTokensFromIqResult({
|
|
1154
|
+
result,
|
|
1155
|
+
fallbackJid: tcJid,
|
|
1156
|
+
keys: authState.keys,
|
|
1157
|
+
getLIDForPN,
|
|
1158
|
+
onNewJidStored: trackTcTokenJid
|
|
1159
|
+
});
|
|
1160
|
+
})().catch(err => {
|
|
1161
|
+
logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change');
|
|
1162
|
+
});
|
|
974
1163
|
};
|
|
975
1164
|
async function decipherLinkPublicKey(data) {
|
|
976
1165
|
const buffer = toRequiredBuffer(data);
|
|
@@ -1437,23 +1626,22 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1437
1626
|
};
|
|
1438
1627
|
const handleBadAck = async ({ attrs }) => {
|
|
1439
1628
|
const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id };
|
|
1440
|
-
// WARNING: REFRAIN FROM ENABLING THIS FOR NOW. IT WILL CAUSE A LOOP
|
|
1441
|
-
// // current hypothesis is that if pash is sent in the ack
|
|
1442
|
-
// // it means -- the message hasn't reached all devices yet
|
|
1443
|
-
// // we'll retry sending the message here
|
|
1444
|
-
// if(attrs.phash) {
|
|
1445
|
-
// logger.info({ attrs }, 'received phash in ack, resending message...')
|
|
1446
|
-
// const msg = await getMessage(key)
|
|
1447
|
-
// if(msg) {
|
|
1448
|
-
// await relayMessage(key.remoteJid!, msg, { messageId: key.id!, useUserDevicesCache: false })
|
|
1449
|
-
// } else {
|
|
1450
|
-
// logger.warn({ attrs }, 'could not send message again, as it was not found')
|
|
1451
|
-
// }
|
|
1452
|
-
// }
|
|
1453
1629
|
// error in acknowledgement,
|
|
1454
1630
|
// device could not display the message
|
|
1455
1631
|
if (attrs.error) {
|
|
1456
|
-
|
|
1632
|
+
if (attrs.error === SERVER_ERROR_CODES.MissingTcToken) {
|
|
1633
|
+
// 463 = account restricted + no tctoken for this contact.
|
|
1634
|
+
// WA Web prevents this client-side (disables compose bar).
|
|
1635
|
+
// No retry — retrying worsens the restriction by counting
|
|
1636
|
+
// as another "reach out" to an unknown contact.
|
|
1637
|
+
logger.warn({ msgId: attrs.id, from: attrs.from }, 'error 463: account restricted or missing tctoken for contact');
|
|
1638
|
+
}
|
|
1639
|
+
else if (attrs.error === SERVER_ERROR_CODES.SmaxInvalid) {
|
|
1640
|
+
logger.warn({ msgId: attrs.id, from: attrs.from }, 'smax-invalid (479): stanza rejected by server — likely stale device session or malformed addressing');
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
logger.warn({ attrs }, 'received error in ack');
|
|
1644
|
+
}
|
|
1457
1645
|
ev.emit('messages.update', [
|
|
1458
1646
|
{
|
|
1459
1647
|
key,
|
|
@@ -1463,19 +1651,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1463
1651
|
}
|
|
1464
1652
|
}
|
|
1465
1653
|
]);
|
|
1466
|
-
// resend the message with device_fanout=false, use at your own risk
|
|
1467
|
-
// if (attrs.error === '475') {
|
|
1468
|
-
// const msg = await getMessage(key)
|
|
1469
|
-
// if (msg) {
|
|
1470
|
-
// await relayMessage(key.remoteJid!, msg, {
|
|
1471
|
-
// messageId: key.id!,
|
|
1472
|
-
// useUserDevicesCache: false,
|
|
1473
|
-
// additionalAttributes: {
|
|
1474
|
-
// device_fanout: 'false'
|
|
1475
|
-
// }
|
|
1476
|
-
// })
|
|
1477
|
-
// }
|
|
1478
|
-
// }
|
|
1479
1654
|
}
|
|
1480
1655
|
};
|
|
1481
1656
|
/// processes a node with the given function
|
|
@@ -1590,12 +1765,92 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1590
1765
|
await upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
|
|
1591
1766
|
}
|
|
1592
1767
|
});
|
|
1593
|
-
|
|
1768
|
+
/** timestamp of last tctoken prune run — throttles to once per 24h */
|
|
1769
|
+
let lastTcTokenPruneTs = 0;
|
|
1770
|
+
ev.on('connection.update', ({ isOnline, connection }) => {
|
|
1594
1771
|
if (typeof isOnline !== 'undefined') {
|
|
1595
1772
|
sendActiveReceipts = isOnline;
|
|
1596
1773
|
logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
|
|
1597
1774
|
}
|
|
1775
|
+
// Flush pending tctoken index save on disconnect to avoid writing after close
|
|
1776
|
+
if (connection === 'close' && tcTokenIndexTimer) {
|
|
1777
|
+
clearTimeout(tcTokenIndexTimer);
|
|
1778
|
+
tcTokenIndexTimer = undefined;
|
|
1779
|
+
try {
|
|
1780
|
+
void Promise.resolve(flushTcTokenIndex()).catch(() => { });
|
|
1781
|
+
}
|
|
1782
|
+
catch {
|
|
1783
|
+
/* ignore sync errors */
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
// Prune expired tctokens when coming online, at most once per 24 hours
|
|
1787
|
+
if (isOnline) {
|
|
1788
|
+
const now = Date.now();
|
|
1789
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
1790
|
+
if (now - lastTcTokenPruneTs >= DAY_MS) {
|
|
1791
|
+
lastTcTokenPruneTs = now;
|
|
1792
|
+
void pruneExpiredTcTokens();
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1598
1795
|
});
|
|
1796
|
+
async function pruneExpiredTcTokens() {
|
|
1797
|
+
try {
|
|
1798
|
+
await tcTokenIndexLoaded;
|
|
1799
|
+
const persisted = await readTcTokenIndex(authState.keys);
|
|
1800
|
+
const allJids = new Set(tcTokenKnownJids);
|
|
1801
|
+
for (const jid of persisted)
|
|
1802
|
+
allJids.add(jid);
|
|
1803
|
+
if (!allJids.size)
|
|
1804
|
+
return;
|
|
1805
|
+
const jids = [...allJids];
|
|
1806
|
+
const allTokens = await authState.keys.get('tctoken', jids);
|
|
1807
|
+
const writes = {};
|
|
1808
|
+
const survivors = new Set();
|
|
1809
|
+
let mutated = 0;
|
|
1810
|
+
for (const jid of jids) {
|
|
1811
|
+
const entry = allTokens[jid];
|
|
1812
|
+
if (!entry) {
|
|
1813
|
+
mutated++;
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
const hasPeerToken = !!entry.token?.length;
|
|
1817
|
+
const peerTokenExpired = hasPeerToken && isTcTokenExpired(entry.timestamp);
|
|
1818
|
+
const hasSenderTs = entry.senderTimestamp !== undefined;
|
|
1819
|
+
const senderTsExpired = hasSenderTs && isTcTokenExpired(entry.senderTimestamp);
|
|
1820
|
+
const keepPeerToken = hasPeerToken && !peerTokenExpired;
|
|
1821
|
+
const keepSenderTs = hasSenderTs && !senderTsExpired;
|
|
1822
|
+
if (!keepPeerToken && !keepSenderTs) {
|
|
1823
|
+
writes[jid] = null;
|
|
1824
|
+
mutated++;
|
|
1825
|
+
}
|
|
1826
|
+
else if (peerTokenExpired && keepSenderTs) {
|
|
1827
|
+
writes[jid] = { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp };
|
|
1828
|
+
survivors.add(jid);
|
|
1829
|
+
mutated++;
|
|
1830
|
+
}
|
|
1831
|
+
else {
|
|
1832
|
+
survivors.add(jid);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
if (mutated === 0)
|
|
1836
|
+
return;
|
|
1837
|
+
await authState.keys.set({
|
|
1838
|
+
tctoken: {
|
|
1839
|
+
...writes,
|
|
1840
|
+
[TC_TOKEN_INDEX_KEY]: {
|
|
1841
|
+
token: Buffer.from(JSON.stringify([...survivors]))
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
tcTokenKnownJids.clear();
|
|
1846
|
+
for (const jid of survivors)
|
|
1847
|
+
tcTokenKnownJids.add(jid);
|
|
1848
|
+
logger.debug({ mutated, remaining: survivors.size }, 'pruned expired tctokens');
|
|
1849
|
+
}
|
|
1850
|
+
catch (err) {
|
|
1851
|
+
logger.warn({ err: err?.message }, 'failed to prune expired tctokens');
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1599
1854
|
return {
|
|
1600
1855
|
...sock,
|
|
1601
1856
|
sendMessageAck,
|