@dongdev/fca-unofficial 3.0.29 → 3.0.30
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/CHANGELOG.md +229 -132
- package/DOCS.md +82 -3
- package/README.md +524 -632
- package/func/logAdapter.js +33 -0
- package/index.d.ts +6 -0
- package/module/config.js +11 -1
- package/module/loginHelper.js +63 -4
- package/package.json +88 -81
- package/src/api/action/changeAvatar.js +1 -1
- package/src/api/action/changeBio.js +1 -1
- package/src/api/action/handleFriendRequest.js +1 -1
- package/src/api/action/logout.js +1 -1
- package/src/api/action/refreshFb_dtsg.js +1 -1
- package/src/api/action/setPostReaction.js +1 -1
- package/src/api/action/unfriend.js +1 -1
- package/src/api/http/postFormData.js +1 -1
- package/src/api/messaging/changeArchivedStatus.js +1 -1
- package/src/api/messaging/changeBlockedStatus.js +1 -1
- package/src/api/messaging/changeGroupImage.js +1 -1
- package/src/api/messaging/changeNickname.js +1 -1
- package/src/api/messaging/changeThreadEmoji.js +1 -1
- package/src/api/messaging/createNewGroup.js +1 -1
- package/src/api/messaging/createThemeAI.js +1 -1
- package/src/api/messaging/deleteMessage.js +1 -1
- package/src/api/messaging/deleteThread.js +1 -1
- package/src/api/messaging/getFriendsList.js +1 -1
- package/src/api/messaging/getMessage.js +1 -1
- package/src/api/messaging/getThemePictures.js +1 -1
- package/src/api/messaging/handleMessageRequest.js +1 -1
- package/src/api/messaging/markAsDelivered.js +1 -1
- package/src/api/messaging/markAsRead.js +1 -1
- package/src/api/messaging/markAsReadAll.js +1 -1
- package/src/api/messaging/markAsSeen.js +1 -1
- package/src/api/messaging/muteThread.js +1 -1
- package/src/api/messaging/resolvePhotoUrl.js +1 -1
- package/src/api/messaging/sendMessage.js +1 -1
- package/src/api/messaging/setTitle.js +1 -1
- package/src/api/messaging/unsendMessage.js +1 -1
- package/src/api/messaging/uploadAttachment.js +1 -1
- package/src/api/socket/core/connectMqtt.js +16 -8
- package/src/api/socket/core/emitAuth.js +4 -0
- package/src/api/socket/core/getSeqID.js +6 -8
- package/src/api/socket/core/getTaskResponseData.js +3 -0
- package/src/api/socket/core/parseDelta.js +9 -0
- package/src/api/socket/detail/buildStream.js +11 -4
- package/src/api/socket/detail/constants.js +4 -0
- package/src/api/socket/listenMqtt.js +11 -5
- package/src/api/threads/getThreadHistory.js +1 -1
- package/src/api/threads/getThreadInfo.js +245 -388
- package/src/api/threads/getThreadList.js +1 -1
- package/src/api/threads/getThreadPictures.js +1 -1
- package/src/api/users/getUserID.js +1 -1
- package/src/api/users/getUserInfo.js +80 -8
- package/src/database/models/thread.js +5 -0
- package/src/remote/remoteClient.js +123 -0
- package/src/utils/broadcast.js +51 -0
- package/src/utils/loginParser.js +19 -1
- package/src/utils/request.js +33 -6
- package/.gitattributes +0 -2
- package/Fca_Database/database.sqlite +0 -0
- package/LICENSE-MIT +0 -21
|
@@ -8,7 +8,7 @@ const fs = require("fs");
|
|
|
8
8
|
const path = require("path");
|
|
9
9
|
const stream = require("stream");
|
|
10
10
|
const { URL } = require("url");
|
|
11
|
-
const log = require("
|
|
11
|
+
const log = require("../../../func/logAdapter");
|
|
12
12
|
|
|
13
13
|
let http = null;
|
|
14
14
|
let cookieJar = new CookieJar();
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MQTT/WebSocket listener for Facebook Messenger real-time events.
|
|
4
|
+
* Connects to edge-chat.facebook.com, subscribes to topics, parses deltas and typing/presence.
|
|
5
|
+
*/
|
|
2
6
|
const { formatID } = require("../../../utils/format");
|
|
7
|
+
|
|
8
|
+
const DEFAULT_RECONNECT_DELAY_MS = 2000;
|
|
9
|
+
const T_MS_WAIT_TIMEOUT_MS = 5000;
|
|
10
|
+
|
|
3
11
|
module.exports = function createListenMqtt(deps) {
|
|
4
12
|
const { WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy,
|
|
5
13
|
topics, parseDelta, getTaskResponseData, logger, emitAuth
|
|
@@ -8,7 +16,7 @@ module.exports = function createListenMqtt(deps) {
|
|
|
8
16
|
return function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
9
17
|
|
|
10
18
|
function scheduleReconnect(delayMs) {
|
|
11
|
-
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) ||
|
|
19
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || DEFAULT_RECONNECT_DELAY_MS;
|
|
12
20
|
const ms = typeof delayMs === "number" ? delayMs : d;
|
|
13
21
|
if (ctx._reconnectTimer) {
|
|
14
22
|
logger("mqtt reconnect already scheduled", "warn");
|
|
@@ -113,9 +121,8 @@ module.exports = function createListenMqtt(deps) {
|
|
|
113
121
|
if (ctx._ending || ctx._cycling) return;
|
|
114
122
|
|
|
115
123
|
if (ctx.globalOptions.autoReconnect && !ctx._ending) {
|
|
116
|
-
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) ||
|
|
124
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || DEFAULT_RECONNECT_DELAY_MS;
|
|
117
125
|
logger(`mqtt autoReconnect listenMqtt() in ${d}ms`, "warn");
|
|
118
|
-
// Use scheduleReconnect to prevent multiple reconnections
|
|
119
126
|
scheduleReconnect(d);
|
|
120
127
|
} else {
|
|
121
128
|
globalCallback({ type: "stop_listen", error: msg || "Connection refused" }, null);
|
|
@@ -124,7 +131,7 @@ module.exports = function createListenMqtt(deps) {
|
|
|
124
131
|
|
|
125
132
|
mqttClient.on("connect", function () {
|
|
126
133
|
if (process.env.OnStatus === undefined) {
|
|
127
|
-
logger("fca-
|
|
134
|
+
logger("fca-unofficial", "info");
|
|
128
135
|
process.env.OnStatus = true;
|
|
129
136
|
}
|
|
130
137
|
ctx._cycling = false;
|
|
@@ -141,7 +148,7 @@ module.exports = function createListenMqtt(deps) {
|
|
|
141
148
|
mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
|
|
142
149
|
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
|
|
143
150
|
mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
|
|
144
|
-
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) ||
|
|
151
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || DEFAULT_RECONNECT_DELAY_MS;
|
|
145
152
|
let rTimeout = setTimeout(function () {
|
|
146
153
|
rTimeout = null;
|
|
147
154
|
if (ctx._ending) {
|
|
@@ -155,7 +162,7 @@ module.exports = function createListenMqtt(deps) {
|
|
|
155
162
|
}
|
|
156
163
|
} catch (_) { }
|
|
157
164
|
scheduleReconnect(d);
|
|
158
|
-
},
|
|
165
|
+
}, T_MS_WAIT_TIMEOUT_MS);
|
|
159
166
|
|
|
160
167
|
// Store timeout reference for cleanup
|
|
161
168
|
ctx._rTimeout = rTimeout;
|
|
@@ -216,8 +223,9 @@ module.exports = function createListenMqtt(deps) {
|
|
|
216
223
|
} else if (topic === "/ls_resp") {
|
|
217
224
|
const parsedPayload = JSON.parse(jsonMessage.payload);
|
|
218
225
|
const reqID = jsonMessage.request_id;
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
const tasks = ctx.tasks;
|
|
227
|
+
if (tasks && tasks instanceof Map && tasks.has(reqID)) {
|
|
228
|
+
const taskData = tasks.get(reqID);
|
|
221
229
|
const { type: taskType, callback: taskCallback } = taskData;
|
|
222
230
|
const taskRespData = getTaskResponseData(taskType, parsedPayload);
|
|
223
231
|
if (taskRespData == null) taskCallback("error", null);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Emits account_inactive to the global callback and cleans up MQTT/timers/scheduler.
|
|
4
|
+
* Used when login is invalid, blocked, or session is lost.
|
|
5
|
+
*/
|
|
2
6
|
module.exports = function createEmitAuth({ logger }) {
|
|
3
7
|
return function emitAuth(ctx, api, globalCallback, reason, detail) {
|
|
4
8
|
// Clean up all timers
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fetches MQTT sync sequence ID from GraphQL and starts listenMqtt.
|
|
4
|
+
* Handles retries and auto re-login via fca-config.json when session expires.
|
|
5
|
+
*/
|
|
2
6
|
const { getType } = require("../../../utils/format");
|
|
3
7
|
const { parseAndCheckLogin, saveCookies } = require("../../../utils/client");
|
|
4
8
|
const path = require("path");
|
|
9
|
+
const loginHelper = require("../../../../module/loginHelper");
|
|
5
10
|
|
|
6
|
-
// Load config for auto-login credentials
|
|
7
11
|
function getConfig() {
|
|
8
12
|
try {
|
|
9
13
|
const configPath = path.join(process.cwd(), "fca-config.json");
|
|
@@ -51,7 +55,6 @@ async function tryAutoLogin(logger, config, ctx, defaultFuncs) {
|
|
|
51
55
|
logger("getSeqID: attempting auto re-login via API...", "warn");
|
|
52
56
|
|
|
53
57
|
try {
|
|
54
|
-
const loginHelper = require("../../../../module/loginHelper");
|
|
55
58
|
const result = await loginHelper.tokensViaAPI(
|
|
56
59
|
email,
|
|
57
60
|
password,
|
|
@@ -60,14 +63,9 @@ async function tryAutoLogin(logger, config, ctx, defaultFuncs) {
|
|
|
60
63
|
);
|
|
61
64
|
|
|
62
65
|
if (result && result.status) {
|
|
63
|
-
// Handle cookies - can be array, cookie string header, or both
|
|
64
|
-
// Import normalizeCookieHeaderString from loginHelper
|
|
65
|
-
const loginHelper = require("../../../../module/loginHelper");
|
|
66
66
|
const normalizeCookieHeaderString = loginHelper.normalizeCookieHeaderString;
|
|
67
|
-
|
|
68
67
|
let cookiePairs = [];
|
|
69
|
-
|
|
70
|
-
// If cookies is a string (cookie header format), parse it
|
|
68
|
+
|
|
71
69
|
if (typeof result.cookies === "string") {
|
|
72
70
|
cookiePairs = normalizeCookieHeaderString(result.cookies);
|
|
73
71
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parses MQTT delta payloads into normalized events: NewMessage, ClientPayload (reactions, unsend, reply), read receipts, etc.
|
|
4
|
+
*/
|
|
2
5
|
const { formatDeltaEvent, formatMessage, _formatAttachment, formatDeltaMessage, formatDeltaReadReceipt, formatID, getType, decodeClientPayload, getMentionsFromDeltaMessage } = require("../../../utils/format");
|
|
3
6
|
const logger = require("../../../../func/logger");
|
|
7
|
+
|
|
4
8
|
module.exports = function createParseDelta(deps) {
|
|
5
9
|
const { parseAndCheckLogin } = deps;
|
|
6
10
|
return function parseDelta(defaultFuncs, api, ctx, globalCallback, { delta }) {
|
|
@@ -15,6 +19,11 @@ module.exports = function createParseDelta(deps) {
|
|
|
15
19
|
}
|
|
16
20
|
if (fmtMsg) {
|
|
17
21
|
if (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID) return;
|
|
22
|
+
if (typeof ctx._updateThreadFromMessage === "function") {
|
|
23
|
+
try {
|
|
24
|
+
ctx._updateThreadFromMessage(fmtMsg);
|
|
25
|
+
} catch { }
|
|
26
|
+
}
|
|
18
27
|
globalCallback(null, fmtMsg);
|
|
19
28
|
}
|
|
20
29
|
} else {
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Builds a duplex stream over WebSocket for MQTT: proxy for writes, PassThrough for reads.
|
|
4
|
+
* Handles ping/pong, liveness timeout, and clean shutdown.
|
|
5
|
+
*/
|
|
3
6
|
const { Writable, PassThrough } = require("stream");
|
|
4
7
|
const Duplexify = require("duplexify");
|
|
5
8
|
|
|
9
|
+
const PING_INTERVAL_MS = 30000;
|
|
10
|
+
const LIVENESS_CHECK_MS = 10000;
|
|
11
|
+
const LIVENESS_MAX_IDLE_MS = 65000;
|
|
12
|
+
|
|
6
13
|
function buildProxy() {
|
|
7
14
|
let target = null;
|
|
8
15
|
let ended = false;
|
|
@@ -94,13 +101,13 @@ function buildStream(options, WebSocket, Proxy) {
|
|
|
94
101
|
} else {
|
|
95
102
|
try { ws.send("ping"); } catch { }
|
|
96
103
|
}
|
|
97
|
-
},
|
|
104
|
+
}, PING_INTERVAL_MS);
|
|
98
105
|
livenessTimer = setInterval(() => {
|
|
99
106
|
if (!ws || ws.readyState !== 1) return;
|
|
100
|
-
if (Date.now() - lastActivity >
|
|
107
|
+
if (Date.now() - lastActivity > LIVENESS_MAX_IDLE_MS) {
|
|
101
108
|
try { typeof ws.terminate === "function" ? ws.terminate() : ws.close(); } catch { }
|
|
102
109
|
}
|
|
103
|
-
},
|
|
110
|
+
}, LIVENESS_CHECK_MS);
|
|
104
111
|
};
|
|
105
112
|
|
|
106
113
|
const onMessage = data => {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Socket/MQTT entry: wires getSeqID -> listenMqtt, middleware, stopListening, and post guard.
|
|
4
|
+
* Exported as the function that attaches listenMqtt + middleware API to the login context.
|
|
5
|
+
*/
|
|
2
6
|
const mqtt = require("mqtt");
|
|
3
7
|
const WebSocket = require("ws");
|
|
4
8
|
const HttpsProxyAgent = require("https-proxy-agent");
|
|
@@ -13,15 +17,17 @@ const createGetSeqID = require("./core/getSeqID");
|
|
|
13
17
|
const getTaskResponseData = require("./core/getTaskResponseData");
|
|
14
18
|
const createEmitAuth = require("./core/emitAuth");
|
|
15
19
|
const createMiddlewareSystem = require("./middleware");
|
|
20
|
+
|
|
21
|
+
const CYCLE_MS_DEFAULT = 60 * 60 * 1000;
|
|
22
|
+
const RECONNECT_DELAY_MS_DEFAULT = 2000;
|
|
23
|
+
const UNSUB_ALL_TIMEOUT_MS = 5000;
|
|
24
|
+
|
|
16
25
|
const parseDelta = createParseDelta({ parseAndCheckLogin });
|
|
17
|
-
// Create emitAuth first so it can be injected into both factories
|
|
18
26
|
const emitAuth = createEmitAuth({ logger });
|
|
19
|
-
// Pass emitAuth into connectMqtt so errors there can signal auth state
|
|
20
27
|
const listenMqtt = createListenMqtt({ WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy, topics, parseDelta, getTaskResponseData, logger, emitAuth });
|
|
21
|
-
// Inject emitAuth into getSeqID so its catch handler can notify properly
|
|
22
28
|
const getSeqIDFactory = createGetSeqID({ parseAndCheckLogin, listenMqtt, logger, emitAuth });
|
|
23
29
|
|
|
24
|
-
const MQTT_DEFAULTS = { cycleMs:
|
|
30
|
+
const MQTT_DEFAULTS = { cycleMs: CYCLE_MS_DEFAULT, reconnectDelayMs: RECONNECT_DELAY_MS_DEFAULT, autoReconnect: true, reconnectAfterStop: false };
|
|
25
31
|
function mqttConf(ctx, overrides) {
|
|
26
32
|
ctx._mqttOpt = Object.assign({}, MQTT_DEFAULTS, ctx._mqttOpt || {}, overrides || {});
|
|
27
33
|
if (typeof ctx._mqttOpt.autoReconnect === "boolean") ctx.globalOptions.autoReconnect = ctx._mqttOpt.autoReconnect;
|
|
@@ -126,7 +132,7 @@ module.exports = function (defaultFuncs, api, ctx, opts) {
|
|
|
126
132
|
logger("unsubAll timeout, proceeding anyway", "warn");
|
|
127
133
|
if (cb) cb();
|
|
128
134
|
}
|
|
129
|
-
},
|
|
135
|
+
}, UNSUB_ALL_TIMEOUT_MS);
|
|
130
136
|
|
|
131
137
|
topics.forEach(t => {
|
|
132
138
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const log = require("
|
|
3
|
+
const log = require("../../../func/logAdapter");
|
|
4
4
|
const { parseAndCheckLogin } = require("../../utils/client");
|
|
5
5
|
const { getAdminTextMessageType } = require("../../utils/format");
|
|
6
6
|
function getExtension(original_extension, filename = "") {
|