fca-shankar-bot 20.2.0 → 20.3.0

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 (53) hide show
  1. package/.replit +14 -1
  2. package/Extra/Balancer.js +49 -0
  3. package/Extra/ExtraAddons.js +4 -4
  4. package/Extra/ExtraGetThread.js +27 -27
  5. package/Extra/ExtraScreenShot.js +3 -3
  6. package/Extra/ExtraUptimeRobot.js +4 -4
  7. package/Extra/Src/Change_Environment.js +2 -2
  8. package/Extra/Src/Check_Update.js +3 -3
  9. package/Extra/Src/Instant_Update.js +2 -2
  10. package/Extra/Src/Premium.js +7 -7
  11. package/Extra/Src/Release_Memory.js +7 -7
  12. package/Extra/Src/Websocket.js +12 -12
  13. package/Func/ClearCache.js +2 -2
  14. package/LICENSE +21 -0
  15. package/Language/index.json +22 -16
  16. package/Main.js +515 -349
  17. package/README.md +198 -0
  18. package/SECURITY.md +17 -0
  19. package/broadcast.js +44 -0
  20. package/index.js +215 -31
  21. package/logger.js +51 -122
  22. package/package.json +17 -15
  23. package/src/Dev_Horizon_Data.js +2 -2
  24. package/src/editMessage.js +45 -38
  25. package/src/listenMqtt.js +395 -373
  26. package/src/listenMqttV1.js +11 -11
  27. package/src/sendMessage.js +2 -2
  28. package/src/sendMqttMessage.js +51 -251
  29. package/src/setMessageReaction.js +64 -66
  30. package/src/shareContact.js +50 -75
  31. package/src/unsendMessage.js +32 -126
  32. package/src/unsendMqttMessage.js +66 -0
  33. package/test/data/shareAttach.js +146 -0
  34. package/test/data/something.mov +0 -0
  35. package/test/data/test.png +0 -0
  36. package/test/data/test.txt +7 -0
  37. package/test/example-config.json +18 -0
  38. package/test/test-page.js +140 -0
  39. package/test/test.js +385 -0
  40. package/test/testv2.js +3 -0
  41. package/utils.js +50 -11
  42. package/.cache/replit/env/latest +0 -49
  43. package/.cache/replit/env/latest.json +0 -1
  44. package/.cache/replit/modules/nodejs-20.res +0 -1
  45. package/.cache/replit/modules/replit.res +0 -1
  46. package/.cache/replit/modules.stamp +0 -0
  47. package/.cache/replit/toolchain.json +0 -1
  48. package/CountTime.json +0 -1
  49. package/src/followUser.js +0 -171
  50. package/src/getFacebookInfo.js +0 -69
  51. package/src/listenMqtt.jk +0 -732
  52. package/src/postStory.js +0 -122
  53. package/src/refreshFb_dtsg.js +0 -81
