@lazyneoaz/metachat 1.0.10 → 1.0.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/metachat",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "type": "commonjs",
5
5
  "types": "src/types/index.d.ts",
6
6
  "description": "Advanced Facebook Chat API client for building Messenger bots — real-time messaging, thread management, MQTT, and session stability.",
@@ -19,7 +19,7 @@ module.exports = (defaultFuncs, api, ctx) => {
19
19
  }
20
20
 
21
21
  try {
22
- if (!ctx.mqttClient) {
22
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
23
23
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
24
24
  }
25
25
 
@@ -37,8 +37,11 @@ module.exports = function (defaultFuncs, api, ctx) {
37
37
  const isAdmin = adminStatus ? 1 : 0;
38
38
  const epochID = utils.generateOfflineThreadingID();
39
39
 
40
+ if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
41
+ if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
42
+
40
43
  if (getType(adminID) === "Array") {
41
- adminID.forEach((id, index) => {
44
+ adminID.forEach((id) => {
42
45
  tasks.push({
43
46
  failure_count: null,
44
47
  label: "25",
@@ -48,7 +51,7 @@ module.exports = function (defaultFuncs, api, ctx) {
48
51
  is_admin: isAdmin
49
52
  }),
50
53
  queue_name: "admin_status",
51
- task_id: index + 1
54
+ task_id: ++ctx.wsTaskNumber
52
55
  });
53
56
  });
54
57
  } else {
@@ -61,11 +64,10 @@ module.exports = function (defaultFuncs, api, ctx) {
61
64
  is_admin: isAdmin
62
65
  }),
63
66
  queue_name: "admin_status",
64
- task_id: 1
67
+ task_id: ++ctx.wsTaskNumber
65
68
  });
66
69
  }
67
70
 
