@dongdev/fca-unofficial 3.0.30 → 4.0.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.
Files changed (104) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +224 -406
  3. package/dist/index.d.mts +1241 -0
  4. package/dist/index.d.ts +1241 -0
  5. package/dist/index.js +27749 -0
  6. package/dist/index.mjs +27713 -0
  7. package/docs/ARCHITECTURE.md +467 -0
  8. package/docs/DOCS.md +686 -0
  9. package/fca-config.example.json +33 -0
  10. package/package.json +33 -22
  11. package/test/fca.test.cjs +533 -0
  12. package/CHANGELOG.md +0 -293
  13. package/DOCS.md +0 -2712
  14. package/func/checkUpdate.js +0 -222
  15. package/func/logAdapter.js +0 -33
  16. package/func/logger.js +0 -48
  17. package/index.d.ts +0 -751
  18. package/index.js +0 -8
  19. package/module/config.js +0 -40
  20. package/module/login.js +0 -133
  21. package/module/loginHelper.js +0 -1296
  22. package/module/options.js +0 -44
  23. package/src/api/action/addExternalModule.js +0 -25
  24. package/src/api/action/changeAvatar.js +0 -137
  25. package/src/api/action/changeBio.js +0 -75
  26. package/src/api/action/enableAutoSaveAppState.js +0 -73
  27. package/src/api/action/getCurrentUserID.js +0 -7
  28. package/src/api/action/handleFriendRequest.js +0 -57
  29. package/src/api/action/logout.js +0 -76
  30. package/src/api/action/refreshFb_dtsg.js +0 -48
  31. package/src/api/action/setPostReaction.js +0 -106
  32. package/src/api/action/unfriend.js +0 -54
  33. package/src/api/http/httpGet.js +0 -46
  34. package/src/api/http/httpPost.js +0 -52
  35. package/src/api/http/postFormData.js +0 -47
  36. package/src/api/messaging/addUserToGroup.js +0 -68
  37. package/src/api/messaging/changeAdminStatus.js +0 -126
  38. package/src/api/messaging/changeArchivedStatus.js +0 -55
  39. package/src/api/messaging/changeBlockedStatus.js +0 -48
  40. package/src/api/messaging/changeGroupImage.js +0 -91
  41. package/src/api/messaging/changeNickname.js +0 -70
  42. package/src/api/messaging/changeThreadColor.js +0 -79
  43. package/src/api/messaging/changeThreadEmoji.js +0 -111
  44. package/src/api/messaging/createNewGroup.js +0 -88
  45. package/src/api/messaging/createPoll.js +0 -46
  46. package/src/api/messaging/createThemeAI.js +0 -98
  47. package/src/api/messaging/deleteMessage.js +0 -136
  48. package/src/api/messaging/deleteThread.js +0 -56
  49. package/src/api/messaging/editMessage.js +0 -68
  50. package/src/api/messaging/forwardAttachment.js +0 -57
  51. package/src/api/messaging/getEmojiUrl.js +0 -29
  52. package/src/api/messaging/getFriendsList.js +0 -82
  53. package/src/api/messaging/getMessage.js +0 -829
  54. package/src/api/messaging/getThemePictures.js +0 -62
  55. package/src/api/messaging/handleMessageRequest.js +0 -65
  56. package/src/api/messaging/markAsDelivered.js +0 -57
  57. package/src/api/messaging/markAsRead.js +0 -88
  58. package/src/api/messaging/markAsReadAll.js +0 -49
  59. package/src/api/messaging/markAsSeen.js +0 -61
  60. package/src/api/messaging/muteThread.js +0 -50
  61. package/src/api/messaging/removeUserFromGroup.js +0 -62
  62. package/src/api/messaging/resolvePhotoUrl.js +0 -43
  63. package/src/api/messaging/scheduler.js +0 -264
  64. package/src/api/messaging/searchForThread.js +0 -52
  65. package/src/api/messaging/sendMessage.js +0 -270
  66. package/src/api/messaging/sendTypingIndicator.js +0 -74
  67. package/src/api/messaging/setMessageReaction.js +0 -91
  68. package/src/api/messaging/setTitle.js +0 -124
  69. package/src/api/messaging/shareContact.js +0 -49
  70. package/src/api/messaging/threadColors.js +0 -128
  71. package/src/api/messaging/unsendMessage.js +0 -81
  72. package/src/api/messaging/uploadAttachment.js +0 -492
  73. package/src/api/socket/core/connectMqtt.js +0 -258
  74. package/src/api/socket/core/emitAuth.js +0 -103
  75. package/src/api/socket/core/getSeqID.js +0 -320
  76. package/src/api/socket/core/getTaskResponseData.js +0 -25
  77. package/src/api/socket/core/parseDelta.js +0 -377
  78. package/src/api/socket/detail/buildStream.js +0 -215
  79. package/src/api/socket/detail/constants.js +0 -28
  80. package/src/api/socket/listenMqtt.js +0 -377
  81. package/src/api/socket/middleware/index.js +0 -216
  82. package/src/api/threads/getThreadHistory.js +0 -664
  83. package/src/api/threads/getThreadInfo.js +0 -295
  84. package/src/api/threads/getThreadList.js +0 -293
  85. package/src/api/threads/getThreadPictures.js +0 -78
  86. package/src/api/users/getUserID.js +0 -65
  87. package/src/api/users/getUserInfo.js +0 -399
  88. package/src/api/users/getUserInfoV2.js +0 -134
  89. package/src/core/sendReqMqtt.js +0 -96
  90. package/src/database/models/index.js +0 -87
  91. package/src/database/models/thread.js +0 -50
  92. package/src/database/models/user.js +0 -46
  93. package/src/database/threadData.js +0 -98
  94. package/src/database/userData.js +0 -89
  95. package/src/remote/remoteClient.js +0 -123
  96. package/src/utils/broadcast.js +0 -51
  97. package/src/utils/client.js +0 -10
  98. package/src/utils/constants.js +0 -23
  99. package/src/utils/cookies.js +0 -68
  100. package/src/utils/format.js +0 -1174
  101. package/src/utils/headers.js +0 -115
  102. package/src/utils/loginParser.js +0 -365
  103. package/src/utils/messageFormat.js +0 -1173
  104. package/src/utils/request.js +0 -332