package/src/listenMqtt.jk DELETED
@@ -1,732 +0,0 @@
1
- 'use strict';
2
-
3
- const utils = require('../utils');
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
- const log = require('npmlog');
11
- const { join } = require('path');
12
-
13
- // Constants
14
- const MQTT_KEEPALIVE = 60;
15
- const MQTT_RECONNECT_PERIOD = 3;
16
- const MQTT_RESTART_INTERVAL = 60 * 60 * 1000; // 60 minutes
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'];
18
-
19
- // Global state
20
- global.Fca.Data.MsgCount = new Map();
21
- global.Fca.Data.event = new Map();
22
- let WebSocket_Global;
23
- let getSeqID;
24
- let form = {};
25
-
26
- function buildProxy() {
27
- return new Transform({
28
- objectMode: false,
29
- transform(chunk, enc, next) {
30
- if (WebSocket_Global.readyState !== WebSocket_Global.OPEN) return next();
31
- const data = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : chunk;
32
- WebSocket_Global.send(data);
33
- next();
34
- },
35
- flush(done) {
36
- WebSocket_Global.close();
37
- done();
38
- }
39
- });
40
- }
41
-
42
- function buildStream(options, WebSocket, Proxy) {
43
- const Stream = Duplexify(undefined, undefined, options);
44
- Stream.socket = WebSocket;
45
-
46
- WebSocket.onclose = () => {
47
- Stream.end();
48
- Stream.destroy();
49
- };
50
-
51
- WebSocket.onerror = (err) => Stream.destroy(err);
52
-
53
- WebSocket.onmessage = (event) => {
54
- const data = event.data instanceof ArrayBuffer ? Buffer.from(event.data) : Buffer.from(event.data, 'utf8');
55
- Stream.push(data);
56
- };
57
-
58
- WebSocket.onopen = () => {
59
- Stream.setReadable(Proxy);
60
- Stream.setWritable(Proxy);
61
- Stream.emit('connect');
62
- };
63
-
64
- WebSocket_Global = WebSocket;
65
- Proxy.on('close', () => WebSocket.close());
66
-
67
- return Stream;
68
- }
69
-
70
- function setupAutoRestart(ctx) {
71
- if (!global.Fca.Data.Setup && global.Fca.Require.ShankarConfig.RestartMQTT_Minutes !== 0) {
72
- global.Fca.Data.Setup = true;
73
- setTimeout(() => {
74
- log.warn('MQTT', 'Auto restarting MQTT client...');
75
- ctx.mqttClient.end();
76
- global.Fca.Data.Setup = false;
77
- getSeqID();
78
- }, MQTT_RESTART_INTERVAL);
79
- }
80
- }
81
-
82
- function setupMemoryManager() {
83
- if (process.env.OnStatus === undefined) {
84
- const MemoryManager = require('../Extra/Src/Release_Memory');
85
-
86
- const settings = {
87
- warningThreshold: 0.7,
88
- releaseThreshold: 0.8,
89
- maxThreshold: 0.9,
90
- interval: 60 * 1000,
91
- logLevel: 'warn',
92
- logFile: join(process.cwd(), 'Shankar_Database', 'memory.log'),
93
- smartReleaseEnabled: true,
94
- allowLog: global.Fca.Require.ShankarConfig.AntiStuckAndMemoryLeak.LogFile.Use || false
95
- };
96
-
97
- const memoryManager = new MemoryManager(settings);
98
- memoryManager.autoStart(60 * 60 * 1000);
99
-
100
- if (global.Fca.Require.ShankarConfig.AntiStuckAndMemoryLeak.AutoRestart.Use) {
101
- memoryManager.onMaxMemory(() => {
102
- log.warn('Memory', 'Usage >= 90% - Auto restarting to avoid crash');
103
- process.exit(1);
104
- });
105
- }
106
-
107
- process.env.OnStatus = true;
108
- }
109
- }
110
- function markDelivery(ctx, api, threadID, messageID) {
111
- if (threadID && messageID) {
112
- api.markAsDelivered(threadID, messageID, (err) => {
113
- if (err) log.error('markAsDelivered', err);
114
- else if (ctx.globalOptions.autoMarkRead) {
115
- api.markAsRead(threadID, (err) => {
116
- if (err) log.error('markAsRead', err);
117
- });
118
- }
119
- });
120
- }
121
- }
122
-
123
- function handleMessageReply(deltaMessageReply, ctx, defaultFuncs, api, globalCallback) {
124
- const mentions = {};
125
- const mdata = deltaMessageReply.message?.data?.prng;
126
-
127
- if (mdata) {
128
- const parsedMdata = JSON.parse(mdata);
129
- parsedMdata.forEach(mention => {
130
- mentions[mention.i] = deltaMessageReply.message.body.substring(mention.o, mention.o + mention.l);
131
- });
132
- }
133
-
134
- const messageReply = {
135
- type: 'message_reply',
136
- threadID: (deltaMessageReply.message.messageMetadata.threadKey.threadFbId || deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
137
- messageID: deltaMessageReply.message.messageMetadata.messageId,
138
- senderID: deltaMessageReply.message.messageMetadata.actorFbId.toString(),
139
- attachments: (deltaMessageReply.message.attachments || []).map(att => {
140
- const mercury = JSON.parse(att.mercuryJSON);
141
- return utils._formatAttachment({ ...att, ...mercury });
142
- }),
143
- body: deltaMessageReply.message.body || '',
144
- isGroup: !!deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
145
- mentions,
146
- timestamp: parseInt(deltaMessageReply.message.messageMetadata.timestamp),
147
- participantIDs: (deltaMessageReply.message.participants || []).map(id => id.toString())
148
- };
149
-
150
- if (deltaMessageReply.repliedToMessage) {
151
- messageReply.messageReply = formatRepliedMessage(deltaMessageReply.repliedToMessage);
152
- } else if (deltaMessageReply.replyToMessageId) {
153
- fetchRepliedMessage(deltaMessageReply, ctx, defaultFuncs, messageReply, globalCallback);
154
- return;
155
- }
156
-
157
- if (ctx.globalOptions.autoMarkDelivery) {
158
- markDelivery(ctx, api, messageReply.threadID, messageReply.messageID);
159
- }
160
-
161
- if (!ctx.globalOptions.selfListen && messageReply.senderID === ctx.userID) return;
162
- globalCallback(null, messageReply);
163
- }
164
-
165
- function formatRepliedMessage(repliedToMessage) {
166
- const mentions = {};
167
- const mdata = repliedToMessage.data?.prng;
168
-
169
- if (mdata) {
170
- const parsedMdata = JSON.parse(mdata);
171
- parsedMdata.forEach(mention => {
172
- mentions[mention.i] = repliedToMessage.body.substring(mention.o, mention.o + mention.l);
173
- });
174
- }
175
-
176
- return {
177
- threadID: (repliedToMessage.messageMetadata.threadKey.threadFbId || repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
178
- messageID: repliedToMessage.messageMetadata.messageId,
179
- senderID: repliedToMessage.messageMetadata.actorFbId.toString(),
180
- attachments: repliedToMessage.attachments.map(att => {
181
- const mercury = JSON.parse(att.mercuryJSON);
182
- return utils._formatAttachment({ ...att, ...mercury });
183
- }),
184
- body: repliedToMessage.body || '',
185
- isGroup: !!repliedToMessage.messageMetadata.threadKey.threadFbId,
186
- mentions,
187
- timestamp: parseInt(repliedToMessage.messageMetadata.timestamp),
188
- participantIDs: (repliedToMessage.participants || []).map(id => id.toString())
189
- };
190
- }
191
-
192
- function fetchRepliedMessage(deltaMessageReply, ctx, defaultFuncs, messageReply, globalCallback) {
193
- const form = {
194
- av: ctx.globalOptions.pageID,
195
- queries: JSON.stringify({
196
- o0: {
197
- doc_id: '2848441488556444',
198
- query_params: {
199
- thread_and_message_id: {
200
- thread_id: messageReply.threadID,
201
- message_id: deltaMessageReply.replyToMessageId.id
202
- }
203
- }
204
- }
205
- })
206
- };
207
-
208
- defaultFuncs
209
- .post('https://www.facebook.com/api/graphqlbatch/', ctx.jar, form)
210
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
211
- .then((json) => {
212
- if (json[json.length - 1].error_results > 0) throw json[0].o0.errors;
213
- if (json[json.length - 1].successful_results === 0) throw new Error('Failed to fetch replied message');
214
-
215
- const fetchData = json[0].o0.data.message;
216
- const mobj = {};
217
-
218
- if (fetchData.message.ranges) {
219
- for (const range of fetchData.message.ranges) {
220
- mobj[range.entity.id] = fetchData.message.text.substr(range.offset, range.length);
221
- }
222
- }
223
-
224
- messageReply.messageReply = {
225
- threadID: messageReply.threadID,
226
- messageID: fetchData.message_id,
227
- senderID: fetchData.message_sender.id.toString(),
228
- attachments: fetchData.message.blob_attachment.map(att =>
229
- utils._formatAttachment({ blob_attachment: att })
230
- ),
231
- body: fetchData.message.text || '',
232
- isGroup: messageReply.isGroup,
233
- mentions: mobj,
234
- timestamp: parseInt(fetchData.timestamp_precise)
235
- };
236
-
237
- if (ctx.globalOptions.autoMarkDelivery) {
238
- markDelivery(ctx, api, messageReply.threadID, messageReply.messageID);
239
- }
240
-
241
- if (!ctx.globalOptions.selfListen && messageReply.senderID === ctx.userID) return;
242
- globalCallback(null, messageReply);
243
- })
244
- .catch(err => log.error('Failed to fetch replied message:', err));
245
- }
246
- function parseDelta(defaultFuncs, api, ctx, globalCallback, { delta }) {
247
- if (delta.class === 'NewMessage') {
248
- if (ctx.globalOptions.pageID && ctx.globalOptions.pageID !== delta.queue) return;
249
-
250
- const resolveAttachmentUrl = (i) => {
251
- if (!delta.attachments || i >= delta.attachments.length) {
252
- let fmtMsg;
253
- try {
254
- fmtMsg = utils.formatDeltaMessage(delta);
255
- } catch (err) {
256
- return log.error('Format Delta Message', err);
257
- }
258
-
259
- if (fmtMsg) {
260
- const { isGroup, threadID, messageID } = fmtMsg;
261
- global.Fca.Data.event.set("Data", { isGroup, threadID, messageID });
262
-
263
- if (global.Fca.Require.ShankarConfig.AntiGetInfo.AntiGetThreadInfo) {
264
- global.Fca.Data.MsgCount.set(threadID, (global.Fca.Data.MsgCount.get(threadID) || 0) + 1);
265
- }
266
-
267
- if (ctx.globalOptions.autoMarkDelivery) {
268
- markDelivery(ctx, api, threadID, messageID);
269
- }
270
-
271
- if (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID) return;
272
- globalCallback(null, fmtMsg);
273
- }
274
- } else {
275
- const attachment = delta.attachments[i];
276
- if (attachment.mercury.attach_type === 'photo') {
277
- api.resolvePhotoUrl(attachment.fbid, (err, url) => {
278
- if (!err) attachment.mercury.metadata.url = url;
279
- resolveAttachmentUrl(i + 1);
280
- });
281
- } else {
282
- resolveAttachmentUrl(i + 1);
283
- }
284
- }
285
- };
286
-
287
- resolveAttachmentUrl(0);
288
- } else if (delta.class === 'ClientPayload') {
289
- const clientPayload = utils.decodeClientPayload(delta.payload);
290
- if (clientPayload && clientPayload.deltas) {
291
- for (const payloadDelta of clientPayload.deltas) {
292
- if (payloadDelta.deltaMessageReaction && ctx.globalOptions.listenEvents) {
293
- const messageReaction = {
294
- type: 'message_reaction',
295
- threadID: (payloadDelta.deltaMessageReaction.threadKey.threadFbId || payloadDelta.deltaMessageReaction.threadKey.otherUserFbId).toString(),
296
- messageID: payloadDelta.deltaMessageReaction.messageId,
297
- reaction: payloadDelta.deltaMessageReaction.reaction,
298
- senderID: payloadDelta.deltaMessageReaction.senderId.toString(),
299
- userID: payloadDelta.deltaMessageReaction.userId.toString()
300
- };
301
- globalCallback(null, messageReaction);
302
- } else if (payloadDelta.deltaRecallMessageData && ctx.globalOptions.listenEvents) {
303
- const messageUnsend = {
304
- type: 'message_unsend',
305
- threadID: (payloadDelta.deltaRecallMessageData.threadKey.threadFbId || payloadDelta.deltaRecallMessageData.threadKey.otherUserFbId).toString(),
306
- messageID: payloadDelta.deltaRecallMessageData.messageID,
307
- senderID: payloadDelta.deltaRecallMessageData.senderID.toString(),
308
- deletionTimestamp: payloadDelta.deltaRecallMessageData.deletionTimestamp,
309
- timestamp: payloadDelta.deltaRecallMessageData.timestamp
310
- };
311
- globalCallback(null, messageUnsend);
312
- } else if (payloadDelta.deltaMessageReply) {
313
- handleMessageReply(payloadDelta.deltaMessageReply, ctx, defaultFuncs, api, globalCallback);
314
- }
315
- }
316
- }
317
- } else {
318
- switch (delta.class) {
319
- case 'AdminTextMessage': {
320
- switch (delta.type) {
321
- case 'joinable_group_link_mode_change':
322
- case 'magic_words':
323
- case 'pin_messages_v2':
324
- case 'change_thread_theme':
325
- case 'change_thread_icon':
326
- case 'change_thread_nickname':
327
- case 'change_thread_admins':
328
- case 'group_poll':
329
- case 'messenger_call_log':
330
- case 'participant_joined_group_call':
331
- case 'change_thread_approval_mode':
332
- case 'thread_image_set':
333
- case 'group_status_changes': {
334
- try {
335
- let fmtMsg = utils.formatDeltaEvent(delta);
336
-
337
- if (delta.type === 'joinable_group_link_mode_change') {
338
- fmtMsg = {
339
- type: "event",
340
- threadID: delta.threadKey.threadFbId ? delta.threadKey.threadFbId : delta.threadKey.otherUserFbId,
341
- logMessageType: "log:link-status",
342
- author: delta.author,
343
- inviteLink: delta.joinableMode?.link || null,
344
- allowedToJoin: delta.joinableMode?.isEnabled || false,
345
- timestamp: Date.now()
346
- };
347
- }
348
- globalCallback(null, fmtMsg);
349
- } catch (err) {
350
- log.error('AdminMessage', err);
351
- }
352
- break;
353
- }
354
- }
355
- break;
356
- }
357
- case 'ThreadName':
358
- case 'ParticipantsAddedToGroupThread':
359
- case 'ParticipantLeftGroupThread': {
360
- try {
361
- const fmtMsg = utils.formatDeltaEvent(delta);
362
- if (!ctx.globalOptions.selfListen && fmtMsg.author.toString() === ctx.userID) return;
363
- if (!ctx.loggedIn) return;
364
- globalCallback(null, fmtMsg);
365
- } catch (err) {
366
- log.error('ThreadEvent', err);
367
- }
368
- break;
369
- }
370
- case 'ReadReceipt': {
371
- try {
372
- const fmtMsg = utils.formatDeltaReadReceipt(delta);
373
- globalCallback(null, fmtMsg);
374
- } catch (err) {
375
- log.error('ReadReceipt', err);
376
- }
377
- break;
378
- }
379
- }
380
- }
381
- }
382
-
383
- function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
384
- const username = {
385
- u: ctx.userID,
386
- s: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER),
387
- chat_on: ctx.globalOptions.online,
388
- fg: false,
389
- d: utils.getGUID(),
390
- ct: 'websocket',
391
- aid: '219994525426954',
392
- mqtt_sid: '',
393
- cp: 3,
394
- ecp: 10,
395
- st: [],
396
- pm: [],
397
- dc: '',
398
- no_auto_fg: true,
399
- gas: null,
400
- pack: []
401
- };
402
-
403
- let host;
404
- if (ctx.mqttEndpoint) {
405
- host = `${ctx.mqttEndpoint}&sid=${username.s}&cid=${username.d}`;
406
- } else if (ctx.region) {
407
- host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${username.s}&cid=${username.d}`;
408
- } else {
409
- host = `wss://edge-chat.facebook.com/chat?sid=${username.s}&cid=${username.d}`;
410
- }
411
-
412
- const options = {
413
- clientId: 'mqttwsclient',
414
- protocolId: 'MQIsdp',
415
- protocolVersion: 3,
416
- username: JSON.stringify(username),
417
- clean: true,
418
- wsOptions: {
419
- headers: {
420
- 'Cookie': ctx.jar.getCookies('https://www.facebook.com').join('; '),
421
- 'Origin': 'https://www.facebook.com',
422
- 'User-Agent': ctx.globalOptions.userAgent,
423
- 'Referer': 'https://www.facebook.com/',
424
- 'Host': new URL(host).hostname
425
- },
426
- origin: 'https://www.facebook.com',
427
- protocolVersion: 13,
428
- binaryType: 'arraybuffer'
429
- },
430
- keepalive: MQTT_KEEPALIVE,
431
- reschedulePings: true,
432
- reconnectPeriod: MQTT_RECONNECT_PERIOD
433
- };
434
-
435
- if (ctx.globalOptions.proxy) {
436
- options.wsOptions.agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
437
- }
438
-
439
- ctx.mqttClient = new mqtt.Client(() =>
440
- buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()),
441
- options
442
- );
443
- global.mqttClient = ctx.mqttClient;
444
-
445
- global.mqttClient.on('error', (err) => {
446
- log.error('MQTT Error:', err);
447
- global.mqttClient.end();
448
-
449
- if (!ctx.lastSeqId || ctx.globalOptions.autoReconnect) {
450
- getSeqID();
451
- } else {
452
- globalCallback({ type: 'stop_listen', error: 'Connection Error - Auto Restart' });
453
- process.exit(1);
454
- }
455
- });
456
-
457
- global.mqttClient.on('connect', () => {
458
- setupAutoRestart(ctx);
459
- setupMemoryManager();
460
-
461
- if (!ctx.lastSeqId) {
462
- log.error("No sequence ID found - Reconnecting...");
463
- global.mqttClient.end();
464
- getSeqID();
465
- return;
466
- }
467
-
468
- TOPICS.forEach(topic => global.mqttClient.subscribe(topic));
469
-
470
- const queue = {
471
- sync_api_version: 11,
472
- max_deltas_able_to_process: 100,
473
- delta_batch_size: 500,
474
- encoding: 'JSON',
475
- entity_fbid: ctx.userID,
476
- initial_titan_sequence_id: ctx.lastSeqId,
477
- device_params: null
478
- };
479
-
480
- global.mqttClient.publish('/messenger_sync_create_queue',
481
- JSON.stringify(queue),
482
- { qos: 1, retain: false }
483
- );
484
-
485
- const restartTimeout = setTimeout(() => {
486
- log.warn("MQTT Sync timeout - Reconnecting...");
487
- global.mqttClient.end();
488
- getSeqID();
489
- }, 5000);
490
-
491
- ctx.tmsWait = () => {
492
- clearTimeout(restartTimeout);
493
- if (ctx.globalOptions.emitReady) {
494
- globalCallback({ type: "ready", error: null });
495
- }
496
- delete ctx.tmsWait;
497
- };
498
- });
499
-
500
- global.mqttClient.on('message', (topic, message) => {
501
- try {
502
- const jsonMessage = JSON.parse(message.toString());
503
-
504
- switch (topic) {
505
- case '/t_ms': {
506
- if (ctx.tmsWait && typeof ctx.tmsWait === "function") ctx.tmsWait();
507
-
508
- if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
509
- ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
510
- ctx.syncToken = jsonMessage.syncToken;
511
- }
512
-
513
- if (jsonMessage.lastIssuedSeqId) {
514
- ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
515
- }
516
-
517
- for (const delta of jsonMessage.deltas || []) {
518
- parseDelta(defaultFuncs, api, ctx, globalCallback, { delta });
519
- }
520
- break;
521
- }
522
- case '/thread_typing':
523
- case '/orca_typing_notifications': {
524
- const typ = {
525
- type: "typ",
526
- isTyping: !!jsonMessage.state,
527
- from: jsonMessage.sender_fbid.toString(),
528
- threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
529
- };
530
- globalCallback(null, typ);
531
- break;
532
- }
533
- case '/orca_presence': {
534
- if (!ctx.globalOptions.updatePresence) {
535
- for (const data of jsonMessage.list || []) {
536
- const presence = {
537
- type: "presence",
538
- userID: data.u.toString(),
539
- timestamp: data.l * 1000,
540
- statuses: data.p
541
- };
542
- globalCallback(null, presence);
543
- }
544
- }
545
- break;
546
- }
547
- case '/ls_resp': {
548
- if (!jsonMessage.request_id || !ctx.callback_Task[jsonMessage.request_id]) {
549
- break;
550
- }
551
- const { callback, type } = ctx.callback_Task[jsonMessage.request_id];
552
- if (callback && type) {
553
- try {
554
- const payload = JSON.parse(jsonMessage.payload);
555
- const data = {
556
- type,
557
- ...(type === 'sendMqttMessage' ? {
558
- threadID: payload.step[1][2][2][1][2],
559
- messageID: payload.step[1][2][2][1][3],
560
- } : {
561
- data: payload.step[1][2][2][1],
562
- }),
563
- payload: payload.step[1][2]
564
- };
565
- callback(null, data);
566
- } catch (err) {
567
- callback("Failed to handle LS response", null);
568
- }
569
- }
570
- break;
571
- }
572
- }
573
- } catch (err) {
574
- log.error('Failed to process MQTT message:', err);
575
- }
576
- });
577
- }
578
- // Main Export Function
579
- module.exports = function(defaultFuncs, api, ctx) {
580
- // Initialize getSeqID function
581
- getSeqID = function() {
582
- ctx.t_mqttCalled = false;
583
-
584
- form = {
585
- av: ctx.globalOptions.pageID,
586
- queries: JSON.stringify({
587
- o0: {
588
- doc_id: '3336396659757871',
589
- query_params: {
590
- limit: 1,
591
- before: null,
592
- tags: ['INBOX'],
593
- includeDeliveryReceipts: false,
594
- includeSeqID: true
595
- }
596
- }
597
- })
598
- };
599
-
600
- defaultFuncs
601
- .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
602
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
603
- .then((resData) => {
604
- if (utils.getType(resData) !== "Array") {
605
- if (global.Fca.Require.ShankarConfig.AutoLogin) {
606
- global.Fca.Require.logger.Warning(global.Fca.Require.Language.Index.AutoLogin, () => {
607
- return global.Fca.Action('AutoLogin');
608
- });
609
- } else {
610
- global.Fca.Require.logger.Error(global.Fca.Require.Language.Index.ErrAppState);
611
- }
612
- return;
613
- }
614
-
615
- if (resData[resData.length - 1].error_results > 0) {
616
- throw resData[0].o0.errors;
617
- }
618
-
619
- if (resData[resData.length - 1].successful_results === 0) {
620
- throw new Error("getSeqId: no successful_results");
621
- }
622
-
623
- const viewer = resData[0].o0.data.viewer;
624
- if (viewer && viewer.message_threads && viewer.message_threads.sync_sequence_id) {
625
- ctx.lastSeqId = viewer.message_threads.sync_sequence_id;
626
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
627
- log.info("Got sequence ID:", ctx.lastSeqId);
628
- } else {
629
- throw new Error("getSeqId: no sync_sequence_id found");
630
- }
631
- })
632
- .catch((err) => {
633
- log.error("GetSeqID Error:", err);
634
- if (utils.getType(err) == "Object" && err.error === global.Fca.Require.Language.Index.ErrAppState) {
635
- ctx.loggedIn = false;
636
- }
637
-
638
- setTimeout(() => {
639
- getSeqID();
640
- }, 5000);
641
-
642
- return globalCallback(err);
643
- });
644
- };
645
-
646
- // Create message emitter
647
- const msgEmitter = new EventEmitter();
648
-
649
- // Initialize global callback
650
- let globalCallback = (error, message) => {
651
- if (error) return msgEmitter.emit('error', error);
652
- msgEmitter.emit('message', message);
653
- };
654
-
655
- // Add stop listening method
656
- msgEmitter.stopListening = () => {
657
- if (ctx.mqttClient) {
658
- TOPICS.forEach(topic => ctx.mqttClient.unsubscribe(topic));
659
- ctx.mqttClient.publish('/browser_close', '{}');
660
- ctx.mqttClient.end(false, () => {
661
- ctx.mqttClient = undefined;
662
- });
663
- }
664
- global.Fca.Data.StopListening = true;
665
- };
666
-
667
- // Initialize listening
668
- if (!ctx.firstListen) ctx.lastSeqId = null;
669
- ctx.syncToken = undefined;
670
- ctx.t_mqttCalled = false;
671
-
672
- if (!ctx.firstListen || !ctx.lastSeqId) {
673
- getSeqID();
674
- } else {
675
- listenMqtt(defaultFuncs, api, ctx, globalCallback);
676
- }
677
-
678
- ctx.firstListen = false;
679
-
680
- // Add listenMqtt method to api
681
- api.listenMqtt = function(callback) {
682
- if (callback) {
683
- globalCallback = callback;
684
- }
685
- return msgEmitter;
686
- };
687
-
688
- return api;
689
- };
690
-
691
- // Add thread info update interval if enabled
692
- if (global.Fca.Require.ShankarConfig.AntiGetInfo.AntiGetThreadInfo) {
693
- setInterval(() => {
694
- try {
695
- const { updateMessageCount, getData, hasData } = require('../Extra/ExtraGetThread');
696
- const Data = global.Fca.Data.MsgCount;
697
- const Arr = Array.from(Data.keys());
698
-
699
- for (let threadID of Arr) {
700
- const Count = parseInt(Data.get(threadID));
701
- if (hasData(threadID)) {
702
- let threadData = getData(threadID);
703
- threadData.messageCount += Count;
704
- updateMessageCount(threadID, threadData);
705
- Data.delete(threadID);
706
- }
707
- }
708
- } catch (e) {
709
- log.error('Thread Info Update Error:', e);
710
- }
711
- }, 30 * 1000);
712
- }
713
-
714
- // Add process event handlers
715
- process.on('SIGINT', () => {
716
- logUptime();
717
- process.exit();
718
- });
719
-
720
- process.on('exit', logUptime);
721
-
722
- // Utility function to log uptime
723
- function logUptime() {
724
- try {
725
- const uptime = process.uptime();
726
- const filePath = join(__dirname, '../CountTime.json');
727
- const currentTime = Number(global.Fca.Require.fs.readFileSync(filePath, 'utf8')) || 0;
728
- global.Fca.Require.fs.writeFileSync(filePath, String(uptime + currentTime), 'utf8');
729
- } catch (err) {
730
- log.error('Failed to log uptime:', err);
731
- }
732
- }