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.
- package/LICENSE +58 -0
- package/README.md +534 -0
- package/index.js +35 -0
- package/package.json +101 -0
- package/phantom/core/builder/bootstrap.js +334 -0
- package/phantom/core/builder/config.js +78 -0
- package/phantom/core/builder/forge.js +113 -0
- package/phantom/core/builder/ignite.js +386 -0
- package/phantom/core/builder/options.js +61 -0
- package/phantom/core/engine.js +71 -0
- package/phantom/core/reactor.js +2 -0
- package/phantom/datastore/appState.js +2 -0
- package/phantom/datastore/appStateBackup.js +34 -0
- package/phantom/datastore/models/cipher/e2ee.js +48 -0
- package/phantom/datastore/models/cipher/vault.js +153 -0
- package/phantom/datastore/models/index.js +3 -0
- package/phantom/datastore/models/matrix/auth.js +151 -0
- package/phantom/datastore/models/matrix/cache.js +3 -0
- package/phantom/datastore/models/matrix/checker.js +2 -0
- package/phantom/datastore/models/matrix/clients.js +2 -0
- package/phantom/datastore/models/matrix/constants.js +2 -0
- package/phantom/datastore/models/matrix/credentials.js +2 -0
- package/phantom/datastore/models/matrix/cycle.js +2 -0
- package/phantom/datastore/models/matrix/gate.js +282 -0
- package/phantom/datastore/models/matrix/ghost.js +332 -0
- package/phantom/datastore/models/matrix/headers.js +193 -0
- package/phantom/datastore/models/matrix/heartbeat.js +298 -0
- package/phantom/datastore/models/matrix/identity.js +235 -0
- package/phantom/datastore/models/matrix/logger.js +271 -0
- package/phantom/datastore/models/matrix/monitor.js +2 -0
- package/phantom/datastore/models/matrix/net.js +316 -0
- package/phantom/datastore/models/matrix/response.js +193 -0
- package/phantom/datastore/models/matrix/revive.js +255 -0
- package/phantom/datastore/models/matrix/signals.js +2 -0
- package/phantom/datastore/models/matrix/store.js +263 -0
- package/phantom/datastore/models/matrix/telemetry.js +272 -0
- package/phantom/datastore/models/matrix/tools.js +93 -0
- package/phantom/datastore/models/matrix/transform/cookieParser.js +2 -0
- package/phantom/datastore/models/matrix/transform/cookies.js +114 -0
- package/phantom/datastore/models/matrix/transform/index.js +203 -0
- package/phantom/datastore/models/matrix/validator.js +157 -0
- package/phantom/datastore/models/types/index.d.ts +498 -0
- package/phantom/datastore/schema.js +167 -0
- package/phantom/datastore/session.js +129 -0
- package/phantom/datastore/threads.js +22 -0
- package/phantom/datastore/users.js +26 -0
- package/phantom/dispatch/addExternalModule.js +239 -0
- package/phantom/dispatch/addUserToGroup.js +161 -0
- package/phantom/dispatch/changeAdminStatus.js +142 -0
- package/phantom/dispatch/changeArchivedStatus.js +135 -0
- package/phantom/dispatch/changeAvatar.js +123 -0
- package/phantom/dispatch/changeBio.js +86 -0
- package/phantom/dispatch/changeBlockedStatus.js +86 -0
- package/phantom/dispatch/changeGroupImage.js +145 -0
- package/phantom/dispatch/changeThreadColor.js +172 -0
- package/phantom/dispatch/changeThreadEmoji.js +130 -0
- package/phantom/dispatch/comment.js +136 -0
- package/phantom/dispatch/createAITheme.js +333 -0
- package/phantom/dispatch/createNewGroup.js +99 -0
- package/phantom/dispatch/createPoll.js +148 -0
- package/phantom/dispatch/deleteMessage.js +131 -0
- package/phantom/dispatch/deleteThread.js +155 -0
- package/phantom/dispatch/e2ee.js +101 -0
- package/phantom/dispatch/editMessage.js +158 -0
- package/phantom/dispatch/emoji.js +143 -0
- package/phantom/dispatch/fetchThemeData.js +233 -0
- package/phantom/dispatch/follow.js +111 -0
- package/phantom/dispatch/forwardMessage.js +110 -0
- package/phantom/dispatch/friend.js +189 -0
- package/phantom/dispatch/gcmember.js +138 -0
- package/phantom/dispatch/gcname.js +131 -0
- package/phantom/dispatch/gcrule.js +111 -0
- package/phantom/dispatch/getAccess.js +109 -0
- package/phantom/dispatch/getBotInfo.js +81 -0
- package/phantom/dispatch/getBotInitialData.js +110 -0
- package/phantom/dispatch/getFriendsList.js +118 -0
- package/phantom/dispatch/getMessage.js +199 -0
- package/phantom/dispatch/getTheme.js +199 -0
- package/phantom/dispatch/getThemeInfo.js +160 -0
- package/phantom/dispatch/getThreadHistory.js +139 -0
- package/phantom/dispatch/getThreadInfo.js +153 -0
- package/phantom/dispatch/getThreadList.js +132 -0
- package/phantom/dispatch/getThreadPictures.js +93 -0
- package/phantom/dispatch/getUserID.js +147 -0
- package/phantom/dispatch/getUserInfo.js +513 -0
- package/phantom/dispatch/getUserInfoV2.js +146 -0
- package/phantom/dispatch/handleMessageRequest.js +50 -0
- package/phantom/dispatch/httpGet.js +63 -0
- package/phantom/dispatch/httpPost.js +89 -0
- package/phantom/dispatch/httpPostFormData.js +69 -0
- package/phantom/dispatch/listenMqtt.js +1236 -0
- package/phantom/dispatch/listenSpeed.js +179 -0
- package/phantom/dispatch/logout.js +93 -0
- package/phantom/dispatch/markAsDelivered.js +92 -0
- package/phantom/dispatch/markAsRead.js +119 -0
- package/phantom/dispatch/markAsReadAll.js +215 -0
- package/phantom/dispatch/markAsSeen.js +70 -0
- package/phantom/dispatch/mqttDeltaValue.js +278 -0
- package/phantom/dispatch/muteThread.js +253 -0
- package/phantom/dispatch/nickname.js +132 -0
- package/phantom/dispatch/notes.js +263 -0
- package/phantom/dispatch/pinMessage.js +238 -0
- package/phantom/dispatch/produceMetaTheme.js +335 -0
- package/phantom/dispatch/realtime.js +291 -0
- package/phantom/dispatch/removeUserFromGroup.js +248 -0
- package/phantom/dispatch/resolvePhotoUrl.js +217 -0
- package/phantom/dispatch/searchForThread.js +258 -0
- package/phantom/dispatch/sendMessage.js +354 -0
- package/phantom/dispatch/sendMessageMqtt.js +249 -0
- package/phantom/dispatch/sendTypingIndicator.js +206 -0
- package/phantom/dispatch/setMessageReaction.js +188 -0
- package/phantom/dispatch/setMessageReactionMqtt.js +248 -0
- package/phantom/dispatch/setThreadTheme.js +330 -0
- package/phantom/dispatch/setThreadThemeMqtt.js +207 -0
- package/phantom/dispatch/share.js +200 -0
- package/phantom/dispatch/shareContact.js +216 -0
- package/phantom/dispatch/stickers.js +395 -0
- package/phantom/dispatch/story.js +240 -0
- package/phantom/dispatch/theme.js +296 -0
- package/phantom/dispatch/unfriend.js +199 -0
- 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
|
+
};
|