@@ -1,332 +0,0 @@
1
- const axios = require("axios");
2
- const { CookieJar } = require("tough-cookie");
3
- const { wrapper } = require("axios-cookiejar-support");
4
- const FormData = require("form-data");
5
- const { HttpsProxyAgent } = require("https-proxy-agent");
6
- const { Readable } = require("stream");
7
-
8
- const headersMod = require("./headers");
9
- const getHeaders = headersMod.getHeaders || headersMod;
10
- const formatMod = require("./format");
11
- const getType = formatMod.getType || formatMod;
12
- const constMod = require("./constants");
13
- const getFrom = constMod.getFrom || constMod;
14
-
15
- // Sanitize header value to remove invalid characters
16
- function sanitizeHeaderValue(value) {
17
- if (value === null || value === undefined) return "";
18
- const str = String(value);
19
- // Remove invalid characters for HTTP headers:
20
- // - Control characters (0x00-0x1F, except HTAB 0x09)
21
- // - DEL character (0x7F)
22
- // - Newlines and carriage returns
23
- return str.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\r\n]/g, "").trim();
24
- }
25
-
26
- // Sanitize header name to ensure it's valid
27
- function sanitizeHeaderName(name) {
28
- if (!name || typeof name !== "string") return "";
29
- // Remove invalid characters for HTTP header names
30
- return name.replace(/[^\x21-\x7E]/g, "").trim();
31
- }
32
-
33
- // Sanitize all headers in an object
34
- function sanitizeHeaders(headers) {
35
- if (!headers || typeof headers !== "object") return {};
36
- const sanitized = {};
37
- for (const [key, value] of Object.entries(headers)) {
38
- const sanitizedKey = sanitizeHeaderName(key);
39
- if (!sanitizedKey) continue;
40
-
41
- // Handle arrays - skip them entirely
42
- if (Array.isArray(value)) continue;
43
-
44
- // Handle objects - skip them
45
- if (value !== null && typeof value === "object") continue;
46
-
47
- // Handle functions - skip them
48
- if (typeof value === "function") continue;
49
-
50
- // Check if string value looks like a stringified array (e.g., "["performAutoLogin"]")
51
- if (typeof value === "string") {
52
- const trimmed = value.trim();
53
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
54
- // Try to parse as JSON array - if successful, skip this header
55
- try {
56
- const parsed = JSON.parse(trimmed);
57
- if (Array.isArray(parsed)) {
58
- continue; // Skip stringified arrays
59
- }
60
- } catch {
61
- // Not valid JSON array, continue with normal sanitization
62
- }
63
- }
64
- }
65
-
66
- // Sanitize the value
67
- const sanitizedValue = sanitizeHeaderValue(value);
68
- if (sanitizedValue !== "") {
69
- sanitized[sanitizedKey] = sanitizedValue;
70
- }
71
- }
72
- return sanitized;
73
- }
74
-
75
- const jar = new CookieJar();
76
- const client = wrapper(axios.create({
77
- jar,
78
- withCredentials: true,
79
- timeout: 60000,
80
- validateStatus: s => s >= 200 && s < 600,
81
- maxRedirects: 5,
82
- maxContentLength: Infinity,
83
- maxBodyLength: Infinity
84
- }));
85
-
86
- const delay = ms => new Promise(r => setTimeout(r, ms));
87
-
88
- async function requestWithRetry(fn, retries = 3, baseDelay = 1000, ctx) {
89
- let lastError;
90
- const emit = (event, payload) => {
91
- try {
92
- if (ctx && ctx._emitter && typeof ctx._emitter.emit === "function") {
93
- ctx._emitter.emit(event, payload);
94
- }
95
- } catch { }
96
- };
97
- for (let i = 0; i < retries; i++) {
98
- try {
99
- return await fn();
100
- } catch (e) {
101
- lastError = e;
102
-
103
- // Handle ERR_INVALID_CHAR and other header errors - don't retry, return error immediately
104
- if (e?.code === "ERR_INVALID_CHAR" || (e?.message && e.message.includes("Invalid character in header"))) {
105
- const err = new Error("Invalid header content detected. Request aborted to prevent crash.");
106
- err.error = "Invalid header content";
107
- err.originalError = e;
108
- err.code = "ERR_INVALID_CHAR";
109
- return Promise.reject(err);
110
- }
111
-
112
- // Don't retry on client errors (4xx) except 429 (rate limit)
113
- const status = e?.response?.status || e?.statusCode || 0;
114
- const url = e?.config?.url || "";
115
- const method = String(e?.config?.method || "").toUpperCase();
116
- if (status === 429) {
117
- emit("rateLimit", { status, url, method });
118
- }
119
- if (status >= 400 && status < 500 && status !== 429) {
120
- return e.response || Promise.reject(e);
121
- }
122
- // Don't retry on last attempt
123
- if (i === retries - 1) {
124
- return e.response || Promise.reject(e);
125
- }
126
- // Network errors (no status code)
127
- const netCode = e?.code || "";
128
- const msg = e && e.message ? e.message : String(e || "");
129
- if (
130
- !status &&
131
- (netCode === "UND_ERR_CONNECT_TIMEOUT" ||
132
- netCode === "ETIMEDOUT" ||
133
- netCode === "ECONNRESET" ||
134
- netCode === "ECONNREFUSED" ||
135
- netCode === "ENOTFOUND" ||
136
- /timeout|connect timeout|network error|fetch failed/i.test(msg))
137
- ) {
138
- emit("networkError", { code: netCode, message: msg, url, method });
139
- }
140
-
141
- // Exponential backoff with jitter
142
- const backoffDelay = Math.min(
143
- baseDelay * Math.pow(2, i) + Math.floor(Math.random() * 200),
144
- 30000 // Max 30 seconds
145
- );
146
- await delay(backoffDelay);
147
- }
148
- }
149
- // Return error instead of throwing to prevent uncaught exception
150
- const finalError = lastError || new Error("Request failed after retries");
151
- return Promise.reject(finalError);
152
- }
153
-
154
- function cfg(base = {}) {
155
- const { reqJar, headers, params, agent, timeout } = base;
156
- return {
157
- headers: sanitizeHeaders(headers),
158
- params,
159
- jar: reqJar || jar,
160
- withCredentials: true,
161
- timeout: timeout || 60000,
162
- httpAgent: agent || client.defaults.httpAgent,
163
- httpsAgent: agent || client.defaults.httpsAgent,
164
- proxy: false,
165
- validateStatus: s => s >= 200 && s < 600
166
- };
167
- }
168
-
169
- function toStringVal(v) {
170
- if (v === undefined || v === null) return "";
171
- if (typeof v === "bigint") return v.toString();
172
- if (typeof v === "boolean") return v ? "true" : "false";
173
- return String(v);
174
- }
175
-
176
- function isStream(v) {
177
- return v && typeof v === "object" && typeof v.pipe === "function" && typeof v.on === "function";
178
- }
179
-
180
- function isBlobLike(v) {
181
- return v && typeof v.arrayBuffer === "function" && (typeof v.type === "string" || typeof v.name === "string");
182
- }
183
-
184
- function isPairArrayList(arr) {
185
- return Array.isArray(arr) && arr.length > 0 && arr.every(x => Array.isArray(x) && x.length === 2 && typeof x[0] === "string");
186
- }
187
-
188
- function cleanGet(url, ctx) {
189
- return requestWithRetry(() => client.get(url, cfg()), 3, 1000, ctx);
190
- }
191
-
192
- function get(url, reqJar, qs, options, ctx, customHeader) {
193
- const headers = getHeaders(url, options, ctx, customHeader);
194
- return requestWithRetry(() => client.get(url, cfg({ reqJar, headers, params: qs })), 3, 1000, ctx);
195
- }
196
-
197
- function post(url, reqJar, form, options, ctx, customHeader) {
198
- const headers = getHeaders(url, options, ctx, customHeader);
199
- const ct = String(headers["Content-Type"] || headers["content-type"] || "application/x-www-form-urlencoded").toLowerCase();
200
- let data;
201
- if (ct.includes("json")) {
202
- data = JSON.stringify(form || {});
203
- headers["Content-Type"] = "application/json";
204
- } else {
205
- const p = new URLSearchParams();
206
- if (form && typeof form === "object") {
207
- for (const k of Object.keys(form)) {
208
- let v = form[k];
209
- if (isPairArrayList(v)) {
210
- for (const [kk, vv] of v) p.append(`${k}[${kk}]`, toStringVal(vv));
211
- continue;
212
- }
213
- if (Array.isArray(v)) {
214
- for (const x of v) {
215
- if (Array.isArray(x) && x.length === 2 && typeof x[1] !== "object") p.append(k, toStringVal(x[1]));
216
- else p.append(k, toStringVal(x));
217
- }
218
- continue;
219
- }
220
- if (getType(v) === "Object") v = JSON.stringify(v);
221
- p.append(k, toStringVal(v));
222
- }
223
- }
224
- data = p.toString();
225
- headers["Content-Type"] = "application/x-www-form-urlencoded";
226
- }
227
- return requestWithRetry(() => client.post(url, data, cfg({ reqJar, headers })), 3, 1000, ctx);
228
- }
229
-
230
- async function postFormData(url, reqJar, form, qs, options, ctx) {
231
- const fd = new FormData();
232
- if (form && typeof form === "object") {
233
- for (const k of Object.keys(form)) {
234
- const v = form[k];
235
- if (v === undefined || v === null) continue;
236
- if (isPairArrayList(v)) {
237
- for (const [kk, vv] of v) fd.append(`${k}[${kk}]`, typeof vv === "object" && !Buffer.isBuffer(vv) && !isStream(vv) ? JSON.stringify(vv) : vv);
238
- continue;
239
- }
240
- if (Array.isArray(v)) {
241
- for (const x of v) {
242
- if (Array.isArray(x) && x.length === 2 && x[1] && typeof x[1] === "object" && !Buffer.isBuffer(x[1]) && !isStream(x[1])) {
243
- fd.append(k, x[0], x[1]);
244
- } else if (Array.isArray(x) && x.length === 2 && typeof x[1] !== "object") {
245
- fd.append(k, toStringVal(x[1]));
246
- } else if (x && typeof x === "object" && "value" in x && "options" in x) {
247
- fd.append(k, x.value, x.options || {});
248
- } else if (isStream(x) || Buffer.isBuffer(x) || typeof x === "string") {
249
- fd.append(k, x);
250
- } else if (isBlobLike(x)) {
251
- const buf = Buffer.from(await x.arrayBuffer());
252
- fd.append(k, buf, { filename: x.name || k, contentType: x.type || undefined });
253
- } else {
254
- fd.append(k, JSON.stringify(x));
255
- }
256
- }
257
- continue;
258
- }
259
- if (v && typeof v === "object" && "value" in v && "options" in v) {
260
- fd.append(k, v.value, v.options || {});
261
- continue;
262
- }
263
- if (isStream(v) || Buffer.isBuffer(v) || typeof v === "string") {
264
- fd.append(k, v);
265
- continue;
266
- }
267
- if (isBlobLike(v)) {
268
- const buf = Buffer.from(await v.arrayBuffer());
269
- fd.append(k, buf, { filename: v.name || k, contentType: v.type || undefined });
270
- continue;
271
- }
272
- if (typeof v === "number" || typeof v === "boolean") {
273
- fd.append(k, toStringVal(v));
274
- continue;
275
- }
276
- fd.append(k, JSON.stringify(v));
277
- }
278
- }
279
- const headers = { ...getHeaders(url, options, ctx), ...fd.getHeaders() };
280
- return requestWithRetry(() => client.post(url, fd, cfg({ reqJar, headers, params: qs })), 3, 1000, ctx);
281
- }
282
-
283
- function makeDefaults(html, userID, ctx) {
284
- let reqCounter = 1;
285
- const revision = getFrom(html || "", 'revision":', ",") || getFrom(html || "", '"client_revision":', ",") || "";
286
- function mergeWithDefaults(obj) {
287
- const base = {
288
- av: userID,
289
- __user: userID,
290
- __req: (reqCounter++).toString(36),
291
- __rev: revision,
292
- __a: 1
293
- };
294
- if (ctx?.fb_dtsg) base.fb_dtsg = ctx.fb_dtsg;
295
- if (ctx?.jazoest) base.jazoest = ctx.jazoest;
296
- if (!obj) return base;
297
- for (const k of Object.keys(obj)) if (!(k in base)) base[k] = obj[k];
298
- return base;
299
- }
300
- return {
301
- get: (url, j, qs, ctxx, customHeader = {}) =>
302
- get(url, j, mergeWithDefaults(qs), ctx?.globalOptions, ctxx || ctx, customHeader),
303
- post: (url, j, form, ctxx, customHeader = {}) =>
304
- post(url, j, mergeWithDefaults(form), ctx?.globalOptions, ctxx || ctx, customHeader),
305
- postFormData: (url, j, form, qs, ctxx) =>
306
- postFormData(url, j, mergeWithDefaults(form), mergeWithDefaults(qs), ctx?.globalOptions, ctxx || ctx)
307
- };
308
- }
309
-
310
- function setProxy(proxyUrl) {
311
- if (!proxyUrl) {
312
- client.defaults.httpAgent = undefined;
313
- client.defaults.httpsAgent = undefined;
314
- client.defaults.proxy = false;
315
- return;
316
- }
317
- const agent = new HttpsProxyAgent(proxyUrl);
318
- client.defaults.httpAgent = agent;
319
- client.defaults.httpsAgent = agent;
320
- client.defaults.proxy = false;
321
- }
322
-
323
- module.exports = {
324
- cleanGet,
325
- get,
326
- post,
327
- postFormData,
328
- jar,
329
- setProxy,
330
- makeDefaults,
331
- client
332
- };