shadowx-fca 1.2.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 (107) hide show
  1. package/index.js +479 -0
  2. package/package.json +55 -0
  3. package/src/OldMessage.js +329 -0
  4. package/src/Screenshot.js +83 -0
  5. package/src/addExternalModule.js +25 -0
  6. package/src/addUserToGroup.js +115 -0
  7. package/src/changeAdminStatus.js +103 -0
  8. package/src/changeArchivedStatus.js +55 -0
  9. package/src/changeAvatar.js +136 -0
  10. package/src/changeAvatarV2.js +86 -0
  11. package/src/changeAvt.js +85 -0
  12. package/src/changeBio.js +76 -0
  13. package/src/changeBlockedStatus.js +49 -0
  14. package/src/changeBlockedStatusMqtt.js +80 -0
  15. package/src/changeCover.js +72 -0
  16. package/src/changeGroupImage.js +135 -0
  17. package/src/changeName.js +79 -0
  18. package/src/changeNickname.js +59 -0
  19. package/src/changeThreadColor.js +65 -0
  20. package/src/changeThreadEmoji.js +55 -0
  21. package/src/changeUsername.js +59 -0
  22. package/src/createCommentPost.js +230 -0
  23. package/src/createNewGroup.js +88 -0
  24. package/src/createPoll.js +71 -0
  25. package/src/createPost.js +276 -0
  26. package/src/data/cache/system/data.json +4 -0
  27. package/src/data/cache/system/datahandle.js +21 -0
  28. package/src/data/getThreadInfo.json +1 -0
  29. package/src/deleteMessage.js +56 -0
  30. package/src/deleteThread.js +56 -0
  31. package/src/editMessage.js +68 -0
  32. package/src/editMessageOld.js +67 -0
  33. package/src/follow.js +74 -0
  34. package/src/forwardAttachment.js +60 -0
  35. package/src/friendList.js +103 -0
  36. package/src/getAccess.js +112 -0
  37. package/src/getAvatarUser.js +78 -0
  38. package/src/getBotInitialData.js +42 -0
  39. package/src/getCtx.js +5 -0
  40. package/src/getCurrentUserID.js +7 -0
  41. package/src/getEmojiUrl.js +29 -0
  42. package/src/getFriendsList.js +83 -0
  43. package/src/getMessage.js +847 -0
  44. package/src/getOptions.js +5 -0
  45. package/src/getRegion.js +7 -0
  46. package/src/getThreadHistory.js +680 -0
  47. package/src/getThreadHistoryDeprecated.js +71 -0
  48. package/src/getThreadInfo.js +232 -0
  49. package/src/getThreadInfoDeprecated.js +56 -0
  50. package/src/getThreadList.js +213 -0
  51. package/src/getThreadListDeprecated.js +46 -0
  52. package/src/getThreadPictures.js +59 -0
  53. package/src/getThreadTheme.js +82 -0
  54. package/src/getUID.js +119 -0
  55. package/src/getUserID.js +61 -0
  56. package/src/getUserInfo.js +66 -0
  57. package/src/handleFriendRequest.js +46 -0
  58. package/src/handleMessageRequest.js +47 -0
  59. package/src/httpGet.js +49 -0
  60. package/src/httpPost.js +48 -0
  61. package/src/httpPostFormData.js +70 -0
  62. package/src/listenMqtt.js +870 -0
  63. package/src/listenNotification.js +85 -0
  64. package/src/logout.js +75 -0
  65. package/src/markAsDelivered.js +47 -0
  66. package/src/markAsRead.js +70 -0
  67. package/src/markAsReadAll.js +40 -0
  68. package/src/markAsSeen.js +48 -0
  69. package/src/metaTheme.js +190 -0
  70. package/src/muteThread.js +45 -0
  71. package/src/note.js +228 -0
  72. package/src/pinMessage.js +59 -0
  73. package/src/refreshFb_dtsg.js +89 -0
  74. package/src/removeSuspiciousAccount.js +79 -0
  75. package/src/removeUserFromGroup.js +79 -0
  76. package/src/reply.js +442 -0
  77. package/src/resolvePhotoUrl.js +45 -0
  78. package/src/searchForThread.js +53 -0
  79. package/src/searchFriends.js +139 -0
  80. package/src/searchStickers.js +53 -0
  81. package/src/send.js +46 -0
  82. package/src/sendComment.js +159 -0
  83. package/src/sendFriendRequest.js +113 -0
  84. package/src/sendMessage.js +243 -0
  85. package/src/sendMessageMqtt.js +322 -0
  86. package/src/sendTypingIndicator.js +101 -0
  87. package/src/sendTypingIndicatorV2.js +28 -0
  88. package/src/setActiveStatus.js +93 -0
  89. package/src/setMessageReaction.js +122 -0
  90. package/src/setMessageReactionMqtt.js +62 -0
  91. package/src/setPostReaction.js +112 -0
  92. package/src/setProfileGuard.js +44 -0
  93. package/src/setProfileLock.js +98 -0
  94. package/src/setStoryReaction.js +134 -0
  95. package/src/setStorySeen.js +109 -0
  96. package/src/setThreadTheme.js +103 -0
  97. package/src/setTitle.js +90 -0
  98. package/src/shareContact.js +110 -0
  99. package/src/shareLink.js +59 -0
  100. package/src/stopListenMqtt.js +23 -0
  101. package/src/storyManager.js +358 -0
  102. package/src/suggestFriend.js +133 -0
  103. package/src/threadColors.js +131 -0
  104. package/src/unfriend.js +52 -0
  105. package/src/unsendMessage.js +45 -0
  106. package/src/uploadAttachment.js +93 -0
  107. package/utils.js +2867 -0
