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
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+
3
+ const { warn, log, error } = require("./logger");
4
+
5
+ const THREAT_PATTERNS = [
6
+ { pattern: /checkpoint/i, score: 80 },
7
+ { pattern: /action_required|action required/i, score: 70 },
8
+ { pattern: /account.{0,10}(locked|suspended|disabled|banned)/i, score: 95 },
9
+ { pattern: /your account has been (suspended|disabled|banned|locked)/i, score: 100 },
10
+ { pattern: /this account has been (suspended|disabled)/i, score: 100 },
11
+ { pattern: /unusual.{0,10}(activity|behavior)/i, score: 75 },
12
+ { pattern: /suspicious.{0,10}activity/i, score: 75 },
13
+ { pattern: /verify.{0,15}(account|identity)/i, score: 65 },
14
+ { pattern: /confirm.{0,15}(identity|it'?s you)/i, score: 65 },
15
+ { pattern: /security.{0,10}check/i, score: 60 },
16
+ { pattern: /login.{0,10}approvals/i, score: 55 },
17
+ { pattern: /two.?factor.{0,10}(authentication|required)/i, score: 55 },
18
+ { pattern: /too.{0,10}many.{0,10}(requests|attempts)/i, score: 70 },
19
+ { pattern: /rate.{0,10}limit(ed)?/i, score: 65 },
20
+ { pattern: /temporarily.{0,10}block(ed)?/i, score: 80 },
21
+ { pattern: /automated.{0,10}behavior/i, score: 90 },
22
+ { pattern: /not.{0,5}a.{0,5}human/i, score: 95 },
23
+ { pattern: /bot.{0,10}detect(ed|ion)/i, score: 90 },
24
+ { pattern: /spam.{0,10}detect(ed)?/i, score: 75 },
25
+ { pattern: /violates.{0,10}(our.{0,5})?community.{0,5}standards/i, score: 70 },
26
+ { pattern: /policy.{0,10}violation/i, score: 75 },
27
+ { pattern: /action.{0,5}blocked/i, score: 70 },
28
+ { pattern: /messaging.{0,10}limit.{0,10}reached/i, score: 80 },
29
+ { pattern: /sending.{0,10}limit/i, score: 75 },
30
+ { pattern: /you'?ve.{0,10}reached.{0,10}your.{0,10}limit/i, score: 75 },
31
+ { pattern: /integrity.{0,10}violation/i, score: 85 },
32
+ { pattern: /anti.{0,5}(spam|abuse)/i, score: 65 },
33
+ { pattern: /device.{0,10}login/i, score: 55 },
34
+ { pattern: /blocked.{0,10}from.{0,10}sending/i, score: 80 },
35
+ ];
36
+
37
+ const THREAT_SCORE_THRESHOLDS = {
38
+ warn: 40,
39
+ throttle:60,
40
+ block: 80,
41
+ trip: 85,
42
+ };
43
+
44
+ const HOUR_MULTIPLIERS = [
45
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.15,
46
+ 0.25, 0.4, 0.6, 0.8, 1.0, 1.0,
47
+ 0.9, 1.0, 1.0, 0.95, 0.9, 0.85,
48
+ 0.8, 0.75, 0.7, 0.5, 0.3, 0.15,
49
+ ];
50
+
51
+ class Shield {
52
+ constructor() {
53
+ this._threadActivity = new Map();
54
+ this._threadLastMsg = new Map();
55
+ this._threadRisk = new Map();
56
+ this._sessionStart = Date.now();
57
+ this._totalSent = 0;
58
+
59
+ this._msgDelay = 120;
60
+ this._threadDelay = 400;
61
+ this._burst = 0;
62
+ this._burstStart = Date.now();
63
+ this._burstWindow = 60_000;
64
+ this._burstLimit = 30;
65
+
66
+ this._daily = {
67
+ date: new Date().toDateString(),
68
+ sent: 0,
69
+ max: 2500,
70
+ perThread: new Map(),
71
+ };
72
+
73
+ this._hourly = {
74
+ hour: new Date().getHours(),
75
+ count: 0,
76
+ max: 350,
77
+ };
78
+
79
+ this._breaker = {
80
+ tripped: false,
81
+ at: null,
82
+ cooldown: 45 * 60_000,
83
+ signals: 0,
84
+ maxSignals: 2,
85
+ lastSignal: null,
86
+ totalTrips: 0,
87
+ history: [],
88
+ };
89
+
90
+ this._threatScore = 0;
91
+ this._fingerprint = null;
92
+ this._consecutive = 0;
93
+ this._maxConsecutive = 10;
94
+ this._responseScores = [];
95
+
96
+ this._warmup = {
97
+ active: false,
98
+ startedAt: null,
99
+ durationMs: 15 * 60_000,
100
+ maxPerHour: 40,
101
+ };
102
+
103
+ this._dailyTimer = setInterval(() => this._checkDailyReset(), 60_000).unref?.() || setInterval(() => this._checkDailyReset(), 60_000);
104
+ this._hourlyTimer = setInterval(() => this._checkHourlyReset(), 30_000).unref?.() || setInterval(() => this._checkHourlyReset(), 30_000);
105
+ try { this._dailyTimer.unref(); } catch (_) {}
106
+ try { this._hourlyTimer.unref(); } catch (_) {}
107
+ }
108
+
109
+ _checkDailyReset() {
110
+ const today = new Date().toDateString();
111
+ if (this._daily.date !== today) {
112
+ this._daily.date = today;
113
+ this._daily.sent = 0;
114
+ this._daily.perThread.clear();
115
+ this._threadRisk.clear();
116
+ log("Shield", "Daily counters reset");
117
+ }
118
+ }
119
+
120
+ _checkHourlyReset() {
121
+ const h = new Date().getHours();
122
+ if (this._hourly.hour !== h) {
123
+ this._hourly.hour = h;
124
+ this._hourly.count = 0;
125
+ }
126
+ }
127
+
128
+ _currentHourMultiplier() {
129
+ return HOUR_MULTIPLIERS[new Date().getHours()] ?? 1;
130
+ }
131
+
132
+ _effectiveHourlyLimit() {
133
+ if (this._warmup.active) return this._warmup.maxPerHour;
134
+ return Math.floor(this._hourly.max * this._currentHourMultiplier()) || 1;
135
+ }
136
+
137
+ lockSessionFingerprint(ua, secChUa, platform, locale, timezone) {
138
+ this._fingerprint = { ua, secChUa, platform, locale, timezone, locked: Date.now() };
139
+ log("Shield", "Session fingerprint locked");
140
+ }
141
+
142
+ getFingerprint() { return this._fingerprint; }
143
+
144
+ enableWarmup(durationMs = null) {
145
+ this._warmup.active = true;
146
+ this._warmup.startedAt = Date.now();
147
+ const dur = durationMs ?? this._warmup.durationMs;
148
+ setTimeout(() => {
149
+ this._warmup.active = false;
150
+ log("Shield", "Warmup mode ended");
151
+ }, dur);
152
+ log("Shield", `Warmup mode activated (${Math.round(dur / 60_000)}min)`);
153
+ }
154
+
155
+ scoreThreat(text) {
156
+ if (!text || typeof text !== "string") return 0;
157
+ let score = 0;
158
+ const lower = text.toLowerCase();
159
+ for (const { pattern, score: s } of THREAT_PATTERNS) {
160
+ if (pattern.test(lower)) score = Math.max(score, s);
161
+ }
162
+ return score;
163
+ }
164
+
165
+ detectThreatSignal(text) {
166
+ return this.scoreThreat(text) >= THREAT_SCORE_THRESHOLDS.warn;
167
+ }
168
+
169
+ processResponse(responseText, threadID = null) {
170
+ if (!responseText || typeof responseText !== "string") return false;
171
+ const score = this.scoreThreat(responseText);
172
+ if (score < THREAT_SCORE_THRESHOLDS.warn) return false;
173
+
174
+ this._threatScore = Math.min(100, this._threatScore + score * 0.1);
175
+ this._breaker.signals++;
176
+ this._responseScores.push({ score, at: Date.now(), threadID });
177
+ if (this._responseScores.length > 20) this._responseScores.shift();
178
+
179
+ if (threadID) {
180
+ const prev = this._threadRisk.get(String(threadID)) || 0;
181
+ this._threadRisk.set(String(threadID), Math.min(100, prev + score * 0.3));
182
+ }
183
+
184
+ const level =
185
+ score >= THREAT_SCORE_THRESHOLDS.trip ? "trip" :
186
+ score >= THREAT_SCORE_THRESHOLDS.block ? "block" :
187
+ score >= THREAT_SCORE_THRESHOLDS.throttle? "throttle" : "warn";
188
+
189
+ warn("Shield", `Threat detected [score:${score}, level:${level}] (signals:${this._breaker.signals}/${this._breaker.maxSignals})`);
190
+
191
+ if (level === "trip" || this._breaker.signals >= this._breaker.maxSignals) {
192
+ if (!this._breaker.tripped) this.tripCircuitBreaker(`threat_level_${level}`, this._breaker.cooldown);
193
+ }
194
+ return true;
195
+ }
196
+
197
+ tripCircuitBreaker(reason = "unknown", cooldown = null) {
198
+ this._breaker.tripped = true;
199
+ this._breaker.at = Date.now();
200
+ this._breaker.signals = Math.max(this._breaker.signals, 1);
201
+ this._breaker.lastSignal = reason;
202
+ this._breaker.totalTrips++;
203
+ this._breaker.history.push({ reason, at: Date.now(), cooldown });
204
+ if (this._breaker.history.length > 20) this._breaker.history.shift();
205
+ const cd = cooldown ?? this._breaker.cooldown;
206
+ warn("Shield", `Circuit breaker TRIPPED — reason: ${reason} — cooldown: ${Math.round(cd / 60_000)}min`);
207
+ setTimeout(() => {
208
+ this.resetCircuitBreaker();
209
+ log("Shield", "Circuit breaker auto-reset after cooldown");
210
+ }, cd);
211
+ }
212
+
213
+ resetCircuitBreaker() {
214
+ this._breaker.tripped = false;
215
+ this._breaker.signals = 0;
216
+ this._breaker.at = null;
217
+ this._threatScore = Math.max(0, this._threatScore - 20);
218
+ log("Shield", "Circuit breaker reset");
219
+ }
220
+
221
+ isCircuitBreakerTripped() { return this._breaker.tripped; }
222
+
223
+ getThreadRisk(threadID) { return this._threadRisk.get(String(threadID)) || 0; }
224
+
225
+ isDailyLimitReached() { return this._daily.sent >= this._daily.max; }
226
+ isHourlyLimitReached() { return this._hourly.count >= this._effectiveHourlyLimit(); }
227
+ isBurstLimitReached() {
228
+ if (Date.now() - this._burstStart > this._burstWindow) { this._burst = 0; this._burstStart = Date.now(); }
229
+ return this._burst >= this._burstLimit;
230
+ }
231
+
232
+ setDailyLimit(n) { if (typeof n === "number" && n > 0) this._daily.max = n; }
233
+ setHourlyLimit(n) { if (typeof n === "number" && n > 0) this._hourly.max = n; }
234
+ setBurstLimit(n) { if (typeof n === "number" && n > 0) this._burstLimit = n; }
235
+
236
+ async addSmartDelay(threadID = null) {
237
+ if (this.isCircuitBreakerTripped()) {
238
+ const remaining = Math.max(0, (this._breaker.at + this._breaker.cooldown) - Date.now());
239
+ warn("Shield", `Circuit breaker active — waiting ${Math.round(remaining / 1000)}s`);
240
+ await new Promise(r => setTimeout(r, Math.min(remaining, 30_000)));
241
+ return;
242
+ }
243
+
244
+ const now = Date.now();
245
+
246
+ if (threadID) {
247
+ const last = this._threadLastMsg.get(String(threadID));
248
+ if (last && now - last < this._threadDelay) {
249
+ const threadWait = this._threadDelay - (now - last) + _jitter(200);
250
+ await new Promise(r => setTimeout(r, threadWait));
251
+ }
252
+ this._threadLastMsg.set(String(threadID), Date.now());
253
+ }
254
+
255
+ const risk = this.getThreadRisk(threadID);
256
+ const riskMs = Math.floor(risk * 10);
257
+ let base = this._msgDelay + riskMs;
258
+
259
+ if (this._warmup.active) base = Math.max(base, 500);
260
+ if (this._threatScore > 30) base = Math.floor(base * (1 + this._threatScore / 100));
261
+ if (this._consecutive > 5) base = Math.floor(base * (1 + this._consecutive * 0.05));
262
+
263
+ const jitter = _jitter(150);
264
+ if (base + jitter > 0) await new Promise(r => setTimeout(r, base + jitter));
265
+
266
+ this._burst++;
267
+ this._daily.sent++;
268
+ this._hourly.count++;
269
+ this._totalSent++;
270
+ this._consecutive++;
271
+
272
+ if (this._burst > this._burstLimit) this._consecutive = 0;
273
+ if (Date.now() - this._burstStart > this._burstWindow) {
274
+ this._burst = 0;
275
+ this._burstStart = Date.now();
276
+ this._consecutive = 0;
277
+ }
278
+
279
+ if (threadID) {
280
+ const ts = this._daily.perThread.get(String(threadID)) || { count: 0 };
281
+ ts.count++;
282
+ ts.last = Date.now();
283
+ this._daily.perThread.set(String(threadID), ts);
284
+ }
285
+ }
286
+
287
+ async simulateTyping(threadID, charCount = 0) {
288
+ const WPM = 180 + _jitter(80);
289
+ const chars = Math.max(charCount, 1);
290
+ const words = chars / 5;
291
+ const base = Math.round((words / WPM) * 60_000);
292
+ const jitter = _jitter(400);
293
+ return Math.max(350, Math.min(base + jitter, 5_000));
294
+ }
295
+
296
+ async prepareBeforeMessage(threadID, body = "") {
297
+ if (this.isDailyLimitReached()) warn("Shield", `Daily limit reached (${this._daily.sent}/${this._daily.max})`);
298
+ if (this.isHourlyLimitReached()) warn("Shield", `Hourly limit reached (${this._hourly.count}/${this._effectiveHourlyLimit()})`);
299
+ await this.addSmartDelay(threadID);
300
+ }
301
+
302
+ getBehaviorProfile() {
303
+ return {
304
+ circuitBreakerTripped: this._breaker.tripped,
305
+ totalSignals: this._breaker.signals,
306
+ totalTrips: this._breaker.totalTrips,
307
+ threatScore: Math.round(this._threatScore),
308
+ warmupActive: this._warmup.active,
309
+ dailySent: this._daily.sent,
310
+ dailyMax: this._daily.max,
311
+ hourlySent: this._hourly.count,
312
+ hourlyMax: this._effectiveHourlyLimit(),
313
+ burstCount: this._burst,
314
+ burstLimit: this._burstLimit,
315
+ consecutiveMsgs: this._consecutive,
316
+ uptime: Date.now() - this._sessionStart,
317
+ totalSent: this._totalSent,
318
+ };
319
+ }
320
+
321
+ getStatus() { return this.getBehaviorProfile(); }
322
+ getConfig() { return this.getBehaviorProfile(); }
323
+
324
+ detectSuspensionSignal(text) { return this.detectThreatSignal(text); }
325
+ }
326
+
327
+ function _jitter(max) { return Math.floor(Math.random() * max); }
328
+
329
+ const globalShield = new Shield();
330
+ const globalAntiSuspension = globalShield;
331
+
332
+ module.exports = { Shield, globalShield, globalAntiSuspension };
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+
3
+ const { generateIdentityByMode } = require("./identity");
4
+
5
+ const LOCALES = [
6
+ "en-US,en;q=0.9",
7
+ "en-GB,en;q=0.9,en-US;q=0.8",
8
+ "en-US,en;q=0.9,es;q=0.8",
9
+ "en-US,en;q=0.9,fr;q=0.8",
10
+ "en-CA,en;q=0.9,fr;q=0.8",
11
+ "en-AU,en;q=0.9,en-GB;q=0.8",
12
+ "en-IN,en;q=0.9,hi;q=0.8",
13
+ "en-US,en;q=0.9,de;q=0.8",
14
+ "en-US,en;q=0.9,ja;q=0.7",
15
+ "en-US,en;q=0.9,ko;q=0.7",
16
+ ];
17
+
18
+ const TIMEZONES = [
19
+ -720, -660, -600, -570, -540, -480, -420, -360, -300, -240,
20
+ -210, -180, -120, -60, 0, 60, 120, 180, 210, 240,
21
+ 270, 300, 330, 345, 360, 390, 420, 480, 525, 540,
22
+ 570, 600, 630, 660, 720,
23
+ ];
24
+
25
+ const DPR_VALUES = ["1", "1.25", "1.5", "2", "2.5", "3"];
26
+ const VIEWPORTS = ["1280", "1366", "1440", "1536", "1600", "1920", "2560"];
27
+
28
+ function getRandomLocale() { return LOCALES[Math.floor(Math.random() * LOCALES.length)]; }
29
+ function getRandomTimezone() { return TIMEZONES[Math.floor(Math.random() * TIMEZONES.length)]; }
30
+ function getRandomDpr() { return DPR_VALUES[Math.floor(Math.random() * DPR_VALUES.length)]; }
31
+ function getRandomViewport() { return VIEWPORTS[Math.floor(Math.random() * VIEWPORTS.length)]; }
32
+
33
+ function sanitizeVal(v) {
34
+ if (v == null) return "";
35
+ let s = String(v);
36
+ if (s.trim().startsWith("[") && s.trim().endsWith("]")) {
37
+ try { if (Array.isArray(JSON.parse(s))) return ""; } catch (_) {}
38
+ }
39
+ return s.replace(/[\x00-\x08\x0A-\x1F\x7F\r\n\[\]]/g, "").trim();
40
+ }
41
+
42
+ function sanitize(hdrs) {
43
+ const out = {};
44
+ for (const [k, v] of Object.entries(hdrs)) {
45
+ if (!k || typeof k !== "string") continue;
46
+ const ck = k.replace(/[^\x21-\x7E]/g, "").trim();
47
+ if (!ck) continue;
48
+ const cv = sanitizeVal(v);
49
+ if (cv !== "") out[ck] = cv;
50
+ }
51
+ return out;
52
+ }
53
+
54
+ function _getHost(url) {
55
+ try { return new URL(url).hostname; } catch (_) { return "www.facebook.com"; }
56
+ }
57
+
58
+ function _getOrigin(url) {
59
+ try { const u = new URL(url); return `${u.protocol}//${u.host}`; } catch (_) { return "https://www.facebook.com"; }
60
+ }
61
+
62
+ function buildAndroidHeaders(url, opts, ctx, custom) {
63
+ const id = generateIdentityByMode("android", opts || {});
64
+ const ua = opts?.cachedAndroidUA || id.userAgent;
65
+ const host = _getHost(url);
66
+ const locale = opts?.cachedLocale || getRandomLocale();
67
+
68
+ const h = {
69
+ "Content-Type": "application/x-www-form-urlencoded",
70
+ "Host": host,
71
+ "Connection": "keep-alive",
72
+ "User-Agent": ua,
73
+ "Accept": "*/*",
74
+ "Accept-Language": locale.split(",")[0].trim(),
75
+ "Accept-Encoding": "gzip, deflate",
76
+ "X-FB-HTTP-Engine": "Liger",
77
+ "X-FB-Client-IP": "True",
78
+ "X-FB-Server-Cluster": "True",
79
+ };
80
+
81
+ if (id.resolution) h["X-FB-Client-Density"] = String(id.resolution.density || "2");
82
+ if (ctx) {
83
+ if (ctx.lsd || ctx.fb_dtsg) h["X-Fb-Lsd"] = ctx.lsd || ctx.fb_dtsg;
84
+ if (ctx.region) h["X-MSGR-Region"] = ctx.region;
85
+ if (ctx.__spin_r) h["X-Fb-Spin-R"] = String(ctx.__spin_r);
86
+ if (ctx.__spin_b) h["X-Fb-Spin-B"] = String(ctx.__spin_b);
87
+ if (ctx.__spin_t) h["X-Fb-Spin-T"] = String(ctx.__spin_t);
88
+ }
89
+ if (custom) { Object.assign(h, custom); if (custom.noRef) delete h.Referer; }
90
+ return sanitize(h);
91
+ }
92
+
93
+ function buildDesktopHeaders(url, opts, ctx, custom, reqType = "navigate") {
94
+ const id = generateIdentityByMode("desktop", opts || {});
95
+ const host = _getHost(url);
96
+ const origin = _getOrigin(url);
97
+ const isXhr = reqType === "xhr";
98
+ const locale = opts?.cachedLocale || getRandomLocale();
99
+ const tz = opts?.cachedTimezone || getRandomTimezone();
100
+ const dpr = getRandomDpr();
101
+ const vp = getRandomViewport();
102
+
103
+ const ua = opts?.cachedUserAgent || id.userAgent;
104
+ const chUa = opts?.cachedSecChUa || id.secChUa;
105
+ const chFvl = opts?.cachedSecChUaFullVersionList || id.secChUaFullVersionList;
106
+ const chPlat = opts?.cachedSecChUaPlatform || id.secChUaPlatform;
107
+ const chPlatV = opts?.cachedSecChUaPlatformVersion || id.secChUaPlatformVersion;
108
+
109
+ const h = {
110
+ "Accept": isXhr
111
+ ? "application/json, text/plain, */*"
112
+ : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
113
+ "Accept-Encoding": "gzip, deflate, br, zstd",
114
+ "Accept-Language": locale,
115
+ "Cache-Control": "no-cache",
116
+ "Connection": "keep-alive",
117
+ "DNT": "1",
118
+ "Dpr": dpr,
119
+ "Host": host,
120
+ "Pragma": "no-cache",
121
+ "Referer": `${origin}/`,
122
+ "Sec-Ch-Prefers-Color-Scheme": "light",
123
+ "Sec-Ch-Ua": chUa,
124
+ "Sec-Ch-Ua-Full-Version-List": chFvl,
125
+ "Sec-Ch-Ua-Mobile": "?0",
126
+ "Sec-Ch-Ua-Model": '""',
127
+ "Sec-Ch-Ua-Platform": chPlat,
128
+ "Sec-Ch-Ua-Platform-Version": chPlatV,
129
+ "Sec-Fetch-Dest": isXhr ? "empty" : "document",
130
+ "Sec-Fetch-Mode": isXhr ? "cors" : "navigate",
131
+ "Sec-Fetch-Site": isXhr ? "same-origin" : "none",
132
+ "Sec-Fetch-User": isXhr ? undefined : "?1",
133
+ "User-Agent": ua,
134
+ "Viewport-Width": vp,
135
+ "Width": vp,
136
+ "X-FB-Timezone-Offset": String(tz * 60),
137
+ };
138
+
139
+ if (chPlat) {
140
+ const platStr = chPlat.toLowerCase();
141
+ if (platStr.includes("windows")) {
142
+ h["Sec-Ch-Ua-Arch"] = '"x86"';
143
+ h["Sec-Ch-Ua-Bitness"] = '"64"';
144
+ h["Sec-Ch-Ua-Wow64"] = "?0";
145
+ } else if (platStr.includes("macos")) {
146
+ h["Sec-Ch-Ua-Arch"] = '"arm"';
147
+ h["Sec-Ch-Ua-Bitness"] = '"64"';
148
+ } else if (platStr.includes("linux")) {
149
+ h["Sec-Ch-Ua-Arch"] = '"x86"';
150
+ h["Sec-Ch-Ua-Bitness"] = '"64"';
151
+ }
152
+ }
153
+
154
+ if (isXhr) {
155
+ h["Origin"] = origin;
156
+ h["X-Requested-With"] = "XMLHttpRequest";
157
+ }
158
+
159
+ if (ctx) {
160
+ if (ctx.lsd) h["X-Fb-Lsd"] = ctx.lsd;
161
+ if (ctx.region) h["X-MSGR-Region"] = ctx.region;
162
+ if (ctx.__spin_r) h["X-Fb-Spin-R"] = String(ctx.__spin_r);
163
+ if (ctx.__spin_b) h["X-Fb-Spin-B"] = String(ctx.__spin_b);
164
+ if (ctx.__spin_t) h["X-Fb-Spin-T"] = String(ctx.__spin_t);
165
+ }
166
+
167
+ if (custom) {
168
+ Object.assign(h, custom);
169
+ if (custom.noRef) delete h.Referer;
170
+ }
171
+
172
+ return sanitize(h);
173
+ }
174
+
175
+ function getHeaders(url, opts, ctx, custom, reqType = "navigate") {
176
+ const mode = opts?.persona || "desktop";
177
+ if (mode === "android" || mode === "mobile") {
178
+ return buildAndroidHeaders(url, opts, ctx, custom);
179
+ }
180
+ return buildDesktopHeaders(url, opts, ctx, custom, reqType);
181
+ }
182
+
183
+ module.exports = {
184
+ getHeaders,
185
+ buildDesktopHeaders,
186
+ buildAndroidHeaders,
187
+ getRandomLocale,
188
+ getRandomTimezone,
189
+ getRandomDpr,
190
+ getRandomViewport,
191
+ sanitizeHeaderValue: sanitizeVal,
192
+ sanitizeHeaders: sanitize,
193
+ };