fca-neokex-fix 1.0.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/LICENSE +26 -0
  3. package/README.md +346 -0
  4. package/THEME_FEATURES.md +137 -0
  5. package/examples/README.md +131 -0
  6. package/examples/apply-ai-theme.js +127 -0
  7. package/examples/check-current-theme.js +74 -0
  8. package/examples/simple-bot.js +114 -0
  9. package/examples/test-bot.js +752 -0
  10. package/examples/test-logging.js +85 -0
  11. package/examples/theme-usage-example.js +53 -0
  12. package/index.js +2 -0
  13. package/package.json +105 -0
  14. package/src/apis/addExternalModule.js +24 -0
  15. package/src/apis/addUserToGroup.js +108 -0
  16. package/src/apis/changeAdminStatus.js +148 -0
  17. package/src/apis/changeArchivedStatus.js +61 -0
  18. package/src/apis/changeAvatar.js +103 -0
  19. package/src/apis/changeBio.js +69 -0
  20. package/src/apis/changeBlockedStatus.js +54 -0
  21. package/src/apis/changeGroupImage.js +136 -0
  22. package/src/apis/changeThreadColor.js +116 -0
  23. package/src/apis/comment.js +207 -0
  24. package/src/apis/createAITheme.js +129 -0
  25. package/src/apis/createNewGroup.js +79 -0
  26. package/src/apis/createPoll.js +73 -0
  27. package/src/apis/deleteMessage.js +44 -0
  28. package/src/apis/deleteThread.js +52 -0
  29. package/src/apis/editMessage.js +70 -0
  30. package/src/apis/emoji.js +124 -0
  31. package/src/apis/fetchThemeData.js +65 -0
  32. package/src/apis/follow.js +81 -0
  33. package/src/apis/forwardMessage.js +52 -0
  34. package/src/apis/friend.js +243 -0
  35. package/src/apis/gcmember.js +122 -0
  36. package/src/apis/gcname.js +123 -0
  37. package/src/apis/gcrule.js +119 -0
  38. package/src/apis/getAccess.js +111 -0
  39. package/src/apis/getBotInfo.js +88 -0
  40. package/src/apis/getBotInitialData.js +43 -0
  41. package/src/apis/getFriendsList.js +79 -0
  42. package/src/apis/getMessage.js +423 -0
  43. package/src/apis/getTheme.js +104 -0
  44. package/src/apis/getThemeInfo.js +96 -0
  45. package/src/apis/getThreadHistory.js +239 -0
  46. package/src/apis/getThreadInfo.js +257 -0
  47. package/src/apis/getThreadList.js +222 -0
  48. package/src/apis/getThreadPictures.js +58 -0
  49. package/src/apis/getUserID.js +83 -0
  50. package/src/apis/getUserInfo.js +495 -0
  51. package/src/apis/getUserInfoV2.js +146 -0
  52. package/src/apis/handleMessageRequest.js +50 -0
  53. package/src/apis/httpGet.js +63 -0
  54. package/src/apis/httpPost.js +89 -0
  55. package/src/apis/httpPostFormData.js +69 -0
  56. package/src/apis/listenMqtt.js +796 -0
  57. package/src/apis/listenSpeed.js +170 -0
  58. package/src/apis/logout.js +63 -0
  59. package/src/apis/markAsDelivered.js +47 -0
  60. package/src/apis/markAsRead.js +95 -0
  61. package/src/apis/markAsReadAll.js +41 -0
  62. package/src/apis/markAsSeen.js +70 -0
  63. package/src/apis/mqttDeltaValue.js +330 -0
  64. package/src/apis/muteThread.js +45 -0
  65. package/src/apis/nickname.js +132 -0
  66. package/src/apis/notes.js +163 -0
  67. package/src/apis/pinMessage.js +141 -0
  68. package/src/apis/produceMetaTheme.js +180 -0
  69. package/src/apis/realtime.js +161 -0
  70. package/src/apis/removeUserFromGroup.js +117 -0
  71. package/src/apis/resolvePhotoUrl.js +58 -0
  72. package/src/apis/searchForThread.js +154 -0
  73. package/src/apis/sendMessage.js +281 -0
  74. package/src/apis/sendMessageMqtt.js +188 -0
  75. package/src/apis/sendTypingIndicator.js +41 -0
  76. package/src/apis/setMessageReaction.js +27 -0
  77. package/src/apis/setMessageReactionMqtt.js +61 -0
  78. package/src/apis/setThreadTheme.js +260 -0
  79. package/src/apis/setThreadThemeMqtt.js +94 -0
  80. package/src/apis/share.js +107 -0
  81. package/src/apis/shareContact.js +66 -0
  82. package/src/apis/stickers.js +257 -0
  83. package/src/apis/story.js +181 -0
  84. package/src/apis/theme.js +233 -0
  85. package/src/apis/unfriend.js +47 -0
  86. package/src/apis/unsendMessage.js +17 -0
  87. package/src/database/appStateBackup.js +189 -0
  88. package/src/database/models/index.js +56 -0
  89. package/src/database/models/thread.js +31 -0
  90. package/src/database/models/user.js +32 -0
  91. package/src/database/threadData.js +101 -0
  92. package/src/database/userData.js +90 -0
  93. package/src/engine/client.js +91 -0
  94. package/src/engine/models/buildAPI.js +109 -0
  95. package/src/engine/models/loginHelper.js +326 -0
  96. package/src/engine/models/setOptions.js +53 -0
  97. package/src/utils/auth-helpers.js +149 -0
  98. package/src/utils/autoReLogin.js +169 -0
  99. package/src/utils/axios.js +290 -0
  100. package/src/utils/clients.js +270 -0
  101. package/src/utils/constants.js +396 -0
  102. package/src/utils/formatters/data/formatAttachment.js +370 -0
  103. package/src/utils/formatters/data/formatDelta.js +153 -0
  104. package/src/utils/formatters/index.js +159 -0
  105. package/src/utils/formatters/value/formatCookie.js +91 -0
  106. package/src/utils/formatters/value/formatDate.js +36 -0
  107. package/src/utils/formatters/value/formatID.js +16 -0
  108. package/src/utils/formatters.js +1067 -0
  109. package/src/utils/headers.js +199 -0
  110. package/src/utils/index.js +151 -0
  111. package/src/utils/monitoring.js +358 -0
  112. package/src/utils/rateLimiter.js +380 -0
  113. package/src/utils/tokenRefresh.js +311 -0
  114. package/src/utils/user-agents.js +238 -0
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+
3
+ const { randomUserAgent, generateUserAgentByPersona } = require("./user-agents");
4
+
5
+ const LOCALE_PROFILES = [
6
+ 'en-US,en;q=0.9',
7
+ 'en-GB,en;q=0.9,en-US;q=0.8',
8
+ 'en-US,en;q=0.9,es;q=0.8',
9
+ 'en-US,en;q=0.9,fr;q=0.8',
10
+ 'en-CA,en;q=0.9,fr;q=0.8',
11
+ 'en-AU,en;q=0.9,en-GB;q=0.8'
12
+ ];
13
+
14
+ const TIMEZONE_OFFSETS = [-480, -420, -360, -300, -240, -180, -120, 0, 60, 120, 180, 240, 300, 360, 480, 540, 600];
15
+
16
+ function getRandomLocale() {
17
+ return LOCALE_PROFILES[Math.floor(Math.random() * LOCALE_PROFILES.length)];
18
+ }
19
+
20
+ function getRandomTimezone() {
21
+ return TIMEZONE_OFFSETS[Math.floor(Math.random() * TIMEZONE_OFFSETS.length)];
22
+ }
23
+
24
+ /**
25
+ * Generates a comprehensive and realistic set of headers for requests to Facebook.
26
+ * @param {string} url - The target URL.
27
+ * @param {object} options - Global options from context.
28
+ * @param {object} ctx - The application context (containing fb_dtsg, lsd, etc.).
29
+ * @param {object} customHeader - Any extra headers to merge.
30
+ * @param {string} requestType - Type of request: 'xhr' for GraphQL/AJAX or 'navigate' for page navigation
31
+ * @returns {object} A complete headers object.
32
+ */
33
+ function getHeaders(url, options, ctx, customHeader, requestType = 'navigate') {
34
+ const persona = options?.persona || 'desktop';
35
+ const isAndroid = persona === 'android' || persona === 'mobile';
36
+
37
+ let userAgent, secChUa, secChUaFullVersionList, secChUaPlatform, secChUaPlatformVersion;
38
+ let androidData = null;
39
+
40
+ if (isAndroid) {
41
+ if (options && options.cachedAndroidUA) {
42
+ userAgent = options.cachedAndroidUA;
43
+ androidData = {
44
+ resolution: options.cachedAndroidResolution,
45
+ locale: options.cachedAndroidLocale,
46
+ device: options.cachedAndroidDevice
47
+ };
48
+ } else {
49
+ const generated = generateUserAgentByPersona('android', options);
50
+ userAgent = generated.userAgent;
51
+ androidData = {
52
+ resolution: generated.resolution,
53
+ locale: generated.locale,
54
+ device: generated.device
55
+ };
56
+ }
57
+ } else {
58
+ if (options && options.cachedUserAgent) {
59
+ userAgent = options.cachedUserAgent;
60
+ secChUa = options.cachedSecChUa;
61
+ secChUaFullVersionList = options.cachedSecChUaFullVersionList;
62
+ secChUaPlatform = options.cachedSecChUaPlatform;
63
+ secChUaPlatformVersion = options.cachedSecChUaPlatformVersion;
64
+ } else {
65
+ const generated = randomUserAgent();
66
+ userAgent = generated.userAgent;
67
+ secChUa = generated.secChUa;
68
+ secChUaFullVersionList = generated.secChUaFullVersionList;
69
+ secChUaPlatform = generated.secChUaPlatform;
70
+ secChUaPlatformVersion = generated.secChUaPlatformVersion;
71
+ }
72
+ }
73
+
74
+ const host = new URL(url).hostname;
75
+ const referer = `https://${host}/`;
76
+
77
+ const isXhr = requestType === 'xhr';
78
+
79
+ const locales = options?.cachedLocale || (androidData?.locale ? androidData.locale.replace('_', '-') : getRandomLocale());
80
+ const timezone = options?.cachedTimezone || getRandomTimezone();
81
+
82
+ if (isAndroid) {
83
+ const headers = {
84
+ 'Content-Type': 'application/x-www-form-urlencoded',
85
+ 'Host': host,
86
+ 'Connection': 'keep-alive',
87
+ 'User-Agent': userAgent,
88
+ 'Accept': '*/*',
89
+ 'Accept-Language': locales,
90
+ 'Accept-Encoding': 'gzip, deflate',
91
+ 'X-FB-HTTP-Engine': 'Liger'
92
+ };
93
+
94
+ if (androidData && androidData.resolution) {
95
+ headers['X-FB-Client-Density'] = String(androidData.resolution.density);
96
+ }
97
+
98
+ if (ctx) {
99
+ if (ctx.lsd || ctx.fb_dtsg) {
100
+ headers['X-Fb-Lsd'] = ctx.lsd || ctx.fb_dtsg;
101
+ }
102
+ if (ctx.region) {
103
+ headers['X-MSGR-Region'] = ctx.region;
104
+ }
105
+ if (ctx.master) {
106
+ const { __spin_r, __spin_b, __spin_t } = ctx.master;
107
+ if (__spin_r) headers['X-Fb-Spin-R'] = String(__spin_r);
108
+ if (__spin_b) headers['X-Fb-Spin-B'] = String(__spin_b);
109
+ if (__spin_t) headers['X-Fb-Spin-T'] = String(__spin_t);
110
+ }
111
+ }
112
+
113
+ if (customHeader) {
114
+ Object.assign(headers, customHeader);
115
+ if (customHeader.noRef) {
116
+ delete headers.Referer;
117
+ }
118
+ }
119
+
120
+ return headers;
121
+ }
122
+
123
+ const isWindows = secChUaPlatform === '"Windows"';
124
+ const isMac = secChUaPlatform === '"macOS"';
125
+ const isLinux = secChUaPlatform === '"Linux"';
126
+
127
+ const headers = {
128
+ 'Accept': isXhr ? '*/*' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
129
+ 'Accept-Language': locales,
130
+ 'Accept-Encoding': 'gzip, deflate, br',
131
+ 'Cache-Control': 'no-cache',
132
+ 'Connection': 'keep-alive',
133
+ 'DNT': '1',
134
+ 'Dpr': '1',
135
+ 'Host': host,
136
+ 'Pragma': 'no-cache',
137
+ 'Referer': referer,
138
+ 'Sec-Ch-Prefers-Color-Scheme': 'light',
139
+ 'Sec-Ch-Ua': secChUa,
140
+ 'Sec-Ch-Ua-Full-Version-List': secChUaFullVersionList,
141
+ 'Sec-Ch-Ua-Mobile': '?0',
142
+ 'Sec-Ch-Ua-Model': '""',
143
+ 'Sec-Ch-Ua-Platform': secChUaPlatform,
144
+ 'Sec-Ch-Ua-Platform-Version': secChUaPlatformVersion,
145
+ 'Sec-Fetch-Dest': isXhr ? 'empty' : 'document',
146
+ 'Sec-Fetch-Mode': isXhr ? 'cors' : 'navigate',
147
+ 'Sec-Fetch-Site': isXhr ? 'same-origin' : 'none',
148
+ 'User-Agent': userAgent,
149
+ 'Viewport-Width': '1920',
150
+ 'X-FB-Timezone-Offset': String(timezone * 60)
151
+ };
152
+
153
+ if (isWindows || isMac || isLinux) {
154
+ headers['Sec-Ch-Ua-Arch'] = '"x86"';
155
+ headers['Sec-Ch-Ua-Bitness'] = '"64"';
156
+ }
157
+
158
+ if (isXhr) {
159
+ headers['Origin'] = `https://${host}`;
160
+ headers['X-Requested-With'] = 'XMLHttpRequest';
161
+ } else {
162
+ headers['Sec-Fetch-User'] = '?1';
163
+ headers['Upgrade-Insecure-Requests'] = '1';
164
+ }
165
+
166
+ if (ctx) {
167
+ if (ctx.lsd || ctx.fb_dtsg) {
168
+ headers['X-Fb-Lsd'] = ctx.lsd || ctx.fb_dtsg;
169
+ }
170
+ if (ctx.region) {
171
+ headers['X-MSGR-Region'] = ctx.region;
172
+ }
173
+ if (ctx.master) {
174
+ const { __spin_r, __spin_b, __spin_t } = ctx.master;
175
+ if (__spin_r) headers['X-Fb-Spin-R'] = String(__spin_r);
176
+ if (__spin_b) headers['X-Fb-Spin-B'] = String(__spin_b);
177
+ if (__spin_t) headers['X-Fb-Spin-T'] = String(__spin_t);
178
+ }
179
+ }
180
+
181
+
182
+ if (customHeader) {
183
+ Object.assign(headers, customHeader);
184
+ if (customHeader.noRef) {
185
+ delete headers.Referer;
186
+ }
187
+ }
188
+
189
+ return headers;
190
+ }
191
+
192
+ const meta = (prop) => new RegExp(`<meta property="${prop}" content="([^"]*)"`);
193
+
194
+ module.exports = {
195
+ getHeaders,
196
+ meta,
197
+ getRandomLocale,
198
+ getRandomTimezone
199
+ };
@@ -0,0 +1,151 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ "use strict";
3
+
4
+ const cheerio = require("cheerio");
5
+ const util = require("util");
6
+
7
+ /**
8
+ * Fetches a URL, scrapes all <script type="application/json"> tags, and returns their parsed content.
9
+ * @param {string} url - The URL to fetch.
10
+ * @param {object} jar - The cookie jar.
11
+ * @param {object} qs - Query string parameters.
12
+ * @param {object} options - Global options.
13
+ * @param {object} ctx - The application context.
14
+ * @param {object} customHeader - Custom headers.
15
+ * @returns {Promise<Array<Object>>} A promise that resolves with an array of parsed JSON objects.
16
+ */
17
+ async function json(url, jar, qs, options, ctx, customHeader) {
18
+ const network = require("./axios");
19
+ const constants = require("./constants");
20
+
21
+ try {
22
+ const res = await network.get(url, jar, qs, options, ctx, customHeader);
23
+ const body = res.body;
24
+ const $ = cheerio.load(body);
25
+ const scripts = $('script[type="application/json"]');
26
+
27
+ if (scripts.length === 0) {
28
+ constants.warn(`No <script type="application/json"> tags found on ${url}`);
29
+ return [];
30
+ }
31
+
32
+ const allJsonData = [];
33
+ scripts.each((index, element) => {
34
+ try {
35
+ const jsonContent = $(element).html();
36
+ if (jsonContent) {
37
+ allJsonData.push(JSON.parse(jsonContent));
38
+ }
39
+ } catch (e) {
40
+ constants.warn(`Could not parse JSON from script #${index + 1} on ${url}`);
41
+ }
42
+ });
43
+
44
+ return allJsonData;
45
+ } catch (error) {
46
+ constants.error(`Error in utils.json fetching from ${url}:`, error);
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Creates an object with pre-filled request defaults based on HTML and user context.
53
+ * @param {string} html - The HTML content.
54
+ * @param {string|number} userID - The user ID.
55
+ * @param {object} ctx - The context containing session data.
56
+ * @returns {object} An object with .get, .post, and .postFormData methods.
57
+ */
58
+ function makeDefaults(html, userID, ctx) {
59
+ const network = require("./axios");
60
+ const constants = require("./constants");
61
+
62
+ let reqCounter = 1;
63
+ const revision = constants.getFrom(html, 'revision":', ",");
64
+
65
+ /**
66
+ * Merges provided data with default request parameters.
67
+ * @param {object} obj - The original query or form object.
68
+ * @returns {object} The merged object.
69
+ */
70
+ function mergeWithDefaults(obj) {
71
+ const newObj = {
72
+ av: userID,
73
+ __user: userID,
74
+ __req: (reqCounter++).toString(36),
75
+ __rev: revision,
76
+ __a: 1,
77
+ ...(ctx && {
78
+ fb_dtsg: ctx.fb_dtsg,
79
+ jazoest: ctx.jazoest,
80
+ }),
81
+ };
82
+
83
+ if (!obj) return newObj;
84
+
85
+ for (const prop in obj) {
86
+ if (obj.hasOwnProperty(prop) && !newObj[prop]) {
87
+ newObj[prop] = obj[prop];
88
+ }
89
+ }
90
+
91
+ return newObj;
92
+ }
93
+
94
+ return {
95
+ /**
96
+ * Makes a GET request with merged defaults.
97
+ * @param {string} url
98
+ * @param {object} jar
99
+ * @param {object} qs
100
+ * @param {object} ctxx
101
+ * @param {object} [customHeader={}]
102
+ * @returns {Promise<object>}
103
+ */
104
+ get: (url, jar, qs, ctxx, customHeader = {}) =>
105
+ network.get(url, jar, mergeWithDefaults(qs), ctx.globalOptions, ctxx || ctx, customHeader),
106
+
107
+ /**
108
+ * Makes a POST request with merged defaults.
109
+ * @param {string} url
110
+ * @param {object} jar
111
+ * @param {object} form
112
+ * @param {object} ctxx
113
+ * @param {object} [customHeader={}]
114
+ * @returns {Promise<object>}
115
+ */
116
+ post: (url, jar, form, ctxx, customHeader = {}) =>
117
+ network.post(url, jar, mergeWithDefaults(form), ctx.globalOptions, ctxx || ctx, customHeader),
118
+
119
+ /**
120
+ * Makes a multipart/form-data POST request with merged defaults.
121
+ * @param {string} url
122
+ * @param {object} jar
123
+ * @param {object} form
124
+ * @param {object} qs
125
+ * @param {object} ctxx
126
+ * @returns {Promise<object>}
127
+ */
128
+ postFormData: (url, jar, form, qs, ctxx) =>
129
+ network.postFormData(
130
+ url,
131
+ jar,
132
+ mergeWithDefaults(form),
133
+ mergeWithDefaults(qs),
134
+ ctx.globalOptions,
135
+ ctxx || ctx
136
+ ),
137
+ };
138
+ }
139
+
140
+ module.exports = {
141
+ ...require('./axios'),
142
+ ...require('./clients'),
143
+ ...require('./constants'),
144
+ ...require('./formatters'),
145
+ ...require('./headers'),
146
+ ...require('./user-agents'),
147
+ json,
148
+ makeDefaults,
149
+ promisify: (func) => util.promisify(func),
150
+ delay: (ms) => new Promise(r => setTimeout(r, ms))
151
+ };
@@ -0,0 +1,358 @@
1
+ "use strict";
2
+
3
+ const utils = require('./index');
4
+ const chalk = require('chalk');
5
+ const gradient = require('gradient-string');
6
+
7
+ class ProductionMonitor {
8
+ constructor() {
9
+ this.metrics = {
10
+ requests: {
11
+ total: 0,
12
+ success: 0,
13
+ failed: 0,
14
+ byEndpoint: new Map()
15
+ },
16
+ errors: {
17
+ total: 0,
18
+ byType: new Map(),
19
+ byCode: new Map(),
20
+ recent: []
21
+ },
22
+ performance: {
23
+ avgResponseTime: 0,
24
+ slowRequests: [],
25
+ requestTimes: []
26
+ },
27
+ session: {
28
+ loginTime: null,
29
+ lastActivity: null,
30
+ tokenRefreshCount: 0,
31
+ reconnectCount: 0
32
+ },
33
+ rateLimiting: {
34
+ hitCount: 0,
35
+ cooldowns: 0,
36
+ delayedRequests: 0
37
+ }
38
+ };
39
+
40
+ this.config = {
41
+ logLevel: 'info',
42
+ enableMetrics: true,
43
+ enableErrorTracking: true,
44
+ performanceThreshold: 5000,
45
+ errorRetentionCount: 100,
46
+ metricsInterval: 60000
47
+ };
48
+
49
+ this.startTime = Date.now();
50
+ this.metricsInterval = null;
51
+ }
52
+
53
+ setConfig(options) {
54
+ Object.assign(this.config, options);
55
+ }
56
+
57
+ trackRequest(endpoint, success, responseTime, error = null) {
58
+ if (!this.config.enableMetrics) return;
59
+
60
+ this.metrics.requests.total++;
61
+ if (success) {
62
+ this.metrics.requests.success++;
63
+ } else {
64
+ this.metrics.requests.failed++;
65
+ }
66
+
67
+ if (!this.metrics.requests.byEndpoint.has(endpoint)) {
68
+ this.metrics.requests.byEndpoint.set(endpoint, {
69
+ total: 0,
70
+ success: 0,
71
+ failed: 0,
72
+ avgTime: 0
73
+ });
74
+ }
75
+
76
+ const endpointStats = this.metrics.requests.byEndpoint.get(endpoint);
77
+ endpointStats.total++;
78
+ if (success) {
79
+ endpointStats.success++;
80
+ } else {
81
+ endpointStats.failed++;
82
+ }
83
+
84
+ endpointStats.avgTime =
85
+ (endpointStats.avgTime * (endpointStats.total - 1) + responseTime) / endpointStats.total;
86
+
87
+ this.trackPerformance(endpoint, responseTime);
88
+
89
+ if (error) {
90
+ this.trackError(error, endpoint);
91
+ }
92
+
93
+ this.metrics.session.lastActivity = Date.now();
94
+ }
95
+
96
+ trackPerformance(endpoint, responseTime) {
97
+ this.metrics.performance.requestTimes.push(responseTime);
98
+
99
+ if (this.metrics.performance.requestTimes.length > 1000) {
100
+ this.metrics.performance.requestTimes.shift();
101
+ }
102
+
103
+ const sum = this.metrics.performance.requestTimes.reduce((a, b) => a + b, 0);
104
+ this.metrics.performance.avgResponseTime =
105
+ sum / this.metrics.performance.requestTimes.length;
106
+
107
+ if (responseTime > this.config.performanceThreshold) {
108
+ this.metrics.performance.slowRequests.push({
109
+ endpoint,
110
+ responseTime,
111
+ timestamp: Date.now()
112
+ });
113
+
114
+ if (this.metrics.performance.slowRequests.length > 50) {
115
+ this.metrics.performance.slowRequests.shift();
116
+ }
117
+
118
+ const perfGradient = gradient(['#ff6b6b', '#ee5a6f']);
119
+ utils.warn(
120
+ chalk.bold("Performance"),
121
+ `Slow request: ${chalk.cyan(endpoint)} took ${perfGradient(responseTime + 'ms')}`
122
+ );
123
+ }
124
+ }
125
+
126
+ trackError(error, context = '') {
127
+ if (!this.config.enableErrorTracking) return;
128
+
129
+ this.metrics.errors.total++;
130
+
131
+ const errorType = error.errorType || error.name || 'UnknownError';
132
+ const errorCode = error.errorCode || error.code || 'N/A';
133
+
134
+ this.metrics.errors.byType.set(
135
+ errorType,
136
+ (this.metrics.errors.byType.get(errorType) || 0) + 1
137
+ );
138
+
139
+ this.metrics.errors.byCode.set(
140
+ errorCode,
141
+ (this.metrics.errors.byCode.get(errorCode) || 0) + 1
142
+ );
143
+
144
+ this.metrics.errors.recent.push({
145
+ type: errorType,
146
+ code: errorCode,
147
+ message: error.message,
148
+ context,
149
+ timestamp: Date.now(),
150
+ stack: error.stack
151
+ });
152
+
153
+ if (this.metrics.errors.recent.length > this.config.errorRetentionCount) {
154
+ this.metrics.errors.recent.shift();
155
+ }
156
+ }
157
+
158
+ trackRateLimit(type, threadID = null) {
159
+ this.metrics.rateLimiting.hitCount++;
160
+
161
+ if (type === 'cooldown') {
162
+ this.metrics.rateLimiting.cooldowns++;
163
+ } else if (type === 'delayed') {
164
+ this.metrics.rateLimiting.delayedRequests++;
165
+ }
166
+ }
167
+
168
+ trackTokenRefresh() {
169
+ this.metrics.session.tokenRefreshCount++;
170
+ }
171
+
172
+ trackReconnect() {
173
+ this.metrics.session.reconnectCount++;
174
+ }
175
+
176
+ setLoginTime() {
177
+ this.metrics.session.loginTime = Date.now();
178
+ }
179
+
180
+ getMetrics() {
181
+ const uptime = Date.now() - this.startTime;
182
+ const sessionDuration = this.metrics.session.loginTime
183
+ ? Date.now() - this.metrics.session.loginTime
184
+ : 0;
185
+
186
+ return {
187
+ uptime,
188
+ sessionDuration,
189
+ requests: {
190
+ ...this.metrics.requests,
191
+ byEndpoint: Object.fromEntries(this.metrics.requests.byEndpoint),
192
+ successRate: this.metrics.requests.total > 0
193
+ ? (this.metrics.requests.success / this.metrics.requests.total * 100).toFixed(2) + '%'
194
+ : 'N/A'
195
+ },
196
+ errors: {
197
+ ...this.metrics.errors,
198
+ byType: Object.fromEntries(this.metrics.errors.byType),
199
+ byCode: Object.fromEntries(this.metrics.errors.byCode),
200
+ errorRate: this.metrics.requests.total > 0
201
+ ? (this.metrics.errors.total / this.metrics.requests.total * 100).toFixed(2) + '%'
202
+ : 'N/A'
203
+ },
204
+ performance: this.metrics.performance,
205
+ session: this.metrics.session,
206
+ rateLimiting: this.metrics.rateLimiting
207
+ };
208
+ }
209
+
210
+ getHealth() {
211
+ const metrics = this.getMetrics();
212
+ const health = {
213
+ status: 'healthy',
214
+ checks: {},
215
+ timestamp: Date.now()
216
+ };
217
+
218
+ const errorRate = this.metrics.requests.total > 0
219
+ ? (this.metrics.errors.total / this.metrics.requests.total) * 100
220
+ : 0;
221
+
222
+ health.checks.errorRate = {
223
+ status: errorRate < 5 ? 'pass' : errorRate < 15 ? 'warn' : 'fail',
224
+ value: errorRate.toFixed(2) + '%',
225
+ threshold: '5%'
226
+ };
227
+
228
+ health.checks.performance = {
229
+ status: this.metrics.performance.avgResponseTime < 2000 ? 'pass' :
230
+ this.metrics.performance.avgResponseTime < 5000 ? 'warn' : 'fail',
231
+ value: Math.round(this.metrics.performance.avgResponseTime) + 'ms',
232
+ threshold: '2000ms'
233
+ };
234
+
235
+ health.checks.session = {
236
+ status: this.metrics.session.loginTime ? 'pass' : 'fail',
237
+ value: this.metrics.session.loginTime ? 'active' : 'not logged in'
238
+ };
239
+
240
+ health.checks.rateLimiting = {
241
+ status: this.metrics.rateLimiting.hitCount < 100 ? 'pass' :
242
+ this.metrics.rateLimiting.hitCount < 500 ? 'warn' : 'fail',
243
+ value: this.metrics.rateLimiting.hitCount,
244
+ threshold: '100 hits'
245
+ };
246
+
247
+ const failedChecks = Object.values(health.checks).filter(c => c.status === 'fail').length;
248
+ const warnChecks = Object.values(health.checks).filter(c => c.status === 'warn').length;
249
+
250
+ if (failedChecks > 0) {
251
+ health.status = 'unhealthy';
252
+ } else if (warnChecks > 0) {
253
+ health.status = 'degraded';
254
+ }
255
+
256
+ return health;
257
+ }
258
+
259
+ reset() {
260
+ this.metrics.requests.total = 0;
261
+ this.metrics.requests.success = 0;
262
+ this.metrics.requests.failed = 0;
263
+ this.metrics.requests.byEndpoint.clear();
264
+ this.metrics.errors.total = 0;
265
+ this.metrics.errors.byType.clear();
266
+ this.metrics.errors.byCode.clear();
267
+ this.metrics.errors.recent = [];
268
+ this.metrics.performance.slowRequests = [];
269
+ this.metrics.performance.requestTimes = [];
270
+ this.metrics.rateLimiting.hitCount = 0;
271
+ this.metrics.rateLimiting.cooldowns = 0;
272
+ this.metrics.rateLimiting.delayedRequests = 0;
273
+
274
+ utils.success(chalk.bold("Monitoring"), chalk.green("All metrics have been reset successfully"));
275
+ }
276
+
277
+ displayHealthStatus() {
278
+ const health = this.getHealth();
279
+ const statusGradient = gradient(['#11998e', '#38ef7d']);
280
+ const warnGradient = gradient(['#f093fb', '#f5576c']);
281
+ const errorGradient = gradient(['#fa709a', '#fee140']);
282
+
283
+ console.log('\n' + chalk.cyan('═'.repeat(60)));
284
+
285
+ if (health.status === 'healthy') {
286
+ console.log(statusGradient.multiline('šŸ’š SYSTEM HEALTH: HEALTHY'));
287
+ } else if (health.status === 'degraded') {
288
+ console.log(warnGradient.multiline('šŸ’› SYSTEM HEALTH: DEGRADED'));
289
+ } else {
290
+ console.log(errorGradient.multiline('ā¤ļø SYSTEM HEALTH: UNHEALTHY'));
291
+ }
292
+
293
+ console.log(chalk.cyan('═'.repeat(60)));
294
+
295
+ Object.entries(health.checks).forEach(([name, check]) => {
296
+ const icon = check.status === 'pass' ? 'āœ“' : check.status === 'warn' ? '⚠' : 'āœ–';
297
+ const color = check.status === 'pass' ? chalk.green : check.status === 'warn' ? chalk.yellow : chalk.red;
298
+ const nameFormatted = name.charAt(0).toUpperCase() + name.slice(1);
299
+
300
+ console.log(color(`\n${icon} ${chalk.bold(nameFormatted)}:`));
301
+ console.log(color(` Status: ${check.status.toUpperCase()}`));
302
+ console.log(color(` Value: ${check.value}`));
303
+ if (check.threshold) {
304
+ console.log(color(` Threshold: ${check.threshold}`));
305
+ }
306
+ });
307
+
308
+ console.log('\n' + chalk.cyan('═'.repeat(60)) + '\n');
309
+ return health;
310
+ }
311
+
312
+ startPeriodicReporting(interval = 60000) {
313
+ if (this.metricsInterval) {
314
+ clearInterval(this.metricsInterval);
315
+ }
316
+
317
+ this.metricsInterval = setInterval(() => {
318
+ const metrics = this.getMetrics();
319
+ const reportGradient = gradient(['#667eea', '#764ba2']);
320
+
321
+ console.log('\n' + chalk.cyan('═'.repeat(60)));
322
+ console.log(reportGradient.multiline('šŸ“Š PERFORMANCE METRICS REPORT'));
323
+ console.log(chalk.cyan('═'.repeat(60)));
324
+
325
+ console.log(chalk.bold.white('\nšŸ”¹ Requests:'));
326
+ console.log(` ${chalk.gray('Total:')} ${chalk.green.bold(metrics.requests.total)}`);
327
+ console.log(` ${chalk.gray('Success Rate:')} ${chalk.green(metrics.requests.successRate)}`);
328
+
329
+ console.log(chalk.bold.white('\nšŸ”¹ Errors:'));
330
+ console.log(` ${chalk.gray('Total:')} ${chalk.red.bold(metrics.errors.total)}`);
331
+ console.log(` ${chalk.gray('Error Rate:')} ${chalk.yellow(metrics.errors.errorRate)}`);
332
+
333
+ console.log(chalk.bold.white('\nšŸ”¹ Performance:'));
334
+ const avgTime = Math.round(metrics.performance.avgResponseTime);
335
+ const timeColor = avgTime < 1000 ? chalk.green : avgTime < 3000 ? chalk.yellow : chalk.red;
336
+ console.log(` ${chalk.gray('Avg Response Time:')} ${timeColor(avgTime + 'ms')}`);
337
+
338
+ console.log(chalk.bold.white('\nšŸ”¹ Rate Limiting:'));
339
+ console.log(` ${chalk.gray('Total Hits:')} ${chalk.cyan(metrics.rateLimiting.hitCount)}`);
340
+
341
+ console.log(chalk.cyan('═'.repeat(60)) + '\n');
342
+ }, interval);
343
+ }
344
+
345
+ stopPeriodicReporting() {
346
+ if (this.metricsInterval) {
347
+ clearInterval(this.metricsInterval);
348
+ this.metricsInterval = null;
349
+ }
350
+ }
351
+ }
352
+
353
+ const globalMonitor = new ProductionMonitor();
354
+
355
+ module.exports = {
356
+ ProductionMonitor,
357
+ globalMonitor
358
+ };