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.
- package/README.md +1066 -0
- package/build/messagix.dll +0 -0
- package/build/messagix.so +0 -0
- package/checkUpdate.js +393 -0
- package/config.json +17 -0
- package/e2ee.js +563 -0
- package/e2eetest.js +356 -0
- package/index.js +611 -0
- package/lib/index.mjs +1412 -0
- package/logger.js +500 -0
- package/package.json +65 -0
- package/src/GetBotInfo.js +66 -0
- package/src/OldMessage.js +182 -0
- package/src/Screenshot.js +83 -0
- package/src/addExternalModule.js +13 -0
- package/src/addUserToGroup.js +33 -0
- package/src/approveGroupJoinRequests.js +18 -0
- package/src/changeAdminStatus.js +16 -0
- package/src/changeArchivedStatus.js +17 -0
- package/src/changeAvatar.js +136 -0
- package/src/changeAvatarV2.js +86 -0
- package/src/changeAvt.js +85 -0
- package/src/changeBio.js +76 -0
- package/src/changeBlockedStatus.js +20 -0
- package/src/changeBlockedStatusMqtt.js +80 -0
- package/src/changeCover.js +72 -0
- package/src/changeGroupImage.js +16 -0
- package/src/changeName.js +79 -0
- package/src/changeNickname.js +16 -0
- package/src/changeThreadColor.js +15 -0
- package/src/changeThreadEmoji.js +15 -0
- package/src/changeThreadMemberNickname.js +6 -0
- package/src/changeUsername.js +59 -0
- package/src/createCommentPost.js +230 -0
- package/src/createNewGroup.js +38 -0
- package/src/createNote.js +35 -0
- package/src/createPoll.js +27 -0
- package/src/createPost.js +276 -0
- package/src/createThemeAI.js +129 -0
- package/src/data/cache/system/data.json +4 -0
- package/src/data/cache/system/datahandle.js +21 -0
- package/src/data/getThreadInfo.json +1 -0
- package/src/deleteComment.js +23 -0
- package/src/deleteMessage.js +15 -0
- package/src/deleteThread.js +15 -0
- package/src/denyGroupJoinRequests.js +18 -0
- package/src/e2ee/crypto.js +173 -0
- package/src/e2ee/index.js +144 -0
- package/src/e2ee/proto/ArmadilloApplication.proto +281 -0
- package/src/e2ee/proto/ArmadilloICDC.proto +14 -0
- package/src/e2ee/proto/ConsumerApplication.proto +232 -0
- package/src/e2ee/proto/MessageApplication.proto +82 -0
- package/src/e2ee/proto/MessageTransport.proto +77 -0
- package/src/e2ee/proto/WACommon.proto +66 -0
- package/src/e2ee/proto/WAMediaTransport.proto +176 -0
- package/src/e2ee/proto/proto-writer.ts +76 -0
- package/src/e2ee/protocol.js +196 -0
- package/src/e2ee/ratchet.js +219 -0
- package/src/e2ee/store.js +182 -0
- package/src/e2ee.js +8 -0
- package/src/editMessage.js +56 -0
- package/src/editMessageOld.js +67 -0
- package/src/enableReactions.js +24 -0
- package/src/follow.js +74 -0
- package/src/followUser.js +23 -0
- package/src/forwardAttachment.js +16 -0
- package/src/friendList.js +98 -0
- package/src/getAccess.js +112 -0
- package/src/getAppState.js +13 -0
- package/src/getAvatarUser.js +11 -0
- package/src/getBio.js +24 -0
- package/src/getBotInitialData.js +42 -0
- package/src/getCtx.js +5 -0
- package/src/getCurrentUserID.js +6 -0
- package/src/getEmojiUrl.js +29 -0
- package/src/getFriendsList.js +36 -0
- package/src/getMessage.js +37 -0
- package/src/getNotes.js +17 -0
- package/src/getOptions.js +5 -0
- package/src/getPinnedMessages.js +33 -0
- package/src/getPostInfo.js +17 -0
- package/src/getProfileInfo.js +17 -0
- package/src/getPublicData.js +25 -0
- package/src/getRegion.js +7 -0
- package/src/getRepInfo.js +17 -0
- package/src/getStickerPacks.js +25 -0
- package/src/getStickers.js +39 -0
- package/src/getStoryReactions.js +18 -0
- package/src/getThreadHistory.js +45 -0
- package/src/getThreadHistoryDeprecated.js +71 -0
- package/src/getThreadInfo.js +73 -0
- package/src/getThreadInfoDeprecated.js +56 -0
- package/src/getThreadList.js +76 -0
- package/src/getThreadListDeprecated.js +46 -0
- package/src/getThreadPictures.js +59 -0
- package/src/getThreadTheme.js +77 -0
- package/src/getUID.js +17 -0
- package/src/getUserID.js +17 -0
- package/src/getUserInfo.js +28 -0
- package/src/handleFriendRequest.js +21 -0
- package/src/handleMessageRequest.js +15 -0
- package/src/httpGet.js +13 -0
- package/src/httpPost.js +12 -0
- package/src/httpPostFormData.js +12 -0
- package/src/listenE2EE.js +75 -0
- package/src/listenMqtt.js +802 -0
- package/src/listenNotification.js +85 -0
- package/src/logout.js +22 -0
- package/src/markAsDelivered.js +17 -0
- package/src/markAsRead.js +14 -0
- package/src/markAsReadAll.js +15 -0
- package/src/markAsSeen.js +15 -0
- package/src/metaTheme.js +185 -0
- package/src/muteThread.js +52 -0
- package/src/note.js +228 -0
- package/src/pin.js +53 -0
- package/src/pinMessage.js +6 -0
- package/src/postComment.js +29 -0
- package/src/postFormData.js +46 -0
- package/src/reactToComment.js +31 -0
- package/src/reactToPost.js +32 -0
- package/src/refreshFb_dtsg.js +31 -0
- package/src/removeSuspiciousAccount.js +74 -0
- package/src/removeUserFromGroup.js +15 -0
- package/src/reply.js +442 -0
- package/src/resolvePhotoUrl.js +15 -0
- package/src/searchForThread.js +20 -0
- package/src/searchFriends.js +28 -0
- package/src/searchStickers.js +53 -0
- package/src/send.js +46 -0
- package/src/sendAudio.js +8 -0
- package/src/sendBroadcast.js +93 -0
- package/src/sendButtons.js +161 -0
- package/src/sendComment.js +159 -0
- package/src/sendEmoji.js +10 -0
- package/src/sendFile.js +9 -0
- package/src/sendFriendRequest.js +33 -0
- package/src/sendGif.js +24 -0
- package/src/sendImage.js +9 -0
- package/src/sendLocation.js +9 -0
- package/src/sendMessage.js +487 -0
- package/src/sendMessage1.js +309 -0
- package/src/sendMessageMqtt.js +68 -0
- package/src/sendSticker.js +8 -0
- package/src/sendTypingIndicator.js +36 -0
- package/src/sendTypingIndicatorV2.js +28 -0
- package/src/sendVideo.js +9 -0
- package/src/sessionGuard.js +130 -0
- package/src/setActiveStatus.js +16 -0
- package/src/setMessageReaction.js +61 -0
- package/src/setMessageReactionMqtt.js +62 -0
- package/src/setOptions.js +22 -0
- package/src/setPollVote.js +17 -0
- package/src/setPostReaction.js +112 -0
- package/src/setProfileGuard.js +44 -0
- package/src/setProfileLock.js +93 -0
- package/src/setStoryReaction.js +129 -0
- package/src/setStorySeen.js +99 -0
- package/src/setThreadTheme.js +17 -0
- package/src/setTitle.js +15 -0
- package/src/shareContact.js +33 -0
- package/src/shareLink.js +8 -0
- package/src/sharePost.js +31 -0
- package/src/stopListenMqtt.js +23 -0
- package/src/storyManager.js +353 -0
- package/src/suggestFriend.js +128 -0
- package/src/threadColors.js +131 -0
- package/src/unfollowUser.js +23 -0
- package/src/unfriend.js +15 -0
- package/src/unpinMessage.js +6 -0
- package/src/unsendMessage.js +14 -0
- package/src/uploadAttachment.js +58 -0
- package/src/uploadImageToImgbb.js +29 -0
- 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
|
+
```
|