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,259 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — MQTT Core Connection
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ //
5
+ // 🔑 أسرار الاستمرارية في هذا الملف:
6
+ // • UUID جديد لكل دورة اتصال (يخدع Facebook)
7
+ // • keepalive 60s (أكثر استقراراً من 10s)
8
+ // • connectTimeout 15s (كافٍ للشبكات البطيئة)
9
+ // • User-Agent عشوائي في كل اتصال
10
+ // • معالجة offline/close/error بذكاء
11
+ // • حد أقصى 15 محاولة مع backoff تصاعدي
12
+ // ============================================================
13
+ "use strict";
14
+
15
+ const { formatID } = require("../../../utils/format");
16
+
17
+ const DEFAULT_RECONNECT_MS = 3000;
18
+ const T_MS_WAIT_MS = 12000;
19
+ const MAX_RECONNECT = 15;
20
+
21
+ // ✅ من ws3: UUID جديد لكل دورة — يمنع Facebook من تتبع الجلسة
22
+ function generateUUID() {
23
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
24
+ const r = Math.random() * 16 | 0;
25
+ return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
26
+ });
27
+ }
28
+
29
+ // ✅ User-Agent عشوائي لكل اتصال MQTT
30
+ const CHROME_VERSIONS = ["120.0.0.0","122.0.0.0","124.0.0.0","126.0.0.0"];
31
+ function randomUA() {
32
+ const v = CHROME_VERSIONS[Math.floor(Math.random() * CHROME_VERSIONS.length)];
33
+ return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${v} Safari/537.36`;
34
+ }
35
+
36
+ module.exports = function createListenMqtt(deps) {
37
+ const {
38
+ WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy,
39
+ topics, parseDelta, getTaskResponseData, logger, emitAuth
40
+ } = deps;
41
+
42
+ return function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
43
+
44
+ if (typeof ctx._reconnectAttempts !== "number") ctx._reconnectAttempts = 0;
45
+
46
+ function scheduleReconnect(delayMs) {
47
+ if (ctx._reconnectTimer) return;
48
+ if (ctx._ending) return;
49
+
50
+ if (ctx._reconnectAttempts >= MAX_RECONNECT) {
51
+ logger(`[ AYMAN ] MQTT وصل الحد الأقصى (${MAX_RECONNECT}) — إيقاف`, "error");
52
+ ctx._reconnectAttempts = 0;
53
+ globalCallback({ type: "stop_listen", error: "max_reconnect_reached" }, null);
54
+ return;
55
+ }
56
+
57
+ const d = ctx._mqttOpt?.reconnectDelayMs || DEFAULT_RECONNECT_MS;
58
+ const ms = typeof delayMs === "number" ? delayMs : d;
59
+ ctx._reconnectAttempts++;
60
+
61
+ logger(`[ AYMAN ] MQTT إعادة اتصال بعد ${ms}ms (${ctx._reconnectAttempts}/${MAX_RECONNECT})`, "warn");
62
+
63
+ ctx._reconnectTimer = setTimeout(() => {
64
+ ctx._reconnectTimer = null;
65
+ // ✅ UUID جديد عند كل إعادة اتصال
66
+ ctx.clientId = generateUUID();
67
+ if (!ctx._ending) listenMqtt(defaultFuncs, api, ctx, globalCallback);
68
+ }, ms);
69
+ }
70
+
71
+ const chatOn = ctx.globalOptions?.online ?? true;
72
+ const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
73
+ const ua = randomUA();
74
+
75
+ const username = {
76
+ u: ctx.userID, s: sessionID, chat_on: chatOn, fg: false,
77
+ d: ctx.clientId, ct: "websocket", aid: 219994525426954,
78
+ aids: null, mqtt_sid: "", cp: 3, ecp: 10, st: [], pm: [],
79
+ dc: "", no_auto_fg: true, gas: null, pack: [], p: null, php_override: ""
80
+ };
81
+
82
+ const cookies = api.getCookies ? api.getCookies() : "";
83
+
84
+ let host;
85
+ if (ctx.mqttEndpoint)
86
+ host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${ctx.clientId}`;
87
+ else if (ctx.region)
88
+ host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${ctx.clientId}`;
89
+ else
90
+ host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${ctx.clientId}`;
91
+
92
+ const options = {
93
+ clientId: "mqttwsclient",
94
+ protocolId: "MQIsdp",
95
+ protocolVersion: 3,
96
+ username: JSON.stringify(username),
97
+ clean: true,
98
+ wsOptions: {
99
+ headers: {
100
+ Cookie: cookies,
101
+ Origin: "https://www.facebook.com",
102
+ "User-Agent": ua,
103
+ Referer: "https://www.facebook.com/",
104
+ Host: "edge-chat.facebook.com",
105
+ Connection: "Upgrade",
106
+ Pragma: "no-cache",
107
+ "Cache-Control": "no-cache",
108
+ Upgrade: "websocket",
109
+ "Sec-WebSocket-Version": "13",
110
+ "Accept-Encoding": "gzip, deflate, br",
111
+ "Accept-Language": "ar,en-US;q=0.9,en;q=0.8",
112
+ "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
113
+ },
114
+ origin: "https://www.facebook.com",
115
+ protocolVersion: 13,
116
+ binaryType: "arraybuffer",
117
+ handshakeTimeout: 15000
118
+ },
119
+ // ✅ keepalive 60s — أكثر استقراراً
120
+ keepalive: 60,
121
+ reschedulePings: true,
122
+ reconnectPeriod: 0,
123
+ connectTimeout: 15000
124
+ };
125
+
126
+ if (ctx.globalOptions?.proxy) {
127
+ options.wsOptions.agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
128
+ }
129
+
130
+ ctx.mqttClient = new mqtt.Client(
131
+ () => buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()),
132
+ options
133
+ );
134
+ const mqttClient = ctx.mqttClient;
135
+
136
+ // ─── Error ───────────────────────────────────────────────
137
+ mqttClient.on("error", function(err) {
138
+ const msg = String(err?.message || err || "");
139
+
140
+ if ((ctx._ending || ctx._cycling) && /No subscription existed|client disconnecting/i.test(msg)) return;
141
+
142
+ if (/Not logged in|blocked the login|401|403/i.test(msg)) {
143
+ try { if (mqttClient?.connected) mqttClient.end(true); } catch (_) {}
144
+ return emitAuth(ctx, api, globalCallback, /blocked/i.test(msg) ? "login_blocked" : "not_logged_in", msg);
145
+ }
146
+
147
+ logger(`[ AYMAN ] MQTT خطأ: ${msg}`, "error");
148
+ try { if (mqttClient?.connected) mqttClient.end(true); } catch (_) {}
149
+ if (ctx._ending || ctx._cycling) return;
150
+ if (ctx.globalOptions?.autoReconnect !== false) scheduleReconnect();
151
+ else globalCallback({ type: "stop_listen", error: msg }, null);
152
+ });
153
+
154
+ // ─── Connect ─────────────────────────────────────────────
155
+ mqttClient.on("connect", function() {
156
+ ctx._reconnectAttempts = 0;
157
+
158
+ if (!process.env.AymanFcaOnline) {
159
+ logger("[ AYMAN-FCA ] KIRA متصل بـ Facebook ✅", "info");
160
+ process.env.AymanFcaOnline = "1";
161
+ }
162
+ ctx._cycling = false;
163
+
164
+ topics.forEach(t => mqttClient.subscribe(t));
165
+
166
+ const queue = {
167
+ sync_api_version: 11, max_deltas_able_to_process: 100,
168
+ delta_batch_size: 500, encoding: "JSON",
169
+ entity_fbid: ctx.userID,
170
+ initial_titan_sequence_id: ctx.lastSeqId,
171
+ device_params: null
172
+ };
173
+ const topic = ctx.syncToken ? "/messenger_sync_get_diffs" : "/messenger_sync_create_queue";
174
+ if (ctx.syncToken) { queue.last_seq_id = ctx.lastSeqId; queue.sync_token = ctx.syncToken; }
175
+
176
+ mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
177
+ mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
178
+ mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
179
+
180
+ // ✅ T_MS_WAIT timeout
181
+ let rTimeout = setTimeout(() => {
182
+ rTimeout = null;
183
+ if (ctx._ending) return;
184
+ logger("[ AYMAN ] MQTT t_ms timeout — إعادة اتصال", "warn");
185
+ try { if (mqttClient?.connected) mqttClient.end(true); } catch (_) {}
186
+ scheduleReconnect();
187
+ }, T_MS_WAIT_MS);
188
+
189
+ ctx._rTimeout = rTimeout;
190
+ ctx.tmsWait = function() {
191
+ if (rTimeout) { clearTimeout(rTimeout); rTimeout = null; }
192
+ if (ctx._rTimeout) delete ctx._rTimeout;
193
+ if (ctx.globalOptions?.emitReady) globalCallback({ type: "ready", error: null });
194
+ delete ctx.tmsWait;
195
+ };
196
+ });
197
+
198
+ // ─── Message ─────────────────────────────────────────────
199
+ mqttClient.on("message", function(topic, message) {
200
+ if (ctx._ending) return;
201
+ try {
202
+ let j = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
203
+ try { j = JSON.parse(j); } catch { j = {}; }
204
+
205
+ if (j.type === "jewel_requests_add") {
206
+ globalCallback(null, { type: "friend_request_received", actorFbId: j.from.toString(), timestamp: Date.now().toString() });
207
+ } else if (j.type === "jewel_requests_remove_old") {
208
+ globalCallback(null, { type: "friend_request_cancel", actorFbId: j.from.toString(), timestamp: Date.now().toString() });
209
+ } else if (topic === "/t_ms") {
210
+ if (ctx.tmsWait) ctx.tmsWait();
211
+ if (j.firstDeltaSeqId && j.syncToken) { ctx.lastSeqId = j.firstDeltaSeqId; ctx.syncToken = j.syncToken; }
212
+ if (j.lastIssuedSeqId) ctx.lastSeqId = parseInt(j.lastIssuedSeqId);
213
+ for (const dlt of (j.deltas || [])) parseDelta(defaultFuncs, api, ctx, globalCallback, { delta: dlt });
214
+ } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
215
+ globalCallback(null, {
216
+ type: "typ", isTyping: !!j.state,
217
+ from: j.sender_fbid.toString(),
218
+ threadID: formatID((j.thread || j.sender_fbid).toString())
219
+ });
220
+ } else if (topic === "/orca_presence") {
221
+ if (!ctx.globalOptions?.updatePresence) {
222
+ for (const d of (j.list || [])) {
223
+ globalCallback(null, { type: "presence", userID: String(d.u), timestamp: d.l * 1000, statuses: d.p });
224
+ }
225
+ }
226
+ } else if (topic === "/ls_resp") {
227
+ try {
228
+ const parsed = JSON.parse(j.payload);
229
+ const reqID = j.request_id;
230
+ if (ctx.tasks instanceof Map && ctx.tasks.has(reqID)) {
231
+ const { type: taskType, callback: cb } = ctx.tasks.get(reqID);
232
+ const data = getTaskResponseData(taskType, parsed);
233
+ cb(data == null ? "error" : null, data == null ? null : Object.assign({ type: taskType, reqID }, data));
234
+ }
235
+ } catch (_) {}
236
+ }
237
+ } catch (ex) {
238
+ logger(`[ AYMAN ] MQTT رسالة خاطئة: ${ex?.message || ex}`, "error");
239
+ }
240
+ });
241
+
242
+ // ─── Close / Offline / Disconnect ────────────────────────
243
+ mqttClient.on("close", function() {
244
+ if (ctx._ending || ctx._cycling) return;
245
+ logger("[ AYMAN ] MQTT انقطع — إعادة اتصال", "warn");
246
+ if (ctx.globalOptions?.autoReconnect !== false && !ctx._reconnectTimer) scheduleReconnect();
247
+ });
248
+
249
+ mqttClient.on("offline", () => {
250
+ if (ctx._ending) return;
251
+ logger("[ AYMAN ] MQTT offline — الشبكة منقطعة", "warn");
252
+ });
253
+
254
+ mqttClient.on("disconnect", () => {
255
+ if (ctx._ending || ctx._cycling) return;
256
+ logger("[ AYMAN ] MQTT disconnect", "warn");
257
+ });
258
+ };
259
+ };
@@ -0,0 +1,79 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — Emit Auth (Session End Handler)
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ //
5
+ // يُشعر البوت بانتهاء الجلسة مع:
6
+ // • حفظ AppState قبل الإيقاف
7
+ // • تنظيف كامل للذاكرة
8
+ // ============================================================
9
+ "use strict";
10
+
11
+ const fs = require("fs");
12
+ const path = require("path");
13
+
14
+ module.exports = function createEmitAuth({ logger }) {
15
+ return function emitAuth(ctx, api, globalCallback, reason, detail) {
16
+
17
+ // ✅ احفظ AppState قبل أي شيء
18
+ try {
19
+ if (api?.getAppState) {
20
+ const state = api.getAppState();
21
+ if (state && state.length > 0) {
22
+ const p = path.join(process.cwd(), "appstate.json");
23
+ const tmp = p + ".tmp";
24
+ fs.writeFileSync(tmp, JSON.stringify(state, null, "\t"), "utf8");
25
+ fs.renameSync(tmp, p);
26
+ logger("[ AYMAN ] AppState محفوظ قبل انتهاء الجلسة ✅", "info");
27
+ }
28
+ }
29
+ } catch (_) {}
30
+
31
+ // ✅ إيقاف Session Keeper
32
+ try { if (ctx._sessionKeeper) ctx._sessionKeeper.stop(); } catch (_) {}
33
+
34
+ // ✅ تنظيف Timers
35
+ try { if (ctx._autoCycleTimer) { clearTimeout(ctx._autoCycleTimer); ctx._autoCycleTimer = null; } } catch (_) {}
36
+ try { if (ctx._reconnectTimer) { clearTimeout(ctx._reconnectTimer); ctx._reconnectTimer = null; } } catch (_) {}
37
+ try { if (ctx._rTimeout) { clearTimeout(ctx._rTimeout); ctx._rTimeout = null; } } catch (_) {}
38
+
39
+ try { ctx._ending = true; ctx._cycling = false; } catch (_) {}
40
+
41
+ // ✅ إيقاف MQTT
42
+ try {
43
+ if (ctx.mqttClient) {
44
+ ctx.mqttClient.removeAllListeners();
45
+ if (ctx.mqttClient.connected) ctx.mqttClient.end(true);
46
+ }
47
+ } catch (_) {}
48
+ ctx.mqttClient = undefined;
49
+ ctx.loggedIn = false;
50
+
51
+ // ✅ تنظيف الذاكرة
52
+ try { if (ctx.tasks instanceof Map) ctx.tasks.clear(); } catch (_) {}
53
+ try {
54
+ (ctx._autoSaveInterval || []).forEach(i => { try { clearInterval(i); } catch (_) {} });
55
+ ctx._autoSaveInterval = [];
56
+ } catch (_) {}
57
+ try {
58
+ if (ctx._scheduler?.destroy) { ctx._scheduler.destroy(); ctx._scheduler = undefined; }
59
+ } catch (_) {}
60
+
61
+ delete process.env.AymanFcaOnline;
62
+
63
+ const msg = detail || reason;
64
+ logger(`[ AYMAN ] auth → ${reason}: ${msg}`, "error");
65
+
66
+ if (typeof globalCallback === "function") {
67
+ try {
68
+ globalCallback({
69
+ type: "account_inactive",
70
+ reason,
71
+ error: msg,
72
+ timestamp: Date.now()
73
+ }, null);
74
+ } catch (e) {
75
+ logger(`[ AYMAN ] emitAuth callback خطأ: ${e?.message || e}`, "error");
76
+ }
77
+ }
78
+ };
79
+ };
@@ -0,0 +1,170 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — GetSeqID + Local Session Recovery
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ //
5
+ // 🔑 استعادة الجلسة محلياً 100% بدون أي سيرفر خارجي
6
+ // مراحل الاستعادة عند انتهاء الجلسة:
7
+ // 1. تجديد fb_dtsg من Facebook مباشرة
8
+ // 2. تجديد الجلسة من www.facebook.com
9
+ // 3. تجديد من m.facebook.com (النسخة المحمولة)
10
+ // 4. تجديد من home.php
11
+ // 5. إشعار البوت بالفشل + حفظ AppState
12
+ // ============================================================
13
+ "use strict";
14
+
15
+ const { getType } = require("../../../utils/format");
16
+ const { parseAndCheckLogin, saveCookies } = require("../../../utils/client");
17
+ const { get } = require("../../../utils/request");
18
+ const { saveAppStateAtomic } = require("../../../utils/sessionKeeper");
19
+ const path = require("path");
20
+ const fs = require("fs");
21
+
22
+ const MAX_RETRIES = 4;
23
+ const RETRY_DELAY = 2500;
24
+
25
+ const isValidUID = uid =>
26
+ uid && uid !== "0" && /^\d+$/.test(uid) && parseInt(uid, 10) > 0;
27
+
28
+ const extractUID = html => {
29
+ const s = typeof html === "string" ? html : String(html || "");
30
+ return s.match(/"USER_ID"\s*:\s*"(\d+)"/)?.[1] ||
31
+ s.match(/\["CurrentUserInitialData",\[\],\{".*?"USER_ID":"(\d+)".*?\},\d+\]/)?.[1];
32
+ };
33
+
34
+ // ✅ تجديد fb_dtsg من Facebook مباشرة
35
+ async function refreshFbDtsgLocal(ctx, logger) {
36
+ try {
37
+ const { getFrom } = require("../../../utils/constants");
38
+ const res = await get("https://www.facebook.com/", ctx.jar, null, ctx.globalOptions, ctx);
39
+ const html = res?.data || "";
40
+ const dtsg = getFrom(html, '["DTSGInitData",[],{"token":"', '","');
41
+ const jaz = getFrom(html, "jazoest=", '",');
42
+ if (dtsg) {
43
+ ctx.fb_dtsg = dtsg;
44
+ if (jaz) ctx.jazoest = jaz;
45
+ logger("[ AYMAN ] fb_dtsg مجدد ✅", "info");
46
+ return true;
47
+ }
48
+ } catch (_) {}
49
+ return false;
50
+ }
51
+
52
+ // ✅ تجديد الجلسة محلياً من عدة نقاط
53
+ async function refreshSessionLocally(ctx, logger) {
54
+ const urls = [
55
+ "https://www.facebook.com/",
56
+ "https://m.facebook.com/",
57
+ "https://www.facebook.com/home.php",
58
+ "https://m.facebook.com/home.php"
59
+ ];
60
+
61
+ for (const url of urls) {
62
+ try {
63
+ const res = await get(url, ctx.jar, null, ctx.globalOptions, ctx).then(saveCookies(ctx.jar));
64
+ const html = res?.data || "";
65
+ const uid = extractUID(html);
66
+ if (isValidUID(uid)) {
67
+ ctx.loggedIn = true;
68
+ ctx.userID = uid;
69
+ logger(`[ AYMAN ] جلسة مستعادة ✅ من ${url} | UID: ${uid}`, "info");
70
+ return true;
71
+ }
72
+ } catch (_) {}
73
+ }
74
+
75
+ logger("[ AYMAN ] فشل استعادة الجلسة من كل المصادر", "warn");
76
+ return false;
77
+ }
78
+
79
+ // ✅ حفظ AppState بعد نجاح الاستعادة
80
+ function saveAfterRecovery(ctx, logger) {
81
+ try {
82
+ const { getAppState } = require("../../../utils/cookies");
83
+ const p = path.join(process.cwd(), "appstate.json");
84
+ const state = getAppState(ctx.jar);
85
+ if (state && state.length > 0) {
86
+ saveAppStateAtomic(p, state);
87
+ logger("[ AYMAN ] AppState محفوظ بعد الاستعادة ✅", "info");
88
+ }
89
+ } catch (_) {}
90
+ }
91
+
92
+ module.exports = function createGetSeqID(deps) {
93
+ const { listenMqtt, logger, emitAuth } = deps;
94
+
95
+ return function getSeqID(defaultFuncs, api, ctx, globalCallback, form, retryCount = 0) {
96
+ ctx.t_mqttCalled = false;
97
+
98
+ return defaultFuncs
99
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
100
+ .then(parseAndCheckLogin(ctx, defaultFuncs))
101
+ .then(async resData => {
102
+ if (getType(resData) !== "Array") {
103
+ const errMsg = resData?.error || resData?.message || "";
104
+ if (/Not logged in|login|blocked|401|403|checkpoint/i.test(errMsg)) {
105
+ throw { error: "Not logged in", res: resData };
106
+ }
107
+ throw { error: "Not logged in", res: resData };
108
+ }
109
+ if (!Array.isArray(resData) || !resData.length) return;
110
+ const last = resData[resData.length - 1];
111
+ if (last?.successful_results === 0) return;
112
+
113
+ const syncSeqId = resData[0]?.o0?.data?.viewer?.message_threads?.sync_sequence_id;
114
+ if (syncSeqId) {
115
+ ctx.lastSeqId = syncSeqId;
116
+ ctx._reconnectAttempts = 0;
117
+ logger("[ AYMAN ] SeqID ✅ — بدء الاستماع", "info");
118
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
119
+ } else {
120
+ throw { error: "getSeqId: no sync_sequence_id found." };
121
+ }
122
+ })
123
+ .catch(async err => {
124
+ const msg = (err?.error) || (err?.message) || String(err || "");
125
+ const isAuth = /Not logged in|no sync_sequence_id|blocked the login|401|403/i.test(msg);
126
+
127
+ if (isAuth) {
128
+ // ── المرحلة 1: retry مع تجديد fb_dtsg ──────────────
129
+ if (retryCount < MAX_RETRIES) {
130
+ const d = RETRY_DELAY * (retryCount + 1);
131
+ logger(`[ AYMAN ] SeqID retry ${retryCount + 1}/${MAX_RETRIES} بعد ${d}ms`, "warn");
132
+ await new Promise(r => setTimeout(r, d));
133
+
134
+ if (retryCount === 0) await refreshFbDtsgLocal(ctx, logger);
135
+ if (retryCount === 1) {
136
+ const ok = await refreshSessionLocally(ctx, logger);
137
+ if (ok) saveAfterRecovery(ctx, logger);
138
+ }
139
+ if (retryCount >= 2) {
140
+ await refreshFbDtsgLocal(ctx, logger);
141
+ const ok = await refreshSessionLocally(ctx, logger);
142
+ if (ok) saveAfterRecovery(ctx, logger);
143
+ }
144
+
145
+ return getSeqID(defaultFuncs, api, ctx, globalCallback, form, retryCount + 1);
146
+ }
147
+
148
+ // ── المرحلة 2: آخر محاولة شاملة ────────────────────
149
+ logger("[ AYMAN ] كل المحاولات فشلت — محاولة أخيرة...", "warn");
150
+ const ok = await refreshSessionLocally(ctx, logger);
151
+ if (ok) {
152
+ await refreshFbDtsgLocal(ctx, logger);
153
+ saveAfterRecovery(ctx, logger);
154
+ await new Promise(r => setTimeout(r, 3000));
155
+ return getSeqID(defaultFuncs, api, ctx, globalCallback, form, 0);
156
+ }
157
+
158
+ // ── فشل كل شيء ───────────────────────────────────────
159
+ // احفظ AppState قبل الإيقاف
160
+ saveAfterRecovery(ctx, logger);
161
+
162
+ if (/blocked/i.test(msg)) return emitAuth(ctx, api, globalCallback, "login_blocked", msg);
163
+ return emitAuth(ctx, api, globalCallback, "not_logged_in", msg);
164
+ }
165
+
166
+ logger(`[ AYMAN ] getSeqID خطأ: ${msg}`, "error");
167
+ return emitAuth(ctx, api, globalCallback, "auth_error", msg);
168
+ });
169
+ };
170
+ };
@@ -0,0 +1,27 @@
1
+ // ============================================================
2
+ // AYMAN-FCA v2.0 — Get Task Response Data
3
+ // © 2025 Ayman. All Rights Reserved.
4
+ // ============================================================
5
+ "use strict";
6
+
7
+ module.exports = function getTaskResponseData(taskType, payload) {
8
+ try {
9
+ switch (taskType) {
10
+ case "send_message_mqtt":
11
+ return {
12
+ type: taskType,
13
+ threadID: payload.step[1][2][2][1][2],
14
+ messageID: payload.step[1][2][2][1][3],
15
+ payload: payload.step[1][2]
16
+ };
17
+ case "set_message_reaction":
18
+ return { mid: payload.step[1][2][2][1][4] };
19
+ case "edit_message":
20
+ return { mid: payload.step[1][2][2][1][2] };
21
+ default:
22
+ return null;
23
+ }
24
+ } catch (_) {
25
+ return null;
26
+ }
27
+ };