@@ -0,0 +1,870 @@
1
+ /* eslint-disable no-redeclare */
2
+ "use strict";
3
+ var utils = require("../utils");
4
+ var log = require("npmlog");
5
+ var mqtt = require('mqtt');
6
+ var websocket = require('websocket-stream');
7
+ var HttpsProxyAgent = require('https-proxy-agent');
8
+ const EventEmitter = require('events');
9
+ const debugSeq = false;
10
+ var identity = function () { };
11
+ var form = {};
12
+ var getSeqID = function () { };
13
+ var topics = [
14
+ "/legacy_web",
15
+ "/webrtc",
16
+ "/rtc_multi",
17
+ "/onevc",
18
+ "/br_sr", //Notification
19
+ //Need to publish /br_sr right after this
20
+ "/sr_res",
21
+ "/t_ms",
22
+ "/thread_typing",
23
+ "/orca_typing_notifications",
24
+ "/notify_disconnect",
25
+ //Need to publish /messenger_sync_create_queue right after this
26
+ "/orca_presence",
27
+ //Will receive /sr_res right here.
28
+
29
+ "/inbox",
30
+ "/mercury",
31
+ "/messaging_events",
32
+ "/orca_message_notifications",
33
+ "/pp",
34
+ "/webrtc_response",
35
+ ];
36
+
37
+ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
38
+ //Don't really know what this does but I think it's for the active state?
39
+ //TODO: Move to ctx when implemented
40
+ var chatOn = ctx.globalOptions.online;
41
+ var foreground = false;
42
+
43
+ var sessionID = Math.floor(Math.random() * 9007199254740991) + 1;
44
+ var GUID = utils.getGUID();
45
+ const username = {
46
+ u: ctx.userID,
47
+ s: sessionID,
48
+ chat_on: chatOn,
49
+ fg: foreground,
50
+ d: GUID,
51
+ ct: 'websocket',
52
+ aid: '219994525426954',
53
+ aids: null,
54
+ mqtt_sid: '',
55
+ cp: 3,
56
+ ecp: 10,
57
+ st: [],
58
+ pm: [],
59
+ dc: '',
60
+ no_auto_fg: true,
61
+ gas: null,
62
+ pack: [],
63
+ p: null,
64
+ php_override: ""
65
+ };
66
+ var cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
67
+
68
+ var host;
69
+ if (ctx.mqttEndpoint) host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${GUID}`;
70
+ else if (ctx.region) host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLocaleLowerCase()}&sid=${sessionID}&cid=${GUID}`;
71
+ else host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${GUID}`;
72
+
73
+ const options = {
74
+ clientId: 'mqttwsclient',
75
+ protocolId: 'MQIsdp',
76
+ protocolVersion: 3,
77
+ username: JSON.stringify(username),
78
+ clean: true,
79
+ wsOptions: {
80
+ headers: {
81
+ Cookie: cookies,
82
+ Origin: 'https://www.facebook.com',
83
+ 'User-Agent': ctx.globalOptions.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36',
84
+ Referer: 'https://www.facebook.com/',
85
+ Host: new URL(host).hostname,
86
+ },
87
+ origin: 'https://www.facebook.com',
88
+ protocolVersion: 13,
89
+ binaryType: 'arraybuffer',
90
+ },
91
+ keepalive: 60,
92
+ reschedulePings: true,
93
+ reconnectPeriod: 3,
94
+ };
95
+
96
+ if (typeof ctx.globalOptions.proxy != "undefined") {
97
+ var agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
98
+ options.wsOptions.agent = agent;
99
+ }
100
+
101
+ ctx.mqttClient = new mqtt.Client(_ => websocket(host, options.wsOptions), options);
102
+
103
+ global.mqttClient = ctx.mqttClient;
104
+
105
+ mqttClient.on('error', function (err) {
106
+ log.error("listenMqtt", err);
107
+ mqttClient.end();
108
+ if (ctx.globalOptions.autoReconnect) getSeqID();
109
+ else globalCallback({ type: "stop_listen", error: "Connection refused: Server unavailable" }, null);
110
+ });
111
+
112
+ mqttClient.on('connect', function () {
113
+ topics.forEach(topicsub => mqttClient.subscribe(topicsub));
114
+
115
+ // Display connection success message with branding and loading animation
116
+ const messages = [
117
+ '\n SHADOWX-FCA MQTT Connected',
118
+ ` Region: ${ctx.region || 'PNB'}`,
119
+ `🔄 Auto-reconnect: ${ctx.globalOptions.autoReconnect ? 'Enabled' : 'Disabled'}${ctx.globalOptions.autoReconnect ? ' (reconnects every 3s on disconnect)' : ''}`,
120
+ `⏱️ MQTT Restart Interval: ${ctx.globalOptions.restartListenMqtt?.enable ? `${ctx.globalOptions.restartListenMqtt.timeRestart / 1000}s` : 'Disabled'}`,
121
+ 'Author: Mueid Mursalin Rifat\n'
122
+ ];
123
+
124
+ let index = 0;
125
+ const displayMessages = () => {
126
+ if (index < messages.length) {
127
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
128
+ let frameIndex = 0;
129
+
130
+ const loadingInterval = setInterval(() => {
131
+ process.stdout.write(`\r${frames[frameIndex]} Loading...`);
132
+ frameIndex = (frameIndex + 1) % frames.length;
133
+ }, 80);
134
+
135
+ setTimeout(() => {
136
+ clearInterval(loadingInterval);
137
+ process.stdout.write('\r' + ' '.repeat(20) + '\r');
138
+ console.log(messages[index]);
139
+ index++;
140
+ displayMessages();
141
+ }, 500);
142
+ }
143
+ };
144
+
145
+ displayMessages();
146
+
147
+ var topic;
148
+ var queue = {
149
+ sync_api_version: 10,
150
+ max_deltas_able_to_process: 1000,
151
+ delta_batch_size: 500,
152
+ encoding: "JSON",
153
+ entity_fbid: ctx.userID,
154
+ };
155
+
156
+ if (ctx.syncToken) {
157
+ topic = "/messenger_sync_get_diffs";
158
+ queue.last_seq_id = ctx.lastSeqId;
159
+ queue.sync_token = ctx.syncToken;
160
+ }
161
+ else {
162
+ topic = "/messenger_sync_create_queue";
163
+ queue.initial_titan_sequence_id = ctx.lastSeqId;
164
+ queue.device_params = null;
165
+ }
166
+
167
+ mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
168
+
169
+ var rTimeout = setTimeout(function () {
170
+ mqttClient.end();
171
+ getSeqID();
172
+ }, 5000);
173
+
174
+ ctx.tmsWait = function () {
175
+ clearTimeout(rTimeout);
176
+ ctx.globalOptions.emitReady ? globalCallback({
177
+ type: "ready",
178
+ error: null
179
+ }) : "";
180
+ delete ctx.tmsWait;
181
+ };
182
+ });
183
+
184
+ mqttClient.on('message', function (topic, message, _packet) {
185
+ try {
186
+ var jsonMessage = JSON.parse(message);
187
+ }
188
+ catch (ex) {
189
+ return log.error("listenMqtt", ex);
190
+ }
191
+ if (topic === "/t_ms") {
192
+ if (ctx.tmsWait && typeof ctx.tmsWait == "function") ctx.tmsWait();
193
+
194
+ if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
195
+ ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
196
+ ctx.syncToken = jsonMessage.syncToken;
197
+ }
198
+
199
+ if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
200
+
201
+ //If it contains more than 1 delta
202
+ for (var i in jsonMessage.deltas) {
203
+ var delta = jsonMessage.deltas[i];
204
+ parseDelta(defaultFuncs, api, ctx, globalCallback, { "delta": delta });
205
+ }
206
+ }
207
+ else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
208
+ var typ = {
209
+ type: "typ",
210
+ isTyping: !!jsonMessage.state,
211
+ from: jsonMessage.sender_fbid.toString(),
212
+ threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
213
+ };
214
+ (function () { globalCallback(null, typ); })();
215
+ }
216
+ else if (topic === "/orca_presence") {
217
+ if (!ctx.globalOptions.updatePresence) {
218
+ for (var i in jsonMessage.list) {
219
+ var data = jsonMessage.list[i];
220
+ var userID = data["u"];
221
+
222
+ var presence = {
223
+ type: "presence",
224
+ userID: userID.toString(),
225
+ //Convert to ms
226
+ timestamp: data["l"] * 1000,
227
+ statuses: data["p"]
228
+ };
229
+ (function () { globalCallback(null, presence); })();
230
+ }
231
+ }
232
+ }
233
+
234
+ });
235
+
236
+ mqttClient.on('close', function () { });
237
+ }
238
+
239
+ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
240
+ if (v.delta.class == "NewMessage") {
241
+ //Not tested for pages
242
+ if (ctx.globalOptions.pageID && ctx.globalOptions.pageID != v.queue) return;
243
+
244
+ (function resolveAttachmentUrl(i) {
245
+ if (i == (v.delta.attachments || []).length) {
246
+ let fmtMsg;
247
+ try {
248
+ fmtMsg = utils.formatDeltaMessage(v);
249
+ // Detect if it's a DM or group thread - enhanced detection
250
+ const otherUserFbId = v.delta.messageMetadata.threadKey.otherUserFbId;
251
+ const threadFbId = v.delta.messageMetadata.threadKey.threadFbId;
252
+
253
+ // A thread is a DM if it has otherUserFbId and no threadFbId
254
+ fmtMsg.isSingleUser = !!otherUserFbId && !threadFbId;
255
+ fmtMsg.isGroup = !!threadFbId;
256
+
257
+ // Store thread type in context for sendMessage to use
258
+ if (!ctx.threadTypes) ctx.threadTypes = {};
259
+ ctx.threadTypes[fmtMsg.threadID] = fmtMsg.isSingleUser ? 'dm' : 'group';
260
+ } catch (err) {
261
+ return globalCallback({
262
+ error: "Problem parsing message object.",
263
+ detail: err,
264
+ res: v,
265
+ type: "parse_error"
266
+ });
267
+ }
268
+ if (fmtMsg) {
269
+ if (ctx.globalOptions.autoMarkDelivery) {
270
+ markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
271
+ }
272
+ }
273
+ return !ctx.globalOptions.selfListen &&
274
+ (fmtMsg.senderID === ctx.i_userID || fmtMsg.senderID === ctx.userID) ?
275
+ undefined :
276
+ (function () { globalCallback(null, fmtMsg); })();
277
+ } else {
278
+ if (v.delta.attachments[i].mercury.attach_type == "photo") {
279
+ api.resolvePhotoUrl(
280
+ v.delta.attachments[i].fbid,
281
+ (err, url) => {
282
+ if (!err)
283
+ v.delta.attachments[
284
+ i
285
+ ].mercury.metadata.url = url;
286
+ return resolveAttachmentUrl(i + 1);
287
+ }
288
+ );
289
+ } else {
290
+ return resolveAttachmentUrl(i + 1);
291
+ }
292
+ }
293
+ })(0);
294
+ }
295
+
296
+ if (v.delta.class == "ClientPayload") {
297
+ var clientPayload = utils.decodeClientPayload(v.delta.payload);
298
+ if (clientPayload && clientPayload.deltas) {
299
+ for (var i in clientPayload.deltas) {
300
+ var delta = clientPayload.deltas[i];
301
+ if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
302
+ (function () {
303
+ globalCallback(null, {
304
+ type: "message_reaction",
305
+ threadID: (delta.deltaMessageReaction.threadKey.threadFbId ? delta.deltaMessageReaction.threadKey.threadFbId : delta.deltaMessageReaction.threadKey.otherUserFbId).toString(),
306
+ messageID: delta.deltaMessageReaction.messageId,
307
+ reaction: delta.deltaMessageReaction.reaction,
308
+ senderID: delta.deltaMessageReaction.senderId.toString(),
309
+ userID: delta.deltaMessageReaction.userId.toString()
310
+ });
311
+ })();
312
+ }
313
+ else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
314
+ (function () {
315
+ globalCallback(null, {
316
+ type: "message_unsend",
317
+ threadID: (delta.deltaRecallMessageData.threadKey.threadFbId ? delta.deltaRecallMessageData.threadKey.threadFbId : delta.deltaRecallMessageData.threadKey.otherUserFbId).toString(),
318
+ messageID: delta.deltaRecallMessageData.messageID,
319
+ senderID: delta.deltaRecallMessageData.senderID.toString(),
320
+ deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
321
+ timestamp: delta.deltaRecallMessageData.timestamp
322
+ });
323
+ })();
324
+ }
325
+ else if (delta.deltaMessageReply) {
326
+ //Mention block - #1
327
+ var mdata = delta.deltaMessageReply.message === undefined ? [] :
328
+ delta.deltaMessageReply.message.data === undefined ? [] :
329
+ delta.deltaMessageReply.message.data.prng === undefined ? [] :
330
+ JSON.parse(delta.deltaMessageReply.message.data.prng);
331
+ var m_id = mdata.map(u => u.i);
332
+ var m_offset = mdata.map(u => u.o);
333
+ var m_length = mdata.map(u => u.l);
334
+
335
+ var mentions = {};
336
+
337
+ for (var i = 0; i < m_id.length; i++) mentions[m_id[i]] = (delta.deltaMessageReply.message.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
338
+ //Mention block - 1#
339
+ var callbackToReturn = {
340
+ type: "message_reply",
341
+ threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
342
+ messageID: delta.deltaMessageReply.message.messageMetadata.messageId,
343
+ senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
344
+ attachments: (delta.deltaMessageReply.message.attachments || []).map(function (att) {
345
+ var mercury = JSON.parse(att.mercuryJSON);
346
+ Object.assign(att, mercury);
347
+ return att;
348
+ }).map(att => {
349
+ var x;
350
+ try {
351
+ x = utils._formatAttachment(att);
352
+ }
353
+ catch (ex) {
354
+ x = att;
355
+ x.error = ex;
356
+ x.type = "unknown";
357
+ }
358
+ return x;
359
+ }),
360
+ args: (delta.deltaMessageReply.message.body || "").trim().split(/\s+/),
361
+ body: (delta.deltaMessageReply.message.body || ""),
362
+ isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
363
+ mentions: mentions,
364
+ timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
365
+ participantIDs: (delta.deltaMessageReply.message.messageMetadata.cid.canonicalParticipantFbids || delta.deltaMessageReply.message.participants || []).map(e => e.toString())
366
+ };
367
+
368
+ if (delta.deltaMessageReply.repliedToMessage) {
369
+ //Mention block - #2
370
+ mdata = delta.deltaMessageReply.repliedToMessage === undefined ? [] :
371
+ delta.deltaMessageReply.repliedToMessage.data === undefined ? [] :
372
+ delta.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] :
373
+ JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng);
374
+ m_id = mdata.map(u => u.i);
375
+ m_offset = mdata.map(u => u.o);
376
+ m_length = mdata.map(u => u.l);
377
+
378
+ var rmentions = {};
379
+
380
+ for (var i = 0; i < m_id.length; i++) rmentions[m_id[i]] = (delta.deltaMessageReply.repliedToMessage.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
381
+ //Mention block - 2#
382
+ callbackToReturn.messageReply = {
383
+ threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
384
+ messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
385
+ senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
386
+ attachments: delta.deltaMessageReply.repliedToMessage.attachments.map(function (att) {
387
+ var mercury = JSON.parse(att.mercuryJSON);
388
+ Object.assign(att, mercury);
389
+ return att;
390
+ }).map(att => {
391
+ var x;
392
+ try {
393
+ x = utils._formatAttachment(att);
394
+ }
395
+ catch (ex) {
396
+ x = att;
397
+ x.error = ex;
398
+ x.type = "unknown";
399
+ }
400
+ return x;
401
+ }),
402
+ args: (delta.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
403
+ body: delta.deltaMessageReply.repliedToMessage.body || "",
404
+ isGroup: !!delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
405
+ mentions: rmentions,
406
+ timestamp: delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp
407
+ };
408
+ }
409
+ else if (delta.deltaMessageReply.replyToMessageId) {
410
+ return defaultFuncs
411
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
412
+ "av": ctx.globalOptions.pageID,
413
+ "queries": JSON.stringify({
414
+ "o0": {
415
+ //Using the same doc_id as forcedFetch
416
+ "doc_id": "2848441488556444",
417
+ "query_params": {
418
+ "thread_and_message_id": {
419
+ "thread_id": callbackToReturn.threadID,
420
+ "message_id": delta.deltaMessageReply.replyToMessageId.id,
421
+ }
422
+ }
423
+ }
424
+ })
425
+ })
426
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
427
+ .then((resData) => {
428
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
429
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
430
+ var fetchData = resData[0].o0.data.message;
431
+ var mobj = {};
432
+ for (var n in fetchData.message.ranges) mobj[fetchData.message.ranges[n].entity.id] = (fetchData.message.text || "").substr(fetchData.message.ranges[n].offset, fetchData.message.ranges[n].length);
433
+
434
+ callbackToReturn.messageReply = {
435
+ threadID: callbackToReturn.threadID,
436
+ messageID: fetchData.message_id,
437
+ senderID: fetchData.message_sender.id.toString(),
438
+ attachments: fetchData.message.blob_attachment.map(att => {
439
+ var x;
440
+ try {
441
+ x = utils._formatAttachment({ blob_attachment: att });
442
+ }
443
+ catch (ex) {
444
+ x = att;
445
+ x.error = ex;
446
+ x.type = "unknown";
447
+ }
448
+ return x;
449
+ }),
450
+ args: (fetchData.message.text || "").trim().split(/\s+/) || [],
451
+ body: fetchData.message.text || "",
452
+ isGroup: callbackToReturn.isGroup,
453
+ mentions: mobj,
454
+ timestamp: parseInt(fetchData.timestamp_precise)
455
+ };
456
+ })
457
+ .catch(err => log.error("forcedFetch", err))
458
+ .finally(function () {
459
+ if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
460
+ !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID ? undefined : (function () { globalCallback(null, callbackToReturn); })();
461
+ });
462
+ }
463
+ else callbackToReturn.delta = delta;
464
+
465
+ if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
466
+
467
+ return !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID ? undefined : (function () { globalCallback(null, callbackToReturn); })();
468
+ }
469
+ }
470
+ return;
471
+ }
472
+ }
473
+
474
+ if (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents) return;
475
+ switch (v.delta.class) {
476
+ case "JoinableMode": {
477
+ let fmtMsg;
478
+ try {
479
+ fmtMsg = utils.formatDeltaEvent(v.delta);
480
+ } catch (err) {
481
+ return globalCallback({
482
+ error: "Lỗi gòi!!",
483
+ detail: err,
484
+ res: v.delta,
485
+ type: "parse_error"
486
+ });
487
+ }
488
+ return globalCallback(null, fmtMsg);
489
+ }
490
+ case "AdminTextMessage":
491
+ switch (v.delta.type) {
492
+ case 'confirm_friend_request':
493
+ case 'shared_album_delete':
494
+ case 'shared_album_addition':
495
+ case 'pin_messages_v2':
496
+ case 'unpin_messages_v2':
497
+ case "change_thread_theme":
498
+ case "change_thread_nickname":
499
+ case "change_thread_icon":
500
+ case "change_thread_quick_reaction":
501
+ case "change_thread_admins":
502
+ case "group_poll":
503
+ case "joinable_group_link_mode_change":
504
+ case "magic_words":
505
+ case "change_thread_approval_mode":
506
+ case "messenger_call_log":
507
+ case "participant_joined_group_call":
508
+ var fmtMsg;
509
+ try {
510
+ fmtMsg = utils.formatDeltaEvent(v.delta);
511
+ }
512
+ catch (err) {
513
+ return globalCallback({
514
+ error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
515
+ detail: err,
516
+ res: v.delta,
517
+ type: "parse_error"
518
+ });
519
+ }
520
+ return (function () { globalCallback(null, fmtMsg); })();
521
+ default:
522
+ // console.log(v.delta)
523
+ return;
524
+ }
525
+ //For group images
526
+ case "ForcedFetch":
527
+ if (!v.delta.threadKey) return;
528
+ var mid = v.delta.messageId;
529
+ var tid = v.delta.threadKey.threadFbId;
530
+ if (mid && tid) {
531
+ const form = {
532
+ "av": ctx.globalOptions.pageID,
533
+ "queries": JSON.stringify({
534
+ "o0": {
535
+ //This doc_id is valid as of March 25, 2020
536
+ "doc_id": "2848441488556444",
537
+ "query_params": {
538
+ "thread_and_message_id": {
539
+ "thread_id": tid.toString(),
540
+ "message_id": mid,
541
+ }
542
+ }
543
+ }
544
+ })
545
+ };
546
+
547
+ defaultFuncs
548
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
549
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
550
+ .then((resData) => {
551
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
552
+
553
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
554
+
555
+ var fetchData = resData[0].o0.data.message;
556
+
557
+ if (utils.getType(fetchData) == "Object") {
558
+ log.info("forcedFetch", fetchData);
559
+ switch (fetchData.__typename) {
560
+ case "ThreadImageMessage":
561
+ (!ctx.globalOptions.selfListen && fetchData.message_sender.id.toString() === ctx.userID) ||
562
+ !ctx.loggedIn ? undefined : (function () {
563
+ globalCallback(null, {
564
+ type: "change_thread_image",
565
+ threadID: utils.formatID(tid.toString()),
566
+ snippet: fetchData.snippet,
567
+ timestamp: fetchData.timestamp_precise,
568
+ author: fetchData.message_sender.id,
569
+ image: {
570
+ attachmentID: fetchData.image_with_metadata && fetchData.image_with_metadata.legacy_attachment_id,
571
+ width: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.x,
572
+ height: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.y,
573
+ url: fetchData.image_with_metadata && fetchData.image_with_metadata.preview.uri
574
+ }
575
+ });
576
+ })();
577
+ break;
578
+ case "UserMessage":
579
+ log.info("ff-Return", {
580
+ type: "message",
581
+ senderID: utils.formatID(fetchData.message_sender.id),
582
+ body: fetchData.message.text || "",
583
+ threadID: utils.formatID(tid.toString()),
584
+ messageID: fetchData.message_id,
585
+ attachments: [{
586
+ type: "share",
587
+ ID: fetchData.extensible_attachment.legacy_attachment_id,
588
+ url: fetchData.extensible_attachment.story_attachment.url,
589
+
590
+ title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
591
+ description: fetchData.extensible_attachment.story_attachment.description.text,
592
+ source: fetchData.extensible_attachment.story_attachment.source,
593
+
594
+ image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
595
+ width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
596
+ height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
597
+ playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
598
+ duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
599
+
600
+ subattachments: fetchData.extensible_attachment.subattachments,
601
+ properties: fetchData.extensible_attachment.story_attachment.properties,
602
+ }],
603
+ mentions: {},
604
+ timestamp: parseInt(fetchData.timestamp_precise),
605
+ participantIDs: (fetchData.participants || (fetchData.messageMetadata ? fetchData.messageMetadata.cid ? fetchData.messageMetadata.cid.canonicalParticipantFbids : fetchData.messageMetadata.participantIds : []) || []),
606
+ isGroup: (fetchData.message_sender.id != tid.toString())
607
+ });
608
+ globalCallback(null, {
609
+ type: "message",
610
+ senderID: utils.formatID(fetchData.message_sender.id),
611
+ body: fetchData.message.text || "",
612
+ threadID: utils.formatID(tid.toString()),
613
+ messageID: fetchData.message_id,
614
+ attachments: [{
615
+ type: "share",
616
+ ID: fetchData.extensible_attachment.legacy_attachment_id,
617
+ url: fetchData.extensible_attachment.story_attachment.url,
618
+
619
+ title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
620
+ description: fetchData.extensible_attachment.story_attachment.description.text,
621
+ source: fetchData.extensible_attachment.story_attachment.source,
622
+
623
+ image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
624
+ width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
625
+ height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
626
+ playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
627
+ duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
628
+
629
+ subattachments: fetchData.extensible_attachment.subattachments,
630
+ properties: fetchData.extensible_attachment.story_attachment.properties,
631
+ }],
632
+ mentions: {},
633
+ timestamp: parseInt(fetchData.timestamp_precise),
634
+ participantIDs: (fetchData.participants || (fetchData.messageMetadata ? fetchData.messageMetadata.cid ? fetchData.messageMetadata.cid.canonicalParticipantFbids : fetchData.messageMetadata.participantIds : []) || []),
635
+ isGroup: (fetchData.message_sender.id != tid.toString())
636
+ });
637
+ }
638
+ }
639
+ else log.error("forcedFetch", fetchData);
640
+ })
641
+ .catch((err) => log.error("forcedFetch", err));
642
+ }
643
+ break;
644
+ case "ThreadName":
645
+ case "ParticipantsAddedToGroupThread":
646
+ case "ParticipantLeftGroupThread":
647
+ var formattedEvent;
648
+ try {
649
+ formattedEvent = utils.formatDeltaEvent(v.delta);
650
+ }
651
+ catch (err) {
652
+ return globalCallback({
653
+ error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
654
+ detail: err,
655
+ res: v.delta,
656
+ type: "parse_error"
657
+ });
658
+ }
659
+ return (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID) || !ctx.loggedIn ? undefined : (function () { globalCallback(null, formattedEvent); })();
660
+ }
661
+ }
662
+
663
+ function markDelivery(ctx, api, threadID, messageID) {
664
+ if (threadID && messageID) {
665
+ api.markAsDelivered(threadID, messageID, (err) => {
666
+ if (err) log.error("markAsDelivered", err);
667
+ else {
668
+ if (ctx.globalOptions.autoMarkRead) {
669
+ api.markAsRead(threadID, (err) => {
670
+ if (err) log.error("markAsDelivered", err);
671
+ });
672
+ }
673
+ }
674
+ });
675
+ }
676
+ }
677
+
678
+ module.exports = function (defaultFuncs, api, ctx) {
679
+ let globalCallback = identity;
680
+ // function getSeqID() {
681
+ // ctx.t_mqttCalled = false;
682
+ // async function attemptRequest(retries = 3) {
683
+ // try {
684
+ // if (!ctx.fb_dtsg) {
685
+ // const dtsg = await api.getFreshDtsg();
686
+ // if (!dtsg) {
687
+ // if (retries > 0) {
688
+ // logger.Warning("Failed to get fb_dtsg, retrying...");
689
+ // await utils.sleep(2000); // Longer delay for token retry
690
+ // return attemptRequest(retries - 1);
691
+ // }
692
+ // throw { error: "Could not obtain fb_dtsg after multiple attempts" };
693
+ // }
694
+ // ctx.fb_dtsg = dtsg;
695
+ // }
696
+
697
+ // const form = {
698
+ // av: ctx.userID,
699
+ // fb_dtsg: ctx.fb_dtsg,
700
+ // queries: JSON.stringify({
701
+ // o0: {
702
+ // doc_id: '3336396659757871',
703
+ // query_params: {
704
+ // limit: 1,
705
+ // before: null,
706
+ // tags: ['INBOX'],
707
+ // includeDeliveryReceipts: false,
708
+ // includeSeqID: true
709
+ // }
710
+ // }
711
+ // }),
712
+ // __user: ctx.userID,
713
+ // __a: '1',
714
+ // __req: '8',
715
+ // __hs: '19577.HYP:comet_pkg.2.1..2.1',
716
+ // dpr: '1',
717
+ // fb_api_caller_class: 'RelayModern',
718
+ // fb_api_req_friendly_name: 'MessengerGraphQLThreadlistFetcher'
719
+ // };
720
+
721
+ // const headers = {
722
+ // 'Content-Type': 'application/x-www-form-urlencoded',
723
+ // 'Referer': 'https://www.facebook.com/',
724
+ // 'Origin': 'https://www.facebook.com',
725
+ // 'sec-fetch-site': 'same-origin',
726
+ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
727
+ // 'Cookie': ctx.jar.getCookieString('https://www.facebook.com'),
728
+ // 'accept': '*/*',
729
+ // 'accept-encoding': 'gzip, deflate, br'
730
+ // };
731
+
732
+ // const resData = await defaultFuncs
733
+ // .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form, { headers })
734
+ // .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
735
+
736
+ // if (debugSeq) {
737
+ // console.log('GraphQL SeqID Response:', JSON.stringify(resData, null, 2));
738
+ // }
739
+
740
+ // if (resData.error === 1357004 || resData.error === 1357001) {
741
+ // if (retries > 0) {
742
+ // logger.Warning("Session error, refreshing token and retrying...");
743
+ // ctx.fb_dtsg = null; // Force new token
744
+ // await utils.sleep(2000);
745
+ // return attemptRequest(retries - 1);
746
+ // }
747
+ // throw { error: "Session refresh failed after retries" };
748
+ // }
749
+
750
+ // if (!Array.isArray(resData)) {
751
+ // throw { error: "Invalid response format", res: resData };
752
+ // }
753
+
754
+ // const seqID = resData[0]?.o0?.data?.viewer?.message_threads?.sync_sequence_id;
755
+ // if (!seqID) {
756
+ // throw { error: "Missing sync_sequence_id", res: resData };
757
+ // }
758
+
759
+ // ctx.lastSeqId = seqID;
760
+ // if (debugSeq) {
761
+ // console.log('Got SeqID:', ctx.lastSeqId);
762
+ // }
763
+
764
+ // return listenMqtt(defaultFuncs, api, ctx, globalCallback);
765
+
766
+ // } catch (err) {
767
+ // if (retries > 0) {
768
+ // console.log("Request failed, retrying...");
769
+
770
+ // return attemptRequest(retries - 1);
771
+ // }
772
+ // throw err;
773
+ // }
774
+ // }
775
+
776
+ // return attemptRequest()
777
+ // .catch((err) => {
778
+ // log.error("getSeqId", err);
779
+ // if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
780
+ // return globalCallback(err);
781
+ // });
782
+ // }
783
+
784
+ getSeqID = function getSeqID() {
785
+ ctx.t_mqttCalled = false;
786
+ defaultFuncs
787
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
788
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
789
+ .then((resData) => {
790
+ if (utils.getType(resData) != "Array") throw { error: "Not logged in", res: resData };
791
+ if (resData && resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
792
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "getSeqId: there was no successful_results", res: resData };
793
+ if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
794
+ ctx.lastSeqId = resData[0].o0.data.viewer.message_threads.sync_sequence_id;
795
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
796
+ } else throw { error: "getSeqId: no sync_sequence_id found.", res: resData };
797
+ })
798
+ .catch((err) => {
799
+ log.error("getSeqId", err);
800
+ if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
801
+ return globalCallback(err);
802
+ });
803
+ };
804
+
805
+ return function (callback) {
806
+ class MessageEmitter extends EventEmitter {
807
+ stopListening(callback) {
808
+
809
+ callback = callback || (() => { });
810
+ globalCallback = identity;
811
+ if (ctx.mqttClient) {
812
+ ctx.mqttClient.unsubscribe("/webrtc");
813
+ ctx.mqttClient.unsubscribe("/rtc_multi");
814
+ ctx.mqttClient.unsubscribe("/onevc");
815
+ ctx.mqttClient.publish("/browser_close", "{}");
816
+ ctx.mqttClient.end(false, function (...data) {
817
+ callback(data);
818
+ ctx.mqttClient = undefined;
819
+ });
820
+ }
821
+ }
822
+
823
+ async stopListeningAsync() {
824
+ return new Promise((resolve) => {
825
+ this.stopListening(resolve);
826
+ });
827
+ }
828
+ }
829
+
830
+ const msgEmitter = new MessageEmitter();
831
+ globalCallback = (callback || function (error, message) {
832
+ if (error) {
833
+ return msgEmitter.emit("error", error);
834
+ }
835
+ msgEmitter.emit("message", message);
836
+ });
837
+
838
+ // Reset some stuff
839
+ if (!ctx.firstListen)
840
+ ctx.lastSeqId = null;
841
+ ctx.syncToken = undefined;
842
+ ctx.t_mqttCalled = false;
843
+
844
+ form = {
845
+ "av": ctx.globalOptions.pageID,
846
+ "queries": JSON.stringify({
847
+ "o0": {
848
+ "doc_id": "3336396659757871",
849
+ "query_params": {
850
+ "limit": 1,
851
+ "before": null,
852
+ "tags": ["INBOX"],
853
+ "includeDeliveryReceipts": false,
854
+ "includeSeqID": true
855
+ }
856
+ }
857
+ })
858
+ };
859
+
860
+ if (!ctx.firstListen || !ctx.lastSeqId) {
861
+ getSeqID(defaultFuncs, api, ctx, globalCallback);
862
+ } else {
863
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
864
+ }
865
+
866
+ api.stopListening = msgEmitter.stopListening;
867
+ api.stopListeningAsync = msgEmitter.stopListeningAsync;
868
+ return msgEmitter;
869
+ };
870
+ };