shadowx-fca 2.4.0 → 2.5.0

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/src/listenMqtt.js CHANGED
@@ -114,27 +114,24 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
114
114
 
115
115
  // Display connection success message with branding and loading animation
116
116
  const messages = [
117
- '\n SHADOWX-FCA MQTT Connected',
118
- ` Region: ${ctx.region || 'PNB'}`,
117
+ '\n🖤 SHADOWX FCA MQTT Connected',
118
+ `🔰 Region: ${ctx.region || 'PNB'}`,
119
119
  `🔄 Auto-reconnect: ${ctx.globalOptions.autoReconnect ? 'Enabled' : 'Disabled'}${ctx.globalOptions.autoReconnect ? ' (reconnects every 3s on disconnect)' : ''}`,
120
120
  `⏱️ MQTT Restart Interval: ${ctx.globalOptions.restartListenMqtt?.enable ? `${ctx.globalOptions.restartListenMqtt.timeRestart / 1000}s` : 'Disabled'}`,
121
- 'Author: Mueid Mursalin Rifat\n'
121
+ ' ShadowX FCA STARTED\n'
122
122
  ];
123
123
 
124
124
  let index = 0;
125
125
  const displayMessages = () => {
126
126
  if (index < messages.length) {
127
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
128
- let frameIndex = 0;
129
127
 
130
- const loadingInterval = setInterval(() => {
131
- process.stdout.write(`\r${frames[frameIndex]} Loading...`);
132
- frameIndex = (frameIndex + 1) % frames.length;
133
- }, 80);
128
+ // loading condition - Using the extra safe method to prevent reconnect spam
129
+ if (index === 0 && !ctx._loadedOnce) {
130
+ process.stdout.write("⏳ Loading...\n");
131
+ ctx._loadedOnce = true;
132
+ }
134
133
 
135
134
  setTimeout(() => {
136
- clearInterval(loadingInterval);
137
- process.stdout.write('\r' + ' '.repeat(20) + '\r');
138
135
  console.log(messages[index]);
139
136
  index++;
140
137
  displayMessages();
@@ -677,110 +674,6 @@ function markDelivery(ctx, api, threadID, messageID) {
677
674
 
678
675
  module.exports = function (defaultFuncs, api, ctx) {
679
676
  let globalCallback = identity;
680
- // function getSeqID() {
681
- // ctx.t_mqttCalled = false;
682
- // async function attemptRequest(retries = 3) {
683
- // try {
684
- // if (!ctx.fb_dtsg) {
685
- // const dtsg = await api.getFreshDtsg();
686
- // if (!dtsg) {
687
- // if (retries > 0) {
688
- // logger.Warning("Failed to get fb_dtsg, retrying...");
689
- // await utils.sleep(2000); // Longer delay for token retry
690
- // return attemptRequest(retries - 1);
691
- // }
692
- // throw { error: "Could not obtain fb_dtsg after multiple attempts" };
693
- // }
694
- // ctx.fb_dtsg = dtsg;
695
- // }
696
-
697
- // const form = {
698
- // av: ctx.userID,
699
- // fb_dtsg: ctx.fb_dtsg,
700
- // queries: JSON.stringify({
701
- // o0: {
702
- // doc_id: '3336396659757871',
703
- // query_params: {
704
- // limit: 1,
705
- // before: null,
706
- // tags: ['INBOX'],
707
- // includeDeliveryReceipts: false,
708
- // includeSeqID: true
709
- // }
710
- // }
711
- // }),
712
- // __user: ctx.userID,
713
- // __a: '1',
714
- // __req: '8',
715
- // __hs: '19577.HYP:comet_pkg.2.1..2.1',
716
- // dpr: '1',
717
- // fb_api_caller_class: 'RelayModern',
718
- // fb_api_req_friendly_name: 'MessengerGraphQLThreadlistFetcher'
719
- // };
720
-
721
- // const headers = {
722
- // 'Content-Type': 'application/x-www-form-urlencoded',
723
- // 'Referer': 'https://www.facebook.com/',
724
- // 'Origin': 'https://www.facebook.com',
725
- // 'sec-fetch-site': 'same-origin',
726
- // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
727
- // 'Cookie': ctx.jar.getCookieString('https://www.facebook.com'),
728
- // 'accept': '*/*',
729
- // 'accept-encoding': 'gzip, deflate, br'
730
- // };
731
-
732
- // const resData = await defaultFuncs
733
- // .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form, { headers })
734
- // .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
735
-
736
- // if (debugSeq) {
737
- // console.log('GraphQL SeqID Response:', JSON.stringify(resData, null, 2));
738
- // }
739
-
740
- // if (resData.error === 1357004 || resData.error === 1357001) {
741
- // if (retries > 0) {
742
- // logger.Warning("Session error, refreshing token and retrying...");
743
- // ctx.fb_dtsg = null; // Force new token
744
- // await utils.sleep(2000);
745
- // return attemptRequest(retries - 1);
746
- // }
747
- // throw { error: "Session refresh failed after retries" };
748
- // }
749
-
750
- // if (!Array.isArray(resData)) {
751
- // throw { error: "Invalid response format", res: resData };
752
- // }
753
-
754
- // const seqID = resData[0]?.o0?.data?.viewer?.message_threads?.sync_sequence_id;
755
- // if (!seqID) {
756
- // throw { error: "Missing sync_sequence_id", res: resData };
757
- // }
758
-
759
- // ctx.lastSeqId = seqID;
760
- // if (debugSeq) {
761
- // console.log('Got SeqID:', ctx.lastSeqId);
762
- // }
763
-
764
- // return listenMqtt(defaultFuncs, api, ctx, globalCallback);
765
-
766
- // } catch (err) {
767
- // if (retries > 0) {
768
- // console.log("Request failed, retrying...");
769
-
770
- // return attemptRequest(retries - 1);
771
- // }
772
- // throw err;
773
- // }
774
- // }
775
-
776
- // return attemptRequest()
777
- // .catch((err) => {
778
- // log.error("getSeqId", err);
779
- // if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
780
- // return globalCallback(err);
781
- // });
782
- // }
783
-
784
677
  getSeqID = function getSeqID() {
785
678
  ctx.t_mqttCalled = false;
786
679
  defaultFuncs
@@ -867,4 +760,4 @@ module.exports = function (defaultFuncs, api, ctx) {
867
760
  api.stopListeningAsync = msgEmitter.stopListeningAsync;
868
761
  return msgEmitter;
869
762
  };
870
- };
763
+ };
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = function (defaultFuncs, api, ctx) {
6
+ /**
7
+ * Author: S4hiilAns4ri (github.com/S4hiilAns4ri)
8
+ * Mqtt
9
+ * Sets a nickname for a participant in a Facebook thread via MQTT.
10
+ *
11
+ * @param {string} nickname The new nickname to set.
12
+ * @param {string} threadID The ID of the thread.
13
+ * @param {string} participantID The ID of the participant whose nickname will be changed. Defaults to the current user's ID if not provided or a function.
14
+ * @param {Function} [callback] Optional callback function to be invoked upon completion.
15
+ * @param {string} [initiatorID] The ID of the user who initiated the nickname change (e.g., from event.senderID).
16
+ * @returns {Promise<object>} A promise that resolves with a structured event object on success or rejects on error.
17
+ */
18
+ return function setNickname(nickname, threadID, participantID, callback, initiatorID) {
19
+ let _callback;
20
+ let _initiatorID;
21
+
22
+ let _resolvePromise;
23
+ let _rejectPromise;
24
+ const returnPromise = new Promise((resolve, reject) => {
25
+ _resolvePromise = resolve;
26
+ _rejectPromise = reject;
27
+ });
28
+
29
+ if (utils.getType(callback) === "Function" || utils.getType(callback) === "AsyncFunction") {
30
+ _callback = callback;
31
+ _initiatorID = initiatorID;
32
+ } else if (utils.getType(threadID) === "Function" || utils.getType(threadID) === "AsyncFunction") {
33
+ _callback = threadID;
34
+ threadID = null;
35
+ _initiatorID = callback;
36
+ } else if (utils.getType(participantID) === "Function" || utils.getType(participantID) === "AsyncFunction") {
37
+ _callback = participantID;
38
+ participantID = ctx.userID;
39
+ _initiatorID = callback;
40
+ }
41
+ else if (utils.getType(callback) === "string") {
42
+ _initiatorID = callback;
43
+ _callback = undefined;
44
+ } else {
45
+ _callback = undefined;
46
+ _initiatorID = undefined;
47
+ }
48
+
49
+ if (!_callback) {
50
+ _callback = function (__err, __data) {
51
+ if (__err) _rejectPromise(__err);
52
+ else _resolvePromise(__data);
53
+ };
54
+ } else {
55
+ const originalCallback = _callback;
56
+ _callback = function(__err, __data) {
57
+ if (__err) {
58
+ originalCallback(__err);
59
+ _rejectPromise(__err);
60
+ } else {
61
+ originalCallback(null, __data);
62
+ _resolvePromise(__data);
63
+ }
64
+ };
65
+ }
66
+
67
+ _initiatorID = _initiatorID || ctx.userID;
68
+
69
+ threadID = threadID || ctx.threadID;
70
+ participantID = participantID || ctx.userID;
71
+
72
+ if (!threadID) {
73
+ return _callback(new Error("threadID is required to set a nickname."));
74
+ }
75
+ if (typeof nickname !== 'string') {
76
+ return _callback(new Error("nickname must be a string."));
77
+ }
78
+
79
+ if (!ctx.mqttClient) {
80
+ return _callback(new Error("Not connected to MQTT"));
81
+ }
82
+
83
+ ctx.wsReqNumber += 1;
84
+ ctx.wsTaskNumber += 1;
85
+
86
+ const queryPayload = {
87
+ thread_key: threadID.toString(),
88
+ contact_id: participantID.toString(),
89
+ nickname: nickname,
90
+ sync_group: 1,
91
+ };
92
+
93
+ const query = {
94
+ failure_count: null,
95
+ label: '44',
96
+ payload: JSON.stringify(queryPayload),
97
+ queue_name: 'thread_participant_nickname',
98
+ task_id: ctx.wsTaskNumber,
99
+ };
100
+
101
+ const context = {
102
+ app_id: ctx.appID,
103
+ payload: {
104
+ epoch_id: parseInt(utils.generateOfflineThreadingID()),
105
+ tasks: [query],
106
+ version_id: '24631415369801570',
107
+ },
108
+ request_id: ctx.wsReqNumber,
109
+ type: 3,
110
+ };
111
+ context.payload = JSON.stringify(context.payload);
112
+
113
+ ctx.mqttClient.publish('/ls_req', JSON.stringify(context), { qos: 1, retain: false }, (err) => {
114
+ if (err) {
115
+ return _callback(new Error(`MQTT publish failed for setNickname: ${err.message || err}`));
116
+ }
117
+
118
+ const nicknameChangeEvent = {
119
+ type: "thread_nickname_update",
120
+ threadID: threadID,
121
+ participantID: participantID,
122
+ newNickname: nickname,
123
+ senderID: _initiatorID,
124
+ BotID: ctx.userID,
125
+ timestamp: Date.now(),
126
+ };
127
+ _callback(null, nicknameChangeEvent);
128
+ });
129
+
130
+ return returnPromise;
131
+ };
132
+ };