@dongdev/fca-unofficial 2.0.7 → 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 +3 -0
  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 +5 -5
  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,1005 +1,80 @@
1
- "use strict";
2
- const utils = require("../../utils");
3
- const log = require("npmlog");
4
- const mqtt = require("mqtt");
5
- const WebSocket = require("ws");
6
- const HttpsProxyAgent = require("https-proxy-agent");
7
- const EventEmitter = require("events");
8
- var identity = function () { };
9
- var form = {};
10
- var getSeqID = function () { };
11
- const logger = require("../../../func/logger");
12
- const { parseAndCheckLogin } = require("../../utils/client");
13
- const { buildProxy, buildStream } = require("./detail/buildStream");
14
- let mqttReconnectCount = 0;
15
- const topics = [
16
- "/ls_req",
17
- "/ls_resp",
18
- "/legacy_web",
19
- "/webrtc",
20
- "/rtc_multi",
21
- "/onevc",
22
- "/br_sr",
23
- "/sr_res",
24
- "/t_ms",
25
- "/thread_typing",
26
- "/orca_typing_notifications",
27
- "/notify_disconnect",
28
- "/orca_presence",
29
- "/inbox",
30
- "/mercury",
31
- "/messaging_events",
32
- "/orca_message_notifications",
33
- "/pp",
34
- "/webrtc_response"
35
- ];
36
- function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
37
- const chatOn = ctx.globalOptions.online;
38
- const foreground = false;
39
- const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
40
- const GUID = utils.getGUID();
41
- const username = {
42
- u: ctx.userID,
43
- s: sessionID,
44
- chat_on: chatOn,
45
- fg: foreground,
46
- d: GUID,
47
- ct: "websocket",
48
- aid: 219994525426954,
49
- aids: null,
50
- mqtt_sid: "",
51
- cp: 3,
52
- ecp: 10,
53
- st: [],
54
- pm: [],
55
- dc: "",
56
- no_auto_fg: true,
57
- gas: null,
58
- pack: [],
59
- p: null,
60
- php_override: ""
61
- };
62
- const cookies = api.getCookies();
63
- let host;
64
- if (ctx.mqttEndpoint) {
65
- host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${GUID}`;
66
- } else if (ctx.region) {
67
- host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${ctx.clientId}`;
68
- } else {
69
- host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${GUID}`;
70
- }
71
- const options = {
72
- clientId: "mqttwsclient",
73
- protocolId: "MQIsdp",
74
- protocolVersion: 3,
75
- username: JSON.stringify(username),
76
- clean: true,
77
- wsOptions: {
78
- headers: {
79
- Cookie: cookies,
80
- Origin: "https://www.facebook.com",
81
- "User-Agent":
82
- ctx.globalOptions.userAgent ||
83
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
84
- Referer: "https://www.facebook.com/",
85
- Host: "edge-chat.facebook.com",
86
- Connection: "Upgrade",
87
- Pragma: "no-cache",
88
- "Cache-Control": "no-cache",
89
- Upgrade: "websocket",
90
- "Sec-WebSocket-Version": "13",
91
- "Accept-Encoding": "gzip, deflate, br",
92
- "Accept-Language": "vi,en;q=0.9",
93
- "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
94
- },
95
- origin: "https://www.facebook.com",
96
- protocolVersion: 13,
97
- binaryType: "arraybuffer"
98
- },
99
- keepalive: 30,
100
- reschedulePings: true,
101
- reconnectPeriod: 1000,
102
- connectTimeout: 5000
103
- };
104
- if (ctx.globalOptions.proxy !== undefined) {
105
- const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
106
- options.wsOptions.agent = agent;
107
- }
108
- ctx.mqttClient = new mqtt.Client(
109
- () =>
110
- buildStream(
111
- options,
112
- new WebSocket(host, options.wsOptions),
113
- buildProxy()
114
- ),
115
- options
116
- );
117
- const mqttClient = ctx.mqttClient;
118
- global.mqttClient = mqttClient;
119
- mqttClient.on("error", function (err) {
120
- log.error("listenMqtt", err);
121
- mqttClient.end();
122
- if (ctx.globalOptions.autoReconnect) {
123
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
124
- } else {
125
- utils
126
- .checkLiveCookie(ctx, defaultFuncs)
127
- .then(res => {
128
- globalCallback(
129
- {
130
- type: "stop_listen",
131
- error: "Connection refused: Server unavailable"
132
- },
133
- null
134
- );
135
- })
136
- .catch(err => {
137
- globalCallback(
138
- {
139
- type: "account_inactive",
140
- error:
141
- "Maybe your account is blocked by facebook, please login and check at https://facebook.com"
142
- },
143
- null
144
- );
145
- });
146
- }
147
- });
148
- mqttClient.on("connect", function () {
149
- // let StopProcessing = true;
150
- // mqttReconnectCount = 0;
151
- // setInterval(() => {
152
- // console.log('Đang chuẩn bị ngắt kết nối MQTT...');
153
- // StopProcessing = true;
154
- // if (ctx.mqttClient) {
155
- // topics.forEach((topic) => {
156
- // try {
157
- // ctx.mqttClient.unsubscribe(topic);
158
- // } catch (e) { }
159
- // });
160
- // try {
161
- // ctx.mqttClient.publish("/browser_close", "{}");
162
- // } catch (e) { }
163
- // ctx.mqttClient.removeAllListeners();
164
- // console.log('Ngắt Kết Nối MQTT...');
165
- // let connectionClosed = false;
166
- // const afterConnectionClosed = () => {
167
- // if (connectionClosed) return;
168
- // connectionClosed = true;
169
- // ctx.lastSeqId = null;
170
- // ctx.syncToken = undefined;
171
- // ctx.t_mqttCalled = false;
172
- // mqttReconnectCount = 0;
173
- // StopProcessing = false;
174
- // console.log('Đang Kết Nối Lại MQTT...');
175
- // setTimeout(() => {
176
- // getSeqID();
177
- // console.log('Kết Nối Lại MQTT Thành Công');
178
- // }, 1000);
179
- // };
180
- // try {
181
- // ctx.mqttClient.end(false, afterConnectionClosed);
182
- // setTimeout(() => {
183
- // if (!connectionClosed) {
184
- // console.warn('Đóng kết nối MQTT bằng timeout');
185
- // ctx.mqttClient = undefined;
186
- // afterConnectionClosed();
187
- // }
188
- // }, 5000);
189
- // } catch (e) {
190
- // console.error('Lỗi khi đóng kết nối MQTT:', e);
191
- // ctx.mqttClient = undefined;
192
- // afterConnectionClosed();
193
- // }
194
- // } else {
195
- // getSeqID();
196
- // console.log('Kết Nối Lại MQTT Thành Công');
197
- // }
198
- // }, 60 * 60 * 1000);
199
- if (process.env.OnStatus === undefined) {
200
- logger("fca-unoffcial premium", "info");
201
- process.env.OnStatus = true;
202
- }
203
- topics.forEach(topicsub => mqttClient.subscribe(topicsub));
204
- var topic;
205
- const queue = {
206
- sync_api_version: 11,
207
- max_deltas_able_to_process: 100,
208
- delta_batch_size: 500,
209
- encoding: "JSON",
210
- entity_fbid: ctx.userID,
211
- initial_titan_sequence_id: ctx.lastSeqId,
212
- device_params: null
213
- };
214
- if (ctx.syncToken) {
215
- topic = "/messenger_sync_get_diffs";
216
- queue.last_seq_id = ctx.lastSeqId;
217
- queue.sync_token = ctx.syncToken;
218
- } else {
219
- topic = "/messenger_sync_create_queue";
220
- queue.initial_titan_sequence_id = ctx.lastSeqId;
221
- queue.device_params = null;
222
- }
223
- mqttClient.publish(topic, JSON.stringify(queue), { qos: 1, retain: false });
224
- mqttClient.publish(
225
- "/foreground_state",
226
- JSON.stringify({ foreground: chatOn }),
227
- { qos: 1 }
228
- );
229
- mqttClient.publish(
230
- "/set_client_settings",
231
- JSON.stringify({ make_user_available_when_in_foreground: true }),
232
- { qos: 1 }
233
- );
234
- const rTimeout = setTimeout(function () {
235
- mqttClient.end();
236
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
237
- }, 5000);
238
- ctx.tmsWait = function () {
239
- clearTimeout(rTimeout);
240
- ctx.globalOptions.emitReady
241
- ? globalCallback({
242
- type: "ready",
243
- error: null
244
- })
245
- : "";
246
- delete ctx.tmsWait;
247
- };
248
- });
249
- mqttClient.on("message", function (topic, message, _packet) {
250
- try {
251
- let jsonMessage = Buffer.isBuffer(message)
252
- ? Buffer.from(message).toString()
253
- : message;
254
- try {
255
- jsonMessage = JSON.parse(jsonMessage);
256
- } catch (e) {
257
- jsonMessage = {};
258
- }
259
- if (jsonMessage.type === "jewel_requests_add") {
260
- globalCallback(null, {
261
- type: "friend_request_received",
262
- actorFbId: jsonMessage.from.toString(),
263
- timestamp: Date.now().toString()
264
- });
265
- } else if (jsonMessage.type === "jewel_requests_remove_old") {
266
- globalCallback(null, {
267
- type: "friend_request_cancel",
268
- actorFbId: jsonMessage.from.toString(),
269
- timestamp: Date.now().toString()
270
- });
271
- } else if (topic === "/t_ms") {
272
- if (ctx.tmsWait && typeof ctx.tmsWait == "function") {
273
- ctx.tmsWait();
274
- }
275
- if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
276
- ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
277
- ctx.syncToken = jsonMessage.syncToken;
278
- }
279
- if (jsonMessage.lastIssuedSeqId) {
280
- ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
281
- }
282
- for (const i in jsonMessage.deltas) {
283
- const delta = jsonMessage.deltas[i];
284
- parseDelta(defaultFuncs, api, ctx, globalCallback, {
285
- delta: delta
286
- });
287
- }
288
- } else if (
289
- topic === "/thread_typing" ||
290
- topic === "/orca_typing_notifications"
291
- ) {
292
- const typ = {
293
- type: "typ",
294
- isTyping: !!jsonMessage.state,
295
- from: jsonMessage.sender_fbid.toString(),
296
- threadID: utils.formatID(
297
- (jsonMessage.thread || jsonMessage.sender_fbid).toString()
298
- )
299
- };
300
- (function () {
301
- globalCallback(null, typ);
302
- })();
303
- } else if (topic === "/orca_presence") {
304
- if (!ctx.globalOptions.updatePresence) {
305
- for (const i in jsonMessage.list) {
306
- const data = jsonMessage.list[i];
307
- const userID = data["u"];
308
- const presence = {
309
- type: "presence",
310
- userID: userID.toString(),
311
- timestamp: data["l"] * 1000,
312
- statuses: data["p"]
313
- };
314
- (function () {
315
- globalCallback(null, presence);
316
- })();
317
- }
318
- }
319
- } else if (topic == "/ls_resp") {
320
- const parsedPayload = JSON.parse(jsonMessage.payload);
321
- const reqID = jsonMessage.request_id;
322
- if (ctx["tasks"].has(reqID)) {
323
- const taskData = ctx["tasks"].get(reqID);
324
- const { type: taskType, callback: taskCallback } = taskData;
325
- const taskRespData = getTaskResponseData(taskType, parsedPayload);
326
- if (taskRespData == null) {
327
- taskCallback("error", null);
328
- } else {
329
- taskCallback(
330
- null,
331
- Object.assign(
332
- {
333
- type: taskType,
334
- reqID: reqID
335
- },
336
- taskRespData
337
- )
338
- );
339
- }
340
- }
341
- }
342
- } catch (ex) {
343
- console.error("Message parsing error:", ex);
344
- if (ex.stack) console.error(ex.stack);
345
- return;
346
- }
347
- });
348
- mqttClient.on("close", function () { });
349
- mqttClient.on("disconnect", () => { });
350
- }
351
- function getTaskResponseData(taskType, payload) {
352
- try {
353
- switch (taskType) {
354
- case "send_message_mqtt": {
355
- return {
356
- type: taskType,
357
- threadID: payload.step[1][2][2][1][2],
358
- messageID: payload.step[1][2][2][1][3],
359
- payload: payload.step[1][2]
360
- };
361
- }
362
- case "set_message_reaction": {
363
- return {
364
- mid: payload.step[1][2][2][1][4]
365
- };
366
- }
367
- case "edit_message": {
368
- return {
369
- mid: payload.step[1][2][2][1][2]
370
- };
371
- }
372
- }
373
- } catch (error) {
374
- return null;
375
- }
376
- }
377
- function parseDelta(defaultFuncs, api, ctx, globalCallback, { delta }) {
378
- if (delta.class === "NewMessage") {
379
- if (ctx.globalOptions.pageID && ctx.globalOptions.pageID !== delta.queue)
380
- return;
381
- const resolveAttachmentUrl = i => {
382
- if (
383
- !delta.attachments ||
384
- i === delta.attachments.length ||
385
- utils.getType(delta.attachments) !== "Array"
386
- ) {
387
- let fmtMsg;
388
- try {
389
- fmtMsg = utils.formatDeltaMessage(delta);
390
- } catch (err) {
391
- return log.error("Lỗi Nhẹ", err);
392
- }
393
- if (fmtMsg) {
394
- if (ctx.globalOptions.autoMarkDelivery) {
395
- markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
396
- }
397
- if (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID)
398
- return;
399
- globalCallback(null, fmtMsg);
400
- }
401
- } else {
402
- const attachment = delta.attachments[i];
403
- if (attachment.mercury.attach_type === "photo") {
404
- api.resolvePhotoUrl(attachment.fbid, (err, url) => {
405
- if (!err) attachment.mercury.metadata.url = url;
406
- resolveAttachmentUrl(i + 1);
407
- });
408
- } else {
409
- resolveAttachmentUrl(i + 1);
410
- }
411
- }
412
- };
413
- resolveAttachmentUrl(0);
414
- } else if (delta.class === "ClientPayload") {
415
- const clientPayload = utils.decodeClientPayload(delta.payload);
416
- if (clientPayload && clientPayload.deltas) {
417
- for (const delta of clientPayload.deltas) {
418
- if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
419
- const messageReaction = {
420
- type: "message_reaction",
421
- threadID: (delta.deltaMessageReaction.threadKey.threadFbId
422
- ? delta.deltaMessageReaction.threadKey.threadFbId
423
- : delta.deltaMessageReaction.threadKey.otherUserFbId
424
- ).toString(),
425
- messageID: delta.deltaMessageReaction.messageId,
426
- reaction: delta.deltaMessageReaction.reaction,
427
- senderID: delta.deltaMessageReaction.senderId.toString(),
428
- userID: delta.deltaMessageReaction.userId.toString()
429
- };
430
- globalCallback(null, messageReaction);
431
- } else if (
432
- delta.deltaRecallMessageData &&
433
- !!ctx.globalOptions.listenEvents
434
- ) {
435
- const messageUnsend = {
436
- type: "message_unsend",
437
- threadID: (delta.deltaRecallMessageData.threadKey.threadFbId
438
- ? delta.deltaRecallMessageData.threadKey.threadFbId
439
- : delta.deltaRecallMessageData.threadKey.otherUserFbId
440
- ).toString(),
441
- messageID: delta.deltaRecallMessageData.messageID,
442
- senderID: delta.deltaRecallMessageData.senderID.toString(),
443
- deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
444
- timestamp: delta.deltaRecallMessageData.timestamp
445
- };
446
- globalCallback(null, messageUnsend);
447
- } else if (delta.deltaMessageReply) {
448
- const mdata =
449
- delta.deltaMessageReply.message === undefined
450
- ? []
451
- : delta.deltaMessageReply.message.data === undefined
452
- ? []
453
- : delta.deltaMessageReply.message.data.prng === undefined
454
- ? []
455
- : JSON.parse(delta.deltaMessageReply.message.data.prng);
456
-
457
- const m_id = mdata.map(u => u.i);
458
- const m_offset = mdata.map(u => u.o);
459
- const m_length = mdata.map(u => u.l);
460
- const mentions = {};
461
- for (let i = 0; i < m_id.length; i++) {
462
- mentions[m_id[i]] = (
463
- delta.deltaMessageReply.message.body || ""
464
- ).substring(m_offset[i], m_offset[i] + m_length[i]);
465
- }
466
- const callbackToReturn = {
467
- type: "message_reply",
468
- threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey
469
- .threadFbId
470
- ? delta.deltaMessageReply.message.messageMetadata.threadKey
471
- .threadFbId
472
- : delta.deltaMessageReply.message.messageMetadata.threadKey
473
- .otherUserFbId
474
- ).toString(),
475
- messageID:
476
- delta.deltaMessageReply.message.messageMetadata.messageId,
477
- senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
478
- attachments: (delta.deltaMessageReply.message.attachments || [])
479
- .map(att => {
480
- const mercury = JSON.parse(att.mercuryJSON);
481
- Object.assign(att, mercury);
482
- return att;
483
- })
484
- .map(att => {
485
- let x;
486
- try {
487
- x = utils._formatAttachment(att);
488
- } catch (ex) {
489
- x = att;
490
- x.error = ex;
491
- x.type = "unknown";
492
- }
493
- return x;
494
- }),
495
- args: (delta.deltaMessageReply.message.body || "")
496
- .trim()
497
- .split(/\s+/),
498
- body: delta.deltaMessageReply.message.body || "",
499
- isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey
500
- .threadFbId,
501
- mentions,
502
- timestamp: parseInt(
503
- delta.deltaMessageReply.message.messageMetadata.timestamp
504
- ),
505
- participantIDs: (
506
- delta.deltaMessageReply.message.participants || []
507
- ).map(e => e.toString())
508
- };
509
- if (delta.deltaMessageReply.repliedToMessage) {
510
- const mdata =
511
- delta.deltaMessageReply.repliedToMessage === undefined
512
- ? []
513
- : delta.deltaMessageReply.repliedToMessage.data === undefined
514
- ? []
515
- : delta.deltaMessageReply.repliedToMessage.data.prng ===
516
- undefined
517
- ? []
518
- : JSON.parse(
519
- delta.deltaMessageReply.repliedToMessage.data.prng
520
- );
521
- const m_id = mdata.map(u => u.i);
522
- const m_offset = mdata.map(u => u.o);
523
- const m_length = mdata.map(u => u.l);
524
- const rmentions = {};
525
- for (let i = 0; i < m_id.length; i++) {
526
- rmentions[m_id[i]] = (
527
- delta.deltaMessageReply.repliedToMessage.body || ""
528
- ).substring(m_offset[i], m_offset[i] + m_length[i]);
529
- }
530
- callbackToReturn.messageReply = {
531
- threadID: (delta.deltaMessageReply.repliedToMessage
532
- .messageMetadata.threadKey.threadFbId
533
- ? delta.deltaMessageReply.repliedToMessage.messageMetadata
534
- .threadKey.threadFbId
535
- : delta.deltaMessageReply.repliedToMessage.messageMetadata
536
- .threadKey.otherUserFbId
537
- ).toString(),
538
- messageID:
539
- delta.deltaMessageReply.repliedToMessage.messageMetadata
540
- .messageId,
541
- senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
542
- attachments: delta.deltaMessageReply.repliedToMessage.attachments
543
- .map(att => {
544
- let mercury;
545
- try {
546
- mercury = JSON.parse(att.mercuryJSON);
547
- Object.assign(att, mercury);
548
- } catch (ex) {
549
- mercury = {};
550
- }
551
- return att;
552
- })
553
- .map(att => {
554
- let x;
555
- try {
556
- x = utils._formatAttachment(att);
557
- } catch (ex) {
558
- x = att;
559
- x.error = ex;
560
- x.type = "unknown";
561
- }
562
- return x;
563
- }),
564
- args: (delta.deltaMessageReply.repliedToMessage.body || "")
565
- .trim()
566
- .split(/\s+/),
567
- body: delta.deltaMessageReply.repliedToMessage.body || "",
568
- isGroup: !!delta.deltaMessageReply.repliedToMessage
569
- .messageMetadata.threadKey.threadFbId,
570
- mentions: rmentions,
571
- timestamp: parseInt(
572
- delta.deltaMessageReply.repliedToMessage.messageMetadata
573
- .timestamp
574
- ),
575
- participantIDs: (
576
- delta.deltaMessageReply.repliedToMessage.participants || []
577
- ).map(e => e.toString())
578
- };
579
- } else if (delta.deltaMessageReply.replyToMessageId) {
580
- return defaultFuncs
581
- .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
582
- av: ctx.globalOptions.pageID,
583
- queries: JSON.stringify({
584
- o0: {
585
- doc_id: "2848441488556444",
586
- query_params: {
587
- thread_and_message_id: {
588
- thread_id: callbackToReturn.threadID,
589
- message_id: delta.deltaMessageReply.replyToMessageId.id
590
- }
591
- }
592
- }
593
- })
594
- })
595
- .then(parseAndCheckLogin(ctx, defaultFuncs))
596
- .then(resData => {
597
- if (resData[resData.length - 1].error_results > 0)
598
- throw resData[0].o0.errors;
599
- if (resData[resData.length - 1].successful_results === 0)
600
- throw {
601
- error: "forcedFetch: there was no successful_results",
602
- res: resData
603
- };
604
- const fetchData = resData[0].o0.data.message;
605
- const mobj = {};
606
- for (const n in fetchData.message.ranges) {
607
- mobj[fetchData.message.ranges[n].entity.id] = (
608
- fetchData.message.text || ""
609
- ).substr(
610
- fetchData.message.ranges[n].offset,
611
- fetchData.message.ranges[n].length
612
- );
613
- }
614
- callbackToReturn.messageReply = {
615
- type: "Message",
616
- threadID: callbackToReturn.threadID,
617
- messageID: fetchData.message_id,
618
- senderID: fetchData.message_sender.id.toString(),
619
- attachments: fetchData.message.blob_attachment.map(att =>
620
- utils._formatAttachment({
621
- blob_attachment: att
622
- })
623
- ),
624
- args:
625
- (fetchData.message.text || "").trim().split(/\s+/) || [],
626
- body: fetchData.message.text || "",
627
- isGroup: callbackToReturn.isGroup,
628
- mentions: mobj,
629
- timestamp: parseInt(fetchData.timestamp_precise)
630
- };
631
- })
632
- .catch(err => log.error("forcedFetch", err))
633
- .finally(() => {
634
- if (ctx.globalOptions.autoMarkDelivery) {
635
- markDelivery(
636
- ctx,
637
- api,
638
- callbackToReturn.threadID,
639
- callbackToReturn.messageID
640
- );
641
- }
642
- if (
643
- !ctx.globalOptions.selfListen &&
644
- callbackToReturn.senderID === ctx.userID
645
- )
646
- return;
647
- globalCallback(null, callbackToReturn);
648
- });
649
- } else {
650
- callbackToReturn.delta = delta;
651
- }
652
- if (ctx.globalOptions.autoMarkDelivery) {
653
- markDelivery(
654
- ctx,
655
- api,
656
- callbackToReturn.threadID,
657
- callbackToReturn.messageID
658
- );
659
- }
660
- if (
661
- !ctx.globalOptions.selfListen &&
662
- callbackToReturn.senderID === ctx.userID
663
- )
664
- return;
665
- globalCallback(null, callbackToReturn);
666
- }
667
- }
668
- return;
669
- }
670
- }
671
- switch (delta.class) {
672
- case "ReadReceipt": {
673
- let fmtMsg;
674
- try {
675
- fmtMsg = utils.formatDeltaReadReceipt(delta);
676
- } catch (err) {
677
- return log.error("Lỗi Nhẹ", err);
678
- }
679
- globalCallback(null, fmtMsg);
680
- break;
681
- }
682
- case "AdminTextMessage": {
683
- switch (delta.type) {
684
- case "instant_game_dynamic_custom_update":
685
- case "accept_pending_thread":
686
- case "confirm_friend_request":
687
- case "shared_album_delete":
688
- case "shared_album_addition":
689
- case "pin_messages_v2":
690
- case "unpin_messages_v2":
691
- case "change_thread_theme":
692
- case "change_thread_nickname":
693
- case "change_thread_icon":
694
- case "change_thread_quick_reaction":
695
- case "change_thread_admins":
696
- case "group_poll":
697
- case "joinable_group_link_mode_change":
698
- case "magic_words":
699
- case "change_thread_approval_mode":
700
- case "messenger_call_log":
701
- case "participant_joined_group_call":
702
- case "rtc_call_log":
703
- case "update_vote": {
704
- let fmtMsg;
705
- try {
706
- fmtMsg = utils.formatDeltaEvent(delta);
707
- } catch (err) {
708
- console.log(delta);
709
- return log.error("Lỗi Nhẹ", err);
710
- }
711
- globalCallback(null, fmtMsg);
712
- break;
713
- }
714
- }
715
- break;
716
- }
717
- case "ForcedFetch": {
718
- if (!delta.threadKey) return;
719
- const mid = delta.messageId;
720
- const tid = delta.threadKey.threadFbId;
721
- if (mid && tid) {
722
- const form = {
723
- av: ctx.globalOptions.pageID,
724
- queries: JSON.stringify({
725
- o0: {
726
- doc_id: "2848441488556444",
727
- query_params: {
728
- thread_and_message_id: {
729
- thread_id: tid.toString(),
730
- message_id: mid
731
- }
732
- }
733
- }
734
- })
735
- };
736
- defaultFuncs
737
- .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
738
- .then(parseAndCheckLogin(ctx, defaultFuncs))
739
- .then(resData => {
740
- if (resData[resData.length - 1].error_results > 0)
741
- throw resData[0].o0.errors;
742
- if (resData[resData.length - 1].successful_results === 0)
743
- throw {
744
- error: "forcedFetch: there was no successful_results",
745
- res: resData
746
- };
747
- const fetchData = resData[0].o0.data.message;
748
- if (utils.getType(fetchData) === "Object") {
749
- log.info("forcedFetch", fetchData);
750
- switch (fetchData.__typename) {
751
- case "ThreadImageMessage":
752
- (!ctx.globalOptions.selfListen &&
753
- fetchData.message_sender.id.toString() === ctx.userID) ||
754
- !ctx.loggedIn
755
- ? undefined
756
- : (function () {
757
- globalCallback(null, {
758
- type: "event",
759
- threadID: utils.formatID(tid.toString()),
760
- logMessageType: "log:thread-image",
761
- logMessageData: {
762
- image: {
763
- attachmentID:
764
- fetchData.image_with_metadata &&
765
- fetchData.image_with_metadata
766
- .legacy_attachment_id,
767
- width:
768
- fetchData.image_with_metadata &&
769
- fetchData.image_with_metadata
770
- .original_dimensions.x,
771
- height:
772
- fetchData.image_with_metadata &&
773
- fetchData.image_with_metadata
774
- .original_dimensions.y,
775
- url:
776
- fetchData.image_with_metadata &&
777
- fetchData.image_with_metadata.preview.uri
778
- }
779
- },
780
- logMessageBody: fetchData.snippet,
781
- timestamp: fetchData.timestamp_precise,
782
- author: fetchData.message_sender.id
783
- });
784
- })();
785
- break;
786
- case "UserMessage": {
787
- const event = {
788
- type: "message",
789
- senderID: utils.formatID(fetchData.message_sender.id),
790
- body: fetchData.message.text || "",
791
- threadID: utils.formatID(tid.toString()),
792
- messageID: fetchData.message_id,
793
- attachments: [
794
- {
795
- type: "share",
796
- ID:
797
- fetchData.extensible_attachment.legacy_attachment_id,
798
- url:
799
- fetchData.extensible_attachment.story_attachment.url,
800
- title:
801
- fetchData.extensible_attachment.story_attachment
802
- .title_with_entities.text,
803
- description:
804
- fetchData.extensible_attachment.story_attachment
805
- .description.text,
806
- source:
807
- fetchData.extensible_attachment.story_attachment
808
- .source,
809
- image: (
810
- (
811
- fetchData.extensible_attachment.story_attachment
812
- .media || {}
813
- ).image || {}
814
- ).uri,
815
- width: (
816
- (
817
- fetchData.extensible_attachment.story_attachment
818
- .media || {}
819
- ).image || {}
820
- ).width,
821
- height: (
822
- (
823
- fetchData.extensible_attachment.story_attachment
824
- .media || {}
825
- ).image || {}
826
- ).height,
827
- playable:
828
- (
829
- fetchData.extensible_attachment.story_attachment
830
- .media || {}
831
- ).is_playable || false,
832
- duration:
833
- (
834
- fetchData.extensible_attachment.story_attachment
835
- .media || {}
836
- ).playable_duration_in_ms || 0,
837
- subattachments:
838
- fetchData.extensible_attachment.subattachments,
839
- properties:
840
- fetchData.extensible_attachment.story_attachment
841
- .properties
842
- }
843
- ],
844
- mentions: {},
845
- timestamp: parseInt(fetchData.timestamp_precise),
846
- isGroup: fetchData.message_sender.id !== tid.toString()
847
- };
848
- log.info("ff-Return", event);
849
- globalCallback(null, event);
850
- break;
851
- }
852
- default:
853
- log.error("forcedFetch", fetchData);
854
- }
855
- } else {
856
- log.error("forcedFetch", fetchData);
857
- }
858
- })
859
- .catch(err => log.error("forcedFetch", err));
860
- }
861
- break;
862
- }
863
- case "ThreadName":
864
- case "ParticipantsAddedToGroupThread":
865
- case "ParticipantLeftGroupThread": {
866
- let formattedEvent;
867
- try {
868
- formattedEvent = utils.formatDeltaEvent(delta);
869
- } catch (err) {
870
- console.log(err);
871
- return log.error("Lỗi Nhẹ", err);
872
- }
873
- if (
874
- !ctx.globalOptions.selfListen &&
875
- formattedEvent.author.toString() === ctx.userID
876
- )
877
- return;
878
- if (!ctx.loggedIn) return;
879
- globalCallback(null, formattedEvent);
880
- break;
881
- }
882
- case "NewMessage": {
883
- const hasLiveLocation = delta => {
884
- const attachment =
885
- delta.attachments?.[0]?.mercury?.extensible_attachment;
886
- const storyAttachment = attachment?.story_attachment;
887
- return storyAttachment?.style_list?.includes("message_live_location");
888
- };
889
- if (delta.attachments?.length === 1 && hasLiveLocation(delta)) {
890
- delta.class = "UserLocation";
891
- try {
892
- const fmtMsg = utils.formatDeltaEvent(delta);
893
- globalCallback(null, fmtMsg);
894
- } catch (err) {
895
- console.log(delta);
896
- log.error("Lỗi Nhẹ", err);
897
- }
898
- }
899
- break;
900
- }
901
- }
902
- }
903
- function markDelivery(ctx, api, threadID, messageID) {
904
- if (threadID && messageID) {
905
- api.markAsDelivered(threadID, messageID, err => {
906
- if (err) log.error("markAsDelivered", err);
907
- else {
908
- if (ctx.globalOptions.autoMarkRead) {
909
- api.markAsRead(threadID, err => {
910
- if (err) log.error("markAsDelivered", err);
911
- });
912
- }
913
- }
914
- });
915
- }
916
- }
917
- module.exports = function (defaultFuncs, api, ctx) {
918
- let globalCallback = identity;
919
- getSeqID = function getSeqID() {
920
- ctx.t_mqttCalled = false;
921
- defaultFuncs
922
- .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
923
- .then(parseAndCheckLogin(ctx, defaultFuncs))
924
- .then(resData => {
925
- if (utils.getType(resData) !== "Array")
926
- throw { error: "Not logged in", res: resData };
927
- if (!Array.isArray(resData) || !resData.length) return;
928
- const lastRes = resData[resData.length - 1];
929
- if (lastRes?.successful_results === 0) return;
930
- const syncSeqId = resData[0]?.o0?.data?.viewer?.message_threads?.sync_sequence_id;
931
- if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
932
- ctx.lastSeqId = syncSeqId
933
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
934
- } else
935
- throw {
936
- error: "getSeqId: no sync_sequence_id found.",
937
- res: resData
938
- };
939
- })
940
- .catch(err => {
941
- // log.error("getSeqId", err);
942
- if (utils.getType(err) === "Object" && err.error === "Not logged in")
943
- ctx.loggedIn = false;
944
- return globalCallback(err);
945
- });
946
- };
947
- return function (callback) {
948
- class MessageEmitter extends EventEmitter {
949
- stopListening(callback) {
950
- callback = callback || (() => { });
951
- globalCallback = identity;
952
- if (ctx.mqttClient) {
953
- ctx.mqttClient.unsubscribe("/webrtc");
954
- ctx.mqttClient.unsubscribe("/rtc_multi");
955
- ctx.mqttClient.unsubscribe("/onevc");
956
- ctx.mqttClient.publish("/browser_close", "{}");
957
- ctx.mqttClient.end(false, function (...data) {
958
- callback(data);
959
- ctx.mqttClient = undefined;
960
- });
961
- }
962
- }
963
- async stopListeningAsync() {
964
- return new Promise(resolve => {
965
- this.stopListening(resolve);
966
- });
967
- }
968
- }
969
- const msgEmitter = new MessageEmitter();
970
- globalCallback =
971
- callback ||
972
- function (error, message) {
973
- if (error) {
974
- return msgEmitter.emit("error", error);
975
- }
976
- msgEmitter.emit("message", message);
977
- };
978
- if (!ctx.firstListen) ctx.lastSeqId = null;
979
- ctx.syncToken = undefined;
980
- ctx.t_mqttCalled = false;
981
- form = {
982
- av: ctx.globalOptions.pageID,
983
- queries: JSON.stringify({
984
- o0: {
985
- doc_id: "3336396659757871",
986
- query_params: {
987
- limit: 1,
988
- before: null,
989
- tags: ["INBOX"],
990
- includeDeliveryReceipts: false,
991
- includeSeqID: true
992
- }
993
- }
994
- })
995
- };
996
- if (!ctx.firstListen || !ctx.lastSeqId) {
997
- getSeqID(defaultFuncs, api, ctx, globalCallback);
998
- } else {
999
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
1000
- }
1001
- api.stopListening = msgEmitter.stopListening;
1002
- api.stopListeningAsync = msgEmitter.stopListeningAsync;
1003
- return msgEmitter;
1004
- };
1005
- };
1
+ "use strict";
2
+ const mqtt = require("mqtt");
3
+ const WebSocket = require("ws");
4
+ const HttpsProxyAgent = require("https-proxy-agent");
5
+ const EventEmitter = require("events");
6
+ const logger = require("../../../func/logger");
7
+ const { parseAndCheckLogin } = require("../../utils/client");
8
+ const { buildProxy, buildStream } = require("./detail/buildStream");
9
+ const { topics } = require("./detail/constants");
10
+ const createParseDelta = require("./core/parseDelta");
11
+ const createListenMqtt = require("./core/connectMqtt");
12
+ const createGetSeqID = require("./core/getSeqID");
13
+ const markDelivery = require("./core/markDelivery");
14
+ const getTaskResponseData = require("./core/getTaskResponseData");
15
+ const parseDelta = createParseDelta({ markDelivery, parseAndCheckLogin });
16
+ const listenMqtt = createListenMqtt({ WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy, topics, parseDelta, getTaskResponseData, logger });
17
+ const getSeqIDFactory = createGetSeqID({ listenMqtt });
18
+ module.exports = function (defaultFuncs, api, ctx) {
19
+ const identity = function () {};
20
+ let globalCallback = identity;
21
+ function getSeqIDWrapper() {
22
+ const form = {
23
+ av: ctx.globalOptions.pageID,
24
+ queries: JSON.stringify({
25
+ o0: {
26
+ doc_id: "3336396659757871",
27
+ query_params: {
28
+ limit: 1,
29
+ before: null,
30
+ tags: ["INBOX"],
31
+ includeDeliveryReceipts: false,
32
+ includeSeqID: true
33
+ }
34
+ }
35
+ })
36
+ };
37
+ return getSeqIDFactory(defaultFuncs, api, ctx, globalCallback, form);
38
+ }
39
+ return function (callback) {
40
+ class MessageEmitter extends EventEmitter {
41
+ stopListening(callback2) {
42
+ const cb = callback2 || function () {};
43
+ globalCallback = identity;
44
+ if (ctx.mqttClient) {
45
+ ctx.mqttClient.unsubscribe("/webrtc");
46
+ ctx.mqttClient.unsubscribe("/rtc_multi");
47
+ ctx.mqttClient.unsubscribe("/onevc");
48
+ ctx.mqttClient.publish("/browser_close", "{}");
49
+ ctx.mqttClient.end(false, function (...data) {
50
+ cb(data);
51
+ ctx.mqttClient = undefined;
52
+ });
53
+ }
54
+ }
55
+ async stopListeningAsync() {
56
+ return new Promise(resolve => {
57
+ this.stopListening(resolve);
58
+ });
59
+ }
60
+ }
61
+ const msgEmitter = new MessageEmitter();
62
+ globalCallback = callback || function (error, message) {
63
+ if (error) {
64
+ return msgEmitter.emit("error", error);
65
+ }
66
+ msgEmitter.emit("message", message);
67
+ };
68
+ if (!ctx.firstListen) ctx.lastSeqId = null;
69
+ ctx.syncToken = undefined;
70
+ ctx.t_mqttCalled = false;
71
+ if (!ctx.firstListen || !ctx.lastSeqId) {
72
+ getSeqIDWrapper();
73
+ } else {
74
+ listenMqtt(defaultFuncs, api, ctx, globalCallback);
75
+ }
76
+ api.stopListening = msgEmitter.stopListening;
77
+ api.stopListeningAsync = msgEmitter.stopListeningAsync;
78
+ return msgEmitter;
79
+ };
80
+ };