@cexy/hoonfca 1.0.4 → 1.0.5
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 +181 -0
- package/DOCS.md +2636 -0
- package/LICENSE-MIT +21 -21
- package/README.md +632 -0
- package/func/checkUpdate.js +222 -0
- package/func/logger.js +48 -0
- package/index.d.ts +731 -605
- package/index.js +6 -774
- package/module/config.js +34 -0
- package/module/login.js +136 -0
- package/module/loginHelper.js +1034 -0
- package/module/options.js +45 -0
- package/package.json +54 -19
- package/src/api/action/addExternalModule.js +25 -0
- package/src/api/action/changeAvatar.js +137 -0
- package/src/{changeBio.js → api/action/changeBio.js} +75 -76
- package/src/api/action/enableAutoSaveAppState.js +73 -0
- package/src/{getCurrentUserID.js → api/action/getCurrentUserID.js} +7 -7
- package/src/{handleFriendRequest.js → api/action/handleFriendRequest.js} +57 -57
- package/src/api/action/logout.js +76 -0
- package/src/api/action/refreshFb_dtsg.js +48 -0
- package/src/api/action/setPostReaction.js +106 -0
- package/src/api/action/unfriend.js +54 -0
- package/src/api/http/httpGet.js +46 -0
- package/src/api/http/httpPost.js +52 -0
- package/src/api/http/postFormData.js +47 -0
- package/src/api/messaging/addUserToGroup.js +68 -0
- package/src/api/messaging/changeAdminStatus.js +122 -0
- package/src/api/messaging/changeArchivedStatus.js +55 -0
- package/src/api/messaging/changeBlockedStatus.js +48 -0
- package/src/api/messaging/changeGroupImage.js +90 -0
- package/src/api/messaging/changeNickname.js +70 -0
- package/src/api/messaging/changeThreadColor.js +79 -0
- package/src/api/messaging/changeThreadEmoji.js +106 -0
- package/src/{createNewGroup.js → api/messaging/createNewGroup.js} +88 -88
- package/src/api/messaging/createPoll.js +43 -0
- package/src/api/messaging/createThemeAI.js +98 -0
- package/src/{deleteMessage.js → api/messaging/deleteMessage.js} +56 -56
- package/src/{deleteThread.js → api/messaging/deleteThread.js} +56 -56
- package/src/api/messaging/editMessage.js +68 -0
- package/src/api/messaging/forwardAttachment.js +51 -0
- package/src/{getEmojiUrl.js → api/messaging/getEmojiUrl.js} +8 -8
- package/src/{getFriendsList.js → api/messaging/getFriendsList.js} +82 -84
- package/src/api/messaging/getMessage.js +829 -0
- package/src/api/messaging/getThemePictures.js +62 -0
- package/src/api/messaging/handleMessageRequest.js +65 -0
- package/src/{markAsDelivered.js → api/messaging/markAsDelivered.js} +57 -58
- package/src/{markAsRead.js → api/messaging/markAsRead.js} +88 -88
- package/src/{markAsReadAll.js → api/messaging/markAsReadAll.js} +49 -50
- package/src/api/messaging/markAsSeen.js +61 -0
- package/src/{muteThread.js → api/messaging/muteThread.js} +50 -52
- package/src/api/messaging/removeUserFromGroup.js +106 -0
- package/src/{resolvePhotoUrl.js → api/messaging/resolvePhotoUrl.js} +43 -45
- package/src/api/messaging/scheduler.js +264 -0
- package/src/{searchForThread.js → api/messaging/searchForThread.js} +52 -53
- package/src/api/messaging/sendMessage.js +272 -0
- package/src/api/messaging/sendTypingIndicator.js +67 -0
- package/src/api/messaging/setMessageReaction.js +76 -0
- package/src/api/messaging/setTitle.js +119 -0
- package/src/{shareContact.js → api/messaging/shareContact.js} +49 -53
- package/src/api/messaging/threadColors.js +128 -0
- package/src/api/messaging/unsendMessage.js +81 -0
- package/src/api/messaging/uploadAttachment.js +94 -0
- package/src/api/socket/core/connectMqtt.js +255 -0
- package/src/api/socket/core/emitAuth.js +106 -0
- package/src/api/socket/core/getSeqID.js +40 -0
- package/src/api/socket/core/getTaskResponseData.js +22 -0
- package/src/api/socket/core/markDelivery.js +12 -0
- package/src/api/socket/core/parseDelta.js +391 -0
- package/src/api/socket/detail/buildStream.js +208 -0
- package/src/api/socket/detail/constants.js +24 -0
- package/src/api/socket/listenMqtt.js +364 -0
- package/src/api/socket/middleware/index.js +216 -0
- package/src/{getThreadHistory.js → api/threads/getThreadHistory.js} +664 -647
- package/src/api/threads/getThreadInfo.js +438 -0
- package/src/{getThreadList.js → api/threads/getThreadList.js} +293 -292
- package/src/{getThreadPictures.js → api/threads/getThreadPictures.js} +78 -79
- package/src/{getUserID.js → api/users/getUserID.js} +65 -66
- package/src/api/users/getUserInfo.js +327 -0
- package/src/api/users/getUserInfoV2.js +134 -0
- package/src/core/sendReqMqtt.js +96 -0
- package/src/database/models/index.js +87 -0
- package/src/database/models/thread.js +45 -0
- package/src/database/models/user.js +46 -0
- package/src/database/threadData.js +98 -0
- package/src/database/userData.js +89 -0
- package/src/utils/client.js +320 -0
- package/src/utils/constants.js +23 -0
- package/{utils.js → src/utils/format.js} +1115 -1447
- package/src/utils/headers.js +115 -0
- package/src/utils/request.js +305 -0
- package/.travis.yml +0 -6
- package/src/addExternalModule.js +0 -23
- package/src/addUserToGroup.js +0 -113
- package/src/changeAdminStatus.js +0 -95
- package/src/changeApprovalMode.js +0 -79
- package/src/changeArchivedStatus.js +0 -55
- package/src/changeBlockedStatus.js +0 -47
- package/src/changeBlockedStatusMqtt.js +0 -86
- package/src/changeGroupImage.js +0 -133
- package/src/changeNickname.js +0 -59
- package/src/changeThreadColor.js +0 -71
- package/src/changeThreadEmoji.js +0 -55
- package/src/createPoll.js +0 -130
- package/src/editMessage.js +0 -63
- package/src/forwardAttachment.js +0 -60
- package/src/forwardMessage.js +0 -63
- package/src/getGroupsList.js +0 -83
- package/src/getMarketplace.js +0 -84
- package/src/getNotifications.js +0 -86
- package/src/getPagesList.js +0 -83
- package/src/getStories.js +0 -88
- package/src/getThreadInfo.js +0 -222
- package/src/getUserInfo.js +0 -128
- package/src/handleMessageRequest.js +0 -65
- package/src/httpGet.js +0 -58
- package/src/httpPost.js +0 -58
- package/src/listenMqtt.js +0 -1018
- package/src/logout.js +0 -75
- package/src/markAsSeen.js +0 -61
- package/src/pinMessage.js +0 -61
- package/src/removeUserFromGroup.js +0 -79
- package/src/searchUsers.js +0 -89
- package/src/sendComment.js +0 -161
- package/src/sendMessage.js +0 -457
- package/src/sendTypingIndicator.js +0 -179
- package/src/setBio.js +0 -86
- package/src/setMessageReaction.js +0 -186
- package/src/setPostReaction.js +0 -71
- package/src/setTheme.js +0 -313
- package/src/setTitle.js +0 -90
- package/src/shareLink.js +0 -62
- package/src/threadColors.js +0 -57
- package/src/unfriend.js +0 -53
- package/src/unsendMessage.js +0 -104
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function(_defaultFuncs, _api, _ctx) {
|
|
4
|
+
// Currently the only colors that can be passed to api.changeThreadColor(); may change if Facebook adds more
|
|
5
|
+
return {
|
|
6
|
+
//Old hex colors.
|
|
7
|
+
////MessengerBlue: null,
|
|
8
|
+
////Viking: "#44bec7",
|
|
9
|
+
////GoldenPoppy: "#ffc300",
|
|
10
|
+
////RadicalRed: "#fa3c4c",
|
|
11
|
+
////Shocking: "#d696bb",
|
|
12
|
+
////PictonBlue: "#6699cc",
|
|
13
|
+
////FreeSpeechGreen: "#13cf13",
|
|
14
|
+
////Pumpkin: "#ff7e29",
|
|
15
|
+
////LightCoral: "#e68585",
|
|
16
|
+
////MediumSlateBlue: "#7646ff",
|
|
17
|
+
////DeepSkyBlue: "#20cef5",
|
|
18
|
+
////Fern: "#67b868",
|
|
19
|
+
////Cameo: "#d4a88c",
|
|
20
|
+
////BrilliantRose: "#ff5ca1",
|
|
21
|
+
////BilobaFlower: "#a695c7"
|
|
22
|
+
|
|
23
|
+
//#region This part is for backward compatibly
|
|
24
|
+
//trying to match the color one-by-one. kill me plz
|
|
25
|
+
MessengerBlue: "196241301102133", //DefaultBlue
|
|
26
|
+
Viking: "1928399724138152", //TealBlue
|
|
27
|
+
GoldenPoppy: "174636906462322", //Yellow
|
|
28
|
+
RadicalRed: "2129984390566328", //Red
|
|
29
|
+
Shocking: "2058653964378557", //LavenderPurple
|
|
30
|
+
FreeSpeechGreen: "2136751179887052", //Green
|
|
31
|
+
Pumpkin: "175615189761153", //Orange
|
|
32
|
+
LightCoral: "980963458735625", //CoralPink
|
|
33
|
+
MediumSlateBlue: "234137870477637", //BrightPurple
|
|
34
|
+
DeepSkyBlue: "2442142322678320", //AquaBlue
|
|
35
|
+
BrilliantRose: "169463077092846", //HotPink
|
|
36
|
+
DefaultBlue: "196241301102133",
|
|
37
|
+
HotPink: "169463077092846",
|
|
38
|
+
AquaBlue: "2442142322678320",
|
|
39
|
+
BrightPurple: "234137870477637",
|
|
40
|
+
CoralPink: "980963458735625",
|
|
41
|
+
Orange: "175615189761153",
|
|
42
|
+
Green: "2136751179887052",
|
|
43
|
+
LavenderPurple: "2058653964378557",
|
|
44
|
+
Red: "2129984390566328",
|
|
45
|
+
Yellow: "174636906462322",
|
|
46
|
+
TealBlue: "1928399724138152",
|
|
47
|
+
Aqua: "417639218648241",
|
|
48
|
+
Mango: "930060997172551",
|
|
49
|
+
Berry: "164535220883264",
|
|
50
|
+
Citrus: "370940413392601",
|
|
51
|
+
Candy: "205488546921017",
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* July 06, 2022
|
|
55
|
+
* added by @NTKhang
|
|
56
|
+
*/
|
|
57
|
+
Earth: "1833559466821043",
|
|
58
|
+
Support: "365557122117011",
|
|
59
|
+
Music: "339021464972092",
|
|
60
|
+
Pride: "1652456634878319",
|
|
61
|
+
DoctorStrange: "538280997628317",
|
|
62
|
+
LoFi: "1060619084701625",
|
|
63
|
+
Sky: "3190514984517598",
|
|
64
|
+
LunarNewYear: "357833546030778",
|
|
65
|
+
Celebration: "627144732056021",
|
|
66
|
+
Chill: "390127158985345",
|
|
67
|
+
StrangerThings: "1059859811490132",
|
|
68
|
+
Dune: "1455149831518874",
|
|
69
|
+
Care: "275041734441112",
|
|
70
|
+
Astrology: "3082966625307060",
|
|
71
|
+
JBalvin: "184305226956268",
|
|
72
|
+
Birthday: "621630955405500",
|
|
73
|
+
Cottagecore: "539927563794799",
|
|
74
|
+
Ocean: "736591620215564",
|
|
75
|
+
Love: "741311439775765",
|
|
76
|
+
TieDye: "230032715012014",
|
|
77
|
+
Monochrome: "788274591712841",
|
|
78
|
+
Default: "3259963564026002",
|
|
79
|
+
Rocket: "582065306070020",
|
|
80
|
+
Berry2: "724096885023603",
|
|
81
|
+
Candy2: "624266884847972",
|
|
82
|
+
Unicorn: "273728810607574",
|
|
83
|
+
Tropical: "262191918210707",
|
|
84
|
+
Maple: "2533652183614000",
|
|
85
|
+
Sushi: "909695489504566",
|
|
86
|
+
Citrus2: "557344741607350",
|
|
87
|
+
Lollipop: "280333826736184",
|
|
88
|
+
Shadow: "271607034185782",
|
|
89
|
+
Rose: "1257453361255152",
|
|
90
|
+
Lavender: "571193503540759",
|
|
91
|
+
Tulip: "2873642949430623",
|
|
92
|
+
Classic: "3273938616164733",
|
|
93
|
+
Peach: "3022526817824329",
|
|
94
|
+
Honey: "672058580051520",
|
|
95
|
+
Kiwi: "3151463484918004",
|
|
96
|
+
Grape: "193497045377796",
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* July 15, 2022
|
|
100
|
+
* added by @NTKhang
|
|
101
|
+
*/
|
|
102
|
+
NonBinary: "737761000603635",
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* November 25, 2022
|
|
106
|
+
* added by @NTKhang
|
|
107
|
+
*/
|
|
108
|
+
ThankfulForFriends: "1318983195536293",
|
|
109
|
+
Transgender: "504518465021637",
|
|
110
|
+
TaylorSwift: "769129927636836",
|
|
111
|
+
NationalComingOutDay: "788102625833584",
|
|
112
|
+
Autumn: "822549609168155",
|
|
113
|
+
Cyberpunk2077: "780962576430091",
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* May 13, 2023
|
|
117
|
+
*/
|
|
118
|
+
MothersDay: "1288506208402340",
|
|
119
|
+
APAHM: "121771470870245",
|
|
120
|
+
Parenthood: "810978360551741",
|
|
121
|
+
StarWars: "1438011086532622",
|
|
122
|
+
GuardianOfTheGalaxy: "101275642962533",
|
|
123
|
+
Bloom: "158263147151440",
|
|
124
|
+
BubbleTea: "195296273246380",
|
|
125
|
+
Basketball: "6026716157422736",
|
|
126
|
+
ElephantsAndFlowers: "693996545771691"
|
|
127
|
+
};
|
|
128
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { generateOfflineThreadingID } = require("../../utils/format.js");
|
|
4
|
+
const log = require("npmlog");
|
|
5
|
+
|
|
6
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
7
|
+
return function unsendMessage(messageID, threadID, callback) {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
if (!ctx.mqttClient) {
|
|
10
|
+
const err = new Error("Not connected to MQTT");
|
|
11
|
+
callback?.(err);
|
|
12
|
+
return reject(err);
|
|
13
|
+
}
|
|
14
|
+
const reqID = ++ctx.wsReqNumber;
|
|
15
|
+
const taskID = ++ctx.wsTaskNumber;
|
|
16
|
+
const taskPayload = {
|
|
17
|
+
message_id: messageID,
|
|
18
|
+
thread_key: threadID,
|
|
19
|
+
sync_group: 1,
|
|
20
|
+
};
|
|
21
|
+
const task = {
|
|
22
|
+
failure_count: null,
|
|
23
|
+
label: "33",
|
|
24
|
+
payload: JSON.stringify(taskPayload),
|
|
25
|
+
queue_name: "unsend_message",
|
|
26
|
+
task_id: taskID,
|
|
27
|
+
};
|
|
28
|
+
const content = {
|
|
29
|
+
app_id: "2220391788200892",
|
|
30
|
+
payload: JSON.stringify({
|
|
31
|
+
tasks: [task],
|
|
32
|
+
epoch_id: parseInt(generateOfflineThreadingID()),
|
|
33
|
+
version_id: "25393437286970779",
|
|
34
|
+
}),
|
|
35
|
+
request_id: reqID,
|
|
36
|
+
type: 3,
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
ctx.mqttClient.publish("/ls_req", JSON.stringify(content), {
|
|
40
|
+
qos: 1,
|
|
41
|
+
retain: false
|
|
42
|
+
});
|
|
43
|
+
} catch (err) {
|
|
44
|
+
log.error("unsendMessage (MQTT publish failed)", err);
|
|
45
|
+
callback?.(err);
|
|
46
|
+
return reject(err);
|
|
47
|
+
}
|
|
48
|
+
const handleRes = (topic, message) => {
|
|
49
|
+
if (topic !== "/ls_resp") return;
|
|
50
|
+
let jsonMsg;
|
|
51
|
+
try {
|
|
52
|
+
jsonMsg = JSON.parse(message.toString());
|
|
53
|
+
jsonMsg.payload = JSON.parse(jsonMsg.payload);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (jsonMsg.request_id !== reqID) return;
|
|
58
|
+
ctx.mqttClient.removeListener("message", handleRes);
|
|
59
|
+
try {
|
|
60
|
+
const msgID = jsonMsg.payload.step?.[1]?.[2]?.[2]?.[1]?.[2];
|
|
61
|
+
const msgReplace = jsonMsg.payload.step?.[1]?.[2]?.[2]?.[1]?.[4];
|
|
62
|
+
if (msgID && msgReplace) {
|
|
63
|
+
const bodies = {
|
|
64
|
+
body: msgReplace,
|
|
65
|
+
messageID: msgID
|
|
66
|
+
};
|
|
67
|
+
callback?.(null, bodies);
|
|
68
|
+
return resolve(bodies);
|
|
69
|
+
} else {
|
|
70
|
+
callback?.(null, { success: true });
|
|
71
|
+
return resolve({ success: true });
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
callback?.(null, { success: true });
|
|
75
|
+
return resolve({ success: true });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
ctx.mqttClient.on("message", handleRes);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const log = require("npmlog");
|
|
3
|
+
const { parseAndCheckLogin } = require("../../utils/client");
|
|
4
|
+
const { getType } = require("../../utils/format");
|
|
5
|
+
const { isReadableStream } = require("../../utils/constants");
|
|
6
|
+
module.exports = function(defaultFuncs, api, ctx) {
|
|
7
|
+
function upload(attachments, callback) {
|
|
8
|
+
callback = callback || function() {};
|
|
9
|
+
const uploads = [];
|
|
10
|
+
|
|
11
|
+
// create an array of promises
|
|
12
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
13
|
+
if (!isReadableStream(attachments[i])) {
|
|
14
|
+
throw {
|
|
15
|
+
error:
|
|
16
|
+
"Attachment should be a readable stream and not " +
|
|
17
|
+
getType(attachments[i]) +
|
|
18
|
+
"."
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const form = {
|
|
23
|
+
farr: attachments[i]
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
uploads.push(
|
|
27
|
+
defaultFuncs
|
|
28
|
+
.postFormData(
|
|
29
|
+
"https://www.facebook.com/ajax/mercury/upload.php",
|
|
30
|
+
ctx.jar,
|
|
31
|
+
form,
|
|
32
|
+
{}
|
|
33
|
+
)
|
|
34
|
+
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
35
|
+
.then(function(resData) {
|
|
36
|
+
if (resData.error) {
|
|
37
|
+
throw resData;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// We have to return the data unformatted unless we want to change it
|
|
41
|
+
// back in sendMessage.
|
|
42
|
+
return resData.payload.metadata[0];
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// resolve all promises
|
|
48
|
+
Promise.all(uploads)
|
|
49
|
+
.then(function(resData) {
|
|
50
|
+
callback(null, resData);
|
|
51
|
+
})
|
|
52
|
+
.catch(function(err) {
|
|
53
|
+
log.error("uploadAttachment", err);
|
|
54
|
+
return callback(err);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return function uploadAttachment(attachments, callback) {
|
|
59
|
+
if (
|
|
60
|
+
!attachments &&
|
|
61
|
+
!isReadableStream(attachments) &&
|
|
62
|
+
!getType(attachments) === "Array" &&
|
|
63
|
+
getType(attachments) === "Array" && !attachments.length
|
|
64
|
+
)
|
|
65
|
+
throw { error: "Please pass an attachment or an array of attachments." };
|
|
66
|
+
|
|
67
|
+
let resolveFunc = function() {};
|
|
68
|
+
let rejectFunc = function() {};
|
|
69
|
+
const returnPromise = new Promise(function(resolve, reject) {
|
|
70
|
+
resolveFunc = resolve;
|
|
71
|
+
rejectFunc = reject;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!callback) {
|
|
75
|
+
callback = function(err, info) {
|
|
76
|
+
if (err) {
|
|
77
|
+
return rejectFunc(err);
|
|
78
|
+
}
|
|
79
|
+
resolveFunc(info);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (getType(attachments) !== "Array") attachments = [attachments];
|
|
84
|
+
|
|
85
|
+
upload(attachments, (err, info) => {
|
|
86
|
+
if (err) {
|
|
87
|
+
return callback(err);
|
|
88
|
+
}
|
|
89
|
+
callback(null, info);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return returnPromise;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { formatID } = require("../../../utils/format");
|
|
3
|
+
module.exports = function createListenMqtt(deps) {
|
|
4
|
+
const { WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy,
|
|
5
|
+
topics, parseDelta, getTaskResponseData, logger, emitAuth
|
|
6
|
+
} = deps;
|
|
7
|
+
|
|
8
|
+
return function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
9
|
+
|
|
10
|
+
function scheduleReconnect(delayMs) {
|
|
11
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
12
|
+
const ms = typeof delayMs === "number" ? delayMs : d;
|
|
13
|
+
if (ctx._reconnectTimer) {
|
|
14
|
+
logger("mqtt reconnect already scheduled", "warn");
|
|
15
|
+
return; // debounce
|
|
16
|
+
}
|
|
17
|
+
if (ctx._ending) {
|
|
18
|
+
logger("mqtt reconnect skipped - ending", "warn");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
logger(`mqtt will reconnect in ${ms}ms`, "warn");
|
|
22
|
+
ctx._reconnectTimer = setTimeout(() => {
|
|
23
|
+
ctx._reconnectTimer = null;
|
|
24
|
+
if (!ctx._ending) {
|
|
25
|
+
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
26
|
+
}
|
|
27
|
+
}, ms);
|
|
28
|
+
}
|
|
29
|
+
function isEndingLikeError(msg) {
|
|
30
|
+
return /No subscription existed|client disconnecting|socket hang up|ECONNRESET/i.test(msg || "");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const chatOn = ctx.globalOptions.online;
|
|
34
|
+
const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
|
|
35
|
+
const username = {
|
|
36
|
+
u: ctx.userID, s: sessionID, chat_on: chatOn, fg: false, d: ctx.clientId,
|
|
37
|
+
ct: "websocket", aid: 219994525426954, aids: null, mqtt_sid: "",
|
|
38
|
+
cp: 3, ecp: 10, st: [], pm: [], dc: "", no_auto_fg: true, gas: null, pack: [], p: null, php_override: ""
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const cookies = api.getCookies();
|
|
42
|
+
let host;
|
|
43
|
+
if (ctx.mqttEndpoint) host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${ctx.clientId}`;
|
|
44
|
+
else if (ctx.region) host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${ctx.clientId}`;
|
|
45
|
+
else host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${ctx.clientId}`;
|
|
46
|
+
|
|
47
|
+
const options = {
|
|
48
|
+
clientId: "mqttwsclient",
|
|
49
|
+
protocolId: "MQIsdp",
|
|
50
|
+
protocolVersion: 3,
|
|
51
|
+
username: JSON.stringify(username),
|
|
52
|
+
clean: true,
|
|
53
|
+
wsOptions: {
|
|
54
|
+
headers: {
|
|
55
|
+
Cookie: cookies,
|
|
56
|
+
Origin: "https://www.facebook.com",
|
|
57
|
+
"User-Agent": ctx.globalOptions.userAgent || "Mozilla/5.0",
|
|
58
|
+
Referer: "https://www.facebook.com/",
|
|
59
|
+
Host: "edge-chat.facebook.com",
|
|
60
|
+
Connection: "Upgrade",
|
|
61
|
+
Pragma: "no-cache",
|
|
62
|
+
"Cache-Control": "no-cache",
|
|
63
|
+
Upgrade: "websocket",
|
|
64
|
+
"Sec-WebSocket-Version": "13",
|
|
65
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
66
|
+
"Accept-Language": "vi,en;q=0.9",
|
|
67
|
+
"Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
|
|
68
|
+
},
|
|
69
|
+
origin: "https://www.facebook.com",
|
|
70
|
+
protocolVersion: 13,
|
|
71
|
+
binaryType: "arraybuffer"
|
|
72
|
+
},
|
|
73
|
+
keepalive: 30,
|
|
74
|
+
reschedulePings: true,
|
|
75
|
+
reconnectPeriod: 0,
|
|
76
|
+
connectTimeout: 5000
|
|
77
|
+
};
|
|
78
|
+
if (ctx.globalOptions.proxy !== undefined) {
|
|
79
|
+
const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
|
|
80
|
+
options.wsOptions.agent = agent;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ctx.mqttClient = new mqtt.Client(
|
|
84
|
+
() => buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()),
|
|
85
|
+
options
|
|
86
|
+
);
|
|
87
|
+
const mqttClient = ctx.mqttClient;
|
|
88
|
+
// Remove global reference to prevent memory leak
|
|
89
|
+
// Only set if needed for debugging, but clear on cleanup
|
|
90
|
+
if (process.env.DEBUG_MQTT) {
|
|
91
|
+
global.mqttClient = mqttClient;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
mqttClient.on("error", function (err) {
|
|
95
|
+
const msg = String(err && err.message ? err.message : err || "");
|
|
96
|
+
if ((ctx._ending || ctx._cycling) && /No subscription existed|client disconnecting/i.test(msg)) {
|
|
97
|
+
logger(`mqtt expected during shutdown: ${msg}`, "info");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (/Not logged in|Not logged in.|blocked the login|401|403/i.test(msg)) {
|
|
102
|
+
try {
|
|
103
|
+
if (mqttClient && mqttClient.connected) {
|
|
104
|
+
mqttClient.end(true);
|
|
105
|
+
}
|
|
106
|
+
} catch (_) { }
|
|
107
|
+
return emitAuth(ctx, api, globalCallback,
|
|
108
|
+
/blocked/i.test(msg) ? "login_blocked" : "not_logged_in",
|
|
109
|
+
msg
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
logger(`mqtt error: ${msg}`, "error");
|
|
113
|
+
try {
|
|
114
|
+
if (mqttClient && mqttClient.connected) {
|
|
115
|
+
mqttClient.end(true);
|
|
116
|
+
}
|
|
117
|
+
} catch (_) { }
|
|
118
|
+
if (ctx._ending || ctx._cycling) return;
|
|
119
|
+
|
|
120
|
+
if (ctx.globalOptions.autoReconnect && !ctx._ending) {
|
|
121
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
122
|
+
logger(`mqtt autoReconnect listenMqtt() in ${d}ms`, "warn");
|
|
123
|
+
// Use scheduleReconnect to prevent multiple reconnections
|
|
124
|
+
scheduleReconnect(d);
|
|
125
|
+
} else {
|
|
126
|
+
globalCallback({ type: "stop_listen", error: msg || "Connection refused" }, null);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
mqttClient.on("connect", function () {
|
|
131
|
+
if (process.env.OnStatus === undefined) {
|
|
132
|
+
logger("fca-unoffcial premium", "info");
|
|
133
|
+
process.env.OnStatus = true;
|
|
134
|
+
}
|
|
135
|
+
ctx._cycling = false;
|
|
136
|
+
|
|
137
|
+
topics.forEach(t => mqttClient.subscribe(t));
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
const queue = {
|
|
141
|
+
sync_api_version: 11, max_deltas_able_to_process: 100, delta_batch_size: 500,
|
|
142
|
+
encoding: "JSON", entity_fbid: ctx.userID, initial_titan_sequence_id: ctx.lastSeqId, device_params: null
|
|
143
|
+
};
|
|
144
|
+
const topic = ctx.syncToken ? "/messenger_sync_get_diffs" : "/messenger_sync_create_queue";
|
|
145
|
+
if (ctx.syncToken) { queue.last_seq_id = ctx.lastSeqId; queue.sync_token = ctx.syncToken; }
|
|
146
|
+
mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
|
|
147
|
+
mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
|
|
148
|
+
mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
|
|
149
|
+
const d = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
|
|
150
|
+
let rTimeout = setTimeout(function () {
|
|
151
|
+
rTimeout = null;
|
|
152
|
+
if (ctx._ending) {
|
|
153
|
+
logger("mqtt t_ms timeout skipped - ending", "warn");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
logger(`mqtt t_ms timeout, cycling in ${d}ms`, "warn");
|
|
157
|
+
try {
|
|
158
|
+
if (mqttClient && mqttClient.connected) {
|
|
159
|
+
mqttClient.end(true);
|
|
160
|
+
}
|
|
161
|
+
} catch (_) { }
|
|
162
|
+
scheduleReconnect(d);
|
|
163
|
+
}, 5000);
|
|
164
|
+
|
|
165
|
+
// Store timeout reference for cleanup
|
|
166
|
+
ctx._rTimeout = rTimeout;
|
|
167
|
+
|
|
168
|
+
ctx.tmsWait = function () {
|
|
169
|
+
if (rTimeout) {
|
|
170
|
+
clearTimeout(rTimeout);
|
|
171
|
+
rTimeout = null;
|
|
172
|
+
}
|
|
173
|
+
if (ctx._rTimeout) {
|
|
174
|
+
delete ctx._rTimeout;
|
|
175
|
+
}
|
|
176
|
+
if (ctx.globalOptions.emitReady) globalCallback({ type: "ready", error: null });
|
|
177
|
+
delete ctx.tmsWait;
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
mqttClient.on("message", function (topic, message) {
|
|
182
|
+
if (ctx._ending) return; // Ignore messages if ending
|
|
183
|
+
try {
|
|
184
|
+
let jsonMessage = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
|
|
185
|
+
try {
|
|
186
|
+
jsonMessage = JSON.parse(jsonMessage);
|
|
187
|
+
} catch (parseErr) {
|
|
188
|
+
logger(`mqtt message parse error for topic ${topic}: ${parseErr && parseErr.message ? parseErr.message : String(parseErr)}`, "warn");
|
|
189
|
+
jsonMessage = {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (jsonMessage.type === "jewel_requests_add") {
|
|
193
|
+
globalCallback(null, { type: "friend_request_received", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
|
|
194
|
+
} else if (jsonMessage.type === "jewel_requests_remove_old") {
|
|
195
|
+
globalCallback(null, { type: "friend_request_cancel", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
|
|
196
|
+
} else if (topic === "/t_ms") {
|
|
197
|
+
if (ctx.tmsWait && typeof ctx.tmsWait == "function") ctx.tmsWait();
|
|
198
|
+
if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
|
|
199
|
+
ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
|
|
200
|
+
ctx.syncToken = jsonMessage.syncToken;
|
|
201
|
+
}
|
|
202
|
+
if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
|
|
203
|
+
for (const dlt of (jsonMessage.deltas || [])) {
|
|
204
|
+
parseDelta(defaultFuncs, api, ctx, globalCallback, { delta: dlt });
|
|
205
|
+
}
|
|
206
|
+
} else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
|
|
207
|
+
const typ = {
|
|
208
|
+
type: "typ",
|
|
209
|
+
isTyping: !!jsonMessage.state,
|
|
210
|
+
from: jsonMessage.sender_fbid.toString(),
|
|
211
|
+
threadID: formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
|
|
212
|
+
};
|
|
213
|
+
globalCallback(null, typ);
|
|
214
|
+
} else if (topic === "/orca_presence") {
|
|
215
|
+
if (!ctx.globalOptions.updatePresence) {
|
|
216
|
+
for (const data of (jsonMessage.list || [])) {
|
|
217
|
+
const presence = { type: "presence", userID: String(data.u), timestamp: data.l * 1000, statuses: data.p };
|
|
218
|
+
globalCallback(null, presence);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} else if (topic === "/ls_resp") {
|
|
222
|
+
const parsedPayload = JSON.parse(jsonMessage.payload);
|
|
223
|
+
const reqID = jsonMessage.request_id;
|
|
224
|
+
if (ctx["tasks"].has(reqID)) {
|
|
225
|
+
const taskData = ctx["tasks"].get(reqID);
|
|
226
|
+
const { type: taskType, callback: taskCallback } = taskData;
|
|
227
|
+
const taskRespData = getTaskResponseData(taskType, parsedPayload);
|
|
228
|
+
if (taskRespData == null) taskCallback("error", null);
|
|
229
|
+
else taskCallback(null, Object.assign({ type: taskType, reqID }, taskRespData));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} catch (ex) {
|
|
233
|
+
const errMsg = ex && ex.message ? ex.message : String(ex || "Unknown error");
|
|
234
|
+
logger(`mqtt message handler error: ${errMsg}`, "error");
|
|
235
|
+
// Don't crash on message parsing errors, just log and continue
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
mqttClient.on("close", function () {
|
|
240
|
+
if (ctx._ending || ctx._cycling) {
|
|
241
|
+
logger("mqtt close expected", "info");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
logger("mqtt connection closed", "warn");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
mqttClient.on("disconnect", () => {
|
|
248
|
+
if (ctx._ending || ctx._cycling) {
|
|
249
|
+
logger("mqtt disconnect expected", "info");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
logger("mqtt disconnected", "warn");
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
module.exports = function createEmitAuth({ logger }) {
|
|
3
|
+
return function emitAuth(ctx, api, globalCallback, reason, detail) {
|
|
4
|
+
// Clean up all timers
|
|
5
|
+
try {
|
|
6
|
+
if (ctx._autoCycleTimer) {
|
|
7
|
+
clearInterval(ctx._autoCycleTimer);
|
|
8
|
+
ctx._autoCycleTimer = null;
|
|
9
|
+
}
|
|
10
|
+
} catch (_) { }
|
|
11
|
+
try {
|
|
12
|
+
if (ctx._reconnectTimer) {
|
|
13
|
+
clearTimeout(ctx._reconnectTimer);
|
|
14
|
+
ctx._reconnectTimer = null;
|
|
15
|
+
}
|
|
16
|
+
} catch (_) { }
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
ctx._ending = true;
|
|
20
|
+
ctx._cycling = false;
|
|
21
|
+
} catch (_) { }
|
|
22
|
+
|
|
23
|
+
// Clean up MQTT client
|
|
24
|
+
try {
|
|
25
|
+
if (ctx.mqttClient) {
|
|
26
|
+
ctx.mqttClient.removeAllListeners();
|
|
27
|
+
if (ctx.mqttClient.connected) {
|
|
28
|
+
ctx.mqttClient.end(true);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch (_) { }
|
|
32
|
+
|
|
33
|
+
ctx.mqttClient = undefined;
|
|
34
|
+
ctx.loggedIn = false;
|
|
35
|
+
|
|
36
|
+
// Clean up timeout references
|
|
37
|
+
try {
|
|
38
|
+
if (ctx._rTimeout) {
|
|
39
|
+
clearTimeout(ctx._rTimeout);
|
|
40
|
+
ctx._rTimeout = null;
|
|
41
|
+
}
|
|
42
|
+
} catch (_) { }
|
|
43
|
+
|
|
44
|
+
// Clean up tasks Map to prevent memory leak
|
|
45
|
+
try {
|
|
46
|
+
if (ctx.tasks && ctx.tasks instanceof Map) {
|
|
47
|
+
ctx.tasks.clear();
|
|
48
|
+
}
|
|
49
|
+
} catch (_) { }
|
|
50
|
+
|
|
51
|
+
// Clean up userInfo intervals
|
|
52
|
+
try {
|
|
53
|
+
if (ctx._userInfoIntervals && Array.isArray(ctx._userInfoIntervals)) {
|
|
54
|
+
ctx._userInfoIntervals.forEach(interval => {
|
|
55
|
+
try {
|
|
56
|
+
clearInterval(interval);
|
|
57
|
+
} catch (_) { }
|
|
58
|
+
});
|
|
59
|
+
ctx._userInfoIntervals = [];
|
|
60
|
+
}
|
|
61
|
+
} catch (_) { }
|
|
62
|
+
|
|
63
|
+
// Clean up autoSave intervals
|
|
64
|
+
try {
|
|
65
|
+
if (ctx._autoSaveInterval && Array.isArray(ctx._autoSaveInterval)) {
|
|
66
|
+
ctx._autoSaveInterval.forEach(interval => {
|
|
67
|
+
try {
|
|
68
|
+
clearInterval(interval);
|
|
69
|
+
} catch (_) { }
|
|
70
|
+
});
|
|
71
|
+
ctx._autoSaveInterval = [];
|
|
72
|
+
}
|
|
73
|
+
} catch (_) { }
|
|
74
|
+
|
|
75
|
+
// Clean up scheduler
|
|
76
|
+
try {
|
|
77
|
+
if (ctx._scheduler && typeof ctx._scheduler.destroy === "function") {
|
|
78
|
+
ctx._scheduler.destroy();
|
|
79
|
+
ctx._scheduler = undefined;
|
|
80
|
+
}
|
|
81
|
+
} catch (_) { }
|
|
82
|
+
|
|
83
|
+
// Clear global mqttClient reference if set
|
|
84
|
+
try {
|
|
85
|
+
if (global.mqttClient) {
|
|
86
|
+
delete global.mqttClient;
|
|
87
|
+
}
|
|
88
|
+
} catch (_) { }
|
|
89
|
+
|
|
90
|
+
const msg = detail || reason;
|
|
91
|
+
logger(`auth change -> ${reason}: ${msg}`, "error");
|
|
92
|
+
|
|
93
|
+
if (typeof globalCallback === "function") {
|
|
94
|
+
try {
|
|
95
|
+
globalCallback({
|
|
96
|
+
type: "account_inactive",
|
|
97
|
+
reason,
|
|
98
|
+
error: msg,
|
|
99
|
+
timestamp: Date.now()
|
|
100
|
+
}, null);
|
|
101
|
+
} catch (cbErr) {
|
|
102
|
+
logger(`emitAuth callback error: ${cbErr && cbErr.message ? cbErr.message : String(cbErr)}`, "error");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
};
|