ayman-fca 1.0.1 → 1.0.3
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/index.js +291 -9
- package/package.json +1 -1
- package/src/api/pinMessage.js +45 -0
- package/src/core/behaviorEngine.js +176 -0
- package/src/core/geoGuard.js +87 -0
- package/src/core/silentRecovery.js +142 -0
- package/src/core/smartCooldown.js +111 -0
- package/src/core/smartMessageQueue.js +152 -0
- package/src/utils/reconnectEngine.js +114 -0
- package/src/utils/sessionKeeper.js +244 -118
package/index.js
CHANGED
|
@@ -1,15 +1,297 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// AYMAN-FCA v2.0
|
|
3
|
-
// Facebook Chat API for Node.js
|
|
2
|
+
// AYMAN-FCA v2.0 — ULTRA MASTER ENGINE
|
|
4
3
|
// © 2025 Ayman. All Rights Reserved.
|
|
5
|
-
// جميع الحقوق محفوظة لأيمن
|
|
6
4
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// يجمع كل الأنظمة:
|
|
6
|
+
// ① SessionManager — جلسة + backup + validate
|
|
7
|
+
// ② ReconnectEngine — Circuit Breaker + backoff
|
|
8
|
+
// ③ KeepAliveEngine — نشاط حقيقي كل 4 دقائق
|
|
9
|
+
// ④ Watchdog — مراقبة MQTT + صمت
|
|
10
|
+
// ⑤ HealthMonitor — Score 0-100
|
|
11
|
+
// ⑥ MemoryManager — تنظيف ذاكرة
|
|
12
|
+
// ⑦ BehaviorEngine — محاكاة إنسان
|
|
13
|
+
// ⑧ SmartCooldown — تحكم ذكي بالسرعة
|
|
14
|
+
// ⑨ SilentRecovery — Soft Restart + Silent Mode
|
|
15
|
+
// ⑩ SmartMessageQueue— Queue ذكي
|
|
16
|
+
// ⑪ GeoGuard — حماية الموقع الجغرافي
|
|
9
17
|
// ============================================================
|
|
18
|
+
"use strict";
|
|
10
19
|
|
|
11
|
-
const
|
|
20
|
+
const EventEmitter = require("events");
|
|
21
|
+
const path = require("path");
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
const logger = require("../func/logger");
|
|
24
|
+
const SessionManager = require("./core/sessionManager");
|
|
25
|
+
const ReconnectEngine = require("./core/reconnectEngine");
|
|
26
|
+
const KeepAliveEngine = require("./core/keepAliveEngine");
|
|
27
|
+
const Watchdog = require("./core/watchdog");
|
|
28
|
+
const HealthMonitor = require("./core/healthMonitor");
|
|
29
|
+
const MemoryManager = require("./core/memoryManager");
|
|
30
|
+
const BehaviorEngine = require("./core/behaviorEngine");
|
|
31
|
+
const SmartCooldown = require("./core/smartCooldown");
|
|
32
|
+
const SilentRecovery = require("./core/silentRecovery");
|
|
33
|
+
const SmartMessageQueue = require("./core/smartMessageQueue");
|
|
34
|
+
const GeoGuard = require("./core/geoGuard");
|
|
35
|
+
const { EVENTS } = require("./system/constants");
|
|
36
|
+
const { isSessionError }= require("./system/errors");
|
|
37
|
+
|
|
38
|
+
class AymanFCAUltra extends EventEmitter {
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
super();
|
|
41
|
+
this._appStatePath = options.appStatePath || path.join(process.cwd(), "appstate.json");
|
|
42
|
+
this._onMessage = options.onMessage || null;
|
|
43
|
+
this._api = null;
|
|
44
|
+
this._ctx = null;
|
|
45
|
+
this._restarting = false;
|
|
46
|
+
this._startedAt = Date.now();
|
|
47
|
+
|
|
48
|
+
// ── الأنظمة ──────────────────────────────────────────────
|
|
49
|
+
this.session = new SessionManager({ primaryPath: this._appStatePath, onSave: options.onSave || null });
|
|
50
|
+
this.reconnect = new ReconnectEngine();
|
|
51
|
+
this.keepAlive = new KeepAliveEngine();
|
|
52
|
+
this.watchdog = new Watchdog();
|
|
53
|
+
this.health = new HealthMonitor();
|
|
54
|
+
this.memory = new MemoryManager();
|
|
55
|
+
this.behavior = new BehaviorEngine({ sessionStart: this._startedAt });
|
|
56
|
+
this.cooldown = new SmartCooldown();
|
|
57
|
+
this.silent = new SilentRecovery();
|
|
58
|
+
this.queue = new SmartMessageQueue({ noise: true });
|
|
59
|
+
this.geo = new GeoGuard({ lockRegion: true });
|
|
60
|
+
|
|
61
|
+
this._wireSystems();
|
|
62
|
+
logger.banner();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── ربط الأنظمة ببعضها ──────────────────────────────────
|
|
66
|
+
_wireSystems() {
|
|
67
|
+
// Watchdog → restart
|
|
68
|
+
this.watchdog.on(EVENTS.WATCHDOG_RESTART, ({ reasons }) => {
|
|
69
|
+
this.health.penalize("mqtt_dead");
|
|
70
|
+
this._restart("watchdog: " + reasons);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Health Critical → restart
|
|
74
|
+
this.health.on(EVENTS.HEALTH_CRITICAL, ({ score }) => {
|
|
75
|
+
logger.error(`Health منخفض (${score}) — restart`, "ULTRA");
|
|
76
|
+
this._restart("health_critical");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Session Expired → restart
|
|
80
|
+
this.session.on(EVENTS.SESSION_EXPIRED, () => {
|
|
81
|
+
this.health.penalize("session_expired");
|
|
82
|
+
this._restart("session_expired");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Memory High → risk up + pause queue
|
|
86
|
+
this.memory.on(EVENTS.MEMORY_HIGH, () => {
|
|
87
|
+
this.health.penalize("memory_high");
|
|
88
|
+
this.queue.setRiskLevel("high");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Silent Mode → pause queue
|
|
92
|
+
this.silent.on("silent:enter", () => {
|
|
93
|
+
this.queue.pause("silent_mode");
|
|
94
|
+
this.cooldown.recordError();
|
|
95
|
+
});
|
|
96
|
+
this.silent.on("silent:exit", () => {
|
|
97
|
+
this.queue.resume();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Reconnect events
|
|
101
|
+
this.reconnect.on(EVENTS.RECONNECT_DONE, () => {
|
|
102
|
+
this.health.reward("reconnect_done");
|
|
103
|
+
this.cooldown.reset();
|
|
104
|
+
this.queue.setRiskLevel("normal");
|
|
105
|
+
});
|
|
106
|
+
this.reconnect.on(EVENTS.RECONNECT_FAIL, () => {
|
|
107
|
+
this.health.penalize("reconnect_fail");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Geo instability
|
|
111
|
+
this.geo.on("region:unstable", () => {
|
|
112
|
+
logger.warn("GeoGuard: عدم استقرار — Silent Mode", "ULTRA");
|
|
113
|
+
this.silent.enterSilentMode("geo_instability");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Cooldown → Smart Queue speed
|
|
117
|
+
// health low → risk up
|
|
118
|
+
this.health.on(EVENTS.HEALTH_LOW, () => {
|
|
119
|
+
this.queue.setRiskLevel("high");
|
|
120
|
+
this.cooldown.recordError();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── استخراج ctx ─────────────────────────────────────────
|
|
125
|
+
_extractCtx(api) {
|
|
126
|
+
for (const k of Object.getOwnPropertyNames(api)) {
|
|
127
|
+
try {
|
|
128
|
+
const v = api[k];
|
|
129
|
+
if (v && typeof v === "object" && v.jar && v.userID && v.userID !== "0") return v;
|
|
130
|
+
} catch(_) {}
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── تشغيل كل الأنظمة بعد Login ─────────────────────────
|
|
136
|
+
_startSystems(api, ctx) {
|
|
137
|
+
this.session.attach(api);
|
|
138
|
+
this.keepAlive.attach(api, ctx);
|
|
139
|
+
this.watchdog.attach(api, ctx);
|
|
140
|
+
this.memory.registerCleanup(() => {
|
|
141
|
+
if (ctx?.tasks instanceof Map && ctx.tasks.size > 100) ctx.tasks.clear();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// تسجيل region
|
|
145
|
+
if (ctx?.region) this.geo.recordRegion(ctx.region);
|
|
146
|
+
|
|
147
|
+
// تشغيل الأنظمة
|
|
148
|
+
this.session.start();
|
|
149
|
+
this.keepAlive.start();
|
|
150
|
+
this.watchdog.start();
|
|
151
|
+
this.health.start();
|
|
152
|
+
this.memory.start();
|
|
153
|
+
this.queue.start();
|
|
154
|
+
this.behavior.start(ctx?.mqttClient);
|
|
155
|
+
|
|
156
|
+
logger.info("ULTRA: كل الأنظمة تعمل ✅", "ULTRA");
|
|
157
|
+
this.emit("ready", { uid: ctx?.userID });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── إيقاف كل الأنظمة ────────────────────────────────────
|
|
161
|
+
_stopSystems() {
|
|
162
|
+
["session","keepAlive","watchdog","health","memory","queue","behavior","silent"].forEach(s => {
|
|
163
|
+
try { this[s].stop(); } catch(_) {}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── إعادة تشغيل ذكية (Soft Restart) ─────────────────────
|
|
168
|
+
async _restart(reason) {
|
|
169
|
+
if (this._restarting) return;
|
|
170
|
+
this._restarting = true;
|
|
171
|
+
logger.warn(`ULTRA: إعادة تشغيل — ${reason}`, "ULTRA");
|
|
172
|
+
|
|
173
|
+
// Soft restart بدل Hard restart
|
|
174
|
+
const ok = await this.silent.softRestart(async () => {
|
|
175
|
+
this._stopSystems();
|
|
176
|
+
try {
|
|
177
|
+
if (this._ctx?.mqttClient) {
|
|
178
|
+
this._ctx.mqttClient.removeAllListeners();
|
|
179
|
+
this._ctx.mqttClient.end(true);
|
|
180
|
+
}
|
|
181
|
+
} catch(_) {}
|
|
182
|
+
await this.reconnect.trigger(async () => {
|
|
183
|
+
await this.attachToApi(this._api);
|
|
184
|
+
}, new Error(reason));
|
|
185
|
+
}, reason);
|
|
186
|
+
|
|
187
|
+
if (!ok) logger.error("ULTRA: فشلت إعادة التشغيل", "ULTRA");
|
|
188
|
+
this._restarting = false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ── بناء Listener Callback ────────────────────────────────
|
|
192
|
+
buildListenerCallback() {
|
|
193
|
+
return (error, message) => {
|
|
194
|
+
if (error) {
|
|
195
|
+
if (error?.type === "stop_listen") return;
|
|
196
|
+
|
|
197
|
+
// Silent Mode إذا لزم
|
|
198
|
+
if (this.silent.shouldGoSilent(error)) {
|
|
199
|
+
this.silent.handleError(error, () => this._restart("silent_recovery"));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (error?.type === "account_inactive" || isSessionError(error)) {
|
|
204
|
+
this.health.penalize("session_expired");
|
|
205
|
+
this._restart("account_inactive");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.health.penalize("error");
|
|
210
|
+
this.cooldown.recordError();
|
|
211
|
+
this._restart("listen_error");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!message) return;
|
|
216
|
+
|
|
217
|
+
// تحديث Watchdog + Health
|
|
218
|
+
this.watchdog.heartbeat();
|
|
219
|
+
this.health.reward("message_ok");
|
|
220
|
+
this.cooldown.recordSuccess();
|
|
221
|
+
|
|
222
|
+
// تحديث Geo إذا تغير
|
|
223
|
+
if (message.region) this.geo.recordRegion(message.region);
|
|
224
|
+
|
|
225
|
+
if (["presence","typ","read_receipt"].includes(message.type)) return;
|
|
226
|
+
|
|
227
|
+
if (this._onMessage) {
|
|
228
|
+
try { this._onMessage(null, message); } catch(e) {}
|
|
229
|
+
}
|
|
230
|
+
this.emit("message", message);
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── ربط بـ API بعد Login ──────────────────────────────────
|
|
235
|
+
async attachToApi(api) {
|
|
236
|
+
this._api = api;
|
|
237
|
+
this._ctx = this._extractCtx(api);
|
|
238
|
+
|
|
239
|
+
if (this._ctx) {
|
|
240
|
+
api.ctx = this._ctx;
|
|
241
|
+
api.ctxMain = this._ctx;
|
|
242
|
+
logger.info(`ULTRA مرتبط | UID: ${this._ctx.userID} | Region: ${this._ctx.region || "?"}`, "ULTRA");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Connection Warm-Up قبل أي نشاط
|
|
246
|
+
await this.behavior.warmUp();
|
|
247
|
+
|
|
248
|
+
// حفظ الجلسة فوراً
|
|
249
|
+
try { this.session.save(api.getAppState(), true); } catch(_) {}
|
|
250
|
+
|
|
251
|
+
// تشغيل الأنظمة
|
|
252
|
+
this._startSystems(api, this._ctx || {});
|
|
253
|
+
|
|
254
|
+
return api;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ── wrap sendMessage بـ Queue + Cooldown ─────────────────
|
|
258
|
+
wrapSendMessage(api) {
|
|
259
|
+
const original = api.sendMessage.bind(api);
|
|
260
|
+
api.sendMessage = (msg, threadID, callback, messageID) => {
|
|
261
|
+
// Anti-Repeat
|
|
262
|
+
this.behavior.antiRepeatDelay("send", typeof msg === "string" ? msg.slice(0, 30) : "obj");
|
|
263
|
+
|
|
264
|
+
// Queue
|
|
265
|
+
this.queue.enqueue(async () => {
|
|
266
|
+
await this.cooldown.waitBeforeSend();
|
|
267
|
+
const start = Date.now();
|
|
268
|
+
return new Promise((res, rej) => {
|
|
269
|
+
original(msg, threadID, (err, info) => {
|
|
270
|
+
this.cooldown.recordLatency(Date.now() - start);
|
|
271
|
+
if (err) { this.cooldown.recordError(); if (callback) callback(err); rej(err); }
|
|
272
|
+
else { this.cooldown.recordSuccess(); if (callback) callback(null, info); res(info); }
|
|
273
|
+
}, messageID);
|
|
274
|
+
});
|
|
275
|
+
}, { threadID });
|
|
276
|
+
};
|
|
277
|
+
logger.info("ULTRA: sendMessage wrapped ✅", "ULTRA");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ── إيقاف آمن ────────────────────────────────────────────
|
|
281
|
+
async stop() {
|
|
282
|
+
this._stopSystems();
|
|
283
|
+
try { if (this._api?.stopListening) this._api.stopListening(); } catch(_) {}
|
|
284
|
+
logger.info("ULTRA: موقوف ✅", "ULTRA");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ── Getters ───────────────────────────────────────────────
|
|
288
|
+
get api() { return this._api; }
|
|
289
|
+
get ctx() { return this._ctx; }
|
|
290
|
+
getHealth() { return this.health.getStats(); }
|
|
291
|
+
getCooldown() { return this.cooldown.getStats(); }
|
|
292
|
+
getQueue() { return this.queue.getStats(); }
|
|
293
|
+
getGeo() { return this.geo.getStats(); }
|
|
294
|
+
getSilent() { return this.silent.getStats(); }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
module.exports = AymanFCAUltra;
|
package/package.json
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const utils = require("../utils");
|
|
3
|
+
module.exports = function(defaultFuncs, api, ctx){
|
|
4
|
+
return function pinMessage(messageID, threadID, callback){
|
|
5
|
+
let resolveFunc, rejectFunc;
|
|
6
|
+
const returnPromise = new Promise((res,rej)=>{ resolveFunc=res; rejectFunc=rej; });
|
|
7
|
+
if (!callback) callback = (err,data)=> err ? rejectFunc(err) : resolveFunc(data);
|
|
8
|
+
defaultFuncs
|
|
9
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, {
|
|
10
|
+
doc_id: "2380599842158525",
|
|
11
|
+
variables: JSON.stringify({ messageID, threadID, isPinnedMessage: true })
|
|
12
|
+
}, ctx.globalOptions, ctx)
|
|
13
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
14
|
+
.then(resData=>{ if(resData.error) throw resData; callback(null,{success:true}); })
|
|
15
|
+
.catch(err=>callback(err));
|
|
16
|
+
return returnPromise;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
forwardMessage.js:
|
|
20
|
+
"use strict";
|
|
21
|
+
const utils = require("../utils");
|
|
22
|
+
module.exports = function(defaultFuncs, api, ctx){
|
|
23
|
+
return function forwardMessage(messageID, threadID, callback){
|
|
24
|
+
let resolveFunc, rejectFunc;
|
|
25
|
+
const returnPromise = new Promise((res,rej)=>{ resolveFunc=res; rejectFunc=rej; });
|
|
26
|
+
if (!callback) callback = (err,data)=> err ? rejectFunc(err) : resolveFunc(data);
|
|
27
|
+
defaultFuncs
|
|
28
|
+
.post("https://www.facebook.com/api/graphql/", ctx.jar, {
|
|
29
|
+
doc_id: "3218892511542012",
|
|
30
|
+
variables: JSON.stringify({
|
|
31
|
+
message: { forwarded_message_id: messageID },
|
|
32
|
+
thread_id: threadID
|
|
33
|
+
})
|
|
34
|
+
}, ctx.globalOptions, ctx)
|
|
35
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
36
|
+
.then(resData=>{ if(resData.error) throw resData; callback(null,{success:true}); })
|
|
37
|
+
.catch(err=>callback(err));
|
|
38
|
+
return returnPromise;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
getRegion.js:
|
|
42
|
+
"use strict";
|
|
43
|
+
module.exports = function(defaultFuncs, api, ctx){
|
|
44
|
+
return function getRegion(){ return ctx?.region || null; };
|
|
45
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// AYMAN-FCA — BehaviorEngine
|
|
3
|
+
// الفكرة 1: Dynamic Human Behavior Simulation
|
|
4
|
+
// الفكرة 14: Idle Simulation
|
|
5
|
+
// الفكرة 15: Time-of-Day Awareness
|
|
6
|
+
// الفكرة 19: Behavior Drift
|
|
7
|
+
// الفكرة 23: Random Heartbeat Pattern
|
|
8
|
+
// ============================================================
|
|
9
|
+
"use strict";
|
|
10
|
+
|
|
11
|
+
const logger = require("../../func/logger");
|
|
12
|
+
|
|
13
|
+
// ── أوقات النشاط البشري ──────────────────────────────────────
|
|
14
|
+
const ACTIVITY_PROFILE = {
|
|
15
|
+
night: { hours: [0,1,2,3,4,5], multiplier: 0.2 }, // خامل جداً
|
|
16
|
+
morning: { hours: [6,7,8,9], multiplier: 0.7 }, // متوسط
|
|
17
|
+
day: { hours: [10,11,12,13,14,15,16,17], multiplier: 1.0 }, // طبيعي
|
|
18
|
+
evening: { hours: [18,19,20,21,22,23], multiplier: 0.8 } // أقل قليلاً
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// ── Behavior Drift: النشاط يتطور بمرور الوقت ─────────────────
|
|
22
|
+
// الأسبوع الأول بطيء جداً، يزيد تدريجياً
|
|
23
|
+
function getDriftMultiplier(sessionStartMs) {
|
|
24
|
+
const ageDays = (Date.now() - sessionStartMs) / (1000 * 60 * 60 * 24);
|
|
25
|
+
if (ageDays < 1) return 0.3; // يوم أول: بطيء جداً
|
|
26
|
+
if (ageDays < 3) return 0.5; // 3 أيام: بطيء
|
|
27
|
+
if (ageDays < 7) return 0.7; // أسبوع: متوسط
|
|
28
|
+
if (ageDays < 14) return 0.9; // أسبوعين: قريب طبيعي
|
|
29
|
+
return 1.0; // بعدها: طبيعي
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── الحصول على multiplier الوقت الحالي ──────────────────────
|
|
33
|
+
function getTimeMultiplier() {
|
|
34
|
+
const h = new Date().getHours();
|
|
35
|
+
for (const [, profile] of Object.entries(ACTIVITY_PROFILE)) {
|
|
36
|
+
if (profile.hours.includes(h)) return profile.multiplier;
|
|
37
|
+
}
|
|
38
|
+
return 1.0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class BehaviorEngine {
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this._sessionStart = options.sessionStart || Date.now();
|
|
44
|
+
this._baseDelay = options.baseDelay || 2000;
|
|
45
|
+
this._patternMap = new Map(); // كشف التكرار
|
|
46
|
+
this._lastActivity = Date.now();
|
|
47
|
+
this._idleTimer = null;
|
|
48
|
+
this._active = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ① delay بشري عشوائي مع مراعاة الوقت والعمر
|
|
52
|
+
humanDelay(min = 500, max = 3000) {
|
|
53
|
+
const timeMul = getTimeMultiplier();
|
|
54
|
+
const driftMul = getDriftMultiplier(this._sessionStart);
|
|
55
|
+
const combined = timeMul * driftMul;
|
|
56
|
+
|
|
57
|
+
const adjustedMin = Math.round(min / combined);
|
|
58
|
+
const adjustedMax = Math.round(max / combined);
|
|
59
|
+
const base = Math.floor(Math.random() * (adjustedMax - adjustedMin + 1)) + adjustedMin;
|
|
60
|
+
|
|
61
|
+
// jitter ±15%
|
|
62
|
+
const jitter = base * 0.15 * (Math.random() * 2 - 1);
|
|
63
|
+
const delay = Math.max(200, Math.round(base + jitter));
|
|
64
|
+
|
|
65
|
+
return new Promise(r => setTimeout(r, delay));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ② كشف الأنماط المتكررة (الفكرة 12: Anti-Pattern Detection)
|
|
69
|
+
trackPattern(key, value) {
|
|
70
|
+
const k = `${key}:${value}`;
|
|
71
|
+
const count = (this._patternMap.get(k) || 0) + 1;
|
|
72
|
+
this._patternMap.set(k, count);
|
|
73
|
+
|
|
74
|
+
// تنظيف القديم كل 100 entry
|
|
75
|
+
if (this._patternMap.size > 100) {
|
|
76
|
+
const first = this._patternMap.keys().next().value;
|
|
77
|
+
this._patternMap.delete(first);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return count;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// إذا نفس الرسالة 3+ مرات → delay إضافي
|
|
84
|
+
async antiRepeatDelay(key, value) {
|
|
85
|
+
const count = this.trackPattern(key, value);
|
|
86
|
+
if (count >= 3) {
|
|
87
|
+
const extra = Math.min(count * 1000, 10000);
|
|
88
|
+
logger.warn(`Anti-Pattern: تكرار (${count}x) — delay ${extra}ms`, "BEHAVIOR");
|
|
89
|
+
await new Promise(r => setTimeout(r, extra));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ③ Connection Warm-Up (الفكرة 13)
|
|
94
|
+
async warmUp() {
|
|
95
|
+
logger.info("Connection Warm-Up بدأ...", "BEHAVIOR");
|
|
96
|
+
await this.humanDelay(2000, 5000); // انتظر
|
|
97
|
+
logger.info("Warm-Up: read phase...", "BEHAVIOR");
|
|
98
|
+
await this.humanDelay(1000, 3000); // قراءة
|
|
99
|
+
logger.info("Warm-Up: idle phase...", "BEHAVIOR");
|
|
100
|
+
await this.humanDelay(3000, 8000); // خمول
|
|
101
|
+
logger.info("Warm-Up مكتمل ✅", "BEHAVIOR");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ④ Idle Simulation - خمول عشوائي
|
|
105
|
+
scheduleIdleBreak(mqttClient) {
|
|
106
|
+
if (!this._active) return;
|
|
107
|
+
// كل 15-45 دقيقة خمول عشوائي من 2-10 دقائق
|
|
108
|
+
const nextIdleIn = (15 + Math.random() * 30) * 60 * 1000;
|
|
109
|
+
|
|
110
|
+
this._idleTimer = setTimeout(async () => {
|
|
111
|
+
if (!this._active) return;
|
|
112
|
+
const idleMs = (2 + Math.random() * 8) * 60 * 1000;
|
|
113
|
+
logger.info(`Idle Simulation: خمول ${Math.round(idleMs/60000)} دقيقة`, "BEHAVIOR");
|
|
114
|
+
|
|
115
|
+
// foreground = false أثناء الخمول
|
|
116
|
+
try {
|
|
117
|
+
if (mqttClient?.connected) {
|
|
118
|
+
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: false }), { qos: 0 });
|
|
119
|
+
}
|
|
120
|
+
} catch(_) {}
|
|
121
|
+
|
|
122
|
+
await new Promise(r => setTimeout(r, idleMs));
|
|
123
|
+
|
|
124
|
+
// عودة للنشاط
|
|
125
|
+
try {
|
|
126
|
+
if (mqttClient?.connected) {
|
|
127
|
+
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: true }), { qos: 0 });
|
|
128
|
+
}
|
|
129
|
+
} catch(_) {}
|
|
130
|
+
|
|
131
|
+
logger.info("Idle Simulation: عادة للنشاط ✅", "BEHAVIOR");
|
|
132
|
+
this.scheduleIdleBreak(mqttClient); // جدول التالي
|
|
133
|
+
}, nextIdleIn);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ⑤ Random Heartbeat - نبضة غير ثابتة
|
|
137
|
+
getHeartbeatDelay() {
|
|
138
|
+
// بين 20-40 ثانية مع variation حسب الوقت
|
|
139
|
+
const base = 25000;
|
|
140
|
+
const range = 15000;
|
|
141
|
+
const timeMul = getTimeMultiplier();
|
|
142
|
+
return Math.round((base + Math.random() * range) / timeMul);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ⑥ Session Age Awareness (الفكرة 2)
|
|
146
|
+
getSessionAge() {
|
|
147
|
+
return Date.now() - this._sessionStart;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
isSessionAging() {
|
|
151
|
+
return this.getSessionAge() > 20 * 60 * 60 * 1000; // أكثر من 20 ساعة
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// تقليل النشاط عند شيخوخة الجلسة
|
|
155
|
+
getAgedActivityMultiplier() {
|
|
156
|
+
const agingMs = this.getSessionAge();
|
|
157
|
+
const agingH = agingMs / (60 * 60 * 1000);
|
|
158
|
+
if (agingH < 12) return 1.0;
|
|
159
|
+
if (agingH < 20) return 0.8;
|
|
160
|
+
if (agingH < 30) return 0.6;
|
|
161
|
+
return 0.4; // بعد 30 ساعة: نشاط منخفض جداً
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
start(mqttClient) {
|
|
165
|
+
this._active = true;
|
|
166
|
+
this.scheduleIdleBreak(mqttClient);
|
|
167
|
+
logger.info("BehaviorEngine: مفعّل ✅", "BEHAVIOR");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
stop() {
|
|
171
|
+
this._active = false;
|
|
172
|
+
if (this._idleTimer) { clearTimeout(this._idleTimer); this._idleTimer = null; }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = BehaviorEngine;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// AYMAN-FCA — GeoStabilityGuard
|
|
3
|
+
// الفكرة 6: Multi-Region Awareness
|
|
4
|
+
// الفكرة 24: Geo Stability Guard
|
|
5
|
+
// ============================================================
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require("events");
|
|
9
|
+
const logger = require("../../func/logger");
|
|
10
|
+
|
|
11
|
+
class GeoStabilityGuard extends EventEmitter {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
super();
|
|
14
|
+
this._lastRegion = null;
|
|
15
|
+
this._regionChanges= 0;
|
|
16
|
+
this._maxChanges = options.maxChanges || 3;
|
|
17
|
+
this._history = [];
|
|
18
|
+
this._lockRegion = options.lockRegion || false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ── تسجيل Region ─────────────────────────────────────────
|
|
22
|
+
recordRegion(region) {
|
|
23
|
+
if (!region) return;
|
|
24
|
+
|
|
25
|
+
this._history.push({ region, ts: Date.now() });
|
|
26
|
+
if (this._history.length > 20) this._history.shift();
|
|
27
|
+
|
|
28
|
+
if (this._lastRegion && this._lastRegion !== region) {
|
|
29
|
+
this._regionChanges++;
|
|
30
|
+
logger.warn(`GeoGuard: Region تغير ${this._lastRegion} → ${region} (#${this._regionChanges})`, "GEO");
|
|
31
|
+
this.emit("region:changed", { from: this._lastRegion, to: region });
|
|
32
|
+
|
|
33
|
+
if (this._regionChanges >= this._maxChanges) {
|
|
34
|
+
logger.error("GeoGuard: تغييرات كثيرة — خطر logout", "GEO");
|
|
35
|
+
this.emit("region:unstable", { changes: this._regionChanges });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this._lastRegion = region;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── هل الموقع مستقر؟ ──────────────────────────────────────
|
|
43
|
+
isStable() {
|
|
44
|
+
return this._regionChanges < this._maxChanges;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── الحصول على Region الموصى به ───────────────────────────
|
|
48
|
+
getPreferredRegion() {
|
|
49
|
+
if (!this._history.length) return null;
|
|
50
|
+
// الـ region الأكثر تكراراً
|
|
51
|
+
const counts = {};
|
|
52
|
+
for (const { region } of this._history) {
|
|
53
|
+
counts[region] = (counts[region] || 0) + 1;
|
|
54
|
+
}
|
|
55
|
+
return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── تطبيق على ctx ──────────────────────────────────────────
|
|
59
|
+
applyToCtx(ctx) {
|
|
60
|
+
if (!ctx) return;
|
|
61
|
+
|
|
62
|
+
// تسجيل region الحالي
|
|
63
|
+
if (ctx.region) this.recordRegion(ctx.region);
|
|
64
|
+
|
|
65
|
+
// إذا lock region، أعد تطبيق الـ region المفضل
|
|
66
|
+
if (this._lockRegion && this._lastRegion && !ctx.region) {
|
|
67
|
+
ctx.region = this._lastRegion;
|
|
68
|
+
logger.info(`GeoGuard: تم تثبيت region = ${ctx.region}`, "GEO");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
reset() {
|
|
73
|
+
this._regionChanges = 0;
|
|
74
|
+
this._history = [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getStats() {
|
|
78
|
+
return {
|
|
79
|
+
currentRegion: this._lastRegion,
|
|
80
|
+
regionChanges: this._regionChanges,
|
|
81
|
+
stable: this.isStable(),
|
|
82
|
+
preferred: this.getPreferredRegion()
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = GeoStabilityGuard;
|