@cexy/hoonfca 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +83 -0
  2. package/index.js +467 -0
  3. package/package.json +12 -0
  4. package/replit.nix +3 -0
  5. package/src/addExternalModule.js +25 -0
  6. package/src/addUserToGroup.js +98 -0
  7. package/src/changeAdminStatus.js +98 -0
  8. package/src/changeArchivedStatus.js +51 -0
  9. package/src/changeAvatar.js +89 -0
  10. package/src/changeBio.js +74 -0
  11. package/src/changeBlockedStatus.js +45 -0
  12. package/src/changeBlockedStatusMqtt.js +65 -0
  13. package/src/changeCover.js +92 -0
  14. package/src/changeGroupImage.js +110 -0
  15. package/src/changeName.js +106 -0
  16. package/src/changeNickname.js +58 -0
  17. package/src/changeThreadColor.js +62 -0
  18. package/src/changeThreadEmoji.js +54 -0
  19. package/src/changeUsername.js +80 -0
  20. package/src/createCommentPost.js +196 -0
  21. package/src/createNewGroup.js +81 -0
  22. package/src/createPoll.js +64 -0
  23. package/src/createPost.js +285 -0
  24. package/src/data/getThreadInfo.json +1 -0
  25. package/src/deleteMessage.js +55 -0
  26. package/src/deleteThread.js +55 -0
  27. package/src/editMessage.js +66 -0
  28. package/src/follow.js +71 -0
  29. package/src/forwardAttachment.js +57 -0
  30. package/src/getAccess.js +130 -0
  31. package/src/getAvatarUser.js +88 -0
  32. package/src/getBotInitialData.js +59 -0
  33. package/src/getCtx.js +5 -0
  34. package/src/getCurrentUserID.js +5 -0
  35. package/src/getEmojiUrl.js +18 -0
  36. package/src/getFriendsList.js +81 -0
  37. package/src/getMessage.js +827 -0
  38. package/src/getOptions.js +5 -0
  39. package/src/getRegion.js +7 -0
  40. package/src/getThreadHistory.js +496 -0
  41. package/src/getThreadHistoryDeprecated.js +95 -0
  42. package/src/getThreadInfo.js +194 -0
  43. package/src/getThreadInfoDeprecated.js +77 -0
  44. package/src/getThreadList.js +219 -0
  45. package/src/getThreadListDeprecated.js +86 -0
  46. package/src/getThreadPictures.js +85 -0
  47. package/src/getUserID.js +67 -0
  48. package/src/getUserInfo.js +89 -0
  49. package/src/handleFriendRequest.js +56 -0
  50. package/src/handleMessageRequest.js +65 -0
  51. package/src/httpGet.js +53 -0
  52. package/src/httpPost.js +53 -0
  53. package/src/httpPostFormData.js +58 -0
  54. package/src/listenMqtt.js +683 -0
  55. package/src/listenNotification.js +96 -0
  56. package/src/logout.js +77 -0
  57. package/src/markAsDelivered.js +53 -0
  58. package/src/markAsRead.js +74 -0
  59. package/src/markAsReadAll.js +46 -0
  60. package/src/markAsSeen.js +57 -0
  61. package/src/muteThread.js +49 -0
  62. package/src/pinMessage.js +60 -0
  63. package/src/refreshFb_dtsg.js +75 -0
  64. package/src/removeUserFromGroup.js +59 -0
  65. package/src/resolvePhotoUrl.js +47 -0
  66. package/src/searchForThread.js +55 -0
  67. package/src/searchStickers.js +59 -0
  68. package/src/sendMessage.js +444 -0
  69. package/src/sendMessageMqtt.js +326 -0
  70. package/src/sendTypingIndicator.js +54 -0
  71. package/src/setMessageReaction.js +126 -0
  72. package/src/setMessageReactionMqtt.js +74 -0
  73. package/src/setPostReaction.js +103 -0
  74. package/src/setProfileGuard.js +66 -0
  75. package/src/setStoryReaction.js +97 -0
  76. package/src/setTitle.js +100 -0
  77. package/src/shareContact.js +101 -0
  78. package/src/shareLink.js +105 -0
  79. package/src/stopListenMqtt.js +47 -0
  80. package/src/threadColors.js +119 -0
  81. package/src/unfriend.js +58 -0
  82. package/src/unsendMessage.js +51 -0
  83. package/src/uploadAttachment.js +98 -0
  84. package/utils.js +919 -0
