@cexy/hoonfca 1.0.1 → 1.0.3
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/.travis.yml +6 -0
- package/LICENSE-MIT +21 -0
- package/index.d.ts +620 -0
- package/index.js +778 -1
- package/package.json +31 -84
- package/src/{apis/addExternalModule.js → addExternalModule.js} +4 -5
- package/src/addUserToGroup.js +113 -0
- package/src/changeAdminStatus.js +95 -0
- package/src/changeApprovalMode.js +79 -0
- package/src/changeArchivedStatus.js +55 -0
- package/src/changeBio.js +76 -0
- package/src/changeBlockedStatus.js +47 -0
- package/src/changeBlockedStatusMqtt.js +86 -0
- package/src/changeGroupImage.js +133 -0
- package/src/changeNickname.js +59 -0
- package/src/changeThreadColor.js +71 -0
- package/src/{apis/changeThreadEmoji.js → changeThreadEmoji.js} +8 -6
- package/src/createNewGroup.js +88 -0
- package/src/createPoll.js +130 -0
- package/src/deleteMessage.js +56 -0
- package/src/deleteThread.js +56 -0
- package/src/editMessage.js +63 -0
- package/src/forwardAttachment.js +60 -0
- package/src/forwardMessage.js +63 -0
- package/src/getCurrentUserID.js +7 -0
- package/src/getEmojiUrl.js +29 -0
- package/src/getFriendsList.js +84 -0
- package/src/getGroupsList.js +83 -0
- package/src/getMarketplace.js +84 -0
- package/src/getNotifications.js +86 -0
- package/src/getPagesList.js +83 -0
- package/src/getStories.js +88 -0
- package/src/getThreadHistory.js +647 -0
- package/src/getThreadInfo.js +222 -0
- package/src/getThreadList.js +292 -0
- package/src/getThreadPictures.js +79 -0
- package/src/getUserID.js +66 -0
- package/src/getUserInfo.js +128 -0
- package/src/handleFriendRequest.js +57 -0
- package/src/handleMessageRequest.js +65 -0
- package/src/{apis/httpGet.js → httpGet.js} +15 -20
- package/src/httpPost.js +58 -0
- package/src/listenMqtt.js +1018 -0
- package/src/logout.js +75 -0
- package/src/markAsDelivered.js +58 -0
- package/src/{apis/markAsRead.js → markAsRead.js} +19 -30
- package/src/markAsReadAll.js +50 -0
- package/src/markAsSeen.js +61 -0
- package/src/muteThread.js +52 -0
- package/src/pinMessage.js +61 -0
- package/src/removeUserFromGroup.js +79 -0
- package/src/resolvePhotoUrl.js +45 -0
- package/src/searchForThread.js +53 -0
- package/src/searchUsers.js +89 -0
- package/src/sendComment.js +161 -0
- package/src/sendMessage.js +457 -0
- package/src/sendTypingIndicator.js +179 -0
- package/src/setBio.js +86 -0
- package/src/setMessageReaction.js +186 -0
- package/src/setPostReaction.js +71 -0
- package/src/setTheme.js +313 -0
- package/src/setTitle.js +90 -0
- package/src/shareContact.js +53 -0
- package/src/shareLink.js +62 -0
- package/src/threadColors.js +57 -0
- package/src/unfriend.js +53 -0
- package/src/unsendMessage.js +104 -0
- package/utils.js +1447 -0
- package/LICENSE +0 -3
- package/README.md +0 -491
- package/examples/login-with-cookies.js +0 -102
- package/examples/verify.js +0 -70
- package/src/apis/addUserToGroup.js +0 -108
- package/src/apis/changeAdminStatus.js +0 -148
- package/src/apis/changeArchivedStatus.js +0 -61
- package/src/apis/changeAvatar.js +0 -103
- package/src/apis/changeBio.js +0 -69
- package/src/apis/changeBlockedStatus.js +0 -54
- package/src/apis/changeGroupImage.js +0 -136
- package/src/apis/changeThreadColor.js +0 -116
- package/src/apis/comment.js +0 -207
- package/src/apis/createAITheme.js +0 -129
- package/src/apis/createNewGroup.js +0 -79
- package/src/apis/createPoll.js +0 -73
- package/src/apis/deleteMessage.js +0 -44
- package/src/apis/deleteThread.js +0 -52
- package/src/apis/e2ee.js +0 -17
- package/src/apis/editMessage.js +0 -70
- package/src/apis/emoji.js +0 -124
- package/src/apis/fetchThemeData.js +0 -82
- package/src/apis/follow.js +0 -81
- package/src/apis/forwardMessage.js +0 -52
- package/src/apis/friend.js +0 -243
- package/src/apis/gcmember.js +0 -122
- package/src/apis/gcname.js +0 -123
- package/src/apis/gcrule.js +0 -119
- package/src/apis/getAccess.js +0 -111
- package/src/apis/getBotInfo.js +0 -88
- package/src/apis/getBotInitialData.js +0 -43
- package/src/apis/getFriendsList.js +0 -79
- package/src/apis/getMessage.js +0 -423
- package/src/apis/getTheme.js +0 -95
- package/src/apis/getThemeInfo.js +0 -116
- package/src/apis/getThreadHistory.js +0 -239
- package/src/apis/getThreadInfo.js +0 -267
- package/src/apis/getThreadList.js +0 -232
- package/src/apis/getThreadPictures.js +0 -58
- package/src/apis/getUserID.js +0 -117
- package/src/apis/getUserInfo.js +0 -513
- package/src/apis/getUserInfoV2.js +0 -146
- package/src/apis/handleMessageRequest.js +0 -50
- package/src/apis/httpPost.js +0 -89
- package/src/apis/httpPostFormData.js +0 -69
- package/src/apis/listenMqtt.js +0 -1236
- package/src/apis/listenSpeed.js +0 -179
- package/src/apis/logout.js +0 -87
- package/src/apis/markAsDelivered.js +0 -47
- package/src/apis/markAsReadAll.js +0 -40
- package/src/apis/markAsSeen.js +0 -70
- package/src/apis/mqttDeltaValue.js +0 -278
- package/src/apis/muteThread.js +0 -45
- package/src/apis/nickname.js +0 -132
- package/src/apis/notes.js +0 -163
- package/src/apis/pinMessage.js +0 -150
- package/src/apis/produceMetaTheme.js +0 -180
- package/src/apis/realtime.js +0 -182
- package/src/apis/removeUserFromGroup.js +0 -117
- package/src/apis/resolvePhotoUrl.js +0 -58
- package/src/apis/searchForThread.js +0 -154
- package/src/apis/sendMessage.js +0 -354
- package/src/apis/sendMessageMqtt.js +0 -249
- package/src/apis/sendTypingIndicator.js +0 -91
- package/src/apis/setMessageReaction.js +0 -27
- package/src/apis/setMessageReactionMqtt.js +0 -61
- package/src/apis/setThreadTheme.js +0 -260
- package/src/apis/setThreadThemeMqtt.js +0 -94
- package/src/apis/share.js +0 -107
- package/src/apis/shareContact.js +0 -66
- package/src/apis/stickers.js +0 -257
- package/src/apis/story.js +0 -181
- package/src/apis/theme.js +0 -233
- package/src/apis/unfriend.js +0 -47
- package/src/apis/unsendMessage.js +0 -17
- package/src/engine/client.js +0 -92
- package/src/engine/models/buildAPI.js +0 -152
- package/src/engine/models/loginHelper.js +0 -519
- package/src/engine/models/setOptions.js +0 -88
- package/src/security/e2ee.js +0 -109
- package/src/types/index.d.ts +0 -498
- package/src/utils/antiSuspension.js +0 -506
- package/src/utils/auth-helpers.js +0 -149
- package/src/utils/autoReLogin.js +0 -336
- package/src/utils/axios.js +0 -436
- package/src/utils/cache.js +0 -54
- package/src/utils/clients.js +0 -282
- package/src/utils/constants.js +0 -410
- package/src/utils/formatters/data/formatAttachment.js +0 -370
- package/src/utils/formatters/data/formatDelta.js +0 -109
- package/src/utils/formatters/index.js +0 -159
- package/src/utils/formatters/value/formatCookie.js +0 -91
- package/src/utils/formatters/value/formatDate.js +0 -36
- package/src/utils/formatters/value/formatID.js +0 -16
- package/src/utils/formatters.js +0 -1373
- package/src/utils/headers.js +0 -230
- package/src/utils/index.js +0 -153
- package/src/utils/monitoring.js +0 -333
- package/src/utils/rateLimiter.js +0 -319
- package/src/utils/tokenRefresh.js +0 -657
- package/src/utils/user-agents.js +0 -238
- package/src/utils/validation.js +0 -157
package/src/utils/monitoring.js
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const utils = require('./index');
|
|
4
|
-
|
|
5
|
-
class ProductionMonitor {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.metrics = {
|
|
8
|
-
requests: {
|
|
9
|
-
total: 0,
|
|
10
|
-
success: 0,
|
|
11
|
-
failed: 0,
|
|
12
|
-
byEndpoint: new Map()
|
|
13
|
-
},
|
|
14
|
-
errors: {
|
|
15
|
-
total: 0,
|
|
16
|
-
byType: new Map(),
|
|
17
|
-
byCode: new Map(),
|
|
18
|
-
recent: []
|
|
19
|
-
},
|
|
20
|
-
performance: {
|
|
21
|
-
avgResponseTime: 0,
|
|
22
|
-
slowRequests: [],
|
|
23
|
-
requestTimes: []
|
|
24
|
-
},
|
|
25
|
-
session: {
|
|
26
|
-
loginTime: null,
|
|
27
|
-
lastActivity: null,
|
|
28
|
-
tokenRefreshCount: 0,
|
|
29
|
-
reconnectCount: 0
|
|
30
|
-
},
|
|
31
|
-
rateLimiting: {
|
|
32
|
-
hitCount: 0,
|
|
33
|
-
cooldowns: 0,
|
|
34
|
-
delayedRequests: 0
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
this.config = {
|
|
39
|
-
logLevel: 'info',
|
|
40
|
-
enableMetrics: true,
|
|
41
|
-
enableErrorTracking: true,
|
|
42
|
-
performanceThreshold: 5000,
|
|
43
|
-
errorRetentionCount: 100,
|
|
44
|
-
metricsInterval: 60000
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
this.startTime = Date.now();
|
|
48
|
-
this.metricsInterval = null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
setConfig(options) {
|
|
52
|
-
Object.assign(this.config, options);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
trackRequest(endpoint, success, responseTime, error = null) {
|
|
56
|
-
if (!this.config.enableMetrics) return;
|
|
57
|
-
|
|
58
|
-
this.metrics.requests.total++;
|
|
59
|
-
if (success) {
|
|
60
|
-
this.metrics.requests.success++;
|
|
61
|
-
} else {
|
|
62
|
-
this.metrics.requests.failed++;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!this.metrics.requests.byEndpoint.has(endpoint)) {
|
|
66
|
-
this.metrics.requests.byEndpoint.set(endpoint, {
|
|
67
|
-
total: 0,
|
|
68
|
-
success: 0,
|
|
69
|
-
failed: 0,
|
|
70
|
-
avgTime: 0
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const endpointStats = this.metrics.requests.byEndpoint.get(endpoint);
|
|
75
|
-
endpointStats.total++;
|
|
76
|
-
if (success) {
|
|
77
|
-
endpointStats.success++;
|
|
78
|
-
} else {
|
|
79
|
-
endpointStats.failed++;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
endpointStats.avgTime =
|
|
83
|
-
(endpointStats.avgTime * (endpointStats.total - 1) + responseTime) / endpointStats.total;
|
|
84
|
-
|
|
85
|
-
this.trackPerformance(endpoint, responseTime);
|
|
86
|
-
|
|
87
|
-
if (error) {
|
|
88
|
-
this.trackError(error, endpoint);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
this.metrics.session.lastActivity = Date.now();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
trackPerformance(endpoint, responseTime) {
|
|
95
|
-
this.metrics.performance.requestTimes.push(responseTime);
|
|
96
|
-
|
|
97
|
-
if (this.metrics.performance.requestTimes.length > 1000) {
|
|
98
|
-
this.metrics.performance.requestTimes.shift();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const sum = this.metrics.performance.requestTimes.reduce((a, b) => a + b, 0);
|
|
102
|
-
this.metrics.performance.avgResponseTime =
|
|
103
|
-
sum / this.metrics.performance.requestTimes.length;
|
|
104
|
-
|
|
105
|
-
if (responseTime > this.config.performanceThreshold) {
|
|
106
|
-
this.metrics.performance.slowRequests.push({
|
|
107
|
-
endpoint,
|
|
108
|
-
responseTime,
|
|
109
|
-
timestamp: Date.now()
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (this.metrics.performance.slowRequests.length > 50) {
|
|
113
|
-
this.metrics.performance.slowRequests.shift();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
utils.warn("Performance", `Slow request: ${endpoint} took ${responseTime}ms`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
trackError(error, context = '') {
|
|
121
|
-
if (!this.config.enableErrorTracking) return;
|
|
122
|
-
|
|
123
|
-
this.metrics.errors.total++;
|
|
124
|
-
|
|
125
|
-
const errorType = error.errorType || error.name || 'UnknownError';
|
|
126
|
-
const errorCode = error.errorCode || error.code || 'N/A';
|
|
127
|
-
|
|
128
|
-
this.metrics.errors.byType.set(
|
|
129
|
-
errorType,
|
|
130
|
-
(this.metrics.errors.byType.get(errorType) || 0) + 1
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
this.metrics.errors.byCode.set(
|
|
134
|
-
errorCode,
|
|
135
|
-
(this.metrics.errors.byCode.get(errorCode) || 0) + 1
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
this.metrics.errors.recent.push({
|
|
139
|
-
type: errorType,
|
|
140
|
-
code: errorCode,
|
|
141
|
-
message: error.message,
|
|
142
|
-
context,
|
|
143
|
-
timestamp: Date.now(),
|
|
144
|
-
stack: error.stack
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (this.metrics.errors.recent.length > this.config.errorRetentionCount) {
|
|
148
|
-
this.metrics.errors.recent.shift();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
trackRateLimit(type, threadID = null) {
|
|
153
|
-
this.metrics.rateLimiting.hitCount++;
|
|
154
|
-
|
|
155
|
-
if (type === 'cooldown') {
|
|
156
|
-
this.metrics.rateLimiting.cooldowns++;
|
|
157
|
-
} else if (type === 'delayed') {
|
|
158
|
-
this.metrics.rateLimiting.delayedRequests++;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
trackTokenRefresh() {
|
|
163
|
-
this.metrics.session.tokenRefreshCount++;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
trackReconnect() {
|
|
167
|
-
this.metrics.session.reconnectCount++;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
setLoginTime() {
|
|
171
|
-
this.metrics.session.loginTime = Date.now();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
getMetrics() {
|
|
175
|
-
const uptime = Date.now() - this.startTime;
|
|
176
|
-
const sessionDuration = this.metrics.session.loginTime
|
|
177
|
-
? Date.now() - this.metrics.session.loginTime
|
|
178
|
-
: 0;
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
uptime,
|
|
182
|
-
sessionDuration,
|
|
183
|
-
requests: {
|
|
184
|
-
...this.metrics.requests,
|
|
185
|
-
byEndpoint: Object.fromEntries(this.metrics.requests.byEndpoint),
|
|
186
|
-
successRate: this.metrics.requests.total > 0
|
|
187
|
-
? (this.metrics.requests.success / this.metrics.requests.total * 100).toFixed(2) + '%'
|
|
188
|
-
: 'N/A'
|
|
189
|
-
},
|
|
190
|
-
errors: {
|
|
191
|
-
...this.metrics.errors,
|
|
192
|
-
byType: Object.fromEntries(this.metrics.errors.byType),
|
|
193
|
-
byCode: Object.fromEntries(this.metrics.errors.byCode),
|
|
194
|
-
errorRate: this.metrics.requests.total > 0
|
|
195
|
-
? (this.metrics.errors.total / this.metrics.requests.total * 100).toFixed(2) + '%'
|
|
196
|
-
: 'N/A'
|
|
197
|
-
},
|
|
198
|
-
performance: this.metrics.performance,
|
|
199
|
-
session: this.metrics.session,
|
|
200
|
-
rateLimiting: this.metrics.rateLimiting
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
getHealth() {
|
|
205
|
-
const metrics = this.getMetrics();
|
|
206
|
-
const health = {
|
|
207
|
-
status: 'healthy',
|
|
208
|
-
checks: {},
|
|
209
|
-
timestamp: Date.now()
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const errorRate = this.metrics.requests.total > 0
|
|
213
|
-
? (this.metrics.errors.total / this.metrics.requests.total) * 100
|
|
214
|
-
: 0;
|
|
215
|
-
|
|
216
|
-
health.checks.errorRate = {
|
|
217
|
-
status: errorRate < 5 ? 'pass' : errorRate < 15 ? 'warn' : 'fail',
|
|
218
|
-
value: errorRate.toFixed(2) + '%',
|
|
219
|
-
threshold: '5%'
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
health.checks.performance = {
|
|
223
|
-
status: this.metrics.performance.avgResponseTime < 2000 ? 'pass' :
|
|
224
|
-
this.metrics.performance.avgResponseTime < 5000 ? 'warn' : 'fail',
|
|
225
|
-
value: Math.round(this.metrics.performance.avgResponseTime) + 'ms',
|
|
226
|
-
threshold: '2000ms'
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
health.checks.session = {
|
|
230
|
-
status: this.metrics.session.loginTime ? 'pass' : 'fail',
|
|
231
|
-
value: this.metrics.session.loginTime ? 'active' : 'not logged in'
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
health.checks.rateLimiting = {
|
|
235
|
-
status: this.metrics.rateLimiting.hitCount < 100 ? 'pass' :
|
|
236
|
-
this.metrics.rateLimiting.hitCount < 500 ? 'warn' : 'fail',
|
|
237
|
-
value: this.metrics.rateLimiting.hitCount,
|
|
238
|
-
threshold: '100 hits'
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const failedChecks = Object.values(health.checks).filter(c => c.status === 'fail').length;
|
|
242
|
-
const warnChecks = Object.values(health.checks).filter(c => c.status === 'warn').length;
|
|
243
|
-
|
|
244
|
-
if (failedChecks > 0) {
|
|
245
|
-
health.status = 'unhealthy';
|
|
246
|
-
} else if (warnChecks > 0) {
|
|
247
|
-
health.status = 'degraded';
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return health;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
reset() {
|
|
254
|
-
this.metrics.requests.total = 0;
|
|
255
|
-
this.metrics.requests.success = 0;
|
|
256
|
-
this.metrics.requests.failed = 0;
|
|
257
|
-
this.metrics.requests.byEndpoint.clear();
|
|
258
|
-
this.metrics.errors.total = 0;
|
|
259
|
-
this.metrics.errors.byType.clear();
|
|
260
|
-
this.metrics.errors.byCode.clear();
|
|
261
|
-
this.metrics.errors.recent = [];
|
|
262
|
-
this.metrics.performance.slowRequests = [];
|
|
263
|
-
this.metrics.performance.requestTimes = [];
|
|
264
|
-
this.metrics.rateLimiting.hitCount = 0;
|
|
265
|
-
this.metrics.rateLimiting.cooldowns = 0;
|
|
266
|
-
this.metrics.rateLimiting.delayedRequests = 0;
|
|
267
|
-
|
|
268
|
-
utils.success("Monitoring", "All metrics have been reset successfully");
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
displayHealthStatus() {
|
|
272
|
-
const health = this.getHealth();
|
|
273
|
-
console.log('\n' + '='.repeat(60));
|
|
274
|
-
if (health.status === 'healthy') {
|
|
275
|
-
console.log('SYSTEM HEALTH: HEALTHY');
|
|
276
|
-
} else if (health.status === 'degraded') {
|
|
277
|
-
console.log('SYSTEM HEALTH: DEGRADED');
|
|
278
|
-
} else {
|
|
279
|
-
console.log('SYSTEM HEALTH: UNHEALTHY');
|
|
280
|
-
}
|
|
281
|
-
console.log('='.repeat(60));
|
|
282
|
-
Object.entries(health.checks).forEach(([name, check]) => {
|
|
283
|
-
const nameFormatted = name.charAt(0).toUpperCase() + name.slice(1);
|
|
284
|
-
console.log(`\n${nameFormatted}:`);
|
|
285
|
-
console.log(` Status: ${check.status.toUpperCase()}`);
|
|
286
|
-
console.log(` Value: ${check.value}`);
|
|
287
|
-
if (check.threshold) {
|
|
288
|
-
console.log(` Threshold: ${check.threshold}`);
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
console.log('\n' + '='.repeat(60) + '\n');
|
|
292
|
-
return health;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
startPeriodicReporting(interval = 60000) {
|
|
296
|
-
if (this.metricsInterval) {
|
|
297
|
-
clearInterval(this.metricsInterval);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
this.metricsInterval = setInterval(() => {
|
|
301
|
-
const metrics = this.getMetrics();
|
|
302
|
-
console.log('\n' + '='.repeat(60));
|
|
303
|
-
console.log('PERFORMANCE METRICS REPORT');
|
|
304
|
-
console.log('='.repeat(60));
|
|
305
|
-
console.log('\nRequests:');
|
|
306
|
-
console.log(` Total: ${metrics.requests.total}`);
|
|
307
|
-
console.log(` Success Rate: ${metrics.requests.successRate}`);
|
|
308
|
-
console.log('\nErrors:');
|
|
309
|
-
console.log(` Total: ${metrics.errors.total}`);
|
|
310
|
-
console.log(` Error Rate: ${metrics.errors.errorRate}`);
|
|
311
|
-
console.log('\nPerformance:');
|
|
312
|
-
const avgTime = Math.round(metrics.performance.avgResponseTime);
|
|
313
|
-
console.log(` Avg Response Time: ${avgTime}ms`);
|
|
314
|
-
console.log('\nRate Limiting:');
|
|
315
|
-
console.log(` Total Hits: ${metrics.rateLimiting.hitCount}`);
|
|
316
|
-
console.log('='.repeat(60) + '\n');
|
|
317
|
-
}, interval);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
stopPeriodicReporting() {
|
|
321
|
-
if (this.metricsInterval) {
|
|
322
|
-
clearInterval(this.metricsInterval);
|
|
323
|
-
this.metricsInterval = null;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const globalMonitor = new ProductionMonitor();
|
|
329
|
-
|
|
330
|
-
module.exports = {
|
|
331
|
-
ProductionMonitor,
|
|
332
|
-
globalMonitor
|
|
333
|
-
};
|
package/src/utils/rateLimiter.js
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Adaptive Rate Limiting Manager - Optimized for Performance
|
|
5
|
-
* Sliding-window per-minute and per-second rate limiting to prevent
|
|
6
|
-
* Facebook from flagging automated behaviour.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
class RateLimiter {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.threadCooldowns = new Map();
|
|
12
|
-
this.endpointCooldowns = new Map();
|
|
13
|
-
this.errorCache = new Map();
|
|
14
|
-
|
|
15
|
-
this.ERROR_CACHE_TTL = 300000;
|
|
16
|
-
this.COOLDOWN_DURATION = 60000;
|
|
17
|
-
this.MAX_REQUESTS_PER_MINUTE = 80; // Increased from 50 for better throughput
|
|
18
|
-
this.MAX_CONCURRENT_REQUESTS = 8; // Increased from 5
|
|
19
|
-
|
|
20
|
-
this.activeRequests = 0;
|
|
21
|
-
|
|
22
|
-
// Sliding window: store timestamps of recent requests
|
|
23
|
-
this._requestWindow = [];
|
|
24
|
-
this._WINDOW_MS = 60000;
|
|
25
|
-
|
|
26
|
-
// Per-endpoint sliding windows
|
|
27
|
-
this._endpointWindows = new Map();
|
|
28
|
-
this._MAX_PER_ENDPOINT_PER_MINUTE = 30; // Increased from 20
|
|
29
|
-
|
|
30
|
-
// Fast path cache for cooldown checks
|
|
31
|
-
this._cooldownCache = new Map();
|
|
32
|
-
this._COOLDOWN_CACHE_TTL = 500; // 500ms cache for cooldown checks
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
configure(opts = {}) {
|
|
36
|
-
if (typeof opts.maxConcurrentRequests === 'number' && opts.maxConcurrentRequests > 0 && opts.maxConcurrentRequests <= 50) {
|
|
37
|
-
this.MAX_CONCURRENT_REQUESTS = Math.floor(opts.maxConcurrentRequests);
|
|
38
|
-
}
|
|
39
|
-
if (typeof opts.maxRequestsPerMinute === 'number' && opts.maxRequestsPerMinute > 0 && opts.maxRequestsPerMinute <= 1000) {
|
|
40
|
-
this.MAX_REQUESTS_PER_MINUTE = Math.floor(opts.maxRequestsPerMinute);
|
|
41
|
-
}
|
|
42
|
-
if (typeof opts.requestCooldownMs === 'number' && opts.requestCooldownMs >= 0 && opts.requestCooldownMs <= 10 * 60 * 1000) {
|
|
43
|
-
this.COOLDOWN_DURATION = Math.floor(opts.requestCooldownMs);
|
|
44
|
-
}
|
|
45
|
-
if (typeof opts.errorCacheTtlMs === 'number' && opts.errorCacheTtlMs >= 0 && opts.errorCacheTtlMs <= 24 * 60 * 60 * 1000) {
|
|
46
|
-
this.ERROR_CACHE_TTL = Math.floor(opts.errorCacheTtlMs);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ─── Thread cooldowns ─────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
isThreadOnCooldown(threadID) {
|
|
53
|
-
const now = Date.now();
|
|
54
|
-
// Fast path: check cache first
|
|
55
|
-
const cacheKey = `t:${threadID}`;
|
|
56
|
-
const cached = this._cooldownCache.get(cacheKey);
|
|
57
|
-
if (cached && now - cached.ts < this._COOLDOWN_CACHE_TTL) {
|
|
58
|
-
return cached.result;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const cooldownEnd = this.threadCooldowns.get(threadID);
|
|
62
|
-
let result = false;
|
|
63
|
-
if (!cooldownEnd) {
|
|
64
|
-
result = false;
|
|
65
|
-
} else if (now >= cooldownEnd) {
|
|
66
|
-
this.threadCooldowns.delete(threadID);
|
|
67
|
-
result = false;
|
|
68
|
-
} else {
|
|
69
|
-
result = true;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
this._cooldownCache.set(cacheKey, { ts: now, result });
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
setThreadCooldown(threadID, duration = null) {
|
|
77
|
-
this.threadCooldowns.set(threadID, Date.now() + (duration || this.COOLDOWN_DURATION));
|
|
78
|
-
// Invalidate cache
|
|
79
|
-
this._cooldownCache.delete(`t:${threadID}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ─── Endpoint cooldowns ───────────────────────────────────────────────────
|
|
83
|
-
|
|
84
|
-
isEndpointOnCooldown(endpoint) {
|
|
85
|
-
const now = Date.now();
|
|
86
|
-
// Fast path: check cache
|
|
87
|
-
const cacheKey = `e:${endpoint}`;
|
|
88
|
-
const cached = this._cooldownCache.get(cacheKey);
|
|
89
|
-
if (cached && now - cached.ts < this._COOLDOWN_CACHE_TTL) {
|
|
90
|
-
return cached.result;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const cooldownEnd = this.endpointCooldowns.get(endpoint);
|
|
94
|
-
let result = false;
|
|
95
|
-
if (!cooldownEnd) {
|
|
96
|
-
result = false;
|
|
97
|
-
} else if (now >= cooldownEnd) {
|
|
98
|
-
this.endpointCooldowns.delete(endpoint);
|
|
99
|
-
result = false;
|
|
100
|
-
} else {
|
|
101
|
-
result = true;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this._cooldownCache.set(cacheKey, { ts: now, result });
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
setEndpointCooldown(endpoint, duration = null) {
|
|
109
|
-
this.endpointCooldowns.set(endpoint, Date.now() + (duration || this.COOLDOWN_DURATION));
|
|
110
|
-
this._cooldownCache.delete(`e:${endpoint}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ─── Error suppression ────────────────────────────────────────────────────
|
|
114
|
-
|
|
115
|
-
shouldSuppressError(key) {
|
|
116
|
-
const cachedTime = this.errorCache.get(key);
|
|
117
|
-
if (!cachedTime) {
|
|
118
|
-
this.errorCache.set(key, Date.now());
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
if (Date.now() - cachedTime > this.ERROR_CACHE_TTL) {
|
|
122
|
-
this.errorCache.set(key, Date.now());
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ─── Sliding-window rate checking ─────────────────────────────────────────
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Prune timestamps older than the window and return the current count.
|
|
132
|
-
* Optimized with batch pruning.
|
|
133
|
-
*/
|
|
134
|
-
_pruneWindow(arr) {
|
|
135
|
-
const cutoff = Date.now() - this._WINDOW_MS;
|
|
136
|
-
// Binary search for the first valid timestamp
|
|
137
|
-
let left = 0, right = arr.length;
|
|
138
|
-
while (left < right) {
|
|
139
|
-
const mid = Math.floor((left + right) / 2);
|
|
140
|
-
if (arr[mid] < cutoff) {
|
|
141
|
-
left = mid + 1;
|
|
142
|
-
} else {
|
|
143
|
-
right = mid;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (left > 0) {
|
|
147
|
-
arr.splice(0, left);
|
|
148
|
-
}
|
|
149
|
-
return arr.length;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
isGloballyRateLimited() {
|
|
153
|
-
const count = this._pruneWindow(this._requestWindow);
|
|
154
|
-
return count >= this.MAX_REQUESTS_PER_MINUTE;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
isEndpointRateLimited(endpoint) {
|
|
158
|
-
if (!this._endpointWindows.has(endpoint)) return false;
|
|
159
|
-
const count = this._pruneWindow(this._endpointWindows.get(endpoint));
|
|
160
|
-
return count >= this._MAX_PER_ENDPOINT_PER_MINUTE;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
_recordRequest(endpoint) {
|
|
164
|
-
const now = Date.now();
|
|
165
|
-
this._requestWindow.push(now);
|
|
166
|
-
// Prune less frequently for better performance
|
|
167
|
-
if (this._requestWindow.length > this.MAX_REQUESTS_PER_MINUTE * 3) {
|
|
168
|
-
this._pruneWindow(this._requestWindow);
|
|
169
|
-
}
|
|
170
|
-
if (endpoint) {
|
|
171
|
-
if (!this._endpointWindows.has(endpoint)) {
|
|
172
|
-
this._endpointWindows.set(endpoint, []);
|
|
173
|
-
}
|
|
174
|
-
const ew = this._endpointWindows.get(endpoint);
|
|
175
|
-
ew.push(now);
|
|
176
|
-
if (ew.length > this._MAX_PER_ENDPOINT_PER_MINUTE * 3) {
|
|
177
|
-
this._pruneWindow(ew);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ─── Adaptive delay ───────────────────────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
getAdaptiveDelay(retryCount, errorCode = null) {
|
|
185
|
-
const baseDelays = [1000, 2500, 5000, 10000]; // Reduced base delays
|
|
186
|
-
const base = baseDelays[Math.min(retryCount, baseDelays.length - 1)];
|
|
187
|
-
|
|
188
|
-
if (errorCode === 1545012 || errorCode === 1675004) {
|
|
189
|
-
return base * 1.5; // Reduced from 2x
|
|
190
|
-
}
|
|
191
|
-
if (errorCode === 368 || errorCode === 10) {
|
|
192
|
-
return base * 2; // Reduced from 3x
|
|
193
|
-
}
|
|
194
|
-
return base;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async addHumanizedDelay(min = 100, max = 350) {
|
|
198
|
-
// Speed mode: humanized send delay disabled — no artificial wait.
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Check global and concurrent rate limits.
|
|
204
|
-
* Will wait until below limit, then record the request.
|
|
205
|
-
*/
|
|
206
|
-
async checkRateLimit(skipHumanDelay = false, endpoint = null) {
|
|
207
|
-
// Fast path: try immediate slot acquisition
|
|
208
|
-
if (this.activeRequests < this.MAX_CONCURRENT_REQUESTS) {
|
|
209
|
-
if (!this.isGloballyRateLimited()) {
|
|
210
|
-
if (!endpoint || !this.isEndpointRateLimited(endpoint)) {
|
|
211
|
-
if (!skipHumanDelay) {
|
|
212
|
-
await this.addHumanizedDelay();
|
|
213
|
-
}
|
|
214
|
-
this.activeRequests++;
|
|
215
|
-
this._recordRequest(endpoint);
|
|
216
|
-
return () => {
|
|
217
|
-
this.activeRequests = Math.max(0, this.activeRequests - 1);
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Slow path: wait for slot
|
|
224
|
-
while (this.activeRequests >= this.MAX_CONCURRENT_REQUESTS) {
|
|
225
|
-
await new Promise(resolve => setTimeout(resolve, 50)); // Reduced from 100ms
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Wait for per-minute global window to clear
|
|
229
|
-
let waitCycles = 0;
|
|
230
|
-
const maxCycles = 120; // Increased from 60
|
|
231
|
-
while (this.isGloballyRateLimited()) {
|
|
232
|
-
if (waitCycles++ > maxCycles) break;
|
|
233
|
-
await new Promise(resolve => setTimeout(resolve, 500)); // Reduced from 1000ms
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Wait for per-endpoint window to clear
|
|
237
|
-
if (endpoint) {
|
|
238
|
-
let epCycles = 0;
|
|
239
|
-
const maxEpCycles = 60; // Increased from 30
|
|
240
|
-
while (this.isEndpointRateLimited(endpoint)) {
|
|
241
|
-
if (epCycles++ > maxEpCycles) break;
|
|
242
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (!skipHumanDelay) {
|
|
247
|
-
await this.addHumanizedDelay();
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
this.activeRequests++;
|
|
251
|
-
this._recordRequest(endpoint);
|
|
252
|
-
|
|
253
|
-
return () => {
|
|
254
|
-
this.activeRequests = Math.max(0, this.activeRequests - 1);
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// ─── Cleanup ──────────────────────────────────────────────────────────────
|
|
259
|
-
|
|
260
|
-
cleanup() {
|
|
261
|
-
const now = Date.now();
|
|
262
|
-
|
|
263
|
-
for (const [key, time] of this.errorCache.entries()) {
|
|
264
|
-
if (now - time > this.ERROR_CACHE_TTL) this.errorCache.delete(key);
|
|
265
|
-
}
|
|
266
|
-
for (const [key, time] of this.threadCooldowns.entries()) {
|
|
267
|
-
if (now >= time) this.threadCooldowns.delete(key);
|
|
268
|
-
}
|
|
269
|
-
for (const [key, time] of this.endpointCooldowns.entries()) {
|
|
270
|
-
if (now >= time) this.endpointCooldowns.delete(key);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Prune all endpoint windows
|
|
274
|
-
for (const [key, arr] of this._endpointWindows.entries()) {
|
|
275
|
-
this._pruneWindow(arr);
|
|
276
|
-
if (arr.length === 0) this._endpointWindows.delete(key);
|
|
277
|
-
}
|
|
278
|
-
this._pruneWindow(this._requestWindow);
|
|
279
|
-
|
|
280
|
-
// Clear cooldown cache
|
|
281
|
-
this._cooldownCache.clear();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
getStats() {
|
|
285
|
-
this._pruneWindow(this._requestWindow);
|
|
286
|
-
return {
|
|
287
|
-
activeRequests: this.activeRequests,
|
|
288
|
-
maxConcurrentRequests: this.MAX_CONCURRENT_REQUESTS,
|
|
289
|
-
maxRequestsPerMinute: this.MAX_REQUESTS_PER_MINUTE,
|
|
290
|
-
requestsInLastMinute: this._requestWindow.length,
|
|
291
|
-
threadCooldowns: this.threadCooldowns.size,
|
|
292
|
-
endpointCooldowns: this.endpointCooldowns.size,
|
|
293
|
-
errorCacheSize: this.errorCache.size
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
getCooldownRemaining(threadID) {
|
|
298
|
-
const cooldownEnd = this.threadCooldowns.get(threadID);
|
|
299
|
-
if (!cooldownEnd) return 0;
|
|
300
|
-
return Math.max(0, cooldownEnd - Date.now());
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
getEndpointCooldownRemaining(endpoint) {
|
|
304
|
-
const cooldownEnd = this.endpointCooldowns.get(endpoint);
|
|
305
|
-
if (!cooldownEnd) return 0;
|
|
306
|
-
return Math.max(0, cooldownEnd - Date.now());
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const globalRateLimiter = new RateLimiter();
|
|
311
|
-
|
|
312
|
-
setInterval(() => globalRateLimiter.cleanup(), 60000);
|
|
313
|
-
|
|
314
|
-
module.exports = {
|
|
315
|
-
RateLimiter,
|
|
316
|
-
globalRateLimiter,
|
|
317
|
-
configureRateLimiter: (opts) => globalRateLimiter.configure(opts),
|
|
318
|
-
getRateLimiterStats: () => globalRateLimiter.getStats()
|
|
319
|
-
};
|