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,169 @@
1
+ "use strict";
2
+
3
+ const utils = require('./index');
4
+
5
+ class AutoReLoginManager {
6
+ constructor() {
7
+ this.credentials = null;
8
+ this.loginOptions = null;
9
+ this.loginCallback = null;
10
+ this.isReLoggingIn = false;
11
+ this.pendingRequests = [];
12
+ this.maxRetries = 3;
13
+ this.retryCount = 0;
14
+ this.onReLoginSuccess = null;
15
+ this.onReLoginFailure = null;
16
+ this.enabled = false;
17
+ }
18
+
19
+ setCredentials(credentials, options, callback) {
20
+ this.credentials = credentials;
21
+ this.loginOptions = options || {};
22
+ this.loginCallback = callback;
23
+ this.enabled = true;
24
+ }
25
+
26
+ isEnabled() {
27
+ return this.enabled && this.credentials !== null;
28
+ }
29
+
30
+ async handleSessionExpiry(api, fbLink, ERROR_RETRIEVING) {
31
+ if (!this.isEnabled()) {
32
+ utils.warn("AutoReLogin", "Auto re-login not enabled. Credentials not stored.");
33
+ return false;
34
+ }
35
+
36
+ if (this.isReLoggingIn) {
37
+ utils.log("AutoReLogin", "Re-login already in progress. Queuing request...");
38
+ return new Promise((resolve, reject) => {
39
+ this.pendingRequests.push({ resolve, reject });
40
+ });
41
+ }
42
+
43
+ if (this.retryCount >= this.maxRetries) {
44
+ utils.error("AutoReLogin", `Maximum re-login attempts (${this.maxRetries}) exceeded`);
45
+ if (this.onReLoginFailure) {
46
+ this.onReLoginFailure(new Error("Max re-login retries exceeded"));
47
+ }
48
+ return false;
49
+ }
50
+
51
+ this.isReLoggingIn = true;
52
+ this.retryCount++;
53
+ utils.log("AutoReLogin", `Starting automatic re-login (attempt ${this.retryCount}/${this.maxRetries})...`);
54
+
55
+ try {
56
+ await this.pauseAPIRequests();
57
+
58
+ const loginHelperModel = require('../engine/models/loginHelper');
59
+ const setOptionsModel = require('../engine/models/setOptions');
60
+ const buildAPIModel = require('../engine/models/buildAPI');
61
+
62
+ await new Promise((resolve, reject) => {
63
+ loginHelperModel(
64
+ this.credentials,
65
+ this.loginOptions,
66
+ (loginError, newApi) => {
67
+ if (loginError) {
68
+ reject(loginError);
69
+ return;
70
+ }
71
+
72
+ if (api) {
73
+ api.ctx = newApi.ctx;
74
+ api.defaultFuncs = newApi.defaultFuncs;
75
+
76
+ if (api.tokenRefreshManager) {
77
+ api.tokenRefreshManager.resetFailureCount();
78
+ }
79
+ }
80
+
81
+ resolve(newApi);
82
+ },
83
+ setOptionsModel,
84
+ buildAPIModel,
85
+ api,
86
+ fbLink,
87
+ ERROR_RETRIEVING
88
+ );
89
+ });
90
+
91
+ utils.log("AutoReLogin", "Re-login successful! Session restored.");
92
+ this.retryCount = 0;
93
+ this.isReLoggingIn = false;
94
+
95
+ this.resolvePendingRequests(true);
96
+
97
+ if (this.onReLoginSuccess) {
98
+ this.onReLoginSuccess();
99
+ }
100
+
101
+ return true;
102
+ } catch (error) {
103
+ utils.error("AutoReLogin", `Re-login failed:`, error.message);
104
+ this.isReLoggingIn = false;
105
+
106
+ if (this.retryCount >= this.maxRetries) {
107
+ this.resolvePendingRequests(false);
108
+ if (this.onReLoginFailure) {
109
+ this.onReLoginFailure(error);
110
+ }
111
+ return false;
112
+ }
113
+
114
+ const backoffDelay = Math.min(30000, Math.pow(2, this.retryCount) * 1000);
115
+ utils.log("AutoReLogin", `Retrying re-login in ${backoffDelay}ms...`);
116
+ await new Promise(resolve => setTimeout(resolve, backoffDelay));
117
+
118
+ return await this.handleSessionExpiry(api, fbLink, ERROR_RETRIEVING);
119
+ }
120
+ }
121
+
122
+ async pauseAPIRequests() {
123
+ utils.log("AutoReLogin", "Pausing API requests during re-login...");
124
+ await new Promise(resolve => setTimeout(resolve, 1000));
125
+ }
126
+
127
+ resolvePendingRequests(success) {
128
+ utils.log("AutoReLogin", `Resolving ${this.pendingRequests.length} pending requests (success: ${success})`);
129
+
130
+ this.pendingRequests.forEach(({ resolve, reject }) => {
131
+ if (success) {
132
+ resolve(true);
133
+ } else {
134
+ reject(new Error("Re-login failed"));
135
+ }
136
+ });
137
+
138
+ this.pendingRequests = [];
139
+ }
140
+
141
+ setReLoginSuccessCallback(callback) {
142
+ this.onReLoginSuccess = callback;
143
+ }
144
+
145
+ setReLoginFailureCallback(callback) {
146
+ this.onReLoginFailure = callback;
147
+ }
148
+
149
+ disable() {
150
+ this.enabled = false;
151
+ this.credentials = null;
152
+ this.loginOptions = null;
153
+ this.loginCallback = null;
154
+ utils.log("AutoReLogin", "Auto re-login disabled and credentials cleared");
155
+ }
156
+
157
+ reset() {
158
+ this.retryCount = 0;
159
+ this.isReLoggingIn = false;
160
+ this.pendingRequests = [];
161
+ }
162
+ }
163
+
164
+ const globalAutoReLoginManager = new AutoReLoginManager();
165
+
166
+ module.exports = {
167
+ AutoReLoginManager,
168
+ globalAutoReLoginManager
169
+ };
@@ -0,0 +1,290 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ "use strict";
3
+
4
+ const axios = require("axios");
5
+ const { CookieJar } = require("tough-cookie");
6
+ const { wrapper } = require("axios-cookiejar-support");
7
+ const FormData = require("form-data");
8
+ const { getHeaders } = require("./headers");
9
+ const { getType } = require("./constants");
10
+ const { globalRateLimiter } = require("./rateLimiter");
11
+
12
+ const jar = new CookieJar();
13
+ const client = wrapper(axios.create({ jar }));
14
+
15
+ let proxyConfig = {};
16
+
17
+ /**
18
+ * A utility to introduce a delay, used for retries.
19
+ * @param {number} ms - The delay in milliseconds.
20
+ * @returns {Promise<void>}
21
+ */
22
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
23
+
24
+ /**
25
+ * Adapts the axios response/error to match the structure expected by the rest of the application.
26
+ * @param {object} res - The axios response or error object.
27
+ * @returns {object} An object that mimics the old 'request' library's response.
28
+ */
29
+ function adaptResponse(res) {
30
+ const response = res.response || res;
31
+ return {
32
+ ...response,
33
+ body: response.data,
34
+ statusCode: response.status,
35
+ request: {
36
+ uri: new URL(response.config.url),
37
+ headers: response.config.headers,
38
+ method: response.config.method.toUpperCase(),
39
+ form: response.config.data,
40
+ formData: response.config.data
41
+ },
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Performs a request with retry logic and exponential backoff.
47
+ * @param {Function} requestFunction - A function that returns an axios promise.
48
+ * @param {number} [retries=3] - The number of retries.
49
+ * @param {string} [endpoint=''] - Endpoint identifier for rate limiting.
50
+ * @param {string} [threadID=''] - Thread identifier for rate limiting.
51
+ * @returns {Promise<object>}
52
+ */
53
+ async function requestWithRetry(requestFunction, retries = 3, endpoint = '', threadID = '') {
54
+ await globalRateLimiter.checkRateLimit();
55
+
56
+ if (endpoint && globalRateLimiter.isEndpointOnCooldown(endpoint)) {
57
+ const cooldown = globalRateLimiter.getEndpointCooldownRemaining(endpoint);
58
+ console.warn(`Endpoint ${endpoint} on cooldown. Waiting ${cooldown}ms...`);
59
+ await delay(cooldown);
60
+ }
61
+
62
+ if (threadID && globalRateLimiter.isThreadOnCooldown(threadID)) {
63
+ const cooldown = globalRateLimiter.getCooldownRemaining(threadID);
64
+ console.warn(`Thread ${threadID} on cooldown. Waiting ${cooldown}ms...`);
65
+ await delay(cooldown);
66
+ }
67
+
68
+ const checkAndApplyRateLimitCooldowns = (responseBody) => {
69
+ const ERROR_COOLDOWNS = {
70
+ 1545012: 60000,
71
+ 1675004: 30000,
72
+ 368: 90000
73
+ };
74
+
75
+ const applyCooldown = (errorCode) => {
76
+ if (errorCode && ERROR_COOLDOWNS[errorCode]) {
77
+ if (threadID) {
78
+ globalRateLimiter.setThreadCooldown(threadID, ERROR_COOLDOWNS[errorCode]);
79
+ }
80
+ if (endpoint) {
81
+ globalRateLimiter.setEndpointCooldown(endpoint, ERROR_COOLDOWNS[errorCode]);
82
+ }
83
+ console.warn(`Rate limit detected (error ${errorCode}). Applied cooldown.`);
84
+ return true;
85
+ }
86
+ return false;
87
+ };
88
+
89
+ if (!responseBody || typeof responseBody !== 'object') {
90
+ return false;
91
+ }
92
+
93
+ if (applyCooldown(responseBody.error)) {
94
+ return true;
95
+ }
96
+
97
+ if (Array.isArray(responseBody)) {
98
+ for (const item of responseBody) {
99
+ if (item && typeof item === 'object') {
100
+ if (applyCooldown(item.error)) return true;
101
+ if (item.errors && Array.isArray(item.errors)) {
102
+ for (const err of item.errors) {
103
+ const code = err.code || err.extensions?.code;
104
+ if (applyCooldown(code)) return true;
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ if (responseBody.errors && Array.isArray(responseBody.errors)) {
112
+ for (const err of responseBody.errors) {
113
+ const code = err.code || err.extensions?.code;
114
+ if (applyCooldown(code)) return true;
115
+ }
116
+ }
117
+
118
+ return false;
119
+ };
120
+
121
+ for (let i = 0; i < retries; i++) {
122
+ try {
123
+ const res = await requestFunction();
124
+ const adapted = adaptResponse(res);
125
+
126
+ checkAndApplyRateLimitCooldowns(adapted.body);
127
+
128
+ return adapted;
129
+ } catch (error) {
130
+ if (error.response) {
131
+ const adapted = adaptResponse(error.response);
132
+ checkAndApplyRateLimitCooldowns(adapted.body);
133
+ }
134
+
135
+ if (i === retries - 1) {
136
+ console.error(`Request failed after ${retries} attempts:`, error.message);
137
+ if (error.response) {
138
+ return adaptResponse(error.response);
139
+ }
140
+ throw error;
141
+ }
142
+ const backoffTime = (1 << i) * 1000 + Math.floor(Math.random() * 200);
143
+ console.warn(`Request attempt ${i + 1} failed. Retrying in ${backoffTime}ms...`);
144
+ await delay(backoffTime);
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Sets a proxy for all subsequent requests.
151
+ * @param {string} proxyUrl - The proxy URL (e.g., "http://user:pass@host:port").
152
+ */
153
+ function setProxy(proxyUrl) {
154
+ if (proxyUrl) {
155
+ try {
156
+ const parsedProxy = new URL(proxyUrl);
157
+ proxyConfig = {
158
+ proxy: {
159
+ host: parsedProxy.hostname,
160
+ port: parsedProxy.port,
161
+ protocol: parsedProxy.protocol.replace(":", ""),
162
+ auth: parsedProxy.username && parsedProxy.password ? {
163
+ username: parsedProxy.username,
164
+ password: parsedProxy.password,
165
+ } : undefined,
166
+ },
167
+ };
168
+ } catch (e) {
169
+ console.error("Invalid proxy URL. Please use a full URL format (e.g., http://user:pass@host:port).");
170
+ proxyConfig = {};
171
+ }
172
+ } else {
173
+ proxyConfig = {};
174
+ }
175
+ }
176
+
177
+ /**
178
+ * A simple GET request without extra options.
179
+ * @param {string} url - The URL to fetch.
180
+ * @returns {Promise<object>} A promise that resolves with the response.
181
+ */
182
+ function cleanGet(url) {
183
+ const fn = () => client.get(url, { timeout: 60000, ...proxyConfig });
184
+ return requestWithRetry(fn);
185
+ }
186
+
187
+ /**
188
+ * Performs a GET request with query parameters and custom options.
189
+ * @param {string} url
190
+ * @param {object} reqJar
191
+ * @param {object} qs - Query string parameters.
192
+ * @param {object} options
193
+ * @param {object} ctx
194
+ * @param {object} customHeader
195
+ * @returns {Promise<object>}
196
+ */
197
+ async function get(url, reqJar, qs, options, ctx, customHeader) {
198
+ const config = {
199
+ headers: getHeaders(url, options, ctx, customHeader),
200
+ timeout: 60000,
201
+ params: qs,
202
+ ...proxyConfig,
203
+ validateStatus: (status) => status >= 200 && status < 600,
204
+ };
205
+ const endpoint = new URL(url).pathname;
206
+ return requestWithRetry(async () => await client.get(url, config), 3, endpoint);
207
+ }
208
+
209
+ /**
210
+ * Performs a POST request, automatically handling JSON or URL-encoded form data.
211
+ * @param {string} url
212
+ * @param {object} reqJar
213
+ * @param {object} form - The form data object.
214
+ * @param {object} options
215
+ * @param {object} ctx
216
+ * @param {object} customHeader
217
+ * @returns {Promise<object>}
218
+ */
219
+ async function post(url, reqJar, form, options, ctx, customHeader) {
220
+ const headers = getHeaders(url, options, ctx, customHeader, 'xhr');
221
+ let data = form;
222
+ let contentType = headers['Content-Type'] || 'application/x-www-form-urlencoded';
223
+
224
+ if (contentType.includes('json')) {
225
+ data = JSON.stringify(form);
226
+ } else {
227
+ const transformedForm = new URLSearchParams();
228
+ for (const key in form) {
229
+ if (form.hasOwnProperty(key)) {
230
+ let value = form[key];
231
+ if (getType(value) === "Object") {
232
+ value = JSON.stringify(value);
233
+ }
234
+ transformedForm.append(key, value);
235
+ }
236
+ }
237
+ data = transformedForm.toString();
238
+ }
239
+
240
+ headers['Content-Type'] = contentType;
241
+
242
+ const config = {
243
+ headers,
244
+ timeout: 60000,
245
+ ...proxyConfig,
246
+ validateStatus: (status) => status >= 200 && status < 600,
247
+ };
248
+ const endpoint = new URL(url).pathname;
249
+ return requestWithRetry(async () => await client.post(url, data, config), 3, endpoint);
250
+ }
251
+
252
+ /**
253
+ * Performs a POST request with multipart/form-data.
254
+ * @param {string} url
255
+ * @param {object} reqJar
256
+ * @param {object} form - The form data object, may contain readable streams.
257
+ * @param {object} qs - Query string parameters.
258
+ * @param {object} options
259
+ * @param {object} ctx
260
+ * @returns {Promise<object>}
261
+ */
262
+ async function postFormData(url, reqJar, form, qs, options, ctx) {
263
+ const formData = new FormData();
264
+ for (const key in form) {
265
+ if (form.hasOwnProperty(key)) {
266
+ formData.append(key, form[key]);
267
+ }
268
+ }
269
+
270
+ const customHeader = { "Content-Type": `multipart/form-data; boundary=${formData.getBoundary()}` };
271
+
272
+ const config = {
273
+ headers: getHeaders(url, options, ctx, customHeader, 'xhr'),
274
+ timeout: 60000,
275
+ params: qs,
276
+ ...proxyConfig,
277
+ validateStatus: (status) => status >= 200 && status < 600,
278
+ };
279
+ const endpoint = new URL(url).pathname;
280
+ return requestWithRetry(async () => await client.post(url, formData, config), 3, endpoint);
281
+ }
282
+
283
+ module.exports = {
284
+ cleanGet,
285
+ get,
286
+ post,
287
+ postFormData,
288
+ getJar: () => jar,
289
+ setProxy,
290
+ };