shadowx-fca 8.0.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.
Files changed (174) hide show
  1. package/README.md +1066 -0
  2. package/build/messagix.dll +0 -0
  3. package/build/messagix.so +0 -0
  4. package/checkUpdate.js +393 -0
  5. package/config.json +17 -0
  6. package/e2ee.js +563 -0
  7. package/e2eetest.js +356 -0
  8. package/index.js +611 -0
  9. package/lib/index.mjs +1412 -0
  10. package/logger.js +500 -0
  11. package/package.json +65 -0
  12. package/src/GetBotInfo.js +66 -0
  13. package/src/OldMessage.js +182 -0
  14. package/src/Screenshot.js +83 -0
  15. package/src/addExternalModule.js +13 -0
  16. package/src/addUserToGroup.js +33 -0
  17. package/src/approveGroupJoinRequests.js +18 -0
  18. package/src/changeAdminStatus.js +16 -0
  19. package/src/changeArchivedStatus.js +17 -0
  20. package/src/changeAvatar.js +136 -0
  21. package/src/changeAvatarV2.js +86 -0
  22. package/src/changeAvt.js +85 -0
  23. package/src/changeBio.js +76 -0
  24. package/src/changeBlockedStatus.js +20 -0
  25. package/src/changeBlockedStatusMqtt.js +80 -0
  26. package/src/changeCover.js +72 -0
  27. package/src/changeGroupImage.js +16 -0
  28. package/src/changeName.js +79 -0
  29. package/src/changeNickname.js +16 -0
  30. package/src/changeThreadColor.js +15 -0
  31. package/src/changeThreadEmoji.js +15 -0
  32. package/src/changeThreadMemberNickname.js +6 -0
  33. package/src/changeUsername.js +59 -0
  34. package/src/createCommentPost.js +230 -0
  35. package/src/createNewGroup.js +38 -0
  36. package/src/createNote.js +35 -0
  37. package/src/createPoll.js +27 -0
  38. package/src/createPost.js +276 -0
  39. package/src/createThemeAI.js +129 -0
  40. package/src/data/cache/system/data.json +4 -0
  41. package/src/data/cache/system/datahandle.js +21 -0
  42. package/src/data/getThreadInfo.json +1 -0
  43. package/src/deleteComment.js +23 -0
  44. package/src/deleteMessage.js +15 -0
  45. package/src/deleteThread.js +15 -0
  46. package/src/denyGroupJoinRequests.js +18 -0
  47. package/src/e2ee/crypto.js +173 -0
  48. package/src/e2ee/index.js +144 -0
  49. package/src/e2ee/proto/ArmadilloApplication.proto +281 -0
  50. package/src/e2ee/proto/ArmadilloICDC.proto +14 -0
  51. package/src/e2ee/proto/ConsumerApplication.proto +232 -0
  52. package/src/e2ee/proto/MessageApplication.proto +82 -0
  53. package/src/e2ee/proto/MessageTransport.proto +77 -0
  54. package/src/e2ee/proto/WACommon.proto +66 -0
  55. package/src/e2ee/proto/WAMediaTransport.proto +176 -0
  56. package/src/e2ee/proto/proto-writer.ts +76 -0
  57. package/src/e2ee/protocol.js +196 -0
  58. package/src/e2ee/ratchet.js +219 -0
  59. package/src/e2ee/store.js +182 -0
  60. package/src/e2ee.js +8 -0
  61. package/src/editMessage.js +56 -0
  62. package/src/editMessageOld.js +67 -0
  63. package/src/enableReactions.js +24 -0
  64. package/src/follow.js +74 -0
  65. package/src/followUser.js +23 -0
  66. package/src/forwardAttachment.js +16 -0
  67. package/src/friendList.js +98 -0
  68. package/src/getAccess.js +112 -0
  69. package/src/getAppState.js +13 -0
  70. package/src/getAvatarUser.js +11 -0
  71. package/src/getBio.js +24 -0
  72. package/src/getBotInitialData.js +42 -0
  73. package/src/getCtx.js +5 -0
  74. package/src/getCurrentUserID.js +6 -0
  75. package/src/getEmojiUrl.js +29 -0
  76. package/src/getFriendsList.js +36 -0
  77. package/src/getMessage.js +37 -0
  78. package/src/getNotes.js +17 -0
  79. package/src/getOptions.js +5 -0
  80. package/src/getPinnedMessages.js +33 -0
  81. package/src/getPostInfo.js +17 -0
  82. package/src/getProfileInfo.js +17 -0
  83. package/src/getPublicData.js +25 -0
  84. package/src/getRegion.js +7 -0
  85. package/src/getRepInfo.js +17 -0
  86. package/src/getStickerPacks.js +25 -0
  87. package/src/getStickers.js +39 -0
  88. package/src/getStoryReactions.js +18 -0
  89. package/src/getThreadHistory.js +45 -0
  90. package/src/getThreadHistoryDeprecated.js +71 -0
  91. package/src/getThreadInfo.js +73 -0
  92. package/src/getThreadInfoDeprecated.js +56 -0
  93. package/src/getThreadList.js +76 -0
  94. package/src/getThreadListDeprecated.js +46 -0
  95. package/src/getThreadPictures.js +59 -0
  96. package/src/getThreadTheme.js +77 -0
  97. package/src/getUID.js +17 -0
  98. package/src/getUserID.js +17 -0
  99. package/src/getUserInfo.js +28 -0
  100. package/src/handleFriendRequest.js +21 -0
  101. package/src/handleMessageRequest.js +15 -0
  102. package/src/httpGet.js +13 -0
  103. package/src/httpPost.js +12 -0
  104. package/src/httpPostFormData.js +12 -0
  105. package/src/listenE2EE.js +75 -0
  106. package/src/listenMqtt.js +802 -0
  107. package/src/listenNotification.js +85 -0
  108. package/src/logout.js +22 -0
  109. package/src/markAsDelivered.js +17 -0
  110. package/src/markAsRead.js +14 -0
  111. package/src/markAsReadAll.js +15 -0
  112. package/src/markAsSeen.js +15 -0
  113. package/src/metaTheme.js +185 -0
  114. package/src/muteThread.js +52 -0
  115. package/src/note.js +228 -0
  116. package/src/pin.js +53 -0
  117. package/src/pinMessage.js +6 -0
  118. package/src/postComment.js +29 -0
  119. package/src/postFormData.js +46 -0
  120. package/src/reactToComment.js +31 -0
  121. package/src/reactToPost.js +32 -0
  122. package/src/refreshFb_dtsg.js +31 -0
  123. package/src/removeSuspiciousAccount.js +74 -0
  124. package/src/removeUserFromGroup.js +15 -0
  125. package/src/reply.js +442 -0
  126. package/src/resolvePhotoUrl.js +15 -0
  127. package/src/searchForThread.js +20 -0
  128. package/src/searchFriends.js +28 -0
  129. package/src/searchStickers.js +53 -0
  130. package/src/send.js +46 -0
  131. package/src/sendAudio.js +8 -0
  132. package/src/sendBroadcast.js +93 -0
  133. package/src/sendButtons.js +161 -0
  134. package/src/sendComment.js +159 -0
  135. package/src/sendEmoji.js +10 -0
  136. package/src/sendFile.js +9 -0
  137. package/src/sendFriendRequest.js +33 -0
  138. package/src/sendGif.js +24 -0
  139. package/src/sendImage.js +9 -0
  140. package/src/sendLocation.js +9 -0
  141. package/src/sendMessage.js +487 -0
  142. package/src/sendMessage1.js +309 -0
  143. package/src/sendMessageMqtt.js +68 -0
  144. package/src/sendSticker.js +8 -0
  145. package/src/sendTypingIndicator.js +36 -0
  146. package/src/sendTypingIndicatorV2.js +28 -0
  147. package/src/sendVideo.js +9 -0
  148. package/src/sessionGuard.js +130 -0
  149. package/src/setActiveStatus.js +16 -0
  150. package/src/setMessageReaction.js +61 -0
  151. package/src/setMessageReactionMqtt.js +62 -0
  152. package/src/setOptions.js +22 -0
  153. package/src/setPollVote.js +17 -0
  154. package/src/setPostReaction.js +112 -0
  155. package/src/setProfileGuard.js +44 -0
  156. package/src/setProfileLock.js +93 -0
  157. package/src/setStoryReaction.js +129 -0
  158. package/src/setStorySeen.js +99 -0
  159. package/src/setThreadTheme.js +17 -0
  160. package/src/setTitle.js +15 -0
  161. package/src/shareContact.js +33 -0
  162. package/src/shareLink.js +8 -0
  163. package/src/sharePost.js +31 -0
  164. package/src/stopListenMqtt.js +23 -0
  165. package/src/storyManager.js +353 -0
  166. package/src/suggestFriend.js +128 -0
  167. package/src/threadColors.js +131 -0
  168. package/src/unfollowUser.js +23 -0
  169. package/src/unfriend.js +15 -0
  170. package/src/unpinMessage.js +6 -0
  171. package/src/unsendMessage.js +14 -0
  172. package/src/uploadAttachment.js +58 -0
  173. package/src/uploadImageToImgbb.js +29 -0
  174. package/utils.js +2945 -0
