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/README.md ADDED
@@ -0,0 +1,1066 @@
1
+
2
+ ```markdown
3
+
4
+ # โšก SHADOWX
5
+
6
+ <p align="center">
7
+ <strong> Facebook Chat API and next-generation Facebook Messenger bot library for Node.js with Auto-Update System</strong><br>
8
+ Modified by Mueid Mursalin Rifat๐Ÿ–ค
9
+ </p>
10
+
11
+ ๐Ÿ” Signal Protocol E2EE Added
12
+
13
+ <br>
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## ๐Ÿ“‘ Table of Contents
20
+
21
+ - [โœจ Installation](#-installation)
22
+ - [๐Ÿš€ Quick Start](#-quick-start)
23
+ - [โš™๏ธ Login Options](#๏ธ-login-options)
24
+ - [๐Ÿ“‹ shadowxConfig.json](#-shadowxconfigjson)
25
+ - [๐Ÿ“ก Event Types](#-event-types)
26
+ - [๐Ÿ“š API Reference](#-api-reference)
27
+ - [๐Ÿ”‘ Login](#-login)
28
+ - [๐Ÿ‘‚ Listening](#-listening)
29
+ - [๐Ÿ” E2EE โ€” Encrypted Conversations](#-e2ee--encrypted-conversations)
30
+ - [๐Ÿ’ฌ Sending Messages](#-sending-messages)
31
+ - [โœ๏ธ Message Actions](#๏ธ-message-actions)
32
+ - [๐Ÿ“Ž Attachments & Media](#-attachments--media)
33
+ - [๐Ÿงต Thread Management](#-thread-management)
34
+ - [๐ŸŽจ Thread Customization](#-thread-customization)
35
+ - [๐Ÿ‘ฅ Thread Members](#-thread-members)
36
+ - [๐Ÿ“Š Polls](#-polls)
37
+ - [๐Ÿ“Œ Pins](#-pins)
38
+ - [๐Ÿ“ฌ Read / Delivery Receipts](#-read--delivery-receipts)
39
+ - [๐Ÿ‘ค User Info & Friends](#-user-info--friends)
40
+ - [๐ŸŒ Social / Posting](#-social--posting)
41
+ - [๐Ÿท๏ธ Stickers](#๏ธ-stickers)
42
+ - [๐Ÿ”ง HTTP Utilities](#-http-utilities)
43
+ - [โšก Misc / Config](#-misc--config)
44
+ - [๐Ÿ“ข Broadcast](#-broadcast)
45
+ - [๐Ÿ›ก๏ธ Session Guard](#-session-guard)
46
+ - [๐Ÿ—๏ธ E2EE Architecture](#-e2ee-architecture)
47
+ - [๐Ÿ”„ Auto Update System](#-auto-update-system)
48
+
49
+ ---
50
+
51
+ ## โœจ Installation
52
+
53
+ ```bash
54
+ npm install shadowx-fca
55
+ ```
56
+
57
+ ๐Ÿ”ฅ Node.js โ‰ฅ 18 required. No TypeScript, no build step โ€” just pure JavaScript power!
58
+
59
+ ๐Ÿ”„ Automatic Updates
60
+
61
+ SHADOWX comes with a built-in auto-update system that keeps your bot running on the latest version:
62
+
63
+ ```bash
64
+ # Auto-update is enabled by default
65
+ node index.js
66
+
67
+ # Disable auto-update
68
+ DISABLE_UPDATE=true node index.js
69
+ # or
70
+ node index.js --no-update
71
+
72
+ # Force update check
73
+ node -e "require('shadowx-fca').checkUpdate()"
74
+ ```
75
+
76
+ ---
77
+
78
+ ๐Ÿš€ Quick Start
79
+
80
+ ```js
81
+ const login = require("shadowx-fca");
82
+ const fs = require("fs");
83
+
84
+ const appState = JSON.parse(fs.readFileSync("./appstate.json", "utf8"));
85
+
86
+ login({ appState }, { listenEvents: true }, async (err, api) => {
87
+ if (err) throw err;
88
+
89
+ console.log("โœ… Logged in as:", api.getCurrentUserID());
90
+ // shadowxConfig.json is auto-created here โ†‘
91
+
92
+ // Optional: Enable session protection
93
+ api.sessionGuard("./appstate.json");
94
+
95
+ api.listen((err, event) => {
96
+ if (err) throw err;
97
+ if (event.type === "message" && event.body === "!ping") {
98
+ api.sendMessage("๐Ÿ“ Pong! I'm alive and kicking!", event.threadID);
99
+ }
100
+ });
101
+ });
102
+ ```
103
+
104
+ ---
105
+
106
+ โš™๏ธ Login Options
107
+
108
+ ๐ŸŽ›๏ธ Option ๐Ÿ”ค Type ๐ŸŽฏ Default ๐Ÿ“ Description
109
+ selfListen boolean false Receive your own sent messages
110
+ listenEvents boolean true Receive thread/group events
111
+ listenTyping boolean false Receive typing indicator events
112
+ updatePresence boolean false Receive online/offline presence events
113
+ autoMarkDelivery boolean false Auto-mark incoming messages as delivered
114
+ autoMarkRead boolean false Auto-mark threads as read after delivery
115
+ autoReconnect boolean true Auto-reconnect MQTT on disconnect
116
+ online boolean false Appear as online to others
117
+ emitReady boolean false Emit a ready event when MQTT is connected
118
+ autoUpdate boolean true Automatically check for updates on login
119
+ pageID string โ€” Act as a Facebook Page
120
+ userAgent string Safari UA Override the HTTP User-Agent
121
+ proxy string โ€” HTTP proxy URL (http://127.0.0.1:8080)
122
+
123
+ ---
124
+
125
+ ๐Ÿ“‹ shadowxConfig.json
126
+
127
+ ๐ŸŽฏ SHADOWX automatically creates shadowxConfig.json in your project directory every time login() succeeds. It's like magic โ€” no setup needed, it just appears!
128
+
129
+ ```json
130
+ {
131
+ "botUID": "61589208980818",
132
+ "botName": "My Awesome Bot",
133
+ "region": "EAG",
134
+ "version": "2.0.0",
135
+ "lastLogin": "2026-05-30T12:00:00.000Z",
136
+ "logs": false
137
+ }
138
+ ```
139
+
140
+ ๐Ÿ” Set "logs": true to enable colorful per-message console logs showing the sender UID, message body, DM/group indicator, and timestamp. Toggle it live without restarting โ€” the bot re-reads the file on every message. Pretty cool, right?
141
+
142
+ ๐Ÿ“– Read it from your bot code at any time:
143
+
144
+ ```js
145
+ const config = require("./shadowxConfig.json");
146
+
147
+ console.log(`๐Ÿค– ${config.botName} running in ${config.region}`);
148
+ api.sendMessage("Running as: " + config.botName, threadID);
149
+ ```
150
+
151
+ ๐Ÿ’ก Add your own fields โ€” SHADOWX preserves custom keys on every login update:
152
+
153
+ ```js
154
+ const fs = require("fs");
155
+ const config = JSON.parse(fs.readFileSync("./shadowxConfig.json", "utf8"));
156
+
157
+ // Customize it your way!
158
+ config.prefix = "!";
159
+ config.adminID = "100000000000001";
160
+ config.customSettings = {
161
+ autoReply: true,
162
+ timezone: "Asia/Dhaka"
163
+ };
164
+
165
+ fs.writeFileSync("./shadowxConfig.json", JSON.stringify(config, null, 2));
166
+ ```
167
+
168
+ ---
169
+
170
+ ๐Ÿ“ก Event Types
171
+
172
+ All events arrive in the listen / listenE2EE callback as callback(err, event). Think of it as your bot's nervous system! ๐Ÿง 
173
+
174
+ ๐ŸŽช event.type โšก When it fires
175
+ message New text or media message
176
+ message_reply A reply to an existing message โ€” includes event.messageReply
177
+ message_reaction Reaction added or removed
178
+ message_unsend A message was unsent โ€” includes original body, attachments, and attachmentType
179
+ event Thread event (rename, add/remove member, etc.)
180
+ typ Typing indicator (listenTyping: true)
181
+ presence User online/offline status (updatePresence: true)
182
+ change_thread_image Group image changed
183
+ ready MQTT ready (emitReady: true)
184
+
185
+ ๐Ÿ’Œ Message Object
186
+
187
+ ```js
188
+ {
189
+ type: "message",
190
+ senderID: "100000000000001",
191
+ threadID: "100000000000002",
192
+ messageID: "mid.$abc...",
193
+ body: "Hello world ๐Ÿ‘‹",
194
+ args: ["Hello", "world"],
195
+ attachments: [],
196
+ mentions: {}, // { "uid": "@Name" }
197
+ timestamp: 1716000000000,
198
+ isGroup: false,
199
+ isE2EE: false // true when from listenE2EE
200
+ }
201
+ ```
202
+
203
+ โ†ฉ๏ธ Reply Object (message_reply)
204
+
205
+ When event.type === "message_reply", the event carries a messageReply field describing the original message being replied to. It's like message archaeology! ๐Ÿบ
206
+
207
+ ```js
208
+ {
209
+ type: "message_reply",
210
+ senderID: "100000000000001", // who sent the reply
211
+ threadID: "100000000000002",
212
+ messageID: "mid.$reply...",
213
+ body: "This is the reply text",
214
+ args: ["This", "is", ...],
215
+ attachments: [],
216
+ mentions: {},
217
+ timestamp: 1716000000001,
218
+ isGroup: true,
219
+ isE2EE: false,
220
+
221
+ // โ”€โ”€ The original message being replied to โ”€โ”€
222
+ messageReply: {
223
+ messageID: "mid.$original...",
224
+ senderID: "100000000000003", // who sent the original
225
+ threadID: "100000000000002",
226
+ body: "Original message text",
227
+ args: ["Original", "message", "text"],
228
+ attachments: [],
229
+ mentions: {},
230
+ isGroup: true,
231
+ timestamp: 1716000000000
232
+ }
233
+ }
234
+ ```
235
+
236
+ ๐ŸŽฏ Reply handler pattern:
237
+
238
+ ```js
239
+ api.listen(async (err, event) => {
240
+ if (event.type === "message_reply" && event.messageReply) {
241
+ const repliedMsgID = event.messageReply.messageID;
242
+
243
+ if (replyHandlers.has(repliedMsgID)) {
244
+ const handler = replyHandlers.get(repliedMsgID);
245
+ replyHandlers.delete(repliedMsgID);
246
+ await handler(event);
247
+ }
248
+ }
249
+ });
250
+ ```
251
+
252
+ ๐Ÿ”„ Works identically in both api.listen (MQTT) and api.listenE2EE (encrypted). E2EE reply events carry isE2EE: true on both the event and the messageReply object.
253
+
254
+ ๐Ÿ—‘๏ธ Unsend Event (message_unsend)
255
+
256
+ Fired when someone unsends a message. SHADOWX automatically caches every incoming message so the original content is available at unsend time โ€” including the full attachments array with URLs. It's like having a photographic memory! ๐Ÿ“ธ
257
+
258
+ ```js
259
+ {
260
+ type: "message_unsend",
261
+ threadID: "100000000000002",
262
+ messageID: "mid.$abc123",
263
+ senderID: "100000000000001",
264
+ deletionTimestamp: 1716000000001,
265
+ timestamp: 1716000000000,
266
+
267
+ // โ”€โ”€ Original message content (from SHADOWX's message cache) โ”€โ”€
268
+ body: "Hello world ๐ŸŒ", // original text, "" if attachment-only
269
+ attachments: [ ... ], // same shape as a normal message's attachments
270
+ attachmentType: "text" // convenience field โ€” see values below
271
+ }
272
+ ```
273
+
274
+ attachmentType values:
275
+
276
+ ๐Ÿท๏ธ Value ๐Ÿ“ What was unsent
277
+ "text" Plain text message
278
+ "photo" Image ๐Ÿ–ผ๏ธ
279
+ "video" Video ๐ŸŽฌ
280
+ "audio" Voice / audio clip ๐ŸŽต
281
+ "sticker" Sticker
282
+ "animated_image" GIF
283
+ "file" File / document ๐Ÿ“„
284
+ "unknown" Not seen before unsend (bot was offline when it arrived) ๐Ÿ˜ด
285
+
286
+ ๐Ÿ›ก๏ธ Anti-unsend example:
287
+
288
+ ```js
289
+ api.listen((err, event) => {
290
+ if (event.type !== "message_unsend") return;
291
+
292
+ const { attachmentType, body, attachments, threadID, senderID } = event;
293
+
294
+ if (attachmentType === "text") {
295
+ api.sendMessage(`โš ๏ธ ${senderID} unsent: "${body}"`, threadID);
296
+ } else if (attachmentType === "photo" && attachments[0]) {
297
+ api.sendMessage({ body: `โš ๏ธ ${senderID} unsent a photo:`, attachment: require("request")(attachments[0].url) }, threadID);
298
+ } else if (attachmentType === "video") {
299
+ api.sendMessage(`โš ๏ธ ${senderID} unsent a video: ${attachments[0] && attachments[0].url}`, threadID);
300
+ } else if (attachmentType === "audio") {
301
+ api.sendMessage(`โš ๏ธ ${senderID} unsent an audio clip: ${attachments[0] && attachments[0].url}`, threadID);
302
+ } else if (attachmentType === "sticker") {
303
+ api.sendMessage(`โš ๏ธ ${senderID} unsent a sticker: ${attachments[0] && attachments[0].url}`, threadID);
304
+ } else if (attachmentType === "animated_image") {
305
+ api.sendMessage(`โš ๏ธ ${senderID} unsent a GIF: ${attachments[0] && attachments[0].url}`, threadID);
306
+ } else {
307
+ api.sendMessage(`โš ๏ธ ${senderID} unsent a ${attachmentType} message.`, threadID);
308
+ }
309
+ });
310
+ ```
311
+
312
+ ๐Ÿ’ก Pro tip: Content is only available if the bot was online when the message arrived. Messages unsent before the bot saw them will have body: "", attachments: [], and attachmentType: "unknown". The cache holds the last 500 messages to keep things snappy!
313
+
314
+ ---
315
+
316
+ ๐ŸŽญ Thread Events (event type)
317
+
318
+ When event.type === "event", the logMessageType field identifies what went down:
319
+
320
+ ๐ŸŽช event.logMessageType ๐Ÿ“ Description
321
+ log:subscribe Members added to a group โ€” logMessageData.addedParticipants
322
+ log:unsubscribe Member left or was removed โ€” logMessageData.leftParticipantFbId
323
+ log:thread-name Group name changed โ€” logMessageData.name
324
+ log:thread-color Chat theme/color changed ๐ŸŽจ
325
+ log:thread-icon Thread emoji changed
326
+ log:user-nickname Nickname changed
327
+ log:thread-admins Admin status changed ๐Ÿ‘‘
328
+ log:thread-poll Poll created or updated ๐Ÿ“Š
329
+ log:thread-pinned Message pinned/unpinned ๐Ÿ“Œ
330
+ log:thread-call Call log entry ๐Ÿ“ž
331
+ log:thread-approval-mode Join approval mode changed
332
+ log:link-status Joinable link reset ๐Ÿ”—
333
+
334
+ ๐Ÿ‘‹ Join/leave handler example:
335
+
336
+ ```js
337
+ api.listen((err, event) => {
338
+ if (event.type !== "event") return;
339
+
340
+ if (event.logMessageType === "log:subscribe") {
341
+ const added = (event.logMessageData.addedParticipants || [])
342
+ .map(p => p.userFbId || p);
343
+ api.sendMessage(`๐ŸŽ‰ Welcome to the group!`, event.threadID);
344
+ }
345
+
346
+ if (event.logMessageType === "log:unsubscribe") {
347
+ const who = event.logMessageData.leftParticipantFbId;
348
+ api.sendMessage(`๐Ÿ‘‹ Goodbye ${who}! We'll miss you!`, event.threadID);
349
+ }
350
+ });
351
+ ```
352
+
353
+ ๐Ÿ“Ž Attachment Shapes
354
+
355
+ ```js
356
+ { type: "photo", ID, url, width, height, filename } // ๐Ÿ–ผ๏ธ
357
+ { type: "video", ID, url, duration, width, height } // ๐ŸŽฌ
358
+ { type: "audio", ID, url, duration, filename } // ๐ŸŽต
359
+ { type: "file", ID, url, size, mimeType, filename } // ๐Ÿ“„
360
+ { type: "sticker", stickerID, url, width, height } // ๐Ÿท๏ธ
361
+ ```
362
+
363
+ ---
364
+
365
+ ๐Ÿ“š API Reference
366
+
367
+ ---
368
+
369
+ ๐Ÿ”‘ Login
370
+
371
+ login(loginData, [options], [callback]) โ†’ Promise<api>
372
+
373
+ ```js
374
+ // appState (recommended) ๐Ÿ”
375
+ const api = await login({ appState: require("./appstate.json") });
376
+
377
+ // Email + password ๐Ÿ“ง
378
+ login({ email: "you@example.com", password: "secret" }, callback);
379
+
380
+ // With options โš™๏ธ
381
+ login({ appState }, { listenEvents: true, autoReconnect: true, autoUpdate: true }, callback);
382
+ ```
383
+
384
+ ---
385
+
386
+ ๐Ÿ‘‚ Listening
387
+
388
+ api.listen(callback) โ†’ MessageEmitter
389
+
390
+ Start listening for all events via MQTT. Your bot's ears are now open! ๐Ÿ‘‚
391
+
392
+ ```js
393
+ const emitter = api.listen((err, event) => {
394
+ if (err) return console.error(err);
395
+
396
+ if (event.type === "message")
397
+ console.log(`๐Ÿ’ฌ ${event.senderID} โ†’ ${event.body}`);
398
+
399
+ if (event.type === "message_reaction")
400
+ console.log(`๐Ÿ‘ ${event.senderID} reacted ${event.reaction}`);
401
+
402
+ if (event.type === "typ")
403
+ console.log(event.from, event.isTyping ? "is typing..." : "stopped typing");
404
+ });
405
+
406
+ // Stop listening
407
+ await emitter.stopListeningAsync();
408
+ ```
409
+
410
+ api.listenE2EE(callback) โ†’ MessageEmitter
411
+
412
+ ๐Ÿ” Combined listener: receives both regular MQTT messages and decrypted E2EE messages in the same callback. Call api.connectE2EE() first.
413
+
414
+ ```js
415
+ await api.connectE2EE(); // auto-creates .shadowx/e2ee_device.json
416
+
417
+ api.listenE2EE((err, event) => {
418
+ if (err) return console.error(err);
419
+
420
+ if (event.type === "message") {
421
+ if (event.isE2EE) {
422
+ // Must reply via E2EE for encrypted threads ๐Ÿ”’
423
+ api.e2ee.sendMessage(event.threadID, "Got your encrypted message! ๐Ÿ”");
424
+ } else {
425
+ api.sendMessage("Got it! ๐Ÿ‘", event.threadID);
426
+ }
427
+ }
428
+ });
429
+ ```
430
+
431
+ โš ๏ธ Important: For E2EE DM replies, always use api.e2ee.sendMessage() โ€” the regular MQTT path does not reach E2EE threads.
432
+
433
+ ---
434
+
435
+ ๐Ÿ” E2EE โ€” Encrypted Conversations
436
+
437
+ SHADOWX connects to Facebook's real E2EE infrastructure using the Signal Protocol โ€” the same battle-tested encryption used by the official Messenger mobile app. Military-grade security for your bot! ๐Ÿ›ก๏ธ
438
+
439
+ api.connectE2EE([deviceStorePath]) โ†’ Promise
440
+
441
+ Initialize E2EE. Automatically creates the device store directory if it doesn't exist.
442
+
443
+ ```js
444
+ // Default path: .shadowx/e2ee_device.json in process.cwd()
445
+ // Directory is auto-created โ€” no manual setup needed! โœจ
446
+ await api.connectE2EE();
447
+
448
+ // Custom path
449
+ await api.connectE2EE("./my_data/e2ee.json");
450
+
451
+ console.log("๐Ÿ” E2EE connected:", api.e2ee.isConnected()); // true
452
+ ```
453
+
454
+ ๐Ÿ”‘ Keep the device store file safe. Deleting it forces device re-registration with Facebook.
455
+
456
+ api.e2ee.sendMessage(threadId, msg, [replyToMessageId]) โ†’ Promise
457
+
458
+ msg can be a plain string or a message object with body and/or attachment (readable stream or array of streams).
459
+ Attachments on E2EE threads are automatically encrypted and sent via the Noise WebSocket.
460
+ Attachments on non-E2EE threads fall back to SHADOWX's own sendMessage.
461
+
462
+ ```js
463
+ // Text only ๐Ÿ“
464
+ await api.e2ee.sendMessage("100000000000001", "Hello, encrypted! ๐Ÿ”’");
465
+
466
+ // Reply โ†ฉ๏ธ
467
+ await api.e2ee.sendMessage("100000000000001", "Reply!", "mid.$replyID");
468
+
469
+ // Image ๐Ÿ–ผ๏ธ
470
+ const fs = require("fs");
471
+ await api.e2ee.sendMessage("100000000000001", {
472
+ body: "Check this out! ๐Ÿ“ธ",
473
+ attachment: fs.createReadStream("photo.jpg")
474
+ });
475
+
476
+ // Audio / Video / File โ€” same pattern, type detected automatically ๐ŸŽฏ
477
+ await api.e2ee.sendMessage("100000000000001", { attachment: fs.createReadStream("voice.ogg") });
478
+ await api.e2ee.sendMessage("100000000000001", { attachment: fs.createReadStream("clip.mp4") });
479
+
480
+ // Multiple attachments ๐Ÿ“Ž
481
+ await api.e2ee.sendMessage("100000000000001", {
482
+ attachment: [fs.createReadStream("a.jpg"), fs.createReadStream("b.jpg")]
483
+ });
484
+ ```
485
+
486
+ api.e2ee.sendReaction(threadId, messageId, reaction) โ†’ Promise
487
+
488
+ ```js
489
+ await api.e2ee.sendReaction("100000000000001", "mid.$abc123", "โค๏ธ");
490
+ ```
491
+
492
+ api.e2ee.sendTyping(threadId, isTyping) โ†’ Promise
493
+
494
+ ```js
495
+ await api.e2ee.sendTyping("100000000000001", true);
496
+ await api.e2ee.sendTyping("100000000000001", false);
497
+ ```
498
+
499
+ api.e2ee.unsendMessage(messageId, threadId) โ†’ Promise
500
+
501
+ ```js
502
+ await api.e2ee.unsendMessage("mid.$abc123", "100000000000001");
503
+ ```
504
+
505
+ api.e2ee.editMessage(threadId, messageId, text) โ†’ Promise
506
+
507
+ ```js
508
+ await api.e2ee.editMessage("100000000000001", "mid.$abc123", "Updated! โœจ");
509
+ ```
510
+
511
+ api.e2ee.onMessage(callback)
512
+
513
+ ```js
514
+ api.e2ee.onMessage((err, event) => {
515
+ // event.isE2EE is always true ๐Ÿ”’
516
+ console.log("[๐Ÿ” E2EE]", event.senderID, ":", event.body);
517
+ });
518
+ ```
519
+
520
+ api.e2ee.isConnected() โ†’ boolean
521
+
522
+ ```js
523
+ if (api.e2ee.isConnected()) await api.e2ee.sendMessage(threadID, "hi ๐Ÿ‘‹");
524
+ ```
525
+
526
+ api.e2ee.disconnect() โ†’ Promise
527
+
528
+ ```js
529
+ await api.e2ee.disconnect();
530
+ ```
531
+
532
+ ---
533
+
534
+ ๐Ÿ’ฌ Sending Messages
535
+
536
+ api.sendMessage(msg, threadID, [callback], [replyToMessageID]) โ†’ Promise
537
+
538
+ MQTT-first, HTTP fallback. Lightning fast! โšก
539
+
540
+ ```js
541
+ // Text ๐Ÿ“
542
+ await api.sendMessage("Hello! ๐Ÿ‘‹", threadID);
543
+
544
+ // Reply โ†ฉ๏ธ
545
+ api.sendMessage("Noted! ๐Ÿ‘", threadID, callback, replyToMessageID);
546
+
547
+ // Mention โ€” tag must include the @ prefix; fromIndex is the position of @ in body
548
+ api.sendMessage({
549
+ body: "Hey @John!",
550
+ mentions: [{ id: "100000000000001", tag: "@John", fromIndex: 4 }]
551
+ }, threadID);
552
+
553
+ // Multiple mentions ๐Ÿ‘ฅ
554
+ api.sendMessage({
555
+ body: "@Alice and @Bob check this out",
556
+ mentions: [
557
+ { id: "111111111111", tag: "@Alice", fromIndex: 0 },
558
+ { id: "222222222222", tag: "@Bob", fromIndex: 11 }
559
+ ]
560
+ }, threadID);
561
+
562
+ // Attachment from file stream ๐Ÿ“Ž
563
+ api.sendMessage({ body: "File!", attachment: fs.createReadStream("./photo.jpg") }, threadID);
564
+
565
+ // Attachment from a remote URL (stream via request) ๐ŸŒ
566
+ const request = require("request");
567
+ api.sendMessage({ attachment: request("https://example.com/img.jpg") }, threadID);
568
+
569
+ // Sticker / Emoji / Location ๐ŸŽฏ
570
+ api.sendMessage({ sticker: "369239263222822" }, threadID);
571
+ api.sendMessage({ emoji: "โค๏ธ", emojiSize: "large" }, threadID);
572
+ api.sendMessage({ location: { latitude: 14.5995, longitude: 120.9842, current: true } }, threadID);
573
+ ```
574
+
575
+ api.sendSticker(stickerID, threadID, [replyTo], [callback])
576
+
577
+ ```js
578
+ api.sendSticker("369239263222822", threadID);
579
+ ```
580
+
581
+ api.sendEmoji(emoji, [emojiSize], threadID, [callback])
582
+
583
+ ```js
584
+ api.sendEmoji("๐Ÿ”ฅ", "large", threadID);
585
+ // emojiSize: "small" | "medium" | "large"
586
+ ```
587
+
588
+ api.sendGif(gifSrc, threadID, [callback])
589
+
590
+ ```js
591
+ api.sendGif("https://media.giphy.com/media/xyz/giphy.gif", threadID);
592
+ ```
593
+
594
+ api.sendLocation(lat, lng, threadID, [isCurrent], [callback])
595
+
596
+ ```js
597
+ api.sendLocation(14.5995, 120.9842, threadID);
598
+ ```
599
+
600
+ api.sendImage(path, threadID, [caption], [callback])
601
+
602
+ ```js
603
+ api.sendImage("./photo.jpg", threadID, "Look at this! ๐Ÿ“ธ");
604
+ ```
605
+
606
+ api.sendVideo(path, threadID, [caption], [callback])
607
+
608
+ ```js
609
+ api.sendVideo("./video.mp4", threadID, "Watch ๐Ÿ‘€");
610
+ ```
611
+
612
+ api.sendAudio(path, threadID, [callback])
613
+
614
+ ```js
615
+ api.sendAudio("./voice.ogg", threadID);
616
+ ```
617
+
618
+ api.sendFile(path, threadID, [caption], [callback])
619
+
620
+ ```js
621
+ api.sendFile("./doc.pdf", threadID, "Here's the doc ๐Ÿ“„");
622
+ ```
623
+
624
+ api.shareLink(url, threadID, [message], [callback])
625
+
626
+ ```js
627
+ api.shareLink("https://github.com", threadID, "Check this out! ๐Ÿ”—");
628
+ ```
629
+
630
+ api.shareContact(text, userID, threadID, [callback])
631
+
632
+ ```js
633
+ api.shareContact("Meet my friend! ๐Ÿ‘ค", "100000000000001", threadID);
634
+ ```
635
+
636
+ api.forwardAttachment(attachmentID, userOrUsers, [callback])
637
+
638
+ ```js
639
+ api.forwardAttachment("attach_fbid", ["uid1", "uid2"]);
640
+ ```
641
+
642
+ Other send methods
643
+
644
+ ๐Ÿš€ Method ๐Ÿ“ Description
645
+ api.sendMessageMqtt(msg, threadID) MQTT-only, no HTTP fallback
646
+ api.OldMessage(msg, threadID, cb, replyTo, isSingleUser) HTTP-only (legacy)
647
+ api.sendMessageDM(msg, threadID) HTTP DM shorthand
648
+
649
+ ---
650
+
651
+ โœ๏ธ Message Actions
652
+
653
+ api.editMessage(text, messageID, [callback])
654
+
655
+ Uses MQTT (label 742) for instant delivery; falls back to HTTP if MQTT is not yet connected.
656
+
657
+ ```js
658
+ // Promise โœจ
659
+ await api.editMessage("Updated text โœ๏ธ", "mid.$abc123");
660
+
661
+ // Callback
662
+ api.editMessage("Updated text โœ๏ธ", "mid.$abc123", (err) => {
663
+ if (err) return console.error(err);
664
+ console.log("โœ… Message edited!");
665
+ });
666
+ ```
667
+
668
+ api.unsendMessage(messageID, [callback])
669
+
670
+ ```js
671
+ api.unsendMessage("mid.$abc123", callback);
672
+ ```
673
+
674
+ api.deleteMessage(messageIDs, [callback])
675
+
676
+ ```js
677
+ api.deleteMessage(["mid.$abc123", "mid.$def456"], callback);
678
+ ```
679
+
680
+ api.setMessageReaction(reaction, messageID, threadID, [callback])
681
+
682
+ ```js
683
+ api.setMessageReaction("๐Ÿ˜", messageID, threadID);
684
+ api.setMessageReaction("", messageID, threadID); // remove
685
+ ```
686
+
687
+ api.getMessage(threadID, messageID, [callback])
688
+
689
+ ```js
690
+ const msg = await api.getMessage(threadID, "mid.$abc123");
691
+ console.log(`๐Ÿ’ฌ ${msg.senderID}: ${msg.body}`);
692
+ ```
693
+
694
+ ---
695
+
696
+ ๐Ÿ“Ž Attachments & Media
697
+
698
+ api.uploadAttachment(attachments, [callback]) โ†’ Promise
699
+
700
+ ```js
701
+ const [meta] = await api.uploadAttachment([fs.createReadStream("./photo.jpg")]);
702
+ console.log("๐Ÿ“ File ID:", meta.attach_fbid);
703
+ ```
704
+
705
+ ๐Ÿ’ก Pro tip โ€” stream a URL as attachment:
706
+
707
+ ```js
708
+ const request = require("request");
709
+ api.sendMessage({ attachment: request("https://example.com/img.jpg") }, threadID);
710
+ ```
711
+
712
+ api.uploadImageToImgbb(image, [expiration], [callback]) โ†’ Promise
713
+
714
+ ```js
715
+ // URL, Buffer, or base64
716
+ const result = await api.uploadImageToImgbb("https://example.com/img.jpg");
717
+ console.log("๐Ÿ–ผ๏ธ URL:", result.data.url);
718
+ ```
719
+
720
+ api.resolvePhotoUrl(photoID, [callback])
721
+
722
+ ```js
723
+ api.resolvePhotoUrl("photo_id", (err, url) => console.log(url));
724
+ ```
725
+
726
+ ---
727
+
728
+ ๐Ÿงต Thread Management
729
+
730
+ api.getThreadInfo(threadID, [callback]) โ†’ Promise
731
+
732
+ ```js
733
+ const info = await api.getThreadInfo(threadID);
734
+ // info.threadName, .participantIDs, .isGroup, .unreadCount, .adminIDs, .emoji, .color
735
+ ```
736
+
737
+ api.getThreadList(limit, [timestamp], [tags], [callback]) โ†’ Promise
738
+
739
+ ```js
740
+ const threads = await api.getThreadList(10, null, ["INBOX"]);
741
+ // tags: "INBOX" | "PENDING" | "ARCHIVED"
742
+ ```
743
+
744
+ api.getThreadHistory(threadID, amount, [timestamp], [callback]) โ†’ Promise
745
+
746
+ ```js
747
+ const messages = await api.getThreadHistory(threadID, 20, null);
748
+ ```
749
+
750
+ api.createGroup(message, participantIDs, [callback])
751
+
752
+ ```js
753
+ const { threadID } = await api.createGroup("Hey! ๐Ÿ‘‹", ["uid1", "uid2"]);
754
+ ```
755
+
756
+ api.deleteThread(threadID, [callback])
757
+
758
+ ```js
759
+ api.deleteThread(threadID);
760
+ ```
761
+
762
+ api.muteThread(threadID, muteSeconds, [callback])
763
+
764
+ ```js
765
+ api.muteThread(threadID, 3600); // mute 1 hour ๐Ÿ”‡
766
+ api.muteThread(threadID, 0); // unmute ๐Ÿ”Š
767
+ ```
768
+
769
+ api.changeArchivedStatus(threadID, archive, [callback])
770
+
771
+ ```js
772
+ api.changeArchivedStatus(threadID, true); // archive ๐Ÿ“ฆ
773
+ api.changeArchivedStatus(threadID, false); // unarchive ๐Ÿ“‚
774
+ ```
775
+
776
+ ---
777
+
778
+ ๐ŸŽจ Thread Customization
779
+
780
+ ```js
781
+ api.setTitle("New Name โœจ", threadID);
782
+ api.changeThreadColor("#0084FF", threadID);
783
+ api.changeThreadEmoji("๐Ÿ”ฅ", threadID);
784
+ api.changeNickname("The Boss ๐Ÿ‘‘", threadID, "100000000000001");
785
+ api.changeGroupImage(fs.createReadStream("./group.jpg"), threadID);
786
+ api.changeThreadTheme("197931430960496", threadID);
787
+ ```
788
+
789
+ ---
790
+
791
+ ๐Ÿ‘ฅ Thread Members
792
+
793
+ ```js
794
+ api.addUserToGroup("100000000000001", threadID);
795
+ api.removeUserFromGroup("100000000000001", threadID);
796
+ api.changeAdminStatus(threadID, "100000000000001", true); // promote ๐Ÿ‘‘
797
+ api.changeAdminStatus(threadID, "100000000000001", false); // demote
798
+ ```
799
+
800
+ ---
801
+
802
+ ๐Ÿ“Š Polls
803
+
804
+ api.createPoll(title, threadID, [options], [callback])
805
+
806
+ ```js
807
+ api.createPoll("Favorite language? ๐Ÿค”", threadID, { JS: false, Python: false });
808
+ ```
809
+
810
+ api.setPollVote(pollID, optionIDs, [newOptions], [callback])
811
+
812
+ ```js
813
+ api.setPollVote(pollID, ["option_id"], [], callback);
814
+ ```
815
+
816
+ ---
817
+
818
+ ๐Ÿ“Œ Pins
819
+
820
+ ```js
821
+ api.pinMessage("mid.$abc123", threadID); // MQTT label 430 โ€” pin_msg_v2_
822
+ api.unpinMessage("mid.$abc123", threadID); // MQTT label 431 โ€” unpin_msg_v2_
823
+ ```
824
+
825
+ ---
826
+
827
+ ๐Ÿ“ฌ Read / Delivery Receipts
828
+
829
+ ```js
830
+ api.markAsRead(threadID);
831
+ api.markAsDelivered(threadID, messageID);
832
+ api.markAsSeen();
833
+ api.markAsReadAll();
834
+
835
+ // Typing indicator โœ๏ธ
836
+ api.sendTypingIndicator(threadID, true);
837
+ setTimeout(() => api.sendTypingIndicator(threadID, false), 3000);
838
+ ```
839
+
840
+ ---
841
+
842
+ ๐Ÿ‘ค User Info & Friends
843
+
844
+ api.getUserInfo(id, [callback]) โ†’ Promise
845
+
846
+ ```js
847
+ const users = await api.getUserInfo(["uid1", "uid2"]);
848
+ // users[uid].name, .thumbSrc, .gender
849
+ ```
850
+
851
+ Quick reference
852
+
853
+ ```js
854
+ api.getCurrentUserID() // โ†’ string
855
+ api.getUserID("John Doe", callback) // search by name ๐Ÿ”
856
+ api.getUID("https://facebook.com/zuck", cb) // โ†’ "4"
857
+ api.getFriendsList(callback) // full friends list ๐Ÿ‘ฅ
858
+ api.searchFriends("Maria", callback)
859
+ api.getAvatarUser("uid", callback) // returns graph.facebook.com picture URL ๐Ÿ–ผ๏ธ
860
+ api.getAvatarUser("uid", "square", callback) // type: square | large | normal | small
861
+ api.getProfileInfo("uid", callback) // GraphQL node
862
+ api.getPublicData("uid", callback) // public scrape (name, vanity, uid)
863
+ api.getRepInfo(callback) // account rep info
864
+ ```
865
+
866
+ Actions
867
+
868
+ ```js
869
+ api.sendFriendRequest("uid"); // GraphQL FriendingCometFriendRequestSendMutation
870
+ api.handleFriendRequest("uid", true); // accept via /requests/friends/ajax/ โœ…
871
+ api.handleFriendRequest("uid", false); // decline โŒ
872
+ api.handleMessageRequest(threadID, true);
873
+ api.changeBlockedStatus("uid", true); // block ๐Ÿšซ
874
+ api.changeBlockedStatus("uid", false); // unblock
875
+ api.followUser("uid");
876
+ api.unfollowUser("uid");
877
+ api.unfriend("uid");
878
+ api.setActiveStatus(true); // appear online ๐ŸŸข
879
+ api.logout();
880
+ ```
881
+
882
+ ---
883
+
884
+ ๐ŸŒ Social / Posting
885
+
886
+ ```js
887
+ // Reactions: "like" | "love" | "haha" | "wow" | "sad" | "angry" | "care" | "none"
888
+ api.reactToPost("postID", "love"); // โค๏ธ
889
+ api.reactToComment("commentID", "haha"); // ๐Ÿ˜„
890
+ api.postComment("postID", "Great post! ๐Ÿ”ฅ");
891
+ api.deleteComment("commentID");
892
+ api.sharePost("postID", "Check this!");
893
+ api.getPostInfo("postID", callback);
894
+ api.getStoryReactions("feedbackID", callback);
895
+ ```
896
+
897
+ ---
898
+
899
+ ๐Ÿท๏ธ Stickers
900
+
901
+ ```js
902
+ api.getStickers("thumbs up", null, (err, stickers) => {
903
+ if (stickers.length) api.sendSticker(stickers[0].id, threadID);
904
+ });
905
+
906
+ api.getStickerPacks((err, packs) => {
907
+ packs.forEach(p => console.log(`๐Ÿท๏ธ ${p.id}: ${p.name}`));
908
+ });
909
+ ```
910
+
911
+ ---
912
+
913
+ ๐Ÿ”ง HTTP Utilities
914
+
915
+ ```js
916
+ api.httpGet(url, params, callback);
917
+ api.httpPost(url, form, callback);
918
+ api.httpPostFormData(url, form, callback);
919
+
920
+ const dtsg = await api.getFreshDtsg();
921
+ ```
922
+
923
+ ---
924
+
925
+ โšก Misc / Config
926
+
927
+ ```js
928
+ api.setOptions({ listenTyping: true, proxy: "http://127.0.0.1:8080" });
929
+ api.getAppState() // โ†’ cookie array (save to reuse session) ๐Ÿช
930
+ api.getCurrentUserID() // โ†’ string
931
+ api.getCtx() // โ†’ internal ctx object (region, fb_dtsg, etc.)
932
+ api.checkForUpdate() // โ†’ Promise<boolean> ๐Ÿ”„
933
+
934
+ // Add a custom method to the api object
935
+ api.addExternalModule("myFunc", (defaultFuncs, api, ctx) => {
936
+ return function (text, threadID) {
937
+ return api.sendMessage("[๐Ÿค– BOT] " + text, threadID);
938
+ };
939
+ });
940
+ api.myFunc("Hello!", threadID);
941
+ ```
942
+
943
+ ---
944
+
945
+ ๐Ÿ“ข Broadcast
946
+
947
+ api.sendBroadcast(msg, threadIDs, [options], [callback]) โ†’ Promise
948
+
949
+ Send to multiple threads with rate limiting and per-thread delivery tracking. Mass messaging made easy! ๐Ÿ“ฃ
950
+
951
+ ```js
952
+ const result = await api.sendBroadcast(
953
+ "Hello everyone! ๐Ÿ‘‹",
954
+ ["THREAD_1", "THREAD_2", "THREAD_3"],
955
+ {
956
+ delay: 2000, // ms between sends
957
+ parallel: 2, // max concurrent sends
958
+ onEach: (err, info, id) => {
959
+ console.log(err ? `โŒ Failed: ${id}` : `โœ… Sent: ${id}`);
960
+ }
961
+ }
962
+ );
963
+
964
+ console.log(`๐Ÿ“Š ${result.sent.length}/${result.total} delivered`);
965
+ ```
966
+
967
+ ---
968
+
969
+ ๐Ÿ›ก๏ธ Session Guard
970
+
971
+ Protects your appstate from corruption and silent logouts. Your appstate's bodyguard! ๐Ÿ’ช
972
+
973
+ ```js
974
+ // Call right after login
975
+ api.sessionGuard("./appstate.json");
976
+
977
+ // Custom timing โฑ๏ธ
978
+ api.sessionGuard("./appstate.json", {
979
+ interval: 3 * 60 * 1000, // save every 3 min
980
+ debounce: 60 * 1000 // cooldown after sendMessage
981
+ });
982
+ ```
983
+
984
+ What it does:
985
+
986
+ ยท ๐Ÿ’พ Auto-saves appstate every N minutes
987
+ ยท ๐Ÿ“ Saves after every successful sendMessage (debounced)
988
+ ยท ๐Ÿ›ก๏ธ Corruption guard: never overwrites a larger appstate with a smaller one
989
+ ยท ๐Ÿ“ฆ Auto-backup: writes appstate.json.bak before every overwrite
990
+
991
+ ```js
992
+ api.saveSession() // โ†’ boolean (force save now) ๐Ÿ’พ
993
+ api.restoreSessionBackup() // โ†’ boolean (restore .bak if corrupted) ๐Ÿ”„
994
+ api.stopSessionGuard() // stop the timer โน๏ธ
995
+ ```
996
+
997
+ ---
998
+
999
+ ๐Ÿ”„ Auto Update System
1000
+
1001
+ SHADOWX includes an intelligent auto-update system that keeps your bot current with the latest features and fixes:
1002
+
1003
+ How It Works
1004
+
1005
+ 1. On Startup: Automatically checks for updates when you require('shadowx-fca')
1006
+ 2. On Login: Checks again after successful Facebook login
1007
+ 3. Graceful: Failed update checks never break your bot
1008
+ 4. Smart: Shows changelog before updating
1009
+
1010
+ Manual Control
1011
+
1012
+ ```js
1013
+ // Check for updates manually
1014
+ const { checkUpdate } = require('shadowx-fca');
1015
+ checkUpdate().then(updated => {
1016
+ console.log(updated ? "Updated! ๐Ÿ”„" : "Already latest! โœ…");
1017
+ });
1018
+
1019
+ // Via API after login
1020
+ api.checkForUpdate();
1021
+
1022
+ // Environment variables
1023
+ DISABLE_UPDATE=true node index.js // Skip update check
1024
+ AUTO_UPDATE=true node index.js // Auto-accept updates (CI/CD)
1025
+ ```
1026
+
1027
+ ---
1028
+
1029
+ ๐Ÿ—๏ธ E2EE Architecture
1030
+
1031
+ SHADOWX harnesses Facebook's real E2EE infrastructure โ€” identical to the official Messenger mobile app:
1032
+
1033
+ ```
1034
+ ๐Ÿ” Signal Protocol stack
1035
+ โ”œโ”€โ”€ @signalapp/libsignal-client โ€” Double Ratchet + X3DH
1036
+ โ”œโ”€โ”€ Noise_XX_25519_AESGCM_SHA256 โ€” WebSocket handshake
1037
+ โ”œโ”€โ”€ WA-binary + Protobuf โ€” message frame encoding
1038
+ โ””โ”€โ”€ ICDC device registration โ€” registers bot as E2EE device
1039
+
1040
+ ๐Ÿš€ Connection flow
1041
+ api.connectE2EE()
1042
+ โ”œโ”€ 1. Auto-create device store directory ๐Ÿ“
1043
+ โ”œโ”€ 2. Bootstrap auth (re-use existing cookie session) ๐Ÿช
1044
+ โ”œโ”€ 3. Register device keys with Facebook (first run only) ๐Ÿ”‘
1045
+ โ””โ”€ 4. Open Noise WebSocket โ†’ ready โœ…
1046
+
1047
+ ๐Ÿ“จ Message flow
1048
+ Incoming โ†’ Noise WS โ†’ listenE2EE callback (event.isE2EE = true)
1049
+ Outgoing โ†’ api.e2ee.sendMessage() โ†’ Noise WS โ†’ Facebook
1050
+ ```
1051
+
1052
+ ๐Ÿ”’ The E2EE engine is vendored inside the package. Your bot continues to work even if the upstream npm dependency is removed or broken. Maximum reliability! ๐Ÿ’ช
1053
+
1054
+ ---
1055
+
1056
+ <div align="center">
1057
+
1058
+ โญ If this project helps you, consider giving it a star!
1059
+ ๐Ÿ“ง Questions? Open an issue or reach out on GitHub.
1060
+
1061
+ <br>
1062
+
1063
+ MIT License ยท Copyright ยฉ 2026 Mueid Mursalin Rifat
1064
+
1065
+ </div>
1066
+ ```