ayman-fca 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 (124) hide show
  1. package/README.md +81 -0
  2. package/func/checkUpdate.js +13 -0
  3. package/func/logAdapter.js +30 -0
  4. package/func/logger.js +66 -0
  5. package/index.d.ts +751 -0
  6. package/index.js +15 -0
  7. package/module/config.js +38 -0
  8. package/module/login.js +111 -0
  9. package/module/loginHelper.js +1296 -0
  10. package/module/options.js +37 -0
  11. package/package.json +78 -0
  12. package/src/api/action/addExternalModule.js +19 -0
  13. package/src/api/action/changeAvatar.js +137 -0
  14. package/src/api/action/changeBio.js +48 -0
  15. package/src/api/action/enableAutoSaveAppState.js +72 -0
  16. package/src/api/action/getCurrentUserID.js +11 -0
  17. package/src/api/action/handleFriendRequest.js +33 -0
  18. package/src/api/action/logout.js +76 -0
  19. package/src/api/action/refreshFb_dtsg.js +62 -0
  20. package/src/api/action/setPostReaction.js +106 -0
  21. package/src/api/action/story.js +118 -0
  22. package/src/api/action/unfriend.js +30 -0
  23. package/src/api/http/httpGet.js +28 -0
  24. package/src/api/http/httpPost.js +32 -0
  25. package/src/api/http/postFormData.js +23 -0
  26. package/src/api/messaging/J +1 -0
  27. package/src/api/messaging/addUserToGroup.js +70 -0
  28. package/src/api/messaging/changeAdminStatus.js +72 -0
  29. package/src/api/messaging/changeArchivedStatus.js +31 -0
  30. package/src/api/messaging/changeBlockedStatus.js +27 -0
  31. package/src/api/messaging/changeGroupImage.js +91 -0
  32. package/src/api/messaging/changeNickname.js +70 -0
  33. package/src/api/messaging/changeThreadColor.js +44 -0
  34. package/src/api/messaging/changeThreadEmoji.js +111 -0
  35. package/src/api/messaging/createNewGroup.js +50 -0
  36. package/src/api/messaging/createPoll.js +52 -0
  37. package/src/api/messaging/createThemeAI.js +98 -0
  38. package/src/api/messaging/deleteMessage.js +73 -0
  39. package/src/api/messaging/deleteThread.js +29 -0
  40. package/src/api/messaging/editMessage.js +67 -0
  41. package/src/api/messaging/forwardAttachment.js +55 -0
  42. package/src/api/messaging/forwardMessage.js +73 -0
  43. package/src/api/messaging/getEmojiUrl.js +29 -0
  44. package/src/api/messaging/getFriendsList.js +82 -0
  45. package/src/api/messaging/getMessage.js +829 -0
  46. package/src/api/messaging/getThemePictures.js +62 -0
  47. package/src/api/messaging/groupActions.js +119 -0
  48. package/src/api/messaging/handleMessageRequest.js +31 -0
  49. package/src/api/messaging/markAsDelivered.js +31 -0
  50. package/src/api/messaging/markAsRead.js +88 -0
  51. package/src/api/messaging/markAsReadAll.js +28 -0
  52. package/src/api/messaging/markAsSeen.js +30 -0
  53. package/src/api/messaging/muteThread.js +27 -0
  54. package/src/api/messaging/notes.js +101 -0
  55. package/src/api/messaging/removeUserFromGroup.js +51 -0
  56. package/src/api/messaging/resolvePhotoUrl.js +29 -0
  57. package/src/api/messaging/scheduler.js +100 -0
  58. package/src/api/messaging/searchForThread.js +32 -0
  59. package/src/api/messaging/sendMessage.js +270 -0
  60. package/src/api/messaging/sendTypingIndicator.js +62 -0
  61. package/src/api/messaging/setMessageReaction.js +91 -0
  62. package/src/api/messaging/setTitle.js +86 -0
  63. package/src/api/messaging/shareContact.js +47 -0
  64. package/src/api/messaging/threadColors.js +128 -0
  65. package/src/api/messaging/unsendMessage.js +73 -0
  66. package/src/api/messaging/uploadAttachment.js +492 -0
  67. package/src/api/socket/core/connectMqtt.js +259 -0
  68. package/src/api/socket/core/emitAuth.js +79 -0
  69. package/src/api/socket/core/getSeqID.js +170 -0
  70. package/src/api/socket/core/getTaskResponseData.js +27 -0
  71. package/src/api/socket/core/parseDelta.js +377 -0
  72. package/src/api/socket/detail/buildStream.js +215 -0
  73. package/src/api/socket/detail/constants.js +29 -0
  74. package/src/api/socket/listenMqtt.js +377 -0
  75. package/src/api/socket/middleware/index.js +80 -0
  76. package/src/api/threads/getThreadHistory.js +664 -0
  77. package/src/api/threads/getThreadInfo.js +296 -0
  78. package/src/api/threads/getThreadList.js +293 -0
  79. package/src/api/threads/getThreadPictures.js +43 -0
  80. package/src/api/user/J +1 -0
  81. package/src/api/user/getUserID.js +48 -0
  82. package/src/api/user/getUserInfo.js +402 -0
  83. package/src/api/user/getUserInfoV2.js +134 -0
  84. package/src/core/sendReqMqtt.js +69 -0
  85. package/src/database/helpers.js +36 -0
  86. package/src/database/models/index.js +55 -0
  87. package/src/database/models/thread.js +44 -0
  88. package/src/database/models/user.js +39 -0
  89. package/src/database/threadData.js +92 -0
  90. package/src/database/userData.js +88 -0
  91. package/src/remote/remoteClient.js +71 -0
  92. package/src/utils/broadcast.js +62 -0
  93. package/src/utils/client.js +10 -0
  94. package/src/utils/constants.js +53 -0
  95. package/src/utils/cookies.js +73 -0
  96. package/src/utils/format/attachment.js +357 -0
  97. package/src/utils/format/cookie.js +9 -0
  98. package/src/utils/format/date.js +50 -0
  99. package/src/utils/format/decode.js +44 -0
  100. package/src/utils/format/delta.js +194 -0
  101. package/src/utils/format/ids.js +64 -0
  102. package/src/utils/format/index.js +64 -0
  103. package/src/utils/format/message.js +88 -0
  104. package/src/utils/format/presence.js +132 -0
  105. package/src/utils/format/readTyp.js +44 -0
  106. package/src/utils/format/thread.js +42 -0
  107. package/src/utils/format/utils.js +141 -0
  108. package/src/utils/headers.js +96 -0
  109. package/src/utils/loginParser/autoLogin.js +125 -0
  110. package/src/utils/loginParser/helpers.js +43 -0
  111. package/src/utils/loginParser/index.js +10 -0
  112. package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
  113. package/src/utils/loginParser/textUtils.js +28 -0
  114. package/src/utils/request/H +1 -0
  115. package/src/utils/request/client.js +33 -0
  116. package/src/utils/request/config.js +25 -0
  117. package/src/utils/request/defaults.js +40 -0
  118. package/src/utils/request/helpers.js +31 -0
  119. package/src/utils/request/index.js +12 -0
  120. package/src/utils/request/methods.js +92 -0
  121. package/src/utils/request/proxy.js +23 -0
  122. package/src/utils/request/retry.js +87 -0
  123. package/src/utils/request/sanitize.js +41 -0
  124. package/src/utils/sessionKeeper.js +275 -0
