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
@@ -0,0 +1,309 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+ var logger = require("../logger");
5
+
6
+ var ALLOWED = {
7
+ attachment: true, url: true, sticker: true, emoji: true,
8
+ emojiSize: true, body: true, mentions: true, location: true,
9
+ replyToMessage: true, forwardAttachmentIds: true
10
+ };
11
+
12
+ var EMOJI_SIZES = { small: 1, medium: 2, large: 3 };
13
+
14
+ function toEmojiSize(size) {
15
+ if (typeof size === "number" && !isNaN(size)) return Math.min(3, Math.max(1, size));
16
+ if (typeof size === "string" && size in EMOJI_SIZES) return EMOJI_SIZES[size];
17
+ return 1;
18
+ }
19
+
20
+ function hasLinks(text) {
21
+ return /(https?:\/\/|www\.|t\.me\/|fb\.me\/|youtu\.be\/|facebook\.com\/|youtube\.com\/)/i.test(text);
22
+ }
23
+
24
+ function buildMentionData(msg, baseBody) {
25
+ if (!Array.isArray(msg.mentions) || !msg.mentions.length) return null;
26
+ var ids = [], offsets = [], lengths = [], types = [];
27
+ for (var i = 0; i < msg.mentions.length; i++) {
28
+ var mention = msg.mentions[i];
29
+ // Ensure tag always has @ prefix — Facebook counts @ in both offset and length
30
+ var tag = String(mention.tag || "");
31
+ if (tag && !tag.startsWith("@")) tag = "@" + tag;
32
+ var fromIndex = Number.isInteger(mention.fromIndex) ? mention.fromIndex : 0;
33
+ var offset = baseBody.indexOf(tag, fromIndex);
34
+ if (offset === -1) {
35
+ // Fallback: search for bare name without @
36
+ offset = baseBody.indexOf(tag.slice(1), fromIndex);
37
+ }
38
+ if (offset < 0) offset = 0;
39
+ ids.push(String(mention.id || 0));
40
+ offsets.push(offset);
41
+ lengths.push(tag.length); // includes @ — matches Facebook's expectation
42
+ types.push("p");
43
+ }
44
+ return {
45
+ mention_ids: ids.join(","),
46
+ mention_offsets: offsets.join(","),
47
+ mention_lengths: lengths.join(","),
48
+ mention_types: types.join(",")
49
+ };
50
+ }
51
+
52
+ function extractIdsFromPayload(payload) {
53
+ var messageID = null, threadID = null;
54
+ function walk(node) {
55
+ if (!Array.isArray(node)) return;
56
+ if (node[0] === 5 && (node[1] === "replaceOptimsiticMessage" || node[1] === "replaceOptimisticMessage")) {
57
+ messageID = String(node[3]);
58
+ }
59
+ if (node[0] === 5 && node[1] === "writeCTAIdToThreadsTable") {
60
+ var candidate = node[2];
61
+ if (Array.isArray(candidate) && candidate[0] === 19) threadID = String(candidate[1]);
62
+ }
63
+ for (var i = 0; i < node.length; i++) walk(node[i]);
64
+ }
65
+ try { walk(payload && payload.step); } catch (_) { }
66
+ return { threadID, messageID };
67
+ }
68
+
69
+ function publishLsRequestWithAck(mqttClient, content, requestId, timeout) {
70
+ timeout = timeout || 15000;
71
+ return new Promise((resolve, reject) => {
72
+ var timer = setTimeout(() => {
73
+ mqttClient.removeListener('message', onMessage);
74
+ reject(new Error('MQTT sendMessage timed out after ' + timeout + 'ms'));
75
+ }, timeout);
76
+
77
+ function onMessage(topic, message) {
78
+ if (topic !== '/ls_resp') return;
79
+ try {
80
+ var data = JSON.parse(message.toString());
81
+ if (String(data.request_id) === String(requestId)) {
82
+ clearTimeout(timer);
83
+ mqttClient.removeListener('message', onMessage);
84
+ var extracted = extractIdsFromPayload(data.payload ? JSON.parse(data.payload) : {});
85
+ resolve({ threadID: extracted.threadID, messageID: extracted.messageID });
86
+ }
87
+ } catch (_) { }
88
+ }
89
+
90
+ mqttClient.on('message', onMessage);
91
+ mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1 }, err => {
92
+ if (err) {
93
+ clearTimeout(timer);
94
+ mqttClient.removeListener('message', onMessage);
95
+ reject(err);
96
+ }
97
+ });
98
+ });
99
+ }
100
+
101
+ module.exports = function (defaultFuncs, api, ctx) {
102
+ var uploadAttachmentFn = require('./uploadAttachment')(defaultFuncs, api, ctx);
103
+
104
+ async function uploadAttachments(attachments) {
105
+ if (!Array.isArray(attachments)) attachments = [attachments];
106
+ return uploadAttachmentFn(attachments);
107
+ }
108
+
109
+ async function sendViaMqtt(msg, threadID, replyToMessage) {
110
+ var mqttClient = ctx.mqttClient || global.mqttClient;
111
+ if (!mqttClient) throw new Error('MQTT client not available');
112
+
113
+ var baseBody = msg.body != null ? String(msg.body) : "";
114
+ var requestId = Math.floor(100 + Math.random() * 900);
115
+ var epoch = (BigInt(Date.now()) << 22n).toString();
116
+
117
+ var payload0 = {
118
+ thread_id: String(threadID),
119
+ otid: utils.generateOfflineThreadingID(),
120
+ source: 2097153,
121
+ send_type: 1,
122
+ sync_group: 1,
123
+ mark_thread_read: 1,
124
+ text: baseBody === "" ? null : baseBody,
125
+ initiating_source: 0,
126
+ skip_url_preview_gen: 0,
127
+ text_has_links: hasLinks(baseBody) ? 1 : 0,
128
+ multitab_env: 0,
129
+ metadata_dataclass: JSON.stringify({ media_accessibility_metadata: { alt_text: null } })
130
+ };
131
+
132
+ var mentionData = buildMentionData(msg, baseBody);
133
+ if (mentionData) payload0.mention_data = mentionData;
134
+
135
+ if (msg.sticker) { payload0.send_type = 2; payload0.sticker_id = msg.sticker; }
136
+ if (msg.emoji) { payload0.send_type = 1; payload0.text = msg.emoji; payload0.hot_emoji_size = toEmojiSize(msg.emojiSize); }
137
+
138
+ if (msg.location && msg.location.latitude != null && msg.location.longitude != null) {
139
+ payload0.send_type = 1;
140
+ payload0.location_data = {
141
+ coordinates: { latitude: msg.location.latitude, longitude: msg.location.longitude },
142
+ is_current_location: Boolean(msg.location.current),
143
+ is_live_location: Boolean(msg.location.live)
144
+ };
145
+ }
146
+
147
+ var effectiveReplyTo = replyToMessage || msg.replyToMessage;
148
+ if (effectiveReplyTo) {
149
+ payload0.reply_metadata = {
150
+ reply_source_id: effectiveReplyTo,
151
+ reply_source_type: 1,
152
+ reply_type: 0
153
+ };
154
+ }
155
+
156
+ if (msg.attachment) {
157
+ payload0.send_type = 3;
158
+ if (payload0.text === "") payload0.text = null;
159
+ payload0.attachment_fbids = [];
160
+ var list = Array.isArray(msg.attachment) ? msg.attachment : [msg.attachment];
161
+ var preuploaded = [], toUpload = [];
162
+ for (var item of list) {
163
+ if (Array.isArray(item) && item.length >= 2 && typeof item[0] === "string") {
164
+ preuploaded.push(String(item[1]));
165
+ } else if (utils.isReadableStream(item)) {
166
+ toUpload.push(item);
167
+ }
168
+ }
169
+ if (preuploaded.length) payload0.attachment_fbids = payload0.attachment_fbids.concat(preuploaded);
170
+ if (msg.forwardAttachmentIds && msg.forwardAttachmentIds.length) {
171
+ payload0.attachment_fbids = payload0.attachment_fbids.concat(msg.forwardAttachmentIds.map(String));
172
+ }
173
+
174
+ if (toUpload.length) {
175
+ var uploaded = await uploadAttachments(toUpload);
176
+ for (var u of uploaded) {
177
+ if (!u) continue;
178
+ var fbid = u.image_id || u.video_id || u.audio_id || u.file_id || u.sticker_id || u.gif_id;
179
+ if (!fbid) {
180
+ var firstKey = Object.keys(u).find(function(k) { return u[k] && /^\d+$/.test(String(u[k])); });
181
+ if (firstKey) fbid = u[firstKey];
182
+ }
183
+ if (fbid) payload0.attachment_fbids.push(String(fbid));
184
+ }
185
+ }
186
+ if (!payload0.attachment_fbids.length) delete payload0.attachment_fbids;
187
+ }
188
+
189
+ var tasks = [
190
+ {
191
+ label: '46',
192
+ payload: JSON.stringify(payload0),
193
+ queue_name: String(threadID),
194
+ task_id: 400,
195
+ failure_count: null
196
+ },
197
+ {
198
+ label: '21',
199
+ payload: JSON.stringify({
200
+ thread_id: String(threadID),
201
+ last_read_watermark_ts: Date.now(),
202
+ sync_group: 1
203
+ }),
204
+ queue_name: String(threadID),
205
+ task_id: 401,
206
+ failure_count: null
207
+ }
208
+ ];
209
+
210
+ var content = {
211
+ app_id: '2220391788200892',
212
+ payload: JSON.stringify({
213
+ tasks: tasks,
214
+ epoch_id: epoch,
215
+ version_id: '24804310205905615',
216
+ data_trace_id: '#' + Buffer.from(String(Math.random())).toString('base64').replace(/=+$/, '')
217
+ }),
218
+ request_id: requestId,
219
+ type: 3
220
+ };
221
+
222
+ return publishLsRequestWithAck(mqttClient, content, requestId, 15000);
223
+ }
224
+
225
+ return async function sendMessage(msg, threadID, callback, replyToMessage, isSingleUser) {
226
+ if (typeof msg === "string") msg = { body: msg };
227
+ if (typeof callback !== "function") { callback = null; }
228
+
229
+ var resolve, reject;
230
+ var promise = new Promise((res, rej) => { resolve = res; reject = rej; });
231
+
232
+ for (var key in msg) {
233
+ if (!ALLOWED[key]) {
234
+ var err = { error: "sendMessage: Unknown property '" + key + "'" };
235
+ if (callback) callback(err);
236
+ else reject(err);
237
+ return promise;
238
+ }
239
+ }
240
+
241
+ // Auto-detect isSingleUser from ctx.threadTypes if not explicitly provided.
242
+ // parseDelta in listenMqtt.js populates ctx.threadTypes[senderID] = 'dm' | 'group'
243
+ if (isSingleUser === undefined && ctx.threadTypes) {
244
+ isSingleUser = ctx.threadTypes[String(threadID)] === 'dm';
245
+ }
246
+
247
+ // DM attachment sends — skip MQTT entirely.
248
+ // For E2EE DMs: route through the E2EE bridge (Noise WebSocket, Signal Protocol).
249
+ // Facebook strips attachment_fbids from MQTT messages in E2EE threads
250
+ // (can't re-encrypt CDN attachments on the fly), so MQTT silently drops them.
251
+ // The vendor's client.sendImage/sendVideo/sendAudio encrypts the file data
252
+ // and sends via the Noise WebSocket — the only path that actually delivers
253
+ // attachments in E2EE threads.
254
+ // For non-E2EE DMs: use OldMessage.
255
+ // /messaging/send/ with other_user_fbid routing works for plain DMs.
256
+ // (For E2EE DMs it returns 404 because the endpoint is deprecated for those.)
257
+ if (isSingleUser && msg.attachment) {
258
+ var useE2EE = api.e2ee && typeof api.e2ee.isConnected === "function" && api.e2ee.isConnected();
259
+ if (useE2EE) {
260
+ try {
261
+ var e2eeResult = await api.e2ee.sendMessage(String(threadID), msg, replyToMessage);
262
+ var wrapped = e2eeResult && e2eeResult.messageId
263
+ ? { threadID: String(threadID), messageID: String(e2eeResult.messageId) }
264
+ : e2eeResult;
265
+ if (callback) callback(null, wrapped);
266
+ else resolve(wrapped);
267
+ } catch (e2eeErr) {
268
+ logger.error("sendMessage", "E2EE DM attachment send failed: " + (e2eeErr.message || e2eeErr));
269
+ if (callback) callback(e2eeErr);
270
+ else reject(e2eeErr);
271
+ }
272
+ } else {
273
+ try {
274
+ var omResult = await new Promise((res2, rej2) => {
275
+ api.OldMessage(msg, threadID, (err2, data2) => err2 ? rej2(err2) : res2(data2), replyToMessage, true);
276
+ });
277
+ if (callback) callback(null, omResult);
278
+ else resolve(omResult);
279
+ } catch (omErr) {
280
+ logger.error("sendMessage", "DM attachment via OldMessage failed: " + (omErr.error || omErr.message || omErr));
281
+ if (callback) callback(omErr);
282
+ else reject(omErr);
283
+ }
284
+ }
285
+ return promise;
286
+ }
287
+
288
+ try {
289
+ var result = await sendViaMqtt(msg, threadID, replyToMessage);
290
+ if (callback) callback(null, result);
291
+ else resolve(result);
292
+ } catch (mqttErr) {
293
+ logger.warn("sendMessage", "MQTT failed, falling back to OldMessage: " + (mqttErr.message || mqttErr));
294
+ try {
295
+ var fbResult = await new Promise((res2, rej2) => {
296
+ api.OldMessage(msg, threadID, (err2, data2) => err2 ? rej2(err2) : res2(data2), replyToMessage, isSingleUser);
297
+ });
298
+ if (callback) callback(null, fbResult);
299
+ else resolve(fbResult);
300
+ } catch (fbErr) {
301
+ logger.error("sendMessage", fbErr.error || fbErr.message || fbErr);
302
+ if (callback) callback(fbErr);
303
+ else reject(fbErr);
304
+ }
305
+ }
306
+
307
+ return promise;
308
+ };
309
+ };
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ var utils = require("../utils");
4
+
5
+ module.exports = function (defaultFuncs, api, ctx) {
6
+ return function sendMessageMqtt(msg, threadID, callback, replyToMessage) {
7
+ if (typeof msg === "string") msg = { body: msg };
8
+ var mqttClient = ctx.mqttClient || global.mqttClient;
9
+ if (!mqttClient) {
10
+ var err = { error: "MQTT not connected. Call listenMqtt first." };
11
+ if (typeof callback === "function") return callback(err);
12
+ return Promise.reject(err);
13
+ }
14
+
15
+ ctx.wsReqNumber = (ctx.wsReqNumber || 0) + 1;
16
+ ctx.wsTaskNumber = (ctx.wsTaskNumber || 0) + 1;
17
+
18
+ var baseBody = msg.body != null ? String(msg.body) : "";
19
+ var requestId = ctx.wsReqNumber;
20
+
21
+ var payload0 = {
22
+ thread_id: String(threadID),
23
+ otid: utils.generateOfflineThreadingID(),
24
+ source: 2097153,
25
+ send_type: 1,
26
+ sync_group: 1,
27
+ mark_thread_read: 1,
28
+ text: baseBody === "" ? null : baseBody,
29
+ initiating_source: 0,
30
+ skip_url_preview_gen: 0,
31
+ text_has_links: 0,
32
+ multitab_env: 0
33
+ };
34
+
35
+ if (msg.sticker) { payload0.send_type = 2; payload0.sticker_id = msg.sticker; }
36
+ if (replyToMessage || msg.replyToMessage) {
37
+ payload0.reply_metadata = {
38
+ reply_source_id: replyToMessage || msg.replyToMessage,
39
+ reply_source_type: 1,
40
+ reply_type: 0
41
+ };
42
+ }
43
+
44
+ var content = {
45
+ app_id: '2220391788200892',
46
+ payload: JSON.stringify({
47
+ tasks: [{
48
+ failure_count: null,
49
+ label: '46',
50
+ payload: JSON.stringify(payload0),
51
+ queue_name: String(threadID),
52
+ task_id: ctx.wsTaskNumber
53
+ }],
54
+ epoch_id: utils.generateOfflineThreadingID(),
55
+ version_id: '7214102258676893'
56
+ }),
57
+ request_id: requestId,
58
+ type: 3
59
+ };
60
+
61
+ if (typeof callback === "function") {
62
+ ctx.reqCallbacks[requestId] = callback;
63
+ }
64
+
65
+ mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1 });
66
+ return Promise.resolve({ requestId });
67
+ };
68
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var utils = require("../utils");
3
+ module.exports = function (defaultFuncs, api, ctx) {
4
+ return function sendSticker(stickerID, threadID, replyToMessage, callback) {
5
+ if (typeof replyToMessage === "function") { callback = replyToMessage; replyToMessage = null; }
6
+ return api.sendMessage({ sticker: stickerID, replyToMessage }, threadID, callback);
7
+ };
8
+ };
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var utils = require("../utils");
3
+ module.exports = function (defaultFuncs, api, ctx) {
4
+ return function sendTypingIndicator(threadID, isTyping, callback) {
5
+ var { promise, callback: cb } = utils.wrapCallback(callback);
6
+ var mqttClient = ctx.mqttClient || global.mqttClient;
7
+ if (!mqttClient) { cb({ error: "MQTT not connected" }); return promise; }
8
+
9
+ ctx.wsReqNumber = (ctx.wsReqNumber || 0) + 1;
10
+ var payload = {
11
+ thread_key: String(threadID),
12
+ is_group_thread: (ctx.threadTypes && ctx.threadTypes[threadID] === 'group') ? 1 : 0,
13
+ is_typing: isTyping ? 1 : 0,
14
+ attribution: 0
15
+ };
16
+ var content = {
17
+ app_id: '2220391788200892',
18
+ payload: JSON.stringify({
19
+ tasks: [{
20
+ failure_count: null,
21
+ label: '3',
22
+ payload: JSON.stringify(payload),
23
+ queue_name: 'typing_state_notify',
24
+ task_id: ctx.wsReqNumber
25
+ }],
26
+ epoch_id: utils.generateOfflineThreadingID(),
27
+ version_id: '7214102258676893'
28
+ }),
29
+ request_id: ctx.wsReqNumber,
30
+ type: 3
31
+ };
32
+ mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1 });
33
+ cb(null, true);
34
+ return promise;
35
+ };
36
+ };
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+
4
+
5
+ var utils = require("../utils");
6
+ // @NethWs3Dev
7
+
8
+ module.exports = function (defaultFuncs, api, ctx) {
9
+ return async function sendTypingIndicatorV2(sendTyping,threadID, callback) {
10
+ let count_req = 0
11
+ var wsContent = {
12
+ app_id: 2220391788200892,
13
+ payload: JSON.stringify({
14
+ label: 3,
15
+ payload: JSON.stringify({
16
+ thread_key: threadID.toString(),
17
+ is_group_thread: +(threadID.toString().length >= 16),
18
+ is_typing: +sendTyping,
19
+ attribution: 0
20
+ }),
21
+ version: 5849951561777440
22
+ }),
23
+ request_id: ++count_req,
24
+ type: 4
25
+ };
26
+ await new Promise((resolve, reject) => mqttClient.publish('/ls_req', JSON.stringify(wsContent), {}, (err, _packet) => err ? reject(err) : resolve()));
27
+ };
28
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var fs = require("fs");
3
+ module.exports = function (defaultFuncs, api, ctx) {
4
+ return function sendVideo(videoPath, threadID, caption, callback) {
5
+ if (typeof caption === "function") { callback = caption; caption = ""; }
6
+ var stream = typeof videoPath === "string" ? fs.createReadStream(videoPath) : videoPath;
7
+ return api.sendMessage({ attachment: stream, body: caption || "" }, threadID, callback);
8
+ };
9
+ };
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * SessionGuard — Protects the appstate (session) from corruption and silent logouts.
5
+ *
6
+ * Features:
7
+ * • Auto-saves appstate to disk periodically (default every 5 min)
8
+ * • Saves on every successful message send (debounced, 30s cooldown)
9
+ * • Backs up the previous appstate before overwriting (.bak file)
10
+ * • Detects logout/checkpoint events from listen stream and alerts
11
+ * • Provides api.saveSession([path]) for manual save at any time
12
+ * • Never overwrites with a shorter/smaller appstate (corruption guard)
13
+ */
14
+
15
+ var fs = require("fs");
16
+ var path = require("path");
17
+ var logger = require("../logger");
18
+
19
+ var SAVE_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
20
+ var DEBOUNCE_MS = 30 * 1000; // 30 seconds cooldown between auto-saves
21
+ var MIN_COOKIES = 5; // minimum cookie count we consider "valid"
22
+
23
+ module.exports = function (defaultFuncs, api, ctx) {
24
+
25
+ function getState() {
26
+ try { return api.getAppState(); } catch (_) { return null; }
27
+ }
28
+
29
+ function saveToDisk(filePath) {
30
+ var state = getState();
31
+ if (!state || !Array.isArray(state) || state.length < MIN_COOKIES) {
32
+ logger.warn("SessionGuard", "Skipped save — state looks empty or invalid (" + (state ? state.length : 0) + " cookies).");
33
+ return false;
34
+ }
35
+
36
+ // Corruption guard: never write a smaller appstate than what's already on disk
37
+ if (fs.existsSync(filePath)) {
38
+ try {
39
+ var existing = JSON.parse(fs.readFileSync(filePath, "utf8"));
40
+ if (Array.isArray(existing) && state.length < existing.length * 0.8) {
41
+ logger.warn("SessionGuard", "Skipped save — new state has " + state.length + " cookies vs " + existing.length + " on disk (possible truncation).");
42
+ return false;
43
+ }
44
+ // Backup the current valid state before overwriting
45
+ fs.writeFileSync(filePath + ".bak", JSON.stringify(existing, null, 2), "utf8");
46
+ } catch (_) {}
47
+ }
48
+
49
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf8");
50
+ logger.success("SessionGuard", "Session saved → " + filePath + " (" + state.length + " cookies)");
51
+ return true;
52
+ }
53
+
54
+ return function sessionGuard(appStatePath, options) {
55
+ options = options || {};
56
+ var interval = options.interval !== undefined ? options.interval : SAVE_INTERVAL_MS;
57
+ var debounce = options.debounce !== undefined ? options.debounce : DEBOUNCE_MS;
58
+
59
+ appStatePath = appStatePath || path.join(process.cwd(), "appstate.json");
60
+
61
+ var lastSave = 0;
62
+ var intervalRef = null;
63
+
64
+ // Periodic save
65
+ if (interval > 0) {
66
+ intervalRef = setInterval(function () {
67
+ logger.info("SessionGuard", "Periodic save...");
68
+ saveToDisk(appStatePath);
69
+ lastSave = Date.now();
70
+ }, interval);
71
+ if (intervalRef.unref) intervalRef.unref(); // don't block process exit
72
+ }
73
+
74
+ // Debounced on-send save
75
+ // Patch sendMessage to auto-save appstate after a successful send
76
+ var originalSendMessage = api.sendMessage;
77
+ api.sendMessage = function () {
78
+ var result = originalSendMessage.apply(this, arguments);
79
+ // After send, debounce-save
80
+ Promise.resolve(result).then(function () {
81
+ if (Date.now() - lastSave > debounce) {
82
+ saveToDisk(appStatePath);
83
+ lastSave = Date.now();
84
+ }
85
+ }).catch(function () {});
86
+ return result;
87
+ };
88
+
89
+ // Expose manual save
90
+ api.saveSession = function (customPath) {
91
+ return saveToDisk(customPath || appStatePath);
92
+ };
93
+
94
+ // Expose restore from backup
95
+ api.restoreSessionBackup = function (customPath) {
96
+ var bakPath = (customPath || appStatePath) + ".bak";
97
+ if (!fs.existsSync(bakPath)) {
98
+ logger.warn("SessionGuard", "No backup file found at " + bakPath);
99
+ return false;
100
+ }
101
+ try {
102
+ var bak = fs.readFileSync(bakPath, "utf8");
103
+ fs.writeFileSync(customPath || appStatePath, bak, "utf8");
104
+ logger.success("SessionGuard", "Backup restored from " + bakPath);
105
+ return true;
106
+ } catch (e) {
107
+ logger.error("SessionGuard", "Restore failed: " + e.message);
108
+ return false;
109
+ }
110
+ };
111
+
112
+ // Expose stop
113
+ api.stopSessionGuard = function () {
114
+ if (intervalRef) clearInterval(intervalRef);
115
+ api.sendMessage = originalSendMessage;
116
+ logger.info("SessionGuard", "Stopped.");
117
+ };
118
+
119
+ logger.success(
120
+ "SessionGuard",
121
+ "Active — auto-save every " + Math.round(interval / 60000) + " min → " + appStatePath
122
+ );
123
+
124
+ return {
125
+ save: function (p) { return saveToDisk(p || appStatePath); },
126
+ stop: api.stopSessionGuard,
127
+ restore: api.restoreSessionBackup
128
+ };
129
+ };
130
+ };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var utils = require("../utils");
3
+ module.exports = function (defaultFuncs, api, ctx) {
4
+ return function setActiveStatus(enable, callback) {
5
+ var { promise, callback: cb } = utils.wrapCallback(callback);
6
+ defaultFuncs.post("https://www.facebook.com/ajax/presence/update.php", ctx.jar, {
7
+ fb_api_req_friendly_name: "updatePresence",
8
+ presence_state: enable ? "ACTIVE" : "OFFLINE",
9
+ __user: ctx.userID
10
+ })
11
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
12
+ .then(resData => { if (resData.error) throw resData; cb(null, true); })
13
+ .catch(err => cb(err));
14
+ return promise;
15
+ };
16
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var utils = require("../utils");
3
+ module.exports = function (defaultFuncs, api, ctx) {
4
+ return function setMessageReaction(reaction, messageID, threadID, callback) {
5
+ var { promise, callback: cb } = utils.wrapCallback(callback);
6
+ var mqttClient = ctx.mqttClient || global.mqttClient;
7
+
8
+ if (mqttClient) {
9
+ ctx.wsReqNumber = (ctx.wsReqNumber || 0) + 1;
10
+ ctx.wsTaskNumber = (ctx.wsTaskNumber || 0) + 1;
11
+ var payload = {
12
+ thread_key: String(threadID),
13
+ timestamp_ms: Date.now(),
14
+ message_id: String(messageID),
15
+ reaction: reaction || "",
16
+ actor_id: ctx.userID,
17
+ reaction_style: null,
18
+ sync_group: 1,
19
+ send_attribution: 65537
20
+ };
21
+ var content = {
22
+ app_id: '2220391788200892',
23
+ payload: JSON.stringify({
24
+ data_trace_id: null,
25
+ epoch_id: parseInt(utils.generateOfflineThreadingID()),
26
+ tasks: [{
27
+ failure_count: null,
28
+ label: '29',
29
+ payload: JSON.stringify(payload),
30
+ queue_name: JSON.stringify(['reaction', String(messageID)]),
31
+ task_id: ctx.wsTaskNumber
32
+ }],
33
+ version_id: '7158486590867448'
34
+ }),
35
+ request_id: ctx.wsReqNumber,
36
+ type: 3
37
+ };
38
+ mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1, retain: false });
39
+ cb(null, true);
40
+ return promise;
41
+ }
42
+
43
+ defaultFuncs.postFormData("https://www.facebook.com/webgraphql/mutation/", ctx.jar, {}, {
44
+ doc_id: "1491398900900362",
45
+ variables: JSON.stringify({
46
+ data: {
47
+ client_mutation_id: String(ctx.clientMutationId++),
48
+ actor_id: ctx.userID,
49
+ action: reaction ? "ADD_REACTION" : "REMOVE_REACTION",
50
+ message_id: String(messageID),
51
+ reaction: reaction || null
52
+ }
53
+ }),
54
+ dpr: 1
55
+ })
56
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
57
+ .then(r => { if (r && r.error) throw r; cb(null, true); })
58
+ .catch(err => cb(err));
59
+ return promise;
60
+ };
61
+ };