fca-pretest 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. package/.github/workflows/publish.yml +20 -0
  2. package/Extra/Database/index.js +360 -0
  3. package/Extra/ExtraAddons.js +80 -0
  4. package/Extra/ExtraFindUID.js +60 -0
  5. package/Extra/ExtraGetThread.js +118 -0
  6. package/Extra/ExtraScreenShot.js +430 -0
  7. package/Extra/ExtraUptimeRobot.js +26 -0
  8. package/Extra/Html/Classic/script.js +119 -0
  9. package/Extra/Html/Classic/style.css +8 -0
  10. package/Extra/Security/Index.js +146 -0
  11. package/Extra/Security/Step_1.js +11 -0
  12. package/Extra/Security/Step_2.js +20 -0
  13. package/Extra/Security/Step_3.js +20 -0
  14. package/Extra/Src/Change_Environment.js +24 -0
  15. package/Extra/Src/History.js +115 -0
  16. package/Extra/Src/Last-Run.js +65 -0
  17. package/Extra/Src/Premium.js +84 -0
  18. package/Extra/Src/uuid.js +137 -0
  19. package/Func/AcceptAgreement.js +32 -0
  20. package/Func/ClearCache.js +64 -0
  21. package/Func/ReportV1.js +54 -0
  22. package/LICENSE +24 -0
  23. package/Language/index.json +177 -0
  24. package/README.md +1 -0
  25. package/SECURITY.md +17 -0
  26. package/broadcast.js +38 -0
  27. package/gitattributes +2 -0
  28. package/gitignore.txt +10 -0
  29. package/index.js +1898 -0
  30. package/logger.js +66 -0
  31. package/package.json +93 -0
  32. package/src/Dev_Horizon_Data.js +125 -0
  33. package/src/Premium.js +30 -0
  34. package/src/Screenshot.js +85 -0
  35. package/src/addExternalModule.js +16 -0
  36. package/src/addUserToGroup.js +79 -0
  37. package/src/changeAdminStatus.js +79 -0
  38. package/src/changeArchivedStatus.js +41 -0
  39. package/src/changeAvt.js +85 -0
  40. package/src/changeBio.js +65 -0
  41. package/src/changeBlockedStatus.js +36 -0
  42. package/src/changeGroupImage.js +106 -0
  43. package/src/changeNickname.js +45 -0
  44. package/src/changeThreadColor.js +62 -0
  45. package/src/changeThreadEmoji.js +42 -0
  46. package/src/createNewGroup.js +70 -0
  47. package/src/createPoll.js +60 -0
  48. package/src/deleteMessage.js +45 -0
  49. package/src/deleteThread.js +43 -0
  50. package/src/forwardAttachment.js +48 -0
  51. package/src/getAccessToken.js +32 -0
  52. package/src/getCurrentUserID.js +7 -0
  53. package/src/getEmojiUrl.js +27 -0
  54. package/src/getFriendsList.js +73 -0
  55. package/src/getMessage.js +80 -0
  56. package/src/getThreadHistory.js +537 -0
  57. package/src/getThreadInfo.js +348 -0
  58. package/src/getThreadList.js +213 -0
  59. package/src/getThreadMain.js +220 -0
  60. package/src/getThreadPictures.js +59 -0
  61. package/src/getUID.js +59 -0
  62. package/src/getUserID.js +62 -0
  63. package/src/getUserInfo.js +129 -0
  64. package/src/getUserInfoMain.js +65 -0
  65. package/src/getUserInfoV2.js +36 -0
  66. package/src/getUserInfoV3.js +63 -0
  67. package/src/getUserInfoV4.js +55 -0
  68. package/src/getUserInfoV5.js +61 -0
  69. package/src/handleFriendRequest.js +46 -0
  70. package/src/handleMessageRequest.js +49 -0
  71. package/src/httpGet.js +49 -0
  72. package/src/httpPost.js +48 -0
  73. package/src/httpPostFormData.js +41 -0
  74. package/src/listenMqtt.js +676 -0
  75. package/src/logout.js +68 -0
  76. package/src/markAsDelivered.js +48 -0
  77. package/src/markAsRead.js +70 -0
  78. package/src/markAsReadAll.js +43 -0
  79. package/src/markAsSeen.js +51 -0
  80. package/src/muteThread.js +47 -0
  81. package/src/removeUserFromGroup.js +49 -0
  82. package/src/resolvePhotoUrl.js +37 -0
  83. package/src/searchForThread.js +43 -0
  84. package/src/sendMessage.js +334 -0
  85. package/src/sendTypingIndicator.js +80 -0
  86. package/src/setMessageReaction.js +109 -0
  87. package/src/setPostReaction.js +102 -0
  88. package/src/setTitle.js +74 -0
  89. package/src/threadColors.js +39 -0
  90. package/src/unfriend.js +43 -0
  91. package/src/unsendMessage.js +40 -0
  92. package/test/data/shareAttach.js +146 -0
  93. package/test/data/something.mov +0 -0
  94. package/test/data/test.png +0 -0
  95. package/test/data/test.txt +7 -0
  96. package/test/example-config.json +18 -0
  97. package/test/test-page.js +140 -0
  98. package/test/test.js +385 -0
  99. package/test/testv2.js +3 -0
  100. package/utils.js +1648 -0
