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.
- package/README.md +81 -0
- package/func/checkUpdate.js +13 -0
- package/func/logAdapter.js +30 -0
- package/func/logger.js +66 -0
- package/index.d.ts +751 -0
- package/index.js +15 -0
- package/module/config.js +38 -0
- package/module/login.js +111 -0
- package/module/loginHelper.js +1296 -0
- package/module/options.js +37 -0
- package/package.json +78 -0
- package/src/api/action/addExternalModule.js +19 -0
- package/src/api/action/changeAvatar.js +137 -0
- package/src/api/action/changeBio.js +48 -0
- package/src/api/action/enableAutoSaveAppState.js +72 -0
- package/src/api/action/getCurrentUserID.js +11 -0
- package/src/api/action/handleFriendRequest.js +33 -0
- package/src/api/action/logout.js +76 -0
- package/src/api/action/refreshFb_dtsg.js +62 -0
- package/src/api/action/setPostReaction.js +106 -0
- package/src/api/action/story.js +118 -0
- package/src/api/action/unfriend.js +30 -0
- package/src/api/http/httpGet.js +28 -0
- package/src/api/http/httpPost.js +32 -0
- package/src/api/http/postFormData.js +23 -0
- package/src/api/messaging/J +1 -0
- package/src/api/messaging/addUserToGroup.js +70 -0
- package/src/api/messaging/changeAdminStatus.js +72 -0
- package/src/api/messaging/changeArchivedStatus.js +31 -0
- package/src/api/messaging/changeBlockedStatus.js +27 -0
- package/src/api/messaging/changeGroupImage.js +91 -0
- package/src/api/messaging/changeNickname.js +70 -0
- package/src/api/messaging/changeThreadColor.js +44 -0
- package/src/api/messaging/changeThreadEmoji.js +111 -0
- package/src/api/messaging/createNewGroup.js +50 -0
- package/src/api/messaging/createPoll.js +52 -0
- package/src/api/messaging/createThemeAI.js +98 -0
- package/src/api/messaging/deleteMessage.js +73 -0
- package/src/api/messaging/deleteThread.js +29 -0
- package/src/api/messaging/editMessage.js +67 -0
- package/src/api/messaging/forwardAttachment.js +55 -0
- package/src/api/messaging/forwardMessage.js +73 -0
- package/src/api/messaging/getEmojiUrl.js +29 -0
- package/src/api/messaging/getFriendsList.js +82 -0
- package/src/api/messaging/getMessage.js +829 -0
- package/src/api/messaging/getThemePictures.js +62 -0
- package/src/api/messaging/groupActions.js +119 -0
- package/src/api/messaging/handleMessageRequest.js +31 -0
- package/src/api/messaging/markAsDelivered.js +31 -0
- package/src/api/messaging/markAsRead.js +88 -0
- package/src/api/messaging/markAsReadAll.js +28 -0
- package/src/api/messaging/markAsSeen.js +30 -0
- package/src/api/messaging/muteThread.js +27 -0
- package/src/api/messaging/notes.js +101 -0
- package/src/api/messaging/removeUserFromGroup.js +51 -0
- package/src/api/messaging/resolvePhotoUrl.js +29 -0
- package/src/api/messaging/scheduler.js +100 -0
- package/src/api/messaging/searchForThread.js +32 -0
- package/src/api/messaging/sendMessage.js +270 -0
- package/src/api/messaging/sendTypingIndicator.js +62 -0
- package/src/api/messaging/setMessageReaction.js +91 -0
- package/src/api/messaging/setTitle.js +86 -0
- package/src/api/messaging/shareContact.js +47 -0
- package/src/api/messaging/threadColors.js +128 -0
- package/src/api/messaging/unsendMessage.js +73 -0
- package/src/api/messaging/uploadAttachment.js +492 -0
- package/src/api/socket/core/connectMqtt.js +259 -0
- package/src/api/socket/core/emitAuth.js +79 -0
- package/src/api/socket/core/getSeqID.js +170 -0
- package/src/api/socket/core/getTaskResponseData.js +27 -0
- package/src/api/socket/core/parseDelta.js +377 -0
- package/src/api/socket/detail/buildStream.js +215 -0
- package/src/api/socket/detail/constants.js +29 -0
- package/src/api/socket/listenMqtt.js +377 -0
- package/src/api/socket/middleware/index.js +80 -0
- package/src/api/threads/getThreadHistory.js +664 -0
- package/src/api/threads/getThreadInfo.js +296 -0
- package/src/api/threads/getThreadList.js +293 -0
- package/src/api/threads/getThreadPictures.js +43 -0
- package/src/api/user/J +1 -0
- package/src/api/user/getUserID.js +48 -0
- package/src/api/user/getUserInfo.js +402 -0
- package/src/api/user/getUserInfoV2.js +134 -0
- package/src/core/sendReqMqtt.js +69 -0
- package/src/database/helpers.js +36 -0
- package/src/database/models/index.js +55 -0
- package/src/database/models/thread.js +44 -0
- package/src/database/models/user.js +39 -0
- package/src/database/threadData.js +92 -0
- package/src/database/userData.js +88 -0
- package/src/remote/remoteClient.js +71 -0
- package/src/utils/broadcast.js +62 -0
- package/src/utils/client.js +10 -0
- package/src/utils/constants.js +53 -0
- package/src/utils/cookies.js +73 -0
- package/src/utils/format/attachment.js +357 -0
- package/src/utils/format/cookie.js +9 -0
- package/src/utils/format/date.js +50 -0
- package/src/utils/format/decode.js +44 -0
- package/src/utils/format/delta.js +194 -0
- package/src/utils/format/ids.js +64 -0
- package/src/utils/format/index.js +64 -0
- package/src/utils/format/message.js +88 -0
- package/src/utils/format/presence.js +132 -0
- package/src/utils/format/readTyp.js +44 -0
- package/src/utils/format/thread.js +42 -0
- package/src/utils/format/utils.js +141 -0
- package/src/utils/headers.js +96 -0
- package/src/utils/loginParser/autoLogin.js +125 -0
- package/src/utils/loginParser/helpers.js +43 -0
- package/src/utils/loginParser/index.js +10 -0
- package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
- package/src/utils/loginParser/textUtils.js +28 -0
- package/src/utils/request/H +1 -0
- package/src/utils/request/client.js +33 -0
- package/src/utils/request/config.js +25 -0
- package/src/utils/request/defaults.js +40 -0
- package/src/utils/request/helpers.js +31 -0
- package/src/utils/request/index.js +12 -0
- package/src/utils/request/methods.js +92 -0
- package/src/utils/request/proxy.js +23 -0
- package/src/utils/request/retry.js +87 -0
- package/src/utils/request/sanitize.js +41 -0
- 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
|
+
};
|