@dongdev/fca-unofficial 3.0.30 → 3.0.31

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 (48) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/package.json +2 -1
  3. package/src/api/http/httpGet.js +2 -2
  4. package/src/api/messaging/addUserToGroup.js +1 -1
  5. package/src/api/messaging/changeGroupImage.js +1 -1
  6. package/src/api/messaging/changeNickname.js +1 -1
  7. package/src/api/messaging/changeThreadColor.js +1 -1
  8. package/src/api/messaging/editMessage.js +1 -1
  9. package/src/api/messaging/searchForThread.js +2 -1
  10. package/src/api/messaging/sendTypingIndicator.js +1 -1
  11. package/src/api/messaging/setMessageReaction.js +3 -4
  12. package/src/api/messaging/unsendMessage.js +1 -1
  13. package/src/api/threads/getThreadInfo.js +2 -1
  14. package/src/api/users/getUserInfo.js +7 -4
  15. package/src/database/helpers.js +53 -0
  16. package/src/database/models/index.js +2 -1
  17. package/src/database/threadData.js +49 -53
  18. package/src/database/userData.js +46 -37
  19. package/src/utils/format/attachment.js +357 -0
  20. package/src/utils/format/cookie.js +9 -0
  21. package/src/utils/format/date.js +50 -0
  22. package/src/utils/format/decode.js +44 -0
  23. package/src/utils/format/delta.js +194 -0
  24. package/src/utils/format/ids.js +64 -0
  25. package/src/utils/format/index.js +64 -0
  26. package/src/utils/format/message.js +88 -0
  27. package/src/utils/format/presence.js +132 -0
  28. package/src/utils/format/readTyp.js +44 -0
  29. package/src/utils/format/thread.js +42 -0
  30. package/src/utils/format/utils.js +141 -0
  31. package/src/utils/loginParser/autoLogin.js +125 -0
  32. package/src/utils/loginParser/helpers.js +43 -0
  33. package/src/utils/loginParser/index.js +10 -0
  34. package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
  35. package/src/utils/loginParser/textUtils.js +28 -0
  36. package/src/utils/request/client.js +26 -0
  37. package/src/utils/request/config.js +23 -0
  38. package/src/utils/request/defaults.js +46 -0
  39. package/src/utils/request/helpers.js +46 -0
  40. package/src/utils/request/index.js +17 -0
  41. package/src/utils/request/methods.js +163 -0
  42. package/src/utils/request/proxy.js +21 -0
  43. package/src/utils/request/retry.js +77 -0
  44. package/src/utils/request/sanitize.js +49 -0
  45. package/src/utils/format.js +0 -1174
  46. package/src/utils/loginParser.js +0 -365
  47. package/src/utils/messageFormat.js +0 -1173
  48. 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
- };