ayman-fca 1.0.3 → 1.0.4
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ============================================================
|
|
2
2
|
// AYMAN-FCA v2.0 — ULTRA MASTER ENGINE
|
|
3
|
-
// ©
|
|
3
|
+
// © 2026 Ayman. All Rights Reserved.
|
|
4
4
|
//
|
|
5
5
|
// يجمع كل الأنظمة:
|
|
6
6
|
// ① SessionManager — جلسة + backup + validate
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
const EventEmitter = require("events");
|
|
21
21
|
const path = require("path");
|
|
22
22
|
|
|
23
|
+
// المسار المصحح للوصول إلى المجلد الوظيفي في الجذر
|
|
23
24
|
const logger = require("../func/logger");
|
|
25
|
+
|
|
24
26
|
const SessionManager = require("./core/sessionManager");
|
|
25
27
|
const ReconnectEngine = require("./core/reconnectEngine");
|
|
26
28
|
const KeepAliveEngine = require("./core/keepAliveEngine");
|
|
@@ -59,36 +61,32 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
59
61
|
this.geo = new GeoGuard({ lockRegion: true });
|
|
60
62
|
|
|
61
63
|
this._wireSystems();
|
|
62
|
-
logger
|
|
64
|
+
// تأكد أن logger يحتوي على دالة banner لتجنب الأخطاء
|
|
65
|
+
if (logger && typeof logger.banner === "function") logger.banner();
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
// ── ربط الأنظمة ببعضها ──────────────────────────────────
|
|
66
69
|
_wireSystems() {
|
|
67
|
-
// Watchdog → restart
|
|
68
70
|
this.watchdog.on(EVENTS.WATCHDOG_RESTART, ({ reasons }) => {
|
|
69
71
|
this.health.penalize("mqtt_dead");
|
|
70
72
|
this._restart("watchdog: " + reasons);
|
|
71
73
|
});
|
|
72
74
|
|
|
73
|
-
// Health Critical → restart
|
|
74
75
|
this.health.on(EVENTS.HEALTH_CRITICAL, ({ score }) => {
|
|
75
76
|
logger.error(`Health منخفض (${score}) — restart`, "ULTRA");
|
|
76
77
|
this._restart("health_critical");
|
|
77
78
|
});
|
|
78
79
|
|
|
79
|
-
// Session Expired → restart
|
|
80
80
|
this.session.on(EVENTS.SESSION_EXPIRED, () => {
|
|
81
81
|
this.health.penalize("session_expired");
|
|
82
82
|
this._restart("session_expired");
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
// Memory High → risk up + pause queue
|
|
86
85
|
this.memory.on(EVENTS.MEMORY_HIGH, () => {
|
|
87
86
|
this.health.penalize("memory_high");
|
|
88
87
|
this.queue.setRiskLevel("high");
|
|
89
88
|
});
|
|
90
89
|
|
|
91
|
-
// Silent Mode → pause queue
|
|
92
90
|
this.silent.on("silent:enter", () => {
|
|
93
91
|
this.queue.pause("silent_mode");
|
|
94
92
|
this.cooldown.recordError();
|
|
@@ -97,7 +95,6 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
97
95
|
this.queue.resume();
|
|
98
96
|
});
|
|
99
97
|
|
|
100
|
-
// Reconnect events
|
|
101
98
|
this.reconnect.on(EVENTS.RECONNECT_DONE, () => {
|
|
102
99
|
this.health.reward("reconnect_done");
|
|
103
100
|
this.cooldown.reset();
|
|
@@ -107,21 +104,17 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
107
104
|
this.health.penalize("reconnect_fail");
|
|
108
105
|
});
|
|
109
106
|
|
|
110
|
-
// Geo instability
|
|
111
107
|
this.geo.on("region:unstable", () => {
|
|
112
108
|
logger.warn("GeoGuard: عدم استقرار — Silent Mode", "ULTRA");
|
|
113
109
|
this.silent.enterSilentMode("geo_instability");
|
|
114
110
|
});
|
|
115
111
|
|
|
116
|
-
// Cooldown → Smart Queue speed
|
|
117
|
-
// health low → risk up
|
|
118
112
|
this.health.on(EVENTS.HEALTH_LOW, () => {
|
|
119
113
|
this.queue.setRiskLevel("high");
|
|
120
114
|
this.cooldown.recordError();
|
|
121
115
|
});
|
|
122
116
|
}
|
|
123
117
|
|
|
124
|
-
// ── استخراج ctx ─────────────────────────────────────────
|
|
125
118
|
_extractCtx(api) {
|
|
126
119
|
for (const k of Object.getOwnPropertyNames(api)) {
|
|
127
120
|
try {
|
|
@@ -132,7 +125,6 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
132
125
|
return null;
|
|
133
126
|
}
|
|
134
127
|
|
|
135
|
-
// ── تشغيل كل الأنظمة بعد Login ─────────────────────────
|
|
136
128
|
_startSystems(api, ctx) {
|
|
137
129
|
this.session.attach(api);
|
|
138
130
|
this.keepAlive.attach(api, ctx);
|
|
@@ -141,10 +133,8 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
141
133
|
if (ctx?.tasks instanceof Map && ctx.tasks.size > 100) ctx.tasks.clear();
|
|
142
134
|
});
|
|
143
135
|
|
|
144
|
-
// تسجيل region
|
|
145
136
|
if (ctx?.region) this.geo.recordRegion(ctx.region);
|
|
146
137
|
|
|
147
|
-
// تشغيل الأنظمة
|
|
148
138
|
this.session.start();
|
|
149
139
|
this.keepAlive.start();
|
|
150
140
|
this.watchdog.start();
|
|
@@ -157,20 +147,17 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
157
147
|
this.emit("ready", { uid: ctx?.userID });
|
|
158
148
|
}
|
|
159
149
|
|
|
160
|
-
// ── إيقاف كل الأنظمة ────────────────────────────────────
|
|
161
150
|
_stopSystems() {
|
|
162
151
|
["session","keepAlive","watchdog","health","memory","queue","behavior","silent"].forEach(s => {
|
|
163
152
|
try { this[s].stop(); } catch(_) {}
|
|
164
153
|
});
|
|
165
154
|
}
|
|
166
155
|
|
|
167
|
-
// ── إعادة تشغيل ذكية (Soft Restart) ─────────────────────
|
|
168
156
|
async _restart(reason) {
|
|
169
157
|
if (this._restarting) return;
|
|
170
158
|
this._restarting = true;
|
|
171
159
|
logger.warn(`ULTRA: إعادة تشغيل — ${reason}`, "ULTRA");
|
|
172
160
|
|
|
173
|
-
// Soft restart بدل Hard restart
|
|
174
161
|
const ok = await this.silent.softRestart(async () => {
|
|
175
162
|
this._stopSystems();
|
|
176
163
|
try {
|
|
@@ -188,13 +175,11 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
188
175
|
this._restarting = false;
|
|
189
176
|
}
|
|
190
177
|
|
|
191
|
-
// ── بناء Listener Callback ────────────────────────────────
|
|
192
178
|
buildListenerCallback() {
|
|
193
179
|
return (error, message) => {
|
|
194
180
|
if (error) {
|
|
195
181
|
if (error?.type === "stop_listen") return;
|
|
196
182
|
|
|
197
|
-
// Silent Mode إذا لزم
|
|
198
183
|
if (this.silent.shouldGoSilent(error)) {
|
|
199
184
|
this.silent.handleError(error, () => this._restart("silent_recovery"));
|
|
200
185
|
return;
|
|
@@ -214,12 +199,10 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
214
199
|
|
|
215
200
|
if (!message) return;
|
|
216
201
|
|
|
217
|
-
// تحديث Watchdog + Health
|
|
218
202
|
this.watchdog.heartbeat();
|
|
219
203
|
this.health.reward("message_ok");
|
|
220
204
|
this.cooldown.recordSuccess();
|
|
221
205
|
|
|
222
|
-
// تحديث Geo إذا تغير
|
|
223
206
|
if (message.region) this.geo.recordRegion(message.region);
|
|
224
207
|
|
|
225
208
|
if (["presence","typ","read_receipt"].includes(message.type)) return;
|
|
@@ -231,7 +214,6 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
231
214
|
};
|
|
232
215
|
}
|
|
233
216
|
|
|
234
|
-
// ── ربط بـ API بعد Login ──────────────────────────────────
|
|
235
217
|
async attachToApi(api) {
|
|
236
218
|
this._api = api;
|
|
237
219
|
this._ctx = this._extractCtx(api);
|
|
@@ -242,26 +224,20 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
242
224
|
logger.info(`ULTRA مرتبط | UID: ${this._ctx.userID} | Region: ${this._ctx.region || "?"}`, "ULTRA");
|
|
243
225
|
}
|
|
244
226
|
|
|
245
|
-
// Connection Warm-Up قبل أي نشاط
|
|
246
227
|
await this.behavior.warmUp();
|
|
247
228
|
|
|
248
|
-
// حفظ الجلسة فوراً
|
|
249
229
|
try { this.session.save(api.getAppState(), true); } catch(_) {}
|
|
250
230
|
|
|
251
|
-
// تشغيل الأنظمة
|
|
252
231
|
this._startSystems(api, this._ctx || {});
|
|
253
232
|
|
|
254
233
|
return api;
|
|
255
234
|
}
|
|
256
235
|
|
|
257
|
-
// ── wrap sendMessage بـ Queue + Cooldown ─────────────────
|
|
258
236
|
wrapSendMessage(api) {
|
|
259
237
|
const original = api.sendMessage.bind(api);
|
|
260
238
|
api.sendMessage = (msg, threadID, callback, messageID) => {
|
|
261
|
-
// Anti-Repeat
|
|
262
239
|
this.behavior.antiRepeatDelay("send", typeof msg === "string" ? msg.slice(0, 30) : "obj");
|
|
263
240
|
|
|
264
|
-
// Queue
|
|
265
241
|
this.queue.enqueue(async () => {
|
|
266
242
|
await this.cooldown.waitBeforeSend();
|
|
267
243
|
const start = Date.now();
|
|
@@ -277,14 +253,12 @@ class AymanFCAUltra extends EventEmitter {
|
|
|
277
253
|
logger.info("ULTRA: sendMessage wrapped ✅", "ULTRA");
|
|
278
254
|
}
|
|
279
255
|
|
|
280
|
-
// ── إيقاف آمن ────────────────────────────────────────────
|
|
281
256
|
async stop() {
|
|
282
257
|
this._stopSystems();
|
|
283
258
|
try { if (this._api?.stopListening) this._api.stopListening(); } catch(_) {}
|
|
284
259
|
logger.info("ULTRA: موقوف ✅", "ULTRA");
|
|
285
260
|
}
|
|
286
261
|
|
|
287
|
-
// ── Getters ───────────────────────────────────────────────
|
|
288
262
|
get api() { return this._api; }
|
|
289
263
|
get ctx() { return this._ctx; }
|
|
290
264
|
getHealth() { return this.health.getStats(); }
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// AYMAN-FCA v2.0 — Auto Save AppState
|
|
2
|
+
// AYMAN-FCA v2.0 — Auto Save AppState [FIXED]
|
|
3
3
|
// © 2025 Ayman. All Rights Reserved.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// الإصلاحات:
|
|
6
|
+
// ✅ hashState أقوى — يكشف أي تغيير في القيم
|
|
7
|
+
// ✅ حفظ كل 5 دقائق (كان 8)
|
|
8
|
+
// ✅ backup نسخة ثانية في session_backups/
|
|
9
|
+
// ✅ cleanup للـ backup القديمة (يحتفظ بآخر 3 فقط)
|
|
8
10
|
// ============================================================
|
|
9
11
|
"use strict";
|
|
10
12
|
|
|
@@ -12,32 +14,65 @@ const fs = require("fs");
|
|
|
12
14
|
const path = require("path");
|
|
13
15
|
const logger = require("../../../func/logger");
|
|
14
16
|
|
|
17
|
+
const BACKUP_DIR = path.join(process.cwd(), "session_backups");
|
|
18
|
+
const MAX_BACKUPS = 3;
|
|
19
|
+
|
|
20
|
+
function ensureBackupDir() {
|
|
21
|
+
try {
|
|
22
|
+
if (!fs.existsSync(BACKUP_DIR)) fs.mkdirSync(BACKUP_DIR, { recursive: true });
|
|
23
|
+
} catch (_) {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ✅ hash أقوى — يأخذ عينة من القيم الفعلية
|
|
27
|
+
function hashState(state) {
|
|
28
|
+
if (!state || !state.length) return "empty";
|
|
29
|
+
const sample = state.slice(0, 5).map(c => `${c.key}=${String(c.value || "").slice(0, 8)}`).join("|");
|
|
30
|
+
return `${state.length}:${sample}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ✅ حفظ backup مع تنظيف القديمة
|
|
34
|
+
function saveBackup(state) {
|
|
35
|
+
try {
|
|
36
|
+
ensureBackupDir();
|
|
37
|
+
const ts = Date.now();
|
|
38
|
+
const file = path.join(BACKUP_DIR, `backup_${ts}.json`);
|
|
39
|
+
fs.writeFileSync(file, JSON.stringify(state, null, "\t"), "utf8");
|
|
40
|
+
|
|
41
|
+
// احذف القديمة — احتفظ بآخر MAX_BACKUPS
|
|
42
|
+
const files = fs.readdirSync(BACKUP_DIR)
|
|
43
|
+
.filter(f => f.startsWith("backup_") && f.endsWith(".json"))
|
|
44
|
+
.sort().reverse();
|
|
45
|
+
for (const old of files.slice(MAX_BACKUPS)) {
|
|
46
|
+
try { fs.unlinkSync(path.join(BACKUP_DIR, old)); } catch (_) {}
|
|
47
|
+
}
|
|
48
|
+
} catch (_) {}
|
|
49
|
+
}
|
|
50
|
+
|
|
15
51
|
module.exports = function(defaultFuncs, api, ctx) {
|
|
16
52
|
return function enableAutoSaveAppState(options = {}) {
|
|
17
53
|
const filePath = options.filePath || path.join(process.cwd(), "appstate.json");
|
|
18
|
-
const intervalMs = options.interval ||
|
|
54
|
+
const intervalMs = options.interval || 5 * 60 * 1000; // ✅ كان 8 دقائق → 5 دقائق
|
|
19
55
|
const saveOnLogin= options.saveOnLogin !== false;
|
|
20
56
|
|
|
21
57
|
let lastHash = null;
|
|
22
58
|
|
|
23
|
-
function hashState(state) {
|
|
24
|
-
return state.length + "_" + (state[0]?.value?.length || 0);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
59
|
function saveState(force = false) {
|
|
28
60
|
try {
|
|
29
61
|
const state = api.getAppState();
|
|
30
62
|
if (!state || state.length === 0) { logger("[ AYMAN ] AppState فارغ — تخطي", "warn"); return; }
|
|
31
63
|
|
|
32
|
-
const h = hashState(state);
|
|
64
|
+
const h = hashState(state); // ✅ hash أقوى
|
|
33
65
|
if (!force && h === lastHash) return; // لا تغيير
|
|
34
66
|
lastHash = h;
|
|
35
67
|
|
|
36
|
-
// Atomic write
|
|
68
|
+
// Atomic write للملف الرئيسي
|
|
37
69
|
const tmp = filePath + ".tmp";
|
|
38
70
|
fs.writeFileSync(tmp, JSON.stringify(state, null, "\t"), "utf8");
|
|
39
71
|
fs.renameSync(tmp, filePath);
|
|
40
72
|
logger(`[ AYMAN ] AppState محفوظ ✅ (${filePath})`, "info");
|
|
73
|
+
|
|
74
|
+
// ✅ حفظ backup نسخة احتياطية
|
|
75
|
+
saveBackup(state);
|
|
41
76
|
} catch (err) {
|
|
42
77
|
logger(`[ AYMAN ] خطأ حفظ AppState: ${err?.message || err}`, "error");
|
|
43
78
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// AYMAN-FCA v2.0 — Refresh fb_dtsg
|
|
2
|
+
// AYMAN-FCA v2.0 — Refresh fb_dtsg [FIXED]
|
|
3
3
|
// © 2025 Ayman. All Rights Reserved.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
5
|
+
// الإصلاحات:
|
|
6
|
+
// ✅ Interval من 6 ساعات → 90 دقيقة (fb_dtsg يحتاج تجديد أكثر)
|
|
7
|
+
// ✅ تجديد lsd أيضاً (كان مفقوداً)
|
|
8
|
+
// ✅ حفظ AppState بعد كل تجديد ناجح
|
|
9
|
+
// ✅ فحص أن المكتبة لا تزال متصلة قبل التجديد
|
|
6
10
|
// ============================================================
|
|
7
11
|
"use strict";
|
|
8
12
|
|
|
@@ -10,17 +14,43 @@ const { getFrom } = require("../../utils/constants");
|
|
|
10
14
|
const { get } = require("../../utils/request");
|
|
11
15
|
const { getType } = require("../../utils/format");
|
|
12
16
|
const logger = require("../../../func/logger");
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
|
|
20
|
+
// ✅ كان 6 ساعات — مخفض إلى 90 دقيقة للاستقرار
|
|
21
|
+
const REFRESH_INTERVAL_MS = 90 * 60 * 1000;
|
|
22
|
+
|
|
23
|
+
function saveAppState(api) {
|
|
24
|
+
try {
|
|
25
|
+
if (!api?.getAppState) return;
|
|
26
|
+
const state = api.getAppState();
|
|
27
|
+
if (!state || !state.length) return;
|
|
28
|
+
const file = path.join(process.cwd(), "appstate.json");
|
|
29
|
+
const tmp = file + ".tmp";
|
|
30
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, "\t"), "utf8");
|
|
31
|
+
fs.renameSync(tmp, file);
|
|
32
|
+
} catch (_) {}
|
|
33
|
+
}
|
|
13
34
|
|
|
14
35
|
module.exports = function(defaultFuncs, api, ctx) {
|
|
15
36
|
|
|
16
|
-
// ✅
|
|
17
|
-
if (ctx._fbDtsgInterval)
|
|
37
|
+
// ✅ إلغاء أي interval سابق قبل إنشاء جديد
|
|
38
|
+
if (ctx._fbDtsgInterval) {
|
|
39
|
+
clearInterval(ctx._fbDtsgInterval);
|
|
40
|
+
ctx._fbDtsgInterval = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ✅ تجديد تلقائي كل 90 دقيقة
|
|
18
44
|
ctx._fbDtsgInterval = setInterval(async () => {
|
|
19
45
|
try {
|
|
46
|
+
// لا تجدد إذا كان MQTT منقطعاً (لا فائدة)
|
|
47
|
+
if (!ctx.mqttClient?.connected && !ctx.jar) return;
|
|
20
48
|
await api.refreshFb_dtsg();
|
|
21
49
|
logger("[ AYMAN ] fb_dtsg مجدد تلقائياً ✅", "info");
|
|
50
|
+
// حفظ appstate بعد كل تجديد ناجح
|
|
51
|
+
saveAppState(api);
|
|
22
52
|
} catch (_) {}
|
|
23
|
-
},
|
|
53
|
+
}, REFRESH_INTERVAL_MS);
|
|
24
54
|
|
|
25
55
|
return function refreshFb_dtsg(obj, callback) {
|
|
26
56
|
if (typeof obj === "function") { callback = obj; obj = {}; }
|
|
@@ -31,23 +61,44 @@ module.exports = function(defaultFuncs, api, ctx) {
|
|
|
31
61
|
if (!callback) callback = (err, data) => err ? reject(err) : resolve(data);
|
|
32
62
|
|
|
33
63
|
if (Object.keys(obj).length === 0) {
|
|
34
|
-
// ✅ 3 محاولات
|
|
64
|
+
// ✅ 3 محاولات مع تأخير تصاعدي
|
|
35
65
|
const tryRefresh = async (attempt = 0) => {
|
|
36
66
|
try {
|
|
37
67
|
const res = await get("https://www.facebook.com/", ctx.jar, null, ctx.globalOptions, { noRef: true });
|
|
38
68
|
const html = res?.data || "";
|
|
39
|
-
|
|
40
|
-
|
|
69
|
+
|
|
70
|
+
// ✅ استخراج fb_dtsg
|
|
71
|
+
const dtsg = getFrom(html, '["DTSGInitData",[],{"token":"', '",')
|
|
72
|
+
|| getFrom(html, '"token":"', '","ttl"');
|
|
73
|
+
// ✅ استخراج jazoest
|
|
74
|
+
const jaz = getFrom(html, "jazoest=", "&")
|
|
75
|
+
|| getFrom(html, 'name="jazoest" value="', '"');
|
|
76
|
+
// ✅ استخراج lsd (مُضاف جديد)
|
|
77
|
+
const lsd = getFrom(html, '["LSD",[],{"token":"', '"}')
|
|
78
|
+
|| getFrom(html, '"lsd":{"token":"', '"');
|
|
41
79
|
|
|
42
80
|
if (!dtsg) {
|
|
43
|
-
if (attempt < 2) {
|
|
81
|
+
if (attempt < 2) {
|
|
82
|
+
await new Promise(r => setTimeout(r, 2000 * (attempt + 1)));
|
|
83
|
+
return tryRefresh(attempt + 1);
|
|
84
|
+
}
|
|
44
85
|
throw new Error("[ AYMAN ] لم يُعثر على fb_dtsg بعد 3 محاولات");
|
|
45
86
|
}
|
|
46
87
|
|
|
47
|
-
|
|
48
|
-
|
|
88
|
+
// ✅ تحديث ctx بكل القيم الجديدة
|
|
89
|
+
ctx.fb_dtsg = dtsg;
|
|
90
|
+
if (jaz) ctx.jazoest = jaz;
|
|
91
|
+
if (lsd) ctx.lsd = lsd;
|
|
92
|
+
|
|
93
|
+
callback(null, {
|
|
94
|
+
data: { fb_dtsg: dtsg, jazoest: jaz, lsd },
|
|
95
|
+
message: "تم تجديد fb_dtsg ✅"
|
|
96
|
+
});
|
|
49
97
|
} catch (err) {
|
|
50
|
-
if (attempt < 2) {
|
|
98
|
+
if (attempt < 2) {
|
|
99
|
+
await new Promise(r => setTimeout(r, 2000 * (attempt + 1)));
|
|
100
|
+
return tryRefresh(attempt + 1);
|
|
101
|
+
}
|
|
51
102
|
callback(err);
|
|
52
103
|
}
|
|
53
104
|
};
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// AYMAN-FCA v2.0 — MQTT Core Connection
|
|
2
|
+
// AYMAN-FCA v2.0 — MQTT Core Connection [FIXED]
|
|
3
3
|
// © 2025 Ayman. All Rights Reserved.
|
|
4
|
+
//
|
|
5
|
+
// الإصلاحات:
|
|
6
|
+
// ✅ keepalive: 10 (كان 60 — السبب الرئيسي للانقطاع)
|
|
7
|
+
// ✅ clean: true (كان false — يُسبب puback errors)
|
|
8
|
+
// ✅ reconnectPeriod: 1000 (كان 0 — يُغرق الشبكة)
|
|
9
|
+
// ✅ T_MS_WAIT_MS: 30000 (كان 12000 — قصير جداً)
|
|
10
|
+
// ✅ MAX_RECONNECT: Infinity (كان 15 — بعدها يموت نهائياً)
|
|
11
|
+
// ✅ exponential backoff لـ reconnect (بدلاً من delay ثابت)
|
|
12
|
+
// ✅ foreground heartbeat كل 8 دقائق داخل MQTT
|
|
4
13
|
// ============================================================
|
|
5
14
|
"use strict";
|
|
6
15
|
|
|
7
16
|
const { formatID } = require("../../../utils/format");
|
|
8
17
|
|
|
9
|
-
const DEFAULT_RECONNECT_MS =
|
|
10
|
-
const T_MS_WAIT_MS = 12000
|
|
11
|
-
const MAX_RECONNECT = 15
|
|
18
|
+
const DEFAULT_RECONNECT_MS = 2000;
|
|
19
|
+
const T_MS_WAIT_MS = 30000; // ✅ كان 12000 — زيادة للشبكات البطيئة
|
|
20
|
+
const MAX_RECONNECT = Infinity; // ✅ كان 15 — لا توقف نهائي
|
|
21
|
+
|
|
22
|
+
// Backoff: 2s, 4s, 8s, 16s, 30s, 30s, 30s...
|
|
23
|
+
function calcBackoff(attempt) {
|
|
24
|
+
const base = Math.min(DEFAULT_RECONNECT_MS * Math.pow(2, attempt), 30000);
|
|
25
|
+
const jitter = Math.random() * 1000;
|
|
26
|
+
return Math.round(base + jitter);
|
|
27
|
+
}
|
|
12
28
|
|
|
13
29
|
function generateUUID() {
|
|
14
30
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
|
|
@@ -65,18 +81,21 @@ module.exports = function createListenMqtt(deps) {
|
|
|
65
81
|
|
|
66
82
|
if (typeof ctx._reconnectAttempts !== "number") ctx._reconnectAttempts = 0;
|
|
67
83
|
|
|
84
|
+
// ✅ exponential backoff — لا يتوقف أبداً
|
|
68
85
|
function scheduleReconnect(delayMs) {
|
|
69
86
|
if (ctx._reconnectTimer) return;
|
|
70
87
|
if (ctx._ending) return;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
const ms = typeof delayMs === "number" ? delayMs : (ctx._mqttOpt?.reconnectDelayMs || DEFAULT_RECONNECT_MS);
|
|
88
|
+
|
|
89
|
+
const ms = typeof delayMs === "number"
|
|
90
|
+
? delayMs
|
|
91
|
+
: calcBackoff(ctx._reconnectAttempts);
|
|
92
|
+
|
|
78
93
|
ctx._reconnectAttempts++;
|
|
79
|
-
|
|
94
|
+
|
|
95
|
+
// بعد 20 محاولة: أعد العداد إلى 10 (يبقى عند ~30s)
|
|
96
|
+
if (ctx._reconnectAttempts > 20) ctx._reconnectAttempts = 10;
|
|
97
|
+
|
|
98
|
+
logger(`[ AYMAN ] MQTT إعادة اتصال #${ctx._reconnectAttempts} بعد ${ms}ms`, "warn");
|
|
80
99
|
ctx._reconnectTimer = setTimeout(() => {
|
|
81
100
|
ctx._reconnectTimer = null;
|
|
82
101
|
ctx.clientId = generateUUID();
|
|
@@ -110,7 +129,7 @@ module.exports = function createListenMqtt(deps) {
|
|
|
110
129
|
protocolId: "MQIsdp",
|
|
111
130
|
protocolVersion: 3,
|
|
112
131
|
username: JSON.stringify(username),
|
|
113
|
-
clean:
|
|
132
|
+
clean: true, // ✅ كان false — true أفضل مع syncToken
|
|
114
133
|
wsOptions: {
|
|
115
134
|
headers: {
|
|
116
135
|
Cookie: cookies,
|
|
@@ -130,12 +149,12 @@ module.exports = function createListenMqtt(deps) {
|
|
|
130
149
|
origin: "https://www.facebook.com",
|
|
131
150
|
protocolVersion: 13,
|
|
132
151
|
binaryType: "arraybuffer",
|
|
133
|
-
handshakeTimeout:
|
|
152
|
+
handshakeTimeout: 20000
|
|
134
153
|
},
|
|
135
|
-
keepalive: 60
|
|
154
|
+
keepalive: 10, // ✅ كان 60 — Facebook يحتاج ping كل 10s
|
|
136
155
|
reschedulePings: true,
|
|
137
|
-
reconnectPeriod: 0
|
|
138
|
-
connectTimeout: 15000
|
|
156
|
+
reconnectPeriod: 1000, // ✅ كان 0 — 1s انتظار قبل retry
|
|
157
|
+
connectTimeout: 20000 // ✅ كان 15000 — زيادة للشبكات البطيئة
|
|
139
158
|
};
|
|
140
159
|
|
|
141
160
|
if (ctx.globalOptions?.proxy) {
|
|
@@ -187,6 +206,17 @@ module.exports = function createListenMqtt(deps) {
|
|
|
187
206
|
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
|
|
188
207
|
mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
|
|
189
208
|
|
|
209
|
+
// ✅ foreground heartbeat كل 8 دقائق (يُثبت النشاط لـ Facebook)
|
|
210
|
+
if (ctx._fgHeartbeat) clearInterval(ctx._fgHeartbeat);
|
|
211
|
+
ctx._fgHeartbeat = setInterval(() => {
|
|
212
|
+
try {
|
|
213
|
+
if (!mqttClient?.connected) return;
|
|
214
|
+
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: true }), { qos: 0 });
|
|
215
|
+
mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 0 });
|
|
216
|
+
} catch (_) {}
|
|
217
|
+
}, 8 * 60 * 1000);
|
|
218
|
+
|
|
219
|
+
// ✅ T_MS_WAIT_MS مرفوع إلى 30s
|
|
190
220
|
let rTimeout = setTimeout(() => {
|
|
191
221
|
rTimeout = null;
|
|
192
222
|
if (ctx._ending) return;
|
|
@@ -248,6 +278,8 @@ module.exports = function createListenMqtt(deps) {
|
|
|
248
278
|
});
|
|
249
279
|
|
|
250
280
|
mqttClient.on("close", function() {
|
|
281
|
+
// ✅ تنظيف heartbeat عند الإغلاق
|
|
282
|
+
if (ctx._fgHeartbeat) { clearInterval(ctx._fgHeartbeat); ctx._fgHeartbeat = null; }
|
|
251
283
|
if (ctx._ending || ctx._cycling) return;
|
|
252
284
|
logger("[ AYMAN ] MQTT انقطع — إعادة اتصال", "warn");
|
|
253
285
|
if (ctx.globalOptions?.autoReconnect !== false && !ctx._reconnectTimer) scheduleReconnect();
|