fca-neokex-fix 1.0.1
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 +220 -0
- package/LICENSE +26 -0
- package/README.md +346 -0
- package/THEME_FEATURES.md +137 -0
- package/examples/README.md +131 -0
- package/examples/apply-ai-theme.js +127 -0
- package/examples/check-current-theme.js +74 -0
- package/examples/simple-bot.js +114 -0
- package/examples/test-bot.js +752 -0
- package/examples/test-logging.js +85 -0
- package/examples/theme-usage-example.js +53 -0
- package/index.js +2 -0
- package/package.json +105 -0
- package/src/apis/addExternalModule.js +24 -0
- package/src/apis/addUserToGroup.js +108 -0
- package/src/apis/changeAdminStatus.js +148 -0
- package/src/apis/changeArchivedStatus.js +61 -0
- package/src/apis/changeAvatar.js +103 -0
- package/src/apis/changeBio.js +69 -0
- package/src/apis/changeBlockedStatus.js +54 -0
- package/src/apis/changeGroupImage.js +136 -0
- package/src/apis/changeThreadColor.js +116 -0
- package/src/apis/comment.js +207 -0
- package/src/apis/createAITheme.js +129 -0
- package/src/apis/createNewGroup.js +79 -0
- package/src/apis/createPoll.js +73 -0
- package/src/apis/deleteMessage.js +44 -0
- package/src/apis/deleteThread.js +52 -0
- package/src/apis/editMessage.js +70 -0
- package/src/apis/emoji.js +124 -0
- package/src/apis/fetchThemeData.js +65 -0
- package/src/apis/follow.js +81 -0
- package/src/apis/forwardMessage.js +52 -0
- package/src/apis/friend.js +243 -0
- package/src/apis/gcmember.js +122 -0
- package/src/apis/gcname.js +123 -0
- package/src/apis/gcrule.js +119 -0
- package/src/apis/getAccess.js +111 -0
- package/src/apis/getBotInfo.js +88 -0
- package/src/apis/getBotInitialData.js +43 -0
- package/src/apis/getFriendsList.js +79 -0
- package/src/apis/getMessage.js +423 -0
- package/src/apis/getTheme.js +104 -0
- package/src/apis/getThemeInfo.js +96 -0
- package/src/apis/getThreadHistory.js +239 -0
- package/src/apis/getThreadInfo.js +257 -0
- package/src/apis/getThreadList.js +222 -0
- package/src/apis/getThreadPictures.js +58 -0
- package/src/apis/getUserID.js +83 -0
- package/src/apis/getUserInfo.js +495 -0
- package/src/apis/getUserInfoV2.js +146 -0
- package/src/apis/handleMessageRequest.js +50 -0
- package/src/apis/httpGet.js +63 -0
- package/src/apis/httpPost.js +89 -0
- package/src/apis/httpPostFormData.js +69 -0
- package/src/apis/listenMqtt.js +796 -0
- package/src/apis/listenSpeed.js +170 -0
- package/src/apis/logout.js +63 -0
- package/src/apis/markAsDelivered.js +47 -0
- package/src/apis/markAsRead.js +95 -0
- package/src/apis/markAsReadAll.js +41 -0
- package/src/apis/markAsSeen.js +70 -0
- package/src/apis/mqttDeltaValue.js +330 -0
- package/src/apis/muteThread.js +45 -0
- package/src/apis/nickname.js +132 -0
- package/src/apis/notes.js +163 -0
- package/src/apis/pinMessage.js +141 -0
- package/src/apis/produceMetaTheme.js +180 -0
- package/src/apis/realtime.js +161 -0
- package/src/apis/removeUserFromGroup.js +117 -0
- package/src/apis/resolvePhotoUrl.js +58 -0
- package/src/apis/searchForThread.js +154 -0
- package/src/apis/sendMessage.js +281 -0
- package/src/apis/sendMessageMqtt.js +188 -0
- package/src/apis/sendTypingIndicator.js +41 -0
- package/src/apis/setMessageReaction.js +27 -0
- package/src/apis/setMessageReactionMqtt.js +61 -0
- package/src/apis/setThreadTheme.js +260 -0
- package/src/apis/setThreadThemeMqtt.js +94 -0
- package/src/apis/share.js +107 -0
- package/src/apis/shareContact.js +66 -0
- package/src/apis/stickers.js +257 -0
- package/src/apis/story.js +181 -0
- package/src/apis/theme.js +233 -0
- package/src/apis/unfriend.js +47 -0
- package/src/apis/unsendMessage.js +17 -0
- package/src/database/appStateBackup.js +189 -0
- package/src/database/models/index.js +56 -0
- package/src/database/models/thread.js +31 -0
- package/src/database/models/user.js +32 -0
- package/src/database/threadData.js +101 -0
- package/src/database/userData.js +90 -0
- package/src/engine/client.js +91 -0
- package/src/engine/models/buildAPI.js +109 -0
- package/src/engine/models/loginHelper.js +326 -0
- package/src/engine/models/setOptions.js +53 -0
- package/src/utils/auth-helpers.js +149 -0
- package/src/utils/autoReLogin.js +169 -0
- package/src/utils/axios.js +290 -0
- package/src/utils/clients.js +270 -0
- package/src/utils/constants.js +396 -0
- package/src/utils/formatters/data/formatAttachment.js +370 -0
- package/src/utils/formatters/data/formatDelta.js +153 -0
- package/src/utils/formatters/index.js +159 -0
- package/src/utils/formatters/value/formatCookie.js +91 -0
- package/src/utils/formatters/value/formatDate.js +36 -0
- package/src/utils/formatters/value/formatID.js +16 -0
- package/src/utils/formatters.js +1067 -0
- package/src/utils/headers.js +199 -0
- package/src/utils/index.js +151 -0
- package/src/utils/monitoring.js +358 -0
- package/src/utils/rateLimiter.js +380 -0
- package/src/utils/tokenRefresh.js +311 -0
- package/src/utils/user-agents.js +238 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const utils = require('../utils');
|
|
3
|
+
const mqtt = require('mqtt');
|
|
4
|
+
const websocket = require('websocket-stream');
|
|
5
|
+
const HttpsProxyAgent = require('https-proxy-agent');
|
|
6
|
+
const EventEmitter = require('events');
|
|
7
|
+
const { parseDelta } = require('./mqttDeltaValue');
|
|
8
|
+
|
|
9
|
+
let form = {};
|
|
10
|
+
let getSeqID;
|
|
11
|
+
|
|
12
|
+
const topics = [
|
|
13
|
+
"/legacy_web", "/webrtc", "/rtc_multi", "/onevc", "/br_sr", "/sr_res",
|
|
14
|
+
"/t_ms", "/thread_typing", "/orca_typing_notifications", "/notify_disconnect",
|
|
15
|
+
"/orca_presence", "/inbox", "/mercury", "/messaging_events",
|
|
16
|
+
"/orca_message_notifications", "/pp", "/webrtc_response"
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function generateUUID() {
|
|
20
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
21
|
+
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
22
|
+
return v.toString(16);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getRandomReconnectTime() {
|
|
27
|
+
const min = 26 * 60 * 1000;
|
|
28
|
+
const max = 60 * 60 * 1000;
|
|
29
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function calculate(previousTimestamp, currentTimestamp){
|
|
33
|
+
return Math.floor(previousTimestamp + (currentTimestamp - previousTimestamp) + 300);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {Object} ctx
|
|
38
|
+
* @param {Object} api
|
|
39
|
+
* @param {string} threadID
|
|
40
|
+
*/
|
|
41
|
+
function markAsRead(ctx, api, threadID) {
|
|
42
|
+
if (ctx.globalOptions.autoMarkRead && threadID) {
|
|
43
|
+
api.markAsRead(threadID, (err) => {
|
|
44
|
+
if (err) utils.error("autoMarkRead", err);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {Object} defaultFuncs
|
|
51
|
+
* @param {Object} api
|
|
52
|
+
* @param {Object} ctx
|
|
53
|
+
* @param {Function} globalCallback
|
|
54
|
+
*/
|
|
55
|
+
async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconnect) {
|
|
56
|
+
function isEndingLikeError(msg) {
|
|
57
|
+
return /No subscription existed|client disconnecting|socket hang up|ECONNRESET/i.test(msg || "");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const chatOn = ctx.globalOptions.online;
|
|
61
|
+
const region = ctx.region;
|
|
62
|
+
const foreground = false;
|
|
63
|
+
const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
|
|
64
|
+
const cid = ctx.clientID;
|
|
65
|
+
const cachedUA = ctx.globalOptions.cachedUserAgent || ctx.globalOptions.userAgent;
|
|
66
|
+
const username = {
|
|
67
|
+
u: ctx.userID,
|
|
68
|
+
s: sessionID,
|
|
69
|
+
chat_on: chatOn,
|
|
70
|
+
fg: foreground,
|
|
71
|
+
d: cid,
|
|
72
|
+
ct: 'websocket',
|
|
73
|
+
aid: ctx.mqttAppID,
|
|
74
|
+
mqtt_sid: '',
|
|
75
|
+
cp: 3,
|
|
76
|
+
ecp: 10,
|
|
77
|
+
st: [],
|
|
78
|
+
pm: [],
|
|
79
|
+
dc: '',
|
|
80
|
+
no_auto_fg: true,
|
|
81
|
+
gas: null,
|
|
82
|
+
pack: [],
|
|
83
|
+
a: cachedUA
|
|
84
|
+
};
|
|
85
|
+
const cookies = ctx.jar.getCookiesSync('https://www.facebook.com').join('; ');
|
|
86
|
+
let host;
|
|
87
|
+
const domain = "wss://edge-chat.messenger.com/chat";
|
|
88
|
+
if (region) {
|
|
89
|
+
host = `${domain}?region=${region.toLowerCase()}&sid=${sessionID}&cid=${cid}`;
|
|
90
|
+
} else {
|
|
91
|
+
host = `${domain}?sid=${sessionID}&cid=${cid}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
utils.log("Connecting to MQTT...", host);
|
|
95
|
+
|
|
96
|
+
const cachedSecChUa = ctx.globalOptions.cachedSecChUa || '"Chromium";v="131", "Not;A=Brand";v="99", "Google Chrome";v="131"';
|
|
97
|
+
const cachedSecChUaPlatform = ctx.globalOptions.cachedSecChUaPlatform || '"Windows"';
|
|
98
|
+
const cachedLocale = ctx.globalOptions.cachedLocale || 'en-US,en;q=0.9';
|
|
99
|
+
|
|
100
|
+
const options = {
|
|
101
|
+
clientId: 'mqttwsclient',
|
|
102
|
+
protocolId: 'MQIsdp',
|
|
103
|
+
protocolVersion: 3,
|
|
104
|
+
username: JSON.stringify(username),
|
|
105
|
+
clean: true,
|
|
106
|
+
wsOptions: {
|
|
107
|
+
headers: {
|
|
108
|
+
'Cookie': cookies,
|
|
109
|
+
'Origin': 'https://www.facebook.com',
|
|
110
|
+
'User-Agent': username.a,
|
|
111
|
+
'Referer': 'https://www.facebook.com/',
|
|
112
|
+
'Host': new URL(host).hostname,
|
|
113
|
+
'Connection': 'Upgrade',
|
|
114
|
+
'Pragma': 'no-cache',
|
|
115
|
+
'Cache-Control': 'no-cache',
|
|
116
|
+
'Upgrade': 'websocket',
|
|
117
|
+
'Sec-WebSocket-Version': '13',
|
|
118
|
+
'Accept-Encoding': 'gzip, deflate, br',
|
|
119
|
+
'Accept-Language': cachedLocale,
|
|
120
|
+
'Sec-Ch-Ua': cachedSecChUa,
|
|
121
|
+
'Sec-Ch-Ua-Mobile': '?0',
|
|
122
|
+
'Sec-Ch-Ua-Platform': cachedSecChUaPlatform,
|
|
123
|
+
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits'
|
|
124
|
+
},
|
|
125
|
+
origin: 'https://www.facebook.com',
|
|
126
|
+
protocolVersion: 13,
|
|
127
|
+
binaryType: 'arraybuffer'
|
|
128
|
+
},
|
|
129
|
+
keepalive: 30,
|
|
130
|
+
reschedulePings: true,
|
|
131
|
+
connectTimeout: 5000,
|
|
132
|
+
reconnectPeriod: 0
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (ctx.globalOptions.proxy) options.wsOptions.agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
|
|
136
|
+
const mqttClient = new mqtt.Client(_ => websocket(host, options.wsOptions), options);
|
|
137
|
+
mqttClient.publishSync = mqttClient.publish.bind(mqttClient);
|
|
138
|
+
mqttClient.publish = (topic, message, opts = {}, callback = () => {}) => new Promise((resolve, reject) => {
|
|
139
|
+
try {
|
|
140
|
+
mqttClient.publishSync(topic, message, opts, (err, data) => {
|
|
141
|
+
if (err) {
|
|
142
|
+
callback(err);
|
|
143
|
+
return reject(err);
|
|
144
|
+
}
|
|
145
|
+
callback(null, data);
|
|
146
|
+
resolve(data);
|
|
147
|
+
});
|
|
148
|
+
} catch (syncErr) {
|
|
149
|
+
callback(syncErr);
|
|
150
|
+
reject(syncErr);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
ctx.mqttClient = mqttClient;
|
|
154
|
+
|
|
155
|
+
mqttClient.on('error', (err) => {
|
|
156
|
+
const msg = String(err && err.message ? err.message : err || "");
|
|
157
|
+
|
|
158
|
+
if ((ctx._ending || ctx._cycling) && isEndingLikeError(msg)) {
|
|
159
|
+
utils.log("MQTT", "Expected error during shutdown: " + msg);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (ctx._tmsTimeout) {
|
|
164
|
+
clearTimeout(ctx._tmsTimeout);
|
|
165
|
+
ctx._tmsTimeout = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (/Not logged in|Not logged in\.|blocked the login|checkpoint|401|403/i.test(msg)) {
|
|
169
|
+
try { mqttClient.end(true); } catch (_) { }
|
|
170
|
+
try { if (ctx._autoCycleTimer) clearInterval(ctx._autoCycleTimer); } catch (_) { }
|
|
171
|
+
ctx._ending = true;
|
|
172
|
+
ctx.mqttClient = undefined;
|
|
173
|
+
ctx.loggedIn = false;
|
|
174
|
+
utils.error("MQTT", "Authentication error detected:", msg);
|
|
175
|
+
globalCallback({
|
|
176
|
+
type: "account_inactive",
|
|
177
|
+
reason: /blocked|checkpoint/i.test(msg) ? "login_blocked" : "not_logged_in",
|
|
178
|
+
error: msg,
|
|
179
|
+
requiresReLogin: true,
|
|
180
|
+
timestamp: Date.now()
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
utils.error("MQTT error:", msg);
|
|
186
|
+
try { mqttClient.end(true); } catch (_) { }
|
|
187
|
+
|
|
188
|
+
if (ctx._ending || ctx._cycling) {
|
|
189
|
+
utils.log("MQTT", "Skipping reconnect: already ending or cycling");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (ctx.globalOptions.autoReconnect) {
|
|
194
|
+
ctx._reconnectAttempts = (ctx._reconnectAttempts || 0) + 1;
|
|
195
|
+
const maxReconnectAttempts = 50;
|
|
196
|
+
|
|
197
|
+
if (ctx._reconnectAttempts > maxReconnectAttempts) {
|
|
198
|
+
utils.error("MQTT", `Max reconnect attempts (${maxReconnectAttempts}) reached. Stopping.`);
|
|
199
|
+
ctx._ending = true;
|
|
200
|
+
globalCallback({ type: "stop_listen", error: "Max reconnect attempts exceeded" });
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
205
|
+
const maxBackoff = 30000;
|
|
206
|
+
const backoffDelay = Math.min(baseDelay * Math.pow(1.5, ctx._reconnectAttempts), maxBackoff);
|
|
207
|
+
const jitter = Math.floor(Math.random() * 1000);
|
|
208
|
+
const d = backoffDelay + jitter;
|
|
209
|
+
utils.warn("MQTT", `Auto-reconnecting in ${d}ms (attempt ${ctx._reconnectAttempts}/${maxReconnectAttempts}) due to error`);
|
|
210
|
+
scheduleReconnect(d);
|
|
211
|
+
} else {
|
|
212
|
+
globalCallback({ type: "stop_listen", error: msg || "Connection refused" });
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
mqttClient.on('connect', async () => {
|
|
217
|
+
if (!ctx._mqttConnected) {
|
|
218
|
+
utils.log("MQTT connected successfully");
|
|
219
|
+
ctx._mqttConnected = true;
|
|
220
|
+
}
|
|
221
|
+
ctx._cycling = false;
|
|
222
|
+
ctx._reconnectAttempts = 0;
|
|
223
|
+
ctx.loggedIn = true;
|
|
224
|
+
|
|
225
|
+
topics.forEach(topic => mqttClient.subscribe(topic));
|
|
226
|
+
|
|
227
|
+
const queue = {
|
|
228
|
+
sync_api_version: 11,
|
|
229
|
+
max_deltas_able_to_process: 100,
|
|
230
|
+
delta_batch_size: 500,
|
|
231
|
+
encoding: "JSON",
|
|
232
|
+
entity_fbid: ctx.userID,
|
|
233
|
+
initial_titan_sequence_id: ctx.lastSeqId,
|
|
234
|
+
device_params: null
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
let topic;
|
|
238
|
+
if (ctx.syncToken) {
|
|
239
|
+
topic = "/messenger_sync_get_diffs";
|
|
240
|
+
queue.last_seq_id = ctx.lastSeqId;
|
|
241
|
+
queue.sync_token = ctx.syncToken;
|
|
242
|
+
} else {
|
|
243
|
+
topic = "/messenger_sync_create_queue";
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
|
|
247
|
+
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
|
|
248
|
+
mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
|
|
249
|
+
|
|
250
|
+
const tmsTimeoutDelay = 10000;
|
|
251
|
+
ctx._tmsTimeout = setTimeout(() => {
|
|
252
|
+
ctx._tmsTimeout = null;
|
|
253
|
+
if (ctx._ending || ctx._cycling) return;
|
|
254
|
+
if (!ctx.globalOptions.autoReconnect) {
|
|
255
|
+
utils.warn("MQTT", "t_ms timeout but autoReconnect is disabled");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
utils.warn("MQTT", `t_ms timeout after ${tmsTimeoutDelay}ms, will cycle connection`);
|
|
259
|
+
try { mqttClient.end(true); } catch (_) { }
|
|
260
|
+
const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
261
|
+
scheduleReconnect(baseDelay);
|
|
262
|
+
}, tmsTimeoutDelay);
|
|
263
|
+
|
|
264
|
+
ctx.tmsWait = function() {
|
|
265
|
+
if (ctx._tmsTimeout) {
|
|
266
|
+
clearTimeout(ctx._tmsTimeout);
|
|
267
|
+
ctx._tmsTimeout = null;
|
|
268
|
+
}
|
|
269
|
+
if (ctx.globalOptions.emitReady) {
|
|
270
|
+
globalCallback(null, { type: "ready", timestamp: Date.now() });
|
|
271
|
+
}
|
|
272
|
+
delete ctx.tmsWait;
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
mqttClient.on('message', async (topic, message, _packet) => {
|
|
277
|
+
try {
|
|
278
|
+
let jsonMessage = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
|
|
279
|
+
try { jsonMessage = JSON.parse(jsonMessage); } catch (_) { jsonMessage = {}; }
|
|
280
|
+
|
|
281
|
+
if (jsonMessage.type === "jewel_requests_add") {
|
|
282
|
+
globalCallback(null, {
|
|
283
|
+
type: "friend_request_received",
|
|
284
|
+
actorFbId: jsonMessage.from.toString(),
|
|
285
|
+
timestamp: Date.now().toString()
|
|
286
|
+
});
|
|
287
|
+
} else if (jsonMessage.type === "jewel_requests_remove_old") {
|
|
288
|
+
globalCallback(null, {
|
|
289
|
+
type: "friend_request_cancel",
|
|
290
|
+
actorFbId: jsonMessage.from.toString(),
|
|
291
|
+
timestamp: Date.now().toString()
|
|
292
|
+
});
|
|
293
|
+
} else if (topic === "/t_ms") {
|
|
294
|
+
if (ctx.tmsWait && typeof ctx.tmsWait === "function") ctx.tmsWait();
|
|
295
|
+
|
|
296
|
+
if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
|
|
297
|
+
ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
|
|
298
|
+
ctx.syncToken = jsonMessage.syncToken;
|
|
299
|
+
}
|
|
300
|
+
if (jsonMessage.lastIssuedSeqId) {
|
|
301
|
+
ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (jsonMessage.deltas) {
|
|
305
|
+
for (const delta of jsonMessage.deltas) {
|
|
306
|
+
parseDelta(defaultFuncs, api, ctx, globalCallback, { delta });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
} else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
|
|
310
|
+
const typ = {
|
|
311
|
+
type: "typ",
|
|
312
|
+
isTyping: !!jsonMessage.state,
|
|
313
|
+
from: jsonMessage.sender_fbid.toString(),
|
|
314
|
+
threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
|
|
315
|
+
};
|
|
316
|
+
globalCallback(null, typ);
|
|
317
|
+
} else if (topic === "/orca_presence") {
|
|
318
|
+
if (!ctx.globalOptions.updatePresence && jsonMessage.list) {
|
|
319
|
+
for (const data of jsonMessage.list) {
|
|
320
|
+
globalCallback(null, {
|
|
321
|
+
type: "presence",
|
|
322
|
+
userID: String(data.u),
|
|
323
|
+
timestamp: data.l * 1000,
|
|
324
|
+
statuses: data.p
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
} catch (ex) {
|
|
330
|
+
utils.error("MQTT message parse error:", ex && ex.message ? ex.message : ex);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
mqttClient.on('close', () => {
|
|
335
|
+
utils.warn("MQTT", "Connection closed");
|
|
336
|
+
if (ctx._tmsTimeout) {
|
|
337
|
+
clearTimeout(ctx._tmsTimeout);
|
|
338
|
+
ctx._tmsTimeout = null;
|
|
339
|
+
}
|
|
340
|
+
if (ctx._ending || ctx._cycling) {
|
|
341
|
+
utils.log("MQTT", "Skipping reconnect on close: already ending or cycling");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (ctx.globalOptions.autoReconnect) {
|
|
346
|
+
ctx._reconnectAttempts = (ctx._reconnectAttempts || 0) + 1;
|
|
347
|
+
const maxReconnectAttempts = 50;
|
|
348
|
+
|
|
349
|
+
if (ctx._reconnectAttempts > maxReconnectAttempts) {
|
|
350
|
+
utils.error("MQTT", `Max reconnect attempts (${maxReconnectAttempts}) reached on close. Stopping.`);
|
|
351
|
+
ctx._ending = true;
|
|
352
|
+
if (typeof globalCallback === "function") {
|
|
353
|
+
globalCallback({ type: "stop_listen", error: "Max reconnect attempts exceeded" });
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
359
|
+
const maxBackoff = 30000;
|
|
360
|
+
const backoffDelay = Math.min(baseDelay * Math.pow(1.5, ctx._reconnectAttempts - 1), maxBackoff);
|
|
361
|
+
const jitter = Math.floor(Math.random() * 500);
|
|
362
|
+
const d = backoffDelay + jitter;
|
|
363
|
+
utils.warn("MQTT", `Reconnecting in ${d}ms (attempt ${ctx._reconnectAttempts}/${maxReconnectAttempts})`);
|
|
364
|
+
scheduleReconnect(d);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
mqttClient.on('disconnect', () => {
|
|
369
|
+
utils.log("MQTT", "Disconnected");
|
|
370
|
+
if (ctx._tmsTimeout) {
|
|
371
|
+
clearTimeout(ctx._tmsTimeout);
|
|
372
|
+
ctx._tmsTimeout = null;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
mqttClient.on('offline', () => {
|
|
377
|
+
utils.warn("MQTT", "Connection went offline");
|
|
378
|
+
if (ctx._tmsTimeout) {
|
|
379
|
+
clearTimeout(ctx._tmsTimeout);
|
|
380
|
+
ctx._tmsTimeout = null;
|
|
381
|
+
}
|
|
382
|
+
if (!ctx._ending && !ctx._cycling && ctx.globalOptions.autoReconnect) {
|
|
383
|
+
try { mqttClient.end(true); } catch (_) { }
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const MQTT_DEFAULTS = {
|
|
389
|
+
cycleMs: 60 * 60 * 1000,
|
|
390
|
+
reconnectDelayMs: 2000,
|
|
391
|
+
autoReconnect: true,
|
|
392
|
+
reconnectAfterStop: false
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
function mqttConf(ctx, overrides) {
|
|
396
|
+
ctx._mqttOpt = Object.assign({}, MQTT_DEFAULTS, ctx._mqttOpt || {}, overrides || {});
|
|
397
|
+
if (typeof ctx._mqttOpt.autoReconnect === "boolean") {
|
|
398
|
+
ctx.globalOptions.autoReconnect = ctx._mqttOpt.autoReconnect;
|
|
399
|
+
}
|
|
400
|
+
return ctx._mqttOpt;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
module.exports = (defaultFuncs, api, ctx, opts) => {
|
|
404
|
+
const identity = () => {};
|
|
405
|
+
let globalCallback = identity;
|
|
406
|
+
|
|
407
|
+
function emitAuthError(reason, detail) {
|
|
408
|
+
try { if (ctx._autoCycleTimer) clearInterval(ctx._autoCycleTimer); } catch (_) { }
|
|
409
|
+
try { ctx._ending = true; } catch (_) { }
|
|
410
|
+
try { if (ctx.mqttClient) ctx.mqttClient.end(true); } catch (_) { }
|
|
411
|
+
ctx.mqttClient = undefined;
|
|
412
|
+
ctx.loggedIn = false;
|
|
413
|
+
|
|
414
|
+
const msg = detail || reason;
|
|
415
|
+
utils.error("AUTH", `Authentication error -> ${reason}: ${msg}`);
|
|
416
|
+
|
|
417
|
+
if (typeof globalCallback === "function") {
|
|
418
|
+
globalCallback({
|
|
419
|
+
type: "account_inactive",
|
|
420
|
+
reason: reason,
|
|
421
|
+
error: msg,
|
|
422
|
+
requiresReLogin: true,
|
|
423
|
+
timestamp: Date.now()
|
|
424
|
+
}, null);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function installPostGuard() {
|
|
429
|
+
if (ctx._postGuarded) return defaultFuncs.post;
|
|
430
|
+
const rawPost = defaultFuncs.post && defaultFuncs.post.bind(defaultFuncs);
|
|
431
|
+
if (!rawPost) return defaultFuncs.post;
|
|
432
|
+
|
|
433
|
+
function postSafe(...args) {
|
|
434
|
+
const lastArg = args[args.length - 1];
|
|
435
|
+
const hasCallback = typeof lastArg === 'function';
|
|
436
|
+
|
|
437
|
+
if (hasCallback) {
|
|
438
|
+
const originalCallback = args[args.length - 1];
|
|
439
|
+
args[args.length - 1] = function(err, ...cbArgs) {
|
|
440
|
+
if (err) {
|
|
441
|
+
const msg = (err && err.error) || (err && err.message) || String(err || "");
|
|
442
|
+
if (/Not logged in|Not logged in\.|blocked the login|checkpoint|security check/i.test(msg)) {
|
|
443
|
+
emitAuthError(
|
|
444
|
+
/blocked|checkpoint|security/i.test(msg) ? "login_blocked" : "not_logged_in",
|
|
445
|
+
msg
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return originalCallback(err, ...cbArgs);
|
|
450
|
+
};
|
|
451
|
+
return rawPost(...args);
|
|
452
|
+
} else {
|
|
453
|
+
const result = rawPost(...args);
|
|
454
|
+
if (result && typeof result.catch === 'function') {
|
|
455
|
+
return result.catch(err => {
|
|
456
|
+
const msg = (err && err.error) || (err && err.message) || String(err || "");
|
|
457
|
+
if (/Not logged in|Not logged in\.|blocked the login|checkpoint|security check/i.test(msg)) {
|
|
458
|
+
emitAuthError(
|
|
459
|
+
/blocked|checkpoint|security/i.test(msg) ? "login_blocked" : "not_logged_in",
|
|
460
|
+
msg
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
throw err;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return result;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
defaultFuncs.post = postSafe;
|
|
470
|
+
ctx._postGuarded = true;
|
|
471
|
+
utils.log("MQTT", "PostSafe guard installed for anti-automation detection");
|
|
472
|
+
return postSafe;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function scheduleReconnect(delayMs) {
|
|
476
|
+
if (ctx._ending) {
|
|
477
|
+
utils.log("MQTT", "scheduleReconnect: skipping because _ending is true");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (ctx._reconnectTimer) {
|
|
482
|
+
clearTimeout(ctx._reconnectTimer);
|
|
483
|
+
ctx._reconnectTimer = null;
|
|
484
|
+
utils.log("MQTT", "Cleared existing reconnect timer");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (ctx.mqttClient) {
|
|
488
|
+
try {
|
|
489
|
+
const oldClient = ctx.mqttClient;
|
|
490
|
+
ctx.mqttClient = undefined;
|
|
491
|
+
|
|
492
|
+
oldClient.removeAllListeners('connect');
|
|
493
|
+
oldClient.removeAllListeners('message');
|
|
494
|
+
oldClient.removeAllListeners('close');
|
|
495
|
+
oldClient.removeAllListeners('disconnect');
|
|
496
|
+
oldClient.removeAllListeners('offline');
|
|
497
|
+
|
|
498
|
+
oldClient.on('error', (err) => {
|
|
499
|
+
utils.log("MQTT", "Error during client teardown (expected):", err && err.message ? err.message : err);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
oldClient.end(true, () => {
|
|
503
|
+
try {
|
|
504
|
+
oldClient.removeAllListeners();
|
|
505
|
+
} catch (_) { }
|
|
506
|
+
});
|
|
507
|
+
} catch (_) { }
|
|
508
|
+
utils.log("MQTT", "Cleaned up old MQTT client");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (ctx._tmsTimeout) {
|
|
512
|
+
clearTimeout(ctx._tmsTimeout);
|
|
513
|
+
ctx._tmsTimeout = null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
ctx._cycling = true;
|
|
517
|
+
|
|
518
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
519
|
+
const ms = typeof delayMs === "number" ? delayMs : d;
|
|
520
|
+
|
|
521
|
+
utils.warn("MQTT", `Scheduling reconnect in ${ms}ms`);
|
|
522
|
+
ctx._reconnectTimer = setTimeout(() => {
|
|
523
|
+
ctx._reconnectTimer = null;
|
|
524
|
+
ctx._consecutiveSeqIDFailures = ctx._consecutiveSeqIDFailures || 0;
|
|
525
|
+
|
|
526
|
+
if (ctx._consecutiveSeqIDFailures >= 10) {
|
|
527
|
+
utils.error("MQTT", "Too many consecutive getSeqID failures (10+), stopping reconnect loop");
|
|
528
|
+
ctx._ending = true;
|
|
529
|
+
ctx._cycling = false;
|
|
530
|
+
if (typeof globalCallback === "function") {
|
|
531
|
+
globalCallback({ type: "stop_listen", error: "Max consecutive getSeqID failures reached" });
|
|
532
|
+
}
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
getSeqIDWrapper();
|
|
537
|
+
}, ms);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
let conf = mqttConf(ctx, opts);
|
|
541
|
+
installPostGuard();
|
|
542
|
+
|
|
543
|
+
getSeqID = async () => {
|
|
544
|
+
try {
|
|
545
|
+
form = {
|
|
546
|
+
av: ctx.globalOptions.pageID,
|
|
547
|
+
queries: JSON.stringify({
|
|
548
|
+
o0: {
|
|
549
|
+
doc_id: "3336396659757871",
|
|
550
|
+
query_params: {
|
|
551
|
+
limit: 1,
|
|
552
|
+
before: null,
|
|
553
|
+
tags: ["INBOX"],
|
|
554
|
+
includeDeliveryReceipts: false,
|
|
555
|
+
includeSeqID: true
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
})
|
|
559
|
+
};
|
|
560
|
+
utils.log("MQTT", "Getting sequence ID...");
|
|
561
|
+
ctx.t_mqttCalled = false;
|
|
562
|
+
const resData = await defaultFuncs.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
563
|
+
|
|
564
|
+
if (utils.getType(resData) !== "Array") {
|
|
565
|
+
throw { error: "Not logged in" };
|
|
566
|
+
}
|
|
567
|
+
if (!Array.isArray(resData) || !resData.length) {
|
|
568
|
+
throw { error: "getSeqID: empty response" };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const lastRes = resData[resData.length - 1];
|
|
572
|
+
if (lastRes && lastRes.successful_results === 0) {
|
|
573
|
+
throw { error: "getSeqID: no successful results" };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const syncSeqId = resData[0] && resData[0].o0 && resData[0].o0.data && resData[0].o0.data.viewer && resData[0].o0.data.viewer.message_threads && resData[0].o0.data.viewer.message_threads.sync_sequence_id;
|
|
577
|
+
if (syncSeqId) {
|
|
578
|
+
ctx.lastSeqId = syncSeqId;
|
|
579
|
+
ctx._consecutiveSeqIDFailures = 0;
|
|
580
|
+
ctx._cycling = false;
|
|
581
|
+
utils.log("MQTT", "getSeqID ok -> listenMqtt()");
|
|
582
|
+
listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconnect);
|
|
583
|
+
} else {
|
|
584
|
+
throw { error: "getSeqID: no sync_sequence_id found" };
|
|
585
|
+
}
|
|
586
|
+
} catch (err) {
|
|
587
|
+
const detail = (err && err.detail && err.detail.message) ? ` | detail=${err.detail.message}` : "";
|
|
588
|
+
const msg = ((err && err.error) || (err && err.message) || String(err || "")) + detail;
|
|
589
|
+
|
|
590
|
+
if (/Not logged in/i.test(msg)) {
|
|
591
|
+
utils.error("MQTT", "Auth error in getSeqID: Not logged in");
|
|
592
|
+
return emitAuthError("not_logged_in", msg);
|
|
593
|
+
}
|
|
594
|
+
if (/blocked the login|checkpoint|security check/i.test(msg)) {
|
|
595
|
+
utils.error("MQTT", "Auth error in getSeqID: Login blocked");
|
|
596
|
+
return emitAuthError("login_blocked", msg);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
utils.error("MQTT", "getSeqID error:", msg);
|
|
600
|
+
ctx._consecutiveSeqIDFailures = (ctx._consecutiveSeqIDFailures || 0) + 1;
|
|
601
|
+
|
|
602
|
+
if (ctx._consecutiveSeqIDFailures >= 10) {
|
|
603
|
+
utils.error("MQTT", `getSeqID failed ${ctx._consecutiveSeqIDFailures} times consecutively. Stopping reconnect loop.`);
|
|
604
|
+
ctx._ending = true;
|
|
605
|
+
ctx._cycling = false;
|
|
606
|
+
if (typeof globalCallback === "function") {
|
|
607
|
+
globalCallback({ type: "stop_listen", error: "Max consecutive getSeqID failures reached" });
|
|
608
|
+
}
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (ctx.globalOptions.autoReconnect && !ctx._ending) {
|
|
613
|
+
const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
614
|
+
const backoffDelay = Math.min(baseDelay * Math.pow(1.5, ctx._consecutiveSeqIDFailures), 30000);
|
|
615
|
+
utils.warn("MQTT", `getSeqID failed (${ctx._consecutiveSeqIDFailures}/10), will retry in ${backoffDelay}ms`);
|
|
616
|
+
scheduleReconnect(backoffDelay);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
function getSeqIDWrapper() {
|
|
622
|
+
if (ctx._ending) {
|
|
623
|
+
utils.log("MQTT", "getSeqIDWrapper: skipping because _ending is true");
|
|
624
|
+
return Promise.resolve();
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
utils.log("MQTT", "getSeqID call");
|
|
628
|
+
return getSeqID()
|
|
629
|
+
.then(() => {
|
|
630
|
+
utils.log("MQTT", "getSeqID done");
|
|
631
|
+
})
|
|
632
|
+
.catch(e => {
|
|
633
|
+
utils.error("MQTT", `getSeqID error in wrapper: ${e && e.message ? e.message : e}`);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function isConnected() {
|
|
638
|
+
return !!(ctx.mqttClient && ctx.mqttClient.connected);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function unsubAll(cb) {
|
|
642
|
+
if (!isConnected()) return cb && cb();
|
|
643
|
+
let pending = topics.length;
|
|
644
|
+
if (!pending) return cb && cb();
|
|
645
|
+
let fired = false;
|
|
646
|
+
topics.forEach(t => {
|
|
647
|
+
ctx.mqttClient.unsubscribe(t, () => {
|
|
648
|
+
if (--pending === 0 && !fired) {
|
|
649
|
+
fired = true;
|
|
650
|
+
cb && cb();
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function endQuietly(next) {
|
|
657
|
+
const finish = () => {
|
|
658
|
+
try {
|
|
659
|
+
ctx.mqttClient && ctx.mqttClient.removeAllListeners();
|
|
660
|
+
} catch (_) { }
|
|
661
|
+
if (ctx._tmsTimeout) {
|
|
662
|
+
clearTimeout(ctx._tmsTimeout);
|
|
663
|
+
ctx._tmsTimeout = null;
|
|
664
|
+
}
|
|
665
|
+
if (ctx._reconnectTimer) {
|
|
666
|
+
clearTimeout(ctx._reconnectTimer);
|
|
667
|
+
ctx._reconnectTimer = null;
|
|
668
|
+
}
|
|
669
|
+
ctx.mqttClient = undefined;
|
|
670
|
+
ctx.lastSeqId = null;
|
|
671
|
+
ctx.syncToken = undefined;
|
|
672
|
+
ctx.t_mqttCalled = false;
|
|
673
|
+
ctx._ending = false;
|
|
674
|
+
next && next();
|
|
675
|
+
};
|
|
676
|
+
try {
|
|
677
|
+
if (ctx.mqttClient) {
|
|
678
|
+
if (isConnected()) {
|
|
679
|
+
try {
|
|
680
|
+
ctx.mqttClient.publish("/browser_close", "{}");
|
|
681
|
+
} catch (_) { }
|
|
682
|
+
}
|
|
683
|
+
ctx.mqttClient.end(true, finish);
|
|
684
|
+
} else finish();
|
|
685
|
+
} catch (_) {
|
|
686
|
+
finish();
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function delayedReconnect() {
|
|
691
|
+
const d = conf.reconnectDelayMs;
|
|
692
|
+
utils.log("MQTT", `Reconnect in ${d}ms`);
|
|
693
|
+
setTimeout(() => getSeqIDWrapper(), d);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function forceCycle() {
|
|
697
|
+
if (ctx._cycling) return;
|
|
698
|
+
ctx._cycling = true;
|
|
699
|
+
ctx._ending = true;
|
|
700
|
+
utils.warn("MQTT", "Force cycle begin");
|
|
701
|
+
unsubAll(() => endQuietly(() => delayedReconnect()));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return (callback) => {
|
|
705
|
+
class MessageEmitter extends EventEmitter {
|
|
706
|
+
stopListening(callback2) {
|
|
707
|
+
const cb = callback2 || function() {};
|
|
708
|
+
utils.log("MQTT", "Stop requested");
|
|
709
|
+
globalCallback = identity;
|
|
710
|
+
|
|
711
|
+
if (ctx._autoCycleTimer) {
|
|
712
|
+
clearInterval(ctx._autoCycleTimer);
|
|
713
|
+
ctx._autoCycleTimer = null;
|
|
714
|
+
utils.log("MQTT", "Auto-cycle cleared");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (ctx._reconnectTimer) {
|
|
718
|
+
clearTimeout(ctx._reconnectTimer);
|
|
719
|
+
ctx._reconnectTimer = null;
|
|
720
|
+
utils.log("MQTT", "Reconnect timer cleared");
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (ctx._tmsTimeout) {
|
|
724
|
+
clearTimeout(ctx._tmsTimeout);
|
|
725
|
+
ctx._tmsTimeout = null;
|
|
726
|
+
utils.log("MQTT", "TMS timeout cleared");
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
ctx._ending = true;
|
|
730
|
+
ctx._reconnectAttempts = 0;
|
|
731
|
+
unsubAll(() => endQuietly(() => {
|
|
732
|
+
utils.log("MQTT", "Stopped successfully");
|
|
733
|
+
cb();
|
|
734
|
+
conf = mqttConf(ctx, conf);
|
|
735
|
+
if (conf.reconnectAfterStop) delayedReconnect();
|
|
736
|
+
}));
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
async stopListeningAsync() {
|
|
740
|
+
return new Promise(resolve => {
|
|
741
|
+
this.stopListening(resolve);
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const msgEmitter = new MessageEmitter();
|
|
747
|
+
|
|
748
|
+
globalCallback = callback || function(error, message) {
|
|
749
|
+
if (error) {
|
|
750
|
+
utils.error("MQTT", "Emit error");
|
|
751
|
+
return msgEmitter.emit("error", error);
|
|
752
|
+
}
|
|
753
|
+
if (message && (message.type === "message" || message.type === "message_reply")) {
|
|
754
|
+
markAsRead(ctx, api, message.threadID);
|
|
755
|
+
}
|
|
756
|
+
msgEmitter.emit("message", message);
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
conf = mqttConf(ctx, conf);
|
|
760
|
+
|
|
761
|
+
if (!ctx.firstListen) ctx.lastSeqId = null;
|
|
762
|
+
ctx.syncToken = undefined;
|
|
763
|
+
ctx.t_mqttCalled = false;
|
|
764
|
+
|
|
765
|
+
if (ctx._autoCycleTimer) {
|
|
766
|
+
clearInterval(ctx._autoCycleTimer);
|
|
767
|
+
ctx._autoCycleTimer = null;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (conf.cycleMs && conf.cycleMs > 0) {
|
|
771
|
+
ctx._autoCycleTimer = setInterval(forceCycle, conf.cycleMs);
|
|
772
|
+
utils.log("MQTT", `Auto-cycle enabled: ${conf.cycleMs}ms`);
|
|
773
|
+
} else {
|
|
774
|
+
utils.log("MQTT", "Auto-cycle disabled");
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (!ctx.firstListen || !ctx.lastSeqId) {
|
|
778
|
+
getSeqIDWrapper();
|
|
779
|
+
} else {
|
|
780
|
+
utils.log("MQTT", "Starting listenMqtt");
|
|
781
|
+
listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconnect);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (ctx.firstListen) {
|
|
785
|
+
api.markAsReadAll().catch(err => {
|
|
786
|
+
utils.error("Failed to mark all messages as read on startup:", err);
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
ctx.firstListen = false;
|
|
791
|
+
|
|
792
|
+
api.stopListening = msgEmitter.stopListening;
|
|
793
|
+
api.stopListeningAsync = msgEmitter.stopListeningAsync;
|
|
794
|
+
return msgEmitter;
|
|
795
|
+
};
|
|
796
|
+
};
|