@dongdev/fca-unofficial 3.0.27 → 3.0.28

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.
@@ -1,250 +1,250 @@
1
- "use strict";
2
- const { formatID } = require("../../../utils/format");
3
- module.exports = function createListenMqtt(deps) {
4
- const { WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy,
5
- topics, parseDelta, getTaskResponseData, logger, emitAuth
6
- } = deps;
7
-
8
- return function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
9
-
10
- function scheduleReconnect(delayMs) {
11
- const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
12
- const ms = typeof delayMs === "number" ? delayMs : d;
13
- if (ctx._reconnectTimer) {
14
- logger("mqtt reconnect already scheduled", "warn");
15
- return; // debounce
16
- }
17
- if (ctx._ending) {
18
- logger("mqtt reconnect skipped - ending", "warn");
19
- return;
20
- }
21
- logger(`mqtt will reconnect in ${ms}ms`, "warn");
22
- ctx._reconnectTimer = setTimeout(() => {
23
- ctx._reconnectTimer = null;
24
- if (!ctx._ending) {
25
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
26
- }
27
- }, ms);
28
- }
29
- function isEndingLikeError(msg) {
30
- return /No subscription existed|client disconnecting|socket hang up|ECONNRESET/i.test(msg || "");
31
- }
32
-
33
- const chatOn = ctx.globalOptions.online;
34
- const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
35
- const username = {
36
- u: ctx.userID, s: sessionID, chat_on: chatOn, fg: false, d: ctx.clientId,
37
- ct: "websocket", aid: 219994525426954, aids: null, mqtt_sid: "",
38
- cp: 3, ecp: 10, st: [], pm: [], dc: "", no_auto_fg: true, gas: null, pack: [], p: null, php_override: ""
39
- };
40
-
41
- const cookies = api.getCookies();
42
- let host;
43
- if (ctx.mqttEndpoint) host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${ctx.clientId}`;
44
- else if (ctx.region) host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${ctx.clientId}`;
45
- else host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${ctx.clientId}`;
46
-
47
- const options = {
48
- clientId: "mqttwsclient",
49
- protocolId: "MQIsdp",
50
- protocolVersion: 3,
51
- username: JSON.stringify(username),
52
- clean: true,
53
- wsOptions: {
54
- headers: {
55
- Cookie: cookies,
56
- Origin: "https://www.facebook.com",
57
- "User-Agent": ctx.globalOptions.userAgent || "Mozilla/5.0",
58
- Referer: "https://www.facebook.com/",
59
- Host: "edge-chat.facebook.com",
60
- Connection: "Upgrade",
61
- Pragma: "no-cache",
62
- "Cache-Control": "no-cache",
63
- Upgrade: "websocket",
64
- "Sec-WebSocket-Version": "13",
65
- "Accept-Encoding": "gzip, deflate, br",
66
- "Accept-Language": "vi,en;q=0.9",
67
- "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
68
- },
69
- origin: "https://www.facebook.com",
70
- protocolVersion: 13,
71
- binaryType: "arraybuffer"
72
- },
73
- keepalive: 30,
74
- reschedulePings: true,
75
- reconnectPeriod: 0,
76
- connectTimeout: 5000
77
- };
78
- if (ctx.globalOptions.proxy !== undefined) {
79
- const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
80
- options.wsOptions.agent = agent;
81
- }
82
-
83
- ctx.mqttClient = new mqtt.Client(
84
- () => buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()),
85
- options
86
- );
87
- const mqttClient = ctx.mqttClient;
88
-
89
- mqttClient.on("error", function (err) {
90
- const msg = String(err && err.message ? err.message : err || "");
91
- if ((ctx._ending || ctx._cycling) && /No subscription existed|client disconnecting/i.test(msg)) {
92
- logger(`mqtt expected during shutdown: ${msg}`, "info");
93
- return;
94
- }
95
-
96
- if (/Not logged in|Not logged in.|blocked the login|401|403/i.test(msg)) {
97
- try {
98
- if (mqttClient && mqttClient.connected) {
99
- mqttClient.end(true);
100
- }
101
- } catch (_) { }
102
- return emitAuth(ctx, api, globalCallback,
103
- /blocked/i.test(msg) ? "login_blocked" : "not_logged_in",
104
- msg
105
- );
106
- }
107
- logger(`mqtt error: ${msg}`, "error");
108
- try {
109
- if (mqttClient && mqttClient.connected) {
110
- mqttClient.end(true);
111
- }
112
- } catch (_) { }
113
- if (ctx._ending || ctx._cycling) return;
114
-
115
- if (ctx.globalOptions.autoReconnect && !ctx._ending) {
116
- const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
117
- logger(`mqtt autoReconnect listenMqtt() in ${d}ms`, "warn");
118
- // Use scheduleReconnect to prevent multiple reconnections
119
- scheduleReconnect(d);
120
- } else {
121
- globalCallback({ type: "stop_listen", error: msg || "Connection refused" }, null);
122
- }
123
- });
124
-
125
- mqttClient.on("connect", function () {
126
- if (process.env.OnStatus === undefined) {
127
- logger("fca-unoffcial premium", "info");
128
- process.env.OnStatus = true;
129
- }
130
- ctx._cycling = false;
131
-
132
- topics.forEach(t => mqttClient.subscribe(t));
133
-
134
-
135
- const queue = {
136
- sync_api_version: 11, max_deltas_able_to_process: 100, delta_batch_size: 500,
137
- encoding: "JSON", entity_fbid: ctx.userID, initial_titan_sequence_id: ctx.lastSeqId, device_params: null
138
- };
139
- const topic = ctx.syncToken ? "/messenger_sync_get_diffs" : "/messenger_sync_create_queue";
140
- if (ctx.syncToken) { queue.last_seq_id = ctx.lastSeqId; queue.sync_token = ctx.syncToken; }
141
- mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
142
- mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
143
- mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
144
- const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
145
- let rTimeout = setTimeout(function () {
146
- rTimeout = null;
147
- if (ctx._ending) {
148
- logger("mqtt t_ms timeout skipped - ending", "warn");
149
- return;
150
- }
151
- logger(`mqtt t_ms timeout, cycling in ${d}ms`, "warn");
152
- try {
153
- if (mqttClient && mqttClient.connected) {
154
- mqttClient.end(true);
155
- }
156
- } catch (_) { }
157
- scheduleReconnect(d);
158
- }, 5000);
159
-
160
- // Store timeout reference for cleanup
161
- ctx._rTimeout = rTimeout;
162
-
163
- ctx.tmsWait = function () {
164
- if (rTimeout) {
165
- clearTimeout(rTimeout);
166
- rTimeout = null;
167
- }
168
- if (ctx._rTimeout) {
169
- delete ctx._rTimeout;
170
- }
171
- if (ctx.globalOptions.emitReady) globalCallback({ type: "ready", error: null });
172
- delete ctx.tmsWait;
173
- };
174
- });
175
-
176
- mqttClient.on("message", function (topic, message) {
177
- if (ctx._ending) return; // Ignore messages if ending
178
- try {
179
- let jsonMessage = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
180
- try {
181
- jsonMessage = JSON.parse(jsonMessage);
182
- } catch (parseErr) {
183
- logger(`mqtt message parse error for topic ${topic}: ${parseErr && parseErr.message ? parseErr.message : String(parseErr)}`, "warn");
184
- jsonMessage = {};
185
- }
186
-
187
- if (jsonMessage.type === "jewel_requests_add") {
188
- globalCallback(null, { type: "friend_request_received", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
189
- } else if (jsonMessage.type === "jewel_requests_remove_old") {
190
- globalCallback(null, { type: "friend_request_cancel", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
191
- } else if (topic === "/t_ms") {
192
- if (ctx.tmsWait && typeof ctx.tmsWait == "function") ctx.tmsWait();
193
- if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
194
- ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
195
- ctx.syncToken = jsonMessage.syncToken;
196
- }
197
- if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
198
- for (const dlt of (jsonMessage.deltas || [])) {
199
- parseDelta(defaultFuncs, api, ctx, globalCallback, { delta: dlt });
200
- }
201
- } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
202
- const typ = {
203
- type: "typ",
204
- isTyping: !!jsonMessage.state,
205
- from: jsonMessage.sender_fbid.toString(),
206
- threadID: formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
207
- };
208
- globalCallback(null, typ);
209
- } else if (topic === "/orca_presence") {
210
- if (!ctx.globalOptions.updatePresence) {
211
- for (const data of (jsonMessage.list || [])) {
212
- const presence = { type: "presence", userID: String(data.u), timestamp: data.l * 1000, statuses: data.p };
213
- globalCallback(null, presence);
214
- }
215
- }
216
- } else if (topic === "/ls_resp") {
217
- const parsedPayload = JSON.parse(jsonMessage.payload);
218
- const reqID = jsonMessage.request_id;
219
- if (ctx["tasks"].has(reqID)) {
220
- const taskData = ctx["tasks"].get(reqID);
221
- const { type: taskType, callback: taskCallback } = taskData;
222
- const taskRespData = getTaskResponseData(taskType, parsedPayload);
223
- if (taskRespData == null) taskCallback("error", null);
224
- else taskCallback(null, Object.assign({ type: taskType, reqID }, taskRespData));
225
- }
226
- }
227
- } catch (ex) {
228
- const errMsg = ex && ex.message ? ex.message : String(ex || "Unknown error");
229
- logger(`mqtt message handler error: ${errMsg}`, "error");
230
- // Don't crash on message parsing errors, just log and continue
231
- }
232
- });
233
-
234
- mqttClient.on("close", function () {
235
- if (ctx._ending || ctx._cycling) {
236
- logger("mqtt close expected", "info");
237
- return;
238
- }
239
- logger("mqtt connection closed", "warn");
240
- });
241
-
242
- mqttClient.on("disconnect", () => {
243
- if (ctx._ending || ctx._cycling) {
244
- logger("mqtt disconnect expected", "info");
245
- return;
246
- }
247
- logger("mqtt disconnected", "warn");
248
- });
249
- };
250
- };
1
+ "use strict";
2
+ const { formatID } = require("../../../utils/format");
3
+ module.exports = function createListenMqtt(deps) {
4
+ const { WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy,
5
+ topics, parseDelta, getTaskResponseData, logger, emitAuth
6
+ } = deps;
7
+
8
+ return function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
9
+
10
+ function scheduleReconnect(delayMs) {
11
+ const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
12
+ const ms = typeof delayMs === "number" ? delayMs : d;
13
+ if (ctx._reconnectTimer) {
14
+ logger("mqtt reconnect already scheduled", "warn");
15
+ return; // debounce
16
+ }
17
+ if (ctx._ending) {
18
+ logger("mqtt reconnect skipped - ending", "warn");
19
+ return;
20
+ }
21
+ logger(`mqtt will reconnect in ${ms}ms`, "warn");
22
+ ctx._reconnectTimer = setTimeout(() => {
23
+ ctx._reconnectTimer = null;
24
+ if (!ctx._ending) {
25
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
26
+ }
27
+ }, ms);
28
+ }
29
+ function isEndingLikeError(msg) {
30
+ return /No subscription existed|client disconnecting|socket hang up|ECONNRESET/i.test(msg || "");
31
+ }
32
+
33
+ const chatOn = ctx.globalOptions.online;
34
+ const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
35
+ const username = {
36
+ u: ctx.userID, s: sessionID, chat_on: chatOn, fg: false, d: ctx.clientId,
37
+ ct: "websocket", aid: 219994525426954, aids: null, mqtt_sid: "",
38
+ cp: 3, ecp: 10, st: [], pm: [], dc: "", no_auto_fg: true, gas: null, pack: [], p: null, php_override: ""
39
+ };
40
+
41
+ const cookies = api.getCookies();
42
+ let host;
43
+ if (ctx.mqttEndpoint) host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${ctx.clientId}`;
44
+ else if (ctx.region) host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${ctx.clientId}`;
45
+ else host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${ctx.clientId}`;
46
+
47
+ const options = {
48
+ clientId: "mqttwsclient",
49
+ protocolId: "MQIsdp",
50
+ protocolVersion: 3,
51
+ username: JSON.stringify(username),
52
+ clean: true,
53
+ wsOptions: {
54
+ headers: {
55
+ Cookie: cookies,
56
+ Origin: "https://www.facebook.com",
57
+ "User-Agent": ctx.globalOptions.userAgent || "Mozilla/5.0",
58
+ Referer: "https://www.facebook.com/",
59
+ Host: "edge-chat.facebook.com",
60
+ Connection: "Upgrade",
61
+ Pragma: "no-cache",
62
+ "Cache-Control": "no-cache",
63
+ Upgrade: "websocket",
64
+ "Sec-WebSocket-Version": "13",
65
+ "Accept-Encoding": "gzip, deflate, br",
66
+ "Accept-Language": "vi,en;q=0.9",
67
+ "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
68
+ },
69
+ origin: "https://www.facebook.com",
70
+ protocolVersion: 13,
71
+ binaryType: "arraybuffer"
72
+ },
73
+ keepalive: 30,
74
+ reschedulePings: true,
75
+ reconnectPeriod: 0,
76
+ connectTimeout: 5000
77
+ };
78
+ if (ctx.globalOptions.proxy !== undefined) {
79
+ const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
80
+ options.wsOptions.agent = agent;
81
+ }
82
+
83
+ ctx.mqttClient = new mqtt.Client(
84
+ () => buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()),
85
+ options
86
+ );
87
+ const mqttClient = ctx.mqttClient;
88
+
89
+ mqttClient.on("error", function (err) {
90
+ const msg = String(err && err.message ? err.message : err || "");
91
+ if ((ctx._ending || ctx._cycling) && /No subscription existed|client disconnecting/i.test(msg)) {
92
+ logger(`mqtt expected during shutdown: ${msg}`, "info");
93
+ return;
94
+ }
95
+
96
+ if (/Not logged in|Not logged in.|blocked the login|401|403/i.test(msg)) {
97
+ try {
98
+ if (mqttClient && mqttClient.connected) {
99
+ mqttClient.end(true);
100
+ }
101
+ } catch (_) { }
102
+ return emitAuth(ctx, api, globalCallback,
103
+ /blocked/i.test(msg) ? "login_blocked" : "not_logged_in",
104
+ msg
105
+ );
106
+ }
107
+ logger(`mqtt error: ${msg}`, "error");
108
+ try {
109
+ if (mqttClient && mqttClient.connected) {
110
+ mqttClient.end(true);
111
+ }
112
+ } catch (_) { }
113
+ if (ctx._ending || ctx._cycling) return;
114
+
115
+ if (ctx.globalOptions.autoReconnect && !ctx._ending) {
116
+ const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
117
+ logger(`mqtt autoReconnect listenMqtt() in ${d}ms`, "warn");
118
+ // Use scheduleReconnect to prevent multiple reconnections
119
+ scheduleReconnect(d);
120
+ } else {
121
+ globalCallback({ type: "stop_listen", error: msg || "Connection refused" }, null);
122
+ }
123
+ });
124
+
125
+ mqttClient.on("connect", function () {
126
+ if (process.env.OnStatus === undefined) {
127
+ logger("fca-unoffcial premium", "info");
128
+ process.env.OnStatus = true;
129
+ }
130
+ ctx._cycling = false;
131
+
132
+ topics.forEach(t => mqttClient.subscribe(t));
133
+
134
+
135
+ const queue = {
136
+ sync_api_version: 11, max_deltas_able_to_process: 100, delta_batch_size: 500,
137
+ encoding: "JSON", entity_fbid: ctx.userID, initial_titan_sequence_id: ctx.lastSeqId, device_params: null
138
+ };
139
+ const topic = ctx.syncToken ? "/messenger_sync_get_diffs" : "/messenger_sync_create_queue";
140
+ if (ctx.syncToken) { queue.last_seq_id = ctx.lastSeqId; queue.sync_token = ctx.syncToken; }
141
+ mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
142
+ mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
143
+ mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
144
+ const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
145
+ let rTimeout = setTimeout(function () {
146
+ rTimeout = null;
147
+ if (ctx._ending) {
148
+ logger("mqtt t_ms timeout skipped - ending", "warn");
149
+ return;
150
+ }
151
+ logger(`mqtt t_ms timeout, cycling in ${d}ms`, "warn");
152
+ try {
153
+ if (mqttClient && mqttClient.connected) {
154
+ mqttClient.end(true);
155
+ }
156
+ } catch (_) { }
157
+ scheduleReconnect(d);
158
+ }, 5000);
159
+
160
+ // Store timeout reference for cleanup
161
+ ctx._rTimeout = rTimeout;
162
+
163
+ ctx.tmsWait = function () {
164
+ if (rTimeout) {
165
+ clearTimeout(rTimeout);
166
+ rTimeout = null;
167
+ }
168
+ if (ctx._rTimeout) {
169
+ delete ctx._rTimeout;
170
+ }
171
+ if (ctx.globalOptions.emitReady) globalCallback({ type: "ready", error: null });
172
+ delete ctx.tmsWait;
173
+ };
174
+ });
175
+
176
+ mqttClient.on("message", function (topic, message) {
177
+ if (ctx._ending) return; // Ignore messages if ending
178
+ try {
179
+ let jsonMessage = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
180
+ try {
181
+ jsonMessage = JSON.parse(jsonMessage);
182
+ } catch (parseErr) {
183
+ logger(`mqtt message parse error for topic ${topic}: ${parseErr && parseErr.message ? parseErr.message : String(parseErr)}`, "warn");
184
+ jsonMessage = {};
185
+ }
186
+
187
+ if (jsonMessage.type === "jewel_requests_add") {
188
+ globalCallback(null, { type: "friend_request_received", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
189
+ } else if (jsonMessage.type === "jewel_requests_remove_old") {
190
+ globalCallback(null, { type: "friend_request_cancel", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
191
+ } else if (topic === "/t_ms") {
192
+ if (ctx.tmsWait && typeof ctx.tmsWait == "function") ctx.tmsWait();
193
+ if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
194
+ ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
195
+ ctx.syncToken = jsonMessage.syncToken;
196
+ }
197
+ if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
198
+ for (const dlt of (jsonMessage.deltas || [])) {
199
+ parseDelta(defaultFuncs, api, ctx, globalCallback, { delta: dlt });
200
+ }
201
+ } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
202
+ const typ = {
203
+ type: "typ",
204
+ isTyping: !!jsonMessage.state,
205
+ from: jsonMessage.sender_fbid.toString(),
206
+ threadID: formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
207
+ };
208
+ globalCallback(null, typ);
209
+ } else if (topic === "/orca_presence") {
210
+ if (!ctx.globalOptions.updatePresence) {
211
+ for (const data of (jsonMessage.list || [])) {
212
+ const presence = { type: "presence", userID: String(data.u), timestamp: data.l * 1000, statuses: data.p };
213
+ globalCallback(null, presence);
214
+ }
215
+ }
216
+ } else if (topic === "/ls_resp") {
217
+ const parsedPayload = JSON.parse(jsonMessage.payload);
218
+ const reqID = jsonMessage.request_id;
219
+ if (ctx["tasks"].has(reqID)) {
220
+ const taskData = ctx["tasks"].get(reqID);
221
+ const { type: taskType, callback: taskCallback } = taskData;
222
+ const taskRespData = getTaskResponseData(taskType, parsedPayload);
223
+ if (taskRespData == null) taskCallback("error", null);
224
+ else taskCallback(null, Object.assign({ type: taskType, reqID }, taskRespData));
225
+ }
226
+ }
227
+ } catch (ex) {
228
+ const errMsg = ex && ex.message ? ex.message : String(ex || "Unknown error");
229
+ logger(`mqtt message handler error: ${errMsg}`, "error");
230
+ // Don't crash on message parsing errors, just log and continue
231
+ }
232
+ });
233
+
234
+ mqttClient.on("close", function () {
235
+ if (ctx._ending || ctx._cycling) {
236
+ logger("mqtt close expected", "info");
237
+ return;
238
+ }
239
+ logger("mqtt connection closed", "warn");
240
+ });
241
+
242
+ mqttClient.on("disconnect", () => {
243
+ if (ctx._ending || ctx._cycling) {
244
+ logger("mqtt disconnect expected", "info");
245
+ return;
246
+ }
247
+ logger("mqtt disconnected", "warn");
248
+ });
249
+ };
250
+ };
@@ -80,7 +80,7 @@ module.exports = function createEmitAuth({ logger }) {
80
80
  }
81
81
  } catch (_) { }
82
82
 
83
- const msg = detail || reason;
83
+ const msg = detail || reason;
84
84
  logger(`auth change -> ${reason}: ${msg}`, "error");
85
85
 
86
86
  if (typeof globalCallback === "function") {