fca-phantom 1.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 (121) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +534 -0
  3. package/index.js +35 -0
  4. package/package.json +101 -0
  5. package/phantom/core/builder/bootstrap.js +334 -0
  6. package/phantom/core/builder/config.js +78 -0
  7. package/phantom/core/builder/forge.js +113 -0
  8. package/phantom/core/builder/ignite.js +386 -0
  9. package/phantom/core/builder/options.js +61 -0
  10. package/phantom/core/engine.js +71 -0
  11. package/phantom/core/reactor.js +2 -0
  12. package/phantom/datastore/appState.js +2 -0
  13. package/phantom/datastore/appStateBackup.js +34 -0
  14. package/phantom/datastore/models/cipher/e2ee.js +48 -0
  15. package/phantom/datastore/models/cipher/vault.js +153 -0
  16. package/phantom/datastore/models/index.js +3 -0
  17. package/phantom/datastore/models/matrix/auth.js +151 -0
  18. package/phantom/datastore/models/matrix/cache.js +3 -0
  19. package/phantom/datastore/models/matrix/checker.js +2 -0
  20. package/phantom/datastore/models/matrix/clients.js +2 -0
  21. package/phantom/datastore/models/matrix/constants.js +2 -0
  22. package/phantom/datastore/models/matrix/credentials.js +2 -0
  23. package/phantom/datastore/models/matrix/cycle.js +2 -0
  24. package/phantom/datastore/models/matrix/gate.js +282 -0
  25. package/phantom/datastore/models/matrix/ghost.js +332 -0
  26. package/phantom/datastore/models/matrix/headers.js +193 -0
  27. package/phantom/datastore/models/matrix/heartbeat.js +298 -0
  28. package/phantom/datastore/models/matrix/identity.js +235 -0
  29. package/phantom/datastore/models/matrix/logger.js +271 -0
  30. package/phantom/datastore/models/matrix/monitor.js +2 -0
  31. package/phantom/datastore/models/matrix/net.js +316 -0
  32. package/phantom/datastore/models/matrix/response.js +193 -0
  33. package/phantom/datastore/models/matrix/revive.js +255 -0
  34. package/phantom/datastore/models/matrix/signals.js +2 -0
  35. package/phantom/datastore/models/matrix/store.js +263 -0
  36. package/phantom/datastore/models/matrix/telemetry.js +272 -0
  37. package/phantom/datastore/models/matrix/tools.js +93 -0
  38. package/phantom/datastore/models/matrix/transform/cookieParser.js +2 -0
  39. package/phantom/datastore/models/matrix/transform/cookies.js +114 -0
  40. package/phantom/datastore/models/matrix/transform/index.js +203 -0
  41. package/phantom/datastore/models/matrix/validator.js +157 -0
  42. package/phantom/datastore/models/types/index.d.ts +498 -0
  43. package/phantom/datastore/schema.js +167 -0
  44. package/phantom/datastore/session.js +129 -0
  45. package/phantom/datastore/threads.js +22 -0
  46. package/phantom/datastore/users.js +26 -0
  47. package/phantom/dispatch/addExternalModule.js +239 -0
  48. package/phantom/dispatch/addUserToGroup.js +161 -0
  49. package/phantom/dispatch/changeAdminStatus.js +142 -0
  50. package/phantom/dispatch/changeArchivedStatus.js +135 -0
  51. package/phantom/dispatch/changeAvatar.js +123 -0
  52. package/phantom/dispatch/changeBio.js +86 -0
  53. package/phantom/dispatch/changeBlockedStatus.js +86 -0
  54. package/phantom/dispatch/changeGroupImage.js +145 -0
  55. package/phantom/dispatch/changeThreadColor.js +172 -0
  56. package/phantom/dispatch/changeThreadEmoji.js +130 -0
  57. package/phantom/dispatch/comment.js +136 -0
  58. package/phantom/dispatch/createAITheme.js +333 -0
  59. package/phantom/dispatch/createNewGroup.js +99 -0
  60. package/phantom/dispatch/createPoll.js +148 -0
  61. package/phantom/dispatch/deleteMessage.js +131 -0
  62. package/phantom/dispatch/deleteThread.js +155 -0
  63. package/phantom/dispatch/e2ee.js +101 -0
  64. package/phantom/dispatch/editMessage.js +158 -0
  65. package/phantom/dispatch/emoji.js +143 -0
  66. package/phantom/dispatch/fetchThemeData.js +233 -0
  67. package/phantom/dispatch/follow.js +111 -0
  68. package/phantom/dispatch/forwardMessage.js +110 -0
  69. package/phantom/dispatch/friend.js +189 -0
  70. package/phantom/dispatch/gcmember.js +138 -0
  71. package/phantom/dispatch/gcname.js +131 -0
  72. package/phantom/dispatch/gcrule.js +111 -0
  73. package/phantom/dispatch/getAccess.js +109 -0
  74. package/phantom/dispatch/getBotInfo.js +81 -0
  75. package/phantom/dispatch/getBotInitialData.js +110 -0
  76. package/phantom/dispatch/getFriendsList.js +118 -0
  77. package/phantom/dispatch/getMessage.js +199 -0
  78. package/phantom/dispatch/getTheme.js +199 -0
  79. package/phantom/dispatch/getThemeInfo.js +160 -0
  80. package/phantom/dispatch/getThreadHistory.js +139 -0
  81. package/phantom/dispatch/getThreadInfo.js +153 -0
  82. package/phantom/dispatch/getThreadList.js +132 -0
  83. package/phantom/dispatch/getThreadPictures.js +93 -0
  84. package/phantom/dispatch/getUserID.js +147 -0
  85. package/phantom/dispatch/getUserInfo.js +513 -0
  86. package/phantom/dispatch/getUserInfoV2.js +146 -0
  87. package/phantom/dispatch/handleMessageRequest.js +50 -0
  88. package/phantom/dispatch/httpGet.js +63 -0
  89. package/phantom/dispatch/httpPost.js +89 -0
  90. package/phantom/dispatch/httpPostFormData.js +69 -0
  91. package/phantom/dispatch/listenMqtt.js +1236 -0
  92. package/phantom/dispatch/listenSpeed.js +179 -0
  93. package/phantom/dispatch/logout.js +93 -0
  94. package/phantom/dispatch/markAsDelivered.js +92 -0
  95. package/phantom/dispatch/markAsRead.js +119 -0
  96. package/phantom/dispatch/markAsReadAll.js +215 -0
  97. package/phantom/dispatch/markAsSeen.js +70 -0
  98. package/phantom/dispatch/mqttDeltaValue.js +278 -0
  99. package/phantom/dispatch/muteThread.js +253 -0
  100. package/phantom/dispatch/nickname.js +132 -0
  101. package/phantom/dispatch/notes.js +263 -0
  102. package/phantom/dispatch/pinMessage.js +238 -0
  103. package/phantom/dispatch/produceMetaTheme.js +335 -0
  104. package/phantom/dispatch/realtime.js +291 -0
  105. package/phantom/dispatch/removeUserFromGroup.js +248 -0
  106. package/phantom/dispatch/resolvePhotoUrl.js +217 -0
  107. package/phantom/dispatch/searchForThread.js +258 -0
  108. package/phantom/dispatch/sendMessage.js +354 -0
  109. package/phantom/dispatch/sendMessageMqtt.js +249 -0
  110. package/phantom/dispatch/sendTypingIndicator.js +206 -0
  111. package/phantom/dispatch/setMessageReaction.js +188 -0
  112. package/phantom/dispatch/setMessageReactionMqtt.js +248 -0
  113. package/phantom/dispatch/setThreadTheme.js +330 -0
  114. package/phantom/dispatch/setThreadThemeMqtt.js +207 -0
  115. package/phantom/dispatch/share.js +200 -0
  116. package/phantom/dispatch/shareContact.js +216 -0
  117. package/phantom/dispatch/stickers.js +395 -0
  118. package/phantom/dispatch/story.js +240 -0
  119. package/phantom/dispatch/theme.js +296 -0
  120. package/phantom/dispatch/unfriend.js +199 -0
  121. package/phantom/dispatch/unsendMessage.js +124 -0
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "fca-phantom",
3
+ "version": "1.0.0",
4
+ "type": "commonjs",
5
+ "description": "PHANTOM-FCA by SIFAT — a powerful Facebook Chat API (FCA) for Node.js with session revival, E2EE encryption, real-time MQTT messaging, and smart rate control. Fast, reliable, and built to last.",
6
+ "main": "index.js",
7
+ "types": "phantom/datastore/models/types/index.d.ts",
8
+ "files": [
9
+ "index.js",
10
+ "phantom/",
11
+ "LICENSE",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/ewr-sifu/phantom-fca.git"
17
+ },
18
+ "author": "phantom-fca",
19
+ "license": "MIT",
20
+ "keywords": [
21
+ "phantom",
22
+ "phantom-fca",
23
+ "sifu",
24
+ "sifu-fca",
25
+ "fca",
26
+ "fca-unofficial",
27
+ "sifat",
28
+ "sdk",
29
+ "api",
30
+ "facebook",
31
+ "messenger",
32
+ "chat",
33
+ "bot",
34
+ "mqtt",
35
+ "stealth",
36
+ "anti-suspension",
37
+ "automation",
38
+ "session-revival"
39
+ ],
40
+ "dependencies": {
41
+ "axios": "^1.9.0",
42
+ "axios-cookiejar-support": "^4.0.7",
43
+ "bluebird": "^3.7.2",
44
+ "cheerio": "^1.0.0",
45
+ "deepdash": "^5.3.9",
46
+ "duplexify": "^4.1.3",
47
+ "form-data": "^4.0.4",
48
+ "https-proxy-agent": "^7.0.6",
49
+ "jsonpath-plus": "^10.3.0",
50
+ "lodash": "^4.17.21",
51
+ "mqtt": "^4.3.8",
52
+ "node-cron": "^3.0.3",
53
+ "npmlog": "^7.0.1",
54
+ "sequelize": "^6.37.5",
55
+ "sqlite3": "^6.0.1",
56
+ "totp-generator": "^2.0.1",
57
+ "tough-cookie": "^5.1.2",
58
+ "undici": "^7.8.0",
59
+ "uuid": "^11.1.0",
60
+ "websocket-stream": "^5.5.2",
61
+ "ws": "^8.18.2"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "^22.15.17",
65
+ "eslint": "^9.26.0",
66
+ "mocha": "^11.3.0",
67
+ "prettier": "^3.5.3",
68
+ "ts-node": "^10.9.2",
69
+ "typescript": "^5.8.3"
70
+ },
71
+ "engines": {
72
+ "node": ">=22.0.0"
73
+ },
74
+ "scripts": {
75
+ "test": "echo 'No tests configured yet'",
76
+ "lint": "eslint phantom/",
77
+ "format": "prettier --write phantom/**/*.js"
78
+ },
79
+ "overrides": {
80
+ "undici": "^7.8.0",
81
+ "ws": "^8.18.2",
82
+ "tough-cookie": "^5.1.2",
83
+ "glob": "^11.0.2",
84
+ "prebuild-install": "^7.1.3",
85
+ "npmlog": "^7.0.1",
86
+ "are-we-there-yet": "^4.0.2",
87
+ "gauge": "^5.0.2",
88
+ "serialize-javascript": "^6.0.2",
89
+ "cacache": "^18.0.4",
90
+ "make-fetch-happen": "^13.0.3",
91
+ "@tootallnate/once": "^3.0.1",
92
+ "mocha": {
93
+ "serialize-javascript": "^6.0.2",
94
+ "debug": "^4.4.0",
95
+ "diff": "^7.0.0",
96
+ "js-yaml": "^4.1.0",
97
+ "minimatch": "^10.0.1",
98
+ "glob": "^11.0.2"
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,334 @@
1
+ "use strict";
2
+
3
+ const tools = require("../../datastore/models/matrix/tools");
4
+ const axios = require("axios");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+ const qs = require("querystring");
8
+ const { normalizeCookieHeaderString, setJarFromPairs } = require("../../datastore/models/matrix/transform/cookies");
9
+ const { parseRegion, genTotp } = require("../../datastore/models/matrix/auth");
10
+ const { generateIdentityByMode, cacheIdentityData } = require("../../datastore/models/matrix/identity");
11
+
12
+ async function ignite(creds, cfg, cb, configFn, forgeFn, seedApi, originFn, errNoIdentity) {
13
+ let ctx = null, coreFuncs = null, api = seedApi;
14
+ const { phantomBanner } = require("../../datastore/models/matrix/tools");
15
+ phantomBanner();
16
+
17
+ try {
18
+ const jar = tools.getJar();
19
+ tools.log("phantom-fca", "Initializing session...");
20
+
21
+ const mode = cfg.persona || "desktop";
22
+ const modeSwitched = cfg.cachedPersona && cfg.cachedPersona !== mode;
23
+
24
+ if (modeSwitched) {
25
+ tools.log("phantom-fca", `Identity mode changed ${cfg.cachedPersona} → ${mode}, resetting fingerprint cache`);
26
+ const desktopKeys = ["cachedUserAgent","cachedSecChUa","cachedSecChUaFullVersionList","cachedSecChUaPlatform","cachedSecChUaPlatformVersion","cachedBrowser"];
27
+ const androidKeys = ["cachedAndroidUA","cachedAndroidVersion","cachedAndroidDevice","cachedAndroidBuildId","cachedAndroidResolution","cachedAndroidFbav","cachedAndroidFbbv","cachedAndroidLocale","cachedAndroidCarrier"];
28
+ [...desktopKeys, ...androidKeys, "cachedLocale", "cachedTimezone"].forEach(k => delete cfg[k]);
29
+ }
30
+
31
+ const needsDesktop = mode === "desktop" && !cfg.cachedUserAgent;
32
+ const needsAndroid = (mode === "android" || mode === "mobile") && !cfg.cachedAndroidUA;
33
+
34
+ if (needsDesktop || needsAndroid) {
35
+ const identity = generateIdentityByMode(mode, cfg);
36
+ cacheIdentityData(cfg, identity);
37
+ cfg.cachedPersona = mode;
38
+ tools.log("phantom-fca", mode === "desktop"
39
+ ? `Desktop identity initialized (${identity.browser})`
40
+ : "Android/Orca mobile identity initialized");
41
+
42
+ const { getRandomLocale, getRandomTimezone } = require("../../datastore/models/matrix/headers");
43
+ if (!cfg.cachedLocale) cfg.cachedLocale = getRandomLocale();
44
+ if (!cfg.cachedTimezone) cfg.cachedTimezone = getRandomTimezone();
45
+
46
+ try {
47
+ const { globalShield } = require("../../datastore/models/matrix/ghost");
48
+ globalShield.lockSessionFingerprint(
49
+ identity.userAgent || cfg.cachedAndroidUA,
50
+ identity.secChUa || "",
51
+ identity.secChUaPlatform || identity.persona || "desktop",
52
+ cfg.cachedLocale,
53
+ cfg.cachedTimezone
54
+ );
55
+ } catch (_) {}
56
+ } else {
57
+ tools.log("phantom-fca", mode === "desktop" ? "Using cached desktop identity" : "Using cached Android identity");
58
+ }
59
+
60
+ let sessionCookies = creds.appState;
61
+
62
+ if (!sessionCookies && !creds.email && !creds.password) {
63
+ try {
64
+ const { hydrateJarFromDB } = require("../../datastore/session");
65
+ const restored = await hydrateJarFromDB(jar, null);
66
+ if (restored) tools.log("phantom-fca", "Session restored from persistent store");
67
+ } catch (e) {
68
+ tools.warn("phantom-fca", "Could not restore session from store:", e.message);
69
+ }
70
+ }
71
+
72
+ if (sessionCookies) {
73
+ let pairs = [];
74
+ if (Array.isArray(sessionCookies)) {
75
+ pairs = sessionCookies.map(c => [c.name || c.key, c.value].join("="));
76
+ } else if (typeof sessionCookies === "string") {
77
+ pairs = normalizeCookieHeaderString(sessionCookies);
78
+ if (!pairs.length) pairs = sessionCookies.split(";").map(s => s.trim()).filter(Boolean);
79
+ } else {
80
+ throw new Error("Invalid session format. Provide cookie array or cookie string.");
81
+ }
82
+ setJarFromPairs(jar, pairs);
83
+ tools.log("phantom-fca", "Session cookies injected");
84
+
85
+ } else if (creds.email && creds.password) {
86
+ if (creds.totpSecret) tools.log("phantom-fca", "TOTP secret detected");
87
+
88
+ const authParams = {
89
+ access_token: "350685531728|62f8ce9f74b12f84c123cc23437a4a32",
90
+ format: "json",
91
+ sdk_version: 2,
92
+ email: creds.email,
93
+ locale: "en_US",
94
+ password: creds.password,
95
+ generate_session_cookies: 1,
96
+ sig: "c1c640010993db92e5afd11634ced864",
97
+ };
98
+
99
+ if (creds.totpSecret) {
100
+ try {
101
+ const otp = await genTotp(creds.totpSecret);
102
+ authParams.credentials_type = "two_factor";
103
+ authParams.twofactor_code = otp;
104
+ tools.log("phantom-fca", "2FA token generated");
105
+ } catch (e) {
106
+ tools.warn("phantom-fca", "2FA token generation failed:", e.message);
107
+ }
108
+ }
109
+
110
+ try {
111
+ const res = await axios.get(`https://api.facebook.com/method/auth.login?${qs.stringify(authParams)}`);
112
+ if (res.status !== 200) throw new Error("Authentication failed");
113
+ setJarFromPairs(jar, res.data.session_cookies.map(c => `${c.name}=${c.value}`));
114
+ tools.log("phantom-fca", "Email/password authentication successful");
115
+ } catch (e) {
116
+ throw new Error("Authentication failed — check credentials");
117
+ }
118
+
119
+ } else {
120
+ throw new Error("No credentials provided. Supply appState cookies or email+password.");
121
+ }
122
+
123
+ if (!api) {
124
+ api = {
125
+ setOptions: configFn.bind(null, cfg),
126
+ getAppState() {
127
+ const state = tools.getAppState(jar);
128
+ if (!Array.isArray(state)) return [];
129
+ const unique = state.filter((item, i, self) => self.findIndex(t => t.key === item.key) === i);
130
+ return unique.length > 0 ? unique : state;
131
+ },
132
+ };
133
+ }
134
+
135
+ try { const { globalShield } = require("../../datastore/models/matrix/ghost"); globalShield.resetCircuitBreaker(); globalShield.enableWarmup(); } catch (_) {}
136
+
137
+ const pageResp = await tools.get(originFn(), jar, null, cfg, { noRef: true }).then(tools.saveCookies(jar));
138
+
139
+ const extractBlobs = (html) => {
140
+ const blobs = [], re = /<script type="application\/json"[^>]*>(.*?)<\/script>/g;
141
+ let m;
142
+ while ((m = re.exec(html)) !== null) { try { blobs.push(JSON.parse(m[1])); } catch (_) {} }
143
+ return blobs;
144
+ };
145
+
146
+ const netBlobs = extractBlobs(pageResp.body);
147
+ const [newCtx, newCoreFuncs] = await forgeFn(pageResp.body, jar, netBlobs, cfg, originFn, errNoIdentity);
148
+ ctx = newCtx;
149
+ coreFuncs = newCoreFuncs;
150
+
151
+ const detectedRegion = parseRegion(pageResp.body);
152
+ ctx.region = detectedRegion;
153
+ tools.log("phantom-fca", "Region detected:", detectedRegion);
154
+
155
+ try { const { backupAppStateSQL } = require("../../datastore/session"); await backupAppStateSQL(jar, ctx.userID); }
156
+ catch (e) { tools.warn("phantom-fca", "Session snapshot failed:", e.message); }
157
+
158
+ api.message = new Map();
159
+ api.timestamp = {};
160
+
161
+ const mountDispatch = () => {
162
+ const dispatchPath = path.join(__dirname, "..", "..", "dispatch");
163
+ if (!fs.existsSync(dispatchPath)) { tools.error("phantom-fca", "Dispatch directory not found:", dispatchPath); return; }
164
+ const skipModules = ["deltaProcessor"];
165
+ fs.readdirSync(dispatchPath)
166
+ .filter(f => f.endsWith(".js"))
167
+ .forEach(f => {
168
+ const name = path.basename(f, ".js");
169
+ if (skipModules.includes(name)) return;
170
+ try {
171
+ const mod = require(path.join(dispatchPath, f));
172
+ if (typeof mod === "function") api[name] = mod(coreFuncs, api, ctx);
173
+ } catch (e) {
174
+ tools.error("phantom-fca", `Failed to mount dispatch module [${name}]:`, e);
175
+ }
176
+ });
177
+ };
178
+
179
+ api.getCurrentUserID = () => ctx.userID;
180
+ api.getOptions = (k) => (k ? cfg[k] : cfg);
181
+ mountDispatch();
182
+
183
+ if (api.nickname && typeof api.nickname === "function") api.changeNickname = api.nickname;
184
+
185
+ try {
186
+ const schema = require("../../datastore/schema");
187
+ const threadsDb = require("../../datastore/threads");
188
+ const usersDb = require("../../datastore/users");
189
+ if (typeof schema.syncAll === "function") schema.syncAll().catch(() => {});
190
+ api.threadData = threadsDb(api);
191
+ api.userData = usersDb(api);
192
+ } catch (e) { tools.warn("phantom-fca", "Data store optional — skipped:", e.message); }
193
+
194
+ api.ctx = ctx;
195
+ api.defaultFuncs = coreFuncs;
196
+ api.globalOptions = cfg;
197
+
198
+ const { CycleManager } = require("../../datastore/models/matrix/heartbeat");
199
+ if (api._cycleManager) api._cycleManager.halt();
200
+ else api._cycleManager = new CycleManager();
201
+
202
+ const { globalReviveManager } = require("../../datastore/models/matrix/revive");
203
+
204
+ if (cfg.autoReLogin !== false) {
205
+ globalReviveManager.setCredentials(creds, cfg, cb);
206
+ tools.log("phantom-fca", "Auto-revive enabled");
207
+ try { globalReviveManager.updateAppState(api.getAppState()); } catch (_) {}
208
+
209
+ api._cycleManager.setExpiryHandler(() => {
210
+ tools.warn("phantom-fca", "Token cycle expired — triggering revive...");
211
+ globalReviveManager.handleSessionExpiry(api, originFn(), errNoIdentity);
212
+ });
213
+
214
+ ctx.performAutoLogin = async () => {
215
+ try { return (await globalReviveManager.handleSessionExpiry(api, originFn(), errNoIdentity)) !== false; }
216
+ catch (_) { return false; }
217
+ };
218
+ }
219
+
220
+ api.disconnect = () => {
221
+ const fn = require("../../dispatch/logout")(coreFuncs, api, ctx);
222
+ return fn();
223
+ };
224
+
225
+ const shutdownCleanup = () => {
226
+ tools.log("phantom-fca", "Graceful shutdown in progress...");
227
+ try { if (api._cycleManager) api._cycleManager.halt(); } catch (_) {}
228
+ try { globalReviveManager.stopSessionMonitoring(); globalReviveManager.disable(); } catch (_) {}
229
+ try { if (ctx.mqttClient?.end) ctx.mqttClient.end(true); } catch (_) {}
230
+ try { if (ctx._emitter) ctx._emitter.removeAllListeners(); } catch (_) {}
231
+ ["_mqttWatchdog","_autoCycleTimer","_periodicBackupInterval"].forEach(k => { try { if (ctx[k]) clearInterval(ctx[k]); } catch (_) {} });
232
+ ["_tmsTimeout","_reconnectTimer"].forEach(k => { try { if (ctx[k]) clearTimeout(ctx[k]); } catch (_) {} });
233
+ tools.log("phantom-fca", "Shutdown complete");
234
+ };
235
+
236
+ if (!process._phantomCleanupRegistered) {
237
+ process._phantomCleanupRegistered = true;
238
+ process.on("exit", () => shutdownCleanup());
239
+ process.on("SIGINT", () => { shutdownCleanup(); process.exit(0); });
240
+ process.on("SIGTERM", () => { shutdownCleanup(); process.exit(0); });
241
+ process.on("uncaughtException", (err) => { tools.error("phantom-fca", err.message); shutdownCleanup(); process.exit(1); });
242
+ process.on("unhandledRejection", (r) => tools.error("phantom-fca", String(r?.message ?? r)));
243
+ }
244
+
245
+ if (ctx._periodicBackupInterval) clearInterval(ctx._periodicBackupInterval);
246
+ ctx._periodicBackupInterval = setInterval(async () => {
247
+ try { const { backupAppStateSQL } = require("../../datastore/session"); await backupAppStateSQL(jar, ctx.userID); } catch (_) {}
248
+ }, 15 * 60_000);
249
+ try { ctx._periodicBackupInterval.unref(); } catch (_) {}
250
+
251
+ api._cycleManager.startAutoRefresh(ctx, coreFuncs, originFn());
252
+
253
+ api.refreshTokens = () => api._cycleManager.refreshTokens(ctx, coreFuncs, originFn());
254
+ api.getCycleStatus = () => api._cycleManager.getStatus();
255
+
256
+ api.getHealthStatus = () => {
257
+ let rateStats = null;
258
+ try { rateStats = require("../../datastore/models/matrix/gate").getRateLimiterStats(); } catch (_) {}
259
+ return {
260
+ mqttConnected: !!(ctx.mqttClient?.connected),
261
+ autoReconnect: !!cfg.autoReconnect,
262
+ tokenCycle: {
263
+ lastRefresh: api._cycleManager.lastRefresh,
264
+ nextRefresh: api._cycleManager.getTimeUntilNextRefresh(),
265
+ failureCount: api._cycleManager.getFailureCount(),
266
+ },
267
+ autoRevive: {
268
+ enabled: globalReviveManager.isEnabled(),
269
+ sessionMonitoring: !!globalReviveManager.sessionMonitorInterval,
270
+ stats: globalReviveManager.getStats(),
271
+ },
272
+ rateLimiter: rateStats,
273
+ };
274
+ };
275
+
276
+ api.enableAutoRevive = (on = true) => on
277
+ ? globalReviveManager.setCredentials(creds, cfg, cb)
278
+ : globalReviveManager.disable();
279
+ api.isAutoReviveActive = () => globalReviveManager.isEnabled();
280
+
281
+ api.isSessionAlive = () => new Promise(async (resolve) => {
282
+ try {
283
+ const probe = { noRef: true, _skipSessionInspect: true };
284
+ const r = await tools.get(
285
+ "https://www.facebook.com/ajax/presence/reconnect.php?reason=14&fb_dtsg_ag=&__a=1",
286
+ ctx.jar, null, ctx.globalOptions, probe
287
+ );
288
+ const html = r.body || "";
289
+ if (html.includes('<form id="login_form"') || html.includes("id=\"loginbutton\"") ||
290
+ html.includes('"login_page"') || html.includes('action="/login.php')) return resolve(false);
291
+ if (html.includes('"checkpoint"') && html.includes('"flow_type"')) {
292
+ try { const { globalShield } = require("../../datastore/models/matrix/ghost"); globalShield.tripCircuitBreaker("checkpoint_detected", 3600_000); } catch (_) {}
293
+ return resolve(false);
294
+ }
295
+ resolve(!!(ctx.fb_dtsg && ctx.fb_dtsg.length > 10));
296
+ } catch (err) {
297
+ const msg = err.message || String(err);
298
+ const code = err.code || "";
299
+ const NET = ["ECONNRESET","ETIMEDOUT","ECONNREFUSED","ENETUNREACH","EHOSTUNREACH","EAI_AGAIN","ENOTFOUND","ESOCKETTIMEDOUT"];
300
+ if (NET.some(c => code === c || msg.includes(c)) || msg.includes("socket hang up")) {
301
+ tools.warn("phantom-fca", "Session probe — transient network issue");
302
+ return resolve("network_error");
303
+ }
304
+ tools.error("phantom-fca", "Session probe failed:", msg);
305
+ resolve(false);
306
+ }
307
+ });
308
+
309
+ api.isSessionValid = api.isSessionAlive;
310
+
311
+ if (cfg.autoReLogin !== false) {
312
+ try { globalReviveManager.startSessionMonitoring(api); tools.log("phantom-fca", "Session watchdog started"); } catch (_) {}
313
+ }
314
+
315
+ try {
316
+ const { globalShield } = require("../../datastore/models/matrix/ghost");
317
+ api.shield = {
318
+ getStatus: () => globalShield.getStatus(),
319
+ reset: () => globalShield.resetCircuitBreaker(),
320
+ addDelay: () => globalShield.addSmartDelay(),
321
+ setDailyLimit:(n) => globalShield.setDailyLimit(n),
322
+ getBehavior: () => globalShield.getBehaviorProfile(),
323
+ };
324
+ } catch (_) {}
325
+
326
+ cb(null, api);
327
+
328
+ } catch (err) {
329
+ tools.error("phantom-fca", "Session launch error:", err.message || err);
330
+ cb(err);
331
+ }
332
+ }
333
+
334
+ module.exports = ignite;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ const tools = require("../../datastore/models/matrix/tools");
4
+
5
+ /**
6
+ * Applies runtime configuration overrides to the live options map.
7
+ *
8
+ * @param {object} cfg Current global options reference.
9
+ * @param {object} [patch] New key/value pairs to apply.
10
+ * @returns {Promise<void>}
11
+ */
12
+ async function applyConfig(cfg, patch = {}) {
13
+ const handlers = {
14
+ online: (v) => (cfg.online = Boolean(v)),
15
+ selfListen: (v) => (cfg.selfListen = Boolean(v)),
16
+ selfListenEvent: (v) => (cfg.selfListenEvent = v),
17
+ listenEvents: (v) => (cfg.listenEvents = Boolean(v)),
18
+ updatePresence: (v) => (cfg.updatePresence = Boolean(v)),
19
+ forceLogin: (v) => (cfg.forceLogin = Boolean(v)),
20
+ userAgent: (v) => (cfg.userAgent = v),
21
+ autoMarkDelivery: (v) => (cfg.autoMarkDelivery = Boolean(v)),
22
+ autoMarkRead: (v) => (cfg.autoMarkRead = Boolean(v)),
23
+ listenTyping: (v) => (cfg.listenTyping = Boolean(v)),
24
+ autoReconnect: (v) => (cfg.autoReconnect = Boolean(v)),
25
+ emitReady: (v) => (cfg.emitReady = Boolean(v)),
26
+ simulateTyping: (v) => (cfg.simulateTyping = Boolean(v)),
27
+ proxy(v) {
28
+ if (typeof v !== "string") { delete cfg.proxy; tools.setProxy(); }
29
+ else { cfg.proxy = v; tools.setProxy(v); }
30
+ },
31
+ randomUserAgent(v) {
32
+ cfg.randomUserAgent = Boolean(v);
33
+ if (v) cfg.userAgent = tools.randomUserAgent();
34
+ },
35
+ bypassRegion(v) {
36
+ cfg.bypassRegion = v ? v.toUpperCase() : v;
37
+ },
38
+ maxConcurrentRequests(v) {
39
+ if (typeof v === "number") {
40
+ cfg.maxConcurrentRequests = Math.floor(v);
41
+ tools.configureRateLimiter({ maxConcurrentRequests: cfg.maxConcurrentRequests });
42
+ }
43
+ },
44
+ maxRequestsPerMinute(v) {
45
+ if (typeof v === "number") {
46
+ cfg.maxRequestsPerMinute = Math.floor(v);
47
+ tools.configureRateLimiter({ maxRequestsPerMinute: cfg.maxRequestsPerMinute });
48
+ }
49
+ },
50
+ requestCooldownMs(v) {
51
+ if (typeof v === "number") {
52
+ cfg.requestCooldownMs = Math.floor(v);
53
+ tools.configureRateLimiter({ requestCooldownMs: cfg.requestCooldownMs });
54
+ }
55
+ },
56
+ errorCacheTtlMs(v) {
57
+ if (typeof v === "number") {
58
+ cfg.errorCacheTtlMs = Math.floor(v);
59
+ tools.configureRateLimiter({ errorCacheTtlMs: cfg.errorCacheTtlMs });
60
+ }
61
+ },
62
+ stealthMode(v) {
63
+ cfg.stealthMode = Boolean(v);
64
+ if (v) {
65
+ cfg.updatePresence = false;
66
+ cfg.online = false;
67
+ cfg.simulateTyping = false;
68
+ tools.configureRateLimiter({ maxConcurrentRequests: 2, maxRequestsPerMinute: 60 });
69
+ }
70
+ },
71
+ };
72
+
73
+ for (const [key, val] of Object.entries(patch)) {
74
+ if (handlers[key]) handlers[key](val);
75
+ }
76
+ }
77
+
78
+ module.exports = applyConfig;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+
3
+ const tools = require("../../datastore/models/matrix/tools");
4
+ const { SessionStore } = require("../../datastore/models/matrix/store");
5
+ const { globalValidator } = require("../../datastore/models/matrix/validator");
6
+
7
+ async function forge(pageHtml, cookieJar, netBlobs, cfg, originFn, errMsg) {
8
+ let accountID;
9
+ const cookies = cookieJar.getCookiesSync(originFn());
10
+ const primary = cookies.find(c => c.cookieString().startsWith("c_user="));
11
+ const secondary = cookies.find(c => c.cookieString().startsWith("i_user="));
12
+ if (!primary && !secondary) throw new Error(errMsg);
13
+ accountID = secondary?.cookieString().split("=")[1] || primary.cookieString().split("=")[1];
14
+
15
+ function scanBlobs(target) {
16
+ for (const blob of netBlobs) {
17
+ if (!blob.require) continue;
18
+ for (const req of blob.require) {
19
+ if (Array.isArray(req) && req[0] === target && req[2]) return req[2];
20
+ if (Array.isArray(req) && req[3]?.[0]?.__bbox?.define) {
21
+ for (const def of req[3][0].__bbox.define) {
22
+ if (Array.isArray(def) && def[0].endsWith(target) && def[2]) return def[2];
23
+ }
24
+ }
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+
30
+ const dtsgBlob = scanBlobs("DTSGInitialData");
31
+ const fb_dtsg = dtsgBlob?.token ?? tools.getFrom(pageHtml, '"token":"', '"');
32
+ const lsdBlob = scanBlobs("LSD");
33
+ const lsd = lsdBlob?.token ?? tools.getFrom(pageHtml, '"LSD",[],{"token":"', '"');
34
+ const dtsgAgBlob = scanBlobs("DTSGAGInitialData");
35
+ const fb_dtsg_ag = dtsgAgBlob?.token ?? tools.getFrom(pageHtml, '"DTSGAGInitialData",[],{"token":"', '"');
36
+
37
+ const spinR = pageHtml.match(/"__spin_r":(\d+)/)?.[1];
38
+ const spinB = pageHtml.match(/"__spin_b":"([^"]+)"/)?.[1];
39
+ const spinT = pageHtml.match(/"__spin_t":(\d+)/)?.[1];
40
+ const hsi = pageHtml.match(/"hsi":"(\d+)"/)?.[1];
41
+ const dyn = pageHtml.match(/"dyn":"([^"]+)"/)?.[1];
42
+ const csr = pageHtml.match(/"csr":"([^"]+)"/)?.[1];
43
+
44
+ const tokenBundle = {
45
+ fb_dtsg,
46
+ jazoest: `2${Array.from(fb_dtsg || "").reduce((a, b) => a + b.charCodeAt(0), "")}`,
47
+ lsd,
48
+ fb_dtsg_ag,
49
+ __spin_r: spinR,
50
+ __spin_b: spinB,
51
+ __spin_t: spinT,
52
+ hsi,
53
+ dyn,
54
+ csr,
55
+ };
56
+
57
+ const mqttDevBlob = scanBlobs("MqttWebDeviceID");
58
+ const mqttCfgBlob = scanBlobs("MqttWebConfig");
59
+ const userBlob = scanBlobs("CurrentUserInitialData");
60
+ const clientID = mqttDevBlob?.clientID;
61
+ const mqttAppID = mqttCfgBlob?.appID;
62
+ const userAppID = userBlob?.APP_ID;
63
+ const primaryAppID = userAppID || mqttAppID;
64
+ let mqttEndpoint = mqttCfgBlob?.endpoint;
65
+
66
+ let region;
67
+ if (mqttEndpoint) {
68
+ try { region = new URL(mqttEndpoint).searchParams.get("region")?.toUpperCase(); } catch (_) {}
69
+ }
70
+
71
+ const irisSeqID = pageHtml.match(/irisSeqID:"(.+?)"/)?.[1] ?? null;
72
+
73
+ if (cfg.bypassRegion && mqttEndpoint) {
74
+ const ep = new URL(mqttEndpoint);
75
+ ep.searchParams.set("region", cfg.bypassRegion.toLowerCase());
76
+ mqttEndpoint = ep.toString();
77
+ region = cfg.bypassRegion.toUpperCase();
78
+ }
79
+
80
+ const sessionStore = new SessionStore({ maxSize: 500, ttlMs: 5 * 60_000 });
81
+
82
+ const ctx = {
83
+ accountID,
84
+ userID: accountID,
85
+ jar: cookieJar,
86
+ clientID,
87
+ appID: primaryAppID,
88
+ mqttAppID,
89
+ userAppID,
90
+ globalOptions: cfg,
91
+ loggedIn: true,
92
+ access_token: "NONE",
93
+ clientMutationId: 0,
94
+ mqttClient: undefined,
95
+ lastSeqId: irisSeqID,
96
+ syncToken: undefined,
97
+ mqttEndpoint,
98
+ wsReqNumber: 0,
99
+ wsTaskNumber: 0,
100
+ reqCallbacks: {},
101
+ callback_Task: {},
102
+ region,
103
+ firstListen: true,
104
+ cache: sessionStore,
105
+ validator: globalValidator,
106
+ ...tokenBundle,
107
+ };
108
+
109
+ const coreFuncs = tools.makeDefaults(pageHtml, accountID, ctx);
110
+ return [ctx, coreFuncs, {}];
111
+ }
112
+
113
+ module.exports = forge;