rapido-fca 0.0.1 → 0.0.2

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