alicezetion 1.6.6 → 1.6.7

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