@@ -0,0 +1,676 @@
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
+
10
+ var identity = function () { };
11
+ var form = {};
12
+ var getSeqID = function () { };
13
+
14
+ var topics = [
15
+ "/legacy_web",
16
+ "/webrtc",
17
+ "/rtc_multi",
18
+ "/onevc",
19
+ "/br_sr", //Notification
20
+ //Need to publish /br_sr right after this
21
+ "/sr_res",
22
+ "/t_ms",
23
+ "/thread_typing",
24
+ "/orca_typing_notifications",
25
+ "/notify_disconnect",
26
+ //Need to publish /messenger_sync_create_queue right after this
27
+ "/orca_presence",
28
+ //Will receive /sr_res right here.
29
+
30
+ "/inbox",
31
+ "/mercury",
32
+ "/messaging_events",
33
+ "/orca_message_notifications",
34
+ "/pp",
35
+ "/webrtc_response",
36
+ ];
37
+
38
+ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
39
+ //Don't really know what this does but I think it's for the active state?
40
+ //TODO: Move to ctx when implemented
41
+ var chatOn = ctx.globalOptions.online;
42
+ var foreground = false;
43
+
44
+ var sessionID = Math.floor(Math.random() * 9007199254740991) + 1;
45
+ var username = {
46
+ u: ctx.userID,
47
+ s: sessionID,
48
+ chat_on: chatOn,
49
+ fg: foreground,
50
+ d: utils.getGUID(),
51
+ ct: "websocket",
52
+ //App id from facebook
53
+ aid: "219994525426954",
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
+ };
64
+ var cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
65
+
66
+ var host;
67
+ if (ctx.mqttEndpoint) host = `${ctx.mqttEndpoint}&sid=${sessionID}`;
68
+ else if (ctx.region) host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLocaleLowerCase()}&sid=${sessionID}`;
69
+ else host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}`;
70
+
71
+ var options = {
72
+ clientId: "mqttwsclient",
73
+ protocolId: 'MQIsdp',
74
+ protocolVersion: 3,
75
+ username: JSON.stringify(username),
76
+ clean: true,
77
+ wsOptions: {
78
+ headers: {
79
+ 'Cookie': cookies,
80
+ 'Origin': 'https://www.facebook.com',
81
+ 'User-Agent': ctx.globalOptions.userAgent,
82
+ 'Referer': 'https://www.facebook.com/',
83
+ 'Host': new URL(host).hostname //'edge-chat.facebook.com'
84
+ },
85
+ origin: 'https://www.facebook.com',
86
+ protocolVersion: 13
87
+ },
88
+ keepalive: 10,
89
+ reschedulePings: false
90
+ };
91
+
92
+ if (typeof ctx.globalOptions.proxy != "undefined") {
93
+ var agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
94
+ options.wsOptions.agent = agent;
95
+ }
96
+
97
+ ctx.mqttClient = new mqtt.Client(_ => websocket(host, options.wsOptions), options);
98
+
99
+ var mqttClient = ctx.mqttClient;
100
+
101
+ mqttClient.on('error', function (err) {
102
+ log.error("listenMqtt", err);
103
+ mqttClient.end();
104
+ if (ctx.globalOptions.autoReconnect) getSeqID();
105
+ else globalCallback({ type: "stop_listen", error: "Connection refused: Server unavailable" }, null);
106
+ });
107
+
108
+ mqttClient.on('connect', function () {
109
+ topics.forEach(topicsub => mqttClient.subscribe(topicsub));
110
+
111
+ var topic;
112
+ var queue = {
113
+ sync_api_version: 10,
114
+ max_deltas_able_to_process: 1000,
115
+ delta_batch_size: 500,
116
+ encoding: "JSON",
117
+ entity_fbid: ctx.userID,
118
+ };
119
+
120
+ if (ctx.syncToken) {
121
+ topic = "/messenger_sync_get_diffs";
122
+ queue.last_seq_id = ctx.lastSeqId;
123
+ queue.sync_token = ctx.syncToken;
124
+ } else {
125
+ topic = "/messenger_sync_create_queue";
126
+ queue.initial_titan_sequence_id = ctx.lastSeqId;
127
+ queue.device_params = null;
128
+ }
129
+
130
+ mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
131
+
132
+ var rTimeout = setTimeout(function () {
133
+ mqttClient.end();
134
+ getSeqID();
135
+ }, 5000);
136
+
137
+ ctx.tmsWait = function () {
138
+ clearTimeout(rTimeout);
139
+ ctx.globalOptions.emitReady ? globalCallback({
140
+ type: "ready",
141
+ error: null
142
+ }) : "";
143
+ delete ctx.tmsWait;
144
+ };
145
+ });
146
+
147
+ mqttClient.on('message', function (topic, message, _packet) {
148
+ try {
149
+ var jsonMessage = JSON.parse(message);
150
+ } catch (ex) {
151
+ return log.error("listenMqtt", ex);
152
+ }
153
+ if (topic === "/t_ms") {
154
+ if (ctx.tmsWait && typeof ctx.tmsWait == "function") ctx.tmsWait();
155
+
156
+ if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
157
+ ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
158
+ ctx.syncToken = jsonMessage.syncToken;
159
+ }
160
+
161
+ if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
162
+
163
+ //If it contains more than 1 delta
164
+ for (var i in jsonMessage.deltas) {
165
+ var delta = jsonMessage.deltas[i];
166
+ parseDelta(defaultFuncs, api, ctx, globalCallback, { "delta": delta });
167
+ }
168
+ } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
169
+ var typ = {
170
+ type: "typ",
171
+ isTyping: !!jsonMessage.state,
172
+ from: jsonMessage.sender_fbid.toString(),
173
+ threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
174
+ };
175
+ (function () { globalCallback(null, typ); })();
176
+ } else if (topic === "/orca_presence") {
177
+ if (!ctx.globalOptions.updatePresence) {
178
+ for (var i in jsonMessage.list) {
179
+ var data = jsonMessage.list[i];
180
+ var userID = data["u"];
181
+
182
+ var presence = {
183
+ type: "presence",
184
+ userID: userID.toString(),
185
+ //Convert to ms
186
+ timestamp: data["l"] * 1000,
187
+ statuses: data["p"]
188
+ };
189
+ (function () { globalCallback(null, presence); })();
190
+ }
191
+ }
192
+ }
193
+
194
+ });
195
+
196
+ mqttClient.on('close', function () {
197
+ //(function () { globalCallback("Connection closed."); })();
198
+ // client.end();
199
+ });
200
+ }
201
+
202
+ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
203
+ if (v.delta.class == "NewMessage") {
204
+ //Not tested for pages
205
+ if (ctx.globalOptions.pageID && ctx.globalOptions.pageID != v.queue) return;
206
+
207
+ (function resolveAttachmentUrl(i) {
208
+ if (v.delta.attachments && (i == v.delta.attachments.length)) {
209
+ var fmtMsg;
210
+ try {
211
+ fmtMsg = utils.formatDeltaMessage(v);
212
+ } catch (err) {
213
+ return globalCallback({
214
+ error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
215
+ detail: err,
216
+ res: v,
217
+ type: "parse_error"
218
+ });
219
+ }
220
+ if (fmtMsg)
221
+ if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
222
+
223
+ return !ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID ? undefined : (function () { globalCallback(null, fmtMsg); })();
224
+ } else {
225
+ if (v.delta.attachments && (v.delta.attachments[i].mercury.attach_type == "photo")) {
226
+ api.resolvePhotoUrl(v.delta.attachments[i].fbid, (err, url) => {
227
+ if (!err) v.delta.attachments[i].mercury.metadata.url = url;
228
+ return resolveAttachmentUrl(i + 1);
229
+ });
230
+ } else return resolveAttachmentUrl(i + 1);
231
+ }
232
+ })(0);
233
+ }
234
+
235
+ if (v.delta.class == "ClientPayload") {
236
+ var clientPayload = utils.decodeClientPayload(v.delta.payload);
237
+ if (clientPayload && clientPayload.deltas) {
238
+ for (var i in clientPayload.deltas) {
239
+ var delta = clientPayload.deltas[i];
240
+ if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
241
+ (function () {
242
+ globalCallback(null, {
243
+ type: "message_reaction",
244
+ threadID: (delta.deltaMessageReaction.threadKey.threadFbId ? delta.deltaMessageReaction.threadKey.threadFbId : delta.deltaMessageReaction.threadKey.otherUserFbId).toString(),
245
+ messageID: delta.deltaMessageReaction.messageId,
246
+ reaction: delta.deltaMessageReaction.reaction,
247
+ senderID: delta.deltaMessageReaction.senderId.toString(),
248
+ userID: delta.deltaMessageReaction.userId.toString()
249
+ });
250
+ })();
251
+ } else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
252
+ (function () {
253
+ globalCallback(null, {
254
+ type: "message_unsend",
255
+ threadID: (delta.deltaRecallMessageData.threadKey.threadFbId ? delta.deltaRecallMessageData.threadKey.threadFbId : delta.deltaRecallMessageData.threadKey.otherUserFbId).toString(),
256
+ messageID: delta.deltaRecallMessageData.messageID,
257
+ senderID: delta.deltaRecallMessageData.senderID.toString(),
258
+ deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
259
+ timestamp: delta.deltaRecallMessageData.timestamp
260
+ });
261
+ })();
262
+ } else if (delta.deltaMessageReply) {
263
+ //Mention block - #1
264
+ var mdata =
265
+ delta.deltaMessageReply.message === undefined ? [] :
266
+ delta.deltaMessageReply.message.data === undefined ? [] :
267
+ delta.deltaMessageReply.message.data.prng === undefined ? [] :
268
+ JSON.parse(delta.deltaMessageReply.message.data.prng);
269
+ var m_id = mdata.map(u => u.i);
270
+ var m_offset = mdata.map(u => u.o);
271
+ var m_length = mdata.map(u => u.l);
272
+
273
+ var mentions = {};
274
+
275
+ 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]);
276
+ //Mention block - 1#
277
+ var callbackToReturn = {
278
+ type: "message_reply",
279
+ threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
280
+ messageID: delta.deltaMessageReply.message.messageMetadata.messageId,
281
+ senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
282
+ attachments: delta.deltaMessageReply.message.attachments.map(function (att) {
283
+ var mercury = JSON.parse(att.mercuryJSON);
284
+ Object.assign(att, mercury);
285
+ return att;
286
+ }).map(att => {
287
+ var x;
288
+ try {
289
+ x = utils._formatAttachment(att);
290
+ } catch (ex) {
291
+ x = att;
292
+ x.error = ex;
293
+ x.type = "unknown";
294
+ }
295
+ return x;
296
+ }),
297
+ args: (delta.deltaMessageReply.message.body || "").trim().split(/\s+/),
298
+ body: (delta.deltaMessageReply.message.body || ""),
299
+ isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
300
+ mentions: mentions,
301
+ timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
302
+ participantIDs: (delta.deltaMessageReply.message.participants || []).map(e => e.toString())
303
+ };
304
+
305
+ if (delta.deltaMessageReply.repliedToMessage) {
306
+ //Mention block - #2
307
+ mdata =
308
+ delta.deltaMessageReply.repliedToMessage === undefined ? [] :
309
+ delta.deltaMessageReply.repliedToMessage.data === undefined ? [] :
310
+ delta.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] :
311
+ JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng);
312
+ m_id = mdata.map(u => u.i);
313
+ m_offset = mdata.map(u => u.o);
314
+ m_length = mdata.map(u => u.l);
315
+
316
+ var rmentions = {};
317
+
318
+ 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]);
319
+ //Mention block - 2#
320
+ callbackToReturn.messageReply = {
321
+ threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
322
+ messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
323
+ senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
324
+ attachments: delta.deltaMessageReply.repliedToMessage.attachments.map(function (att) {
325
+ var mercury = JSON.parse(att.mercuryJSON);
326
+ Object.assign(att, mercury);
327
+ return att;
328
+ }).map(att => {
329
+ var x;
330
+ try {
331
+ x = utils._formatAttachment(att);
332
+ } catch (ex) {
333
+ x = att;
334
+ x.error = ex;
335
+ x.type = "unknown";
336
+ }
337
+ return x;
338
+ }),
339
+ args: (delta.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
340
+ body: delta.deltaMessageReply.repliedToMessage.body || "",
341
+ isGroup: !!delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
342
+ mentions: rmentions,
343
+ timestamp: delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp,
344
+ participantIDs: (delta.deltaMessageReply.repliedToMessage.participants || []).map(e => e.toString())
345
+ };
346
+ } else if (delta.deltaMessageReply.replyToMessageId) {
347
+ return defaultFuncs
348
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
349
+ "av": ctx.globalOptions.pageID,
350
+ "queries": JSON.stringify({
351
+ "o0": {
352
+ //Using the same doc_id as forcedFetch
353
+ "doc_id": "2848441488556444",
354
+ "query_params": {
355
+ "thread_and_message_id": {
356
+ "thread_id": callbackToReturn.threadID,
357
+ "message_id": delta.deltaMessageReply.replyToMessageId.id,
358
+ }
359
+ }
360
+ }
361
+ })
362
+ })
363
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
364
+ .then((resData) => {
365
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
366
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
367
+ var fetchData = resData[0].o0.data.message;
368
+ var mobj = {};
369
+ 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);
370
+
371
+ callbackToReturn.messageReply = {
372
+ threadID: callbackToReturn.threadID,
373
+ messageID: fetchData.message_id,
374
+ senderID: fetchData.message_sender.id.toString(),
375
+ attachments: fetchData.message.blob_attachment.map(att => {
376
+ var x;
377
+ try {
378
+ x = utils._formatAttachment({ blob_attachment: att });
379
+ } catch (ex) {
380
+ x = att;
381
+ x.error = ex;
382
+ x.type = "unknown";
383
+ }
384
+ return x;
385
+ }),
386
+ args: (fetchData.message.text || "").trim().split(/\s+/) || [],
387
+ body: fetchData.message.text || "",
388
+ isGroup: callbackToReturn.isGroup,
389
+ mentions: mobj,
390
+ timestamp: parseInt(fetchData.timestamp_precise)
391
+ };
392
+ })
393
+ .catch(err => log.error("forcedFetch", err))
394
+ .finally(function () {
395
+ if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
396
+ !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID ? undefined : (function () { globalCallback(null, callbackToReturn); })();
397
+ });
398
+ } else callbackToReturn.delta = delta;
399
+
400
+ if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
401
+
402
+ return !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID ? undefined : (function () { globalCallback(null, callbackToReturn); })();
403
+ }
404
+ }
405
+ return;
406
+ }
407
+ }
408
+
409
+ if (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents) return;
410
+ switch (v.delta.class) {
411
+ case "ReadReceipt":
412
+ var fmtMsg;
413
+ try {
414
+ fmtMsg = utils.formatDeltaReadReceipt(v.delta);
415
+ } catch (err) {
416
+ return globalCallback({
417
+ error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
418
+ detail: err,
419
+ res: v.delta,
420
+ type: "parse_error"
421
+ });
422
+ }
423
+ return (function () { globalCallback(null, fmtMsg); })();
424
+ case "AdminTextMessage":
425
+ switch (v.delta.type) {
426
+ case "change_thread_theme":
427
+ case "change_thread_icon":
428
+ case "change_thread_nickname":
429
+ case "change_thread_admins":
430
+ case "change_thread_approval_mode":
431
+ case "group_poll":
432
+ case "messenger_call_log":
433
+ case "participant_joined_group_call":
434
+ var fmtMsg;
435
+ try {
436
+ fmtMsg = utils.formatDeltaEvent(v.delta);
437
+ } catch (err) {
438
+ return globalCallback({
439
+ error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
440
+ detail: err,
441
+ res: v.delta,
442
+ type: "parse_error"
443
+ });
444
+ }
445
+ return (function () { globalCallback(null, fmtMsg); })();
446
+ default:
447
+ return;
448
+ }
449
+ break;
450
+ //For group images
451
+ case "ForcedFetch":
452
+ if (!v.delta.threadKey) return;
453
+ var mid = v.delta.messageId;
454
+ var tid = v.delta.threadKey.threadFbId;
455
+ if (mid && tid) {
456
+ const form = {
457
+ "av": ctx.globalOptions.pageID,
458
+ "queries": JSON.stringify({
459
+ "o0": {
460
+ //This doc_id is valid as of March 25, 2020
461
+ "doc_id": "2848441488556444",
462
+ "query_params": {
463
+ "thread_and_message_id": {
464
+ "thread_id": tid.toString(),
465
+ "message_id": mid,
466
+ }
467
+ }
468
+ }
469
+ })
470
+ };
471
+
472
+ defaultFuncs
473
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
474
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
475
+ .then((resData) => {
476
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
477
+
478
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
479
+
480
+ var fetchData = resData[0].o0.data.message;
481
+
482
+ if (utils.getType(fetchData) == "Object") {
483
+ log.info("forcedFetch", fetchData);
484
+ switch (fetchData.__typename) {
485
+ case "ThreadImageMessage":
486
+ (!ctx.globalOptions.selfListen &&
487
+ fetchData.message_sender.id.toString() === ctx.userID) ||
488
+ !ctx.loggedIn ?
489
+ undefined :
490
+ (function () {
491
+ globalCallback(null, {
492
+ type: "change_thread_image",
493
+ threadID: utils.formatID(tid.toString()),
494
+ snippet: fetchData.snippet,
495
+ timestamp: fetchData.timestamp_precise,
496
+ author: fetchData.message_sender.id,
497
+ image: {
498
+ attachmentID: fetchData.image_with_metadata && fetchData.image_with_metadata.legacy_attachment_id,
499
+ width: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.x,
500
+ height: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.y,
501
+ url: fetchData.image_with_metadata && fetchData.image_with_metadata.preview.uri
502
+ }
503
+ });
504
+ })();
505
+ break;
506
+ case "UserMessage":
507
+ log.info("ff-Return", {
508
+ type: "message",
509
+ senderID: utils.formatID(fetchData.message_sender.id),
510
+ body: fetchData.message.text || "",
511
+ threadID: utils.formatID(tid.toString()),
512
+ messageID: fetchData.message_id,
513
+ attachments: [{
514
+ type: "share",
515
+ ID: fetchData.extensible_attachment.legacy_attachment_id,
516
+ url: fetchData.extensible_attachment.story_attachment.url,
517
+
518
+ title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
519
+ description: fetchData.extensible_attachment.story_attachment.description.text,
520
+ source: fetchData.extensible_attachment.story_attachment.source,
521
+
522
+ image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
523
+ width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
524
+ height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
525
+ playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
526
+ duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
527
+
528
+ subattachments: fetchData.extensible_attachment.subattachments,
529
+ properties: fetchData.extensible_attachment.story_attachment.properties,
530
+ }],
531
+ mentions: {},
532
+ timestamp: parseInt(fetchData.timestamp_precise),
533
+ isGroup: (fetchData.message_sender.id != tid.toString())
534
+ });
535
+ globalCallback(null, {
536
+ type: "message",
537
+ senderID: utils.formatID(fetchData.message_sender.id),
538
+ body: fetchData.message.text || "",
539
+ threadID: utils.formatID(tid.toString()),
540
+ messageID: fetchData.message_id,
541
+ attachments: [{
542
+ type: "share",
543
+ ID: fetchData.extensible_attachment.legacy_attachment_id,
544
+ url: fetchData.extensible_attachment.story_attachment.url,
545
+
546
+ title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
547
+ description: fetchData.extensible_attachment.story_attachment.description.text,
548
+ source: fetchData.extensible_attachment.story_attachment.source,
549
+
550
+ image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
551
+ width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
552
+ height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
553
+ playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
554
+ duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
555
+
556
+ subattachments: fetchData.extensible_attachment.subattachments,
557
+ properties: fetchData.extensible_attachment.story_attachment.properties,
558
+ }],
559
+ mentions: {},
560
+ timestamp: parseInt(fetchData.timestamp_precise),
561
+ isGroup: (fetchData.message_sender.id != tid.toString())
562
+ });
563
+ }
564
+ } else log.error("forcedFetch", fetchData);
565
+ })
566
+ .catch((err) => log.error("forcedFetch", err));
567
+ }
568
+ break;
569
+ case "ThreadName":
570
+ case "ParticipantsAddedToGroupThread":
571
+ case "ParticipantLeftGroupThread":
572
+ var formattedEvent;
573
+ try {
574
+ formattedEvent = utils.formatDeltaEvent(v.delta);
575
+ } catch (err) {
576
+ return globalCallback({
577
+ error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
578
+ detail: err,
579
+ res: v.delta,
580
+ type: "parse_error"
581
+ });
582
+ }
583
+ return (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID) || !ctx.loggedIn ? undefined : (function () { globalCallback(null, formattedEvent); })();
584
+ }
585
+ }
586
+
587
+ function markDelivery(ctx, api, threadID, messageID) {
588
+ if (threadID && messageID) {
589
+ api.markAsDelivered(threadID, messageID, (err) => {
590
+ if (err) log.error("markAsDelivered", err);
591
+ else {
592
+ if (ctx.globalOptions.autoMarkRead) {
593
+ api.markAsRead(threadID, (err) => {
594
+ if (err) log.error("markAsDelivered", err);
595
+ });
596
+ }
597
+ }
598
+ });
599
+ }
600
+ }
601
+
602
+ module.exports = function (defaultFuncs, api, ctx) {
603
+ var globalCallback = identity;
604
+ getSeqID = function getSeqID() {
605
+ ctx.t_mqttCalled = false;
606
+ defaultFuncs
607
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
608
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
609
+ .then((resData) => {
610
+ if (utils.getType(resData) != "Array") throw { error: "Not logged in", res: resData };
611
+ if (resData && resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
612
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "getSeqId: there was no successful_results", res: resData };
613
+ if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
614
+ ctx.lastSeqId = resData[0].o0.data.viewer.message_threads.sync_sequence_id;
615
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
616
+ } else throw { error: "getSeqId: no sync_sequence_id found.", res: resData };
617
+ })
618
+ .catch((err) => {
619
+ log.error("getSeqId", err);
620
+ if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
621
+ return globalCallback(err);
622
+ });
623
+ };
624
+
625
+ return async function (callback) {
626
+ class MessageEmitter extends EventEmitter {
627
+ stopListening(callback) {
628
+ callback = callback || (() => { });
629
+ globalCallback = identity;
630
+ if (ctx.mqttClient) {
631
+ ctx.mqttClient.unsubscribe("/webrtc");
632
+ ctx.mqttClient.unsubscribe("/rtc_multi");
633
+ ctx.mqttClient.unsubscribe("/onevc");
634
+ ctx.mqttClient.publish("/browser_close", "{}");
635
+ ctx.mqttClient.end(false, function (...data) {
636
+ callback(data);
637
+ ctx.mqttClient = undefined;
638
+ });
639
+ }
640
+ }
641
+ }
642
+
643
+ var msgEmitter = new MessageEmitter();
644
+ globalCallback = (callback || function (error, message) {
645
+ if (error) return msgEmitter.emit("error", error);
646
+ msgEmitter.emit("message", message);
647
+ });
648
+
649
+ //Reset some stuff
650
+ if (!ctx.firstListen) ctx.lastSeqId = null;
651
+ ctx.syncToken = undefined;
652
+ ctx.t_mqttCalled = false;
653
+
654
+ //Same request as getThreadList
655
+ form = {
656
+ "av": ctx.globalOptions.pageID,
657
+ "queries": JSON.stringify({
658
+ "o0": {
659
+ "doc_id": "3336396659757871",
660
+ "query_params": {
661
+ "limit": 1,
662
+ "before": null,
663
+ "tags": ["INBOX"],
664
+ "includeDeliveryReceipts": false,
665
+ "includeSeqID": true
666
+ }
667
+ }
668
+ })
669
+ };
670
+
671
+ if (!ctx.firstListen || !ctx.lastSeqId) getSeqID();
672
+ else listenMqtt(defaultFuncs, api, ctx, globalCallback);
673
+ ctx.firstListen = false;
674
+ return msgEmitter;
675
+ };
676
+ };