stfca 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/LICENSE-MIT +4 -0
- package/README.md +325 -0
- package/index.d.ts +615 -0
- package/index.js +1 -0
- package/module/config.js +33 -0
- package/module/login.js +48 -0
- package/module/loginHelper.js +722 -0
- package/module/options.js +44 -0
- package/package.json +69 -0
- package/src/api/action/addExternalModule.js +25 -0
- package/src/api/action/changeAvatar.js +137 -0
- package/src/api/action/changeBio.js +75 -0
- package/src/api/action/getCurrentUserID.js +7 -0
- package/src/api/action/handleFriendRequest.js +57 -0
- package/src/api/action/logout.js +76 -0
- package/src/api/action/refreshFb_dtsg.js +71 -0
- package/src/api/action/setPostReaction.js +106 -0
- package/src/api/action/unfriend.js +54 -0
- package/src/api/http/httpGet.js +46 -0
- package/src/api/http/httpPost.js +52 -0
- package/src/api/http/postFormData.js +47 -0
- package/src/api/messaging/addUserToGroup.js +68 -0
- package/src/api/messaging/changeAdminStatus.js +122 -0
- package/src/api/messaging/changeArchivedStatus.js +55 -0
- package/src/api/messaging/changeBlockedStatus.js +48 -0
- package/src/api/messaging/changeGroupImage.js +90 -0
- package/src/api/messaging/changeNickname.js +70 -0
- package/src/api/messaging/changeThreadColor.js +79 -0
- package/src/api/messaging/changeThreadEmoji.js +106 -0
- package/src/api/messaging/createNewGroup.js +88 -0
- package/src/api/messaging/createPoll.js +43 -0
- package/src/api/messaging/deleteMessage.js +56 -0
- package/src/api/messaging/deleteThread.js +56 -0
- package/src/api/messaging/editMessage.js +68 -0
- package/src/api/messaging/forwardAttachment.js +51 -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/handleMessageRequest.js +65 -0
- package/src/api/messaging/markAsDelivered.js +57 -0
- package/src/api/messaging/markAsRead.js +88 -0
- package/src/api/messaging/markAsReadAll.js +49 -0
- package/src/api/messaging/markAsSeen.js +61 -0
- package/src/api/messaging/muteThread.js +50 -0
- package/src/api/messaging/removeUserFromGroup.js +105 -0
- package/src/api/messaging/resolvePhotoUrl.js +43 -0
- package/src/api/messaging/searchForThread.js +52 -0
- package/src/api/messaging/sendMessage.js +379 -0
- package/src/api/messaging/sendMessageMqtt.js +323 -0
- package/src/api/messaging/sendTypingIndicator.js +67 -0
- package/src/api/messaging/setMessageReaction.js +75 -0
- package/src/api/messaging/setTitle.js +119 -0
- package/src/api/messaging/shareContact.js +49 -0
- package/src/api/messaging/threadColors.js +128 -0
- package/src/api/messaging/unsendMessage.js +81 -0
- package/src/api/messaging/uploadAttachment.js +95 -0
- package/src/api/socket/core/connectMqtt.js +179 -0
- package/src/api/socket/core/getSeqID.js +25 -0
- package/src/api/socket/core/getTaskResponseData.js +22 -0
- package/src/api/socket/core/markDelivery.js +12 -0
- package/src/api/socket/core/parseDelta.js +351 -0
- package/src/api/socket/detail/buildStream.js +208 -0
- package/src/api/socket/detail/constants.js +24 -0
- package/src/api/socket/listenMqtt.js +133 -0
- package/src/api/threads/getThreadHistory.js +664 -0
- package/src/api/threads/getThreadInfo.js +358 -0
- package/src/api/threads/getThreadList.js +248 -0
- package/src/api/threads/getThreadPictures.js +78 -0
- package/src/api/users/getUserID.js +65 -0
- package/src/api/users/getUserInfo.js +319 -0
- package/src/api/users/getUserInfoV2.js +133 -0
- package/src/core/sendReqMqtt.js +63 -0
- package/src/database/models/index.js +49 -0
- package/src/database/models/thread.js +31 -0
- package/src/database/models/user.js +32 -0
- package/src/database/threadData.js +98 -0
- package/src/database/userData.js +89 -0
- package/src/utils/client.js +214 -0
- package/src/utils/constants.js +23 -0
- package/src/utils/format.js +1111 -0
- package/src/utils/headers.js +41 -0
- package/src/utils/request.js +215 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { Writable, PassThrough } = require("stream");
|
|
4
|
+
const Duplexify = require("duplexify");
|
|
5
|
+
|
|
6
|
+
function buildProxy() {
|
|
7
|
+
let target = null;
|
|
8
|
+
let ended = false;
|
|
9
|
+
const Proxy = new Writable({
|
|
10
|
+
autoDestroy: true,
|
|
11
|
+
write(chunk, enc, cb) {
|
|
12
|
+
if (ended || this.destroyed) return cb();
|
|
13
|
+
const ws = target;
|
|
14
|
+
if (ws && ws.readyState === 1) {
|
|
15
|
+
try {
|
|
16
|
+
ws.send(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk), cb);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
cb(e);
|
|
19
|
+
}
|
|
20
|
+
} else cb();
|
|
21
|
+
},
|
|
22
|
+
writev(chunks, cb) {
|
|
23
|
+
if (ended || this.destroyed) return cb();
|
|
24
|
+
const ws = target;
|
|
25
|
+
if (!ws || ws.readyState !== 1) return cb();
|
|
26
|
+
try {
|
|
27
|
+
for (const { chunk } of chunks) {
|
|
28
|
+
ws.send(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
29
|
+
}
|
|
30
|
+
cb();
|
|
31
|
+
} catch (e) {
|
|
32
|
+
cb(e);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
final(cb) {
|
|
36
|
+
ended = true;
|
|
37
|
+
const ws = target;
|
|
38
|
+
target = null;
|
|
39
|
+
if (ws && (ws.readyState === 0 || ws.readyState === 1)) {
|
|
40
|
+
try {
|
|
41
|
+
typeof ws.terminate === "function" ? ws.terminate() : ws.close();
|
|
42
|
+
} catch { }
|
|
43
|
+
}
|
|
44
|
+
cb();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
Proxy.setTarget = ws => {
|
|
48
|
+
if (ended) return;
|
|
49
|
+
target = ws;
|
|
50
|
+
};
|
|
51
|
+
Proxy.hardEnd = () => {
|
|
52
|
+
ended = true;
|
|
53
|
+
target = null;
|
|
54
|
+
};
|
|
55
|
+
return Proxy;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildStream(options, WebSocket, Proxy) {
|
|
59
|
+
const readable = new PassThrough();
|
|
60
|
+
const Stream = Duplexify(undefined, undefined, Object.assign({ end: false, autoDestroy: true }, options));
|
|
61
|
+
const NoopWritable = new Writable({ write(_c, _e, cb) { cb(); } });
|
|
62
|
+
let ws = WebSocket;
|
|
63
|
+
let pingTimer = null;
|
|
64
|
+
let livenessTimer = null;
|
|
65
|
+
let lastActivity = Date.now();
|
|
66
|
+
let attached = false;
|
|
67
|
+
let style = "prop";
|
|
68
|
+
let closed = false;
|
|
69
|
+
|
|
70
|
+
const toBuffer = d => {
|
|
71
|
+
if (Buffer.isBuffer(d)) return d;
|
|
72
|
+
if (d instanceof ArrayBuffer) return Buffer.from(d);
|
|
73
|
+
if (ArrayBuffer.isView(d)) return Buffer.from(d.buffer, d.byteOffset, d.byteLength);
|
|
74
|
+
return Buffer.from(String(d));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const swapToNoopWritable = () => {
|
|
78
|
+
try { Stream.setWritable(NoopWritable); } catch { }
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const onOpen = () => {
|
|
82
|
+
if (closed) return;
|
|
83
|
+
Proxy.setTarget(ws);
|
|
84
|
+
Stream.setWritable(Proxy);
|
|
85
|
+
Stream.setReadable(readable);
|
|
86
|
+
Stream.emit("connect");
|
|
87
|
+
lastActivity = Date.now();
|
|
88
|
+
clearInterval(pingTimer);
|
|
89
|
+
clearInterval(livenessTimer);
|
|
90
|
+
pingTimer = setInterval(() => {
|
|
91
|
+
if (!ws || ws.readyState !== 1) return;
|
|
92
|
+
if (typeof ws.ping === "function") {
|
|
93
|
+
try { ws.ping(); } catch { }
|
|
94
|
+
} else {
|
|
95
|
+
try { ws.send("ping"); } catch { }
|
|
96
|
+
}
|
|
97
|
+
}, 30000);
|
|
98
|
+
livenessTimer = setInterval(() => {
|
|
99
|
+
if (!ws || ws.readyState !== 1) return;
|
|
100
|
+
if (Date.now() - lastActivity > 65000) {
|
|
101
|
+
try { typeof ws.terminate === "function" ? ws.terminate() : ws.close(); } catch { }
|
|
102
|
+
}
|
|
103
|
+
}, 10000);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const onMessage = data => {
|
|
107
|
+
lastActivity = Date.now();
|
|
108
|
+
readable.write(toBuffer(style === "dom" && data && data.data !== undefined ? data.data : data));
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const onPong = () => {
|
|
112
|
+
lastActivity = Date.now();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const cleanup = () => {
|
|
116
|
+
if (closed) return;
|
|
117
|
+
closed = true;
|
|
118
|
+
clearInterval(pingTimer);
|
|
119
|
+
clearInterval(livenessTimer);
|
|
120
|
+
pingTimer = null;
|
|
121
|
+
livenessTimer = null;
|
|
122
|
+
Proxy.hardEnd();
|
|
123
|
+
swapToNoopWritable();
|
|
124
|
+
if (ws) {
|
|
125
|
+
detach(ws);
|
|
126
|
+
try {
|
|
127
|
+
if (ws.readyState === 1) {
|
|
128
|
+
typeof ws.terminate === "function" ? ws.terminate() : ws.close();
|
|
129
|
+
}
|
|
130
|
+
} catch { }
|
|
131
|
+
ws = null;
|
|
132
|
+
}
|
|
133
|
+
readable.end();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const onError = err => {
|
|
137
|
+
cleanup();
|
|
138
|
+
Stream.destroy(err);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const onClose = () => {
|
|
142
|
+
cleanup();
|
|
143
|
+
Stream.end();
|
|
144
|
+
if (!Stream.destroyed) Stream.destroy();
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const attach = w => {
|
|
148
|
+
if (attached || !w) return;
|
|
149
|
+
attached = true;
|
|
150
|
+
if (typeof w.on === "function" && typeof w.off === "function") {
|
|
151
|
+
style = "node";
|
|
152
|
+
w.on("open", onOpen);
|
|
153
|
+
w.on("message", onMessage);
|
|
154
|
+
w.on("error", onError);
|
|
155
|
+
w.on("close", onClose);
|
|
156
|
+
if (typeof w.on === "function") w.on("pong", onPong);
|
|
157
|
+
} else if (typeof w.addEventListener === "function" && typeof w.removeEventListener === "function") {
|
|
158
|
+
style = "dom";
|
|
159
|
+
w.addEventListener("open", onOpen);
|
|
160
|
+
w.addEventListener("message", onMessage);
|
|
161
|
+
w.addEventListener("error", onError);
|
|
162
|
+
w.addEventListener("close", onClose);
|
|
163
|
+
} else {
|
|
164
|
+
style = "prop";
|
|
165
|
+
w.onopen = onOpen;
|
|
166
|
+
w.onmessage = onMessage;
|
|
167
|
+
w.onerror = onError;
|
|
168
|
+
w.onclose = onClose;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const detach = w => {
|
|
173
|
+
if (!attached || !w) return;
|
|
174
|
+
attached = false;
|
|
175
|
+
if (style === "node" && typeof w.off === "function") {
|
|
176
|
+
w.off("open", onOpen);
|
|
177
|
+
w.off("message", onMessage);
|
|
178
|
+
w.off("error", onError);
|
|
179
|
+
w.off("close", onClose);
|
|
180
|
+
if (typeof w.off === "function") w.off("pong", onPong);
|
|
181
|
+
} else if (style === "dom" && typeof w.removeEventListener === "function") {
|
|
182
|
+
w.removeEventListener("open", onOpen);
|
|
183
|
+
w.removeEventListener("message", onMessage);
|
|
184
|
+
w.removeEventListener("error", onError);
|
|
185
|
+
w.removeEventListener("close", onClose);
|
|
186
|
+
} else {
|
|
187
|
+
w.onopen = null;
|
|
188
|
+
w.onmessage = null;
|
|
189
|
+
w.onerror = null;
|
|
190
|
+
w.onclose = null;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
attach(ws);
|
|
195
|
+
if (ws && ws.readyState === 1) onOpen();
|
|
196
|
+
|
|
197
|
+
Stream.on("prefinish", swapToNoopWritable);
|
|
198
|
+
Stream.on("finish", cleanup);
|
|
199
|
+
Stream.on("close", cleanup);
|
|
200
|
+
Proxy.on("close", swapToNoopWritable);
|
|
201
|
+
|
|
202
|
+
return Stream;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = {
|
|
206
|
+
buildProxy,
|
|
207
|
+
buildStream
|
|
208
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = {
|
|
3
|
+
topics: [
|
|
4
|
+
"/ls_req",
|
|
5
|
+
"/ls_resp",
|
|
6
|
+
"/legacy_web",
|
|
7
|
+
"/webrtc",
|
|
8
|
+
"/rtc_multi",
|
|
9
|
+
"/onevc",
|
|
10
|
+
"/br_sr",
|
|
11
|
+
"/sr_res",
|
|
12
|
+
"/t_ms",
|
|
13
|
+
"/thread_typing",
|
|
14
|
+
"/orca_typing_notifications",
|
|
15
|
+
"/notify_disconnect",
|
|
16
|
+
"/orca_presence",
|
|
17
|
+
"/inbox",
|
|
18
|
+
"/mercury",
|
|
19
|
+
"/messaging_events",
|
|
20
|
+
"/orca_message_notifications",
|
|
21
|
+
"/pp",
|
|
22
|
+
"/webrtc_response"
|
|
23
|
+
]
|
|
24
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const mqtt = require("mqtt");
|
|
3
|
+
const WebSocket = require("ws");
|
|
4
|
+
const HttpsProxyAgent = require("https-proxy-agent");
|
|
5
|
+
const EventEmitter = require("events");
|
|
6
|
+
const { parseAndCheckLogin } = require("../../utils/client");
|
|
7
|
+
const { buildProxy, buildStream } = require("./detail/buildStream");
|
|
8
|
+
const { topics } = require("./detail/constants");
|
|
9
|
+
const createParseDelta = require("./core/parseDelta");
|
|
10
|
+
const createListenMqtt = require("./core/connectMqtt");
|
|
11
|
+
const createGetSeqID = require("./core/getSeqID");
|
|
12
|
+
const markDelivery = require("./core/markDelivery");
|
|
13
|
+
const getTaskResponseData = require("./core/getTaskResponseData");
|
|
14
|
+
const parseDelta = createParseDelta({ markDelivery, parseAndCheckLogin });
|
|
15
|
+
const listenMqtt = createListenMqtt({ WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy, topics, parseDelta, getTaskResponseData });
|
|
16
|
+
const getSeqIDFactory = createGetSeqID({ parseAndCheckLogin, listenMqtt });
|
|
17
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
18
|
+
const identity = function () { };
|
|
19
|
+
let globalCallback = identity;
|
|
20
|
+
function getSeqIDWrapper() {
|
|
21
|
+
const form = {
|
|
22
|
+
av: ctx.userID,
|
|
23
|
+
queries: JSON.stringify({
|
|
24
|
+
o0: { doc_id: "3336396659757871", query_params: { limit: 1, before: null, tags: ["INBOX"], includeDeliveryReceipts: false, includeSeqID: true } }
|
|
25
|
+
})
|
|
26
|
+
};
|
|
27
|
+
console.log("[FCA-INFO] MQTT getSeqID call");
|
|
28
|
+
return getSeqIDFactory(defaultFuncs, api, ctx, globalCallback, form).then(() => {
|
|
29
|
+
console.log("[FCA-INFO] MQTT getSeqID done");
|
|
30
|
+
}).catch(e => {
|
|
31
|
+
console.error(`[FCA-ERROR] MQTT getSeqID error: ${e && e.message ? e.message : e}`);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function isConnected() {
|
|
35
|
+
return !!(ctx.mqttClient && ctx.mqttClient.connected);
|
|
36
|
+
}
|
|
37
|
+
function unsubAll(cb) {
|
|
38
|
+
if (!isConnected()) return cb && cb();
|
|
39
|
+
let pending = topics.length;
|
|
40
|
+
if (!pending) return cb && cb();
|
|
41
|
+
let done = false;
|
|
42
|
+
topics.forEach(t => {
|
|
43
|
+
ctx.mqttClient.unsubscribe(t, err => {
|
|
44
|
+
const msg = String(err && err.message ? err.message : err || "");
|
|
45
|
+
if (msg && /No subscription existed/i.test(msg)) err = null;
|
|
46
|
+
if (--pending === 0 && !done) {
|
|
47
|
+
done = true;
|
|
48
|
+
cb && cb();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function endQuietly(next) {
|
|
54
|
+
const finish = () => {
|
|
55
|
+
ctx.mqttClient = undefined;
|
|
56
|
+
ctx.lastSeqId = null;
|
|
57
|
+
ctx.syncToken = undefined;
|
|
58
|
+
ctx.t_mqttCalled = false;
|
|
59
|
+
ctx._ending = false;
|
|
60
|
+
next && next();
|
|
61
|
+
};
|
|
62
|
+
try {
|
|
63
|
+
if (ctx.mqttClient) {
|
|
64
|
+
if (isConnected()) {
|
|
65
|
+
try { ctx.mqttClient.publish("/browser_close", "{}"); } catch (_) { }
|
|
66
|
+
}
|
|
67
|
+
ctx.mqttClient.end(true, finish);
|
|
68
|
+
} else finish();
|
|
69
|
+
} catch (_) {
|
|
70
|
+
finish();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function delayedReconnect() {
|
|
74
|
+
console.log("[FCA-INFO] MQTT reconnect in 2000ms");
|
|
75
|
+
setTimeout(() => getSeqIDWrapper(), 2000);
|
|
76
|
+
}
|
|
77
|
+
function forceCycle() {
|
|
78
|
+
ctx._ending = true;
|
|
79
|
+
console.warn("[FCA-WARN] MQTT force cycle begin");
|
|
80
|
+
unsubAll(() => {
|
|
81
|
+
endQuietly(() => {
|
|
82
|
+
delayedReconnect();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return function (callback) {
|
|
87
|
+
class MessageEmitter extends EventEmitter {
|
|
88
|
+
stopListening(callback2) {
|
|
89
|
+
const cb = callback2 || function () { };
|
|
90
|
+
console.log("[FCA-INFO] MQTT stop requested");
|
|
91
|
+
globalCallback = identity;
|
|
92
|
+
if (ctx._autoCycleTimer) {
|
|
93
|
+
clearInterval(ctx._autoCycleTimer);
|
|
94
|
+
ctx._autoCycleTimer = null;
|
|
95
|
+
console.log("[FCA-INFO] MQTT auto-cycle cleared");
|
|
96
|
+
}
|
|
97
|
+
ctx._ending = true;
|
|
98
|
+
unsubAll(() => {
|
|
99
|
+
endQuietly(() => {
|
|
100
|
+
console.log("[FCA-INFO] MQTT stopped");
|
|
101
|
+
cb();
|
|
102
|
+
delayedReconnect();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async stopListeningAsync() {
|
|
107
|
+
return new Promise(resolve => { this.stopListening(resolve); });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const msgEmitter = new MessageEmitter();
|
|
111
|
+
globalCallback = callback || function (error, message) {
|
|
112
|
+
if (error) { console.error("[FCA-ERROR] MQTT emit error"); return msgEmitter.emit("error", error); }
|
|
113
|
+
msgEmitter.emit("message", message);
|
|
114
|
+
};
|
|
115
|
+
if (!ctx.firstListen) ctx.lastSeqId = null;
|
|
116
|
+
ctx.syncToken = undefined;
|
|
117
|
+
ctx.t_mqttCalled = false;
|
|
118
|
+
if (ctx._autoCycleTimer) {
|
|
119
|
+
clearInterval(ctx._autoCycleTimer);
|
|
120
|
+
ctx._autoCycleTimer = null;
|
|
121
|
+
}
|
|
122
|
+
ctx._autoCycleTimer = setInterval(forceCycle, 60 * 60 * 1000);
|
|
123
|
+
console.log("[FCA-INFO] MQTT auto-cycle enabled 3600000ms");
|
|
124
|
+
if (!ctx.firstListen || !ctx.lastSeqId) getSeqIDWrapper();
|
|
125
|
+
else {
|
|
126
|
+
console.log("[FCA-INFO] MQTT starting listenMqtt");
|
|
127
|
+
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
128
|
+
}
|
|
129
|
+
api.stopListening = msgEmitter.stopListening;
|
|
130
|
+
api.stopListeningAsync = msgEmitter.stopListeningAsync;
|
|
131
|
+
return msgEmitter;
|
|
132
|
+
};
|
|
133
|
+
};
|