@dongdev/fca-unofficial 2.0.6 → 2.0.8

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 (74) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/func/checkUpdate.js +9 -9
  3. package/func/logger.js +40 -104
  4. package/module/login.js +4 -4
  5. package/module/loginHelper.js +3 -3
  6. package/module/options.js +5 -9
  7. package/package.json +6 -6
  8. package/src/api/action/addExternalModule.js +5 -5
  9. package/src/api/action/changeAvatar.js +11 -10
  10. package/src/api/action/changeBio.js +7 -8
  11. package/src/api/action/getCurrentUserID.js +1 -1
  12. package/src/api/action/handleFriendRequest.js +5 -5
  13. package/src/api/action/logout.js +9 -8
  14. package/src/api/action/refreshFb_dtsg.js +17 -12
  15. package/src/api/action/setPostReaction.js +10 -11
  16. package/src/api/action/unfriend.js +3 -4
  17. package/src/api/http/httpGet.js +7 -8
  18. package/src/api/http/httpPost.js +7 -8
  19. package/src/api/http/postFormData.js +6 -5
  20. package/src/api/messaging/addUserToGroup.js +0 -1
  21. package/src/api/messaging/changeAdminStatus.js +108 -89
  22. package/src/api/messaging/changeArchivedStatus.js +6 -6
  23. package/src/api/messaging/changeBlockedStatus.js +3 -4
  24. package/src/api/messaging/changeGroupImage.js +72 -117
  25. package/src/api/messaging/changeNickname.js +59 -48
  26. package/src/api/messaging/changeThreadColor.js +61 -47
  27. package/src/api/messaging/changeThreadEmoji.js +106 -0
  28. package/src/api/messaging/createNewGroup.js +5 -5
  29. package/src/api/messaging/createPoll.js +36 -63
  30. package/src/api/messaging/deleteMessage.js +4 -4
  31. package/src/api/messaging/deleteThread.js +4 -4
  32. package/src/api/messaging/forwardAttachment.js +38 -47
  33. package/src/api/messaging/getFriendsList.js +5 -6
  34. package/src/api/messaging/getMessage.js +4 -9
  35. package/src/api/messaging/handleMessageRequest.js +5 -5
  36. package/src/api/messaging/markAsDelivered.js +5 -5
  37. package/src/api/messaging/markAsRead.js +7 -7
  38. package/src/api/messaging/markAsReadAll.js +3 -4
  39. package/src/api/messaging/markAsSeen.js +7 -7
  40. package/src/api/messaging/muteThread.js +3 -4
  41. package/src/api/messaging/removeUserFromGroup.js +82 -56
  42. package/src/api/messaging/resolvePhotoUrl.js +2 -3
  43. package/src/api/messaging/searchForThread.js +2 -3
  44. package/src/api/messaging/sendMessage.js +171 -101
  45. package/src/api/messaging/sendMessageMqtt.js +14 -12
  46. package/src/api/messaging/sendTypingIndicator.js +11 -11
  47. package/src/api/messaging/setMessageReaction.js +68 -82
  48. package/src/api/messaging/setTitle.js +77 -48
  49. package/src/api/messaging/shareContact.js +2 -4
  50. package/src/api/messaging/threadColors.js +0 -3
  51. package/src/api/messaging/unsendMessage.js +74 -37
  52. package/src/api/messaging/uploadAttachment.js +11 -9
  53. package/src/api/socket/core/connectMqtt.js +180 -0
  54. package/src/api/socket/core/getSeqID.js +25 -0
  55. package/src/api/socket/core/getTaskResponseData.js +22 -0
  56. package/src/api/socket/core/markDelivery.js +12 -0
  57. package/src/api/socket/core/parseDelta.js +351 -0
  58. package/src/api/socket/detail/buildStream.js +176 -68
  59. package/src/api/socket/detail/constants.js +24 -0
  60. package/src/api/socket/listenMqtt.js +80 -1005
  61. package/src/api/{messaging → threads}/getThreadHistory.js +5 -22
  62. package/src/api/threads/getThreadInfo.js +35 -248
  63. package/src/api/threads/getThreadList.js +20 -20
  64. package/src/api/threads/getThreadPictures.js +3 -4
  65. package/src/api/users/getUserID.js +5 -6
  66. package/src/api/users/getUserInfo.js +305 -73
  67. package/src/api/users/getUserInfoV2.js +134 -0
  68. package/src/database/models/user.js +32 -0
  69. package/src/database/userData.js +89 -0
  70. package/src/utils/constants.js +12 -2
  71. package/src/utils/format.js +1051 -0
  72. package/src/utils/request.js +75 -7
  73. package/src/api/threads/changeThreadEmoji.js +0 -55
  74. package/src/utils/index.js +0 -1497
