@cexy/hoonfca 1.0.4 → 1.0.6

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.
Files changed (135) hide show
  1. package/CHANGELOG.md +181 -0
  2. package/DOCS.md +2636 -0
  3. package/LICENSE-MIT +21 -21
  4. package/README.md +632 -0
  5. package/func/checkUpdate.js +231 -0
  6. package/func/logger.js +48 -0
  7. package/index.d.ts +731 -605
  8. package/index.js +6 -774
  9. package/module/config.js +67 -0
  10. package/module/login.js +154 -0
  11. package/module/loginHelper.js +1034 -0
  12. package/module/options.js +54 -0
  13. package/package.json +54 -19
  14. package/src/api/action/addExternalModule.js +25 -0
  15. package/src/api/action/changeAvatar.js +137 -0
  16. package/src/{changeBio.js → api/action/changeBio.js} +75 -76
  17. package/src/api/action/enableAutoSaveAppState.js +73 -0
  18. package/src/{getCurrentUserID.js → api/action/getCurrentUserID.js} +7 -7
  19. package/src/{handleFriendRequest.js → api/action/handleFriendRequest.js} +57 -57
  20. package/src/api/action/logout.js +76 -0
  21. package/src/api/action/refreshFb_dtsg.js +48 -0
  22. package/src/api/action/setPostReaction.js +106 -0
  23. package/src/api/action/unfriend.js +54 -0
  24. package/src/api/http/httpGet.js +46 -0
  25. package/src/api/http/httpPost.js +52 -0
  26. package/src/api/http/postFormData.js +47 -0
  27. package/src/api/messaging/addUserToGroup.js +68 -0
  28. package/src/api/messaging/changeAdminStatus.js +122 -0
  29. package/src/api/messaging/changeArchivedStatus.js +55 -0
  30. package/src/api/messaging/changeBlockedStatus.js +48 -0
  31. package/src/api/messaging/changeGroupImage.js +90 -0
  32. package/src/api/messaging/changeNickname.js +70 -0
  33. package/src/api/messaging/changeThreadColor.js +79 -0
  34. package/src/api/messaging/changeThreadEmoji.js +106 -0
  35. package/src/{createNewGroup.js → api/messaging/createNewGroup.js} +88 -88
  36. package/src/api/messaging/createPoll.js +43 -0
  37. package/src/api/messaging/createThemeAI.js +98 -0
  38. package/src/{deleteMessage.js → api/messaging/deleteMessage.js} +56 -56
  39. package/src/{deleteThread.js → api/messaging/deleteThread.js} +56 -56
  40. package/src/api/messaging/editMessage.js +68 -0
  41. package/src/api/messaging/forwardAttachment.js +51 -0
  42. package/src/{getEmojiUrl.js → api/messaging/getEmojiUrl.js} +8 -8
  43. package/src/{getFriendsList.js → api/messaging/getFriendsList.js} +82 -84
  44. package/src/api/messaging/getMessage.js +829 -0
  45. package/src/api/messaging/getThemePictures.js +62 -0
  46. package/src/api/messaging/handleMessageRequest.js +65 -0
  47. package/src/{markAsDelivered.js → api/messaging/markAsDelivered.js} +57 -58
  48. package/src/{markAsRead.js → api/messaging/markAsRead.js} +88 -88
  49. package/src/{markAsReadAll.js → api/messaging/markAsReadAll.js} +49 -50
  50. package/src/api/messaging/markAsSeen.js +61 -0
  51. package/src/{muteThread.js → api/messaging/muteThread.js} +50 -52
  52. package/src/api/messaging/removeUserFromGroup.js +106 -0
  53. package/src/{resolvePhotoUrl.js → api/messaging/resolvePhotoUrl.js} +43 -45
  54. package/src/api/messaging/scheduler.js +264 -0
  55. package/src/{searchForThread.js → api/messaging/searchForThread.js} +52 -53
  56. package/src/api/messaging/sendMessage.js +272 -0
  57. package/src/api/messaging/sendTypingIndicator.js +67 -0
  58. package/src/api/messaging/setMessageReaction.js +76 -0
  59. package/src/api/messaging/setTitle.js +119 -0
  60. package/src/{shareContact.js → api/messaging/shareContact.js} +49 -53
  61. package/src/api/messaging/threadColors.js +128 -0
  62. package/src/api/messaging/unsendMessage.js +81 -0
  63. package/src/api/messaging/uploadAttachment.js +94 -0
  64. package/src/api/socket/core/connectMqtt.js +255 -0
  65. package/src/api/socket/core/emitAuth.js +106 -0
  66. package/src/api/socket/core/getSeqID.js +40 -0
  67. package/src/api/socket/core/getTaskResponseData.js +22 -0
  68. package/src/api/socket/core/markDelivery.js +12 -0
  69. package/src/api/socket/core/parseDelta.js +391 -0
  70. package/src/api/socket/detail/buildStream.js +208 -0
  71. package/src/api/socket/detail/constants.js +24 -0
  72. package/src/api/socket/listenMqtt.js +364 -0
  73. package/src/api/socket/middleware/index.js +216 -0
  74. package/src/{getThreadHistory.js → api/threads/getThreadHistory.js} +664 -647
  75. package/src/api/threads/getThreadInfo.js +438 -0
  76. package/src/{getThreadList.js → api/threads/getThreadList.js} +293 -292
  77. package/src/{getThreadPictures.js → api/threads/getThreadPictures.js} +78 -79
  78. package/src/{getUserID.js → api/users/getUserID.js} +65 -66
  79. package/src/api/users/getUserInfo.js +327 -0
  80. package/src/api/users/getUserInfoV2.js +134 -0
  81. package/src/core/sendReqMqtt.js +96 -0
  82. package/src/database/models/index.js +87 -0
  83. package/src/database/models/thread.js +45 -0
  84. package/src/database/models/user.js +46 -0
  85. package/src/database/threadData.js +98 -0
  86. package/src/database/userData.js +89 -0
  87. package/src/utils/client.js +320 -0
  88. package/src/utils/constants.js +23 -0
  89. package/{utils.js → src/utils/format.js} +1115 -1447
  90. package/src/utils/headers.js +115 -0
  91. package/src/utils/request.js +305 -0
  92. package/.travis.yml +0 -6
  93. package/src/addExternalModule.js +0 -23
  94. package/src/addUserToGroup.js +0 -113
  95. package/src/changeAdminStatus.js +0 -95
  96. package/src/changeApprovalMode.js +0 -79
  97. package/src/changeArchivedStatus.js +0 -55
  98. package/src/changeBlockedStatus.js +0 -47
  99. package/src/changeBlockedStatusMqtt.js +0 -86
  100. package/src/changeGroupImage.js +0 -133
  101. package/src/changeNickname.js +0 -59
  102. package/src/changeThreadColor.js +0 -71
  103. package/src/changeThreadEmoji.js +0 -55
  104. package/src/createPoll.js +0 -130
  105. package/src/editMessage.js +0 -63
  106. package/src/forwardAttachment.js +0 -60
  107. package/src/forwardMessage.js +0 -63
  108. package/src/getGroupsList.js +0 -83
  109. package/src/getMarketplace.js +0 -84
  110. package/src/getNotifications.js +0 -86
  111. package/src/getPagesList.js +0 -83
  112. package/src/getStories.js +0 -88
  113. package/src/getThreadInfo.js +0 -222
  114. package/src/getUserInfo.js +0 -128
  115. package/src/handleMessageRequest.js +0 -65
  116. package/src/httpGet.js +0 -58
  117. package/src/httpPost.js +0 -58
  118. package/src/listenMqtt.js +0 -1018
  119. package/src/logout.js +0 -75
  120. package/src/markAsSeen.js +0 -61
  121. package/src/pinMessage.js +0 -61
  122. package/src/removeUserFromGroup.js +0 -79
  123. package/src/searchUsers.js +0 -89
  124. package/src/sendComment.js +0 -161
  125. package/src/sendMessage.js +0 -457
  126. package/src/sendTypingIndicator.js +0 -179
  127. package/src/setBio.js +0 -86
  128. package/src/setMessageReaction.js +0 -186
  129. package/src/setPostReaction.js +0 -71
  130. package/src/setTheme.js +0 -313
  131. package/src/setTitle.js +0 -90
  132. package/src/shareLink.js +0 -62
  133. package/src/threadColors.js +0 -57
  134. package/src/unfriend.js +0 -53
  135. 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
+ };