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