stfca 1.0.2 → 1.0.4

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