@cexy/hoonfca 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/.travis.yml +6 -0
  2. package/LICENSE-MIT +21 -0
  3. package/index.d.ts +620 -0
  4. package/index.js +778 -1
  5. package/package.json +31 -84
  6. package/src/{apis/addExternalModule.js → addExternalModule.js} +4 -5
  7. package/src/addUserToGroup.js +113 -0
  8. package/src/changeAdminStatus.js +95 -0
  9. package/src/changeApprovalMode.js +79 -0
  10. package/src/changeArchivedStatus.js +55 -0
  11. package/src/changeBio.js +76 -0
  12. package/src/changeBlockedStatus.js +47 -0
  13. package/src/changeBlockedStatusMqtt.js +86 -0
  14. package/src/changeGroupImage.js +133 -0
  15. package/src/changeNickname.js +59 -0
  16. package/src/changeThreadColor.js +71 -0
  17. package/src/{apis/changeThreadEmoji.js → changeThreadEmoji.js} +8 -6
  18. package/src/createNewGroup.js +88 -0
  19. package/src/createPoll.js +130 -0
  20. package/src/deleteMessage.js +56 -0
  21. package/src/deleteThread.js +56 -0
  22. package/src/editMessage.js +63 -0
  23. package/src/forwardAttachment.js +60 -0
  24. package/src/forwardMessage.js +63 -0
  25. package/src/getCurrentUserID.js +7 -0
  26. package/src/getEmojiUrl.js +29 -0
  27. package/src/getFriendsList.js +84 -0
  28. package/src/getGroupsList.js +83 -0
  29. package/src/getMarketplace.js +84 -0
  30. package/src/getNotifications.js +86 -0
  31. package/src/getPagesList.js +83 -0
  32. package/src/getStories.js +88 -0
  33. package/src/getThreadHistory.js +647 -0
  34. package/src/getThreadInfo.js +222 -0
  35. package/src/getThreadList.js +292 -0
  36. package/src/getThreadPictures.js +79 -0
  37. package/src/getUserID.js +66 -0
  38. package/src/getUserInfo.js +128 -0
  39. package/src/handleFriendRequest.js +57 -0
  40. package/src/handleMessageRequest.js +65 -0
  41. package/src/{apis/httpGet.js → httpGet.js} +15 -20
  42. package/src/httpPost.js +58 -0
  43. package/src/listenMqtt.js +1018 -0
  44. package/src/logout.js +75 -0
  45. package/src/markAsDelivered.js +58 -0
  46. package/src/{apis/markAsRead.js → markAsRead.js} +19 -30
  47. package/src/markAsReadAll.js +50 -0
  48. package/src/markAsSeen.js +61 -0
  49. package/src/muteThread.js +52 -0
  50. package/src/pinMessage.js +61 -0
  51. package/src/removeUserFromGroup.js +79 -0
  52. package/src/resolvePhotoUrl.js +45 -0
  53. package/src/searchForThread.js +53 -0
  54. package/src/searchUsers.js +89 -0
  55. package/src/sendComment.js +161 -0
  56. package/src/sendMessage.js +457 -0
  57. package/src/sendTypingIndicator.js +179 -0
  58. package/src/setBio.js +86 -0
  59. package/src/setMessageReaction.js +186 -0
  60. package/src/setPostReaction.js +71 -0
  61. package/src/setTheme.js +313 -0
  62. package/src/setTitle.js +90 -0
  63. package/src/shareContact.js +53 -0
  64. package/src/shareLink.js +62 -0
  65. package/src/threadColors.js +57 -0
  66. package/src/unfriend.js +53 -0
  67. package/src/unsendMessage.js +104 -0
  68. package/utils.js +1447 -0
  69. package/LICENSE +0 -3
  70. package/README.md +0 -491
  71. package/examples/login-with-cookies.js +0 -102
  72. package/examples/verify.js +0 -70
  73. package/src/apis/addUserToGroup.js +0 -108
  74. package/src/apis/changeAdminStatus.js +0 -148
  75. package/src/apis/changeArchivedStatus.js +0 -61
  76. package/src/apis/changeAvatar.js +0 -103
  77. package/src/apis/changeBio.js +0 -69
  78. package/src/apis/changeBlockedStatus.js +0 -54
  79. package/src/apis/changeGroupImage.js +0 -136
  80. package/src/apis/changeThreadColor.js +0 -116
  81. package/src/apis/comment.js +0 -207
  82. package/src/apis/createAITheme.js +0 -129
  83. package/src/apis/createNewGroup.js +0 -79
  84. package/src/apis/createPoll.js +0 -73
  85. package/src/apis/deleteMessage.js +0 -44
  86. package/src/apis/deleteThread.js +0 -52
  87. package/src/apis/e2ee.js +0 -17
  88. package/src/apis/editMessage.js +0 -70
  89. package/src/apis/emoji.js +0 -124
  90. package/src/apis/fetchThemeData.js +0 -82
  91. package/src/apis/follow.js +0 -81
  92. package/src/apis/forwardMessage.js +0 -52
  93. package/src/apis/friend.js +0 -243
  94. package/src/apis/gcmember.js +0 -122
  95. package/src/apis/gcname.js +0 -123
  96. package/src/apis/gcrule.js +0 -119
  97. package/src/apis/getAccess.js +0 -111
  98. package/src/apis/getBotInfo.js +0 -88
  99. package/src/apis/getBotInitialData.js +0 -43
  100. package/src/apis/getFriendsList.js +0 -79
  101. package/src/apis/getMessage.js +0 -423
  102. package/src/apis/getTheme.js +0 -95
  103. package/src/apis/getThemeInfo.js +0 -116
  104. package/src/apis/getThreadHistory.js +0 -239
  105. package/src/apis/getThreadInfo.js +0 -267
  106. package/src/apis/getThreadList.js +0 -232
  107. package/src/apis/getThreadPictures.js +0 -58
  108. package/src/apis/getUserID.js +0 -117
  109. package/src/apis/getUserInfo.js +0 -513
  110. package/src/apis/getUserInfoV2.js +0 -146
  111. package/src/apis/handleMessageRequest.js +0 -50
  112. package/src/apis/httpPost.js +0 -89
  113. package/src/apis/httpPostFormData.js +0 -69
  114. package/src/apis/listenMqtt.js +0 -1236
  115. package/src/apis/listenSpeed.js +0 -179
  116. package/src/apis/logout.js +0 -87
  117. package/src/apis/markAsDelivered.js +0 -47
  118. package/src/apis/markAsReadAll.js +0 -40
  119. package/src/apis/markAsSeen.js +0 -70
  120. package/src/apis/mqttDeltaValue.js +0 -278
  121. package/src/apis/muteThread.js +0 -45
  122. package/src/apis/nickname.js +0 -132
  123. package/src/apis/notes.js +0 -163
  124. package/src/apis/pinMessage.js +0 -150
  125. package/src/apis/produceMetaTheme.js +0 -180
  126. package/src/apis/realtime.js +0 -182
  127. package/src/apis/removeUserFromGroup.js +0 -117
  128. package/src/apis/resolvePhotoUrl.js +0 -58
  129. package/src/apis/searchForThread.js +0 -154
  130. package/src/apis/sendMessage.js +0 -354
  131. package/src/apis/sendMessageMqtt.js +0 -249
  132. package/src/apis/sendTypingIndicator.js +0 -91
  133. package/src/apis/setMessageReaction.js +0 -27
  134. package/src/apis/setMessageReactionMqtt.js +0 -61
  135. package/src/apis/setThreadTheme.js +0 -260
  136. package/src/apis/setThreadThemeMqtt.js +0 -94
  137. package/src/apis/share.js +0 -107
  138. package/src/apis/shareContact.js +0 -66
  139. package/src/apis/stickers.js +0 -257
  140. package/src/apis/story.js +0 -181
  141. package/src/apis/theme.js +0 -233
  142. package/src/apis/unfriend.js +0 -47
  143. package/src/apis/unsendMessage.js +0 -17
  144. package/src/engine/client.js +0 -92
  145. package/src/engine/models/buildAPI.js +0 -152
  146. package/src/engine/models/loginHelper.js +0 -519
  147. package/src/engine/models/setOptions.js +0 -88
  148. package/src/security/e2ee.js +0 -109
  149. package/src/types/index.d.ts +0 -498
  150. package/src/utils/antiSuspension.js +0 -506
  151. package/src/utils/auth-helpers.js +0 -149
  152. package/src/utils/autoReLogin.js +0 -336
  153. package/src/utils/axios.js +0 -436
  154. package/src/utils/cache.js +0 -54
  155. package/src/utils/clients.js +0 -282
  156. package/src/utils/constants.js +0 -410
  157. package/src/utils/formatters/data/formatAttachment.js +0 -370
  158. package/src/utils/formatters/data/formatDelta.js +0 -109
  159. package/src/utils/formatters/index.js +0 -159
  160. package/src/utils/formatters/value/formatCookie.js +0 -91
  161. package/src/utils/formatters/value/formatDate.js +0 -36
  162. package/src/utils/formatters/value/formatID.js +0 -16
  163. package/src/utils/formatters.js +0 -1373
  164. package/src/utils/headers.js +0 -230
  165. package/src/utils/index.js +0 -153
  166. package/src/utils/monitoring.js +0 -333
  167. package/src/utils/rateLimiter.js +0 -319
  168. package/src/utils/tokenRefresh.js +0 -657
  169. package/src/utils/user-agents.js +0 -238
  170. package/src/utils/validation.js +0 -157