@@ -1,6 +1,8 @@
1
- const utils = require("../../utils");
1
+ "use strict";
2
2
  const log = require("npmlog");
3
-
3
+ const { parseAndCheckLogin } = require("../../utils/client");
4
+ const { getType } = require("../../utils/format");
5
+ const { isReadableStream } = require("../../utils/constants");
4
6
  module.exports = function(defaultFuncs, api, ctx) {
5
7
  function upload(attachments, callback) {
6
8
  callback = callback || function() {};
@@ -8,11 +10,11 @@ module.exports = function(defaultFuncs, api, ctx) {
8
10
 
9
11
  // create an array of promises
10
12
  for (let i = 0; i < attachments.length; i++) {
11
- if (!utils.isReadableStream(attachments[i])) {
13
+ if (!isReadableStream(attachments[i])) {
12
14
  throw {
13
15
  error:
14
16
  "Attachment should be a readable stream and not " +
15
- utils.getType(attachments[i]) +
17
+ getType(attachments[i]) +
16
18
  "."
17
19
  };
18
20
  }
@@ -30,7 +32,7 @@ module.exports = function(defaultFuncs, api, ctx) {
30
32
  form,
31
33
  {}
32
34
  )
33
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
35
+ .then(parseAndCheckLogin(ctx, defaultFuncs))
34
36
  .then(function(resData) {
35
37
  if (resData.error) {
36
38
  throw resData;
@@ -57,9 +59,9 @@ module.exports = function(defaultFuncs, api, ctx) {
57
59
  return function uploadAttachment(attachments, callback) {
58
60
  if (
59
61
  !attachments &&
60
- !utils.isReadableStream(attachments) &&
61
- !utils.getType(attachments) === "Array" &&
62
- utils.getType(attachments) === "Array" && !attachments.length
62
+ !isReadableStream(attachments) &&
63
+ !getType(attachments) === "Array" &&
64
+ getType(attachments) === "Array" && !attachments.length
63
65
  )
64
66
  throw { error: "Please pass an attachment or an array of attachments." };
65
67
 
@@ -79,7 +81,7 @@ module.exports = function(defaultFuncs, api, ctx) {
79
81
  };
80
82
  }
81
83
 
82
- if (utils.getType(attachments) !== "Array") attachments = [attachments];
84
+ if (getType(attachments) !== "Array") attachments = [attachments];
83
85
 
84
86
  upload(attachments, (err, info) => {
85
87
  if (err) {
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ const { formatID } = require("../../../utils/format");
3
+ module.exports = function createListenMqtt(deps) {
4
+ const { WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy, topics, parseDelta, getTaskResponseData, logger } = deps;
5
+ return function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
6
+ const chatOn = ctx.globalOptions.online;
7
+ const foreground = false;
8
+ const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
9
+ const username = {
10
+ u: ctx.userID,
11
+ s: sessionID,
12
+ chat_on: chatOn,
13
+ fg: foreground,
14
+ d: ctx.clientId,
15
+ ct: "websocket",
16
+ aid: 219994525426954,
17
+ aids: null,
18
+ mqtt_sid: "",
19
+ cp: 3,
20
+ ecp: 10,
21
+ st: [],
22
+ pm: [],
23
+ dc: "",
24
+ no_auto_fg: true,
25
+ gas: null,
26
+ pack: [],
27
+ p: null,
28
+ php_override: ""
29
+ };
30
+ const cookies = api.getCookies();
31
+ let host;
32
+ if (ctx.mqttEndpoint) {
33
+ host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${ctx.clientId}`;
34
+ } else if (ctx.region) {
35
+ host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${ctx.clientId}`;
36
+ } else {
37
+ host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${ctx.clientId}`;
38
+ }
39
+ const options = {
40
+ clientId: "mqttwsclient",
41
+ protocolId: "MQIsdp",
42
+ protocolVersion: 3,
43
+ username: JSON.stringify(username),
44
+ clean: true,
45
+ wsOptions: {
46
+ headers: {
47
+ Cookie: cookies,
48
+ Origin: "https://www.facebook.com",
49
+ "User-Agent": ctx.globalOptions.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
50
+ Referer: "https://www.facebook.com/",
51
+ Host: "edge-chat.facebook.com",
52
+ Connection: "Upgrade",
53
+ Pragma: "no-cache",
54
+ "Cache-Control": "no-cache",
55
+ Upgrade: "websocket",
56
+ "Sec-WebSocket-Version": "13",
57
+ "Accept-Encoding": "gzip, deflate, br",
58
+ "Accept-Language": "vi,en;q=0.9",
59
+ "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
60
+ },
61
+ origin: "https://www.facebook.com",
62
+ protocolVersion: 13,
63
+ binaryType: "arraybuffer"
64
+ },
65
+ keepalive: 30,
66
+ reschedulePings: true,
67
+ reconnectPeriod: 1000,
68
+ connectTimeout: 5000
69
+ };
70
+ if (ctx.globalOptions.proxy !== undefined) {
71
+ const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
72
+ options.wsOptions.agent = agent;
73
+ }
74
+ ctx.mqttClient = new mqtt.Client(() => buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()), options);
75
+ const mqttClient = ctx.mqttClient;
76
+ global.mqttClient = mqttClient;
77
+ mqttClient.on("error", function (err) {
78
+ logger("listenMqtt" + err, "error");
79
+ mqttClient.end();
80
+ if (ctx.globalOptions.autoReconnect) {
81
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
82
+ } else {
83
+ // utils.checkLiveCookie(ctx, defaultFuncs).then(res => {
84
+ // globalCallback({ type: "stop_listen", error: "Connection refused: Server unavailable" }, null);
85
+ // }).catch(err => {
86
+ // globalCallback({ type: "account_inactive", error: "Maybe your account is blocked by facebook, please login and check at https://facebook.com" }, null);
87
+ // });
88
+ }
89
+ });
90
+ mqttClient.on("connect", function () {
91
+ if (process.env.OnStatus === undefined) {
92
+ logger("fca-unoffcial premium", "info");
93
+ process.env.OnStatus = true;
94
+ }
95
+ topics.forEach(topicsub => mqttClient.subscribe(topicsub));
96
+ let topic;
97
+ const queue = { sync_api_version: 11, max_deltas_able_to_process: 100, delta_batch_size: 500, encoding: "JSON", entity_fbid: ctx.userID, initial_titan_sequence_id: ctx.lastSeqId, device_params: null };
98
+ if (ctx.syncToken) {
99
+ topic = "/messenger_sync_get_diffs";
100
+ queue.last_seq_id = ctx.lastSeqId;
101
+ queue.sync_token = ctx.syncToken;
102
+ } else {
103
+ topic = "/messenger_sync_create_queue";
104
+ queue.initial_titan_sequence_id = ctx.lastSeqId;
105
+ queue.device_params = null;
106
+ }
107
+ mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
108
+ mqttClient.publish("/foreground_state", JSON.stringify({ foreground: chatOn }), { qos: 1 });
109
+ mqttClient.publish("/set_client_settings", JSON.stringify({ make_user_available_when_in_foreground: true }), { qos: 1 });
110
+ const rTimeout = setTimeout(function () {
111
+ mqttClient.end();
112
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
113
+ }, 5000);
114
+ ctx.tmsWait = function () {
115
+ clearTimeout(rTimeout);
116
+ ctx.globalOptions.emitReady ? globalCallback({ type: "ready", error: null }) : "";
117
+ delete ctx.tmsWait;
118
+ };
119
+ });
120
+ mqttClient.on("message", function (topic, message, _packet) {
121
+ try {
122
+ let jsonMessage = Buffer.isBuffer(message) ? Buffer.from(message).toString() : message;
123
+ try {
124
+ jsonMessage = JSON.parse(jsonMessage);
125
+ } catch (e) {
126
+ jsonMessage = {};
127
+ }
128
+ if (jsonMessage.type === "jewel_requests_add") {
129
+ globalCallback(null, { type: "friend_request_received", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
130
+ } else if (jsonMessage.type === "jewel_requests_remove_old") {
131
+ globalCallback(null, { type: "friend_request_cancel", actorFbId: jsonMessage.from.toString(), timestamp: Date.now().toString() });
132
+ } else if (topic === "/t_ms") {
133
+ if (ctx.tmsWait && typeof ctx.tmsWait == "function") {
134
+ ctx.tmsWait();
135
+ }
136
+ if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
137
+ ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
138
+ ctx.syncToken = jsonMessage.syncToken;
139
+ }
140
+ if (jsonMessage.lastIssuedSeqId) {
141
+ ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
142
+ }
143
+ for (const i in jsonMessage.deltas) {
144
+ const delta = jsonMessage.deltas[i];
145
+ parseDelta(defaultFuncs, api, ctx, globalCallback, { delta });
146
+ }
147
+ } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
148
+ const typ = { type: "typ", isTyping: !!jsonMessage.state, from: jsonMessage.sender_fbid.toString(), threadID: formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString()) };
149
+ globalCallback(null, typ);
150
+ } else if (topic === "/orca_presence") {
151
+ if (!ctx.globalOptions.updatePresence) {
152
+ for (const i in jsonMessage.list) {
153
+ const data = jsonMessage.list[i];
154
+ const userID = data["u"];
155
+ const presence = { type: "presence", userID: userID.toString(), timestamp: data["l"] * 1000, statuses: data["p"] };
156
+ globalCallback(null, presence);
157
+ }
158
+ }
159
+ } else if (topic == "/ls_resp") {
160
+ const parsedPayload = JSON.parse(jsonMessage.payload);
161
+ const reqID = jsonMessage.request_id;
162
+ if (ctx["tasks"].has(reqID)) {
163
+ const taskData = ctx["tasks"].get(reqID);
164
+ const { type: taskType, callback: taskCallback } = taskData;
165
+ const taskRespData = getTaskResponseData(taskType, parsedPayload);
166
+ if (taskRespData == null) {
167
+ taskCallback("error", null);
168
+ } else {
169
+ taskCallback(null, Object.assign({ type: taskType, reqID: reqID }, taskRespData));
170
+ }
171
+ }
172
+ }
173
+ } catch (ex) {
174
+ return;
175
+ }
176
+ });
177
+ mqttClient.on("close", function () {});
178
+ mqttClient.on("disconnect", () => {});
179
+ };
180
+ };
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ const { getType } = require("../../../utils/format");
3
+ const { parseAndCheckLogin } = require("../../../utils/client");
4
+ module.exports = function createGetSeqID(deps) {
5
+ const { listenMqtt } = deps;
6
+ return function getSeqID(defaultFuncs, api, ctx, globalCallback, form) {
7
+ ctx.t_mqttCalled = false;
8
+ return defaultFuncs.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form).then(parseAndCheckLogin(ctx, defaultFuncs)).then(resData => {
9
+ if (getType(resData) !== "Array") throw { error: "Not logged in", res: resData };
10
+ if (!Array.isArray(resData) || !resData.length) return;
11
+ const lastRes = resData[resData.length - 1];
12
+ if (lastRes && lastRes.successful_results === 0) return;
13
+ const syncSeqId = resData[0] && resData[0].o0 && resData[0].o0.data && resData[0].o0.data.viewer && resData[0].o0.data.viewer.message_threads && resData[0].o0.data.viewer.message_threads.sync_sequence_id;
14
+ if (syncSeqId) {
15
+ ctx.lastSeqId = syncSeqId;
16
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
17
+ } else {
18
+ throw { error: "getSeqId: no sync_sequence_id found.", res: resData };
19
+ }
20
+ }).catch(err => {
21
+ if (getType(err) === "Object" && err.error === "Not logged in") ctx.loggedIn = false;
22
+ return globalCallback(err);
23
+ });
24
+ };
25
+ };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ module.exports = function getTaskResponseData(taskType, payload) {
3
+ try {
4
+ switch (taskType) {
5
+ case "send_message_mqtt":
6
+ return {
7
+ type: taskType,
8
+ threadID: payload.step[1][2][2][1][2],
9
+ messageID: payload.step[1][2][2][1][3],
10
+ payload: payload.step[1][2]
11
+ };
12
+ case "set_message_reaction":
13
+ return { mid: payload.step[1][2][2][1][4] };
14
+ case "edit_message":
15
+ return { mid: payload.step[1][2][2][1][2] };
16
+ default:
17
+ return null;
18
+ }
19
+ } catch (e) {
20
+ return null;
21
+ }
22
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ module.exports = function markDelivery(ctx, api, threadID, messageID) {
3
+ if (threadID && messageID) {
4
+ api.markAsDelivered(threadID, messageID, err => {
5
+ if (err) {} else {
6
+ if (ctx.globalOptions.autoMarkRead) {
7
+ api.markAsRead(threadID, err2 => {});
8
+ }
9
+ }
10
+ });
11
+ }
12
+ };
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ const { formatDeltaEvent, formatMessage, _formatAttachment, formatDeltaMessage, formatDeltaReadReceipt, formatID, getType, decodeClientPayload } = require("../../../utils/format");
3
+ module.exports = function createParseDelta(deps) {
4
+ const { markDelivery, parseAndCheckLogin } = deps;
5
+ return function parseDelta(defaultFuncs, api, ctx, globalCallback, { delta }) {
6
+ if (delta.class === "NewMessage") {
7
+ const resolveAttachmentUrl = i => {
8
+ if (!delta.attachments || i === delta.attachments.length || getType(delta.attachments) !== "Array") {
9
+ let fmtMsg;
10
+ try {
11
+ fmtMsg = formatDeltaMessage(delta);
12
+ } catch (err) {
13
+ return;
14
+ }
15
+ if (fmtMsg) {
16
+ if (ctx.globalOptions.autoMarkDelivery) {
17
+ markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
18
+ }
19
+ if (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID) return;
20
+ globalCallback(null, fmtMsg);
21
+ }
22
+ } else {
23
+ const attachment = delta.attachments[i];
24
+ if (attachment.mercury.attach_type === "photo") {
25
+ api.resolvePhotoUrl(attachment.fbid, (err, url) => {
26
+ if (!err) attachment.mercury.metadata.url = url;
27
+ resolveAttachmentUrl(i + 1);
28
+ });
29
+ } else {
30
+ resolveAttachmentUrl(i + 1);
31
+ }
32
+ }
33
+ };
34
+ resolveAttachmentUrl(0);
35
+ } else if (delta.class === "ClientPayload") {
36
+ const clientPayload = decodeClientPayload(delta.payload);
37
+ if (clientPayload && clientPayload.deltas) {
38
+ for (const d of clientPayload.deltas) {
39
+ if (d.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
40
+ const messageReaction = {
41
+ type: "message_reaction",
42
+ threadID: (d.deltaMessageReaction.threadKey.threadFbId ? d.deltaMessageReaction.threadKey.threadFbId : d.deltaMessageReaction.threadKey.otherUserFbId).toString(),
43
+ messageID: d.deltaMessageReaction.messageId,
44
+ reaction: d.deltaMessageReaction.reaction,
45
+ senderID: d.deltaMessageReaction.senderId.toString(),
46
+ userID: d.deltaMessageReaction.userId.toString()
47
+ };
48
+ globalCallback(null, messageReaction);
49
+ } else if (d.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
50
+ const messageUnsend = {
51
+ type: "message_unsend",
52
+ threadID: (d.deltaRecallMessageData.threadKey.threadFbId ? d.deltaRecallMessageData.threadKey.threadFbId : d.deltaRecallMessageData.threadKey.otherUserFbId).toString(),
53
+ messageID: d.deltaRecallMessageData.messageID,
54
+ senderID: d.deltaRecallMessageData.senderID.toString(),
55
+ deletionTimestamp: d.deltaRecallMessageData.deletionTimestamp,
56
+ timestamp: d.deltaRecallMessageData.timestamp
57
+ };
58
+ globalCallback(null, messageUnsend);
59
+ } else if (d.deltaMessageReply) {
60
+ const mdata = d.deltaMessageReply.message === undefined ? [] : d.deltaMessageReply.message.data === undefined ? [] : d.deltaMessageReply.message.data.prng === undefined ? [] : JSON.parse(d.deltaMessageReply.message.data.prng);
61
+ const m_id = mdata.map(u => u.i);
62
+ const m_offset = mdata.map(u => u.o);
63
+ const m_length = mdata.map(u => u.l);
64
+ const mentions = {};
65
+ for (let i = 0; i < m_id.length; i++) {
66
+ mentions[m_id[i]] = (d.deltaMessageReply.message.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
67
+ }
68
+ const callbackToReturn = {
69
+ type: "message_reply",
70
+ threadID: (d.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? d.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : d.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
71
+ messageID: d.deltaMessageReply.message.messageMetadata.messageId,
72
+ senderID: d.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
73
+ attachments: (d.deltaMessageReply.message.attachments || []).map(att => {
74
+ const mercury = JSON.parse(att.mercuryJSON);
75
+ Object.assign(att, mercury);
76
+ return att;
77
+ }).map(att => {
78
+ let x;
79
+ try {
80
+ x = _formatAttachment(att);
81
+ } catch (ex) {
82
+ x = att;
83
+ x.error = ex;
84
+ x.type = "unknown";
85
+ }
86
+ return x;
87
+ }),
88
+ args: (d.deltaMessageReply.message.body || "").trim().split(/\s+/),
89
+ body: d.deltaMessageReply.message.body || "",
90
+ isGroup: !!d.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
91
+ mentions,
92
+ timestamp: parseInt(d.deltaMessageReply.message.messageMetadata.timestamp),
93
+ participantIDs: (d.deltaMessageReply.message.participants || []).map(e => e.toString())
94
+ };
95
+ if (d.deltaMessageReply.repliedToMessage) {
96
+ const mdata2 = d.deltaMessageReply.repliedToMessage === undefined ? [] : d.deltaMessageReply.repliedToMessage.data === undefined ? [] : d.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] : JSON.parse(d.deltaMessageReply.repliedToMessage.data.prng);
97
+ const m_id2 = mdata2.map(u => u.i);
98
+ const m_offset2 = mdata2.map(u => u.o);
99
+ const m_length2 = mdata2.map(u => u.l);
100
+ const rmentions = {};
101
+ for (let i = 0; i < m_id2.length; i++) {
102
+ rmentions[m_id2[i]] = (d.deltaMessageReply.repliedToMessage.body || "").substring(m_offset2[i], m_offset2[i] + m_length2[i]);
103
+ }
104
+ callbackToReturn.messageReply = {
105
+ threadID: (d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
106
+ messageID: d.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
107
+ senderID: d.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
108
+ attachments: d.deltaMessageReply.repliedToMessage.attachments.map(att => {
109
+ let mercury;
110
+ try {
111
+ mercury = JSON.parse(att.mercuryJSON);
112
+ Object.assign(att, mercury);
113
+ } catch (ex) {
114
+ mercury = {};
115
+ }
116
+ return att;
117
+ }).map(att => {
118
+ let x;
119
+ try {
120
+ x = _formatAttachment(att);
121
+ } catch (ex) {
122
+ x = att;
123
+ x.error = ex;
124
+ x.type = "unknown";
125
+ }
126
+ return x;
127
+ }),
128
+ args: (d.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
129
+ body: d.deltaMessageReply.repliedToMessage.body || "",
130
+ isGroup: !!d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
131
+ mentions: rmentions,
132
+ timestamp: parseInt(d.deltaMessageReply.repliedToMessage.messageMetadata.timestamp),
133
+ participantIDs: (d.deltaMessageReply.repliedToMessage.participants || []).map(e => e.toString())
134
+ };
135
+ } else if (d.deltaMessageReply.replyToMessageId) {
136
+ return defaultFuncs.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
137
+ av: ctx.globalOptions.pageID,
138
+ queries: JSON.stringify({
139
+ o0: {
140
+ doc_id: "2848441488556444",
141
+ query_params: {
142
+ thread_and_message_id: {
143
+ thread_id: callbackToReturn.threadID,
144
+ message_id: d.deltaMessageReply.replyToMessageId.id
145
+ }
146
+ }
147
+ }
148
+ })
149
+ }).then(parseAndCheckLogin(ctx, defaultFuncs)).then(resData => {
150
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
151
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
152
+ const fetchData = resData[0].o0.data.message;
153
+ const mobj = {};
154
+ for (const n in fetchData.message.ranges) {
155
+ mobj[fetchData.message.ranges[n].entity.id] = (fetchData.message.text || "").substr(fetchData.message.ranges[n].offset, fetchData.message.ranges[n].length);
156
+ }
157
+ callbackToReturn.messageReply = {
158
+ type: "Message",
159
+ threadID: callbackToReturn.threadID,
160
+ messageID: fetchData.message_id,
161
+ senderID: fetchData.message_sender.id.toString(),
162
+ attachments: fetchData.message.blob_attachment.map(att => _formatAttachment({ blob_attachment: att })),
163
+ args: (fetchData.message.text || "").trim().split(/\s+/) || [],
164
+ body: fetchData.message.text || "",
165
+ isGroup: callbackToReturn.isGroup,
166
+ mentions: mobj,
167
+ timestamp: parseInt(fetchData.timestamp_precise)
168
+ };
169
+ }).catch(err => {}).finally(() => {
170
+ if (ctx.globalOptions.autoMarkDelivery) {
171
+ markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
172
+ }
173
+ if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
174
+ globalCallback(null, callbackToReturn);
175
+ });
176
+ } else {
177
+ callbackToReturn.delta = d;
178
+ }
179
+ if (ctx.globalOptions.autoMarkDelivery) {
180
+ markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
181
+ }
182
+ if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
183
+ globalCallback(null, callbackToReturn);
184
+ }
185
+ }
186
+ return;
187
+ }
188
+ }
189
+ switch (delta.class) {
190
+ case "ReadReceipt": {
191
+ let fmtMsg;
192
+ try {
193
+ fmtMsg = formatDeltaReadReceipt(delta);
194
+ } catch (err) {
195
+ return;
196
+ }
197
+ globalCallback(null, fmtMsg);
198
+ break;
199
+ }
200
+ case "AdminTextMessage": {
201
+ switch (delta.type) {
202
+ case "instant_game_dynamic_custom_update":
203
+ case "accept_pending_thread":
204
+ case "confirm_friend_request":
205
+ case "shared_album_delete":
206
+ case "shared_album_addition":
207
+ case "pin_messages_v2":
208
+ case "unpin_messages_v2":
209
+ case "change_thread_theme":
210
+ case "change_thread_nickname":
211
+ case "change_thread_icon":
212
+ case "change_thread_quick_reaction":
213
+ case "change_thread_admins":
214
+ case "group_poll":
215
+ case "joinable_group_link_mode_change":
216
+ case "magic_words":
217
+ case "change_thread_approval_mode":
218
+ case "messenger_call_log":
219
+ case "participant_joined_group_call":
220
+ case "rtc_call_log":
221
+ case "update_vote": {
222
+ let fmtMsg;
223
+ try {
224
+ fmtMsg = formatDeltaEvent(delta);
225
+ } catch (err) {
226
+ return;
227
+ }
228
+ globalCallback(null, fmtMsg);
229
+ break;
230
+ }
231
+ }
232
+ break;
233
+ }
234
+ case "ForcedFetch": {
235
+ if (!delta.threadKey) return;
236
+ const mid = delta.messageId;
237
+ const tid = delta.threadKey.threadFbId;
238
+ if (mid && tid) {
239
+ const form = {
240
+ av: ctx.globalOptions.pageID,
241
+ queries: JSON.stringify({
242
+ o0: {
243
+ doc_id: "2848441488556444",
244
+ query_params: {
245
+ thread_and_message_id: {
246
+ thread_id: tid.toString(),
247
+ message_id: mid
248
+ }
249
+ }
250
+ }
251
+ })
252
+ };
253
+ defaultFuncs.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form).then(parseAndCheckLogin(ctx, defaultFuncs)).then(resData => {
254
+ if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
255
+ if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
256
+ const fetchData = resData[0].o0.data.message;
257
+ if (getType(fetchData) === "Object") {
258
+ switch (fetchData.__typename) {
259
+ case "ThreadImageMessage":
260
+ if ((!ctx.globalOptions.selfListen && fetchData.message_sender.id.toString() === ctx.userID) || !ctx.loggedIn) {} else {
261
+ globalCallback(null, {
262
+ type: "event",
263
+ threadID: formatID(tid.toString()),
264
+ logMessageType: "log:thread-image",
265
+ logMessageData: {
266
+ image: {
267
+ attachmentID: fetchData.image_with_metadata && fetchData.image_with_metadata.legacy_attachment_id,
268
+ width: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.x,
269
+ height: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.y,
270
+ url: fetchData.image_with_metadata && fetchData.image_with_metadata.preview.uri
271
+ }
272
+ },
273
+ logMessageBody: fetchData.snippet,
274
+ timestamp: fetchData.timestamp_precise,
275
+ author: fetchData.message_sender.id
276
+ });
277
+ }
278
+ break;
279
+ case "UserMessage": {
280
+ const event = {
281
+ type: "message",
282
+ senderID: formatID(fetchData.message_sender.id),
283
+ body: fetchData.message.text || "",
284
+ threadID: formatID(tid.toString()),
285
+ messageID: fetchData.message_id,
286
+ attachments: [
287
+ {
288
+ type: "share",
289
+ ID: fetchData.extensible_attachment.legacy_attachment_id,
290
+ url: fetchData.extensible_attachment.story_attachment.url,
291
+ title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
292
+ description: fetchData.extensible_attachment.story_attachment.description.text,
293
+ source: fetchData.extensible_attachment.story_attachment.source,
294
+ image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
295
+ width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
296
+ height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
297
+ playable: ((fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false),
298
+ duration: ((fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0),
299
+ subattachments: fetchData.extensible_attachment.subattachments,
300
+ properties: fetchData.extensible_attachment.story_attachment.properties
301
+ }
302
+ ],
303
+ mentions: {},
304
+ timestamp: parseInt(fetchData.timestamp_precise),
305
+ isGroup: fetchData.message_sender.id !== tid.toString()
306
+ };
307
+ globalCallback(null, event);
308
+ break;
309
+ }
310
+ default:
311
+ break;
312
+ }
313
+ } else {
314
+ return;
315
+ }
316
+ }).catch(err => {});
317
+ }
318
+ break;
319
+ }
320
+ case "ThreadName":
321
+ case "ParticipantsAddedToGroupThread":
322
+ case "ParticipantLeftGroupThread": {
323
+ let formattedEvent;
324
+ try {
325
+ formattedEvent = formatDeltaEvent(delta);
326
+ } catch (err) {
327
+ return;
328
+ }
329
+ if (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID) return;
330
+ if (!ctx.loggedIn) return;
331
+ globalCallback(null, formattedEvent);
332
+ break;
333
+ }
334
+ case "NewMessage": {
335
+ const hasLiveLocation = d => {
336
+ const attachment = d.attachments && d.attachments[0] && d.attachments[0].mercury && d.attachments[0].mercury.extensible_attachment;
337
+ const storyAttachment = attachment && attachment.story_attachment;
338
+ return storyAttachment && storyAttachment.style_list && storyAttachment.style_list.includes("message_live_location");
339
+ };
340
+ if (delta.attachments && delta.attachments.length === 1 && hasLiveLocation(delta)) {
341
+ delta.class = "UserLocation";
342
+ try {
343
+ const fmtMsg = formatDeltaEvent(delta);
344
+ globalCallback(null, fmtMsg);
345
+ } catch (err) {}
346
+ }
347
+ break;
348
+ }
349
+ }
350
+ };
351
+ };