@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 +1 -1
- package/src/apis/addUserToGroup.js +1 -1
- package/src/apis/changeAdminStatus.js +11 -9
- package/src/apis/changeGroupImage.js +1 -1
- package/src/apis/changeThreadColor.js +1 -1
- package/src/apis/createNewGroup.js +12 -4
- package/src/apis/createPoll.js +5 -2
- package/src/apis/editMessage.js +59 -48
- package/src/apis/emoji.js +2 -2
- package/src/apis/follow.js +1 -1
- package/src/apis/forwardAttachment.js +3 -2
- package/src/apis/gcmember.js +4 -3
- package/src/apis/gcname.js +2 -2
- package/src/apis/gcrule.js +4 -3
- package/src/apis/listenMqtt.js +72 -3
- package/src/apis/nickname.js +62 -48
- package/src/apis/pinMessage.js +1 -1
- package/src/apis/removeUserFromGroup.js +7 -0
- package/src/apis/sendEffect.js +3 -2
- package/src/apis/sendMessage.js +6 -2
- package/src/apis/setMessageReaction.js +59 -20
- package/src/apis/setMessageReactionMqtt.js +96 -56
- package/src/apis/setThreadThemeMqtt.js +4 -2
- package/src/apis/shareContact.js +37 -31
- package/src/apis/theme.js +1 -1
- package/src/engine/models/loginHelper.js +5 -0
- package/src/utils/antiSuspension.js +1 -1
- package/src/utils/clients.js +21 -0
- package/src/utils/headers.js +1 -1
- package/src/utils/lsRequest.js +176 -0
- package/src/utils/rateLimiter.js +6 -2
- package/src/utils/tokenRefresh.js +14 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyneoaz/metachat",
|
|
3
|
-
"version": "1.0.
|
|
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.",
|
|
@@ -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
|
|
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:
|
|
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:
|
|
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: ++
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
118
|
+
request_id: ++ctx.wsReqNumber,
|
|
117
119
|
type: 3,
|
|
118
120
|
payload: {
|
|
119
121
|
version_id: '3816854585040595',
|
|
@@ -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.");
|
package/src/apis/createPoll.js
CHANGED
|
@@ -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:
|
|
54
|
+
task_id: ++ctx.wsTaskNumber
|
|
52
55
|
}
|
|
53
56
|
],
|
|
54
57
|
version_id: "8768858626531631"
|
package/src/apis/editMessage.js
CHANGED
|
@@ -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
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @param {string}
|
|
22
|
-
* @param {
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
32
|
-
ctx.wsTaskNumber
|
|
30
|
+
if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
|
|
31
|
+
if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
|
|
33
32
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
text
|
|
37
|
-
};
|
|
33
|
+
const requestId = ++ctx.wsReqNumber;
|
|
34
|
+
const taskId = ++ctx.wsTaskNumber;
|
|
38
35
|
|
|
39
|
-
const
|
|
36
|
+
const task = {
|
|
40
37
|
failure_count: null,
|
|
41
|
-
label:
|
|
42
|
-
payload: JSON.stringify(
|
|
43
|
-
queue_name:
|
|
44
|
-
task_id:
|
|
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
|
|
48
|
-
app_id:
|
|
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: [
|
|
53
|
-
version_id:
|
|
54
|
-
},
|
|
55
|
-
request_id:
|
|
49
|
+
tasks: [task],
|
|
50
|
+
version_id: "6903494529735864"
|
|
51
|
+
}),
|
|
52
|
+
request_id: requestId,
|
|
56
53
|
type: 3
|
|
57
54
|
};
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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],
|
package/src/apis/follow.js
CHANGED
|
@@ -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: "
|
|
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:
|
|
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:
|
|
88
|
+
task_id: ++ctx.wsTaskNumber,
|
|
88
89
|
failure_count: null,
|
|
89
90
|
},
|
|
90
91
|
],
|
package/src/apis/gcmember.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/src/apis/gcname.js
CHANGED
|
@@ -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],
|
package/src/apis/gcrule.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/src/apis/listenMqtt.js
CHANGED
|
@@ -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
|
|