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/e2ee.js ADDED
@@ -0,0 +1,563 @@
1
+ "use strict";
2
+ /* ═══════════════════════════════════════════════════════════════════════════
3
+ * fca/e2ee.js – Merged E2EE core for SHADOWX-FCA
4
+ * Combines: thread · mediaServer · bridge · patchApi
5
+ * Native binary : fca/messagix.so (loaded via fca/lib/index.mjs / koffi)
6
+ * ═══════════════════════════════════════════════════════════════════════════
7
+ */
8
+
9
+ var log = require("npmlog");
10
+ var path = require("path");
11
+ var urlMod = require("url");
12
+ var http = require("http");
13
+ var crypto = require("crypto");
14
+ var stream = require("stream");
15
+
16
+ // ─────────────────────────────────────────────────────────────────────────────
17
+ // § 1 THREAD – detect E2EE JIDs (any string containing "@")
18
+ // ─────────────────────────────────────────────────────────────────────────────
19
+ function isE2EEChatJid(value) {
20
+ return typeof value === "string" && value.indexOf("@") !== -1;
21
+ }
22
+
23
+ // ─────────────────────────────────────────────────────────────────────────────
24
+ // § 2 MEDIA SERVER – local HTTP cache for decrypted E2EE attachments
25
+ // ─────────────────────────────────────────────────────────────────────────────
26
+ var _mediaCache = new Map();
27
+ var _mediaServer = null;
28
+ var _mediaPort = null;
29
+
30
+ function _cleanExpired() {
31
+ var now = Date.now();
32
+ _mediaCache.forEach(function (entry, id) {
33
+ if (entry.expiry < now) _mediaCache.delete(id);
34
+ });
35
+ }
36
+
37
+ function _startMediaServer() {
38
+ if (_mediaServer && _mediaPort) return Promise.resolve(_mediaPort);
39
+ return new Promise(function (resolve, reject) {
40
+ var s = http.createServer(function (req, res) {
41
+ var id = req.url.replace(/^\/e2ee\//, "").split("?")[0];
42
+ var entry = _mediaCache.get(id);
43
+ if (!entry) { res.writeHead(404); return res.end("Not found"); }
44
+ res.writeHead(200, {
45
+ "Content-Type" : entry.mimeType || "application/octet-stream",
46
+ "Content-Length": entry.buffer.length,
47
+ "Cache-Control" : "no-cache"
48
+ });
49
+ res.end(entry.buffer);
50
+ });
51
+ s.listen(0, "127.0.0.1", function () {
52
+ _mediaPort = s.address().port;
53
+ _mediaServer = s;
54
+ resolve(_mediaPort);
55
+ });
56
+ s.on("error", reject);
57
+ });
58
+ }
59
+
60
+ async function storeMedia(buffer, mimeType) {
61
+ var port = await _startMediaServer();
62
+ _cleanExpired();
63
+ var id = crypto.randomBytes(10).toString("hex");
64
+ _mediaCache.set(id, {
65
+ buffer : buffer,
66
+ mimeType: mimeType || "application/octet-stream",
67
+ expiry : Date.now() + 10 * 60 * 1000
68
+ });
69
+ return "http://127.0.0.1:" + port + "/e2ee/" + id;
70
+ }
71
+
72
+ // ─────────────────────────────────────────────────────────────────────────────
73
+ // § 3 BRIDGE – manages the native Labyrinth E2EE client lifecycle
74
+ // ─────────────────────────────────────────────────────────────────────────────
75
+ var _dynamicImport = null;
76
+ function _getDynamicImport() {
77
+ if (!_dynamicImport)
78
+ _dynamicImport = new Function("specifier", "return import(specifier);");
79
+ return _dynamicImport;
80
+ }
81
+
82
+ // ESM bundle + native binary are relative to fca/ (this file's directory)
83
+ var _E2EE_LIB_URL = urlMod.pathToFileURL(
84
+ path.join(__dirname, "lib", "index.mjs")
85
+ ).href;
86
+
87
+ // Polyfill File / Blob for Node < 20 before the ESM bundle initialises
88
+ (function _polyfillFileGlobal() {
89
+ try {
90
+ if (typeof globalThis.File === "undefined") {
91
+ var b = require("buffer");
92
+ if (b && typeof b.File === "function") globalThis.File = b.File;
93
+ }
94
+ if (typeof globalThis.Blob === "undefined") {
95
+ var b2 = require("buffer");
96
+ if (b2 && typeof b2.Blob === "function") globalThis.Blob = b2.Blob;
97
+ }
98
+ } catch (_) {}
99
+ })();
100
+
101
+ // ── bridge internal helpers ───────────────────────────────────────────────────
102
+ function _isPromiseLike(v) { return v && typeof v.then === "function"; }
103
+
104
+ function _callUserCallback(cb, err, msg) {
105
+ if (typeof cb !== "function") return;
106
+ try {
107
+ var r = cb(err, msg);
108
+ if (_isPromiseLike(r)) r.catch(function (e) { log.error("e2ee", e); });
109
+ } catch (e) { log.error("e2ee", e); }
110
+ }
111
+
112
+ function _parseMentions(arr, text) {
113
+ var out = {};
114
+ if (!Array.isArray(arr) || !text) return out;
115
+ arr.forEach(function (m) {
116
+ if (!m || m.userId == null) return;
117
+ var o = Number(m.offset || 0), l = Number(m.length || 0);
118
+ out[String(m.userId)] = text.substring(o, o + l);
119
+ });
120
+ return out;
121
+ }
122
+
123
+ function _normalizeAttType(t) {
124
+ if (!t) return t;
125
+ t = String(t).toLowerCase();
126
+ if (t === "image") return "photo";
127
+ if (t === "document") return "file";
128
+ if (t === "voice" || t === "ptt") return "audio";
129
+ return t;
130
+ }
131
+
132
+ function _normalizeAtt(a) {
133
+ if (!a || typeof a !== "object") return a;
134
+ return {
135
+ type: _normalizeAttType(a.type),
136
+ ID: a.stickerId != null ? String(a.stickerId) : undefined,
137
+ url: a.url, filename: a.fileName, mimeType: a.mimeType,
138
+ fileSize: a.fileSize != null ? String(a.fileSize) : undefined,
139
+ width: a.width, height: a.height, duration: a.duration,
140
+ previewUrl: a.previewUrl, description: a.description, source: a.sourceText,
141
+ mediaKey: a.mediaKey, mediaSha256: a.mediaSha256, mediaEncSha256: a.mediaEncSha256,
142
+ directPath: a.directPath, latitude: a.latitude, longitude: a.longitude, isE2EE: true
143
+ };
144
+ }
145
+
146
+ // Extract the numeric prefix from an E2EE JID like "61568577897207:69@msgr" → "61568577897207"
147
+ function _numericId(jid) {
148
+ if (!jid) return "";
149
+ var s = String(jid);
150
+ var m = s.match(/^(\d+)/);
151
+ return m ? m[1] : s;
152
+ }
153
+
154
+ function _mapMsg(ev) {
155
+ var text = ev && ev.text ? String(ev.text) : "";
156
+ var sid = ev && ev.senderId != null ? _numericId(String(ev.senderId)) : "";
157
+ var tid = ev && ev.chatJid ? String(ev.chatJid)
158
+ : (ev && ev.threadId != null ? String(ev.threadId) : "");
159
+ var messageReply = null;
160
+ if (ev && ev.replyTo) {
161
+ // Native bridge returns replyTo.messageId (not .id); support both for safety
162
+ var _rtId = ev.replyTo.messageId != null ? ev.replyTo.messageId
163
+ : ev.replyTo.id != null ? ev.replyTo.id : undefined;
164
+ // senderId can arrive as an empty protobuf object {} — treat that as unknown
165
+ var _rtSender = (ev.replyTo.senderId != null &&
166
+ typeof ev.replyTo.senderId !== 'object')
167
+ ? _numericId(String(ev.replyTo.senderId)) : "";
168
+ messageReply = {
169
+ messageID: _rtId != null ? String(_rtId) : undefined,
170
+ senderID: _rtSender,
171
+ body: ev.replyTo.text != null ? String(ev.replyTo.text) : "",
172
+ isE2EE: true
173
+ };
174
+ }
175
+ return {
176
+ type: "e2ee_message", senderID: sid, body: text, threadID: tid,
177
+ messageID: ev.id != null ? String(ev.id) : ev.id,
178
+ messageReply: messageReply,
179
+ attachments: Array.isArray(ev.attachments) ? ev.attachments.map(_normalizeAtt) : [],
180
+ mentions: _parseMentions(ev.mentions, text),
181
+ timestamp: ev.timestampMs != null ? Number(ev.timestampMs) : Date.now(),
182
+ isGroup: /@group\.facebook\.com$/i.test(ev.chatJid || ""),
183
+ isE2EE: true,
184
+ e2ee: { chatJid: ev.chatJid, senderJid: ev.senderJid, replyTo: ev.replyTo || null, rawMentions: ev.mentions || [] },
185
+ args: text.trim() ? text.trim().split(/\s+/) : []
186
+ };
187
+ }
188
+
189
+ function _mapEdit(ev) {
190
+ var text = ev && ev.text ? String(ev.text) : "";
191
+ return {
192
+ type: "e2ee_message_edit", senderID: ev && ev.senderId != null ? String(ev.senderId) : "",
193
+ body: text, threadID: ev && ev.chatJid ? String(ev.chatJid) : "",
194
+ messageID: ev ? ev.messageId : undefined,
195
+ timestamp: ev && ev.timestampMs != null ? Number(ev.timestampMs) : Date.now(),
196
+ isGroup: /@group\.facebook\.com$/i.test(ev && ev.chatJid ? ev.chatJid : ""),
197
+ isE2EE: true,
198
+ e2ee: { chatJid: ev ? ev.chatJid : undefined, senderJid: ev ? ev.senderJid : undefined },
199
+ args: text.trim() ? text.trim().split(/\s+/) : []
200
+ };
201
+ }
202
+
203
+ function _mapReaction(ev) {
204
+ return {
205
+ type: "e2ee_message_reaction",
206
+ threadID: ev && ev.chatJid ? String(ev.chatJid) : "",
207
+ messageID: ev ? ev.messageId : undefined, reaction: ev ? ev.reaction : undefined,
208
+ senderID: ev && ev.senderId != null ? String(ev.senderId) : undefined,
209
+ userID: ev && ev.senderId != null ? String(ev.senderId) : undefined,
210
+ isE2EE: true,
211
+ e2ee: { chatJid: ev ? ev.chatJid : undefined, senderJid: ev ? ev.senderJid : undefined }
212
+ };
213
+ }
214
+
215
+ function _mapReceipt(ev) {
216
+ return {
217
+ type: "e2ee_receipt", isE2EE: true,
218
+ e2ee: {
219
+ receiptType: ev ? ev.type : undefined, chatJid: ev ? ev.chat : undefined,
220
+ senderJid: ev ? ev.sender : undefined, messageIds: ev ? ev.messageIds : []
221
+ }
222
+ };
223
+ }
224
+
225
+ function _cookiesFromJar(ctx) {
226
+ var out = {};
227
+ var jar = [];
228
+ try { jar = ctx.jar.getCookies("https://www.facebook.com"); } catch (_) {}
229
+ jar.forEach(function (c) { if (c && c.key) out[c.key] = c.value; });
230
+ if (!out.c_user && out.i_user) out.c_user = out.i_user;
231
+ return out;
232
+ }
233
+
234
+ function _normalizeMediaInput(input) {
235
+ if (Buffer.isBuffer(input)) return input;
236
+ if (Array.isArray(input)) return Buffer.from(input);
237
+ if (input && input.type === "Buffer" && Array.isArray(input.data)) return Buffer.from(input.data);
238
+ if (typeof input === "string") return Buffer.from(input, "base64");
239
+ throw new Error("E2EE media data must be Buffer, byte array, Buffer-JSON, or base64 string");
240
+ }
241
+
242
+ // ── createBridge ──────────────────────────────────────────────────────────────
243
+ function createBridge(ctx) {
244
+ if (ctx._e2eeBridge) return ctx._e2eeBridge;
245
+
246
+ var state = {
247
+ client: null, connected: false, connectingPromise: null,
248
+ listenerAttached: false, lastGlobalCallback: null,
249
+ lastReadyPayload: null, fullyReady: false
250
+ };
251
+
252
+ function _ensureEnabled() {
253
+ if (ctx.globalOptions.enableE2EE === false)
254
+ throw new Error("E2EE is disabled. Set enableE2EE:true in config.");
255
+ }
256
+
257
+ async function _loadClient() {
258
+ var mod;
259
+ try { mod = await _getDynamicImport()(_E2EE_LIB_URL); }
260
+ catch (err) {
261
+ throw new Error("Cannot load E2EE bundle (" + _E2EE_LIB_URL + "): " +
262
+ (err && err.message ? err.message : String(err)));
263
+ }
264
+ if (!mod || !mod.Client)
265
+ throw new Error("E2EE bundle loaded but Client export not found");
266
+ return mod.Client;
267
+ }
268
+
269
+ function _attachEvents(initCb) {
270
+ if (!state.client || state.listenerAttached) return;
271
+ state.listenerAttached = true;
272
+ if (typeof initCb === "function") state.lastGlobalCallback = initCb;
273
+
274
+ state.client.on("ready", function (p) {
275
+ state.lastReadyPayload = p;
276
+ _callUserCallback(state.lastGlobalCallback, null, { type: "e2ee_ready", isE2EE: true, data: p || null });
277
+ });
278
+ state.client.on("fullyReady", function () {
279
+ state.fullyReady = true;
280
+ _callUserCallback(state.lastGlobalCallback, null, { type: "e2ee_fully_ready", isE2EE: true });
281
+ });
282
+ state.client.on("e2eeConnected", function () {
283
+ _callUserCallback(state.lastGlobalCallback, null, { type: "e2ee_connected", isE2EE: true });
284
+ });
285
+ state.client.on("deviceDataChanged", function (p) {
286
+ if (p && p.deviceData) ctx._e2eeDeviceData = p.deviceData;
287
+ _callUserCallback(state.lastGlobalCallback, null,
288
+ { type: "e2ee_device_data_changed", isE2EE: true, deviceData: p ? p.deviceData : undefined });
289
+ });
290
+ state.client.on("e2eeMessage", function (ev) {
291
+ var mapped = _mapMsg(ev);
292
+ global._e2eeMessageMap = global._e2eeMessageMap || new Map();
293
+ global._e2eeSenderJidMap = global._e2eeSenderJidMap || new Map();
294
+ if (mapped.messageID && mapped.threadID)
295
+ global._e2eeMessageMap.set(String(mapped.messageID), String(mapped.threadID));
296
+ if (mapped.messageID && ev.senderJid)
297
+ global._e2eeSenderJidMap.set(String(mapped.messageID), String(ev.senderJid));
298
+ // Register replyTo message ID so unsend/react works on replied messages.
299
+ // Native bridge uses replyTo.messageId; fall back to replyTo.id just in case.
300
+ if (ev.replyTo && ev.chatJid) {
301
+ var _rtReg = ev.replyTo.messageId || ev.replyTo.id;
302
+ if (_rtReg) global._e2eeMessageMap.set(String(_rtReg), String(ev.chatJid));
303
+ }
304
+ _callUserCallback(state.lastGlobalCallback, null, mapped);
305
+ });
306
+ state.client.on("e2eeMessageEdit", function (ev) { _callUserCallback(state.lastGlobalCallback, null, _mapEdit(ev)); });
307
+ state.client.on("e2eeReaction", function (ev) { _callUserCallback(state.lastGlobalCallback, null, _mapReaction(ev)); });
308
+ state.client.on("e2eeReceipt", function (ev) { _callUserCallback(state.lastGlobalCallback, null, _mapReceipt(ev)); });
309
+ state.client.on("error", function (err) {
310
+ var msg = err && err.message ? err.message : String(err || "");
311
+ if (/close 1006|unexpected EOF|ECONNRESET|ETIMEDOUT|read loop/i.test(msg)) {
312
+ log.warn("e2ee", "Transient network error — will reconnect:", msg); return;
313
+ }
314
+ _callUserCallback(state.lastGlobalCallback, err || new Error("Unknown E2EE error"));
315
+ });
316
+ state.client.on("disconnected", function (info) {
317
+ state.connected = false; state.fullyReady = false;
318
+ log.warn("e2ee", "E2EE disconnected — reconnecting in 5s");
319
+ setTimeout(function () {
320
+ if (!state.connectingPromise) {
321
+ var cb = (ctx && ctx._globalCallback) || state.lastGlobalCallback;
322
+ connect(cb).catch(function (e) { log.error("e2ee", "Reconnect failed:", e && e.message ? e.message : e); });
323
+ }
324
+ }, 5000);
325
+ _callUserCallback(state.lastGlobalCallback, null, { type: "e2ee_disconnected", isE2EE: true, data: info || null });
326
+ });
327
+ }
328
+
329
+ async function connect(globalCallback) {
330
+ _ensureEnabled();
331
+ if (typeof globalCallback === "function") state.lastGlobalCallback = globalCallback;
332
+ if (state.connected && state.client) return state.client;
333
+ if (state.connectingPromise) return state.connectingPromise;
334
+
335
+ state.connectingPromise = (async function () {
336
+ var Client = await _loadClient();
337
+ if (!state.client) {
338
+ var cookies = _cookiesFromJar(ctx);
339
+ if (!cookies.c_user || !cookies.xs)
340
+ throw new Error("Cannot start E2EE: c_user/xs cookies missing");
341
+
342
+ var opts = {
343
+ enableE2EE: true,
344
+ e2eeMemoryOnly: ctx.globalOptions.e2eeMemoryOnly !== false,
345
+ autoReconnect: true, logLevel: "none"
346
+ };
347
+ if (ctx.globalOptions.e2eeDevicePath) opts.devicePath = ctx.globalOptions.e2eeDevicePath;
348
+ if (ctx.globalOptions.e2eeDeviceData) opts.deviceData = ctx.globalOptions.e2eeDeviceData;
349
+ if (!opts.deviceData && global._pendingE2eeDeviceData) {
350
+ opts.deviceData = global._pendingE2eeDeviceData;
351
+ delete global._pendingE2eeDeviceData;
352
+ }
353
+
354
+ state.client = new Client(cookies, opts);
355
+ _attachEvents(globalCallback);
356
+ }
357
+ await state.client.connect();
358
+ state.connected = true; state.fullyReady = false;
359
+ return state.client;
360
+ })();
361
+
362
+ try { return await state.connectingPromise; }
363
+ finally { state.connectingPromise = null; }
364
+ }
365
+
366
+ async function disconnect() {
367
+ if (!state.client) { state.connected = false; return; }
368
+ try { await state.client.disconnect(); }
369
+ finally {
370
+ state.connected = false; state.connectingPromise = null;
371
+ state.listenerAttached = false; state.client = null;
372
+ }
373
+ }
374
+
375
+ async function _ensureClient() {
376
+ _ensureEnabled();
377
+ if (state.connected && state.client) return state.client;
378
+ return connect();
379
+ }
380
+
381
+ var bridge = {
382
+ connect, disconnect,
383
+ isConnected : function () { return !!(state.client && state.connected); },
384
+ isFullyReady: function () {
385
+ if (!state.client || !state.connected) return false;
386
+ if (typeof state.client.isFullyReady === "function") {
387
+ try { return !!state.client.isFullyReady(); } catch (_) {}
388
+ }
389
+ return !!state.fullyReady;
390
+ },
391
+ getState : function () { return state; },
392
+ getDeviceData: async function () { return (await _ensureClient()).getDeviceData(); },
393
+ sendMessage : async function (jid, text, opts) {
394
+ return (await _ensureClient()).sendE2EEMessage(jid, text, opts || {});
395
+ },
396
+ sendReaction : async function (jid, msgId, senderJid, emoji) {
397
+ return (await _ensureClient()).sendE2EEReaction(jid, msgId, senderJid, emoji);
398
+ },
399
+ sendTyping : async function (jid, isTyping) {
400
+ return (await _ensureClient()).sendE2EETyping(jid, isTyping !== false);
401
+ },
402
+ unsendMessage: async function (jid, msgId) {
403
+ return (await _ensureClient()).unsendE2EEMessage(jid, msgId);
404
+ },
405
+ editMessage : async function (jid, msgId, text) {
406
+ return (await _ensureClient()).editE2EEMessage(jid, msgId, text);
407
+ },
408
+ downloadMedia: async function (opts) {
409
+ var client = await _ensureClient();
410
+ var size = opts.fileSize != null ? Number(opts.fileSize) : undefined;
411
+ var res = await client.downloadE2EEMedia({
412
+ directPath: opts.directPath, mediaKey: opts.mediaKey,
413
+ mediaSha256: opts.mediaSha256, mediaEncSha256: opts.mediaEncSha256,
414
+ mediaType: opts.mediaType, mimeType: opts.mimeType, fileSize: size
415
+ });
416
+ return { data: res.data, mimeType: res.mimeType, fileSize: Number(res.fileSize) };
417
+ },
418
+ sendMedia: async function (jid, mediaType, data, opts) {
419
+ var client = await _ensureClient();
420
+ var buf = _normalizeMediaInput(data);
421
+ var o = opts || {};
422
+ var ntype = String(mediaType || "").toLowerCase();
423
+ switch (ntype) {
424
+ case "image":
425
+ return client.sendE2EEImage(jid, buf, o.mimeType || "image/jpeg",
426
+ { caption: o.caption || "", width: o.width, height: o.height,
427
+ replyToId: o.replyToId, replyToSenderJid: o.replyToSenderJid });
428
+ case "video":
429
+ return client.sendE2EEVideo(jid, buf, o.mimeType || "video/mp4",
430
+ { caption: o.caption || "", duration: o.duration, width: o.width, height: o.height,
431
+ replyToId: o.replyToId, replyToSenderJid: o.replyToSenderJid });
432
+ case "audio": case "voice": {
433
+ var mime = o.mimeType || "audio/ogg; codecs=opus";
434
+ var isVoice = ntype === "voice" || !!o.ptt;
435
+ return client.sendE2EEAudio(jid, buf, mime,
436
+ { ptt: isVoice, duration: o.duration != null ? Number(o.duration) : undefined,
437
+ replyToId: o.replyToId, replyToSenderJid: o.replyToSenderJid });
438
+ }
439
+ case "file": case "document":
440
+ return client.sendE2EEDocument(jid, buf, o.filename || "file.bin",
441
+ o.mimeType || "application/octet-stream",
442
+ { replyToId: o.replyToId, replyToSenderJid: o.replyToSenderJid });
443
+ case "sticker":
444
+ return client.sendE2EESticker(jid, buf, o.mimeType || "image/webp",
445
+ { replyToId: o.replyToId, replyToSenderJid: o.replyToSenderJid });
446
+ default: throw new Error("Unsupported E2EE mediaType: " + ntype);
447
+ }
448
+ }
449
+ };
450
+
451
+ ctx._e2eeBridge = bridge;
452
+ return bridge;
453
+ }
454
+
455
+ // ─────────────────────────────────────────────────────────────────────────────
456
+ // § 4 PATCH API – wraps existing api methods to auto-route E2EE JIDs
457
+ // Call patchApiForE2EE(api, ctx) once after buildAPI() when E2EE enabled.
458
+ // ─────────────────────────────────────────────────────────────────────────────
459
+ global._e2eeMessageMap = global._e2eeMessageMap || new Map();
460
+ global._e2eeSenderJidMap = global._e2eeSenderJidMap || new Map();
461
+
462
+ function _regMsg(msgID, jid) {
463
+ if (msgID && jid) global._e2eeMessageMap.set(String(msgID), String(jid));
464
+ }
465
+
466
+ var _EXT_MIME = {
467
+ jpg:"image/jpeg", jpeg:"image/jpeg", png:"image/png", gif:"image/gif",
468
+ webp:"image/webp", bmp:"image/bmp",
469
+ mp4:"video/mp4", mov:"video/quicktime", avi:"video/x-msvideo",
470
+ mkv:"video/x-matroska", webm:"video/webm",
471
+ mp3:"audio/mpeg", ogg:"audio/ogg; codecs=opus", oga:"audio/ogg; codecs=opus",
472
+ opus:"audio/ogg; codecs=opus", wav:"audio/wav", m4a:"audio/mp4",
473
+ aac:"audio/aac", flac:"audio/flac",
474
+ pdf:"application/pdf", txt:"text/plain", json:"application/json"
475
+ };
476
+
477
+ function _ext(att) {
478
+ var p = (att && att.path) ? String(att.path) : (att && att.filename ? String(att.filename) : "");
479
+ return p.split(".").pop().toLowerCase();
480
+ }
481
+ function _mediaType(att) {
482
+ var e = _ext(att);
483
+ if (["jpg","jpeg","png","gif","webp","bmp"].includes(e)) return "image";
484
+ if (["mp4","mov","avi","mkv","webm"].includes(e)) return "video";
485
+ if (["mp3","ogg","oga","opus","wav","m4a","aac","flac"].includes(e)) return "audio";
486
+ return "document";
487
+ }
488
+ function _mimeType(att, mt) {
489
+ if (att && att.mimeType) return String(att.mimeType);
490
+ if (att && att.contentType) return String(att.contentType);
491
+ var e = _ext(att);
492
+ if (_EXT_MIME[e]) return _EXT_MIME[e];
493
+ if (mt === "image") return "image/jpeg";
494
+ if (mt === "video") return "video/mp4";
495
+ if (mt === "audio") return "audio/ogg; codecs=opus";
496
+ return "application/octet-stream";
497
+ }
498
+ function _filename(att) {
499
+ if (att && att.filename) return String(att.filename);
500
+ if (att && att.path) { var p = String(att.path).split(/[\\/]/); return p[p.length-1] || "file.bin"; }
501
+ return "file.bin";
502
+ }
503
+ function _streamToBuffer(r) {
504
+ return new Promise(function (resolve, reject) {
505
+ var chunks = [];
506
+ r.on("data", function (c) { chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)); });
507
+ r.on("end", function () { resolve(Buffer.concat(chunks)); });
508
+ r.on("error", reject);
509
+ });
510
+ }
511
+
512
+ function patchApiForE2EE(api, ctx) {
513
+ // Routing for sendMessage/editMessage/setMessageReaction/unsendMessage is
514
+ // integrated directly inside each fca/src/*.js file.
515
+ // patchApiForE2EE adds the shared helper methods onto the api object.
516
+
517
+ // downloadE2EEMedia helper
518
+ if (typeof api.downloadE2EEMedia !== "function") {
519
+ api.downloadE2EEMedia = function (options) {
520
+ return createBridge(ctx).downloadMedia(options);
521
+ };
522
+ }
523
+
524
+ // resolveE2EEAttachment – download encrypted attachment, return local URL
525
+ if (typeof api.resolveE2EEAttachment !== "function") {
526
+ api.resolveE2EEAttachment = async function (att) {
527
+ if (!att || !att.isE2EE) return att;
528
+ if (att.url && /^https?:\/\//.test(att.url)) return att;
529
+ if (!att.directPath || !att.mediaKey || !att.mediaSha256 || !att.mimeType) return att;
530
+ try {
531
+ var rawType = att.type === "photo" ? "image" : (att.type || "image");
532
+ var res = await api.downloadE2EEMedia({
533
+ directPath: att.directPath, mediaKey: att.mediaKey,
534
+ mediaSha256: att.mediaSha256, mediaEncSha256: att.mediaEncSha256 || undefined,
535
+ mediaType: rawType, mimeType: att.mimeType, fileSize: Number(att.fileSize)
536
+ });
537
+ var localUrl = await storeMedia(res.data, res.mimeType || att.mimeType || "image/jpeg");
538
+ return Object.assign({}, att, { url: localUrl });
539
+ } catch (e) {
540
+ log.error("E2EE", "resolveE2EEAttachment failed:", e && e.message ? e.message : String(e));
541
+ return att;
542
+ }
543
+ };
544
+ }
545
+
546
+ // sendTypingE2EE
547
+ if (typeof api.sendTypingE2EE !== "function") {
548
+ api.sendTypingE2EE = function (chatJid, isTyping) {
549
+ if (!isE2EEChatJid(chatJid)) return Promise.resolve();
550
+ return createBridge(ctx).sendTyping(chatJid, isTyping !== false).catch(function () {});
551
+ };
552
+ }
553
+ }
554
+
555
+ // ─────────────────────────────────────────────────────────────────────────────
556
+ // Exports
557
+ // ─────────────────────────────────────────────────────────────────────────────
558
+ module.exports = {
559
+ isE2EEChatJid : isE2EEChatJid,
560
+ storeMedia : storeMedia,
561
+ createBridge : createBridge,
562
+ patchApiForE2EE: patchApiForE2EE
563
+ };