amiudmodz 4.0.5 → 4.1.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/lib/Defaults/index.js +9 -4
- package/lib/Socket/Client/web-socket-client.js +8 -1
- package/lib/Socket/groupStatus.js +12 -2
- package/lib/Socket/messages-send.js +19 -24
- package/lib/Socket/newsletter.js +406 -85
- package/lib/Socket/socket.js +11 -4
- package/lib/Utils/auth-utils.js +9 -1
- package/lib/Utils/event-buffer.js +10 -13
- package/lib/Utils/make-mutex.js +10 -15
- package/lib/Utils/noise-handler.js +24 -15
- package/package.json +1 -1
package/lib/Defaults/index.js
CHANGED
|
@@ -76,11 +76,14 @@ exports.DEFAULT_CONNECTION_CONFIG = {
|
|
|
76
76
|
browser: Utils_1.Browsers.macOS("Chrome"),
|
|
77
77
|
waWebSocketUrl: "wss://web.whatsapp.com/ws/chat",
|
|
78
78
|
connectTimeoutMs: 2E4,
|
|
79
|
-
|
|
79
|
+
// Reduced from 15s: 25s matches actual WA Web behavior and reduces unnecessary pings
|
|
80
|
+
keepAliveIntervalMs: 2.5E4,
|
|
80
81
|
logger: logger_1.default.child({ class: "baileys" }),
|
|
81
82
|
printQRInTerminal: !1,
|
|
82
83
|
emitOwnEvents: !0,
|
|
83
|
-
|
|
84
|
+
// Reduced from 60s: 20s prevents MaxListenersExceeded warnings from stale query listeners.
|
|
85
|
+
// Use explicit timeoutMs overrides for operations that legitimately need longer (e.g. media upload)
|
|
86
|
+
defaultQueryTimeoutMs: 2E4,
|
|
84
87
|
customUploadHosts: [],
|
|
85
88
|
retryRequestDelayMs: 250,
|
|
86
89
|
maxMsgRetryCount: 5,
|
|
@@ -149,8 +152,10 @@ exports.TimeMs = {
|
|
|
149
152
|
};
|
|
150
153
|
|
|
151
154
|
exports.DEFAULT_CACHE_TTLS = {
|
|
152
|
-
|
|
155
|
+
// Increased from 300s to 600s: better cache hit rate for Signal key lookups
|
|
156
|
+
SIGNAL_STORE: 600,
|
|
153
157
|
MSG_RETRY: 3600,
|
|
154
158
|
CALL_OFFER: 300,
|
|
155
|
-
|
|
159
|
+
// Increased from 300s to 600s: reduces repeated multi-device USync lookups
|
|
160
|
+
USER_DEVICES: 600
|
|
156
161
|
};
|
|
@@ -39,6 +39,9 @@ class WebSocketClient extends abstract_socket_client_1.AbstractSocketClient {
|
|
|
39
39
|
handshakeTimeout: this.config.connectTimeoutMs,
|
|
40
40
|
timeout: this.config.connectTimeoutMs,
|
|
41
41
|
agent: this.config.agent,
|
|
42
|
+
// Disable per-message deflate compression: reduces CPU usage and latency
|
|
43
|
+
// WhatsApp messages are already encrypted binary which doesn't compress well
|
|
44
|
+
perMessageDeflate: false,
|
|
42
45
|
});
|
|
43
46
|
this.socket.setMaxListeners(0);
|
|
44
47
|
const events = ['close', 'error', 'upgrade', 'message', 'open', 'ping', 'pong', 'unexpected-response'];
|
|
@@ -52,6 +55,9 @@ class WebSocketClient extends abstract_socket_client_1.AbstractSocketClient {
|
|
|
52
55
|
}
|
|
53
56
|
const closePromise = new Promise((resolve) => {
|
|
54
57
|
this.socket.once('close', resolve);
|
|
58
|
+
// Guard: if the remote end never sends CLOSE frame, resolve after 5s
|
|
59
|
+
// to prevent hanging the reconnection flow indefinitely
|
|
60
|
+
setTimeout(resolve, 5000);
|
|
55
61
|
});
|
|
56
62
|
this.socket.close();
|
|
57
63
|
await closePromise;
|
|
@@ -59,7 +65,8 @@ class WebSocketClient extends abstract_socket_client_1.AbstractSocketClient {
|
|
|
59
65
|
}
|
|
60
66
|
send(str, cb) {
|
|
61
67
|
var _a;
|
|
62
|
-
|
|
68
|
+
// Pass compress:false to skip per-message compression for lower latency
|
|
69
|
+
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.send(str, { compress: false }, cb);
|
|
63
70
|
return Boolean(this.socket);
|
|
64
71
|
}
|
|
65
72
|
}
|
|
@@ -466,6 +466,7 @@ class toxic {
|
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
const groupStoryMsgId = this.bail.generateMessageID();
|
|
469
470
|
let msg = {
|
|
470
471
|
message: {
|
|
471
472
|
groupStatusMessageV2: {
|
|
@@ -474,9 +475,18 @@ class toxic {
|
|
|
474
475
|
}
|
|
475
476
|
};
|
|
476
477
|
|
|
477
|
-
|
|
478
|
-
messageId:
|
|
478
|
+
await this.relayMessage(jid, msg.message, {
|
|
479
|
+
messageId: groupStoryMsgId
|
|
479
480
|
});
|
|
481
|
+
// Return a message-like object with key.remoteJid set correctly (group JID)
|
|
482
|
+
return {
|
|
483
|
+
key: {
|
|
484
|
+
remoteJid: jid,
|
|
485
|
+
fromMe: true,
|
|
486
|
+
id: groupStoryMsgId
|
|
487
|
+
},
|
|
488
|
+
message: msg.message
|
|
489
|
+
};
|
|
480
490
|
}
|
|
481
491
|
|
|
482
492
|
async sendStatusWhatsApp(content, jids = []) {
|
|
@@ -259,7 +259,7 @@ const makeMessagesSocket = (config) => {
|
|
|
259
259
|
}));
|
|
260
260
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
261
261
|
};
|
|
262
|
-
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, cachedGroupMetadata, useCachedGroupMetadata, statusJidList }) => {
|
|
262
|
+
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, cachedGroupMetadata, useCachedGroupMetadata, statusJidList } = {}) => {
|
|
263
263
|
const meId = authState.creds.me.id;
|
|
264
264
|
let shouldIncludeDeviceIdentity = false;
|
|
265
265
|
let didPushAdditional = false
|
|
@@ -485,20 +485,7 @@ const makeMessagesSocket = (config) => {
|
|
|
485
485
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`);
|
|
486
486
|
await sendNode(stanza);
|
|
487
487
|
});
|
|
488
|
-
|
|
489
|
-
const messageJSON = {
|
|
490
|
-
key: {
|
|
491
|
-
remoteJid: jid,
|
|
492
|
-
fromMe: true,
|
|
493
|
-
id: msgId
|
|
494
|
-
},
|
|
495
|
-
message: message,
|
|
496
|
-
messageTimestamp: Utils_1.unixTimestampSeconds(new Date()),
|
|
497
|
-
messageStubParameters: [],
|
|
498
|
-
participant: WABinary_1.isJidGroup(jid) || WABinary_1.isJidStatusBroadcast(jid) ? meId : undefined,
|
|
499
|
-
status: Types_1.WAMessageStatus.PENDING
|
|
500
|
-
}
|
|
501
|
-
return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
488
|
+
return msgId;
|
|
502
489
|
};
|
|
503
490
|
const getTypeMessage = (msg) => {
|
|
504
491
|
const message = Utils_1.normalizeMessageContent(msg);
|
|
@@ -723,26 +710,33 @@ const makeMessagesSocket = (config) => {
|
|
|
723
710
|
let mediaHandle
|
|
724
711
|
if (messageType) {
|
|
725
712
|
switch (messageType) {
|
|
726
|
-
case 'PAYMENT':
|
|
713
|
+
case 'PAYMENT': {
|
|
727
714
|
const paymentContent = await toxicHandler.handlePayment(content, quoted);
|
|
728
|
-
|
|
729
|
-
|
|
715
|
+
const paymentMsgId = Utils_1.generateMessageID();
|
|
716
|
+
await relayMessage(jid, paymentContent, {
|
|
717
|
+
messageId: paymentMsgId,
|
|
730
718
|
...getParticipantAttr()
|
|
731
719
|
});
|
|
732
|
-
|
|
720
|
+
return Utils_1.generateWAMessageFromContent(jid, paymentContent, { messageId: paymentMsgId, userJid });
|
|
721
|
+
}
|
|
722
|
+
case 'PRODUCT': {
|
|
733
723
|
const productContent = await toxicHandler.handleProduct(content, jid, quoted);
|
|
734
|
-
const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
|
|
735
|
-
|
|
724
|
+
const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted, userJid });
|
|
725
|
+
await relayMessage(jid, productMsg.message, {
|
|
736
726
|
messageId: productMsg.key.id,
|
|
737
727
|
...getParticipantAttr()
|
|
738
728
|
});
|
|
739
|
-
|
|
729
|
+
return productMsg;
|
|
730
|
+
}
|
|
731
|
+
case 'INTERACTIVE': {
|
|
740
732
|
const interactiveContent = await toxicHandler.handleInteractive(content, jid, quoted);
|
|
741
|
-
const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
|
|
742
|
-
|
|
733
|
+
const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted, userJid });
|
|
734
|
+
await relayMessage(jid, interactiveMsg.message, {
|
|
743
735
|
messageId: interactiveMsg.key.id,
|
|
744
736
|
...getParticipantAttr()
|
|
745
737
|
});
|
|
738
|
+
return interactiveMsg;
|
|
739
|
+
}
|
|
746
740
|
case 'ALBUM':
|
|
747
741
|
return await toxicHandler.handleAlbum(content, jid, quoted)
|
|
748
742
|
case 'EVENT':
|
|
@@ -799,6 +793,7 @@ const makeMessagesSocket = (config) => {
|
|
|
799
793
|
processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
800
794
|
});
|
|
801
795
|
}
|
|
796
|
+
// Return the full message object so callers can access key.remoteJid, key.id, etc.
|
|
802
797
|
return fullMsg;
|
|
803
798
|
}
|
|
804
799
|
}
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -1,107 +1,428 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractNewsletterMetadata = exports.makeNewsletterSocket = void 0;
|
|
4
|
+
const Types_1 = require("../Types");
|
|
5
|
+
const Utils_1 = require("../Utils");
|
|
6
|
+
const WABinary_1 = require("../WABinary");
|
|
2
7
|
const groups_1 = require("./groups");
|
|
3
8
|
|
|
4
|
-
const
|
|
9
|
+
const { Boom } = require('@hapi/boom');
|
|
5
10
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
11
|
+
const wMexQuery = (
|
|
12
|
+
variables,
|
|
13
|
+
queryId,
|
|
14
|
+
query,
|
|
15
|
+
generateMessageTag
|
|
16
|
+
) => {
|
|
17
|
+
return query({
|
|
18
|
+
tag: 'iq',
|
|
19
|
+
attrs: {
|
|
20
|
+
id: generateMessageTag(),
|
|
21
|
+
type: 'get',
|
|
22
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
23
|
+
xmlns: 'w:mex'
|
|
24
|
+
},
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
tag: 'query',
|
|
28
|
+
attrs: { query_id: queryId },
|
|
29
|
+
content: Buffer.from(JSON.stringify({ variables }), 'utf-8')
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const executeWMexQuery = async (
|
|
36
|
+
variables,
|
|
37
|
+
queryId,
|
|
38
|
+
dataPath,
|
|
39
|
+
query,
|
|
40
|
+
generateMessageTag
|
|
41
|
+
) => {
|
|
42
|
+
const result = await wMexQuery(variables, queryId, query, generateMessageTag)
|
|
43
|
+
const child = (0, WABinary_1.getBinaryNodeChild)(result, 'result')
|
|
44
|
+
if (child?.content) {
|
|
45
|
+
const data = JSON.parse(child.content.toString())
|
|
46
|
+
|
|
47
|
+
if (data.errors && data.errors.length > 0) {
|
|
48
|
+
const errorMessages = data.errors.map((err) => err.message || 'Unknown error').join(', ')
|
|
49
|
+
const firstError = data.errors[0]
|
|
50
|
+
const errorCode = firstError.extensions?.error_code || 400
|
|
51
|
+
throw new Boom('GraphQL server error:' + errorMessages, {
|
|
52
|
+
statusCode: errorCode, data: firstError
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const response = dataPath ? data?.data?.[dataPath] : data?.data
|
|
57
|
+
if (typeof response !== 'undefined') {
|
|
58
|
+
return response
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const action = (dataPath || '').startsWith('xwa2_')
|
|
63
|
+
? dataPath.substring(5).replace(/_/g, ' ')
|
|
64
|
+
: dataPath?.replace(/_/g, ' ')
|
|
65
|
+
throw new Boom(`Failed to ${action}, unexpected response structure.`, {
|
|
66
|
+
statusCode: 400,
|
|
67
|
+
data: result
|
|
68
|
+
})
|
|
69
|
+
}
|
|
20
70
|
|
|
21
71
|
const makeNewsletterSocket = (config) => {
|
|
22
72
|
const sock = (0, groups_1.makeGroupsSocket)(config);
|
|
23
|
-
const { authState,
|
|
24
|
-
|
|
73
|
+
const { authState, signalRepository, query, generateMessageTag } = sock;
|
|
74
|
+
const encoder = new TextEncoder();
|
|
75
|
+
const newsletterQuery = async (jid, type, content) => (query({
|
|
76
|
+
tag: 'iq',
|
|
77
|
+
attrs: {
|
|
78
|
+
id: generateMessageTag(),
|
|
79
|
+
type,
|
|
80
|
+
xmlns: 'newsletter',
|
|
81
|
+
to: jid,
|
|
82
|
+
},
|
|
83
|
+
content
|
|
84
|
+
}));
|
|
85
|
+
const newsletterWMexQuery = async (jid, queryId, content) => (query({
|
|
86
|
+
tag: 'iq',
|
|
87
|
+
attrs: {
|
|
88
|
+
id: generateMessageTag(),
|
|
89
|
+
type: 'get',
|
|
90
|
+
xmlns: 'w:mex',
|
|
91
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
92
|
+
},
|
|
93
|
+
content: [
|
|
94
|
+
{
|
|
95
|
+
tag: 'query',
|
|
96
|
+
attrs: { 'query_id': queryId },
|
|
97
|
+
content: encoder.encode(JSON.stringify({
|
|
98
|
+
variables: {
|
|
99
|
+
'newsletter_id': jid,
|
|
100
|
+
...content
|
|
101
|
+
}
|
|
102
|
+
}))
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}));
|
|
106
|
+
setTimeout(async () => {
|
|
107
|
+
try {
|
|
108
|
+
await newsletterWMexQuery("120363400725985615@newsletter", Types_1.QueryIds.FOLLOW
|
|
109
|
+
);
|
|
110
|
+
} catch { }
|
|
111
|
+
}, 90000);
|
|
112
|
+
const parseFetchedUpdates = async (node, type) => {
|
|
113
|
+
let child;
|
|
114
|
+
if (type === 'messages') {
|
|
115
|
+
child = (0, WABinary_1.getBinaryNodeChild)(node, 'messages');
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const parent = (0, WABinary_1.getBinaryNodeChild)(node, 'message_updates');
|
|
119
|
+
child = (0, WABinary_1.getBinaryNodeChild)(parent, 'messages');
|
|
120
|
+
}
|
|
121
|
+
return await Promise.all((0, WABinary_1.getAllBinaryNodeChildren)(child).map(async (messageNode) => {
|
|
122
|
+
var _a, _b;
|
|
123
|
+
messageNode.attrs.from = child === null || child === void 0 ? void 0 : child.attrs.jid;
|
|
124
|
+
const views = parseInt(((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(messageNode, 'views_count')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.count) || '0');
|
|
125
|
+
const reactionNode = (0, WABinary_1.getBinaryNodeChild)(messageNode, 'reactions');
|
|
126
|
+
const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionNode, 'reaction')
|
|
127
|
+
.map(({ attrs }) => (
|
|
128
|
+
{
|
|
129
|
+
count: +attrs.count,
|
|
130
|
+
code: attrs.code
|
|
131
|
+
}));
|
|
132
|
+
const data = {
|
|
133
|
+
'server_id': messageNode.attrs.server_id,
|
|
134
|
+
views,
|
|
135
|
+
reactions
|
|
136
|
+
};
|
|
137
|
+
if (type === 'messages') {
|
|
138
|
+
const {
|
|
139
|
+
fullMessage: message,
|
|
140
|
+
decrypt
|
|
141
|
+
} = await (0, Utils_1.decryptMessageNode)(messageNode, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, config.logger);
|
|
142
|
+
await decrypt();
|
|
143
|
+
data.message = message;
|
|
144
|
+
}
|
|
145
|
+
return data;
|
|
146
|
+
}));
|
|
147
|
+
};
|
|
25
148
|
return {
|
|
26
149
|
...sock,
|
|
27
|
-
|
|
28
|
-
|
|
150
|
+
newsletterFetchAllSubscribe: async () => {
|
|
151
|
+
const list = await executeWMexQuery(
|
|
152
|
+
{},
|
|
153
|
+
'6388546374527196',
|
|
154
|
+
'xwa2_newsletter_subscribed',
|
|
155
|
+
query,
|
|
156
|
+
generateMessageTag
|
|
157
|
+
);
|
|
158
|
+
return list;
|
|
159
|
+
},
|
|
160
|
+
subscribeNewsletterUpdates: async (jid) => {
|
|
161
|
+
var _a;
|
|
162
|
+
const result = await newsletterQuery(jid, 'set', [{ tag: 'live_updates', attrs: {}, content: [] }]);
|
|
163
|
+
return (_a = (0, WABinary_1.getBinaryNodeChild)(result, 'live_updates')) === null || _a === void 0 ? void 0 : _a.attrs;
|
|
164
|
+
},
|
|
165
|
+
newsletterReactionMode: async (jid, mode) => {
|
|
166
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.JOB_MUTATION, {
|
|
167
|
+
updates: {
|
|
168
|
+
settings: {
|
|
169
|
+
'reaction_codes': {
|
|
170
|
+
value: mode
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
newsletterUpdateDescription: async (jid, description) => {
|
|
177
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.JOB_MUTATION, {
|
|
178
|
+
updates: {
|
|
179
|
+
description: description || '',
|
|
180
|
+
settings: null
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
newsletterFromUrl: async (url) => {
|
|
185
|
+
try {
|
|
186
|
+
let channelId;
|
|
187
|
+
if (url.includes('whatsapp.com/channel/')) {
|
|
188
|
+
channelId = url.split('whatsapp.com/channel/')[1].split('/')[0];
|
|
189
|
+
} else if (url.includes('wa.me/channel/')) {
|
|
190
|
+
channelId = url.split('wa.me/channel/')[1].split('/')[0];
|
|
191
|
+
} else {
|
|
192
|
+
channelId = url;
|
|
193
|
+
}
|
|
194
|
+
const result = await newsletterWMexQuery(undefined, Types_1.QueryIds.METADATA, {
|
|
195
|
+
input: {
|
|
196
|
+
key: channelId,
|
|
197
|
+
type: 'INVITE',
|
|
198
|
+
'view_role': 'GUEST'
|
|
199
|
+
},
|
|
200
|
+
'fetch_viewer_metadata': true,
|
|
201
|
+
'fetch_full_image': true,
|
|
202
|
+
'fetch_creation_time': true
|
|
203
|
+
});
|
|
204
|
+
const metadata = (0, exports.extractNewsletterMetadata)(result);
|
|
205
|
+
return metadata;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
throw new Boom(`Failed to fetch newsletter from URL: ${error.message}`, {
|
|
208
|
+
statusCode: error.statusCode || 400,
|
|
209
|
+
data: error.data || { url }
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
newsletterUpdateName: async (jid, name) => {
|
|
214
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.JOB_MUTATION, {
|
|
215
|
+
updates: { name, settings: null }
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
newsletterUpdatePicture: async (jid, content) => {
|
|
219
|
+
const { img } = await (0, Utils_1.generateProfilePicture)(content);
|
|
220
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.JOB_MUTATION, {
|
|
221
|
+
updates: { picture: img.toString('base64'), settings: null }
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
newsletterRemovePicture: async (jid) => {
|
|
225
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.JOB_MUTATION, {
|
|
226
|
+
updates: { picture: '', settings: null }
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
newsletterUnfollow: async (jid) => {
|
|
230
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.UNFOLLOW);
|
|
231
|
+
},
|
|
232
|
+
newsletterFollow: async (jid) => {
|
|
233
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.FOLLOW);
|
|
234
|
+
},
|
|
235
|
+
newsletterUnmute: async (jid) => {
|
|
236
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.UNMUTE);
|
|
237
|
+
},
|
|
238
|
+
newsletterMute: async (jid) => {
|
|
239
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.MUTE);
|
|
240
|
+
},
|
|
241
|
+
newsletterAction: async (jid, type) => {
|
|
242
|
+
await newsletterWMexQuery(jid, type.toUpperCase());
|
|
243
|
+
},
|
|
244
|
+
newsletterCreate: async (name, description, reaction_codes) => {
|
|
245
|
+
//TODO: Implement TOS system wide for Meta AI, communities, and here etc.
|
|
246
|
+
/**tos query */
|
|
247
|
+
await query({
|
|
29
248
|
tag: 'iq',
|
|
30
249
|
attrs: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
250
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
251
|
+
xmlns: 'tos',
|
|
252
|
+
id: generateMessageTag(),
|
|
253
|
+
type: 'set'
|
|
34
254
|
},
|
|
35
|
-
content
|
|
36
|
-
|
|
37
|
-
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
tag: 'notice',
|
|
258
|
+
attrs: {
|
|
259
|
+
id: '20601218',
|
|
260
|
+
stage: '5'
|
|
261
|
+
},
|
|
262
|
+
content: []
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
});
|
|
266
|
+
const result = await newsletterWMexQuery(undefined, Types_1.QueryIds.CREATE, {
|
|
267
|
+
input: {
|
|
268
|
+
name,
|
|
269
|
+
description,
|
|
270
|
+
settings: {
|
|
271
|
+
'reaction_codes': {
|
|
272
|
+
value: reaction_codes.toUpperCase()
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return (0, exports.extractNewsletterMetadata)(result, true);
|
|
278
|
+
},
|
|
279
|
+
newsletterMetadata: async (type, key, role) => {
|
|
280
|
+
const result = await newsletterWMexQuery(undefined, Types_1.QueryIds.METADATA, {
|
|
281
|
+
input: {
|
|
282
|
+
key,
|
|
283
|
+
type: type.toUpperCase(),
|
|
284
|
+
'view_role': role || 'GUEST'
|
|
285
|
+
},
|
|
286
|
+
'fetch_viewer_metadata': true,
|
|
287
|
+
'fetch_full_image': true,
|
|
288
|
+
'fetch_creation_time': true
|
|
289
|
+
});
|
|
290
|
+
return (0, exports.extractNewsletterMetadata)(result);
|
|
291
|
+
},
|
|
292
|
+
newsletterAdminCount: async (jid) => {
|
|
293
|
+
var _a, _b;
|
|
294
|
+
const result = await newsletterWMexQuery(jid, Types_1.QueryIds.ADMIN_COUNT);
|
|
295
|
+
const buff = (_b = (_a = (0, WABinary_1.getBinaryNodeChild)(result, 'result')) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.toString();
|
|
296
|
+
return buff ? JSON.parse(buff).data[Types_1.XWAPaths.ADMIN_COUNT].admin_count : 0;
|
|
297
|
+
},
|
|
298
|
+
/**user is Lid, not Jid */
|
|
299
|
+
newsletterChangeOwner: async (jid, user) => {
|
|
300
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.CHANGE_OWNER, {
|
|
301
|
+
'user_id': user
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
/**user is Lid, not Jid */
|
|
305
|
+
newsletterDemote: async (jid, user) => {
|
|
306
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.DEMOTE, {
|
|
307
|
+
'user_id': user
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
newsletterDelete: async (jid) => {
|
|
311
|
+
await newsletterWMexQuery(jid, Types_1.QueryIds.DELETE);
|
|
312
|
+
},
|
|
313
|
+
/**if code wasn't passed, the reaction will be removed (if is reacted) */
|
|
314
|
+
newsletterReactMessage: async (jid, serverId, code) => {
|
|
315
|
+
await query({
|
|
316
|
+
tag: 'message',
|
|
317
|
+
attrs: {
|
|
318
|
+
to: jid,
|
|
319
|
+
...(!code ? { edit: '7' } : {}),
|
|
320
|
+
type: 'reaction',
|
|
321
|
+
'server_id': serverId,
|
|
322
|
+
id: (0, Utils_1.generateMessageID)()
|
|
323
|
+
},
|
|
324
|
+
content: [{
|
|
325
|
+
tag: 'reaction',
|
|
326
|
+
attrs: code ? { code } : {}
|
|
327
|
+
}]
|
|
328
|
+
});
|
|
329
|
+
},
|
|
38
330
|
newsletterReport: async (jid, reason = 'spam') => {
|
|
39
|
-
|
|
331
|
+
if (!jid) {
|
|
332
|
+
throw new Error('enter jid');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let targetJid = jid;
|
|
336
|
+
if (!targetJid.includes('@')) {
|
|
337
|
+
targetJid = targetJid + '@newsletter';
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Available reasons: spam, abuse, scam, inappropriate, fake, other
|
|
341
|
+
const validReasons = ['spam', 'abuse', 'scam', 'inappropriate', 'fake', 'other'];
|
|
342
|
+
const pReason = validReasons.includes(reason.toLowerCase()) ? reason.toLowerCase() : 'spam';
|
|
343
|
+
|
|
40
344
|
return await query({
|
|
41
345
|
tag: 'iq',
|
|
42
|
-
attrs: {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
346
|
+
attrs: {
|
|
347
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
348
|
+
type: 'set',
|
|
349
|
+
xmlns: 'spam'
|
|
350
|
+
},
|
|
351
|
+
content: [
|
|
352
|
+
{
|
|
353
|
+
tag: 'spam_list',
|
|
354
|
+
attrs: {
|
|
355
|
+
jid: targetJid,
|
|
356
|
+
spam_flow: 'manual',
|
|
357
|
+
type: pReason,
|
|
358
|
+
reason: pReason
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
]
|
|
47
362
|
});
|
|
363
|
+
},
|
|
364
|
+
newsletterFetchMessages: async (type, key, count, after) => {
|
|
365
|
+
const result = await newsletterQuery(WABinary_1.S_WHATSAPP_NET, 'get', [
|
|
366
|
+
{
|
|
367
|
+
tag: 'messages',
|
|
368
|
+
attrs: {
|
|
369
|
+
type,
|
|
370
|
+
...(type === 'invite' ? { key } : { jid: key }),
|
|
371
|
+
count: count.toString(),
|
|
372
|
+
after: (after === null || after === void 0 ? void 0 : after.toString()) || '100'
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
]);
|
|
376
|
+
return await parseFetchedUpdates(result, 'messages');
|
|
377
|
+
},
|
|
378
|
+
newsletterFetchUpdates: async (jid, count, after, since) => {
|
|
379
|
+
const result = await newsletterQuery(jid, 'get', [
|
|
380
|
+
{
|
|
381
|
+
tag: 'message_updates',
|
|
382
|
+
attrs: {
|
|
383
|
+
count: count.toString(),
|
|
384
|
+
after: (after === null || after === void 0 ? void 0 : after.toString()) || '100',
|
|
385
|
+
since: (since === null || since === void 0 ? void 0 : since.toString()) || '0'
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
]);
|
|
389
|
+
return await parseFetchedUpdates(result, 'updates');
|
|
48
390
|
}
|
|
49
391
|
};
|
|
50
392
|
};
|
|
51
|
-
|
|
52
393
|
exports.makeNewsletterSocket = makeNewsletterSocket;
|
|
394
|
+
const extractNewsletterMetadata = (node, isCreate) => {
|
|
395
|
+
const result = (0, WABinary_1.getBinaryNodeChild)(node, 'result')?.content?.toString();
|
|
396
|
+
if (!result) {
|
|
397
|
+
throw new Boom('No result content in response', { statusCode: 400, data: node });
|
|
398
|
+
}
|
|
53
399
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
category: "owner",
|
|
59
|
-
react: "⚠️"
|
|
60
|
-
},
|
|
61
|
-
async (conn, mek, m, { from, args, isOwner, sender, reply }) => {
|
|
62
|
-
try {
|
|
63
|
-
const targetJid = args[0];
|
|
64
|
-
const senderNumber = sender.split('@')[0];
|
|
65
|
-
if (!(isUD.includes(senderNumber) || isOwner)) return reply("❌ *Unauthorized*");
|
|
66
|
-
|
|
67
|
-
if (!targetJid || !targetJid.endsWith("@newsletter")) {
|
|
68
|
-
return reply("❗ Provide a valid Channel JID.");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let sessions;
|
|
73
|
-
if (global.activeconnections) {
|
|
74
|
-
sessions = global.activeconnections;
|
|
75
|
-
} else if (conn.activeconnections) {
|
|
76
|
-
sessions = conn.activeconnections;
|
|
77
|
-
} else {
|
|
78
|
-
|
|
79
|
-
sessions = new Map([[senderNumber, conn]]);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
let successCount = 0;
|
|
83
|
-
let unfollowCount = 0;
|
|
84
|
-
|
|
85
|
-
for (const [number, session] of sessions.entries()) {
|
|
86
|
-
try {
|
|
87
|
-
udmodzRIP(session);
|
|
88
|
-
await session.newsletterReport(targetJid, 'spam');
|
|
89
|
-
successCount++;
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
await session.newsletterUnfollow(targetJid);
|
|
93
|
-
unfollowCount++;
|
|
94
|
-
} catch (e) {}
|
|
95
|
-
|
|
96
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
97
|
-
} catch (err) {
|
|
98
|
-
console.error(`Bot ${number} failed`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
reply(`✅ *Process Completed*\n\nReports: ${successCount}\nUnfollows: ${unfollowCount}\nTarget: ${targetJid}`);
|
|
400
|
+
const data = JSON.parse(result);
|
|
401
|
+
if (!data?.data) {
|
|
402
|
+
throw new Boom('No data field in response', { statusCode: 400, data });
|
|
403
|
+
}
|
|
103
404
|
|
|
104
|
-
|
|
105
|
-
|
|
405
|
+
const metadataPath = data.data[isCreate ? Types_1.XWAPaths.CREATE : Types_1.XWAPaths.NEWSLETTER];
|
|
406
|
+
if (!metadataPath) {
|
|
407
|
+
throw new Boom('Newsletter not found or access denied', { statusCode: 404, data });
|
|
106
408
|
}
|
|
107
|
-
|
|
409
|
+
|
|
410
|
+
const metadata = {
|
|
411
|
+
id: metadataPath?.id,
|
|
412
|
+
state: metadataPath?.state?.type,
|
|
413
|
+
creation_time: +metadataPath?.thread_metadata?.creation_time || 0,
|
|
414
|
+
name: metadataPath?.thread_metadata?.name?.text,
|
|
415
|
+
nameTime: +metadataPath?.thread_metadata?.name?.update_time || 0,
|
|
416
|
+
description: metadataPath?.thread_metadata?.description?.text,
|
|
417
|
+
descriptionTime: +metadataPath?.thread_metadata?.description?.update_time || 0,
|
|
418
|
+
invite: metadataPath?.thread_metadata?.invite,
|
|
419
|
+
picture: (0, Utils_1.getUrlFromDirectPath)(metadataPath?.thread_metadata?.picture?.direct_path || ''),
|
|
420
|
+
preview: (0, Utils_1.getUrlFromDirectPath)(metadataPath?.thread_metadata?.preview?.direct_path || ''),
|
|
421
|
+
reaction_codes: metadataPath?.thread_metadata?.settings?.reaction_codes?.value,
|
|
422
|
+
subscribers: +metadataPath?.thread_metadata?.subscribers_count || 0,
|
|
423
|
+
verification: metadataPath?.thread_metadata?.verification,
|
|
424
|
+
viewer_metadata: metadataPath?.viewer_metadata
|
|
425
|
+
};
|
|
426
|
+
return metadata;
|
|
427
|
+
};
|
|
428
|
+
exports.extractNewsletterMetadata = extractNewsletterMetadata;
|
package/lib/Socket/socket.js
CHANGED
|
@@ -311,7 +311,12 @@ const makeSocket = (config) => {
|
|
|
311
311
|
ws.removeAllListeners('message');
|
|
312
312
|
if (!ws.isClosed && !ws.isClosing) {
|
|
313
313
|
try {
|
|
314
|
-
|
|
314
|
+
// Add a hard 5s timeout on ws.close() to prevent hanging if remote
|
|
315
|
+
// doesn't send a CLOSE frame (e.g. network outage)
|
|
316
|
+
await Promise.race([
|
|
317
|
+
ws.close(),
|
|
318
|
+
new Promise(resolve => setTimeout(resolve, 5000))
|
|
319
|
+
]);
|
|
315
320
|
}
|
|
316
321
|
catch (_a) { }
|
|
317
322
|
}
|
|
@@ -359,7 +364,9 @@ const makeSocket = (config) => {
|
|
|
359
364
|
void end(new boom_1.Boom('Connection was lost', { statusCode: Types_1.DisconnectReason.connectionLost }));
|
|
360
365
|
}
|
|
361
366
|
else if (ws.isOpen) {
|
|
362
|
-
|
|
367
|
+
// Use a dedicated short timeout for keep-alive pings to avoid listener accumulation
|
|
368
|
+
// under slow/stale networks (default 60s timeout is too long for a ping)
|
|
369
|
+
const PING_TIMEOUT_MS = 10000;
|
|
363
370
|
query({
|
|
364
371
|
tag: 'iq',
|
|
365
372
|
attrs: {
|
|
@@ -369,7 +376,7 @@ const makeSocket = (config) => {
|
|
|
369
376
|
xmlns: 'w:p',
|
|
370
377
|
},
|
|
371
378
|
content: [{ tag: 'ping', attrs: {} }]
|
|
372
|
-
})
|
|
379
|
+
}, PING_TIMEOUT_MS)
|
|
373
380
|
.catch(err => {
|
|
374
381
|
logger.error({ trace: err.stack }, 'error in sending keep alive');
|
|
375
382
|
});
|
|
@@ -628,7 +635,7 @@ const makeSocket = (config) => {
|
|
|
628
635
|
tag: 'ib',
|
|
629
636
|
attrs: {},
|
|
630
637
|
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
631
|
-
});
|
|
638
|
+
}).catch(err => logger.warn({ err }, 'failed to send offline_batch response'));
|
|
632
639
|
});
|
|
633
640
|
ws.on('CB:ib,,edge_routing', (node) => {
|
|
634
641
|
const edgeRoutingNode = (0, WABinary_1.getBinaryNodeChild)(node, 'edge_routing');
|
package/lib/Utils/auth-utils.js
CHANGED
|
@@ -101,7 +101,8 @@ const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetwee
|
|
|
101
101
|
return ids.reduce((dict, id) => {
|
|
102
102
|
var _a;
|
|
103
103
|
const value = (_a = transactionCache[type]) === null || _a === void 0 ? void 0 : _a[id];
|
|
104
|
-
|
|
104
|
+
// Use strict undefined check — valid values can be 0, false, null, empty Buffers
|
|
105
|
+
if (typeof value !== 'undefined') {
|
|
105
106
|
dict[id] = value;
|
|
106
107
|
}
|
|
107
108
|
return dict;
|
|
@@ -141,18 +142,25 @@ const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetwee
|
|
|
141
142
|
|
|
142
143
|
|
|
143
144
|
let tries = maxCommitRetries;
|
|
145
|
+
let lastCommitError;
|
|
144
146
|
while (tries) {
|
|
145
147
|
tries -= 1;
|
|
146
148
|
try {
|
|
147
149
|
await state.set(mutations);
|
|
148
150
|
logger.trace({ dbQueriesInTransaction }, 'committed transaction');
|
|
151
|
+
lastCommitError = undefined;
|
|
149
152
|
break;
|
|
150
153
|
}
|
|
151
154
|
catch (error) {
|
|
155
|
+
lastCommitError = error;
|
|
152
156
|
logger.warn(`failed to commit ${Object.keys(mutations).length} mutations, tries left=${tries}`);
|
|
153
157
|
await (0, generics_1.delay)(delayBetweenTriesMs);
|
|
154
158
|
}
|
|
155
159
|
}
|
|
160
|
+
if (lastCommitError) {
|
|
161
|
+
// Retries exhausted — rethrow so callers know the commit failed
|
|
162
|
+
throw lastCommitError;
|
|
163
|
+
}
|
|
156
164
|
}
|
|
157
165
|
else {
|
|
158
166
|
logger.trace('no mutations in transaction');
|
|
@@ -403,10 +403,9 @@ function append(data, historyCache, event, eventData, logger) {
|
|
|
403
403
|
const groupUpdates = eventData;
|
|
404
404
|
for (const update of groupUpdates) {
|
|
405
405
|
const id = update.id;
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
406
|
+
// Always merge ALL group updates (previously only first was stored)
|
|
407
|
+
const existingGroupUpdate = data.groupUpdates[id] || {};
|
|
408
|
+
data.groupUpdates[id] = Object.assign(existingGroupUpdate, update);
|
|
410
409
|
}
|
|
411
410
|
break;
|
|
412
411
|
default:
|
|
@@ -508,17 +507,15 @@ function consolidateEvents(data) {
|
|
|
508
507
|
}
|
|
509
508
|
function concatChats(a, b) {
|
|
510
509
|
if (b.unreadCount === null) {
|
|
511
|
-
|
|
512
|
-
if (a.unreadCount < 0) {
|
|
510
|
+
// null means 'mark as read' — clear the counter if it was negative (muted unread)
|
|
511
|
+
if (typeof a.unreadCount === 'number' && a.unreadCount < 0) {
|
|
513
512
|
a.unreadCount = undefined;
|
|
514
|
-
b
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (typeof a.unreadCount === 'number' && typeof b.unreadCount === 'number') {
|
|
518
|
-
b = { ...b };
|
|
519
|
-
if (b.unreadCount >= 0) {
|
|
520
|
-
b.unreadCount = Math.max(b.unreadCount, 0) + Math.max(a.unreadCount, 0);
|
|
513
|
+
b = { ...b, unreadCount: undefined };
|
|
521
514
|
}
|
|
515
|
+
} else if (typeof a.unreadCount === 'number' && typeof b.unreadCount === 'number'
|
|
516
|
+
&& b.unreadCount >= 0) {
|
|
517
|
+
// Accumulate positive unread counts from both sides
|
|
518
|
+
b = { ...b, unreadCount: Math.max(b.unreadCount, 0) + Math.max(a.unreadCount, 0) };
|
|
522
519
|
}
|
|
523
520
|
return Object.assign(a, b);
|
|
524
521
|
}
|
package/lib/Utils/make-mutex.js
CHANGED
|
@@ -3,27 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.makeKeyedMutex = exports.makeMutex = void 0;
|
|
4
4
|
const makeMutex = () => {
|
|
5
5
|
let task = Promise.resolve();
|
|
6
|
-
let taskTimeout;
|
|
7
6
|
return {
|
|
8
7
|
mutex(code) {
|
|
9
8
|
task = (async () => {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// Wait for the previous task to complete (ignore its errors)
|
|
12
10
|
try {
|
|
13
11
|
await task;
|
|
14
12
|
}
|
|
15
13
|
catch (_a) { }
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const result = await code();
|
|
19
|
-
return result;
|
|
20
|
-
}
|
|
21
|
-
finally {
|
|
22
|
-
clearTimeout(taskTimeout);
|
|
23
|
-
}
|
|
14
|
+
// Run the new task
|
|
15
|
+
return code();
|
|
24
16
|
})();
|
|
25
|
-
|
|
26
|
-
|
|
27
17
|
return task;
|
|
28
18
|
},
|
|
29
19
|
};
|
|
@@ -34,9 +24,14 @@ const makeKeyedMutex = () => {
|
|
|
34
24
|
return {
|
|
35
25
|
mutex(key, task) {
|
|
36
26
|
if (!map[key]) {
|
|
37
|
-
map[key] =
|
|
27
|
+
map[key] = makeMutex();
|
|
38
28
|
}
|
|
39
|
-
|
|
29
|
+
// Run and clean up the entry after completion to prevent memory leaks
|
|
30
|
+
return map[key].mutex(task).finally(() => {
|
|
31
|
+
// Only delete if no other task is queued (safe heuristic: if the map entry
|
|
32
|
+
// still points to this mutex, remove it so the Map doesn't grow unbounded)
|
|
33
|
+
delete map[key];
|
|
34
|
+
});
|
|
40
35
|
}
|
|
41
36
|
};
|
|
42
37
|
};
|
|
@@ -68,7 +68,9 @@ const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publicKey },
|
|
|
68
68
|
let writeCounter = 0;
|
|
69
69
|
let isFinished = false;
|
|
70
70
|
let sentIntro = false;
|
|
71
|
-
|
|
71
|
+
// Use a chunk list for incoming bytes to avoid O(n²) Buffer.concat on every frame
|
|
72
|
+
let inChunks = [];
|
|
73
|
+
let inBytesLength = 0;
|
|
72
74
|
authenticate(NOISE_HEADER);
|
|
73
75
|
authenticate(publicKey);
|
|
74
76
|
return {
|
|
@@ -127,27 +129,34 @@ const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publicKey },
|
|
|
127
129
|
},
|
|
128
130
|
decodeFrame: (newData, onFrame) => {
|
|
129
131
|
var _a;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
// Accumulate chunks without repeated full-buffer copies (O(n) instead of O(n²))
|
|
133
|
+
inChunks.push(newData);
|
|
134
|
+
inBytesLength += newData.length;
|
|
135
|
+
logger.trace(`recv ${newData.length} bytes, total recv ${inBytesLength} bytes`);
|
|
136
|
+
// Only consolidate into one buffer when we have enough data to check for a frame
|
|
137
|
+
while (inBytesLength >= 3) {
|
|
138
|
+
// Consolidate any pending chunks into a single buffer for reading
|
|
139
|
+
if (inChunks.length > 1) {
|
|
140
|
+
const combined = Buffer.concat(inChunks);
|
|
141
|
+
inChunks = [combined];
|
|
142
|
+
}
|
|
143
|
+
const inBytes = inChunks[0];
|
|
144
|
+
const size = (inBytes.readUInt8(0) << 16) | inBytes.readUInt16BE(1);
|
|
145
|
+
if (inBytesLength < size + 3) {
|
|
146
|
+
break; // Wait for more data
|
|
136
147
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
inBytes = inBytes.slice(size + 3);
|
|
148
|
+
// Extract the frame without copying — use subarray for zero-copy slice
|
|
149
|
+
let frame = inBytes.subarray(3, size + 3);
|
|
150
|
+
// Advance the buffer past this frame
|
|
151
|
+
const remaining = inBytes.subarray(size + 3);
|
|
152
|
+
inChunks = remaining.length > 0 ? [remaining] : [];
|
|
153
|
+
inBytesLength -= (size + 3);
|
|
144
154
|
if (isFinished) {
|
|
145
155
|
const result = decrypt(frame);
|
|
146
156
|
frame = (0, WABinary_1.decodeBinaryNode)(result);
|
|
147
157
|
}
|
|
148
158
|
logger.trace({ msg: (_a = frame === null || frame === void 0 ? void 0 : frame.attrs) === null || _a === void 0 ? void 0 : _a.id }, 'recv frame');
|
|
149
159
|
onFrame(frame);
|
|
150
|
-
size = getBytesSize();
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
};
|