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/lib/index.mjs ADDED
@@ -0,0 +1,1412 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/client.ts
5
+ import { EventEmitter } from "events";
6
+
7
+ // src/native.ts
8
+ import fs from "fs";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+ import koffi from "koffi";
12
+ import JSONBig from "yumi-json-bigint";
13
+ var JSONBigNative = JSONBig({
14
+ useNativeBigInt: true,
15
+ parseAsBigInt32: true
16
+ });
17
+ function resolveDirname() {
18
+ return path.dirname(fileURLToPath(import.meta.url));
19
+ }
20
+ __name(resolveDirname, "resolveDirname");
21
+ function libPath() {
22
+ const base = path.join(resolveDirname(), "..", "build");
23
+ if (process.platform === "win32") return path.join(base, "messagix.dll");
24
+ if (process.platform === "darwin") return path.join(base, "messagix.dylib");
25
+ return path.join(base, "messagix.so");
26
+ }
27
+ __name(libPath, "libPath");
28
+ var LIB_FILE = libPath();
29
+ if (!fs.existsSync(LIB_FILE)) {
30
+ throw new Error(`Native library not found at ${LIB_FILE}. Run: npm run build:go`);
31
+ }
32
+ var lib = koffi.load(LIB_FILE);
33
+ var mk = /* @__PURE__ */ __name((ret, name, args) => lib.func(name, ret, args), "mk");
34
+ var fns = {
35
+ MxFreeCString: mk("void", "MxFreeCString", ["char*"]),
36
+ MxNewClient: mk("str", "MxNewClient", ["str"]),
37
+ MxConnect: mk("str", "MxConnect", ["str"]),
38
+ MxConnectE2EE: mk("str", "MxConnectE2EE", ["str"]),
39
+ MxDisconnect: mk("str", "MxDisconnect", ["str"]),
40
+ MxIsConnected: mk("str", "MxIsConnected", ["str"]),
41
+ MxSendMessage: mk("str", "MxSendMessage", ["str"]),
42
+ MxSendReaction: mk("str", "MxSendReaction", ["str"]),
43
+ MxEditMessage: mk("str", "MxEditMessage", ["str"]),
44
+ MxUnsendMessage: mk("str", "MxUnsendMessage", ["str"]),
45
+ MxSendTyping: mk("str", "MxSendTyping", ["str"]),
46
+ MxMarkRead: mk("str", "MxMarkRead", ["str"]),
47
+ MxUploadMedia: mk("str", "MxUploadMedia", ["str"]),
48
+ MxSendImage: mk("str", "MxSendImage", ["str"]),
49
+ MxSendVideo: mk("str", "MxSendVideo", ["str"]),
50
+ MxSendVoice: mk("str", "MxSendVoice", ["str"]),
51
+ MxSendFile: mk("str", "MxSendFile", ["str"]),
52
+ MxSendSticker: mk("str", "MxSendSticker", ["str"]),
53
+ MxCreateThread: mk("str", "MxCreateThread", ["str"]),
54
+ MxGetUserInfo: mk("str", "MxGetUserInfo", ["str"]),
55
+ MxSetGroupPhoto: mk("str", "MxSetGroupPhoto", ["str"]),
56
+ MxRenameThread: mk("str", "MxRenameThread", ["str"]),
57
+ MxMuteThread: mk("str", "MxMuteThread", ["str"]),
58
+ MxDeleteThread: mk("str", "MxDeleteThread", ["str"]),
59
+ MxSearchUsers: mk("str", "MxSearchUsers", ["str"]),
60
+ MxPollEvents: mk("str", "MxPollEvents", ["str"]),
61
+ MxSendE2EEMessage: mk("str", "MxSendE2EEMessage", ["str"]),
62
+ MxSendE2EEReaction: mk("str", "MxSendE2EEReaction", ["str"]),
63
+ MxSendE2EETyping: mk("str", "MxSendE2EETyping", ["str"]),
64
+ MxEditE2EEMessage: mk("str", "MxEditE2EEMessage", ["str"]),
65
+ MxUnsendE2EEMessage: mk("str", "MxUnsendE2EEMessage", ["str"]),
66
+ MxGetDeviceData: mk("str", "MxGetDeviceData", ["str"]),
67
+ // E2EE Media functions
68
+ MxSendE2EEImage: mk("str", "MxSendE2EEImage", ["str"]),
69
+ MxSendE2EEVideo: mk("str", "MxSendE2EEVideo", ["str"]),
70
+ MxSendE2EEAudio: mk("str", "MxSendE2EEAudio", ["str"]),
71
+ MxSendE2EEDocument: mk("str", "MxSendE2EEDocument", ["str"]),
72
+ MxSendE2EESticker: mk("str", "MxSendE2EESticker", ["str"]),
73
+ MxDownloadE2EEMedia: mk("str", "MxDownloadE2EEMedia", ["str"]),
74
+ // Cookie and push notification functions
75
+ MxGetCookies: mk("str", "MxGetCookies", ["str"]),
76
+ MxRegisterPushNotifications: mk("str", "MxRegisterPushNotifications", ["str"])
77
+ };
78
+ function call(fn, payload) {
79
+ const input = JSONBigNative.stringify(payload);
80
+ const bound = fns[fn];
81
+ const out = bound(input);
82
+ const data = JSONBigNative.parse(out);
83
+ if (!data.ok) throw new Error(data.error || "Unknown error");
84
+ return data.data;
85
+ }
86
+ __name(call, "call");
87
+ function callAsync(fn, payload) {
88
+ return new Promise((resolve, reject) => {
89
+ setTimeout(() => {
90
+ try {
91
+ const result = call(fn, payload);
92
+ resolve(result);
93
+ } catch (err) {
94
+ reject(err);
95
+ }
96
+ }, 0);
97
+ });
98
+ }
99
+ __name(callAsync, "callAsync");
100
+ var native = {
101
+ newClient: /* @__PURE__ */ __name((cfg) => call("MxNewClient", cfg), "newClient"),
102
+ connect: /* @__PURE__ */ __name((handle) => call("MxConnect", { handle }), "connect"),
103
+ connectE2EE: /* @__PURE__ */ __name((handle) => callAsync("MxConnectE2EE", { handle }), "connectE2EE"),
104
+ disconnect: /* @__PURE__ */ __name((handle) => call("MxDisconnect", { handle }), "disconnect"),
105
+ isConnected: /* @__PURE__ */ __name((handle) => call("MxIsConnected", { handle }), "isConnected"),
106
+ sendMessage: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendMessage", { handle, options }), "sendMessage"),
107
+ sendReaction: /* @__PURE__ */ __name((handle, threadId, messageId, emoji) => callAsync("MxSendReaction", { handle, threadId, messageId, emoji }), "sendReaction"),
108
+ editMessage: /* @__PURE__ */ __name((handle, messageId, newText) => callAsync("MxEditMessage", { handle, messageId, newText }), "editMessage"),
109
+ unsendMessage: /* @__PURE__ */ __name((handle, messageId) => callAsync("MxUnsendMessage", { handle, messageId }), "unsendMessage"),
110
+ sendTyping: /* @__PURE__ */ __name((handle, threadId, isTyping, isGroup, threadType) => callAsync("MxSendTyping", { handle, threadId, isTyping, isGroup, threadType }), "sendTyping"),
111
+ markRead: /* @__PURE__ */ __name((handle, threadId, watermarkTs) => callAsync("MxMarkRead", { handle, threadId, watermarkTs: watermarkTs || 0n }), "markRead"),
112
+ uploadMedia: /* @__PURE__ */ __name((handle, options) => callAsync("MxUploadMedia", { handle, options }), "uploadMedia"),
113
+ sendImage: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendImage", { handle, options }), "sendImage"),
114
+ sendVideo: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendVideo", { handle, options }), "sendVideo"),
115
+ sendVoice: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendVoice", { handle, options }), "sendVoice"),
116
+ sendFile: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendFile", { handle, options }), "sendFile"),
117
+ sendSticker: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendSticker", { handle, options }), "sendSticker"),
118
+ createThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxCreateThread", { handle, options }), "createThread"),
119
+ getUserInfo: /* @__PURE__ */ __name((handle, options) => callAsync("MxGetUserInfo", { handle, options }), "getUserInfo"),
120
+ setGroupPhoto: /* @__PURE__ */ __name((handle, threadId, data, mimeType) => callAsync("MxSetGroupPhoto", { handle, threadId, data, mimeType }), "setGroupPhoto"),
121
+ renameThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxRenameThread", { handle, options }), "renameThread"),
122
+ muteThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxMuteThread", { handle, options }), "muteThread"),
123
+ deleteThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxDeleteThread", { handle, options }), "deleteThread"),
124
+ searchUsers: /* @__PURE__ */ __name((handle, options) => callAsync("MxSearchUsers", {
125
+ handle,
126
+ options
127
+ }), "searchUsers"),
128
+ pollEvents: /* @__PURE__ */ __name((handle, timeoutMs) => callAsync("MxPollEvents", { handle, timeoutMs }), "pollEvents"),
129
+ // E2EE functions
130
+ sendE2EEMessage: /* @__PURE__ */ __name((handle, chatJid, text, replyToId, replyToSenderJid) => callAsync("MxSendE2EEMessage", {
131
+ handle,
132
+ chatJid,
133
+ text,
134
+ replyToId,
135
+ replyToSenderJid
136
+ }), "sendE2EEMessage"),
137
+ sendE2EEReaction: /* @__PURE__ */ __name((handle, chatJid, messageId, senderJid, emoji) => callAsync("MxSendE2EEReaction", { handle, chatJid, messageId, senderJid, emoji }), "sendE2EEReaction"),
138
+ sendE2EETyping: /* @__PURE__ */ __name((handle, chatJid, isTyping) => callAsync("MxSendE2EETyping", { handle, chatJid, isTyping }), "sendE2EETyping"),
139
+ editE2EEMessage: /* @__PURE__ */ __name((handle, chatJid, messageId, newText) => callAsync("MxEditE2EEMessage", { handle, chatJid, messageId, newText }), "editE2EEMessage"),
140
+ unsendE2EEMessage: /* @__PURE__ */ __name((handle, chatJid, messageId) => callAsync("MxUnsendE2EEMessage", { handle, chatJid, messageId }), "unsendE2EEMessage"),
141
+ getDeviceData: /* @__PURE__ */ __name((handle) => call("MxGetDeviceData", { handle }), "getDeviceData"),
142
+ // E2EE Media functions
143
+ sendE2EEImage: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEImage", { handle, options }), "sendE2EEImage"),
144
+ sendE2EEVideo: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEVideo", { handle, options }), "sendE2EEVideo"),
145
+ sendE2EEAudio: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEAudio", { handle, options }), "sendE2EEAudio"),
146
+ sendE2EEDocument: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEDocument", { handle, options }), "sendE2EEDocument"),
147
+ sendE2EESticker: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EESticker", { handle, options }), "sendE2EESticker"),
148
+ downloadE2EEMedia: /* @__PURE__ */ __name((handle, options) => callAsync("MxDownloadE2EEMedia", { handle, options }), "downloadE2EEMedia"),
149
+ // Cookie and push notification functions
150
+ getCookies: /* @__PURE__ */ __name((handle) => call("MxGetCookies", { handle }), "getCookies"),
151
+ registerPushNotifications: /* @__PURE__ */ __name((handle, options) => callAsync("MxRegisterPushNotifications", { handle, options }), "registerPushNotifications"),
152
+ unload: /* @__PURE__ */ __name(() => lib.unload(), "unload")
153
+ };
154
+
155
+ // src/client.ts
156
+ var Client = class extends EventEmitter {
157
+ static {
158
+ __name(this, "Client");
159
+ }
160
+ handle = null;
161
+ options;
162
+ cookies;
163
+ _user = null;
164
+ _initialData = null;
165
+ eventLoopRunning = false;
166
+ eventLoopAbort = null;
167
+ _socketReady = false;
168
+ _e2eeConnected = false;
169
+ _fullyReadyEmitted = false;
170
+ pendingEvents = [];
171
+ /**
172
+ * Create a new Messenger client
173
+ *
174
+ * @param cookies - Authentication cookies
175
+ * @param options - Client options
176
+ */
177
+ constructor(cookies, options = {}) {
178
+ super();
179
+ this.cookies = cookies;
180
+ this.options = {
181
+ // ! todo: detect platform automatically
182
+ platform: "facebook",
183
+ logLevel: "none",
184
+ enableE2EE: true,
185
+ autoReconnect: true,
186
+ e2eeMemoryOnly: true,
187
+ ...options
188
+ };
189
+ }
190
+ /**
191
+ * Get the current user info
192
+ */
193
+ get user() {
194
+ return this._user;
195
+ }
196
+ /**
197
+ * Get the current user's Facebook ID
198
+ */
199
+ get currentUserId() {
200
+ return this._user?.id ?? null;
201
+ }
202
+ /**
203
+ * Get initial sync data (threads and messages)
204
+ */
205
+ get initialData() {
206
+ return this._initialData;
207
+ }
208
+ /**
209
+ * Check if client is fully ready (socket ready + E2EE connected if enabled)
210
+ */
211
+ isFullyReady() {
212
+ if (!this._socketReady) return false;
213
+ if (this.options.enableE2EE && !this._e2eeConnected) return false;
214
+ return true;
215
+ }
216
+ /**
217
+ * Check if client is connected
218
+ */
219
+ get isConnected() {
220
+ if (!this.handle) return false;
221
+ try {
222
+ const status = native.isConnected(this.handle);
223
+ return status.connected;
224
+ } catch {
225
+ return false;
226
+ }
227
+ }
228
+ /**
229
+ * Check if E2EE is connected
230
+ */
231
+ get isE2EEConnected() {
232
+ if (!this.handle) return false;
233
+ try {
234
+ const status = native.isConnected(this.handle);
235
+ return status.e2eeConnected;
236
+ } catch {
237
+ return false;
238
+ }
239
+ }
240
+ /**
241
+ * Connect to Messenger
242
+ *
243
+ * @returns User info and initial data
244
+ */
245
+ async connect() {
246
+ const { handle } = native.newClient({
247
+ cookies: this.cookies,
248
+ platform: this.options.platform,
249
+ devicePath: this.options.devicePath,
250
+ deviceData: this.options.deviceData,
251
+ e2eeMemoryOnly: this.options.e2eeMemoryOnly,
252
+ logLevel: this.options.logLevel
253
+ });
254
+ this.handle = handle;
255
+ const result = native.connect(handle);
256
+ this._user = result.user;
257
+ this._initialData = result.initialData;
258
+ this.startEventLoop();
259
+ if (this.options.enableE2EE) {
260
+ this.connectE2EE().catch((err) => {
261
+ this.emit("error", err);
262
+ });
263
+ }
264
+ return {
265
+ user: this._user,
266
+ initialData: this._initialData
267
+ };
268
+ }
269
+ /**
270
+ * Connect E2EE (end-to-end encryption)
271
+ * @warn This Promise is not resolved after the connection setup is completed; instead, it is resolved after the function finishes executing.\
272
+ * You should not rely on this Promise to wait for the E2EE connection to be fully established.
273
+ */
274
+ async connectE2EE() {
275
+ if (!this.handle) throw new Error("Not connected");
276
+ await native.connectE2EE(this.handle);
277
+ }
278
+ /**
279
+ * Disconnect from Messenger
280
+ */
281
+ async disconnect() {
282
+ this.stopEventLoop();
283
+ if (this.handle) {
284
+ native.disconnect(this.handle);
285
+ this.handle = null;
286
+ }
287
+ }
288
+ /**
289
+ * Send a text message
290
+ *
291
+ * @param threadId - Thread ID to send to
292
+ * @param options - Message options (text, reply, mentions)
293
+ * @returns Send result with message ID
294
+ */
295
+ async sendMessage(threadId, options) {
296
+ if (!this.handle) throw new Error("Not connected");
297
+ const opts = typeof options === "string" ? { text: options } : options;
298
+ return native.sendMessage(this.handle, {
299
+ threadId,
300
+ text: opts.text,
301
+ replyToId: opts.replyToId,
302
+ attachmentFbIds: opts.attachmentFbIds,
303
+ mentionIds: opts.mentions?.map((m) => m.userId),
304
+ mentionOffsets: opts.mentions?.map((m) => m.offset),
305
+ mentionLengths: opts.mentions?.map((m) => m.length)
306
+ });
307
+ }
308
+ /**
309
+ * Send / Remove a reaction to a message
310
+ *
311
+ * @param threadId - Thread ID
312
+ * @param messageId - Message ID to react to
313
+ * @param emoji - Reaction emoji (to remove, simply omit this parameter)
314
+ */
315
+ async sendReaction(threadId, messageId, emoji) {
316
+ if (!this.handle) throw new Error("Not connected");
317
+ await native.sendReaction(this.handle, threadId, messageId, emoji || "");
318
+ }
319
+ /**
320
+ * Edit a message
321
+ *
322
+ * @param messageId - Message ID to edit
323
+ * @param newText - New text content
324
+ */
325
+ async editMessage(messageId, newText) {
326
+ if (!this.handle) throw new Error("Not connected");
327
+ await native.editMessage(this.handle, messageId, newText);
328
+ }
329
+ /**
330
+ * Unsend/delete a message
331
+ *
332
+ * @param messageId - Message ID to unsend
333
+ */
334
+ async unsendMessage(messageId) {
335
+ if (!this.handle) throw new Error("Not connected");
336
+ await native.unsendMessage(this.handle, messageId);
337
+ }
338
+ /**
339
+ * Send typing indicator
340
+ *
341
+ * @param threadId - Thread ID
342
+ * @param isTyping - Whether typing or not
343
+ * @param isGroup - Whether it's a group chat
344
+ */
345
+ async sendTypingIndicator(threadId, isTyping = true, isGroup = false) {
346
+ if (!this.handle) throw new Error("Not connected");
347
+ await native.sendTyping(this.handle, threadId, isTyping, isGroup, isGroup ? 2 : 1);
348
+ }
349
+ /**
350
+ * Mark messages as read
351
+ *
352
+ * @param threadId - Thread ID
353
+ * @param watermarkTs - Timestamp to mark read up to (optional)
354
+ */
355
+ async markAsRead(threadId, watermarkTs) {
356
+ if (!this.handle) throw new Error("Not connected");
357
+ await native.markRead(this.handle, threadId, watermarkTs);
358
+ }
359
+ /**
360
+ * Upload media to Messenger
361
+ *
362
+ * @param threadId - Thread ID
363
+ * @param data - File data as Buffer
364
+ * @param filename - Filename
365
+ * @param mimeType - MIME type
366
+ * @param isVoice - Whether it's a voice message
367
+ * @returns Upload result with Facebook ID
368
+ */
369
+ async uploadMedia(threadId, data, filename, mimeType, isVoice = false) {
370
+ if (!this.handle) throw new Error("Not connected");
371
+ return native.uploadMedia(this.handle, {
372
+ threadId,
373
+ filename,
374
+ mimeType,
375
+ data: Array.from(data),
376
+ isVoice
377
+ });
378
+ }
379
+ /**
380
+ * Send an image
381
+ *
382
+ * @param threadId - Thread ID
383
+ * @param data - Image data as Buffer
384
+ * @param filename - Filename
385
+ * @param options - Optional: caption and replyToId
386
+ */
387
+ async sendImage(threadId, data, filename, options) {
388
+ if (!this.handle) throw new Error("Not connected");
389
+ const opts = typeof options === "string" ? { caption: options } : options;
390
+ return native.sendImage(this.handle, {
391
+ threadId,
392
+ data: Array.from(data),
393
+ filename,
394
+ caption: opts?.caption,
395
+ replyToId: opts?.replyToId
396
+ });
397
+ }
398
+ /**
399
+ * Send a video
400
+ *
401
+ * @param threadId - Thread ID
402
+ * @param data - Video data as Buffer
403
+ * @param filename - Filename
404
+ * @param options - Optional: caption and replyToId
405
+ */
406
+ async sendVideo(threadId, data, filename, options) {
407
+ if (!this.handle) throw new Error("Not connected");
408
+ const opts = typeof options === "string" ? { caption: options } : options;
409
+ return native.sendVideo(this.handle, {
410
+ threadId,
411
+ data: Array.from(data),
412
+ filename,
413
+ caption: opts?.caption,
414
+ replyToId: opts?.replyToId
415
+ });
416
+ }
417
+ /**
418
+ * Send a voice message
419
+ *
420
+ * @param threadId - Thread ID
421
+ * @param data - Audio data as Buffer
422
+ * @param filename - Filename
423
+ * @param options - Optional: replyToId
424
+ */
425
+ async sendVoice(threadId, data, filename, options) {
426
+ if (!this.handle) throw new Error("Not connected");
427
+ return native.sendVoice(this.handle, {
428
+ threadId,
429
+ data: Array.from(data),
430
+ filename,
431
+ replyToId: options?.replyToId
432
+ });
433
+ }
434
+ /**
435
+ * Send a file
436
+ *
437
+ * @param threadId - Thread ID
438
+ * @param data - File data as Buffer
439
+ * @param filename - Filename
440
+ * @param mimeType - MIME type
441
+ * @param options - Optional: caption and replyToId
442
+ */
443
+ async sendFile(threadId, data, filename, mimeType, options) {
444
+ if (!this.handle) throw new Error("Not connected");
445
+ const opts = typeof options === "string" ? { caption: options } : options;
446
+ return native.sendFile(this.handle, {
447
+ threadId,
448
+ data: Array.from(data),
449
+ filename,
450
+ mimeType,
451
+ caption: opts?.caption,
452
+ replyToId: opts?.replyToId
453
+ });
454
+ }
455
+ /**
456
+ * Send a sticker
457
+ *
458
+ * @param threadId - Thread ID
459
+ * @param stickerId - Sticker ID
460
+ * @param options - Optional: replyToId
461
+ */
462
+ async sendSticker(threadId, stickerId, options) {
463
+ if (!this.handle) throw new Error("Not connected");
464
+ return native.sendSticker(this.handle, { threadId, stickerId, replyToId: options?.replyToId });
465
+ }
466
+ /**
467
+ * Create a 1:1 thread with a user
468
+ *
469
+ * @param userId - User ID to create thread with
470
+ * @returns Created thread info
471
+ */
472
+ async createThread(userId) {
473
+ if (!this.handle) throw new Error("Not connected");
474
+ return native.createThread(this.handle, { userId });
475
+ }
476
+ /**
477
+ * Get detailed information about a user
478
+ *
479
+ * @param userId - User ID
480
+ * @returns User info
481
+ */
482
+ async getUserInfo(userId) {
483
+ if (!this.handle) throw new Error("Not connected");
484
+ return native.getUserInfo(this.handle, { userId });
485
+ }
486
+ /**
487
+ * Set group photo/avatar
488
+ *
489
+ * @param threadId - Thread ID
490
+ * @param data - Image data as Buffer or base64 string
491
+ * @param mimeType - MIME type (e.g., 'image/jpeg', 'image/png')
492
+ *
493
+ * @warn Cannot remove group photo. Messenger web doesn't have a remove option?
494
+ */
495
+ async setGroupPhoto(threadId, data, mimeType = "image/jpeg") {
496
+ if (!this.handle) throw new Error("Not connected");
497
+ const base64 = Buffer.isBuffer(data) ? data.toString("base64") : data;
498
+ await native.setGroupPhoto(this.handle, threadId, base64, mimeType);
499
+ }
500
+ /**
501
+ * Rename a group thread
502
+ *
503
+ * @param threadId - Thread ID
504
+ * @param newName - New name
505
+ */
506
+ async renameThread(threadId, newName) {
507
+ if (!this.handle) throw new Error("Not connected");
508
+ native.renameThread(this.handle, { threadId, newName });
509
+ }
510
+ /**
511
+ * Mute a thread
512
+ *
513
+ * @param threadId - Thread ID
514
+ * @param muteSeconds - Duration in seconds (-1 for forever, 0 to unmute)
515
+ */
516
+ async muteThread(threadId, muteSeconds = -1) {
517
+ if (!this.handle) throw new Error("Not connected");
518
+ native.muteThread(this.handle, { threadId, muteSeconds });
519
+ }
520
+ /**
521
+ * Unmute a thread
522
+ *
523
+ * @param threadId - Thread ID
524
+ */
525
+ async unmuteThread(threadId) {
526
+ return this.muteThread(threadId, 0);
527
+ }
528
+ /**
529
+ * Delete a thread
530
+ *
531
+ * @param threadId - Thread ID
532
+ */
533
+ async deleteThread(threadId) {
534
+ if (!this.handle) throw new Error("Not connected");
535
+ native.deleteThread(this.handle, { threadId });
536
+ }
537
+ /**
538
+ * Search for users
539
+ *
540
+ * @param query - Search query
541
+ * @returns List of matching users
542
+ */
543
+ async searchUsers(query) {
544
+ if (!this.handle) throw new Error("Not connected");
545
+ const result = await native.searchUsers(this.handle, { query });
546
+ return result.users;
547
+ }
548
+ // ========== E2EE Methods ==========
549
+ /**
550
+ * Send an E2EE message
551
+ *
552
+ * @param chatJid - Chat JID
553
+ * @param text - Message text
554
+ * @param options - Optional: replyToId and replyToSenderJid for replies
555
+ */
556
+ async sendE2EEMessage(chatJid, text, options) {
557
+ if (!this.handle) throw new Error("Not connected");
558
+ return native.sendE2EEMessage(this.handle, chatJid, text, options?.replyToId, options?.replyToSenderJid);
559
+ }
560
+ /**
561
+ * Send / Remove an E2EE reaction
562
+ *
563
+ * @param chatJid - Chat JID
564
+ * @param messageId - Message ID
565
+ * @param senderJid - Sender JID
566
+ * @param emoji - Reaction emoji (To remove it, simply omit this parameter)
567
+ */
568
+ async sendE2EEReaction(chatJid, messageId, senderJid, emoji) {
569
+ if (!this.handle) throw new Error("Not connected");
570
+ await native.sendE2EEReaction(this.handle, chatJid, messageId, senderJid, emoji || "");
571
+ }
572
+ /**
573
+ * Send E2EE typing indicator
574
+ *
575
+ * @param chatJid - Chat JID
576
+ * @param isTyping - Whether typing
577
+ */
578
+ async sendE2EETyping(chatJid, isTyping = true) {
579
+ if (!this.handle) throw new Error("Not connected");
580
+ await native.sendE2EETyping(this.handle, chatJid, isTyping);
581
+ }
582
+ /**
583
+ * Edit an E2EE message
584
+ *
585
+ * @param chatJid - Chat JID
586
+ * @param messageId - Message ID to edit
587
+ * @param newText - New message text
588
+ */
589
+ async editE2EEMessage(chatJid, messageId, newText) {
590
+ if (!this.handle) throw new Error("Not connected");
591
+ await native.editE2EEMessage(this.handle, chatJid, messageId, newText);
592
+ }
593
+ /**
594
+ * Unsend/delete an E2EE message
595
+ *
596
+ * @param chatJid - Chat JID
597
+ * @param messageId - Message ID to unsend
598
+ */
599
+ async unsendE2EEMessage(chatJid, messageId) {
600
+ if (!this.handle) throw new Error("Not connected");
601
+ await native.unsendE2EEMessage(this.handle, chatJid, messageId);
602
+ }
603
+ // ========== E2EE Media Methods ==========
604
+ /**
605
+ * Send an E2EE image
606
+ *
607
+ * @param chatJid - Chat JID
608
+ * @param data - Image data as Buffer
609
+ * @param mimeType - MIME type (e.g., image/jpeg, image/png)
610
+ * @param options - Optional caption, dimensions, and reply options
611
+ */
612
+ async sendE2EEImage(chatJid, data, mimeType = "image/jpeg", options) {
613
+ if (!this.handle) throw new Error("Not connected");
614
+ return native.sendE2EEImage(this.handle, {
615
+ chatJid,
616
+ data: Array.from(data),
617
+ mimeType,
618
+ caption: options?.caption,
619
+ width: options?.width,
620
+ height: options?.height,
621
+ replyToId: options?.replyToId,
622
+ replyToSenderJid: options?.replyToSenderJid
623
+ });
624
+ }
625
+ /**
626
+ * Send an E2EE video
627
+ *
628
+ * @param chatJid - Chat JID
629
+ * @param data - Video data as Buffer
630
+ * @param mimeType - MIME type (default: video/mp4)
631
+ * @param options - Optional caption, dimensions, duration, and reply options
632
+ */
633
+ async sendE2EEVideo(chatJid, data, mimeType = "video/mp4", options) {
634
+ if (!this.handle) throw new Error("Not connected");
635
+ return native.sendE2EEVideo(this.handle, {
636
+ chatJid,
637
+ data: Array.from(data),
638
+ mimeType,
639
+ caption: options?.caption,
640
+ width: options?.width,
641
+ height: options?.height,
642
+ duration: options?.duration,
643
+ replyToId: options?.replyToId,
644
+ replyToSenderJid: options?.replyToSenderJid
645
+ });
646
+ }
647
+ /**
648
+ * Send an E2EE audio/voice message
649
+ *
650
+ * @param chatJid - Chat JID
651
+ * @param data - Audio data as Buffer
652
+ * @param mimeType - MIME type (default: audio/ogg)
653
+ * @param options - Optional PTT (push-to-talk/voice message), duration, and reply options
654
+ */
655
+ async sendE2EEAudio(chatJid, data, mimeType = "audio/ogg", options) {
656
+ if (!this.handle) throw new Error("Not connected");
657
+ return native.sendE2EEAudio(this.handle, {
658
+ chatJid,
659
+ data: Array.from(data),
660
+ mimeType,
661
+ ptt: options?.ptt ?? false,
662
+ duration: options?.duration,
663
+ replyToId: options?.replyToId,
664
+ replyToSenderJid: options?.replyToSenderJid
665
+ });
666
+ }
667
+ /**
668
+ * Send an E2EE document/file
669
+ *
670
+ * @param chatJid - Chat JID
671
+ * @param data - File data as Buffer
672
+ * @param filename - Filename
673
+ * @param mimeType - MIME type
674
+ * @param options - Optional reply options
675
+ */
676
+ async sendE2EEDocument(chatJid, data, filename, mimeType, options) {
677
+ if (!this.handle) throw new Error("Not connected");
678
+ return native.sendE2EEDocument(this.handle, {
679
+ chatJid,
680
+ data: Array.from(data),
681
+ filename,
682
+ mimeType,
683
+ replyToId: options?.replyToId,
684
+ replyToSenderJid: options?.replyToSenderJid
685
+ });
686
+ }
687
+ /**
688
+ * Send an E2EE sticker
689
+ *
690
+ * @param chatJid - Chat JID
691
+ * @param data - Sticker data as Buffer (WebP format)
692
+ * @param mimeType - MIME type (default: image/webp)
693
+ * @param options - Optional reply options
694
+ */
695
+ async sendE2EESticker(chatJid, data, mimeType = "image/webp", options) {
696
+ if (!this.handle) throw new Error("Not connected");
697
+ return native.sendE2EESticker(this.handle, {
698
+ chatJid,
699
+ data: Array.from(data),
700
+ mimeType,
701
+ replyToId: options?.replyToId,
702
+ replyToSenderJid: options?.replyToSenderJid
703
+ });
704
+ }
705
+ /**
706
+ * Get E2EE device data as JSON string
707
+ *
708
+ * Use this to persist device data externally (e.g., in a database)
709
+ *
710
+ * @returns Device data as JSON string
711
+ */
712
+ getDeviceData() {
713
+ if (!this.handle) throw new Error("Not connected");
714
+ const result = native.getDeviceData(this.handle);
715
+ return result.deviceData;
716
+ }
717
+ /**
718
+ * Get the current cookies from the internal client state
719
+ *
720
+ * Use this to export updated cookies after they've been refreshed
721
+ *
722
+ * @returns Current cookies as key-value object
723
+ */
724
+ getCookies() {
725
+ if (!this.handle) throw new Error("Not connected");
726
+ const result = native.getCookies(this.handle);
727
+ return result.cookies;
728
+ }
729
+ /**
730
+ * Register for web push notifications
731
+ *
732
+ * @param endpoint - Push notification endpoint URL
733
+ * @param keys - Push notification keys (p256dh and auth, base64 encoded)
734
+ */
735
+ async registerPushNotifications(endpoint, keys) {
736
+ if (!this.handle) throw new Error("Not connected");
737
+ await native.registerPushNotifications(this.handle, {
738
+ endpoint,
739
+ p256dh: keys.p256dh,
740
+ auth: keys.auth
741
+ });
742
+ }
743
+ /**
744
+ * Download and decrypt E2EE media
745
+ *
746
+ * Use the mediaKey, mediaSha256, and directPath from attachment metadata
747
+ * to download and decrypt encrypted media.
748
+ *
749
+ * @param options - Download options from attachment metadata
750
+ * @returns Decrypted media data as Buffer
751
+ *
752
+ * @example
753
+ * ```typescript
754
+ * const attachment = message.attachments[0];
755
+ * const result = await client.downloadE2EEMedia({
756
+ * directPath: attachment.directPath!,
757
+ * mediaKey: attachment.mediaKey!,
758
+ * mediaSha256: attachment.mediaSha256!,
759
+ * mediaEncSha256: attachment.mediaEncSha256,
760
+ * mediaType: attachment.type,
761
+ * mimeType: attachment.mimeType!,
762
+ * fileSize: attachment.fileSize!,
763
+ * });
764
+ * fs.writeFileSync('downloaded.jpg', result.data);
765
+ * ```
766
+ */
767
+ async downloadE2EEMedia(options) {
768
+ if (!this.handle) throw new Error("Not connected");
769
+ const result = await native.downloadE2EEMedia(this.handle, options);
770
+ return {
771
+ data: Buffer.from(result.data, "base64"),
772
+ mimeType: result.mimeType,
773
+ fileSize: result.fileSize
774
+ };
775
+ }
776
+ startEventLoop() {
777
+ if (this.eventLoopRunning) return;
778
+ this.eventLoopRunning = true;
779
+ this.eventLoopAbort = new AbortController();
780
+ const loop = /* @__PURE__ */ __name(async () => {
781
+ while (this.eventLoopRunning && this.handle) {
782
+ try {
783
+ await new Promise((resolve) => setImmediate(resolve));
784
+ const event = await native.pollEvents(this.handle, 1e3);
785
+ if (!event || event.type === "timeout") continue;
786
+ if (event.type === "closed") {
787
+ this.eventLoopRunning = false;
788
+ break;
789
+ }
790
+ this.handleEvent(event);
791
+ } catch (err) {
792
+ if (this.eventLoopRunning) {
793
+ this.emit("error", err);
794
+ }
795
+ }
796
+ }
797
+ }, "loop");
798
+ setImmediate(loop).unref();
799
+ }
800
+ stopEventLoop() {
801
+ this.eventLoopRunning = false;
802
+ this.eventLoopAbort?.abort();
803
+ this.eventLoopAbort = null;
804
+ }
805
+ checkFullyReady() {
806
+ if (this.isFullyReady() && !this._fullyReadyEmitted) {
807
+ this._fullyReadyEmitted = true;
808
+ this.emit("fullyReady");
809
+ const pending = this.pendingEvents;
810
+ this.pendingEvents = [];
811
+ for (const event of pending) {
812
+ this.emitEvent(event);
813
+ }
814
+ }
815
+ }
816
+ handleEvent(event) {
817
+ switch (event.type) {
818
+ // System events
819
+ case "ready":
820
+ this._socketReady = true;
821
+ this.emit("ready", event.data);
822
+ this.checkFullyReady();
823
+ break;
824
+ case "reconnected":
825
+ this.emit("reconnected");
826
+ break;
827
+ case "disconnected":
828
+ this._socketReady = false;
829
+ this._e2eeConnected = false;
830
+ this._fullyReadyEmitted = false;
831
+ this.pendingEvents = [];
832
+ this.emit("disconnected", event.data || {});
833
+ break;
834
+ case "error":
835
+ this.emit("error", new Error(event.data.message));
836
+ if (event.data.code === 1) {
837
+ this.stopEventLoop();
838
+ this._socketReady = false;
839
+ this._e2eeConnected = false;
840
+ this._fullyReadyEmitted = false;
841
+ this.pendingEvents = [];
842
+ }
843
+ break;
844
+ case "e2eeConnected":
845
+ this._e2eeConnected = true;
846
+ this.emit("e2eeConnected");
847
+ this.checkFullyReady();
848
+ break;
849
+ case "deviceDataChanged":
850
+ this.emit("deviceDataChanged", event.data);
851
+ break;
852
+ case "raw":
853
+ this.emit("raw", event.data);
854
+ break;
855
+ // queue until fullyReady
856
+ case "message":
857
+ case "messageEdit":
858
+ case "messageUnsend":
859
+ case "reaction":
860
+ case "typing":
861
+ case "readReceipt":
862
+ case "e2eeMessage":
863
+ case "e2eeReaction":
864
+ case "e2eeReceipt":
865
+ if (this._fullyReadyEmitted) {
866
+ this.emitEvent(event);
867
+ } else {
868
+ this.pendingEvents.push(event);
869
+ }
870
+ break;
871
+ }
872
+ }
873
+ emitEvent(event) {
874
+ switch (event.type) {
875
+ case "message":
876
+ this.emit("message", event.data);
877
+ break;
878
+ case "messageEdit":
879
+ this.emit("messageEdit", event.data);
880
+ break;
881
+ case "messageUnsend":
882
+ this.emit("messageUnsend", event.data);
883
+ break;
884
+ case "reaction":
885
+ this.emit("reaction", event.data);
886
+ break;
887
+ case "typing":
888
+ this.emit("typing", event.data);
889
+ break;
890
+ case "readReceipt":
891
+ this.emit("readReceipt", event.data);
892
+ break;
893
+ case "e2eeMessage":
894
+ this.emit("e2eeMessage", event.data);
895
+ break;
896
+ case "e2eeReaction":
897
+ this.emit("e2eeReaction", event.data);
898
+ break;
899
+ case "e2eeReceipt":
900
+ this.emit("e2eeReceipt", event.data);
901
+ break;
902
+ }
903
+ }
904
+ /**
905
+ * Unload the native library (for cleanup)
906
+ * @warn Any attempt to find or call a function from this library after unloading it will crash.
907
+ * @returns void
908
+ */
909
+ unloadLibrary() {
910
+ if (this.handle) {
911
+ native.unload();
912
+ }
913
+ }
914
+ };
915
+
916
+ // src/login.ts
917
+ import { randomUUID } from "crypto";
918
+ import { fetch } from "undici";
919
+ var UIDLogin = class extends null {
920
+ static {
921
+ __name(this, "UIDLogin");
922
+ }
923
+ static API_ENDPOINT = "https://b-graph.facebook.com/graphql";
924
+ static USER_AGENT = "[FBAN/FB4A;FBAV/498.1.0.64.74;FBBV/692621185;FBDM/{density=1.5,width=540,height=960};FBLC/vi_VN;FBRV/0;FBCR/MobiFone;FBMF/Xiaomi;FBBD/Xiaomi;FBPN/com.facebook.katana;FBDV/2211133C;FBSV/9;FBOP/1;FBCA/x86_64:arm64-v8a;]";
925
+ static AUTH_TOKEN = "OAuth 350685531728|62f8ce9f74b12f84c123cc23437a4a32";
926
+ static CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
927
+ static randomString(len) {
928
+ let result = "";
929
+ for (let i = 0; i < len; i++) {
930
+ result += this.CHARS[Math.floor(Math.random() * this.CHARS.length)];
931
+ }
932
+ return result;
933
+ }
934
+ static generateNonce(size) {
935
+ const bytes = new Uint8Array(size);
936
+ for (let i = 0; i < size; i++) bytes[i] = Math.floor(Math.random() * 256);
937
+ return Buffer.from(bytes).toString("base64");
938
+ }
939
+ static toBase64(text) {
940
+ return Buffer.from(text).toString("base64");
941
+ }
942
+ static formatPassword(pwd) {
943
+ return `#PWD_FB4A:0:${Math.floor(Date.now() / 1e3)}:${pwd}`;
944
+ }
945
+ static hashData(uid) {
946
+ return this.toBase64(JSON.stringify({ challenge_nonce: this.generateNonce(32), username: uid }));
947
+ }
948
+ static generateVariable(account) {
949
+ const deviceId = randomUUID();
950
+ const variable = {
951
+ params: {
952
+ params: JSON.stringify({
953
+ client_input_params: {
954
+ sim_phones: [],
955
+ secure_family_device_id: randomUUID(),
956
+ /*
957
+ attestation_result: {
958
+ data: this.hashData(account.uid),
959
+ signature:
960
+ "MEYCIQDtz5TqO0pwysy82Ko92FErORasLag9o/pQYlZl8+zaMgIhAKon529upFiPfGgoS6OkPKg0/VahBuSDxwiTgtzpYQA3",
961
+ keyHash: "92398b3e4d9ee926bae93a61fd75e18d750100c1e73fd44d4faa7b9ba9353eee",
962
+ },
963
+ */
964
+ has_granted_read_contacts_permissions: 0,
965
+ auth_secure_device_id: "",
966
+ has_whatsapp_installed: 0,
967
+ password: this.formatPassword(account.password),
968
+ sso_token_map_json_string: "",
969
+ event_flow: "login_manual",
970
+ password_contains_non_ascii: "false",
971
+ sim_serials: [],
972
+ client_known_key_hash: "",
973
+ encrypted_msisdn: "",
974
+ has_granted_read_phone_permissions: 0,
975
+ app_manager_id: "null",
976
+ should_show_nested_nta_from_aymh: 0,
977
+ device_id: deviceId,
978
+ login_attempt_count: 1,
979
+ machine_id: this.randomString(22),
980
+ flash_call_permission_status: {
981
+ READ_PHONE_STATE: "DENIED",
982
+ READ_CALL_LOG: "DENIED",
983
+ ANSWER_PHONE_CALLS: "DENIED"
984
+ },
985
+ accounts_list: [],
986
+ family_device_id: deviceId,
987
+ fb_ig_device_id: [],
988
+ device_emails: [],
989
+ try_num: 2,
990
+ lois_settings: { lois_token: "" },
991
+ event_step: "home_page",
992
+ headers_infra_flow_id: "",
993
+ openid_tokens: {},
994
+ contact_point: account.uid
995
+ },
996
+ server_params: {
997
+ should_trigger_override_login_2fa_action: 0,
998
+ is_from_logged_out: 0,
999
+ should_trigger_override_login_success_action: 0,
1000
+ login_credential_type: "none",
1001
+ server_login_source: "login",
1002
+ waterfall_id: randomUUID(),
1003
+ login_source: "Login",
1004
+ is_platform_login: 0,
1005
+ pw_encryption_try_count: 1,
1006
+ INTERNAL__latency_qpl_marker_id: 36707139,
1007
+ offline_experiment_group: "caa_iteration_v6_perf_fb_2",
1008
+ is_from_landing_page: 0,
1009
+ password_text_input_id: "jirv90:99",
1010
+ is_from_empty_password: 0,
1011
+ is_from_msplit_fallback: 0,
1012
+ ar_event_source: "login_home_page",
1013
+ username_text_input_id: "jirv90:98",
1014
+ layered_homepage_experiment_group: null,
1015
+ device_id: deviceId,
1016
+ INTERNAL__latency_qpl_instance_id: 118039064400779,
1017
+ reg_flow_source: "login_home_native_integration_point",
1018
+ is_caa_perf_enabled: 1,
1019
+ credential_type: "password",
1020
+ is_from_password_entry_page: 0,
1021
+ caller: "gslr",
1022
+ family_device_id: deviceId,
1023
+ is_from_assistive_id: 0,
1024
+ access_flow_version: "F2_FLOW",
1025
+ is_from_logged_in_switcher: 0
1026
+ }
1027
+ }),
1028
+ // const
1029
+ bloks_versioning_id: "cb6ac324faea83da28649a4d5046c3a4f0486cb987f8ab769765e316b075a76c",
1030
+ app_id: "com.bloks.www.bloks.caa.login.async.send_login_request"
1031
+ },
1032
+ scale: "1.5",
1033
+ nt_context: {
1034
+ using_white_navbar: true,
1035
+ // const
1036
+ styles_id: "55d2af294359fa6bbdb8e045ff01fc5e",
1037
+ pixel_ratio: 1.5,
1038
+ is_push_on: true,
1039
+ debug_tooling_metadata_token: null,
1040
+ is_flipper_enabled: false,
1041
+ theme_params: [],
1042
+ // can be dynamic
1043
+ bloks_version: "cb6ac324faea83da28649a4d5046c3a4f0486cb987f8ab769765e316b075a76c"
1044
+ }
1045
+ };
1046
+ return JSON.stringify(variable);
1047
+ }
1048
+ static extractCookieToken(data) {
1049
+ const tokenMatch = data.match(/"access_token":"([^"]+)"/);
1050
+ const cookiesMatch = data.match(/"session_cookies":\s*\[([^\]]+)\]/);
1051
+ const cookies = [];
1052
+ if (cookiesMatch) {
1053
+ const pattern = /"name":"([^"]+)","value":"([^"]+)"/g;
1054
+ let m;
1055
+ while (m = pattern.exec(cookiesMatch[1])) cookies.push(`${m[1]}=${m[2]}`);
1056
+ }
1057
+ return {
1058
+ token: tokenMatch?.[1] ?? "Access token not found",
1059
+ cookie: cookies.join("; ")
1060
+ };
1061
+ }
1062
+ /**
1063
+ * Performs Facebook login via Katana API
1064
+ * @param account - Account with UID and password
1065
+ * @returns Cookie and token upon successful authentication
1066
+ * @warn Accounts with 2FA are not supported, and the function will return an error
1067
+ * @deprecated This login method is unstable and may be blocked by Facebook at any time.
1068
+ */
1069
+ static async login(account) {
1070
+ const headers = {
1071
+ "User-Agent": this.USER_AGENT,
1072
+ Authorization: this.AUTH_TOKEN,
1073
+ "Content-Type": "application/x-www-form-urlencoded",
1074
+ "x-fb-sim-hni": "45201",
1075
+ "x-fb-net-hni": "45201",
1076
+ "x-fb-device-group": "2789",
1077
+ "x-fb-connection-type": "WIFI",
1078
+ "x-fb-http-engine": "Tigon/Liger",
1079
+ "x-fb-client-ip": "True",
1080
+ "x-fb-server-cluster": "True",
1081
+ "x-graphql-client-library": "graphservice",
1082
+ "x-graphql-request-purpose": "fetch",
1083
+ "x-fb-friendly-name": "FbBloksActionRootQuery-com.bloks.www.bloks.caa.login.async.send_login_request",
1084
+ "x-tigon-is-retry": "False",
1085
+ "x-zero-eh": "error",
1086
+ "Accept-Encoding": "identity"
1087
+ };
1088
+ const body = new URLSearchParams({
1089
+ method: "post",
1090
+ pretty: "false",
1091
+ format: "json",
1092
+ server_timestamps: "true",
1093
+ locale: "vi_VN",
1094
+ purpose: "fetch",
1095
+ fb_api_req_friendly_name: "FbBloksActionRootQuery-com.bloks.www.bloks.caa.login.async.send_login_request",
1096
+ fb_api_caller_class: "graphservice",
1097
+ client_doc_id: "11994080423986492941384902285",
1098
+ variables: this.generateVariable(account),
1099
+ fb_api_analytics_tags: '["GraphServices"]',
1100
+ client_trace_id: randomUUID()
1101
+ });
1102
+ const res = await fetch(this.API_ENDPOINT, { method: "POST", headers, body: body.toString() });
1103
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1104
+ const result = this.extractCookieToken((await res.text()).replace(/\\/g, ""));
1105
+ return result;
1106
+ }
1107
+ };
1108
+
1109
+ // src/types.ts
1110
+ var ThreadType = /* @__PURE__ */ ((ThreadType2) => {
1111
+ ThreadType2[ThreadType2["ONE_TO_ONE"] = 1] = "ONE_TO_ONE";
1112
+ ThreadType2[ThreadType2["GROUP"] = 2] = "GROUP";
1113
+ ThreadType2[ThreadType2["PAGE"] = 3] = "PAGE";
1114
+ ThreadType2[ThreadType2["MARKETPLACE"] = 4] = "MARKETPLACE";
1115
+ ThreadType2[ThreadType2["ENCRYPTED_ONE_TO_ONE"] = 7] = "ENCRYPTED_ONE_TO_ONE";
1116
+ ThreadType2[ThreadType2["ENCRYPTED_GROUP"] = 8] = "ENCRYPTED_GROUP";
1117
+ return ThreadType2;
1118
+ })(ThreadType || {});
1119
+
1120
+ // src/utils.ts
1121
+ var Utils = class _Utils extends null {
1122
+ static {
1123
+ __name(this, "Utils");
1124
+ }
1125
+ /**
1126
+ * Parse cookies from various formats
1127
+ * Automatically detects the format and parses accordingly
1128
+ *
1129
+ * @param input - Cookie data in any supported format
1130
+ * @returns Parsed cookies object
1131
+ */
1132
+ static parseCookies(input) {
1133
+ if (typeof input === "object" && !Array.isArray(input)) {
1134
+ return input;
1135
+ }
1136
+ if (Array.isArray(input)) {
1137
+ return _Utils.fromCookieArray(input);
1138
+ }
1139
+ if (typeof input === "string") {
1140
+ const trimmed = input.trim();
1141
+ if (_Utils.isBase64(trimmed)) {
1142
+ try {
1143
+ const decoded = Buffer.from(trimmed, "base64").toString("utf-8");
1144
+ return _Utils.parseCookies(decoded);
1145
+ } catch {
1146
+ }
1147
+ }
1148
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
1149
+ try {
1150
+ const parsed = JSON.parse(trimmed);
1151
+ return _Utils.parseCookies(parsed);
1152
+ } catch {
1153
+ }
1154
+ }
1155
+ if (trimmed.includes(" ") && (trimmed.startsWith("#") || trimmed.includes(".facebook.com") || trimmed.includes(".messenger.com"))) {
1156
+ return _Utils.fromNetscape(trimmed);
1157
+ }
1158
+ if (trimmed.includes("=")) {
1159
+ return _Utils.fromCookieString(trimmed);
1160
+ }
1161
+ }
1162
+ throw new Error("Unable to parse cookies: unknown format");
1163
+ }
1164
+ /**
1165
+ * Parse cookies from C3C UFC Utility / EditThisCookie array format
1166
+ *
1167
+ * @param cookies - Array of cookie objects
1168
+ * @returns Parsed cookies object
1169
+ *
1170
+ * @example
1171
+ * ```typescript
1172
+ * const cookies = Utils.fromCookieArray([
1173
+ * { name: 'c_user', value: '123456' },
1174
+ * { name: 'xs', value: 'abc...' }
1175
+ * ])
1176
+ * ```
1177
+ */
1178
+ static fromCookieArray(cookies) {
1179
+ const result = {};
1180
+ for (const cookie of cookies) {
1181
+ if (cookie.name && cookie.value !== void 0) {
1182
+ result[cookie.name] = String(cookie.value);
1183
+ }
1184
+ }
1185
+ return result;
1186
+ }
1187
+ /**
1188
+ * Parse cookies from cookie header string format
1189
+ *
1190
+ * @param cookieString - Cookie string (e.g., "name1=value1; name2=value2")
1191
+ * @returns Parsed cookies object
1192
+ *
1193
+ * @example
1194
+ * ```typescript
1195
+ * const cookies = Utils.fromCookieString('c_user=123456; xs=abc...; datr=xyz...')
1196
+ * ```
1197
+ */
1198
+ static fromCookieString(cookieString) {
1199
+ const result = {};
1200
+ const pairs = cookieString.split(/;\s*/);
1201
+ for (const pair of pairs) {
1202
+ const [name, ...valueParts] = pair.split("=");
1203
+ if (name && valueParts.length > 0) {
1204
+ const trimmedName = name.trim();
1205
+ const value = valueParts.join("=").trim();
1206
+ if (trimmedName && value) {
1207
+ result[trimmedName] = value;
1208
+ }
1209
+ }
1210
+ }
1211
+ return result;
1212
+ }
1213
+ /**
1214
+ * Parse cookies from Netscape/HTTP cookie file format
1215
+ *
1216
+ * @param content - Netscape cookie file content
1217
+ * @returns Parsed cookies object
1218
+ *
1219
+ * @example
1220
+ * ```typescript
1221
+ * const cookies = Utils.fromNetscape(`
1222
+ * # Netscape HTTP Cookie File
1223
+ * .facebook.com TRUE / TRUE 1234567890 c_user 123456
1224
+ * .facebook.com TRUE / TRUE 1234567890 xs abc...
1225
+ * `)
1226
+ * ```
1227
+ */
1228
+ static fromNetscape(content) {
1229
+ const result = {};
1230
+ const lines = content.split("\n");
1231
+ for (const line of lines) {
1232
+ const trimmed = line.trim();
1233
+ if (!trimmed || trimmed.startsWith("#")) {
1234
+ continue;
1235
+ }
1236
+ const parts = trimmed.split(" ");
1237
+ if (parts.length >= 7) {
1238
+ const name = parts[5];
1239
+ const value = parts[6];
1240
+ if (name && value) {
1241
+ result[name] = value;
1242
+ }
1243
+ }
1244
+ }
1245
+ return result;
1246
+ }
1247
+ /**
1248
+ * Parse cookies from Base64 encoded string
1249
+ *
1250
+ * @param base64 - Base64 encoded cookie data
1251
+ * @returns Parsed cookies object
1252
+ */
1253
+ static fromBase64(base64) {
1254
+ const decoded = Buffer.from(base64, "base64").toString("utf-8");
1255
+ return _Utils.parseCookies(decoded);
1256
+ }
1257
+ /**
1258
+ * Convert cookies object to cookie header string
1259
+ *
1260
+ * @param cookies - Cookies object
1261
+ * @returns Cookie header string
1262
+ *
1263
+ * @example
1264
+ * ```typescript
1265
+ * const header = Utils.toCookieString({ c_user: '123456', xs: 'abc...' })
1266
+ * // Returns: "c_user=123456; xs=abc..."
1267
+ * ```
1268
+ */
1269
+ static toCookieString(cookies) {
1270
+ return Object.entries(cookies).map(([name, value]) => `${name}=${value}`).join("; ");
1271
+ }
1272
+ /**
1273
+ * Convert cookies object to array format (C3C UFC Utility style)
1274
+ *
1275
+ * @param cookies - Cookies object
1276
+ * @param domain - Cookie domain (default: .facebook.com)
1277
+ * @returns Array of cookie objects
1278
+ *
1279
+ * @example
1280
+ * ```typescript
1281
+ * const arr = Utils.toCookieArray({ c_user: '123456', xs: 'abc...' })
1282
+ * ```
1283
+ */
1284
+ static toCookieArray(cookies, domain = ".facebook.com") {
1285
+ return Object.entries(cookies).filter(([, value]) => value !== void 0).map(([name, value]) => ({
1286
+ name,
1287
+ value,
1288
+ domain,
1289
+ path: "/",
1290
+ secure: true,
1291
+ httpOnly: true
1292
+ }));
1293
+ }
1294
+ /**
1295
+ * Convert cookies object to Netscape format
1296
+ *
1297
+ * @param cookies - Cookies object
1298
+ * @param domain - Cookie domain (default: .facebook.com)
1299
+ * @returns Netscape cookie file content
1300
+ */
1301
+ static toNetscape(cookies, domain = ".facebook.com") {
1302
+ const lines = ["# Netscape HTTP Cookie File", "# Generated by meta-messenger.js", ""];
1303
+ for (const [name, value] of Object.entries(cookies)) {
1304
+ const expiration = Math.floor(Date.now() / 1e3) + 365 * 24 * 60 * 60;
1305
+ lines.push(`${domain} TRUE / TRUE ${expiration} ${name} ${value}`);
1306
+ }
1307
+ return lines.join("\n");
1308
+ }
1309
+ /**
1310
+ * Convert cookies to Base64 encoded JSON
1311
+ *
1312
+ * @param cookies - Cookies object
1313
+ * @returns Base64 encoded string
1314
+ */
1315
+ static toBase64(cookies) {
1316
+ return Buffer.from(JSON.stringify(cookies)).toString("base64");
1317
+ }
1318
+ /**
1319
+ * Filter cookies to only essential ones for Facebook/Messenger
1320
+ *
1321
+ * @param cookies - Cookies object
1322
+ * @returns Filtered cookies with only essential keys
1323
+ */
1324
+ static filterEssential(cookies) {
1325
+ const essential = ["c_user", "xs", "datr", "fr", "sb", "wd", "presence"];
1326
+ const result = {};
1327
+ for (const key of essential) {
1328
+ if (cookies[key]) {
1329
+ result[key] = cookies[key];
1330
+ }
1331
+ }
1332
+ return result;
1333
+ }
1334
+ /**
1335
+ * Validate that cookies contain required fields
1336
+ *
1337
+ * @param cookies - Cookies object
1338
+ * @returns True if cookies are valid
1339
+ */
1340
+ static validate(cookies) {
1341
+ const required = ["c_user", "xs"];
1342
+ return required.every((key) => cookies[key] && cookies[key].length > 0);
1343
+ }
1344
+ /**
1345
+ * Get missing required cookies
1346
+ *
1347
+ * @param cookies - Cookies object
1348
+ * @returns Array of missing cookie names
1349
+ */
1350
+ static getMissing(cookies) {
1351
+ const required = ["c_user", "xs"];
1352
+ return required.filter((key) => !cookies[key] || cookies[key].length === 0);
1353
+ }
1354
+ /**
1355
+ * Check if a string is valid Base64
1356
+ */
1357
+ static isBase64(str) {
1358
+ if (str.length < 4) return false;
1359
+ const base64Regex = /^[A-Za-z0-9+/]+=*$/;
1360
+ if (!base64Regex.test(str)) return false;
1361
+ return str.length > 20 && str.length % 4 === 0;
1362
+ }
1363
+ };
1364
+ var THUMBS_UP_STICKER_IDS = {
1365
+ SMALL: 369239263222822,
1366
+ MEDIUM: 369239343222814,
1367
+ LARGE: 369239383222810
1368
+ };
1369
+ function isThumbsUpSticker(stickerId) {
1370
+ if (!stickerId) return false;
1371
+ return stickerId === THUMBS_UP_STICKER_IDS.SMALL || stickerId === THUMBS_UP_STICKER_IDS.MEDIUM || stickerId === THUMBS_UP_STICKER_IDS.LARGE;
1372
+ }
1373
+ __name(isThumbsUpSticker, "isThumbsUpSticker");
1374
+ function extractUrlFromLPHP(url) {
1375
+ if (!url) return url;
1376
+ try {
1377
+ const parsed = new URL(url);
1378
+ if (parsed.pathname === "/l.php" || parsed.pathname.endsWith("/l.php")) {
1379
+ const actualUrl = parsed.searchParams.get("u");
1380
+ if (actualUrl) return actualUrl;
1381
+ }
1382
+ } catch {
1383
+ }
1384
+ return url;
1385
+ }
1386
+ __name(extractUrlFromLPHP, "extractUrlFromLPHP");
1387
+
1388
+ // src/index.ts
1389
+ async function login(cookies, options) {
1390
+ const client = new Client(cookies, options);
1391
+ await client.connect();
1392
+ return client;
1393
+ }
1394
+ __name(login, "login");
1395
+ function createClient(cookies, options) {
1396
+ return new Client(cookies, options);
1397
+ }
1398
+ __name(createClient, "createClient");
1399
+ var index_default = { Client, login, createClient };
1400
+ export {
1401
+ Client,
1402
+ THUMBS_UP_STICKER_IDS,
1403
+ ThreadType,
1404
+ UIDLogin,
1405
+ Utils,
1406
+ createClient,
1407
+ index_default as default,
1408
+ extractUrlFromLPHP,
1409
+ isThumbsUpSticker,
1410
+ login
1411
+ };
1412
+ //# sourceMappingURL=index.js.map