@@ -0,0 +1,1018 @@
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
+ "/ls_resp",
31
+ // "/ls_req",
32
+
33
+ "/inbox",
34
+ "/mercury",
35
+ "/messaging_events",
36
+ "/orca_message_notifications",
37
+ "/pp",
38
+ "/webrtc_response",
39
+ ];
40
+
41
+ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
42
+ //Don't really know what this does but I think it's for the active state?
43
+ //TODO: Move to ctx when implemented
44
+ var chatOn = ctx.globalOptions.online;
45
+ var foreground = false;
46
+
47
+ ctx["tasks"].clear(); // Clear tasks
48
+
49
+ var sessionID = Math.floor(Math.random() * 9007199254740991) + 1;
50
+ var username = {
51
+ u: ctx.userID,
52
+ s: sessionID,
53
+ chat_on: chatOn,
54
+ fg: foreground,
55
+ d: utils.getGUID(),
56
+ ct: "websocket",
57
+ //App id from facebook
58
+ aid: "219994525426954",
59
+ mqtt_sid: "",
60
+ cp: 3,
61
+ ecp: 10,
62
+ st: [],
63
+ pm: [],
64
+ dc: "",
65
+ no_auto_fg: true,
66
+ gas: null,
67
+ pack: [],
68
+ };
69
+ var cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
70
+
71
+ var host;
72
+ if (ctx.mqttEndpoint) {
73
+ host = `${ctx.mqttEndpoint}&sid=${sessionID}`;
74
+ } else if (ctx.region) {
75
+ host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLocaleLowerCase()}&sid=${sessionID}`;
76
+ } else {
77
+ host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}`;
78
+ }
79
+
80
+ var options = {
81
+ clientId: "mqttwsclient",
82
+ protocolId: "MQIsdp",
83
+ protocolVersion: 3,
84
+ username: JSON.stringify(username),
85
+ clean: true,
86
+ wsOptions: {
87
+ headers: {
88
+ Cookie: cookies,
89
+ Origin: "https://www.facebook.com",
90
+ "User-Agent": ctx.globalOptions.userAgent,
91
+ Referer: "https://www.facebook.com/",
92
+ Host: new URL(host).hostname, //'edge-chat.facebook.com'
93
+ },
94
+ origin: "https://www.facebook.com",
95
+ protocolVersion: 13,
96
+ },
97
+ keepalive: 10,
98
+ reschedulePings: false,
99
+ };
100
+
101
+ if (typeof ctx.globalOptions.proxy != "undefined") {
102
+ var agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
103
+ options.wsOptions.agent = agent;
104
+ }
105
+
106
+ ctx.mqttClient = new mqtt.Client(
107
+ (_) => websocket(host, options.wsOptions),
108
+ options,
109
+ );
110
+
111
+ var mqttClient = ctx.mqttClient;
112
+
113
+ mqttClient.on("error", function (err) {
114
+ log.error("listenMqtt", err);
115
+ mqttClient.end();
116
+ if (ctx.globalOptions.autoReconnect) {
117
+ getSeqID();
118
+ } else {
119
+ globalCallback(
120
+ {
121
+ type: "stop_listen",
122
+ error: "Connection refused: Server unavailable",
123
+ },
124
+ null,
125
+ );
126
+ }
127
+ });
128
+
129
+ mqttClient.on("connect", function () {
130
+ topics.forEach(function (topicsub) {
131
+ mqttClient.subscribe(topicsub);
132
+ });
133
+
134
+ var topic;
135
+ var queue = {
136
+ sync_api_version: 10,
137
+ max_deltas_able_to_process: 1000,
138
+ delta_batch_size: 500,
139
+ encoding: "JSON",
140
+ entity_fbid: ctx.userID,
141
+ };
142
+
143
+ if (ctx.syncToken) {
144
+ topic = "/messenger_sync_get_diffs";
145
+ queue.last_seq_id = ctx.lastSeqId;
146
+ queue.sync_token = ctx.syncToken;
147
+ } else {
148
+ topic = "/messenger_sync_create_queue";
149
+ queue.initial_titan_sequence_id = ctx.lastSeqId;
150
+ queue.device_params = null;
151
+ }
152
+
153
+ mqttClient.publish(topic, JSON.stringify(queue), {
154
+ qos: 1,
155
+ retain: false,
156
+ });
157
+
158
+ var rTimeout = setTimeout(function () {
159
+ mqttClient.end();
160
+ getSeqID();
161
+ }, 5000);
162
+
163
+ ctx.tmsWait = function () {
164
+ clearTimeout(rTimeout);
165
+ ctx.globalOptions.emitReady
166
+ ? globalCallback({
167
+ type: "ready",
168
+ error: null,
169
+ })
170
+ : "";
171
+ delete ctx.tmsWait;
172
+ };
173
+ });
174
+
175
+ mqttClient.on("message", function (topic, message, _packet) {
176
+ try {
177
+ var jsonMessage = JSON.parse(message);
178
+ } catch (ex) {
179
+ return log.error("listenMqtt", ex);
180
+ }
181
+ if (topic === "/t_ms") {
182
+ if (ctx.tmsWait && typeof ctx.tmsWait == "function") {
183
+ ctx.tmsWait();
184
+ }
185
+
186
+ if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
187
+ ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
188
+ ctx.syncToken = jsonMessage.syncToken;
189
+ }
190
+
191
+ if (jsonMessage.lastIssuedSeqId) {
192
+ ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
193
+ }
194
+
195
+ //If it contains more than 1 delta
196
+ for (var i in jsonMessage.deltas) {
197
+ var delta = jsonMessage.deltas[i];
198
+ parseDelta(defaultFuncs, api, ctx, globalCallback, {
199
+ delta: delta,
200
+ });
201
+ }
202
+ } else if (
203
+ topic === "/thread_typing" ||
204
+ topic === "/orca_typing_notifications"
205
+ ) {
206
+ var typ = {
207
+ type: "typ",
208
+ isTyping: !!jsonMessage.state,
209
+ from: jsonMessage.sender_fbid.toString(),
210
+ threadID: utils.formatID(
211
+ (jsonMessage.thread || jsonMessage.sender_fbid).toString(),
212
+ ),
213
+ };
214
+ (function () {
215
+ globalCallback(null, typ);
216
+ })();
217
+ } else if (topic === "/orca_presence") {
218
+ if (!ctx.globalOptions.updatePresence) {
219
+ for (var i in jsonMessage.list) {
220
+ var data = jsonMessage.list[i];
221
+ var userID = data["u"];
222
+
223
+ var presence = {
224
+ type: "presence",
225
+ userID: userID.toString(),
226
+ //Convert to ms
227
+ timestamp: data["l"] * 1000,
228
+ statuses: data["p"],
229
+ };
230
+ (function () {
231
+ globalCallback(null, presence);
232
+ })();
233
+ }
234
+ }
235
+ } else if (topic == "/ls_resp") {
236
+ const parsedPayload = JSON.parse(jsonMessage.payload);
237
+ const reqID = jsonMessage.request_id;
238
+ if (ctx["tasks"].has(reqID)) {
239
+ const taskData = ctx["tasks"].get(reqID);
240
+ const { type: taskType, callback: taskCallback } = taskData;
241
+ const taskRespData = getTaskResponseData(taskType, parsedPayload);
242
+
243
+ if (taskRespData == null) {
244
+ taskCallback("error", null);
245
+ } else {
246
+ taskCallback(null, {
247
+ type: taskType,
248
+ reqID: reqID,
249
+ ...taskRespData,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ });
255
+
256
+ mqttClient.on("close", function () {
257
+ //(function () { globalCallback("Connection closed."); })();
258
+ // client.end();
259
+ });
260
+ }
261
+
262
+ function getTaskResponseData(taskType, payload) {
263
+ try {
264
+ switch (taskType) {
265
+ case "set_message_reaction": {
266
+ return {
267
+ mid: payload.step[1][2][2][1][4],
268
+ };
269
+ }
270
+ case "edit_message": {
271
+ return {
272
+ mid: payload.step[1][2][2][1][2],
273
+ };
274
+ }
275
+ }
276
+ } catch (error) {
277
+ return null;
278
+ }
279
+ }
280
+
281
+ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
282
+ if (v.delta.class == "NewMessage") {
283
+ //Not tested for pages
284
+ if (ctx.globalOptions.pageID && ctx.globalOptions.pageID != v.queue) return;
285
+
286
+ (function resolveAttachmentUrl(i) {
287
+ if (v.delta.attachments && i == v.delta.attachments.length) {
288
+ var fmtMsg;
289
+ try {
290
+ fmtMsg = utils.formatDeltaMessage(v);
291
+ } catch (err) {
292
+ return globalCallback({
293
+ error:
294
+ "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
295
+ detail: err,
296
+ res: v,
297
+ type: "parse_error",
298
+ });
299
+ }
300
+ if (fmtMsg) {
301
+ if (ctx.globalOptions.autoMarkDelivery) {
302
+ markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
303
+ }
304
+ }
305
+ return !ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID
306
+ ? undefined
307
+ : (function () {
308
+ globalCallback(null, fmtMsg);
309
+ })();
310
+ } else {
311
+ if (v.delta.attachments[i].mercury.attach_type == "photo") {
312
+ api.resolvePhotoUrl(v.delta.attachments[i].fbid, (err, url) => {
313
+ if (!err) v.delta.attachments[i].mercury.metadata.url = url;
314
+ return resolveAttachmentUrl(i + 1);
315
+ });
316
+ } else {
317
+ return resolveAttachmentUrl(i + 1);
318
+ }
319
+ }
320
+ })(0);
321
+ }
322
+
323
+ if (v.delta.class == "ClientPayload") {
324
+ var clientPayload = utils.decodeClientPayload(v.delta.payload);
325
+ if (clientPayload && clientPayload.deltas) {
326
+ for (var i in clientPayload.deltas) {
327
+ var delta = clientPayload.deltas[i];
328
+ if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
329
+ (function () {
330
+ globalCallback(null, {
331
+ type: "message_reaction",
332
+ threadID: (delta.deltaMessageReaction.threadKey.threadFbId
333
+ ? delta.deltaMessageReaction.threadKey.threadFbId
334
+ : delta.deltaMessageReaction.threadKey.otherUserFbId
335
+ ).toString(),
336
+ messageID: delta.deltaMessageReaction.messageId,
337
+ reaction: delta.deltaMessageReaction.reaction,
338
+ senderID: delta.deltaMessageReaction.senderId.toString(),
339
+ userID: delta.deltaMessageReaction.userId.toString(),
340
+ reactionTimestamp: delta.deltaMessageReaction.reactionTimestamp,
341
+ });
342
+ })();
343
+ } else if (
344
+ delta.deltaRecallMessageData &&
345
+ !!ctx.globalOptions.listenEvents
346
+ ) {
347
+ (function () {
348
+ globalCallback(null, {
349
+ type: "message_unsend",
350
+ threadID: (delta.deltaRecallMessageData.threadKey.threadFbId
351
+ ? delta.deltaRecallMessageData.threadKey.threadFbId
352
+ : delta.deltaRecallMessageData.threadKey.otherUserFbId
353
+ ).toString(),
354
+ messageID: delta.deltaRecallMessageData.messageID,
355
+ senderID: delta.deltaRecallMessageData.senderID.toString(),
356
+ deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
357
+ timestamp: delta.deltaRecallMessageData.messageTimestamp,
358
+ });
359
+ })();
360
+ } else if (delta.deltaMessageReply) {
361
+ //Mention block - #1
362
+ var mdata =
363
+ delta.deltaMessageReply.message === undefined
364
+ ? []
365
+ : delta.deltaMessageReply.message.data === undefined
366
+ ? []
367
+ : delta.deltaMessageReply.message.data.prng === undefined
368
+ ? []
369
+ : JSON.parse(delta.deltaMessageReply.message.data.prng);
370
+ var m_id = mdata.map((u) => u.i);
371
+ var m_offset = mdata.map((u) => u.o);
372
+ var m_length = mdata.map((u) => u.l);
373
+
374
+ var mentions = {};
375
+
376
+ for (var i = 0; i < m_id.length; i++) {
377
+ mentions[m_id[i]] = (
378
+ delta.deltaMessageReply.message.body || ""
379
+ ).substring(m_offset[i], m_offset[i] + m_length[i]);
380
+ }
381
+ //Mention block - 1#
382
+ var callbackToReturn = {
383
+ type: "message_reply",
384
+ threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey
385
+ .threadFbId
386
+ ? delta.deltaMessageReply.message.messageMetadata.threadKey
387
+ .threadFbId
388
+ : delta.deltaMessageReply.message.messageMetadata.threadKey
389
+ .otherUserFbId
390
+ ).toString(),
391
+ messageID:
392
+ delta.deltaMessageReply.message.messageMetadata.messageId,
393
+ senderID:
394
+ delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
395
+ attachments: delta.deltaMessageReply.message.attachments
396
+ .map(function (att) {
397
+ var mercury = JSON.parse(att.mercuryJSON);
398
+ Object.assign(att, mercury);
399
+ return att;
400
+ })
401
+ .map((att) => {
402
+ var x;
403
+ try {
404
+ x = utils._formatAttachment(att);
405
+ } catch (ex) {
406
+ x = att;
407
+ x.error = ex;
408
+ x.type = "unknown";
409
+ }
410
+ return x;
411
+ }),
412
+ args: (delta.deltaMessageReply.message.body || "")
413
+ .trim()
414
+ .split(/\s+/),
415
+ body: delta.deltaMessageReply.message.body || "",
416
+ mentions: mentions,
417
+ timestamp:
418
+ delta.deltaMessageReply.message.messageMetadata.timestamp,
419
+ isGroup:
420
+ !!delta.deltaMessageReply.message.messageMetadata.threadKey
421
+ .threadFbId,
422
+ participantIDs: delta.deltaMessageReply.message.participants?.map(
423
+ (u) => u.toString(),
424
+ ),
425
+ };
426
+
427
+ if (delta.deltaMessageReply.repliedToMessage) {
428
+ //Mention block - #2
429
+ mdata =
430
+ delta.deltaMessageReply.repliedToMessage === undefined
431
+ ? []
432
+ : delta.deltaMessageReply.repliedToMessage.data === undefined
433
+ ? []
434
+ : delta.deltaMessageReply.repliedToMessage.data.prng ===
435
+ undefined
436
+ ? []
437
+ : JSON.parse(
438
+ delta.deltaMessageReply.repliedToMessage.data.prng,
439
+ );
440
+ m_id = mdata.map((u) => u.i);
441
+ m_offset = mdata.map((u) => u.o);
442
+ m_length = mdata.map((u) => u.l);
443
+
444
+ var rmentions = {};
445
+
446
+ for (var i = 0; i < m_id.length; i++) {
447
+ rmentions[m_id[i]] = (
448
+ delta.deltaMessageReply.repliedToMessage.body || ""
449
+ ).substring(m_offset[i], m_offset[i] + m_length[i]);
450
+ }
451
+ //Mention block - 2#
452
+ callbackToReturn.messageReply = {
453
+ threadID: (delta.deltaMessageReply.repliedToMessage
454
+ .messageMetadata.threadKey.threadFbId
455
+ ? delta.deltaMessageReply.repliedToMessage.messageMetadata
456
+ .threadKey.threadFbId
457
+ : delta.deltaMessageReply.repliedToMessage.messageMetadata
458
+ .threadKey.otherUserFbId
459
+ ).toString(),
460
+ messageID:
461
+ delta.deltaMessageReply.repliedToMessage.messageMetadata
462
+ .messageId,
463
+ senderID:
464
+ delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
465
+ attachments: delta.deltaMessageReply.repliedToMessage.attachments
466
+ .map(function (att) {
467
+ var mercury = JSON.parse(att.mercuryJSON);
468
+ Object.assign(att, mercury);
469
+ return att;
470
+ })
471
+ .map((att) => {
472
+ var x;
473
+ try {
474
+ x = utils._formatAttachment(att);
475
+ } catch (ex) {
476
+ x = att;
477
+ x.error = ex;
478
+ x.type = "unknown";
479
+ }
480
+ return x;
481
+ }),
482
+ args: (delta.deltaMessageReply.repliedToMessage.body || "")
483
+ .trim()
484
+ .split(/\s+/),
485
+ body: delta.deltaMessageReply.repliedToMessage.body || "",
486
+ isGroup:
487
+ !!delta.deltaMessageReply.repliedToMessage.messageMetadata
488
+ .threadKey.threadFbId,
489
+ mentions: rmentions,
490
+ timestamp:
491
+ delta.deltaMessageReply.repliedToMessage.messageMetadata
492
+ .timestamp,
493
+ };
494
+ } else if (delta.deltaMessageReply.replyToMessageId) {
495
+ return defaultFuncs
496
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
497
+ av: ctx.globalOptions.pageID,
498
+ queries: JSON.stringify({
499
+ o0: {
500
+ //Using the same doc_id as forcedFetch
501
+ doc_id: "2848441488556444",
502
+ query_params: {
503
+ thread_and_message_id: {
504
+ thread_id: callbackToReturn.threadID,
505
+ message_id: delta.deltaMessageReply.replyToMessageId.id,
506
+ },
507
+ },
508
+ },
509
+ }),
510
+ })
511
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
512
+ .then((resData) => {
513
+ if (resData[resData.length - 1].error_results > 0) {
514
+ throw resData[0].o0.errors;
515
+ }
516
+
517
+ if (resData[resData.length - 1].successful_results === 0) {
518
+ throw {
519
+ error: "forcedFetch: there was no successful_results",
520
+ res: resData,
521
+ };
522
+ }
523
+
524
+ var fetchData = resData[0].o0.data.message;
525
+
526
+ var mobj = {};
527
+ for (var n in fetchData.message.ranges) {
528
+ mobj[fetchData.message.ranges[n].entity.id] = (
529
+ fetchData.message.text || ""
530
+ ).substr(
531
+ fetchData.message.ranges[n].offset,
532
+ fetchData.message.ranges[n].length,
533
+ );
534
+ }
535
+
536
+ callbackToReturn.messageReply = {
537
+ threadID: callbackToReturn.threadID,
538
+ messageID: fetchData.message_id,
539
+ senderID: fetchData.message_sender.id.toString(),
540
+ attachments: fetchData.message.blob_attachment.map((att) => {
541
+ var x;
542
+ try {
543
+ x = utils._formatAttachment({
544
+ blob_attachment: att,
545
+ });
546
+ } catch (ex) {
547
+ x = att;
548
+ x.error = ex;
549
+ x.type = "unknown";
550
+ }
551
+ return x;
552
+ }),
553
+ args: (delta.deltaMessageReply.repliedToMessage.body || "")
554
+ .trim()
555
+ .split(/\s+/),
556
+ body: fetchData.message.text || "",
557
+ isGroup: callbackToReturn.isGroup,
558
+ mentions: mobj,
559
+ timestamp: parseInt(fetchData.timestamp_precise),
560
+ };
561
+ })
562
+ .catch((err) => {
563
+ log.error("forcedFetch", err);
564
+ })
565
+ .finally(function () {
566
+ if (ctx.globalOptions.autoMarkDelivery) {
567
+ markDelivery(
568
+ ctx,
569
+ api,
570
+ callbackToReturn.threadID,
571
+ callbackToReturn.messageID,
572
+ );
573
+ }
574
+ !ctx.globalOptions.selfListen &&
575
+ callbackToReturn.senderID === ctx.userID
576
+ ? undefined
577
+ : (function () {
578
+ globalCallback(null, callbackToReturn);
579
+ })();
580
+ });
581
+ } else {
582
+ callbackToReturn.delta = delta;
583
+ }
584
+
585
+ if (ctx.globalOptions.autoMarkDelivery) {
586
+ markDelivery(
587
+ ctx,
588
+ api,
589
+ callbackToReturn.threadID,
590
+ callbackToReturn.messageID,
591
+ );
592
+ }
593
+
594
+ return !ctx.globalOptions.selfListen &&
595
+ callbackToReturn.senderID === ctx.userID
596
+ ? undefined
597
+ : (function () {
598
+ globalCallback(null, callbackToReturn);
599
+ })();
600
+ }
601
+ }
602
+ return;
603
+ }
604
+ }
605
+
606
+ if (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents) return;
607
+
608
+ switch (v.delta.class) {
609
+ case "ReadReceipt":
610
+ var fmtMsg;
611
+ try {
612
+ fmtMsg = utils.formatDeltaReadReceipt(v.delta);
613
+ } catch (err) {
614
+ return globalCallback({
615
+ error:
616
+ "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
617
+ detail: err,
618
+ res: v.delta,
619
+ type: "parse_error",
620
+ });
621
+ }
622
+ return (function () {
623
+ globalCallback(null, fmtMsg);
624
+ })();
625
+ case "AdminTextMessage":
626
+ switch (v.delta.type) {
627
+ case "change_thread_theme":
628
+ // case "change_thread_icon": deprecated
629
+ case "change_thread_quick_reaction":
630
+ case "change_thread_nickname":
631
+ case "change_thread_admins":
632
+ case "change_thread_approval_mode":
633
+ case "group_poll":
634
+ case "messenger_call_log":
635
+ case "participant_joined_group_call":
636
+ var fmtMsg;
637
+ try {
638
+ fmtMsg = utils.formatDeltaEvent(v.delta);
639
+ } catch (err) {
640
+ return globalCallback({
641
+ error:
642
+ "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
643
+ detail: err,
644
+ res: v.delta,
645
+ type: "parse_error",
646
+ });
647
+ }
648
+ return (function () {
649
+ globalCallback(null, fmtMsg);
650
+ })();
651
+ default:
652
+ return;
653
+ }
654
+ break;
655
+ //For group images
656
+ case "ForcedFetch":
657
+ if (!v.delta.threadKey) return;
658
+ var mid = v.delta.messageId;
659
+ var tid = v.delta.threadKey.threadFbId;
660
+ if (mid && tid) {
661
+ const form = {
662
+ av: ctx.globalOptions.pageID,
663
+ queries: JSON.stringify({
664
+ o0: {
665
+ //This doc_id is valid as of March 25, 2020
666
+ doc_id: "2848441488556444",
667
+ query_params: {
668
+ thread_and_message_id: {
669
+ thread_id: tid.toString(),
670
+ message_id: mid,
671
+ },
672
+ },
673
+ },
674
+ }),
675
+ };
676
+
677
+ defaultFuncs
678
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
679
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
680
+ .then((resData) => {
681
+ if (resData[resData.length - 1].error_results > 0) {
682
+ throw resData[0].o0.errors;
683
+ }
684
+
685
+ if (resData[resData.length - 1].successful_results === 0) {
686
+ throw {
687
+ error: "forcedFetch: there was no successful_results",
688
+ res: resData,
689
+ };
690
+ }
691
+
692
+ var fetchData = resData[0].o0.data.message;
693
+
694
+ if (utils.getType(fetchData) == "Object") {
695
+ log.info("forcedFetch", fetchData);
696
+ switch (fetchData.__typename) {
697
+ case "ThreadImageMessage":
698
+ (!ctx.globalOptions.selfListen &&
699
+ fetchData.message_sender.id.toString() === ctx.userID) ||
700
+ !ctx.loggedIn
701
+ ? undefined
702
+ : (function () {
703
+ globalCallback(null, {
704
+ type: "event",
705
+ threadID: utils.formatID(tid.toString()),
706
+ logMessageType: "log:thread-image",
707
+ logMessageData: {
708
+ image: {
709
+ attachmentID:
710
+ fetchData.image_with_metadata &&
711
+ fetchData.image_with_metadata
712
+ .legacy_attachment_id,
713
+ width:
714
+ fetchData.image_with_metadata &&
715
+ fetchData.image_with_metadata
716
+ .original_dimensions.x,
717
+ height:
718
+ fetchData.image_with_metadata &&
719
+ fetchData.image_with_metadata
720
+ .original_dimensions.y,
721
+ url:
722
+ fetchData.image_with_metadata &&
723
+ fetchData.image_with_metadata.preview.uri,
724
+ },
725
+ },
726
+ logMessageBody: fetchData.snippet,
727
+ timestamp: fetchData.timestamp_precise,
728
+ author: fetchData.message_sender.id,
729
+ });
730
+ })();
731
+ break;
732
+ case "UserMessage":
733
+ log.info("ff-Return", {
734
+ type: "message",
735
+ senderID: utils.formatID(fetchData.message_sender.id),
736
+ body: fetchData.message.text || "",
737
+ threadID: utils.formatID(tid.toString()),
738
+ messageID: fetchData.message_id,
739
+ attachments: [
740
+ {
741
+ type: "share",
742
+ ID: fetchData.extensible_attachment
743
+ .legacy_attachment_id,
744
+ url: fetchData.extensible_attachment.story_attachment
745
+ .url,
746
+
747
+ title:
748
+ fetchData.extensible_attachment.story_attachment
749
+ .title_with_entities.text,
750
+ description:
751
+ fetchData.extensible_attachment.story_attachment
752
+ .description.text,
753
+ source:
754
+ fetchData.extensible_attachment.story_attachment
755
+ .source,
756
+
757
+ image: (
758
+ (
759
+ fetchData.extensible_attachment.story_attachment
760
+ .media || {}
761
+ ).image || {}
762
+ ).uri,
763
+ width: (
764
+ (
765
+ fetchData.extensible_attachment.story_attachment
766
+ .media || {}
767
+ ).image || {}
768
+ ).width,
769
+ height: (
770
+ (
771
+ fetchData.extensible_attachment.story_attachment
772
+ .media || {}
773
+ ).image || {}
774
+ ).height,
775
+ playable:
776
+ (
777
+ fetchData.extensible_attachment.story_attachment
778
+ .media || {}
779
+ ).is_playable || false,
780
+ duration:
781
+ (
782
+ fetchData.extensible_attachment.story_attachment
783
+ .media || {}
784
+ ).playable_duration_in_ms || 0,
785
+
786
+ subattachments:
787
+ fetchData.extensible_attachment.subattachments,
788
+ properties:
789
+ fetchData.extensible_attachment.story_attachment
790
+ .properties,
791
+ },
792
+ ],
793
+ mentions: {},
794
+ timestamp: parseInt(fetchData.timestamp_precise),
795
+ isGroup: fetchData.message_sender.id != tid.toString(),
796
+ });
797
+ globalCallback(null, {
798
+ type: "message",
799
+ senderID: utils.formatID(fetchData.message_sender.id),
800
+ body: fetchData.message.text || "",
801
+ threadID: utils.formatID(tid.toString()),
802
+ messageID: fetchData.message_id,
803
+ attachments: [
804
+ {
805
+ type: "share",
806
+ ID: fetchData.extensible_attachment
807
+ .legacy_attachment_id,
808
+ url: fetchData.extensible_attachment.story_attachment
809
+ .url,
810
+
811
+ title:
812
+ fetchData.extensible_attachment.story_attachment
813
+ .title_with_entities.text,
814
+ description:
815
+ fetchData.extensible_attachment.story_attachment
816
+ .description.text,
817
+ source:
818
+ fetchData.extensible_attachment.story_attachment
819
+ .source,
820
+
821
+ image: (
822
+ (
823
+ fetchData.extensible_attachment.story_attachment
824
+ .media || {}
825
+ ).image || {}
826
+ ).uri,
827
+ width: (
828
+ (
829
+ fetchData.extensible_attachment.story_attachment
830
+ .media || {}
831
+ ).image || {}
832
+ ).width,
833
+ height: (
834
+ (
835
+ fetchData.extensible_attachment.story_attachment
836
+ .media || {}
837
+ ).image || {}
838
+ ).height,
839
+ playable:
840
+ (
841
+ fetchData.extensible_attachment.story_attachment
842
+ .media || {}
843
+ ).is_playable || false,
844
+ duration:
845
+ (
846
+ fetchData.extensible_attachment.story_attachment
847
+ .media || {}
848
+ ).playable_duration_in_ms || 0,
849
+
850
+ subattachments:
851
+ fetchData.extensible_attachment.subattachments,
852
+ properties:
853
+ fetchData.extensible_attachment.story_attachment
854
+ .properties,
855
+ },
856
+ ],
857
+ mentions: {},
858
+ timestamp: parseInt(fetchData.timestamp_precise),
859
+ isGroup: fetchData.message_sender.id != tid.toString(),
860
+ });
861
+ }
862
+ } else {
863
+ log.error("forcedFetch", fetchData);
864
+ }
865
+ })
866
+ .catch((err) => {
867
+ log.error("forcedFetch", err);
868
+ });
869
+ }
870
+ break;
871
+ case "ThreadName":
872
+ case "ParticipantsAddedToGroupThread":
873
+ case "ParticipantLeftGroupThread":
874
+ var formattedEvent;
875
+ try {
876
+ formattedEvent = utils.formatDeltaEvent(v.delta);
877
+ } catch (err) {
878
+ return globalCallback({
879
+ error:
880
+ "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
881
+ detail: err,
882
+ res: v.delta,
883
+ type: "parse_error",
884
+ });
885
+ }
886
+ return (!ctx.globalOptions.selfListen &&
887
+ formattedEvent.author.toString() === ctx.userID) ||
888
+ !ctx.loggedIn
889
+ ? undefined
890
+ : (function () {
891
+ globalCallback(null, formattedEvent);
892
+ })();
893
+ }
894
+ }
895
+
896
+ function markDelivery(ctx, api, threadID, messageID) {
897
+ if (threadID && messageID) {
898
+ api.markAsDelivered(threadID, messageID, (err) => {
899
+ if (err) {
900
+ log.error("markAsDelivered", err);
901
+ } else {
902
+ if (ctx.globalOptions.autoMarkRead) {
903
+ api.markAsRead(threadID, (err) => {
904
+ if (err) {
905
+ log.error("markAsDelivered", err);
906
+ }
907
+ });
908
+ }
909
+ }
910
+ });
911
+ }
912
+ }
913
+
914
+ module.exports = function (defaultFuncs, api, ctx) {
915
+ var globalCallback = identity;
916
+ getSeqID = function getSeqID() {
917
+ ctx.t_mqttCalled = false;
918
+ defaultFuncs
919
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
920
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
921
+ .then((resData) => {
922
+ if (utils.getType(resData) != "Array") {
923
+ throw {
924
+ error: "Not logged in",
925
+ res: resData,
926
+ };
927
+ }
928
+
929
+ if (resData && resData[resData.length - 1].error_results > 0) {
930
+ throw resData[0].o0.errors;
931
+ }
932
+
933
+ if (resData[resData.length - 1].successful_results === 0) {
934
+ throw {
935
+ error: "getSeqId: there was no successful_results",
936
+ res: resData,
937
+ };
938
+ }
939
+
940
+ if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
941
+ ctx.lastSeqId =
942
+ resData[0].o0.data.viewer.message_threads.sync_sequence_id;
943
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
944
+ } else {
945
+ throw {
946
+ error: "getSeqId: no sync_sequence_id found.",
947
+ res: resData,
948
+ };
949
+ }
950
+ })
951
+ .catch((err) => {
952
+ log.error("getSeqId", err);
953
+ if (utils.getType(err) == "Object" && err.error === "Not logged in") {
954
+ ctx.loggedIn = false;
955
+ }
956
+ return globalCallback(err);
957
+ });
958
+ };
959
+
960
+ return function (callback) {
961
+ class MessageEmitter extends EventEmitter {
962
+ stopListening(callback) {
963
+ callback = callback || (() => {});
964
+ globalCallback = identity;
965
+ if (ctx.mqttClient) {
966
+ ctx.mqttClient.unsubscribe("/webrtc");
967
+ ctx.mqttClient.unsubscribe("/rtc_multi");
968
+ ctx.mqttClient.unsubscribe("/onevc");
969
+ ctx.mqttClient.publish("/browser_close", "{}");
970
+ ctx.mqttClient.end(false, function (...data) {
971
+ callback(data);
972
+ ctx.mqttClient = undefined;
973
+ });
974
+ }
975
+ }
976
+ }
977
+
978
+ var msgEmitter = new MessageEmitter();
979
+ globalCallback =
980
+ callback ||
981
+ function (error, message) {
982
+ if (error) {
983
+ return msgEmitter.emit("error", error);
984
+ }
985
+ msgEmitter.emit("message", message);
986
+ };
987
+
988
+ //Reset some stuff
989
+ if (!ctx.firstListen) ctx.lastSeqId = null;
990
+ ctx.syncToken = undefined;
991
+ ctx.t_mqttCalled = false;
992
+
993
+ //Same request as getThreadList
994
+ form = {
995
+ av: ctx.globalOptions.pageID,
996
+ queries: JSON.stringify({
997
+ o0: {
998
+ doc_id: "3336396659757871",
999
+ query_params: {
1000
+ limit: 1,
1001
+ before: null,
1002
+ tags: ["INBOX"],
1003
+ includeDeliveryReceipts: false,
1004
+ includeSeqID: true,
1005
+ },
1006
+ },
1007
+ }),
1008
+ };
1009
+
1010
+ if (!ctx.firstListen || !ctx.lastSeqId) {
1011
+ getSeqID();
1012
+ } else {
1013
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
1014
+ }
1015
+ ctx.firstListen = false;
1016
+ return msgEmitter;
1017
+ };
1018
+ };