@periskope/baileys 7.0.0-alpha-4 → 7.0.0-beta-1
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 +0 -6
- package/lib/Defaults/index.d.ts +1 -2
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +2 -3
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Signal/Group/group_cipher.d.ts +1 -0
- package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
- package/lib/Signal/Group/group_cipher.js +37 -28
- package/lib/Signal/Group/group_cipher.js.map +1 -1
- package/lib/Signal/Group/keyhelper.d.ts.map +1 -1
- package/lib/Signal/Group/keyhelper.js +1 -0
- package/lib/Signal/Group/keyhelper.js.map +1 -1
- package/lib/Signal/Group/queue-job.d.ts +1 -0
- package/lib/Signal/Group/queue-job.d.ts.map +1 -1
- package/lib/Signal/Group/queue-job.js +3 -0
- package/lib/Signal/Group/queue-job.js.map +1 -1
- package/lib/Signal/Group/sender-chain-key.d.ts +1 -1
- package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -1
- package/lib/Signal/Group/sender-chain-key.js +3 -9
- package/lib/Signal/Group/sender-chain-key.js.map +1 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-message.js +1 -0
- package/lib/Signal/Group/sender-key-message.js.map +1 -1
- package/lib/Signal/Group/sender-key-state.d.ts +4 -4
- package/lib/Signal/Group/sender-key-state.d.ts.map +1 -1
- package/lib/Signal/Group/sender-key-state.js +24 -47
- package/lib/Signal/Group/sender-key-state.js.map +1 -1
- package/lib/Signal/Group/sender-message-key.d.ts.map +1 -1
- package/lib/Signal/Group/sender-message-key.js +1 -0
- package/lib/Signal/Group/sender-message-key.js.map +1 -1
- package/lib/Signal/libsignal.d.ts +1 -5
- package/lib/Signal/libsignal.d.ts.map +1 -1
- package/lib/Signal/libsignal.js +38 -70
- package/lib/Signal/libsignal.js.map +1 -1
- package/lib/Signal/lid-mapping.d.ts +1 -13
- package/lib/Signal/lid-mapping.d.ts.map +1 -1
- package/lib/Signal/lid-mapping.js +25 -45
- package/lib/Signal/lid-mapping.js.map +1 -1
- package/lib/Socket/business.d.ts +6 -7
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/chats.d.ts +6 -6
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +19 -15
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +6 -7
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/groups.d.ts +6 -6
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +10 -12
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +6 -7
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/messages-recv.d.ts +6 -7
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-recv.js +254 -354
- package/lib/Socket/messages-recv.js.map +1 -1
- package/lib/Socket/messages-send.d.ts +6 -8
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +21 -40
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/newsletter.d.ts +9 -8
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/newsletter.js +1 -3
- package/lib/Socket/newsletter.js.map +1 -1
- package/lib/Socket/socket.d.ts +0 -7
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +50 -124
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Socket/usync.d.ts +2 -2
- package/lib/Types/Auth.d.ts +1 -1
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Chat.d.ts +0 -2
- package/lib/Types/Chat.d.ts.map +1 -1
- package/lib/Types/Contact.d.ts +4 -4
- package/lib/Types/Contact.d.ts.map +1 -1
- package/lib/Types/Events.d.ts +2 -2
- package/lib/Types/Events.d.ts.map +1 -1
- package/lib/Types/GroupMetadata.d.ts +4 -6
- package/lib/Types/GroupMetadata.d.ts.map +1 -1
- package/lib/Types/Message.d.ts +6 -7
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js +1 -5
- package/lib/Types/Message.js.map +1 -1
- package/lib/Types/Signal.d.ts +0 -4
- package/lib/Types/Signal.d.ts.map +1 -1
- package/lib/Types/Socket.d.ts +6 -22
- package/lib/Types/Socket.d.ts.map +1 -1
- package/lib/Utils/auth-utils.d.ts.map +1 -1
- package/lib/Utils/auth-utils.js +79 -363
- package/lib/Utils/auth-utils.js.map +1 -1
- package/lib/Utils/chat-utils.d.ts +2 -2
- package/lib/Utils/chat-utils.d.ts.map +1 -1
- package/lib/Utils/chat-utils.js +2 -2
- package/lib/Utils/chat-utils.js.map +1 -1
- package/lib/Utils/crypto.d.ts +3 -3
- package/lib/Utils/crypto.d.ts.map +1 -1
- package/lib/Utils/crypto.js +12 -16
- package/lib/Utils/crypto.js.map +1 -1
- package/lib/Utils/decode-wa-message.d.ts +0 -5
- package/lib/Utils/decode-wa-message.d.ts.map +1 -1
- package/lib/Utils/decode-wa-message.js +27 -50
- package/lib/Utils/decode-wa-message.js.map +1 -1
- package/lib/Utils/event-buffer.d.ts +1 -0
- package/lib/Utils/event-buffer.d.ts.map +1 -1
- package/lib/Utils/event-buffer.js +4 -48
- package/lib/Utils/event-buffer.js.map +1 -1
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +8 -17
- package/lib/Utils/generics.js.map +1 -1
- package/lib/Utils/history.d.ts.map +1 -1
- package/lib/Utils/history.js +2 -1
- package/lib/Utils/history.js.map +1 -1
- package/lib/Utils/index.d.ts +0 -1
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +0 -1
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/messages-media.d.ts +2 -3
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +3 -3
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/process-message.d.ts +2 -3
- package/lib/Utils/process-message.d.ts.map +1 -1
- package/lib/Utils/process-message.js +3 -14
- package/lib/Utils/process-message.js.map +1 -1
- package/lib/Utils/validate-connection.d.ts.map +1 -1
- package/lib/Utils/validate-connection.js +2 -3
- package/lib/Utils/validate-connection.js.map +1 -1
- package/lib/WABinary/jid-utils.d.ts +5 -6
- package/lib/WABinary/jid-utils.d.ts.map +1 -1
- package/lib/WABinary/jid-utils.js +5 -11
- package/lib/WABinary/jid-utils.js.map +1 -1
- package/lib/WAM/encode.d.ts.map +1 -1
- package/lib/WAM/encode.js +1 -0
- package/lib/WAM/encode.js.map +1 -1
- package/package.json +8 -10
|
@@ -7,13 +7,13 @@ import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaul
|
|
|
7
7
|
import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
|
|
8
8
|
import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
|
|
9
9
|
import { makeMutex } from '../Utils/make-mutex.js';
|
|
10
|
-
import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
10
|
+
import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isJidUser, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
11
11
|
import { extractGroupMetadata } from './groups.js';
|
|
12
12
|
import { makeMessagesSocket } from './messages-send.js';
|
|
13
13
|
export const makeMessagesRecvSocket = (config) => {
|
|
14
|
-
const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid
|
|
14
|
+
const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid } = config;
|
|
15
15
|
const sock = makeMessagesSocket(config);
|
|
16
|
-
const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage
|
|
16
|
+
const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage } = sock;
|
|
17
17
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
18
18
|
const retryMutex = makeMutex();
|
|
19
19
|
const msgRetryCache = config.msgRetryCounterCache ||
|
|
@@ -32,187 +32,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
32
32
|
useClones: false
|
|
33
33
|
});
|
|
34
34
|
let sendActiveReceipts = false;
|
|
35
|
-
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
36
|
-
if (!authState.creds.me?.id) {
|
|
37
|
-
throw new Boom('Not authenticated');
|
|
38
|
-
}
|
|
39
|
-
const pdoMessage = {
|
|
40
|
-
historySyncOnDemandRequest: {
|
|
41
|
-
chatJid: oldestMsgKey.remoteJid,
|
|
42
|
-
oldestMsgFromMe: oldestMsgKey.fromMe,
|
|
43
|
-
oldestMsgId: oldestMsgKey.id,
|
|
44
|
-
oldestMsgTimestampMs: oldestMsgTimestamp,
|
|
45
|
-
onDemandMsgCount: count
|
|
46
|
-
},
|
|
47
|
-
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
|
|
48
|
-
};
|
|
49
|
-
return sendPeerDataOperationMessage(pdoMessage);
|
|
50
|
-
};
|
|
51
|
-
const requestPlaceholderResend = async (messageKey) => {
|
|
52
|
-
if (!authState.creds.me?.id) {
|
|
53
|
-
throw new Boom('Not authenticated');
|
|
54
|
-
}
|
|
55
|
-
if (placeholderResendCache.get(messageKey?.id)) {
|
|
56
|
-
logger.debug({ messageKey }, 'already requested resend');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
placeholderResendCache.set(messageKey?.id, true);
|
|
61
|
-
}
|
|
62
|
-
await delay(5000);
|
|
63
|
-
if (!placeholderResendCache.get(messageKey?.id)) {
|
|
64
|
-
logger.debug({ messageKey }, 'message received while resend requested');
|
|
65
|
-
return 'RESOLVED';
|
|
66
|
-
}
|
|
67
|
-
const pdoMessage = {
|
|
68
|
-
placeholderMessageResendRequest: [
|
|
69
|
-
{
|
|
70
|
-
messageKey
|
|
71
|
-
}
|
|
72
|
-
],
|
|
73
|
-
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
74
|
-
};
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
if (placeholderResendCache.get(messageKey?.id)) {
|
|
77
|
-
logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
|
|
78
|
-
placeholderResendCache.del(messageKey?.id);
|
|
79
|
-
}
|
|
80
|
-
}, 15000);
|
|
81
|
-
return sendPeerDataOperationMessage(pdoMessage);
|
|
82
|
-
};
|
|
83
|
-
// Handles mex newsletter notifications
|
|
84
|
-
const handleMexNewsletterNotification = async (node) => {
|
|
85
|
-
const mexNode = getBinaryNodeChild(node, 'mex');
|
|
86
|
-
if (!mexNode?.content) {
|
|
87
|
-
logger.warn({ node }, 'Invalid mex newsletter notification');
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
let data;
|
|
91
|
-
try {
|
|
92
|
-
data = JSON.parse(mexNode.content.toString());
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const operation = data?.operation;
|
|
99
|
-
const updates = data?.updates;
|
|
100
|
-
if (!updates || !operation) {
|
|
101
|
-
logger.warn({ data }, 'Invalid mex newsletter notification content');
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
logger.info({ operation, updates }, 'got mex newsletter notification');
|
|
105
|
-
switch (operation) {
|
|
106
|
-
case 'NotificationNewsletterUpdate':
|
|
107
|
-
for (const update of updates) {
|
|
108
|
-
if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
|
|
109
|
-
ev.emit('newsletter-settings.update', {
|
|
110
|
-
id: update.jid,
|
|
111
|
-
update: update.settings
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
break;
|
|
116
|
-
case 'NotificationNewsletterAdminPromote':
|
|
117
|
-
for (const update of updates) {
|
|
118
|
-
if (update.jid && update.user) {
|
|
119
|
-
ev.emit('newsletter-participants.update', {
|
|
120
|
-
id: update.jid,
|
|
121
|
-
author: node.attrs.from,
|
|
122
|
-
user: update.user,
|
|
123
|
-
new_role: 'ADMIN',
|
|
124
|
-
action: 'promote'
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
break;
|
|
129
|
-
default:
|
|
130
|
-
logger.info({ operation, data }, 'Unhandled mex newsletter notification');
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
// Handles newsletter notifications
|
|
135
|
-
const handleNewsletterNotification = async (node) => {
|
|
136
|
-
const from = node.attrs.from;
|
|
137
|
-
const child = getAllBinaryNodeChildren(node)[0];
|
|
138
|
-
const author = node.attrs.participant;
|
|
139
|
-
logger.info({ from, child }, 'got newsletter notification');
|
|
140
|
-
switch (child.tag) {
|
|
141
|
-
case 'reaction':
|
|
142
|
-
const reactionUpdate = {
|
|
143
|
-
id: from,
|
|
144
|
-
server_id: child.attrs.message_id,
|
|
145
|
-
reaction: {
|
|
146
|
-
code: getBinaryNodeChildString(child, 'reaction'),
|
|
147
|
-
count: 1
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
ev.emit('newsletter.reaction', reactionUpdate);
|
|
151
|
-
break;
|
|
152
|
-
case 'view':
|
|
153
|
-
const viewUpdate = {
|
|
154
|
-
id: from,
|
|
155
|
-
server_id: child.attrs.message_id,
|
|
156
|
-
count: parseInt(child.content?.toString() || '0', 10)
|
|
157
|
-
};
|
|
158
|
-
ev.emit('newsletter.view', viewUpdate);
|
|
159
|
-
break;
|
|
160
|
-
case 'participant':
|
|
161
|
-
const participantUpdate = {
|
|
162
|
-
id: from,
|
|
163
|
-
author,
|
|
164
|
-
user: child.attrs.jid,
|
|
165
|
-
action: child.attrs.action,
|
|
166
|
-
new_role: child.attrs.role
|
|
167
|
-
};
|
|
168
|
-
ev.emit('newsletter-participants.update', participantUpdate);
|
|
169
|
-
break;
|
|
170
|
-
case 'update':
|
|
171
|
-
const settingsNode = getBinaryNodeChild(child, 'settings');
|
|
172
|
-
if (settingsNode) {
|
|
173
|
-
const update = {};
|
|
174
|
-
const nameNode = getBinaryNodeChild(settingsNode, 'name');
|
|
175
|
-
if (nameNode?.content)
|
|
176
|
-
update.name = nameNode.content.toString();
|
|
177
|
-
const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
|
|
178
|
-
if (descriptionNode?.content)
|
|
179
|
-
update.description = descriptionNode.content.toString();
|
|
180
|
-
ev.emit('newsletter-settings.update', {
|
|
181
|
-
id: from,
|
|
182
|
-
update
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
break;
|
|
186
|
-
case 'message':
|
|
187
|
-
const plaintextNode = getBinaryNodeChild(child, 'plaintext');
|
|
188
|
-
if (plaintextNode?.content) {
|
|
189
|
-
try {
|
|
190
|
-
const contentBuf = typeof plaintextNode.content === 'string'
|
|
191
|
-
? Buffer.from(plaintextNode.content, 'binary')
|
|
192
|
-
: Buffer.from(plaintextNode.content);
|
|
193
|
-
const messageProto = proto.Message.decode(contentBuf);
|
|
194
|
-
const fullMessage = proto.WebMessageInfo.create({
|
|
195
|
-
key: {
|
|
196
|
-
remoteJid: from,
|
|
197
|
-
id: child.attrs.message_id || child.attrs.server_id,
|
|
198
|
-
fromMe: false
|
|
199
|
-
},
|
|
200
|
-
message: messageProto,
|
|
201
|
-
messageTimestamp: +child.attrs.t
|
|
202
|
-
});
|
|
203
|
-
await upsertMessage(fullMessage, 'append');
|
|
204
|
-
logger.info('Processed plaintext newsletter message');
|
|
205
|
-
}
|
|
206
|
-
catch (error) {
|
|
207
|
-
logger.error({ error }, 'Failed to decode plaintext newsletter message');
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
default:
|
|
212
|
-
logger.warn({ node }, 'Unknown newsletter notification');
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
35
|
const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
|
|
217
36
|
const stanza = {
|
|
218
37
|
tag: 'ack',
|
|
@@ -288,76 +107,20 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
288
107
|
const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
|
|
289
108
|
const { key: msgKey } = fullMessage;
|
|
290
109
|
const msgId = msgKey.id;
|
|
291
|
-
if (messageRetryManager) {
|
|
292
|
-
// Check if we've exceeded max retries using the new system
|
|
293
|
-
if (messageRetryManager.hasExceededMaxRetries(msgId)) {
|
|
294
|
-
logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing');
|
|
295
|
-
messageRetryManager.markRetryFailed(msgId);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
// Increment retry count using new system
|
|
299
|
-
const retryCount = messageRetryManager.incrementRetryCount(msgId);
|
|
300
|
-
// Use the new retry count for the rest of the logic
|
|
301
|
-
const key = `${msgId}:${msgKey?.participant}`;
|
|
302
|
-
msgRetryCache.set(key, retryCount);
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
// Fallback to old system
|
|
306
|
-
const key = `${msgId}:${msgKey?.participant}`;
|
|
307
|
-
let retryCount = (await msgRetryCache.get(key)) || 0;
|
|
308
|
-
if (retryCount >= maxMsgRetryCount) {
|
|
309
|
-
logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
|
|
310
|
-
msgRetryCache.del(key);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
retryCount += 1;
|
|
314
|
-
await msgRetryCache.set(key, retryCount);
|
|
315
|
-
}
|
|
316
110
|
const key = `${msgId}:${msgKey?.participant}`;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
let recreateReason = '';
|
|
323
|
-
if (enableAutoSessionRecreation && messageRetryManager) {
|
|
324
|
-
try {
|
|
325
|
-
// Check if we have a session with this JID
|
|
326
|
-
const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
|
|
327
|
-
const hasSession = await signalRepository.validateSession(fromJid);
|
|
328
|
-
const result = messageRetryManager.shouldRecreateSession(fromJid, retryCount, hasSession.exists);
|
|
329
|
-
shouldRecreateSession = result.recreate;
|
|
330
|
-
recreateReason = result.reason;
|
|
331
|
-
if (shouldRecreateSession) {
|
|
332
|
-
logger.info({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
|
|
333
|
-
// Delete existing session to force recreation
|
|
334
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
335
|
-
forceIncludeKeys = true;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
catch (error) {
|
|
339
|
-
logger.warn({ error, fromJid }, 'failed to check session recreation');
|
|
340
|
-
}
|
|
111
|
+
let retryCount = msgRetryCache.get(key) || 0;
|
|
112
|
+
if (retryCount >= maxMsgRetryCount) {
|
|
113
|
+
logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
|
|
114
|
+
msgRetryCache.del(key);
|
|
115
|
+
return;
|
|
341
116
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId} (scheduled)`);
|
|
350
|
-
}
|
|
351
|
-
catch (error) {
|
|
352
|
-
logger.warn({ error, msgId }, 'failed to send scheduled phone request');
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
// Fallback to immediate request
|
|
358
|
-
const msgId = await requestPlaceholderResend(msgKey);
|
|
359
|
-
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
|
|
360
|
-
}
|
|
117
|
+
retryCount += 1;
|
|
118
|
+
msgRetryCache.set(key, retryCount);
|
|
119
|
+
const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
|
|
120
|
+
if (retryCount === 1) {
|
|
121
|
+
//request a resend via phone
|
|
122
|
+
const msgId = await requestPlaceholderResend(msgKey);
|
|
123
|
+
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
|
|
361
124
|
}
|
|
362
125
|
const deviceIdentity = encodeSignedDeviceIdentity(account, true);
|
|
363
126
|
await authState.keys.transaction(async () => {
|
|
@@ -391,7 +154,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
391
154
|
if (node.attrs.participant) {
|
|
392
155
|
receipt.attrs.participant = node.attrs.participant;
|
|
393
156
|
}
|
|
394
|
-
if (retryCount > 1 || forceIncludeKeys
|
|
157
|
+
if (retryCount > 1 || forceIncludeKeys) {
|
|
395
158
|
const { update, preKeys } = await getNextPreKeys(authState, 1);
|
|
396
159
|
const [keyId] = Object.keys(preKeys);
|
|
397
160
|
const key = preKeys[+keyId];
|
|
@@ -411,7 +174,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
411
174
|
}
|
|
412
175
|
await sendNode(receipt);
|
|
413
176
|
logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
|
|
414
|
-
}
|
|
177
|
+
});
|
|
415
178
|
};
|
|
416
179
|
const handleEncryptNotification = async (node) => {
|
|
417
180
|
const from = node.attrs.from;
|
|
@@ -438,7 +201,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
438
201
|
};
|
|
439
202
|
const handleGroupNotification = (participant, child, msg) => {
|
|
440
203
|
const participantJid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant;
|
|
441
|
-
// TODO: Add participant LID
|
|
442
204
|
switch (child?.tag) {
|
|
443
205
|
case 'create':
|
|
444
206
|
const metadata = extractGroupMetadata(child);
|
|
@@ -575,12 +337,10 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
575
337
|
break;
|
|
576
338
|
case 'devices':
|
|
577
339
|
const devices = getBinaryNodeChildren(child, 'device');
|
|
578
|
-
if (areJidsSameUser(child.attrs.jid, authState.creds.me.id)
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
logger.info({ deviceData }, 'my own devices changed');
|
|
340
|
+
if (areJidsSameUser(child.attrs.jid, authState.creds.me.id)) {
|
|
341
|
+
const deviceJids = devices.map(d => d.attrs.jid);
|
|
342
|
+
logger.info({ deviceJids }, 'got my own devices');
|
|
582
343
|
}
|
|
583
|
-
//TODO: drop a new event, add hashes
|
|
584
344
|
break;
|
|
585
345
|
case 'server_sync':
|
|
586
346
|
const update = getBinaryNodeChild(node, 'collection');
|
|
@@ -715,79 +475,34 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
715
475
|
}
|
|
716
476
|
return data instanceof Buffer ? data : Buffer.from(data);
|
|
717
477
|
}
|
|
718
|
-
const willSendMessageAgain =
|
|
478
|
+
const willSendMessageAgain = (id, participant) => {
|
|
719
479
|
const key = `${id}:${participant}`;
|
|
720
|
-
const retryCount =
|
|
721
|
-
return retryCount
|
|
480
|
+
const retryCount = msgRetryCache.get(key) || 0;
|
|
481
|
+
return retryCount <= maxMsgRetryCount;
|
|
722
482
|
};
|
|
723
|
-
const updateSendMessageAgainCount =
|
|
483
|
+
const updateSendMessageAgainCount = (id, participant) => {
|
|
724
484
|
const key = `${id}:${participant}`;
|
|
725
|
-
const newValue = (
|
|
726
|
-
|
|
485
|
+
const newValue = (msgRetryCache.get(key) || 0) + 1;
|
|
486
|
+
msgRetryCache.set(key, newValue);
|
|
727
487
|
};
|
|
728
488
|
const sendMessagesAgain = async (key, ids, retryNode) => {
|
|
489
|
+
// todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
|
|
490
|
+
const msgs = await Promise.all(ids.map(id => getMessage({ ...key, id })));
|
|
729
491
|
const remoteJid = key.remoteJid;
|
|
730
492
|
const participant = key.participant || remoteJid;
|
|
731
|
-
const retryCount = +retryNode.attrs.count || 1;
|
|
732
|
-
// Try to get messages from cache first, then fallback to getMessage
|
|
733
|
-
const msgs = [];
|
|
734
|
-
for (const id of ids) {
|
|
735
|
-
let msg;
|
|
736
|
-
// Try to get from retry cache first if enabled
|
|
737
|
-
if (messageRetryManager) {
|
|
738
|
-
const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
|
|
739
|
-
if (cachedMsg) {
|
|
740
|
-
msg = cachedMsg.message;
|
|
741
|
-
logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
|
|
742
|
-
// Mark retry as successful since we found the message
|
|
743
|
-
messageRetryManager.markRetrySuccess(id);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
// Fallback to getMessage if not found in cache
|
|
747
|
-
if (!msg) {
|
|
748
|
-
msg = await getMessage({ ...key, id });
|
|
749
|
-
if (msg) {
|
|
750
|
-
logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
|
|
751
|
-
// Also mark as successful if found via getMessage
|
|
752
|
-
if (messageRetryManager) {
|
|
753
|
-
messageRetryManager.markRetrySuccess(id);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
msgs.push(msg);
|
|
758
|
-
}
|
|
759
493
|
// if it's the primary jid sending the request
|
|
760
494
|
// just re-send the message to everyone
|
|
761
495
|
// prevents the first message decryption failure
|
|
762
496
|
const sendToAll = !jidDecode(participant)?.device;
|
|
763
|
-
|
|
764
|
-
let shouldRecreateSession = false;
|
|
765
|
-
let recreateReason = '';
|
|
766
|
-
if (enableAutoSessionRecreation && messageRetryManager) {
|
|
767
|
-
try {
|
|
768
|
-
const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
|
|
769
|
-
const hasSession = await signalRepository.validateSession(participant);
|
|
770
|
-
const result = messageRetryManager.shouldRecreateSession(participant, retryCount, hasSession.exists);
|
|
771
|
-
shouldRecreateSession = result.recreate;
|
|
772
|
-
recreateReason = result.reason;
|
|
773
|
-
if (shouldRecreateSession) {
|
|
774
|
-
logger.info({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
|
|
775
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
catch (error) {
|
|
779
|
-
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
await assertSessions([participant], shouldRecreateSession);
|
|
497
|
+
await assertSessions([participant], true);
|
|
783
498
|
if (isJidGroup(remoteJid)) {
|
|
784
499
|
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
|
|
785
500
|
}
|
|
786
|
-
logger.debug({ participant, sendToAll
|
|
501
|
+
logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
|
|
787
502
|
for (const [i, msg] of msgs.entries()) {
|
|
788
503
|
if (!ids[i])
|
|
789
504
|
continue;
|
|
790
|
-
if (msg &&
|
|
505
|
+
if (msg && willSendMessageAgain(ids[i], participant)) {
|
|
791
506
|
updateSendMessageAgainCount(ids[i], participant);
|
|
792
507
|
const msgRelayOpts = { messageId: ids[i] };
|
|
793
508
|
if (sendToAll) {
|
|
@@ -835,7 +550,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
835
550
|
if (typeof status !== 'undefined' &&
|
|
836
551
|
// basically, we only want to know when a message from us has been delivered to/read by the other person
|
|
837
552
|
// or another device of ours has read some messages
|
|
838
|
-
status >= proto.WebMessageInfo.Status.SERVER_ACK) {
|
|
553
|
+
(status >= proto.WebMessageInfo.Status.SERVER_ACK)) {
|
|
839
554
|
if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
|
|
840
555
|
const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
|
|
841
556
|
if (attrs.participant) {
|
|
@@ -877,7 +592,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
877
592
|
// correctly set who is asking for the retry
|
|
878
593
|
key.participant = key.participant || attrs.from;
|
|
879
594
|
const retryNode = getBinaryNodeChild(node, 'retry');
|
|
880
|
-
if (ids[0] && key.participant &&
|
|
595
|
+
if (ids[0] && key.participant && willSendMessageAgain(ids[0], key.participant)) {
|
|
881
596
|
if (key.fromMe) {
|
|
882
597
|
try {
|
|
883
598
|
updateSendMessageAgainCount(ids[0], key.participant);
|
|
@@ -946,22 +661,22 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
946
661
|
// TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
|
|
947
662
|
if (encNode && encNode.attrs.type === 'msmsg') {
|
|
948
663
|
logger.debug({ key: node.attrs.key }, 'ignored msmsg');
|
|
949
|
-
await sendMessageAck(node
|
|
664
|
+
await sendMessageAck(node);
|
|
950
665
|
return;
|
|
951
666
|
}
|
|
952
667
|
let response;
|
|
953
668
|
if (getBinaryNodeChild(node, 'unavailable') && !encNode) {
|
|
954
669
|
await sendMessageAck(node);
|
|
955
670
|
const { key } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage;
|
|
956
|
-
response = await requestPlaceholderResend(key);
|
|
671
|
+
response = await requestPlaceholderResend(key);
|
|
957
672
|
if (response === 'RESOLVED') {
|
|
958
673
|
return;
|
|
959
674
|
}
|
|
960
675
|
logger.debug('received unavailable message, acked and requested resend from phone');
|
|
961
676
|
}
|
|
962
677
|
else {
|
|
963
|
-
if (
|
|
964
|
-
|
|
678
|
+
if (placeholderResendCache.get(node.attrs.id)) {
|
|
679
|
+
placeholderResendCache.del(node.attrs.id);
|
|
965
680
|
}
|
|
966
681
|
}
|
|
967
682
|
const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
|
|
@@ -970,33 +685,38 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
970
685
|
}
|
|
971
686
|
if (msg.message?.protocolMessage?.type === proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
|
|
972
687
|
node.attrs.sender_pn) {
|
|
973
|
-
|
|
974
|
-
ev.emit('lid-mapping.update', { lid, pn });
|
|
975
|
-
await signalRepository.storeLIDPNMapping(lid, pn);
|
|
688
|
+
ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
|
|
976
689
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
690
|
+
if (msg.message?.protocolMessage?.lidMigrationMappingSyncMessage?.encodedMappingPayload) {
|
|
691
|
+
try {
|
|
692
|
+
const payload = msg.message.protocolMessage.lidMigrationMappingSyncMessage.encodedMappingPayload;
|
|
693
|
+
const decoded = proto.LIDMigrationMappingSyncPayload.decode(payload);
|
|
694
|
+
logger.debug({
|
|
695
|
+
mappingCount: decoded.pnToLidMappings?.length || 0,
|
|
696
|
+
timestamp: decoded.chatDbMigrationTimestamp
|
|
697
|
+
}, 'Received LID migration sync message from server');
|
|
698
|
+
const lidMapping = signalRepository.getLIDMappingStore();
|
|
699
|
+
if (decoded.pnToLidMappings && decoded.pnToLidMappings.length > 0) {
|
|
700
|
+
for (const mapping of decoded.pnToLidMappings) {
|
|
701
|
+
const pn = `${mapping.pn}@s.whatsapp.net`;
|
|
702
|
+
// Use latestLid if available, otherwise assignedLid (proper LID refresh)
|
|
703
|
+
const lidValue = mapping.latestLid || mapping.assignedLid;
|
|
704
|
+
const lid = `${lidValue}@lid`;
|
|
705
|
+
await lidMapping.storeLIDPNMapping(lid, pn);
|
|
706
|
+
logger.debug({
|
|
707
|
+
pn,
|
|
708
|
+
lid,
|
|
709
|
+
assignedLid: mapping.assignedLid,
|
|
710
|
+
latestLid: mapping.latestLid,
|
|
711
|
+
usedLatest: !!mapping.latestLid
|
|
712
|
+
}, 'Stored server-provided PN-LID mapping');
|
|
713
|
+
}
|
|
985
714
|
}
|
|
986
715
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid, alt);
|
|
990
|
-
}
|
|
716
|
+
catch (error) {
|
|
717
|
+
logger.error({ error }, 'Failed to process LID migration sync message');
|
|
991
718
|
}
|
|
992
719
|
}
|
|
993
|
-
if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
|
|
994
|
-
messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
|
|
995
|
-
logger.debug({
|
|
996
|
-
jid: msg.key.remoteJid,
|
|
997
|
-
id: msg.key.id
|
|
998
|
-
}, 'Added message to recent cache for retry receipts');
|
|
999
|
-
}
|
|
1000
720
|
try {
|
|
1001
721
|
await Promise.all([
|
|
1002
722
|
processingMutex.mutex(async () => {
|
|
@@ -1008,7 +728,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1008
728
|
}
|
|
1009
729
|
const errorMessage = msg?.messageStubParameters?.[0] || '';
|
|
1010
730
|
const isPreKeyError = errorMessage.includes('PreKey');
|
|
1011
|
-
|
|
731
|
+
console.debug(`[handleMessage] Attempting retry request for failed decryption`);
|
|
1012
732
|
// Handle both pre-key and normal retries in single mutex
|
|
1013
733
|
retryMutex.mutex(async () => {
|
|
1014
734
|
try {
|
|
@@ -1064,8 +784,8 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1064
784
|
// message was sent by us from a different device
|
|
1065
785
|
type = 'sender';
|
|
1066
786
|
// need to specially handle this case
|
|
1067
|
-
if (
|
|
1068
|
-
participant = author;
|
|
787
|
+
if (isJidUser(msg.key.remoteJid)) {
|
|
788
|
+
participant = author;
|
|
1069
789
|
}
|
|
1070
790
|
}
|
|
1071
791
|
else if (!sendActiveReceipts) {
|
|
@@ -1089,6 +809,54 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1089
809
|
logger.error({ error, node }, 'error in handling message');
|
|
1090
810
|
}
|
|
1091
811
|
};
|
|
812
|
+
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
813
|
+
if (!authState.creds.me?.id) {
|
|
814
|
+
throw new Boom('Not authenticated');
|
|
815
|
+
}
|
|
816
|
+
const pdoMessage = {
|
|
817
|
+
historySyncOnDemandRequest: {
|
|
818
|
+
chatJid: oldestMsgKey.remoteJid,
|
|
819
|
+
oldestMsgFromMe: oldestMsgKey.fromMe,
|
|
820
|
+
oldestMsgId: oldestMsgKey.id,
|
|
821
|
+
oldestMsgTimestampMs: oldestMsgTimestamp,
|
|
822
|
+
onDemandMsgCount: count
|
|
823
|
+
},
|
|
824
|
+
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
|
|
825
|
+
};
|
|
826
|
+
return sendPeerDataOperationMessage(pdoMessage);
|
|
827
|
+
};
|
|
828
|
+
const requestPlaceholderResend = async (messageKey) => {
|
|
829
|
+
if (!authState.creds.me?.id) {
|
|
830
|
+
throw new Boom('Not authenticated');
|
|
831
|
+
}
|
|
832
|
+
if (placeholderResendCache.get(messageKey?.id)) {
|
|
833
|
+
logger.debug({ messageKey }, 'already requested resend');
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
placeholderResendCache.set(messageKey?.id, true);
|
|
838
|
+
}
|
|
839
|
+
await delay(5000);
|
|
840
|
+
if (!placeholderResendCache.get(messageKey?.id)) {
|
|
841
|
+
logger.debug({ messageKey }, 'message received while resend requested');
|
|
842
|
+
return 'RESOLVED';
|
|
843
|
+
}
|
|
844
|
+
const pdoMessage = {
|
|
845
|
+
placeholderMessageResendRequest: [
|
|
846
|
+
{
|
|
847
|
+
messageKey
|
|
848
|
+
}
|
|
849
|
+
],
|
|
850
|
+
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
851
|
+
};
|
|
852
|
+
setTimeout(() => {
|
|
853
|
+
if (placeholderResendCache.get(messageKey?.id)) {
|
|
854
|
+
logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
|
|
855
|
+
placeholderResendCache.del(messageKey?.id);
|
|
856
|
+
}
|
|
857
|
+
}, 15000);
|
|
858
|
+
return sendPeerDataOperationMessage(pdoMessage);
|
|
859
|
+
};
|
|
1092
860
|
const handleCall = async (node) => {
|
|
1093
861
|
let status;
|
|
1094
862
|
const { attrs } = node;
|
|
@@ -1100,7 +868,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1100
868
|
const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
|
|
1101
869
|
status = getCallStatusFromNode(infoChild);
|
|
1102
870
|
if (isLidUser(from) && infoChild.tag === 'relaylatency') {
|
|
1103
|
-
const verify =
|
|
871
|
+
const verify = callOfferCache.get(callId);
|
|
1104
872
|
if (!verify) {
|
|
1105
873
|
status = 'offer';
|
|
1106
874
|
const callLid = {
|
|
@@ -1111,7 +879,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1111
879
|
offline: !!attrs.offline,
|
|
1112
880
|
status
|
|
1113
881
|
};
|
|
1114
|
-
|
|
882
|
+
callOfferCache.set(callId, callLid);
|
|
1115
883
|
}
|
|
1116
884
|
}
|
|
1117
885
|
const call = {
|
|
@@ -1126,9 +894,9 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1126
894
|
call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
|
|
1127
895
|
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
|
|
1128
896
|
call.groupJid = infoChild.attrs['group-jid'];
|
|
1129
|
-
|
|
897
|
+
callOfferCache.set(call.id, call);
|
|
1130
898
|
}
|
|
1131
|
-
const existingCall =
|
|
899
|
+
const existingCall = callOfferCache.get(call.id);
|
|
1132
900
|
// use existing call info to populate this event
|
|
1133
901
|
if (existingCall) {
|
|
1134
902
|
call.isVideo = existingCall.isVideo;
|
|
@@ -1136,7 +904,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1136
904
|
}
|
|
1137
905
|
// delete data once call has ended
|
|
1138
906
|
if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
|
|
1139
|
-
|
|
907
|
+
callOfferCache.del(call.id);
|
|
1140
908
|
}
|
|
1141
909
|
ev.emit('call', [call]);
|
|
1142
910
|
await sendMessageAck(node);
|
|
@@ -1222,6 +990,139 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1222
990
|
processNodeWithBuffer(node, identifier, exec);
|
|
1223
991
|
}
|
|
1224
992
|
};
|
|
993
|
+
// Handles newsletter notifications
|
|
994
|
+
async function handleNewsletterNotification(node) {
|
|
995
|
+
const from = node.attrs.from;
|
|
996
|
+
const child = getAllBinaryNodeChildren(node)[0];
|
|
997
|
+
const author = node.attrs.participant;
|
|
998
|
+
logger.info({ from, child }, 'got newsletter notification');
|
|
999
|
+
switch (child.tag) {
|
|
1000
|
+
case 'reaction':
|
|
1001
|
+
const reactionUpdate = {
|
|
1002
|
+
id: from,
|
|
1003
|
+
server_id: child.attrs.message_id,
|
|
1004
|
+
reaction: {
|
|
1005
|
+
code: getBinaryNodeChildString(child, 'reaction'),
|
|
1006
|
+
count: 1
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
ev.emit('newsletter.reaction', reactionUpdate);
|
|
1010
|
+
break;
|
|
1011
|
+
case 'view':
|
|
1012
|
+
const viewUpdate = {
|
|
1013
|
+
id: from,
|
|
1014
|
+
server_id: child.attrs.message_id,
|
|
1015
|
+
count: parseInt(child.content?.toString() || '0', 10)
|
|
1016
|
+
};
|
|
1017
|
+
ev.emit('newsletter.view', viewUpdate);
|
|
1018
|
+
break;
|
|
1019
|
+
case 'participant':
|
|
1020
|
+
const participantUpdate = {
|
|
1021
|
+
id: from,
|
|
1022
|
+
author,
|
|
1023
|
+
user: child.attrs.jid,
|
|
1024
|
+
action: child.attrs.action,
|
|
1025
|
+
new_role: child.attrs.role
|
|
1026
|
+
};
|
|
1027
|
+
ev.emit('newsletter-participants.update', participantUpdate);
|
|
1028
|
+
break;
|
|
1029
|
+
case 'update':
|
|
1030
|
+
const settingsNode = getBinaryNodeChild(child, 'settings');
|
|
1031
|
+
if (settingsNode) {
|
|
1032
|
+
const update = {};
|
|
1033
|
+
const nameNode = getBinaryNodeChild(settingsNode, 'name');
|
|
1034
|
+
if (nameNode?.content)
|
|
1035
|
+
update.name = nameNode.content.toString();
|
|
1036
|
+
const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
|
|
1037
|
+
if (descriptionNode?.content)
|
|
1038
|
+
update.description = descriptionNode.content.toString();
|
|
1039
|
+
ev.emit('newsletter-settings.update', {
|
|
1040
|
+
id: from,
|
|
1041
|
+
update
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
break;
|
|
1045
|
+
case 'message':
|
|
1046
|
+
const plaintextNode = getBinaryNodeChild(child, 'plaintext');
|
|
1047
|
+
if (plaintextNode?.content) {
|
|
1048
|
+
try {
|
|
1049
|
+
const contentBuf = typeof plaintextNode.content === 'string'
|
|
1050
|
+
? Buffer.from(plaintextNode.content, 'binary')
|
|
1051
|
+
: Buffer.from(plaintextNode.content);
|
|
1052
|
+
const messageProto = proto.Message.decode(contentBuf);
|
|
1053
|
+
const fullMessage = proto.WebMessageInfo.create({
|
|
1054
|
+
key: {
|
|
1055
|
+
remoteJid: from,
|
|
1056
|
+
id: child.attrs.message_id || child.attrs.server_id,
|
|
1057
|
+
fromMe: false
|
|
1058
|
+
},
|
|
1059
|
+
message: messageProto,
|
|
1060
|
+
messageTimestamp: +child.attrs.t
|
|
1061
|
+
});
|
|
1062
|
+
await upsertMessage(fullMessage, 'append');
|
|
1063
|
+
logger.info('Processed plaintext newsletter message');
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
logger.error({ error }, 'Failed to decode plaintext newsletter message');
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
break;
|
|
1070
|
+
default:
|
|
1071
|
+
logger.warn({ node }, 'Unknown newsletter notification');
|
|
1072
|
+
break;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
// Handles mex newsletter notifications
|
|
1076
|
+
async function handleMexNewsletterNotification(node) {
|
|
1077
|
+
const mexNode = getBinaryNodeChild(node, 'mex');
|
|
1078
|
+
if (!mexNode?.content) {
|
|
1079
|
+
logger.warn({ node }, 'Invalid mex newsletter notification');
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
let data;
|
|
1083
|
+
try {
|
|
1084
|
+
data = JSON.parse(mexNode.content.toString());
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const operation = data?.operation;
|
|
1091
|
+
const updates = data?.updates;
|
|
1092
|
+
if (!updates || !operation) {
|
|
1093
|
+
logger.warn({ data }, 'Invalid mex newsletter notification content');
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
logger.info({ operation, updates }, 'got mex newsletter notification');
|
|
1097
|
+
switch (operation) {
|
|
1098
|
+
case 'NotificationNewsletterUpdate':
|
|
1099
|
+
for (const update of updates) {
|
|
1100
|
+
if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
|
|
1101
|
+
ev.emit('newsletter-settings.update', {
|
|
1102
|
+
id: update.jid,
|
|
1103
|
+
update: update.settings
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
break;
|
|
1108
|
+
case 'NotificationNewsletterAdminPromote':
|
|
1109
|
+
for (const update of updates) {
|
|
1110
|
+
if (update.jid && update.user) {
|
|
1111
|
+
ev.emit('newsletter-participants.update', {
|
|
1112
|
+
id: update.jid,
|
|
1113
|
+
author: node.attrs.from,
|
|
1114
|
+
user: update.user,
|
|
1115
|
+
new_role: 'ADMIN',
|
|
1116
|
+
action: 'promote'
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
break;
|
|
1121
|
+
default:
|
|
1122
|
+
logger.info({ operation, data }, 'Unhandled mex newsletter notification');
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1225
1126
|
// recv a message
|
|
1226
1127
|
ws.on('CB:message', (node) => {
|
|
1227
1128
|
processNode('message', node, 'processing message', handleMessage);
|
|
@@ -1281,8 +1182,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1281
1182
|
sendRetryRequest,
|
|
1282
1183
|
rejectCall,
|
|
1283
1184
|
fetchMessageHistory,
|
|
1284
|
-
requestPlaceholderResend
|
|
1285
|
-
messageRetryManager
|
|
1185
|
+
requestPlaceholderResend
|
|
1286
1186
|
};
|
|
1287
1187
|
};
|
|
1288
1188
|
//# sourceMappingURL=messages-recv.js.map
|