@dongdev/fca-unofficial 2.0.12 → 2.0.14

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.
@@ -304,72 +304,62 @@ module.exports = function (defaultFuncs, api, ctx) {
304
304
  resolveFunc(data);
305
305
  };
306
306
  }
307
-
308
- try {
309
- const msgType = getType(msg);
310
- const threadIDType = getType(threadID);
311
- const messageIDType = getType(replyToMessage);
312
- if (msgType !== "String" && msgType !== "Object") return callback({ error: "Message should be of type string or object and not " + msgType + "." });
313
- if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") return callback({ error: "ThreadID should be of type number, string, or array and not " + threadIDType + "." });
314
- if (replyToMessage && messageIDType !== "String") return callback({ error: "MessageID should be of type string and not " + threadIDType + "." });
315
- if (msgType === "String") msg = { body: msg };
316
-
317
- const disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
318
- if (disallowedProperties.length > 0) return callback({ error: "Disallowed props: `" + disallowedProperties.join(", ") + "`" });
319
-
320
- const messageAndOTID = generateOfflineThreadingID();
321
- const form = {
322
- client: "mercury",
323
- action_type: "ma-type:user-generated-message",
324
- author: "fbid:" + ctx.userID,
325
- timestamp: Date.now(),
326
- timestamp_absolute: "Today",
327
- timestamp_relative: generateTimestampRelative(),
328
- timestamp_time_passed: "0",
329
- is_unread: false,
330
- is_cleared: false,
331
- is_forward: false,
332
- is_filtered_content: false,
333
- is_filtered_content_bh: false,
334
- is_filtered_content_account: false,
335
- is_filtered_content_quasar: false,
336
- is_filtered_content_invalid_app: false,
337
- is_spoof_warning: false,
338
- source: "source:chat:web",
339
- "source_tags[0]": "source:chat",
340
- body: msg.body ? msg.body.toString() : "",
341
- html_body: false,
342
- ui_push_phase: "V3",
343
- status: "0",
344
- offline_threading_id: messageAndOTID,
345
- message_id: messageAndOTID,
346
- threading_id: generateThreadingID(ctx.clientID),
347
- ephemeral_ttl_mode: "0",
348
- manual_retry_cnt: "0",
349
- signatureID: getSignatureID(),
350
- replied_to_message_id: replyToMessage ? replyToMessage.toString() : ""
351
- };
352
- applyPageAuthor(form, msg);
353
- handleLocation(msg, form, callback, () =>
354
- handleSticker(msg, form, callback, () =>
355
- handleAttachment(msg, form, callback, () =>
356
- handleUrl(msg, form, callback, () =>
357
- handleEmoji(msg, form, callback, () =>
358
- handleMention(msg, form, callback, () => {
359
- finalizeHasAttachment(form);
360
- send(form, threadID, messageAndOTID, callback, isGroup);
361
- })
362
- )
307
+ const msgType = getType(msg);
308
+ const threadIDType = getType(threadID);
309
+ const messageIDType = getType(replyToMessage);
310
+ if (msgType !== "String" && msgType !== "Object") return callback({ error: "Message should be of type string or object and not " + msgType + "." });
311
+ if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String") return callback({ error: "ThreadID should be of type number, string, or array and not " + threadIDType + "." });
312
+ if (replyToMessage && messageIDType !== "String") return callback({ error: "MessageID should be of type string and not " + threadIDType + "." });
313
+ if (msgType === "String") msg = { body: msg };
314
+ const disallowedProperties = Object.keys(msg).filter(prop => !allowedProperties[prop]);
315
+ if (disallowedProperties.length > 0) return callback({ error: "Disallowed props: `" + disallowedProperties.join(", ") + "`" });
316
+ const messageAndOTID = generateOfflineThreadingID();
317
+ const form = {
318
+ client: "mercury",
319
+ action_type: "ma-type:user-generated-message",
320
+ author: "fbid:" + ctx.userID,
321
+ timestamp: Date.now(),
322
+ timestamp_absolute: "Today",
323
+ timestamp_relative: generateTimestampRelative(),
324
+ timestamp_time_passed: "0",
325
+ is_unread: false,
326
+ is_cleared: false,
327
+ is_forward: false,
328
+ is_filtered_content: false,
329
+ is_filtered_content_bh: false,
330
+ is_filtered_content_account: false,
331
+ is_filtered_content_quasar: false,
332
+ is_filtered_content_invalid_app: false,
333
+ is_spoof_warning: false,
334
+ source: "source:chat:web",
335
+ "source_tags[0]": "source:chat",
336
+ body: msg.body ? msg.body.toString() : "",
337
+ html_body: false,
338
+ ui_push_phase: "V3",
339
+ status: "0",
340
+ offline_threading_id: messageAndOTID,
341
+ message_id: messageAndOTID,
342
+ threading_id: generateThreadingID(ctx.clientID),
343
+ ephemeral_ttl_mode: "0",
344
+ manual_retry_cnt: "0",
345
+ signatureID: getSignatureID(),
346
+ replied_to_message_id: replyToMessage ? replyToMessage.toString() : ""
347
+ };
348
+ applyPageAuthor(form, msg);
349
+ handleLocation(msg, form, callback, () =>
350
+ handleSticker(msg, form, callback, () =>
351
+ handleAttachment(msg, form, callback, () =>
352
+ handleUrl(msg, form, callback, () =>
353
+ handleEmoji(msg, form, callback, () =>
354
+ handleMention(msg, form, callback, () => {
355
+ finalizeHasAttachment(form);
356
+ send(form, threadID, messageAndOTID, callback, isGroup);
357
+ })
363
358
  )
364
359
  )
365
360
  )
366
- );
367
- } catch (e) {
368
- log.error("sendMessage", e);
369
- if (getType(e) === "Object" && e.error === "Not logged in.") ctx.loggedIn = false;
370
- return callback(e);
371
- }
372
-
361
+ )
362
+ );
373
363
  return returnPromise;
374
364
  };
375
365
 
@@ -1,110 +1,67 @@
1
1
  "use strict";
2
-
3
- const log = require("npmlog");
4
- const { getType } = require("../../utils/format");
5
- const { parseAndCheckLogin } = require("../../utils/client");
6
- module.exports = function(defaultFuncs, api, ctx) {
7
- function makeTypingIndicator(typ, threadID, callback, isGroup) {
8
- const form = {
9
- typ: +typ,
10
- to: "",
11
- source: "mercury-chat",
12
- thread: threadID
13
- };
14
-
15
- // Check if thread is a single person chat or a group chat
16
- // More info on this is in api.sendMessage
17
- if (getType(isGroup) == "Boolean") {
18
- if (!isGroup) {
19
- form.to = threadID;
20
- }
21
- defaultFuncs
22
- .post("https://www.facebook.com/ajax/messaging/typ.php", ctx.jar, form)
23
- .then(parseAndCheckLogin(ctx, defaultFuncs))
24
- .then(function(resData) {
25
- if (resData.error) {
26
- throw resData;
27
- }
28
-
29
- return callback();
30
- })
31
- .catch(function(err) {
32
- log.error("sendTypingIndicator", err);
33
- if (getType(err) == "Object" && err.error === "Not logged in") {
34
- ctx.loggedIn = false;
35
- }
36
- return callback(err);
37
- });
38
- } else {
39
- api.getUserInfo(threadID, function(err, res) {
40
- if (err) {
41
- return callback(err);
42
- }
43
-
44
- // If id is single person chat
45
- if (Object.keys(res).length > 0) {
46
- form.to = threadID;
47
- }
48
-
49
- defaultFuncs
50
- .post(
51
- "https://www.facebook.com/ajax/messaging/typ.php",
52
- ctx.jar,
53
- form
54
- )
55
- .then(parseAndCheckLogin(ctx, defaultFuncs))
56
- .then(function(resData) {
57
- if (resData.error) {
58
- throw resData;
59
- }
60
-
61
- return callback();
62
- })
63
- .catch(function(err) {
64
- log.error("sendTypingIndicator", err);
65
- if (
66
- getType(err) == "Object" &&
67
- err.error === "Not logged in."
68
- ) {
69
- ctx.loggedIn = false;
70
- }
71
- return callback(err);
72
- });
73
- });
2
+ const { getType } = require("../../utils/format.js");
3
+ module.exports = function (defaultFuncs, api, ctx) {
4
+ return function sendTyping(threadID, isTyping, options, callback) {
5
+ var resolveFunc = function () { };
6
+ var rejectFunc = function () { };
7
+ var returnPromise = new Promise(function (resolve, reject) {
8
+ resolveFunc = resolve;
9
+ rejectFunc = reject;
10
+ });
11
+ if (getType(options) == "Function" || getType(options) == "AsyncFunction") {
12
+ callback = options;
13
+ options = {};
74
14
  }
75
- }
76
-
77
- return function sendTypingIndicator(threadID, callback, isGroup) {
78
- if (
79
- getType(callback) !== "Function" &&
80
- getType(callback) !== "AsyncFunction"
81
- ) {
82
- if (callback) {
83
- log.warn(
84
- "sendTypingIndicator",
85
- "callback is not a function - ignoring."
86
- );
87
- }
88
- callback = () => {};
15
+ options = options || {};
16
+ if (!callback || getType(callback) != "Function" && getType(callback) != "AsyncFunction") {
17
+ callback = function (err, data) {
18
+ if (err) return rejectFunc(err);
19
+ resolveFunc(data);
20
+ };
21
+ }
22
+ if (!threadID) {
23
+ return callback(new Error("threadID is required"));
89
24
  }
90
-
91
- makeTypingIndicator(true, threadID, callback, isGroup);
92
-
93
- return function end(cb) {
94
- if (
95
- getType(cb) !== "Function" &&
96
- getType(cb) !== "AsyncFunction"
97
- ) {
98
- if (cb) {
99
- log.warn(
100
- "sendTypingIndicator",
101
- "callback is not a function - ignoring."
102
- );
103
- }
104
- cb = () => {};
25
+ const threadIDs = Array.isArray(threadID) ? threadID : [threadID];
26
+ threadIDs.forEach(tid => {
27
+ var isGroupThread = getType(tid) === "Array" ? 0 : 1;
28
+ var threadType = isGroupThread ? 2 : 1;
29
+ var duration = options.duration || 10000;
30
+ var autoStop = options.autoStop !== false;
31
+ var attribution = options.type || 0;
32
+ const publishTypingStatus = (isTypingStatus) => {
33
+ ctx.mqttClient.publish('/ls_req',
34
+ JSON.stringify({
35
+ app_id: "772021112871879",
36
+ payload: JSON.stringify({
37
+ label: "3",
38
+ payload: JSON.stringify({
39
+ "thread_key": parseInt(tid),
40
+ "is_group_thread": isGroupThread,
41
+ "is_typing": isTypingStatus ? 1 : 0,
42
+ "attribution": attribution,
43
+ "sync_group": 1,
44
+ "thread_type": threadType
45
+ }),
46
+ version: "8965252033599983"
47
+ }),
48
+ request_id: ++ctx.req_ID,
49
+ type: 4
50
+ }),
51
+ {
52
+ qos: 1,
53
+ retain: false,
54
+ }
55
+ );
56
+ };
57
+ publishTypingStatus(isTyping);
58
+ if (isTyping && autoStop) {
59
+ setTimeout(() => {
60
+ publishTypingStatus(false);
61
+ }, duration);
105
62
  }
106
-
107
- makeTypingIndicator(false, threadID, cb, isGroup);
108
- };
63
+ });
64
+ callback(null, true);
65
+ return returnPromise;
109
66
  };
110
67
  };
@@ -1,105 +1,76 @@
1
1
  "use strict";
2
2
 
3
- const log = require("npmlog");
3
+ const logger = require("../../../func/logger");
4
4
  const { generateOfflineThreadingID, getCurrentTimestamp } = require("../../utils/format");
5
- const { parseAndCheckLogin } = require("../../utils/client");
6
5
 
7
6
  module.exports = function (defaultFuncs, api, ctx) {
8
- function setMessageReactionNoMqtt(reaction, messageID, threadID, callback) {
9
- let resolveFunc = function () { };
10
- let rejectFunc = function () { };
11
- const returnPromise = new Promise(function (resolve, reject) {
12
- resolveFunc = resolve;
13
- rejectFunc = reject;
14
- });
15
- if (!callback) {
16
- callback = function (err) {
17
- if (err) return rejectFunc(err);
18
- resolveFunc();
19
- };
20
- }
21
- const variables = {
22
- data: {
23
- client_mutation_id: ctx.clientMutationId++,
24
- actor_id: ctx.userID,
25
- action: reaction === "" ? "REMOVE_REACTION" : "ADD_REACTION",
26
- message_id: messageID,
27
- reaction: reaction
28
- }
29
- };
30
- const qs = {
31
- doc_id: "1491398900900362",
32
- variables: JSON.stringify(variables),
33
- dpr: 1
34
- };
35
- defaultFuncs
36
- .postFormData("https://www.facebook.com/webgraphql/mutation/", ctx.jar, {}, qs)
37
- .then(parseAndCheckLogin(ctx.jar, defaultFuncs))
38
- .then(function (resData) {
39
- if (!resData) throw { error: "setReaction returned empty object." };
40
- if (resData.error) throw resData;
41
- callback(null);
42
- })
43
- .catch(function (err) {
44
- log.error("setReaction", err);
45
- return callback(err);
46
- });
47
- return returnPromise;
48
- }
49
-
50
- function setMessageReactionMqtt(reaction, messageID, threadID, callback) {
51
- if (!ctx.mqttClient) return setMessageReactionNoMqtt(reaction, messageID, callback);
7
+ return function setMessageReaction(reaction, messageID, threadID, callback) {
52
8
  return new Promise((resolve, reject) => {
53
- try {
54
- ctx.wsReqNumber += 1;
55
- const taskNumber = ++ctx.wsTaskNumber;
56
- const taskPayload = {
57
- thread_key: threadID,
58
- timestamp_ms: getCurrentTimestamp(),
59
- message_id: messageID,
60
- reaction: reaction,
61
- actor_id: ctx.userID,
62
- reaction_style: null,
63
- sync_group: 1,
64
- send_attribution: Math.random() < 0.5 ? 65537 : 524289
65
- };
66
- const task = {
67
- failure_count: null,
68
- label: "29",
69
- payload: JSON.stringify(taskPayload),
70
- queue_name: JSON.stringify(["reaction", messageID]),
71
- task_id: taskNumber
72
- };
73
- const content = {
74
- app_id: "2220391788200892",
75
- payload: JSON.stringify({
76
- data_trace_id: null,
77
- epoch_id: parseInt(generateOfflineThreadingID()),
78
- tasks: [task],
79
- version_id: "7158486590867448"
80
- }),
81
- request_id: ctx.wsReqNumber,
82
- type: 3
83
- };
84
- const internalCb = (err) => {
85
- if (typeof callback === "function") callback(err);
86
- if (err) return reject(err);
87
- return resolve();
88
- };
89
- ctx.tasks.set(taskNumber, { type: "set_message_reaction", callback: internalCb });
90
- ctx.mqttClient.publish("/ls_req", JSON.stringify(content), { qos: 1, retain: false });
91
- if (typeof callback !== "function") resolve();
92
- } catch (e) {
93
- if (typeof callback === "function") callback(e);
94
- reject(e);
9
+ if (!ctx.mqttClient) {
10
+ const err = new Error("MQTT client not connected");
11
+ if (typeof callback === 'function') callback(err);
12
+ return reject(err);
13
+ }
14
+ if (!reaction || !messageID || !threadID) {
15
+ const err = new Error("Missing required parameters");
16
+ if (typeof callback === 'function') callback(err);
17
+ return reject(err);
95
18
  }
19
+ const reqID = ++ctx.wsReqNumber;
20
+ const taskID = ++ctx.wsTaskNumber;
21
+ const taskPayload = {
22
+ thread_key: threadID,
23
+ timestamp_ms: getCurrentTimestamp(),
24
+ message_id: messageID,
25
+ reaction: reaction,
26
+ actor_id: ctx.userID,
27
+ reaction_style: null,
28
+ sync_group: 1,
29
+ send_attribution: 65537,
30
+ dataclass_params: null,
31
+ attachment_fbid: null
32
+ };
33
+ const task = {
34
+ failure_count: null,
35
+ label: "29",
36
+ payload: JSON.stringify(taskPayload),
37
+ queue_name: JSON.stringify(["reaction", messageID]),
38
+ task_id: taskID,
39
+ };
40
+ const mqttForm = {
41
+ app_id: "772021112871879",
42
+ payload: JSON.stringify({
43
+ data_trace_id: null,
44
+ epoch_id: parseInt(generateOfflineThreadingID()),
45
+ tasks: [task],
46
+ version_id: "25376272951962053"
47
+ }),
48
+ request_id: reqID,
49
+ type: 3
50
+ };
51
+ const handleResponse = (topic, message) => {
52
+ if (topic !== "/ls_resp") return;
53
+ let json;
54
+ try {
55
+ json = JSON.parse(message.toString());
56
+ json.payload = JSON.parse(json.payload);
57
+ } catch {
58
+ return;
59
+ }
60
+ if (json.request_id !== reqID) return;
61
+ ctx.mqttClient.removeListener("message", handleResponse);
62
+ if (typeof callback === 'function') callback(null, { success: true });
63
+ return resolve({ success: true });
64
+ };
65
+ ctx.mqttClient.on("message", handleResponse);
66
+ ctx.mqttClient.publish("/ls_req", JSON.stringify(mqttForm), { qos: 1, retain: false }, (err) => {
67
+ if (err) {
68
+ ctx.mqttClient.removeListener("message", handleResponse);
69
+ logger("setMessageReaction" + err, "error");
70
+ if (typeof callback === 'function') callback(err);
71
+ return reject(err);
72
+ }
73
+ });
96
74
  });
97
- }
98
-
99
- return function setMessageReaction(reaction, messageID, threadID, callback) {
100
- if (ctx.mqttClient) {
101
- return setMessageReactionMqtt(reaction, messageID, threadID, callback);
102
- }
103
- return setMessageReactionNoMqtt(reaction, messageID, threadID, callback);
104
75
  };
105
76
  };
@@ -75,17 +75,15 @@ module.exports = function createListenMqtt(deps) {
75
75
  const mqttClient = ctx.mqttClient;
76
76
  global.mqttClient = mqttClient;
77
77
  mqttClient.on("error", function (err) {
78
- logger("listenMqtt" + err, "error");
79
- mqttClient.end();
80
- if (ctx.globalOptions.autoReconnect) {
81
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
82
- } else {
83
- // utils.checkLiveCookie(ctx, defaultFuncs).then(res => {
84
- // globalCallback({ type: "stop_listen", error: "Connection refused: Server unavailable" }, null);
85
- // }).catch(err => {
86
- // globalCallback({ type: "account_inactive", error: "Maybe your account is blocked by facebook, please login and check at https://facebook.com" }, null);
87
- // });
78
+ const msg = String(err && err.message ? err.message : err || "");
79
+ if (ctx._ending && /No subscription existed/i.test(msg)) {
80
+ logger("MQTT ignore unsubscribe error during shutdown", "warn");
81
+ return;
88
82
  }
83
+ logger(`MQTT error: ${msg}`, "error");
84
+ mqttClient.end();
85
+ logger("MQTT autoReconnect listenMqtt() in 2000ms", "warn");
86
+ setTimeout(() => listenMqtt(defaultFuncs, api, ctx, globalCallback), 2000);
89
87
  });
90
88
  mqttClient.on("connect", function () {
91
89
  if (process.env.OnStatus === undefined) {
@@ -174,7 +172,7 @@ module.exports = function createListenMqtt(deps) {
174
172
  return;
175
173
  }
176
174
  });
177
- mqttClient.on("close", function () {});
178
- mqttClient.on("disconnect", () => {});
175
+ mqttClient.on("close", function () { });
176
+ mqttClient.on("disconnect", () => { });
179
177
  };
180
178
  };
@@ -3,7 +3,7 @@ const mqtt = require("mqtt");
3
3
  const WebSocket = require("ws");
4
4
  const HttpsProxyAgent = require("https-proxy-agent");
5
5
  const EventEmitter = require("events");
6
- const logger = require("../../../func/logger.js");
6
+ const logger = require("../../../func/logger");
7
7
  const { parseAndCheckLogin } = require("../../utils/client");
8
8
  const { buildProxy, buildStream } = require("./detail/buildStream");
9
9
  const { topics } = require("./detail/constants");
@@ -14,63 +14,117 @@ const markDelivery = require("./core/markDelivery");
14
14
  const getTaskResponseData = require("./core/getTaskResponseData");
15
15
  const parseDelta = createParseDelta({ markDelivery, parseAndCheckLogin });
16
16
  const listenMqtt = createListenMqtt({ WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy, topics, parseDelta, getTaskResponseData, logger });
17
- const getSeqIDFactory = createGetSeqID({ listenMqtt });
17
+ const getSeqIDFactory = createGetSeqID({ parseAndCheckLogin, listenMqtt, logger });
18
18
  module.exports = function (defaultFuncs, api, ctx) {
19
- const identity = function () {};
19
+ const identity = function () { };
20
20
  let globalCallback = identity;
21
21
  function getSeqIDWrapper() {
22
22
  const form = {
23
- av: ctx.globalOptions.pageID,
23
+ av: ctx.userID,
24
24
  queries: JSON.stringify({
25
- o0: {
26
- doc_id: "3336396659757871",
27
- query_params: {
28
- limit: 1,
29
- before: null,
30
- tags: ["INBOX"],
31
- includeDeliveryReceipts: false,
32
- includeSeqID: true
33
- }
34
- }
25
+ o0: { doc_id: "3336396659757871", query_params: { limit: 1, before: null, tags: ["INBOX"], includeDeliveryReceipts: false, includeSeqID: true } }
35
26
  })
36
27
  };
37
- return getSeqIDFactory(defaultFuncs, api, ctx, globalCallback, form);
28
+ logger("MQTT getSeqID call", "info");
29
+ return getSeqIDFactory(defaultFuncs, api, ctx, globalCallback, form).then(() => {
30
+ logger("MQTT getSeqID done", "info");
31
+ }).catch(e => {
32
+ logger(`MQTT getSeqID error: ${e && e.message ? e.message : e}`, "error");
33
+ });
34
+ }
35
+ function isConnected() {
36
+ return !!(ctx.mqttClient && ctx.mqttClient.connected);
37
+ }
38
+ function unsubAll(cb) {
39
+ if (!isConnected()) return cb && cb();
40
+ let pending = topics.length;
41
+ if (!pending) return cb && cb();
42
+ let done = false;
43
+ topics.forEach(t => {
44
+ ctx.mqttClient.unsubscribe(t, err => {
45
+ const msg = String(err && err.message ? err.message : err || "");
46
+ if (msg && /No subscription existed/i.test(msg)) err = null;
47
+ if (--pending === 0 && !done) {
48
+ done = true;
49
+ cb && cb();
50
+ }
51
+ });
52
+ });
53
+ }
54
+ function endQuietly(next) {
55
+ const finish = () => {
56
+ ctx.mqttClient = undefined;
57
+ ctx.lastSeqId = null;
58
+ ctx.syncToken = undefined;
59
+ ctx.t_mqttCalled = false;
60
+ ctx._ending = false;
61
+ next && next();
62
+ };
63
+ try {
64
+ if (ctx.mqttClient) {
65
+ if (isConnected()) {
66
+ try { ctx.mqttClient.publish("/browser_close", "{}"); } catch (_) { }
67
+ }
68
+ ctx.mqttClient.end(true, finish);
69
+ } else finish();
70
+ } catch (_) {
71
+ finish();
72
+ }
73
+ }
74
+ function delayedReconnect() {
75
+ logger("MQTT reconnect in 2000ms", "info");
76
+ setTimeout(() => getSeqIDWrapper(), 2000);
77
+ }
78
+ function forceCycle() {
79
+ ctx._ending = true;
80
+ logger("MQTT force cycle begin", "warn");
81
+ unsubAll(() => {
82
+ endQuietly(() => {
83
+ delayedReconnect();
84
+ });
85
+ });
38
86
  }
39
87
  return function (callback) {
40
88
  class MessageEmitter extends EventEmitter {
41
89
  stopListening(callback2) {
42
- const cb = callback2 || function () {};
90
+ const cb = callback2 || function () { };
91
+ logger("MQTT stop requested", "info");
43
92
  globalCallback = identity;
44
- if (ctx.mqttClient) {
45
- ctx.mqttClient.unsubscribe("/webrtc");
46
- ctx.mqttClient.unsubscribe("/rtc_multi");
47
- ctx.mqttClient.unsubscribe("/onevc");
48
- ctx.mqttClient.publish("/browser_close", "{}");
49
- ctx.mqttClient.end(false, function (...data) {
50
- cb(data);
51
- ctx.mqttClient = undefined;
52
- });
93
+ if (ctx._autoCycleTimer) {
94
+ clearInterval(ctx._autoCycleTimer);
95
+ ctx._autoCycleTimer = null;
96
+ logger("MQTT auto-cycle cleared", "info");
53
97
  }
98
+ ctx._ending = true;
99
+ unsubAll(() => {
100
+ endQuietly(() => {
101
+ logger("MQTT stopped", "info");
102
+ cb();
103
+ delayedReconnect();
104
+ });
105
+ });
54
106
  }
55
107
  async stopListeningAsync() {
56
- return new Promise(resolve => {
57
- this.stopListening(resolve);
58
- });
108
+ return new Promise(resolve => { this.stopListening(resolve); });
59
109
  }
60
110
  }
61
111
  const msgEmitter = new MessageEmitter();
62
112
  globalCallback = callback || function (error, message) {
63
- if (error) {
64
- return msgEmitter.emit("error", error);
65
- }
113
+ if (error) { logger("MQTT emit error", "error"); return msgEmitter.emit("error", error); }
66
114
  msgEmitter.emit("message", message);
67
115
  };
68
116
  if (!ctx.firstListen) ctx.lastSeqId = null;
69
117
  ctx.syncToken = undefined;
70
118
  ctx.t_mqttCalled = false;
71
- if (!ctx.firstListen || !ctx.lastSeqId) {
72
- getSeqIDWrapper();
73
- } else {
119
+ if (ctx._autoCycleTimer) {
120
+ clearInterval(ctx._autoCycleTimer);
121
+ ctx._autoCycleTimer = null;
122
+ }
123
+ ctx._autoCycleTimer = setInterval(forceCycle, 60 * 60 * 1000);
124
+ logger("MQTT auto-cycle enabled 3600000ms", "info");
125
+ if (!ctx.firstListen || !ctx.lastSeqId) getSeqIDWrapper();
126
+ else {
127
+ logger("MQTT starting listenMqtt", "info");
74
128
  listenMqtt(defaultFuncs, api, ctx, globalCallback);
75
129
  }
76
130
  api.stopListening = msgEmitter.stopListening;