package/index.js ADDED
@@ -0,0 +1,611 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * ╔══════════════════════════════════════════════════════════╗
5
+ * ║ SHADOWX-FCA - Ultimate Facebook API ║
6
+ * ║ Facebook Chat API - All Features Merged ║
7
+ * ║ Dev by Mueid Mursalin Rifat ║
8
+ * ╚══════════════════════════════════════════════════════════╝
9
+ */
10
+
11
+ if (typeof WebSocket === "undefined") {
12
+ global.WebSocket = require("ws");
13
+ }
14
+
15
+ const cheerio = require("cheerio");
16
+ const path = require("path");
17
+ const fs = require("fs");
18
+ const utils = require("./utils");
19
+ const logger = require("./logger");
20
+ const { initAutoUpdate, checkForFCAUpdate } = require("./checkUpdate");
21
+
22
+ const PKG = require("./package.json");
23
+ const VERSION = PKG.version;
24
+ const NAME = "SHADOWX-FCA";
25
+ const DEV = "Mueid Mursalin Rifat";
26
+
27
+ // Initialize auto-update on startup
28
+ initAutoUpdate();
29
+
30
+ // Check for updates immediately on require
31
+ checkForFCAUpdate().catch(() => {
32
+ // Silently fail - updates are non-critical
33
+ });
34
+
35
+ const BOOLEAN_OPTIONS = [
36
+ 'online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin',
37
+ 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'
38
+ ];
39
+
40
+ function setOptions(globalOptions, options) {
41
+ Object.keys(options || {}).forEach(key => {
42
+ if (BOOLEAN_OPTIONS.includes(key)) {
43
+ globalOptions[key] = Boolean(options[key]);
44
+ } else {
45
+ switch (key) {
46
+ case 'pauseLog':
47
+ if (options.pauseLog) logger.pause && logger.pause();
48
+ else logger.resume && logger.resume();
49
+ break;
50
+ case 'logLevel':
51
+ globalOptions.logLevel = options.logLevel;
52
+ break;
53
+ case 'logRecordSize':
54
+ globalOptions.logRecordSize = options.logRecordSize;
55
+ break;
56
+ case 'pageID':
57
+ globalOptions.pageID = String(options.pageID);
58
+ break;
59
+ case 'userAgent':
60
+ globalOptions.userAgent = options.userAgent || globalOptions.userAgent;
61
+ break;
62
+ case 'proxy':
63
+ if (typeof options.proxy !== "string") {
64
+ delete globalOptions.proxy;
65
+ utils.setProxy();
66
+ } else {
67
+ globalOptions.proxy = options.proxy;
68
+ utils.setProxy(globalOptions.proxy);
69
+ }
70
+ break;
71
+ case 'autoUpdate':
72
+ globalOptions.autoUpdate = Boolean(options.autoUpdate);
73
+ if (globalOptions.autoUpdate) {
74
+ process.env.AUTO_UPDATE = 'true';
75
+ }
76
+ break;
77
+ default:
78
+ logger.warn && logger.warn("setOptions", "Unrecognized option given to setOptions: " + key);
79
+ break;
80
+ }
81
+ }
82
+ });
83
+ }
84
+
85
+ function extractFbDtsg(html) {
86
+ let htmlString = html;
87
+ if (Buffer.isBuffer(html)) htmlString = html.toString('utf8');
88
+ else if (typeof html !== 'string') htmlString = String(html);
89
+
90
+ const patterns = [
91
+ /\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
92
+ /\["DTSGInitData",\[\],{"token":"([^"]+)"/,
93
+ /{"dtsg":{"token":"([^"]+)"/,
94
+ /"token":"([^"]+)"/,
95
+ /name="fb_dtsg" value="([^"]+)"/
96
+ ];
97
+
98
+ for (const pat of patterns) {
99
+ const m = htmlString.match(pat);
100
+ if (m && m[1]) return m[1];
101
+ }
102
+
103
+ try {
104
+ const $ = cheerio.load(htmlString);
105
+ const dtsgInput = $('input[name="fb_dtsg"]').val();
106
+ if (dtsgInput) return dtsgInput;
107
+ } catch (_) {}
108
+
109
+ return null;
110
+ }
111
+
112
+ function buildAPI(globalOptions, html, jar) {
113
+ let htmlString = html;
114
+ if (Buffer.isBuffer(html)) htmlString = html.toString('utf8');
115
+ else if (typeof html !== 'string') htmlString = String(html);
116
+
117
+ const fb_dtsg = extractFbDtsg(htmlString);
118
+ const irisSeqMatch = htmlString.match(/irisSeqID":"([^"]+)"/);
119
+ const irisSeqID = irisSeqMatch ? irisSeqMatch[1] : null;
120
+
121
+ const FB_DOMAINS = [
122
+ "https://www.facebook.com",
123
+ "https://facebook.com",
124
+ "https://mbasic.facebook.com",
125
+ "https://web.facebook.com",
126
+ ];
127
+ const allCookies = [];
128
+ const seenKeys = new Set();
129
+ FB_DOMAINS.forEach(domain => {
130
+ try {
131
+ jar.getCookies(domain).forEach(c => {
132
+ if (!seenKeys.has(c.key)) {
133
+ seenKeys.add(c.key);
134
+ allCookies.push(c);
135
+ }
136
+ });
137
+ } catch (_) {}
138
+ });
139
+
140
+ const userCookie = allCookies.find(c => c.key === "c_user");
141
+ const i_userCookie = allCookies.find(c => c.key === "i_user");
142
+
143
+ if (!userCookie && !i_userCookie) {
144
+ const presentKeys = allCookies.map(c => c.key).join(", ") || "(none)";
145
+ throw new Error(
146
+ "AppState is invalid or expired — no c_user/i_user cookie found.\n" +
147
+ " Cookies present: " + presentKeys + "\n" +
148
+ " Make sure your appstate.json was exported from an active Facebook session."
149
+ );
150
+ }
151
+ if (htmlString.includes("/checkpoint/block/?next")) {
152
+ throw new Error("This account has been checkpointed by Facebook.");
153
+ }
154
+
155
+ const userID = (i_userCookie || userCookie).value;
156
+ const clientID = (Math.random() * 2147483648 | 0).toString(16);
157
+
158
+ let mqttEndpoint = "wss://edge-chat.facebook.com/chat?region=pnb";
159
+ let region = "PNB";
160
+ try {
161
+ const epMatch = htmlString.match(/"endpoint":"([^"]+)"/);
162
+ if (epMatch) {
163
+ let ep = epMatch[1].replace(/\\\//g, '/');
164
+ try {
165
+ const epUrl = new URL(ep);
166
+ epUrl.searchParams.delete('sid');
167
+ epUrl.searchParams.delete('cid');
168
+ region = (epUrl.searchParams.get('region') || "PNB").toUpperCase();
169
+ mqttEndpoint = epUrl.toString();
170
+ } catch (_) {
171
+ mqttEndpoint = ep.replace(/[?&]sid=[^&]*/g, '').replace(/[?&]cid=[^&]*/g, '');
172
+ region = (mqttEndpoint.match(/region=([^&]+)/) || [])[1]?.toUpperCase() || "PNB";
173
+ }
174
+ }
175
+ } catch (_) {}
176
+
177
+ // Load config
178
+ let config = { enableTypingIndicator: false, typingDuration: 4000 };
179
+ try {
180
+ const configPaths = [
181
+ path.join(process.cwd(), 'config.json'),
182
+ path.join(__dirname, 'config.json')
183
+ ];
184
+ for (const configPath of configPaths) {
185
+ if (fs.existsSync(configPath)) {
186
+ const fileConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
187
+ if (fileConfig && typeof fileConfig === 'object') {
188
+ if (typeof fileConfig.enableTypingIndicator !== 'undefined')
189
+ config.enableTypingIndicator = fileConfig.enableTypingIndicator;
190
+ if (typeof fileConfig.typingDuration !== 'undefined')
191
+ config.typingDuration = fileConfig.typingDuration;
192
+ // Map e2ee.enable from config.json -> enableE2EE
193
+ if (fileConfig.e2ee && typeof fileConfig.e2ee.enable !== 'undefined')
194
+ config.enableE2EE = Boolean(fileConfig.e2ee.enable);
195
+ if (fileConfig.e2ee && typeof fileConfig.e2ee.memoryOnly !== 'undefined')
196
+ config.e2eeMemoryOnly = Boolean(fileConfig.e2ee.memoryOnly);
197
+ if (fileConfig.e2ee && fileConfig.e2ee.devicePath)
198
+ config.e2eeDevicePath = fileConfig.e2ee.devicePath;
199
+ }
200
+ }
201
+ }
202
+ if (global.GoatBot && global.GoatBot.config) {
203
+ if (typeof global.GoatBot.config.enableTypingIndicator !== 'undefined')
204
+ config.enableTypingIndicator = global.GoatBot.config.enableTypingIndicator;
205
+ if (typeof global.GoatBot.config.typingDuration !== 'undefined')
206
+ config.typingDuration = global.GoatBot.config.typingDuration;
207
+ }
208
+ } catch (_) {}
209
+
210
+ const refreshFcaConfig = () => {
211
+ try {
212
+ const updatedConfig = { enableTypingIndicator: false, typingDuration: 4000 };
213
+ const configPaths = [
214
+ path.join(process.cwd(), 'config.json'),
215
+ path.join(__dirname, 'config.json')
216
+ ];
217
+ for (const configPath of configPaths) {
218
+ if (fs.existsSync(configPath)) {
219
+ const fileConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
220
+ if (fileConfig && typeof fileConfig === 'object') {
221
+ if (typeof fileConfig.enableTypingIndicator !== 'undefined')
222
+ updatedConfig.enableTypingIndicator = fileConfig.enableTypingIndicator;
223
+ if (typeof fileConfig.typingDuration !== 'undefined')
224
+ updatedConfig.typingDuration = fileConfig.typingDuration;
225
+ }
226
+ }
227
+ }
228
+ if (global.GoatBot && global.GoatBot.config) {
229
+ if (typeof global.GoatBot.config.enableTypingIndicator !== 'undefined')
230
+ updatedConfig.enableTypingIndicator = global.GoatBot.config.enableTypingIndicator;
231
+ if (typeof global.GoatBot.config.typingDuration !== 'undefined')
232
+ updatedConfig.typingDuration = global.GoatBot.config.typingDuration;
233
+ }
234
+ ctx.config = updatedConfig;
235
+ } catch (_) {}
236
+ };
237
+
238
+ const ctx = {
239
+ userID,
240
+ i_userID: i_userCookie ? i_userCookie.value : userID,
241
+ jar,
242
+ clientID,
243
+ globalOptions,
244
+ loggedIn: true,
245
+ access_token: 'NONE',
246
+ clientMutationId: 0,
247
+ mqttClient: undefined,
248
+ lastSeqId: irisSeqID,
249
+ syncToken: undefined,
250
+ mqttEndpoint,
251
+ region,
252
+ firstListen: true,
253
+ fb_dtsg,
254
+ req_ID: 0,
255
+ callback_Task: {},
256
+ wsReqNumber: 0,
257
+ wsTaskNumber: 0,
258
+ reqCallbacks: {},
259
+ threadTypes: {},
260
+ config,
261
+ enableE2EE: config.enableE2EE !== undefined ? config.enableE2EE : false,
262
+ e2eeMemoryOnly: config.e2eeMemoryOnly !== undefined ? config.e2eeMemoryOnly : true,
263
+ e2eeDevicePath: config.e2eeDevicePath || null,
264
+ refreshFcaConfig
265
+ };
266
+
267
+ if (global.GoatBot) {
268
+ global.GoatBot.refreshFcaConfig = refreshFcaConfig;
269
+ }
270
+
271
+ const defaultFuncs = utils.makeDefaults(htmlString, userID, ctx);
272
+
273
+ const api = {
274
+ setOptions: setOptions.bind(null, globalOptions),
275
+ getAppState: () => utils.getAppState(jar),
276
+ getCurrentUserID: () => userID,
277
+ getCtx: () => ctx,
278
+ postFormData: (url, body) => defaultFuncs.postFormData(url, ctx.jar, body),
279
+ checkForUpdate: () => checkForFCAUpdate(),
280
+ };
281
+
282
+ // Load all src modules dynamically (merged from all 3 FCAs)
283
+ const SRC = path.join(__dirname, "src");
284
+ const EXCLUDED = new Set([
285
+ "listenMqtt.js", "sendMessage.js", "OldMessage.js", "e2ee.js",
286
+ "httpPost.js", "httpGet.js", "httpPostFormData.js",
287
+ "uploadImageToImgbb.js", "refreshFb_dtsg.js", "addExternalModule.js",
288
+ "listenE2EE.js", "sendBroadcast.js", "sessionGuard.js", "sendMessageMqtt.js"
289
+ ]);
290
+
291
+ // Load critical modules first
292
+ api.httpPost = require("./src/httpPost")(defaultFuncs, api, ctx);
293
+ api.httpGet = require("./src/httpGet")(defaultFuncs, api, ctx);
294
+ api.httpPostFormData = require("./src/httpPostFormData")(defaultFuncs, api, ctx);
295
+ api.getFreshDtsg = require("./src/refreshFb_dtsg")(defaultFuncs, api, ctx);
296
+ api.addExternalModule = require("./src/addExternalModule")(defaultFuncs, api, ctx);
297
+
298
+ // Optional: imgbb uploader (may not exist in all variants)
299
+ if (fs.existsSync(path.join(SRC, "uploadImageToImgbb.js"))) {
300
+ try {
301
+ api.uploadImageToImgbb = require("./src/uploadImageToImgbb")(defaultFuncs, api, ctx);
302
+ } catch (_) {}
303
+ }
304
+
305
+ // Load all remaining modules from merged src/
306
+ fs.readdirSync(SRC)
307
+ .filter(f => f.endsWith('.js') && !EXCLUDED.has(f))
308
+ .forEach(f => {
309
+ const name = f.replace('.js', '');
310
+ try {
311
+ const mod = require(path.join(SRC, f))(defaultFuncs, api, ctx);
312
+ if (mod !== undefined) api[name] = mod;
313
+ } catch (e) {
314
+ logger.warn && logger.warn("SHADOWX-FCA", `Failed to load module '${name}': ${e.message}`);
315
+ }
316
+ });
317
+
318
+ // sendMessage with OldMessage fallback
319
+ const originalSendMessage = require("./src/sendMessage")(defaultFuncs, api, ctx);
320
+ const originalOldMessage = require("./src/OldMessage")(defaultFuncs, api, ctx);
321
+
322
+ api.sendMessage = async function(msg, threadID, callback, replyToMessage, isSingleUser) {
323
+ try {
324
+ return await originalSendMessage(msg, threadID, callback, replyToMessage, isSingleUser);
325
+ } catch (error) {
326
+ logger.warn && logger.warn('SHADOWX-FCA', 'sendMessage failed, using OldMessage fallback');
327
+ return originalOldMessage(msg, threadID, callback, replyToMessage, isSingleUser);
328
+ }
329
+ };
330
+ api.OldMessage = originalOldMessage;
331
+ api.sendMessageDM = (msg, threadID, cb, replyTo) => originalOldMessage(msg, threadID, cb, replyTo, true);
332
+ api.sendMessageMqtt = require("./src/sendMessageMqtt")(defaultFuncs, api, ctx);
333
+
334
+ // MQTT listener with binary message filter
335
+ const originalListenMqtt = require("./src/listenMqtt")(defaultFuncs, api, ctx);
336
+ api.listenMqtt = function(callback) {
337
+ return originalListenMqtt((err, event) => {
338
+ if (err && err.error && err.error.includes('JSON.parse')) return;
339
+ if (err && err.isBinaryResponse === true) return;
340
+ if (err && err.res && Buffer.isBuffer(err.res)) return;
341
+ callback(err, event);
342
+ });
343
+ };
344
+ api.listen = api.listenMqtt;
345
+
346
+ // E2EE support
347
+ try {
348
+ const e2eeModule = require("./src/e2ee");
349
+ api.e2ee = new e2eeModule.E2EEBridge(ctx, api, defaultFuncs);
350
+ ctx.e2ee = api.e2ee;
351
+ api.connectE2EE = async (deviceStorePath) => {
352
+ await api.e2ee.connect(deviceStorePath, userID);
353
+ logger.info && logger.info("E2EE", "Connected e2ee ok");
354
+ return api.e2ee;
355
+ };
356
+ api.listenE2EE = require("./src/listenE2EE")(defaultFuncs, api, ctx);
357
+
358
+ // Auto-connect when config.json has "e2ee": { "enable": true }
359
+ if (ctx.globalOptions.enableE2EE) {
360
+ api.connectE2EE().catch(function(err) {
361
+ logger.warn && logger.warn("SHADOWX-FCA", "E2EE auto-connect failed: " + err.message);
362
+ });
363
+ }
364
+ } catch (e) {
365
+ logger.warn && logger.warn('SHADOWX-FCA', 'E2EE module failed to load: ' + e.message);
366
+ }
367
+
368
+ // Extra modules from all 3 FCAs
369
+ try { api.sendBroadcast = require("./src/sendBroadcast")(defaultFuncs, api, ctx); } catch (_) {}
370
+ try { api.sessionGuard = require("./src/sessionGuard")(defaultFuncs, api, ctx); } catch (_) {}
371
+
372
+ // Image upload helper
373
+ Object.defineProperty(api, '_imgUpload', {
374
+ value: async (imageUrl) => {
375
+ try {
376
+ if (api.uploadImageToImgbb) {
377
+ const r = await api.uploadImageToImgbb(imageUrl);
378
+ if (r && r.data) return r.data.url || r.data.display_url;
379
+ }
380
+ } catch (_) {}
381
+ return null;
382
+ },
383
+ enumerable: false,
384
+ writable: true
385
+ });
386
+ Object.defineProperty(ctx, '_imgUpload', {
387
+ value: api._imgUpload,
388
+ enumerable: false,
389
+ writable: true
390
+ });
391
+
392
+ return { ctx, defaultFuncs, api };
393
+ }
394
+
395
+ function makeLoginForm(html, jar, email, password) {
396
+ let htmlString = html;
397
+ if (Buffer.isBuffer(html)) htmlString = html.toString('utf8');
398
+ else if (typeof html !== 'string') htmlString = String(html);
399
+
400
+ const $ = cheerio.load(htmlString);
401
+ let arr = [];
402
+ $("#login_form input").each((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
403
+ arr = arr.filter(v => v.val && v.val.length);
404
+ const form = utils.arrToForm(arr);
405
+ form.lsd = utils.getFrom(htmlString, "[\"LSD\",[],{\"token\":\"", "\"}");
406
+ form.lgndim = Buffer.from(JSON.stringify({ w: 1440, h: 900, aw: 1440, ah: 834, c: 24 })).toString('base64');
407
+ form.email = email;
408
+ form.pass = password;
409
+ form.default_persistent = '0';
410
+ form.lgnrnd = utils.getFrom(htmlString, "name=\"lgnrnd\" value=\"", "\"");
411
+ form.locale = 'en_US';
412
+ form.timezone = '240';
413
+ form.lgnjs = Math.floor(Date.now() / 1000);
414
+ const willBeCookies = htmlString.split("\"_js_");
415
+ willBeCookies.slice(1).forEach(val => {
416
+ try {
417
+ const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "\"]");
418
+ jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
419
+ } catch (_) {}
420
+ });
421
+ return form;
422
+ }
423
+
424
+ function saveShadowXConfig(userID, botName, region) {
425
+ try {
426
+ const cfgPath = path.join(process.cwd(), "shadowx-fca-config.json");
427
+ let existing = {};
428
+ try { existing = JSON.parse(fs.readFileSync(cfgPath, "utf8")); } catch (_) {}
429
+ const config = Object.assign({}, existing, {
430
+ botUID: userID,
431
+ botName: botName || existing.botName || "",
432
+ region: region || existing.region || "",
433
+ version: VERSION,
434
+ dev: DEV,
435
+ lastLogin: new Date().toISOString(),
436
+ logs: existing.logs !== undefined ? existing.logs : false,
437
+ });
438
+ fs.writeFileSync(cfgPath, JSON.stringify(config, null, 2));
439
+ } catch (_) {}
440
+ }
441
+
442
+ async function loginHelper(appState, email, password, globalOptions, callback) {
443
+ const jar = utils.getJar();
444
+
445
+ let mainPromise;
446
+ if (appState) {
447
+ let parsed = appState;
448
+ if (typeof parsed === "string") {
449
+ try { parsed = JSON.parse(parsed); } catch (_) {}
450
+ }
451
+ parsed.forEach(c => {
452
+ const cookieName = c.key || c.name;
453
+ if (!cookieName || !c.value) return;
454
+ const domain = c.domain || '.facebook.com';
455
+ const expires = c.expirationDate
456
+ ? new Date(c.expirationDate * 1000).toUTCString()
457
+ : (c.expires || '');
458
+ const str = `${cookieName}=${c.value}; expires=${expires}; domain=${domain}; path=${c.path || '/'};`;
459
+ const url = 'http://' + domain.replace(/^\./, 'www.');
460
+ try { jar.setCookie(str, url); } catch (_) {}
461
+ });
462
+ mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
463
+ .then(utils.saveCookies(jar));
464
+ } else {
465
+ mainPromise = utils.get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
466
+ .then(utils.saveCookies(jar))
467
+ .then(async res => {
468
+ let html = res.body;
469
+ if (Buffer.isBuffer(html)) html = html.toString('utf8');
470
+ const form = makeLoginForm(html, jar, email, password);
471
+ logger.info && logger.info("SHADOWX-FCA", "Logging in with email/password...");
472
+ const loginRes = await utils.post(
473
+ "https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
474
+ jar, form, globalOptions
475
+ );
476
+ await utils.saveCookies(jar)(loginRes);
477
+ const headers = loginRes.headers;
478
+ if (!headers.location) throw new Error("Wrong username/password.");
479
+ if (headers.location.includes('/checkpoint/')) {
480
+ if (!globalOptions.forceLogin) throw new Error("Account checkpoint detected. Enable forceLogin to bypass.");
481
+ }
482
+ return utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar));
483
+ });
484
+ }
485
+
486
+ let ctx, api;
487
+ mainPromise = mainPromise
488
+ .then(async res => {
489
+ let body = res.body;
490
+ if (Buffer.isBuffer(body)) body = body.toString('utf8');
491
+ const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
492
+ const redirect = reg.exec(body);
493
+ if (redirect && redirect[1]) {
494
+ res = await utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
495
+ body = res.body;
496
+ if (Buffer.isBuffer(body)) body = body.toString('utf8');
497
+ }
498
+ const mobileAgentRegex = /MPageLoadClientMetrics/gs;
499
+ if (!mobileAgentRegex.test(body)) {
500
+ globalOptions.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36";
501
+ res = await utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
502
+ body = res.body;
503
+ if (Buffer.isBuffer(body)) body = body.toString('utf8');
504
+ }
505
+ return { res, body };
506
+ })
507
+ .then(({ res, body }) => {
508
+ const built = buildAPI(globalOptions, body, jar);
509
+ ctx = built.ctx;
510
+ api = built.api;
511
+ return res;
512
+ });
513
+
514
+ if (globalOptions.pageID) {
515
+ mainPromise = mainPromise
516
+ .then(() => utils.get(
517
+ `https://www.facebook.com/${globalOptions.pageID}/messages/?section=messages&subsection=inbox`,
518
+ jar, null, globalOptions
519
+ ))
520
+ .then(resData => {
521
+ let body = resData.body;
522
+ if (Buffer.isBuffer(body)) body = body.toString('utf8');
523
+ let url = utils.getFrom(body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
524
+ url = url.substring(0, url.length - 1);
525
+ return utils.get('https://www.facebook.com' + url, jar, null, globalOptions);
526
+ });
527
+ }
528
+
529
+ mainPromise
530
+ .then(async () => {
531
+ let botName = "";
532
+ try {
533
+ const users = await api.getUserInfo([ctx.userID]);
534
+ if (users && users[ctx.userID]) botName = users[ctx.userID].name || "";
535
+ } catch (_) {}
536
+
537
+ if (logger.banner) {
538
+ logger.banner(NAME, VERSION, ctx.userID, botName, ctx.region, false);
539
+ } else {
540
+ logger.info && logger.info(NAME, `v${VERSION} | Dev: ${DEV} | UID: ${ctx.userID} | Region: ${ctx.region}`);
541
+ }
542
+
543
+ saveShadowXConfig(ctx.userID, botName, ctx.region);
544
+
545
+ checkForFCAUpdate().catch(() => {});
546
+
547
+ callback(null, api);
548
+ })
549
+ .catch(e => {
550
+ logger.error && logger.error('SHADOWX-FCA', 'Login failed: ' + (e.message || e));
551
+ callback(e);
552
+ });
553
+ }
554
+
555
+ function login(loginData, options, callback) {
556
+ if (typeof options === "function") {
557
+ callback = options;
558
+ options = {};
559
+ }
560
+
561
+ const globalOptions = {
562
+ selfListen: false,
563
+ listenEvents: true,
564
+ listenTyping: false,
565
+ updatePresence: false,
566
+ forceLogin: false,
567
+ autoMarkDelivery: false,
568
+ autoMarkRead: false,
569
+ autoReconnect: true,
570
+ logRecordSize: 100,
571
+ online: false,
572
+ emitReady: false,
573
+ autoUpdate: true,
574
+ userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15"
575
+ };
576
+
577
+ setOptions(globalOptions, options || {});
578
+
579
+ let promise;
580
+ if (typeof callback !== "function" && typeof callback !== "undefined") {
581
+ callback = undefined;
582
+ }
583
+ if (!callback) {
584
+ promise = new Promise((resolve, reject) => {
585
+ callback = (err, api) => err ? reject(err) : resolve(api);
586
+ });
587
+ }
588
+
589
+ if (loginData.email && loginData.password) {
590
+ setOptions(globalOptions, {
591
+ logLevel: "silent",
592
+ forceLogin: true,
593
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
594
+ });
595
+ loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback);
596
+ } else if (loginData.appState) {
597
+ setOptions(globalOptions, options);
598
+ loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback);
599
+ } else {
600
+ callback(new Error("loginData must contain either appState or email+password."));
601
+ }
602
+
603
+ return promise;
604
+ }
605
+
606
+ module.exports = login;
607
+ module.exports.login = login;
608
+ module.exports.VERSION = VERSION;
609
+ module.exports.NAME = NAME;
610
+ module.exports.DEV = DEV;
611
+ module.exports.checkUpdate = checkForFCAUpdate;