@@ -0,0 +1,23 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — Proxy Handler
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ // ============================================================
5
+ "use strict";
6
+
7
+ const { HttpsProxyAgent } = require("https-proxy-agent");
8
+ const { client } = require("./client");
9
+
10
+ function setProxy(proxyUrl) {
11
+ if (!proxyUrl) {
12
+ client.defaults.proxy = false;
13
+ client.defaults.httpAgent = undefined;
14
+ client.defaults.httpsAgent = undefined;
15
+ return;
16
+ }
17
+ const agent = new HttpsProxyAgent(proxyUrl);
18
+ client.defaults.proxy = false;
19
+ client.defaults.httpAgent = agent;
20
+ client.defaults.httpsAgent = agent;
21
+ }
22
+
23
+ module.exports = { setProxy };
@@ -0,0 +1,87 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — Smart Retry System
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ //
5
+ // نظام إعادة محاولة ذكي مع Exponential Backoff + Jitter
6
+ // يتعامل مع: timeout, 429, 5xx, network errors
7
+ // ============================================================
8
+ "use strict";
9
+
10
+ const { delay } = require("./client");
11
+
12
+ const RETRYABLE_CODES = new Set([
13
+ "UND_ERR_CONNECT_TIMEOUT","ETIMEDOUT","ECONNRESET",
14
+ "ECONNREFUSED","ENOTFOUND","EPIPE","EAI_AGAIN","ENETUNREACH"
15
+ ]);
16
+
17
+ function isRetryable(e) {
18
+ const code = e?.code || e?.cause?.code || "";
19
+ const msg = (e?.message || "").toLowerCase();
20
+ if (RETRYABLE_CODES.has(code)) return true;
21
+ if (/timeout|connect timeout|network error|fetch failed|socket hang up|econnreset/i.test(msg)) return true;
22
+ return false;
23
+ }
24
+
25
+ async function requestWithRetry(fn, retries = 3, baseDelay = 800, ctx) {
26
+ let lastError;
27
+
28
+ const emit = (event, payload) => {
29
+ try {
30
+ if (ctx?._emitter?.emit) ctx._emitter.emit(event, payload);
31
+ } catch {}
32
+ };
33
+
34
+ for (let i = 0; i < retries; i++) {
35
+ try {
36
+ return await fn();
37
+ } catch (e) {
38
+ lastError = e;
39
+
40
+ // ✅ خطأ Header — لا تعيد المحاولة أبداً
41
+ if (e?.code === "ERR_INVALID_CHAR" || e?.message?.includes("Invalid character in header")) {
42
+ const err = new Error("AYMAN-FCA: Invalid header — request aborted.");
43
+ err.code = "ERR_INVALID_CHAR";
44
+ err.originalError = e;
45
+ return Promise.reject(err);
46
+ }
47
+
48
+ const status = e?.response?.status || e?.statusCode || 0;
49
+ const url = e?.config?.url || "";
50
+ const method = String(e?.config?.method || "").toUpperCase();
51
+
52
+ // ✅ Rate Limit (429) — انتظر أطول
53
+ if (status === 429) {
54
+ emit("rateLimit", { status, url, method, attempt: i + 1 });
55
+ const wait = Math.min(baseDelay * Math.pow(3, i), 60000);
56
+ await delay(wait);
57
+ continue;
58
+ }
59
+
60
+ // ✅ 4xx (غير 429) — لا تعيد
61
+ if (status >= 400 && status < 500) {
62
+ return e.response || Promise.reject(e);
63
+ }
64
+
65
+ // ✅ آخر محاولة
66
+ if (i === retries - 1) {
67
+ return e.response || Promise.reject(e);
68
+ }
69
+
70
+ // ✅ أخطاء الشبكة
71
+ if (!status && isRetryable(e)) {
72
+ emit("networkError", { code: e?.code || "", message: e?.message || "", url, method, attempt: i + 1 });
73
+ }
74
+
75
+ // ✅ Exponential Backoff + Jitter
76
+ const wait = Math.min(
77
+ baseDelay * Math.pow(2, i) + Math.floor(Math.random() * 400),
78
+ 30000
79
+ );
80
+ await delay(wait);
81
+ }
82
+ }
83
+
84
+ return Promise.reject(lastError || new Error("AYMAN-FCA: Request failed after retries"));
85
+ }
86
+
87
+ module.exports = { requestWithRetry };
@@ -0,0 +1,41 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — Header Sanitizer
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ // ============================================================
5
+ "use strict";
6
+
7
+ function sanitizeHeaderValue(value) {
8
+ if (value === null || value === undefined) return "";
9
+ const str = String(value);
10
+ // ✅ إزالة كل الأحرف غير الصالحة في HTTP headers
11
+ return str.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\r\n]/g, "").trim();
12
+ }
13
+
14
+ function sanitizeHeaderName(name) {
15
+ if (!name || typeof name !== "string") return "";
16
+ return name.replace(/[^\x21-\x7E]/g, "").trim();
17
+ }
18
+
19
+ function sanitizeHeaders(headers) {
20
+ if (!headers || typeof headers !== "object") return {};
21
+ const out = {};
22
+ for (const [key, value] of Object.entries(headers)) {
23
+ const k = sanitizeHeaderName(key);
24
+ if (!k) continue;
25
+ if (Array.isArray(value) || (value !== null && typeof value === "object") || typeof value === "function") continue;
26
+
27
+ // ✅ كشف القيم المسلسلة كـ array خطأً
28
+ if (typeof value === "string") {
29
+ const t = value.trim();
30
+ if (t.startsWith("[") && t.endsWith("]")) {
31
+ try { if (Array.isArray(JSON.parse(t))) continue; } catch (_) {}
32
+ }
33
+ }
34
+
35
+ const v = sanitizeHeaderValue(value);
36
+ if (v !== "") out[k] = v;
37
+ }
38
+ return out;
39
+ }
40
+
41
+ module.exports = { sanitizeHeaderValue, sanitizeHeaderName, sanitizeHeaders };
@@ -0,0 +1,275 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.2 — Session Keeper (FIXED)
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ // ============================================================
5
+ "use strict";
6
+
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+
10
+ let logger;
11
+ try {
12
+ logger = require("../../../func/logger");
13
+ } catch(_) {
14
+ logger = (msg, type) => process.stderr.write(`[ AYMAN-SK ][ ${type || "info"} ] ${msg}\n`);
15
+ }
16
+
17
+ // ── Presence Encoder ──────────────────────────────────────────
18
+ const PRESENCE_MAP = {
19
+ _:"%", A:"%2", B:"000", C:"%7d", D:"%7b%22", E:"%2c%22",
20
+ F:"%22%3a", G:"%2c%22ut%22%3a1", H:"%2c%22bls%22%3a",
21
+ I:"%2c%22n%22%3a%22%", J:"%22%3a%7b%22i%22%3a0%7d",
22
+ K:"%2c%22pt%22%3a0%2c%22vis%22%3a", L:"%2c%22ch%22%3a%7b%22h%22%3a%22",
23
+ M:"%7b%22v%22%3a2%2c%22time%22%3a1", N:".channel%22%2c%22sub%22%3a%5b",
24
+ O:"%2c%22sb%22%3a1%2c%22t%22%3a%5b", P:"%2c%22ud%22%3a100%2c%22lc%22%3a0",
25
+ Q:"%5d%2c%22f%22%3anull%2c%22uct%22%3a", R:".channel%22%2c%22sub%22%3a%5b1%5d",
26
+ Z:"%2c%22sb%22%3a1%2c%22t%22%3a%5b%5d%2c%22f%22%3anull%2c%22uct%22%3a0%2c%22s%22%3a0%2c%22blo%22%3a0%7d%2c%22bl%22%3a%7b%22ac%22%3a"
27
+ };
28
+ const encodeMap = {}, decodeList = [];
29
+ for (const [k,v] of Object.entries(PRESENCE_MAP)) { encodeMap[v]=k; decodeList.push(v); }
30
+ decodeList.sort((a,b)=>b.length-a.length);
31
+ const presenceRegex = new RegExp(decodeList.map(s=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|"),"g");
32
+ function presenceEncode(str){ return encodeURIComponent(str).replace(presenceRegex,m=>encodeMap[m]||m); }
33
+
34
+ function generatePresence(userID) {
35
+ const time = Date.now();
36
+ return "E" + presenceEncode(JSON.stringify({
37
+ v:3, time:Math.floor(time/1000), user:userID,
38
+ state:{ ut:0, t2:[], lm2:null, uct2:time, tr:null,
39
+ tw:Math.floor(Math.random()*4294967295)+1, at:time },
40
+ ch:{ [`p_${userID}`]:0 }
41
+ }));
42
+ }
43
+
44
+ function generateAccessibilityCookie() {
45
+ const time = Date.now();
46
+ return encodeURIComponent(JSON.stringify({
47
+ sr:0,"sr-ts":time,jk:0,"jk-ts":time,kb:0,"kb-ts":time,hcm:0,"hcm-ts":time
48
+ }));
49
+ }
50
+
51
+ function saveAppStateAtomic(statePath, state) {
52
+ try {
53
+ const tmp = statePath + ".tmp";
54
+ fs.writeFileSync(tmp, JSON.stringify(state, null, "\t"), "utf8");
55
+ fs.renameSync(tmp, statePath);
56
+ return true;
57
+ } catch(e) {
58
+ logger(`خطأ في حفظ AppState: ${e?.message||e}`, "error");
59
+ return false;
60
+ }
61
+ }
62
+
63
+ function setCookieSafe(jar, cookieStr, url) {
64
+ try {
65
+ if (typeof jar?.setCookieSync==="function") jar.setCookieSync(cookieStr, url);
66
+ else if (typeof jar?.setCookie==="function") jar.setCookie(cookieStr, url);
67
+ } catch(_) {}
68
+ }
69
+
70
+ // ✅ استخراج ctx الحقيقي من api
71
+ function extractCtx(api, hint) {
72
+ if (hint && hint.jar && hint.userID && hint.userID !== "0") return hint;
73
+ for (const key of Object.getOwnPropertyNames(api || {})) {
74
+ try {
75
+ const v = api[key];
76
+ if (v && typeof v==="object" && v.jar && v.userID && v.userID!=="0") return v;
77
+ } catch(_) {}
78
+ }
79
+ return hint || {};
80
+ }
81
+
82
+ // ✅ xs refresh آمن — المسار الصحيح
83
+ async function safeFetch(url, jar, ctx) {
84
+ // ✅ الإصلاح: require("./request") وليس require("../request")
85
+ try {
86
+ const { get } = require("./request");
87
+ const res = await get(url, jar, null, ctx?.globalOptions||{}, ctx);
88
+ const sc = res?.headers?.["set-cookie"] || [];
89
+ for (const c of sc) { try { setCookieSafe(jar, c, "https://www.facebook.com"); } catch(_) {} }
90
+ return res;
91
+ } catch(_) {
92
+ // fallback: axios مباشرة
93
+ try {
94
+ const axios = require("axios");
95
+ const cookies = jar?.getCookiesSync?.(url)?.map(c=>`${c.key}=${c.value}`).join("; ")||"";
96
+ const res = await axios.get(url, {
97
+ headers:{ "Cookie":cookies, "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36" },
98
+ timeout:20000, validateStatus:s=>s<600
99
+ });
100
+ const sc = res.headers?.["set-cookie"]||[];
101
+ for (const c of sc) { try { setCookieSafe(jar, c, "https://www.facebook.com"); } catch(_) {} }
102
+ return res;
103
+ } catch(_) { return null; }
104
+ }
105
+ }
106
+
107
+ // ════════════════════════════════════════════════════════════════
108
+ function createSessionKeeper(api, ctxHint, options = {}) {
109
+ const appStatePath = options.appStatePath || path.join(process.cwd(), "appstate.json");
110
+ const onSave = typeof options.onSave==="function" ? options.onSave : null;
111
+
112
+ let ctx = extractCtx(api, ctxHint);
113
+
114
+ let _presence=null, _activity=null, _dtsg=null, _save=null;
115
+ let _access=null, _ping=null, _xsRefresh=null, _watchdog=null;
116
+ let _active=false, _lastHash=null;
117
+
118
+ function getCtx() {
119
+ if (!ctx?.jar || !ctx?.userID || ctx.userID==="0") ctx = extractCtx(api, ctxHint);
120
+ return ctx;
121
+ }
122
+
123
+ function _triggerSave(force=false) {
124
+ try {
125
+ if (!api?.getAppState) return;
126
+ const state = api.getAppState();
127
+ if (!state || state.length===0) return;
128
+ const xs = state.find(c=>(c.key||c.name)==="xs");
129
+ const cu = state.find(c=>(c.key||c.name)==="c_user");
130
+ const hash= `${state.length}_${cu?.value?.slice(0,6)||""}_${xs?.value?.slice(0,8)||""}`;
131
+ if (!force && hash===_lastHash) return;
132
+ _lastHash = hash;
133
+ const ok = saveAppStateAtomic(appStatePath, state);
134
+ if (ok) { logger("AppState محفوظ ✅","info"); if (onSave) onSave(state); }
135
+ } catch(_) {}
136
+ }
137
+
138
+ // ① Presence كل 45 ثانية
139
+ function startPresence() {
140
+ if (_presence) return;
141
+ _presence = setInterval(()=>{
142
+ try {
143
+ const c = getCtx();
144
+ if (!c?.mqttClient?.connected || !c?.userID) return;
145
+ const p = generatePresence(c.userID);
146
+ c.mqttClient.publish("/orca_presence", JSON.stringify({p, c:c.userID}), {qos:0});
147
+ } catch(_) {}
148
+ }, 45*1000);
149
+ }
150
+
151
+ // ② نشاط عشوائي كل 3-7 دقائق
152
+ function scheduleActivity() {
153
+ const ms = (3 + Math.random()*4)*60*1000;
154
+ _activity = setTimeout(async ()=>{
155
+ try {
156
+ const c = getCtx();
157
+ if (Math.random()>0.5) { if (api?.markAsReadAll) api.markAsReadAll(()=>{}); }
158
+ else if (c?.mqttClient?.connected)
159
+ c.mqttClient.publish("/foreground_state", JSON.stringify({foreground:true}), {qos:0});
160
+ } catch(_) {}
161
+ scheduleActivity();
162
+ }, ms);
163
+ }
164
+
165
+ // ③ تجديد fb_dtsg كل 4 ساعات
166
+ function startDtsgRefresh() {
167
+ if (_dtsg) return;
168
+ _dtsg = setInterval(async ()=>{
169
+ try {
170
+ if (api?.refreshFb_dtsg) { await api.refreshFb_dtsg(); logger("fb_dtsg مجدد ✅","info"); _triggerSave(); }
171
+ } catch(_) {}
172
+ }, 4*60*60*1000);
173
+ }
174
+
175
+ // ④ حفظ كل 8 دقائق
176
+ function startAutoSave() {
177
+ if (_save) return;
178
+ _save = setInterval(()=> _triggerSave(), 8*60*1000);
179
+ }
180
+
181
+ // ⑤ Accessibility cookie كل 30 دقيقة
182
+ function startAccessibilityCookie() {
183
+ if (_access) return;
184
+ function update() {
185
+ try {
186
+ const c = getCtx();
187
+ if (!c?.jar) return;
188
+ const val = generateAccessibilityCookie();
189
+ const exp = new Date(Date.now()+365*24*3600*1000).toUTCString();
190
+ setCookieSafe(c.jar, `wd=${val}; domain=.facebook.com; path=/; expires=${exp}`, "https://www.facebook.com");
191
+ } catch(_) {}
192
+ }
193
+ update();
194
+ _access = setInterval(update, 30*60*1000);
195
+ }
196
+
197
+ // ⑥ ✅ تجديد xs كل 4 ساعات — إصلاح المسار
198
+ function startXsRefresh() {
199
+ if (_xsRefresh) return;
200
+ _xsRefresh = setInterval(async ()=>{
201
+ try {
202
+ const c = getCtx();
203
+ if (!c?.jar) return;
204
+ const res = await safeFetch("https://www.facebook.com/", c.jar, c);
205
+ if (res) { logger("xs cookie مجدد ✅","info"); _triggerSave(); }
206
+ } catch(_) {}
207
+ }, 4*60*60*1000);
208
+ }
209
+
210
+ // ⑦ Ping MQTT كل ساعة
211
+ function startPing() {
212
+ if (_ping) return;
213
+ _ping = setInterval(()=>{
214
+ try {
215
+ const c = getCtx();
216
+ if (!c?.mqttClient?.connected) return;
217
+ c.mqttClient.publish("/set_client_settings",
218
+ JSON.stringify({make_user_available_when_in_foreground:true}), {qos:0});
219
+ } catch(_) {}
220
+ }, 60*60*1000);
221
+ }
222
+
223
+ // ⑧ Watchdog — يراقب MQTT كل دقيقتين
224
+ function startWatchdog() {
225
+ if (_watchdog) return;
226
+ let lastAlive = Date.now(), deadCount = 0;
227
+ _watchdog = setInterval(()=>{
228
+ try {
229
+ const c = getCtx();
230
+ if (!c?.mqttClient) return;
231
+ if (c.mqttClient?.connected) { lastAlive=Date.now(); deadCount=0; return; }
232
+ const deadMs = Date.now()-lastAlive;
233
+ if (deadMs > 5*60*1000 && !c.mqttClient?.reconnecting) {
234
+ deadCount++;
235
+ logger(`[ Watchdog ] MQTT منقطع ${Math.round(deadMs/60000)} دقيقة (${deadCount}/3)`, "warn");
236
+ if (deadCount>=3) {
237
+ deadCount=0; lastAlive=Date.now();
238
+ try { if (c?._emitter?.emit) c._emitter.emit("watchdog_reconnect",{reason:"mqtt_dead"}); }
239
+ catch(_) {}
240
+ }
241
+ }
242
+ } catch(_) {}
243
+ }, 2*60*1000);
244
+ }
245
+
246
+ function start() {
247
+ if (_active) return;
248
+ _active = true;
249
+ setTimeout(()=>{ ctx = extractCtx(api, ctxHint); }, 1000);
250
+ startPresence();
251
+ scheduleActivity();
252
+ startDtsgRefresh();
253
+ startAutoSave();
254
+ startAccessibilityCookie();
255
+ startXsRefresh();
256
+ startPing();
257
+ startWatchdog();
258
+ logger("Session Keeper v2.2 ✅ — 8 أنظمة تعمل", "info");
259
+ }
260
+
261
+ function stop() {
262
+ _active = false;
263
+ [_presence,_dtsg,_save,_access,_xsRefresh,_ping,_watchdog].forEach(t=>{ try{if(t)clearInterval(t);}catch(_){} });
264
+ try{if(_activity)clearTimeout(_activity);}catch(_){}
265
+ _presence=_activity=_dtsg=_save=_access=_xsRefresh=_ping=_watchdog=null;
266
+ logger("Session Keeper موقوف","info");
267
+ }
268
+
269
+ function saveNow() { _triggerSave(true); return true; }
270
+ function updateCtx(c) { if (c?.jar&&c?.userID) { ctx=c; logger("ctx مُحدَّث ✅","info"); } }
271
+
272
+ return { start, stop, saveNow, updateCtx, generatePresence, generateAccessibilityCookie };
273
+ }
274
+
275
+ module.exports = { createSessionKeeper, generatePresence, generateAccessibilityCookie, saveAppStateAtomic };