nayan-remake-api 0.0.1-security → 3.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.

Potentially problematic release.


This version of nayan-remake-api might be problematic. Click here for more details.

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