@@ -0,0 +1,683 @@
1
+ "use strict";
2
+
3
+ const utils = require("../utils");
4
+ const mqtt = require('mqtt');
5
+ const websocket = require('websocket-stream');
6
+ const HttpsProxyAgent = require('https-proxy-agent');
7
+ const EventEmitter = require('events');
8
+
9
+ const identity = () => {};
10
+ let form = {};
11
+ let getSeqID = () => {};
12
+
13
+ const topics = [
14
+ "/legacy_web",
15
+ "/webrtc",
16
+ "/rtc_multi",
17
+ "/onevc",
18
+ "/br_sr",
19
+ "/sr_res",
20
+ "/t_ms",
21
+ "/thread_typing",
22
+ "/orca_typing_notifications",
23
+ "/notify_disconnect",
24
+ "/orca_presence",
25
+ "/inbox",
26
+ "/mercury",
27
+ "/messaging_events",
28
+ "/orca_message_notifications",
29
+ "/pp",
30
+ "/webrtc_response",
31
+ ];
32
+
33
+ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
34
+ const chatOn = ctx.globalOptions.online;
35
+ const foreground = false;
36
+ const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
37
+ const GUID = utils.getGUID();
38
+
39
+ const username = {
40
+ u: ctx.userID,
41
+ s: sessionID,
42
+ chat_on: chatOn,
43
+ fg: foreground,
44
+ d: GUID,
45
+ ct: 'websocket',
46
+ aid: '219994525426954',
47
+ mqtt_sid: '',
48
+ cp: 3,
49
+ ecp: 10,
50
+ st: [],
51
+ pm: [],
52
+ dc: '',
53
+ no_auto_fg: true,
54
+ gas: null,
55
+ pack: [],
56
+ a: ctx.globalOptions.userAgent,
57
+ p: null,
58
+ aids: null, // ডুপ্লিকেট 'aids' কী-টি রিমুভ করা হয়েছে
59
+ php_override: ""
60
+ };
61
+
62
+ const cookies = ctx.jar.getCookies('https://www.facebook.com').join('; ');
63
+ let host;
64
+ if (ctx.mqttEndpoint) {
65
+ host = `${ctx.mqttEndpoint}&sid=${sessionID}`;
66
+ } else if (ctx.region) {
67
+ host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}`;
68
+ } else {
69
+ host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}`;
70
+ }
71
+
72
+ const options = {
73
+ clientId: 'mqttwsclient',
74
+ protocolId: 'MQIsdp',
75
+ protocolVersion: 3,
76
+ username: JSON.stringify(username),
77
+ clean: true,
78
+ wsOptions: {
79
+ headers: {
80
+ Cookie: cookies,
81
+ Origin: 'https://www.facebook.com',
82
+ Referer: 'https://www.facebook.com/',
83
+ "User-Agent": username.a,
84
+ Host: new URL(host).hostname,
85
+ },
86
+ origin: 'https://www.facebook.com',
87
+ protocolVersion: 13,
88
+ binaryType: 'arraybuffer',
89
+ },
90
+ keepalive: 10,
91
+ reschedulePings: true,
92
+ connectTimeout: 10 * 1000,
93
+ reconnectPeriod: 1 * 1000,
94
+ };
95
+
96
+ if (typeof ctx.globalOptions.proxy !== "undefined") {
97
+ const 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
+ let mqttClient = ctx.mqttClient;
103
+
104
+ function stopListening() {
105
+ if (mqttClient) {
106
+ mqttClient.unsubscribe("/webrtc");
107
+ mqttClient.unsubscribe("/rtc_multi");
108
+ mqttClient.unsubscribe("/onevc");
109
+ mqttClient.publish("/browser_close", "{}");
110
+ mqttClient.end(false, () => {
111
+ ctx.mqttClient = null;
112
+ mqttClient = null;
113
+ });
114
+ }
115
+ }
116
+
117
+ mqttClient.on('error', err => {
118
+ stopListening();
119
+ if (ctx.globalOptions.autoReconnect) {
120
+ getSeqID();
121
+ } else {
122
+ globalCallback({ type: "stop_listen", error: "Connection refused: Server unavailable" }, null);
123
+ }
124
+ utils.warn("Error detected. Will relogin automatically...");
125
+ api.ws3?.relogin(); // অপশনাল চেইনিং
126
+ });
127
+
128
+ mqttClient.on('connect', () => {
129
+ topics.forEach(topicsub => mqttClient.subscribe(topicsub));
130
+ let topic;
131
+ const queue = {
132
+ sync_api_version: 10,
133
+ max_deltas_able_to_process: 1000,
134
+ delta_batch_size: 500,
135
+ encoding: "JSON",
136
+ entity_fbid: ctx.userID,
137
+ };
138
+
139
+ if (ctx.syncToken) {
140
+ topic = "/messenger_sync_get_diffs";
141
+ queue.last_seq_id = ctx.lastSeqId;
142
+ queue.sync_token = ctx.syncToken;
143
+ } else {
144
+ topic = "/messenger_sync_create_queue";
145
+ queue.initial_titan_sequence_id = ctx.lastSeqId;
146
+ queue.device_params = null;
147
+ }
148
+
149
+ mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
150
+ mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
151
+ mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
152
+
153
+ const rTimeout = setTimeout(() => {
154
+ mqttClient.end();
155
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
156
+ }, 3000);
157
+
158
+ ctx.tmsWait = () => {
159
+ clearTimeout(rTimeout);
160
+ if (ctx.globalOptions.emitReady) {
161
+ globalCallback({ type: "ready", error: null });
162
+ }
163
+ delete ctx.tmsWait;
164
+ };
165
+ });
166
+
167
+ mqttClient.on('message', (topic, message, _packet) => {
168
+ let jsonMessage;
169
+ try {
170
+ jsonMessage = JSON.parse(message);
171
+ } catch (ex) {
172
+ return utils.error("listenMqtt", ex);
173
+ }
174
+
175
+ if (topic === "/t_ms") {
176
+ if (ctx.tmsWait && typeof ctx.tmsWait === "function") ctx.tmsWait();
177
+ if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
178
+ ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
179
+ ctx.syncToken = jsonMessage.syncToken;
180
+ }
181
+ if (jsonMessage.lastIssuedSeqId) {
182
+ ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
183
+ }
184
+
185
+ for (const i in jsonMessage.deltas) {
186
+ const delta = jsonMessage.deltas[i];
187
+ parseDelta(defaultFuncs, api, ctx, globalCallback, { "delta": delta });
188
+ }
189
+ } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
190
+ const typ = {
191
+ type: "typ",
192
+ isTyping: !!jsonMessage.state,
193
+ from: jsonMessage.sender_fbid.toString(),
194
+ threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
195
+ };
196
+ globalCallback(null, typ);
197
+ } else if (topic === "/orca_presence") {
198
+ if (!ctx.globalOptions.updatePresence) {
199
+ for (const i in jsonMessage.list) {
200
+ const data = jsonMessage.list[i];
201
+ const userID = data["u"];
202
+ const presence = {
203
+ type: "presence",
204
+ userID: userID.toString(),
205
+ timestamp: data["l"] * 1000,
206
+ statuses: data["p"]
207
+ };
208
+ globalCallback(null, presence);
209
+ }
210
+ }
211
+ }
212
+ });
213
+
214
+ mqttClient.on('close', () => {});
215
+ mqttClient.on('disconnect', () => {});
216
+ }
217
+
218
+ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
219
+ if (v.delta.class === "NewMessage") {
220
+ if (ctx.globalOptions.pageID && ctx.globalOptions.pageID != v.queue) return;
221
+
222
+ (function resolveAttachmentUrl(i) {
223
+ if (v.delta.attachments && (i === v.delta.attachments.length)) {
224
+ let fmtMsg;
225
+ try {
226
+ fmtMsg = utils.formatDeltaMessage(v);
227
+ } catch (err) {
228
+ return globalCallback({
229
+ error: "Problem parsing message object.",
230
+ detail: err,
231
+ res: v,
232
+ type: "parse_error"
233
+ });
234
+ }
235
+ if (fmtMsg && ctx.globalOptions.autoMarkDelivery) {
236
+ markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
237
+ }
238
+
239
+ return !ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID
240
+ ? undefined
241
+ : globalCallback(null, fmtMsg);
242
+ } else {
243
+ if (v.delta.attachments && (v.delta.attachments[i].mercury?.attach_type === "photo")) {
244
+ api.resolvePhotoUrl(v.delta.attachments[i].fbid, (err, url) => {
245
+ if (!err) v.delta.attachments[i].mercury.metadata.url = url;
246
+ return resolveAttachmentUrl(i + 1);
247
+ });
248
+ } else {
249
+ return resolveAttachmentUrl(i + 1);
250
+ }
251
+ }
252
+ })(0);
253
+ }
254
+
255
+ if (v.delta.class === "ClientPayload") {
256
+ const clientPayload = utils.decodeClientPayload(v.delta.payload);
257
+ if (clientPayload && clientPayload.deltas) {
258
+ for (const i in clientPayload.deltas) {
259
+ const delta = clientPayload.deltas[i];
260
+ if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
261
+ globalCallback(null, {
262
+ type: "message_reaction",
263
+ threadID: (delta.deltaMessageReaction.threadKey.threadFbId ? delta.deltaMessageReaction.threadKey.threadFbId : delta.deltaMessageReaction.threadKey.otherUserFbId).toString(),
264
+ messageID: delta.deltaMessageReaction.messageId,
265
+ reaction: delta.deltaMessageReaction.reaction,
266
+ senderID: delta.deltaMessageReaction.senderId.toString(),
267
+ userID: delta.deltaMessageReaction.userId.toString()
268
+ });
269
+ } else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
270
+ globalCallback(null, {
271
+ type: "message_unsend",
272
+ threadID: (delta.deltaRecallMessageData.threadKey.threadFbId ? delta.deltaRecallMessageData.threadKey.threadFbId : delta.deltaRecallMessageData.threadKey.otherUserFbId).toString(),
273
+ messageID: delta.deltaRecallMessageData.messageID,
274
+ senderID: delta.deltaRecallMessageData.senderID.toString(),
275
+ deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
276
+ timestamp: delta.deltaRecallMessageData.timestamp
277
+ });
278
+ } else if (delta.deltaMessageReply) {
279
+ const mdata = delta.deltaMessageReply.message?.data?.prng
280
+ ? JSON.parse(delta.deltaMessageReply.message.data.prng)
281
+ : [];
282
+ const m_id = mdata.map(u => u.i);
283
+ const m_offset = mdata.map(u => u.o);
284
+ const m_length = mdata.map(u => u.l);
285
+ const mentions = {};
286
+
287
+ for (let i = 0; i < m_id.length; i++) {
288
+ mentions[m_id[i]] = (delta.deltaMessageReply.message.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
289
+ }
290
+
291
+ const callbackToReturn = {
292
+ type: "message_reply",
293
+ threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
294
+ messageID: delta.deltaMessageReply.message.messageMetadata.messageId,
295
+ senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
296
+ attachments: (delta.deltaMessageReply.message.attachments || []).map(att => {
297
+ const mercury = JSON.parse(att.mercuryJSON);
298
+ Object.assign(att, mercury);
299
+ return att;
300
+ }).map(att => {
301
+ let x;
302
+ try {
303
+ x = utils._formatAttachment(att);
304
+ } catch (ex) {
305
+ x = att;
306
+ x.error = ex;
307
+ x.type = "unknown";
308
+ }
309
+ return x;
310
+ }),
311
+ args: (delta.deltaMessageReply.message.body || "").trim().split(/\s+/),
312
+ body: (delta.deltaMessageReply.message.body || ""),
313
+ isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
314
+ mentions,
315
+ timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
316
+ participantIDs: (delta.deltaMessageReply.message.participants || []).map(e => e.toString())
317
+ };
318
+
319
+ if (delta.deltaMessageReply.repliedToMessage) {
320
+ const r_mdata = delta.deltaMessageReply.repliedToMessage?.data?.prng
321
+ ? JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng)
322
+ : [];
323
+ const r_m_id = r_mdata.map(u => u.i);
324
+ const r_m_offset = r_mdata.map(u => u.o);
325
+ const r_m_length = r_mdata.map(u => u.l);
326
+ const rmentions = {};
327
+
328
+ for (let i = 0; i < r_m_id.length; i++) {
329
+ rmentions[r_m_id[i]] = (delta.deltaMessageReply.repliedToMessage.body || "").substring(r_m_offset[i], r_m_offset[i] + r_m_length[i]);
330
+ }
331
+
332
+ callbackToReturn.messageReply = {
333
+ threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
334
+ messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
335
+ senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
336
+ attachments: (delta.deltaMessageReply.repliedToMessage.attachments || []).map(att => {
337
+ const mercury = JSON.parse(att.mercuryJSON);
338
+ Object.assign(att, mercury);
339
+ return att;
340
+ }).map(att => {
341
+ let x;
342
+ try {
343
+ x = utils._formatAttachment(att);
344
+ } catch (ex) {
345
+ x = att;
346
+ x.error = ex;
347
+ x.type = "unknown";
348
+ }
349
+ return x;
350
+ }),
351
+ args: (delta.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
352
+ body: delta.deltaMessageReply.repliedToMessage.body || "",
353
+ isGroup: !!delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
354
+ mentions: rmentions,
355
+ timestamp: delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp,
356
+ participantIDs: (delta.deltaMessageReply.repliedToMessage.participants || []).map(e => e.toString())
357
+ };
358
+ } else if (delta.deltaMessageReply.replyToMessageId) {
359
+ return defaultFuncs
360
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
361
+ "av": ctx.globalOptions.pageID,
362
+ "queries": JSON.stringify({
363
+ "o0": {
364
+ "doc_id": "2848441488556444",
365
+ "query_params": {
366
+ "thread_and_message_id": {
367
+ "thread_id": callbackToReturn.threadID,
368
+ "message_id": delta.deltaMessageReply.replyToMessageId.id,
369
+ }
370
+ }
371
+ }
372
+ })
373
+ })
374
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
375
+ .then(resData => {
376
+ if (resData[resData.length - 1].error_results > 0) {
377
+ throw resData[0].o0.errors;
378
+ }
379
+ if (resData[resData.length - 1].successful_results === 0) {
380
+ throw { error: "forcedFetch: there was no successful_results", res: resData };
381
+ }
382
+
383
+ const fetchData = resData[0].o0.data.message;
384
+ const mobj = {};
385
+ for (const n in fetchData.message?.ranges) {
386
+ mobj[fetchData.message.ranges[n].entity.id] = (fetchData.message.text || "").substr(fetchData.message.ranges[n].offset, fetchData.message.ranges[n].length);
387
+ }
388
+
389
+ callbackToReturn.messageReply = {
390
+ threadID: callbackToReturn.threadID,
391
+ messageID: fetchData.message_id,
392
+ senderID: fetchData.message_sender?.id?.toString(),
393
+ attachments: (fetchData.message?.blob_attachment || []).map(att => {
394
+ let x;
395
+ try {
396
+ x = utils._formatAttachment({ blob_attachment: att });
397
+ } catch (ex) {
398
+ x = att;
399
+ x.error = ex;
400
+ x.type = "unknown";
401
+ }
402
+ return x;
403
+ }),
404
+ args: (fetchData.message?.text || "").trim().split(/\s+/) || [],
405
+ body: fetchData.message?.text || "",
406
+ isGroup: callbackToReturn.isGroup,
407
+ mentions: mobj,
408
+ timestamp: parseInt(fetchData.timestamp_precise),
409
+ };
410
+ })
411
+ .catch(err => utils.error("forcedFetch", err))
412
+ .finally(() => {
413
+ if (ctx.globalOptions.autoMarkDelivery) {
414
+ markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
415
+ }
416
+ return !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID
417
+ ? undefined
418
+ : globalCallback(null, callbackToReturn);
419
+ });
420
+ } else {
421
+ callbackToReturn.delta = delta;
422
+ }
423
+
424
+ if (ctx.globalOptions.autoMarkDelivery) {
425
+ markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
426
+ }
427
+ return !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID
428
+ ? undefined
429
+ : globalCallback(null, callbackToReturn);
430
+ }
431
+ }
432
+ return;
433
+ }
434
+ }
435
+
436
+ if (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents) return;
437
+
438
+ switch (v.delta.class) {
439
+ case "ReadReceipt": {
440
+ let fmtMsg;
441
+ try {
442
+ fmtMsg = utils.formatDeltaReadReceipt(v.delta);
443
+ } catch (err) {
444
+ return globalCallback({
445
+ error: "Problem parsing message object.",
446
+ detail: err,
447
+ res: v.delta,
448
+ type: "parse_error"
449
+ });
450
+ }
451
+ return globalCallback(null, fmtMsg);
452
+ }
453
+ case "AdminTextMessage": {
454
+ switch (v.delta.type) {
455
+ case "change_thread_theme":
456
+ case "change_thread_nickname":
457
+ case "change_thread_admins":
458
+ case "change_thread_approval_mode":
459
+ case "joinable_group_link_mode_change":
460
+ case "rtc_call_log":
461
+ case "group_poll":
462
+ case "update_vote":
463
+ case "magic_words":
464
+ case "messenger_call_log":
465
+ case "participant_joined_group_call": {
466
+ let fmtMsg;
467
+ try {
468
+ fmtMsg = utils.formatDeltaEvent(v.delta);
469
+ } catch (err) {
470
+ return globalCallback({
471
+ error: "Problem parsing message object.",
472
+ detail: err,
473
+ res: v.delta,
474
+ type: "parse_error"
475
+ });
476
+ }
477
+ return globalCallback(null, fmtMsg);
478
+ }
479
+ default:
480
+ return;
481
+ }
482
+ }
483
+ case "ForcedFetch": {
484
+ if (!v.delta.threadKey) return;
485
+ const mid = v.delta.messageId;
486
+ const tid = v.delta.threadKey.threadFbId;
487
+
488
+ if (mid && tid) {
489
+ const fetchForm = {
490
+ "av": ctx.globalOptions.pageID,
491
+ "queries": JSON.stringify({
492
+ "o0": {
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, fetchForm)
506
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
507
+ .then(resData => {
508
+ if (resData[resData.length - 1].error_results > 0) {
509
+ throw resData[0].o0.errors;
510
+ }
511
+
512
+ if (resData[resData.length - 1].successful_results === 0) {
513
+ throw { error: "forcedFetch: there was no successful_results", res: resData };
514
+ }
515
+
516
+ const fetchData = resData[0].o0.data.message;
517
+
518
+ if (utils.getType(fetchData) === "Object") {
519
+ utils.log("forcedFetch", fetchData);
520
+ switch (fetchData.__typename) {
521
+ case "ThreadImageMessage":
522
+ if ((!ctx.globalOptions.selfListen && fetchData.message_sender?.id?.toString() === ctx.userID) || !ctx.loggedIn) {
523
+ return;
524
+ }
525
+ globalCallback(null, {
526
+ type: "change_thread_image",
527
+ threadID: utils.formatID(tid.toString()),
528
+ snippet: fetchData.snippet,
529
+ timestamp: fetchData.timestamp_precise,
530
+ author: fetchData.message_sender?.id,
531
+ image: {
532
+ attachmentID: fetchData.image_with_metadata?.legacy_attachment_id,
533
+ width: fetchData.image_with_metadata?.original_dimensions?.x,
534
+ height: fetchData.image_with_metadata?.original_dimensions?.y,
535
+ url: fetchData.image_with_metadata?.preview?.uri
536
+ }
537
+ });
538
+ break;
539
+ case "UserMessage":
540
+ globalCallback(null, {
541
+ type: "message",
542
+ senderID: utils.formatID(fetchData.message_sender?.id),
543
+ body: fetchData.message?.text || "",
544
+ threadID: utils.formatID(tid.toString()),
545
+ messageID: fetchData.message_id,
546
+ attachments: [{
547
+ type: "share",
548
+ ID: fetchData.extensible_attachment?.legacy_attachment_id,
549
+ url: fetchData.extensible_attachment?.story_attachment?.url,
550
+ title: fetchData.extensible_attachment?.story_attachment?.title_with_entities?.text,
551
+ description: fetchData.extensible_attachment?.story_attachment?.description?.text,
552
+ source: fetchData.extensible_attachment?.story_attachment?.source,
553
+ image: fetchData.extensible_attachment?.story_attachment?.media?.image?.uri,
554
+ width: fetchData.extensible_attachment?.story_attachment?.media?.image?.width,
555
+ height: fetchData.extensible_attachment?.story_attachment?.media?.image?.height,
556
+ playable: fetchData.extensible_attachment?.story_attachment?.media?.is_playable || false,
557
+ duration: fetchData.extensible_attachment?.story_attachment?.media?.playable_duration_in_ms || 0,
558
+ subattachments: fetchData.extensible_attachment?.subattachments,
559
+ properties: fetchData.extensible_attachment?.story_attachment?.properties,
560
+ }],
561
+ mentions: {},
562
+ timestamp: parseInt(fetchData.timestamp_precise),
563
+ isGroup: (fetchData.message_sender?.id !== tid.toString())
564
+ });
565
+ break;
566
+ }
567
+ } else {
568
+ utils.error("forcedFetch", fetchData);
569
+ }
570
+ })
571
+ .catch(err => utils.error("forcedFetch", err));
572
+ }
573
+ break;
574
+ }
575
+ case "ThreadName":
576
+ case "ParticipantsAddedToGroupThread":
577
+ case "ParticipantLeftGroupThread": {
578
+ let formattedEvent;
579
+ try {
580
+ formattedEvent = utils.formatDeltaEvent(v.delta);
581
+ } catch (err) {
582
+ return globalCallback({
583
+ error: "Problem parsing message object.",
584
+ detail: err,
585
+ res: v.delta,
586
+ type: "parse_error"
587
+ });
588
+ }
589
+ return (!ctx.globalOptions.selfListen && formattedEvent.author?.toString() === ctx.userID) || !ctx.loggedIn
590
+ ? undefined
591
+ : globalCallback(null, formattedEvent);
592
+ }
593
+ }
594
+ }
595
+
596
+ function markDelivery(ctx, api, threadID, messageID) {
597
+ if (threadID && messageID) {
598
+ api.markAsDelivered(threadID, messageID, err => {
599
+ if (err) {
600
+ utils.error("markAsDelivered", err);
601
+ } else {
602
+ if (ctx.globalOptions.autoMarkRead) {
603
+ api.markAsRead(threadID, readErr => {
604
+ if (readErr) utils.error("markAsDelivered", readErr);
605
+ });
606
+ }
607
+ }
608
+ });
609
+ }
610
+ }
611
+
612
+ module.exports = function(defaultFuncs, api, ctx) {
613
+ let globalCallback = identity;
614
+
615
+ getSeqID = function getSeqID() {
616
+ ctx.t_mqttCalled = false;
617
+ defaultFuncs
618
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
619
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
620
+ .then(resData => {
621
+ if (utils.getType(resData) !== "Array") {
622
+ throw { error: "Not logged in", res: resData };
623
+ }
624
+ if (resData[resData.length - 1].error_results > 0) {
625
+ throw resData[0].o0.errors;
626
+ }
627
+ if (resData[resData.length - 1].successful_results === 0) {
628
+ throw { error: "getSeqId: there was no successful_results", res: resData };
629
+ }
630
+ if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
631
+ ctx.lastSeqId = resData[0].o0.data.viewer.message_threads.sync_sequence_id;
632
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
633
+ } else {
634
+ throw { error: "getSeqId: no sync_sequence_id found.", res: resData };
635
+ }
636
+ })
637
+ .catch(err => {
638
+ if (utils.getType(err) === "Object" && err.error === "Not logged in") {
639
+ ctx.loggedIn = false;
640
+ }
641
+ return globalCallback(err);
642
+ });
643
+ };
644
+
645
+ return async (callback) => {
646
+ const msgEmitter = new EventEmitter();
647
+ globalCallback = callback || ((error, message) => {
648
+ if (error) return msgEmitter.emit("error", error);
649
+ msgEmitter.emit("message", message);
650
+ });
651
+
652
+ if (!ctx.firstListen) {
653
+ ctx.lastSeqId = null;
654
+ }
655
+ ctx.syncToken = undefined;
656
+ ctx.t_mqttCalled = false;
657
+
658
+ form = {
659
+ "av": ctx.globalOptions.pageID,
660
+ "queries": JSON.stringify({
661
+ "o0": {
662
+ "doc_id": "3336396659757871",
663
+ "query_params": {
664
+ "limit": 1,
665
+ "before": null,
666
+ "tags": ["INBOX"],
667
+ "includeDeliveryReceipts": false,
668
+ "includeSeqID": true
669
+ }
670
+ }
671
+ })
672
+ };
673
+
674
+ if (!ctx.firstListen || !ctx.lastSeqId) {
675
+ getSeqID();
676
+ } else {
677
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
678
+ }
679
+
680
+ ctx.firstListen = false;
681
+ return msgEmitter;
682
+ };
683
+ };