@dongdev/fca-unofficial 3.0.8 → 3.0.9

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/CHANGELOG.md CHANGED
@@ -152,3 +152,6 @@ Too lazy to write changelog, sorry! (will write changelog in the next release, t
152
152
 
153
153
  ## v3.0.7 - 2025-11-27
154
154
  - Hotfix / auto bump
155
+
156
+ ## v3.0.8 - 2025-11-27
157
+ - Hotfix / auto bump
package/module/login.js CHANGED
@@ -3,10 +3,77 @@ const { setOptions } = require("./options");
3
3
  const { loadConfig } = require("./config");
4
4
  const { checkAndUpdateVersion } = require("../func/checkUpdate");
5
5
  const loginHelper = require("./loginHelper");
6
+ const logger = require("../func/logger");
6
7
 
7
8
  const { config } = loadConfig();
8
9
  global.fca = { config };
9
10
 
11
+ // Global error handlers to prevent bot crashes
12
+ // Handle unhandled promise rejections (e.g., fetch timeouts, network errors)
13
+ if (!global.fca._errorHandlersInstalled) {
14
+ global.fca._errorHandlersInstalled = true;
15
+
16
+ process.on("unhandledRejection", (reason, promise) => {
17
+ try {
18
+ // Check if it's a fetch/network timeout error
19
+ if (reason && typeof reason === "object") {
20
+ const errorCode = reason.code || reason.cause?.code;
21
+ const errorMessage = reason.message || String(reason);
22
+
23
+ // Handle fetch timeout errors gracefully
24
+ if (errorCode === "UND_ERR_CONNECT_TIMEOUT" ||
25
+ errorCode === "ETIMEDOUT" ||
26
+ errorMessage.includes("Connect Timeout") ||
27
+ errorMessage.includes("fetch failed")) {
28
+ logger(`Network timeout error caught (non-fatal): ${errorMessage}`, "warn");
29
+ return; // Don't crash, just log
30
+ }
31
+
32
+ // Handle other network errors
33
+ if (errorCode === "ECONNREFUSED" ||
34
+ errorCode === "ENOTFOUND" ||
35
+ errorCode === "ECONNRESET" ||
36
+ errorMessage.includes("ECONNREFUSED") ||
37
+ errorMessage.includes("ENOTFOUND")) {
38
+ logger(`Network connection error caught (non-fatal): ${errorMessage}`, "warn");
39
+ return; // Don't crash, just log
40
+ }
41
+ }
42
+
43
+ // For other unhandled rejections, log but don't crash
44
+ logger(`Unhandled promise rejection (non-fatal): ${reason && reason.message ? reason.message : String(reason)}`, "error");
45
+ } catch (e) {
46
+ // Fallback if logger fails
47
+ console.error("[FCA-ERROR] Unhandled promise rejection:", reason);
48
+ }
49
+ });
50
+
51
+ // Handle uncaught exceptions (last resort)
52
+ process.on("uncaughtException", (error) => {
53
+ try {
54
+ const errorMessage = error.message || String(error);
55
+ const errorCode = error.code;
56
+
57
+ // Handle fetch/network errors
58
+ if (errorCode === "UND_ERR_CONNECT_TIMEOUT" ||
59
+ errorCode === "ETIMEDOUT" ||
60
+ errorMessage.includes("Connect Timeout") ||
61
+ errorMessage.includes("fetch failed")) {
62
+ logger(`Uncaught network timeout error (non-fatal): ${errorMessage}`, "warn");
63
+ return; // Don't crash
64
+ }
65
+
66
+ // For other uncaught exceptions, log but try to continue
67
+ logger(`Uncaught exception (attempting to continue): ${errorMessage}`, "error");
68
+ // Note: We don't exit here to allow bot to continue running
69
+ // In production, you might want to restart the process instead
70
+ } catch (e) {
71
+ // Fallback if logger fails
72
+ console.error("[FCA-ERROR] Uncaught exception:", error);
73
+ }
74
+ });
75
+ }
76
+
10
77
  function login(loginData, options, callback) {
11
78
  if (getType(options) === "Function" || getType(options) === "AsyncFunction") {
12
79
  callback = options;
@@ -465,7 +465,7 @@ async function hydrateJarFromDB(userID) {
465
465
  }
466
466
  }
467
467
 
468
- async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions, ctxRef) {
468
+ async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions, ctxRef, hadAppStateInput = false) {
469
469
  const getUID = cs =>
470
470
  cs.find(c => c.key === "i_user")?.value ||
471
471
  cs.find(c => c.key === "c_user")?.value ||
@@ -473,6 +473,15 @@ async function tryAutoLoginIfNeeded(currentHtml, currentCookies, globalOptions,
473
473
  cs.find(c => c.name === "c_user")?.value;
474
474
  let userID = getUID(currentCookies);
475
475
  if (userID) return { html: currentHtml, cookies: currentCookies, userID };
476
+ // If appState/Cookie was provided and is not dead (not checkpointed), skip backup
477
+ if (hadAppStateInput) {
478
+ const isCheckpoint = currentHtml.includes("/checkpoint/block/?next");
479
+ if (!isCheckpoint) {
480
+ // AppState provided and not checkpointed, skip backup - just throw error
481
+ throw new Error("Missing user cookie from provided appState");
482
+ }
483
+ // AppState is dead (checkpointed), proceed to backup/email login
484
+ }
476
485
  const hydrated = await hydrateJarFromDB(null);
477
486
  if (hydrated) {
478
487
  logger("AppState backup live — proceeding to login", "info");
@@ -679,7 +688,8 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
679
688
  cookies.find(c => c.name === "i_user")?.value ||
680
689
  cookies.find(c => c.name === "c_user")?.value;
681
690
  if (!userID) {
682
- const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx);
691
+ // Pass skipBackup=true if appState/Cookie was originally provided
692
+ const retried = await tryAutoLoginIfNeeded(html, cookies, globalOptions, ctx, !!(appState || Cookie));
683
693
  html = retried.html;
684
694
  cookies = retried.cookies;
685
695
  userID = retried.userID;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongdev/fca-unofficial",
3
- "version": "3.0.8",
3
+ "version": "3.0.9",
4
4
  "description": "Unofficial Facebook Chat API for Node.js - Interact with Facebook Messenger programmatically",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",