68
- let count_req = 0;
69
71
  const form = JSON.stringify({
70
72
  app_id: "2220391788200892",
71
73
  payload: JSON.stringify({
@@ -73,7 +75,7 @@ module.exports = function (defaultFuncs, api, ctx) {
73
75
  tasks: tasks,
74
76
  version_id: "8798795233522156"
75
77
  }),
76
- request_id: ++count_req,
78
+ request_id: ++ctx.wsReqNumber,
77
79
  type: 3
78
80
  });
79
81
 
@@ -92,12 +94,12 @@ module.exports = function (defaultFuncs, api, ctx) {
92
94
  const epochID = utils.generateOfflineThreadingID();
93
95
 
94
96
  if (getType(adminID) === "Array") {
95
- adminID.forEach((id, index) => {
97
+ adminID.forEach((id) => {
96
98
  tasks.push({
97
99
  label: '25',
98
100
  payload: JSON.stringify({ thread_key: threadID, contact_id: id, is_admin: adminStatus }),
99
101
  queue_name: 'admin_status',
100
- task_id: index + 1,
102
+ task_id: ++ctx.wsTaskNumber,
101
103
  failure_count: null
102
104
  });
103
105
  });
@@ -106,14 +108,14 @@ module.exports = function (defaultFuncs, api, ctx) {
106
108
  label: '25',
107
109
  payload: JSON.stringify({ thread_key: threadID, contact_id: adminID, is_admin: adminStatus }),
108
110
  queue_name: 'admin_status',
109
- task_id: 1,
111
+ task_id: ++ctx.wsTaskNumber,
110
112
  failure_count: null
111
113
  });
112
114
  }
113
115
 
114
116
  const form = {
115
117
  fb_dtsg: ctx.fb_dtsg,
116
- request_id: 1,
118
+ request_id: ++ctx.wsReqNumber,
117
119
  type: 3,
118
120
  payload: {
119
121
  version_id: '3816854585040595',
@@ -33,7 +33,7 @@ module.exports = (defaultFuncs, api, ctx) => {
33
33
  }
34
34
 
35
35
  try {
36
- if (!ctx.mqttClient) {
36
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
37
37
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
38
38
  }
39
39
 
@@ -20,7 +20,7 @@ module.exports = (defaultFuncs, api, ctx) => {
20
20
  }
21
21
 
22
22
  try {
23
- if (!ctx.mqttClient) {
23
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
24
24
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
25
25
  }
26
26
 
@@ -11,18 +11,26 @@ module.exports = (defaultFuncs, api, ctx) => {
11
11
  rejectFunc = reject;
12
12
  });
13
13
 
14
+ if (utils.getType(groupTitle) === "Function") {
15
+ callback = groupTitle;
16
+ groupTitle = null;
17
+ }
18
+
14
19
  if (!callback) {
15
20
  callback = (err, result) => {
16
21
  if (err) return rejectFunc(err);
17
22
  resolveFunc(result);
18
23
  };
24
+ } else {
25
+ const _userCb = callback;
26
+ callback = (err, result) => {
27
+ if (err) { _userCb(err); return rejectFunc(err); }
28
+ _userCb(null, result);
29
+ resolveFunc(result);
30
+ };
19
31
  }
20
32
 
21
33
  try {
22
- if (utils.getType(groupTitle) === "Function") {
23
- callback = groupTitle;
24
- groupTitle = null;
25
- }
26
34
 
27
35
  if (utils.getType(participantIDs) !== "Array") {
28
36
  throw new Error("createNewGroup: participantIDs should be an array.");
@@ -19,7 +19,7 @@ module.exports = (defaultFuncs, api, ctx) => {
19
19
  }
20
20
 
21
21
  try {
22
- if (!ctx.mqttClient) {
22
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
23
23
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
24
24
  }
25
25
 
@@ -35,6 +35,9 @@ module.exports = (defaultFuncs, api, ctx) => {
35
35
  throw new Error("options must be an array with at least 2 options");
36
36
  }
37
37
 
38
+ if (typeof ctx.wsReqNumber !== 'number') ctx.wsReqNumber = 0;
39
+ if (typeof ctx.wsTaskNumber !== 'number') ctx.wsTaskNumber = 0;
40
+
38
41
  const payload = {
39
42
  epoch_id: utils.generateOfflineThreadingID(),
40
43
  tasks: [
@@ -48,7 +51,7 @@ module.exports = (defaultFuncs, api, ctx) => {
48
51
  sync_group: 1
49
52
  }),
50
53
  queue_name: "poll_creation",
51
- task_id: Math.floor(Math.random() * 1001)
54
+ task_id: ++ctx.wsTaskNumber
52
55
  }
53
56
  ],
54
57
  version_id: "8768858626531631"
@@ -1,70 +1,81 @@
1
1
  "use strict";
2
- /**
3
- * @author RFS-ADRENO
4
- * @rewrittenBy Isai Ivanov
5
- */
6
2
 
7
3
  const utils = require('../utils');
8
-
9
- function canBeCalled(func) {
10
- try {
11
- Reflect.apply(func, null, []);
12
- return true;
13
- } catch (error) {
14
- return false;
15
- }
16
- }
4
+ const { publishLsRequestWithAck } = require('../utils/lsRequest');
17
5
 
18
6
  /**
19
- * A function for editing bot's messages.
20
- * @param {string} text - The text with which the bot will edit its messages.
21
- * @param {string} messageID - The message ID of the message the bot will edit.
22
- * @param {Object} callback - Callback for the function.
23
- */
24
-
7
+ * Edits a previously sent bot message via MQTT with ACK confirmation.
8
+ *
9
+ * @param {string} text New text content for the message.
10
+ * @param {string} messageID The ID of the message to edit.
11
+ * @param {Function} [callback] Optional callback(err, result).
12
+ * @returns {Promise}
13
+ */
25
14
  module.exports = function (defaultFuncs, api, ctx) {
26
15
  return function editMessage(text, messageID, callback) {
27
- if (!ctx.mqttClient) {
28
- throw new Error('Not connected to MQTT');
16
+ let resolveFunc, rejectFunc;
17
+ const returnPromise = new Promise((resolve, reject) => {
18
+ resolveFunc = resolve;
19
+ rejectFunc = reject;
20
+ });
21
+
22
+ const cb = (typeof callback === "function")
23
+ ? (err, data) => { callback(err, data); if (err) rejectFunc(err); else resolveFunc(data); }
24
+ : (err, data) => { if (err) rejectFunc(err); else resolveFunc(data); };
25
+
26
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
27
+ return cb(new Error("Not connected to MQTT — call listenMqtt() first")), returnPromise;
29
28
  }
30
29
 
31
- ctx.wsReqNumber += 1;
32
- ctx.wsTaskNumber += 1;
30
+ if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
31
+ if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
33
32
 
34
- const queryPayload = {
35
- message_id: messageID,
36
- text
37
- };
33
+ const requestId = ++ctx.wsReqNumber;
34
+ const taskId = ++ctx.wsTaskNumber;
38
35
 
39
- const query = {
36
+ const task = {
40
37
  failure_count: null,
41
- label: '742',
42
- payload: JSON.stringify(queryPayload),
43
- queue_name: 'edit_message',
44
- task_id: ctx.wsTaskNumber
38
+ label: "742",
39
+ payload: JSON.stringify({ message_id: messageID, text }),
40
+ queue_name: "edit_message",
41
+ task_id: taskId
45
42
  };
46
43
 
47
- const context = {
48
- app_id: '2220391788200892',
49
- payload: {
44
+ const content = {
45
+ app_id: "2220391788200892",
46
+ payload: JSON.stringify({
50
47
  data_trace_id: null,
51
48
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
52
- tasks: [query],
53
- version_id: '6903494529735864'
54
- },
55
- request_id: ctx.wsReqNumber,
49
+ tasks: [task],
50
+ version_id: "6903494529735864"
51
+ }),
52
+ request_id: requestId,
56
53
  type: 3
57
54
  };
58
55
 
59
- context.payload = JSON.stringify(context.payload);
60
-
61
-
62
-
63
-
64
-
65
- ctx.mqttClient.publish('/ls_req', JSON.stringify(context), {
66
- qos: 1, retain: false
56
+ publishLsRequestWithAck({
57
+ client: ctx.mqttClient,
58
+ content,
59
+ requestId,
60
+ timeoutMs: 10000,
61
+ extract: (message) => ({
62
+ success: true,
63
+ messageID,
64
+ text,
65
+ ack: message.payload
66
+ })
67
+ }).then((result) => {
68
+ cb(null, result);
69
+ }).catch((err) => {
70
+ if (err && err.message && err.message.includes("Timeout waiting for LS ACK")) {
71
+ utils.warn("editMessage", "ACK timed out — edit may still have been applied");
72
+ cb(null, { success: true, messageID, text, ackTimeout: true });
73
+ } else {
74
+ utils.error("editMessage", err && err.message ? err.message : err);
75
+ cb(err instanceof Error ? err : new Error(String(err && err.message ? err.message : err)));
76
+ }
67
77
  });
78
+
79
+ return returnPromise;
68
80
  };
69
81
  };
70
-
package/src/apis/emoji.js CHANGED
@@ -69,7 +69,7 @@ module.exports = function (defaultFuncs, api, ctx) {
69
69
  return _callback(new Error("An emoji character is required."));
70
70
  }
71
71
 
72
- if (!ctx.mqttClient) {
72
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
73
73
  return _callback(new Error("Not connected to MQTT"));
74
74
  }
75
75
 
@@ -92,7 +92,7 @@ module.exports = function (defaultFuncs, api, ctx) {
92
92
  };
93
93
 
94
94
  const context = {
95
- app_id: ctx.appID,
95
+ app_id: ctx.appID || '2220391788200892',
96
96
  payload: {
97
97
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
98
98
  tasks: [query],
@@ -48,7 +48,7 @@ module.exports = function (defaultFuncs, api, ctx) {
48
48
  av: ctx.userID,
49
49
  fb_api_req_friendly_name: "CometUserUnfollowMutation",
50
50
  fb_api_caller_class: "RelayModern",
51
- doc_id: "25472099855769847",
51
+ doc_id: "7033020913412778",
52
52
  variables: JSON.stringify({
53
53
  action_render_location: "WWW_COMET_FRIEND_MENU",
54
54
  input: {
@@ -48,6 +48,7 @@ module.exports = function (defaultFuncs, api, ctx) {
48
48
  const timestamp = Date.now();
49
49
  const tid = String(threadID);
50
50
  if (typeof ctx.wsReqNumber !== 'number') ctx.wsReqNumber = 0;
51
+ if (typeof ctx.wsTaskNumber !== 'number') ctx.wsTaskNumber = 0;
51
52
  const reqID = ++ctx.wsReqNumber;
52
53
 
53
54
  const sendPayload = {
@@ -73,7 +74,7 @@ module.exports = function (defaultFuncs, api, ctx) {
73
74
  label: "46",
74
75
  payload: JSON.stringify(sendPayload),
75
76
  queue_name: tid,
76
- task_id: 400,
77
+ task_id: ++ctx.wsTaskNumber,
77
78
  failure_count: null,
78
79
  },
79
80
  {
@@ -84,7 +85,7 @@ module.exports = function (defaultFuncs, api, ctx) {
84
85
  sync_group: 1,
85
86
  }),
86
87
  queue_name: tid,
87
- task_id: 401,
88
+ task_id: ++ctx.wsTaskNumber,
88
89
  failure_count: null,
89
90
  },
90
91
  ],
@@ -54,7 +54,7 @@ module.exports = function (defaultFuncs, api, ctx) {
54
54
  _callback(null, { type: "error_gc", error: "threadID is required." });
55
55
  return returnPromise;
56
56
  }
57
- if (!ctx.mqttClient) {
57
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
58
58
  _callback(null, { type: "error_gc", error: "Not connected to MQTT" });
59
59
  return returnPromise;
60
60
  }
@@ -99,7 +99,7 @@ module.exports = function (defaultFuncs, api, ctx) {
99
99
  }
100
100
 
101
101
  const context = {
102
- app_id: ctx.appID,
102
+ app_id: ctx.appID || '2220391788200892',
103
103
  payload: { epoch_id: parseInt(utils.generateOfflineThreadingID()), tasks: [query], version_id: "24631415369801570" },
104
104
  request_id: ctx.wsReqNumber,
105
105
  type: 3
@@ -121,7 +121,8 @@ module.exports = function (defaultFuncs, api, ctx) {
121
121
  return _callback(null, gcmemberInfo);
122
122
  });
123
123
  } catch (err) {
124
- _callback(null, { type: "error_gc", error: err.message || "An unknown error occurred." });
124
+ utils.error("gcmember", err);
125
+ _callback(err instanceof Error ? err : new Error(err.message || "An unknown error occurred."));
125
126
  }
126
127
 
127
128
  return returnPromise;
@@ -69,7 +69,7 @@ module.exports = function (defaultFuncs, api, ctx) {
69
69
  return _callback(new Error("newName must be a string."));
70
70
  }
71
71
 
72
- if (!ctx.mqttClient) {
72
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
73
73
  return _callback(new Error("Not connected to MQTT"));
74
74
  }
75
75
 
@@ -91,7 +91,7 @@ module.exports = function (defaultFuncs, api, ctx) {
91
91
  };
92
92
 
93
93
  const context = {
94
- app_id: ctx.appID,
94
+ app_id: ctx.appID || '2220391788200892',
95
95
  payload: {
96
96
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
97
97
  tasks: [query],
@@ -46,7 +46,7 @@ module.exports = function (defaultFuncs, api, ctx) {
46
46
  }
47
47
  if (!userID) { _callback(null, { type: "error_gc_rule", error: "userID is required." }); return returnPromise; }
48
48
  if (!threadID) { _callback(null, { type: "error_gc_rule", error: "threadID is required." }); return returnPromise; }
49
- if (!ctx.mqttClient) { _callback(null, { type: "error_gc_rule", error: "Not connected to MQTT" }); return returnPromise; }
49
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) { _callback(null, { type: "error_gc_rule", error: "Not connected to MQTT" }); return returnPromise; }
50
50
 
51
51
  const threadInfo = await api.getThreadInfo(threadID);
52
52
  if (!threadInfo) {
@@ -90,7 +90,7 @@ module.exports = function (defaultFuncs, api, ctx) {
90
90
  task_id: ctx.wsTaskNumber
91
91
  };
92
92
  const context = {
93
- app_id: ctx.appID,
93
+ app_id: ctx.appID || '2220391788200892',
94
94
  payload: {
95
95
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
96
96
  tasks: [query],
@@ -116,7 +116,8 @@ module.exports = function (defaultFuncs, api, ctx) {
116
116
  });
117
117
 
118
118
  } catch (err) {
119
- _callback(null, { type: "error_gc_rule", error: err.message || "An unknown error occurred." });
119
+ utils.error("gcrule", err);
120
+ _callback(err instanceof Error ? err : new Error(err.message || "An unknown error occurred."));
120
121
  }
121
122
 
122
123
  return returnPromise;
@@ -331,6 +331,7 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
331
331
  }));
332
332
 
333
333
  mqttClient.on('connect', guard("connect", async () => {
334
+ const wasReconnect = !ctx._mqttConnected && (ctx._reconnectAttempts || 0) > 0;
334
335
  if (!ctx._mqttConnected) {
335
336
  utils.log("MQTT connected successfully");
336
337
  ctx._mqttConnected = true;
@@ -338,6 +339,17 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
338
339
  ctx._cycling = false;
339
340
  ctx._reconnectAttempts = 0;
340
341
  ctx._mqttQuickCloseCount = 0;
342
+
343
+ // Update reconnect stats and emit lifecycle events
344
+ if (!ctx._reconnectStats) ctx._reconnectStats = { totalAttempts: 0, lastAttemptAt: null, nextAttemptAt: null, lastSuccessAt: null };
345
+ ctx._reconnectStats.nextAttemptAt = null;
346
+ ctx._reconnectStats.lastSuccessAt = Date.now();
347
+ try {
348
+ if (ctx._emitter) {
349
+ const eventName = wasReconnect ? 'reconnected' : 'connected';
350
+ ctx._emitter.emit(eventName, { timestamp: Date.now(), totalReconnects: ctx._reconnectStats.totalAttempts });
351
+ }
352
+ } catch (_) {}
341
353
  if (ctx._reconnectTimer) {
342
354
  clearTimeout(ctx._reconnectTimer);
343
355
  ctx._reconnectTimer = null;
@@ -440,7 +452,7 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
440
452
  ctx.syncToken = jsonMessage.syncToken;
441
453
  }
442
454
  if (jsonMessage.lastIssuedSeqId) {
443
- ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
455
+ ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId, 10);
444
456
  }
445
457
 
446
458
  if (jsonMessage.deltas) {
@@ -583,7 +595,8 @@ const MQTT_DEFAULTS = {
583
595
  autoReconnect: true,
584
596
  watchdogIntervalMs: 60000,
585
597
  staleMs: 300000,
586
- reconnectAfterStop: false
598
+ reconnectAfterStop: false,
599
+ maxReconnectAttempts: 100
587
600
  };
588
601
 
589
602
  function mqttConf(ctx, overrides) {
@@ -812,11 +825,67 @@ module.exports = (defaultFuncs, api, ctx, opts) => {
812
825
  const msg = ((err && err.error) || (err && err.message) || String(err || "")) + detail;
813
826
 
814
827
  if (/Not logged in/i.test(msg)) {
815
- utils.error("MQTT", "Auth error in getSeqID: Not logged in");
828
+ utils.error("MQTT", "Auth error in getSeqID: Not logged in — attempting recovery...");
829
+
830
+ // Step 1: Try token refresh first (fastest, least invasive)
831
+ let tokenRefreshed = false;
832
+ try {
833
+ if (api.tokenRefreshManager && typeof api.tokenRefreshManager.refreshTokens === 'function') {
834
+ utils.log("MQTT", "getSeqID: refreshing tokens before giving up...");
835
+ await api.tokenRefreshManager.refreshTokens(ctx, defaultFuncs, 'https://www.facebook.com');
836
+ tokenRefreshed = true;
837
+ utils.log("MQTT", "getSeqID: token refresh succeeded, scheduling reconnect");
838
+ }
839
+ } catch (refreshErr) {
840
+ utils.warn("MQTT", `getSeqID: token refresh failed: ${refreshErr && refreshErr.message ? refreshErr.message : refreshErr}`);
841
+ }
842
+
843
+ if (tokenRefreshed && ctx.globalOptions.autoReconnect) {
844
+ ctx._reconnectAttempts = Math.max(0, (ctx._reconnectAttempts || 0) - 1);
845
+ const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 3000;
846
+ return scheduleReconnect(baseDelay);
847
+ }
848
+
849
+ // Step 2: Try full auto re-login (email+password) as last resort
850
+ let reloginOk = false;
851
+ try {
852
+ const { globalAutoReLoginManager } = require('../utils/autoReLogin');
853
+ if (globalAutoReLoginManager && globalAutoReLoginManager.isEnabled && globalAutoReLoginManager.isEnabled()) {
854
+ utils.log("MQTT", "getSeqID: attempting auto re-login...");
855
+ reloginOk = await globalAutoReLoginManager.handleSessionExpiry(api, 'https://www.facebook.com', "MQTT getSeqID Not logged in");
856
+ if (reloginOk) {
857
+ utils.log("MQTT", "getSeqID: re-login succeeded, scheduling MQTT reconnect");
858
+ }
859
+ }
860
+ } catch (reloginErr) {
861
+ utils.warn("MQTT", `getSeqID: auto re-login failed: ${reloginErr && reloginErr.message ? reloginErr.message : reloginErr}`);
862
+ }
863
+
864
+ if (reloginOk && ctx.globalOptions.autoReconnect) {
865
+ ctx._reconnectAttempts = 0;
866
+ const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 5000;
867
+ return scheduleReconnect(baseDelay);
868
+ }
869
+
870
+ // Both recovery paths exhausted — emit auth error to signal the user
816
871
  return emitAuthError("not_logged_in", msg);
817
872
  }
818
873
  if (/blocked the login|checkpoint|security check|session.*expir|invalid.*session|authentication.*fail|auth.*fail|login.*block|account.*lock|verification.*requir/i.test(msg)) {
819
874
  utils.error("MQTT", "Auth error in getSeqID: Session/Login blocked");
875
+
876
+ // Still try token refresh for session expiry before giving up
877
+ try {
878
+ if (api.tokenRefreshManager && typeof api.tokenRefreshManager.refreshTokens === 'function') {
879
+ utils.log("MQTT", "getSeqID: refreshing tokens on session expiry...");
880
+ await api.tokenRefreshManager.refreshTokens(ctx, defaultFuncs, 'https://www.facebook.com');
881
+ if (ctx.globalOptions.autoReconnect) {
882
+ ctx._reconnectAttempts = 0;
883
+ const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 5000;
884
+ return scheduleReconnect(baseDelay);
885
+ }
886
+ }
887
+ } catch (_) {}
888
+
820
889
  return emitAuthError("login_blocked", msg);
821
890
  }
822
891