From 5a395d7a421e996eac7fff37f1a60af86771a106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Sz=C5=B1cs?= Date: Sat, 26 Aug 2023 13:05:13 +0200 Subject: [PATCH 1/2] Emit error on 'offline' event and fix getMessage bug when attachment is present --- package.json | 2 +- src/getMessage.js | 2 +- src/listenMqtt.js | 27 +++++++++++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a2990ff..df94615 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "license": "MIT", "dependencies": { "https-proxy-agent": "^4.0.0", - "mqtt": "^3.0.0", + "mqtt": "4.3.7", "npmlog": "^1.2.0", "request": "^2.53.0", "websocket-stream": "^5.5.0" diff --git a/src/getMessage.js b/src/getMessage.js index e2f205f..ca40b5c 100644 --- a/src/getMessage.js +++ b/src/getMessage.js @@ -139,7 +139,7 @@ function formatMessage(threadID, data) { [r.user.id]: r.reaction })), attachments: data.blob_attachments && data.blob_attachments.length > 0 ? - data.blob_attachments.length.map(att => { + data.blob_attachments.map(att => { let x; try { x = utils._formatAttachment(att); diff --git a/src/listenMqtt.js b/src/listenMqtt.js index a5a6bb7..ad651ed 100644 --- a/src/listenMqtt.js +++ b/src/listenMqtt.js @@ -104,7 +104,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) { const mqttClient = ctx.mqttClient; mqttClient.on('error', function (err) { - log.error("listenMqtt", err); + log.error("ERROR", err); mqttClient.end(); if (ctx.globalOptions.autoReconnect) { listenMqtt(defaultFuncs, api, ctx, globalCallback); @@ -126,9 +126,32 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) { }); mqttClient.on('close', function () { - + log.error("CLOSE", "Client closed"); + mqttClient.end(); + globalCallback({ + type: "close", + error: "Client closed" + }, null) }); + mqttClient.on('offline', () => { + log.error("OFFLINE", "Client went offline"); + mqttClient.end(); + globalCallback({ + type: "offline", + error: "Client went offline, closing" + }, null) + }) + + mqttClient.on('disconnect', (packet) => { + log.error("DISCONNECT", "Received disconnect packet"); + mqttClient.end(); + globalCallback({ + type: "disconnect", + error: "Client disconnected" + }, null) + }) + mqttClient.on('connect', function () { topics.forEach(function (topicsub) { mqttClient.subscribe(topicsub); From fe112f220b1db97826348182ef6f69c202c58e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Sz=C5=B1cs?= Date: Fri, 27 Sep 2024 10:09:04 +0200 Subject: [PATCH 2/2] Require fb_dtsg token for all requests from now on --- src/listenMqtt.js | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/listenMqtt.js b/src/listenMqtt.js index ad651ed..55ae907 100644 --- a/src/listenMqtt.js +++ b/src/listenMqtt.js @@ -24,7 +24,6 @@ const topics = [ //Need to publish /messenger_sync_create_queue right after this "/orca_presence", //Will receive /sr_res right here. - "/legacy_web_mtouch" // "/inbox", // "/mercury", @@ -39,6 +38,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) { //TODO: Move to ctx when implemented const chatOn = ctx.globalOptions.online; const foreground = false; + const cid = utils.getGUID(); const sessionID = Math.floor(Math.random() * 9007199254740991) + 1; const username = { @@ -46,7 +46,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) { s: sessionID, chat_on: chatOn, fg: foreground, - d: utils.getGUID(), + d: cid, ct: "websocket", //App id from facebook aid: "219994525426954", @@ -66,7 +66,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) { let host; if (ctx.mqttEndpoint) { - host = `${ctx.mqttEndpoint}&sid=${sessionID}`; + host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${cid}`; } else if (ctx.region) { host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLocaleLowerCase()}&sid=${sessionID}`; } else { @@ -771,7 +771,7 @@ function getSeqId(defaultFuncs, api, ctx, globalCallback) { utils .get('https://www.facebook.com/', jar, null, ctx.globalOptions, { noRef: true }) .then(utils.saveCookies(jar)) - .then(function (resData) { + .then(async function (resData) { const html = resData.body; const oldFBMQTTMatch = html.match(/irisSeqID:"(.+?)",appID:219994525426954,endpoint:"(.+?)"/); let mqttEndpoint = null; @@ -792,10 +792,14 @@ function getSeqId(defaultFuncs, api, ctx, globalCallback) { region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase(); log.info("login", `Got this account's message region: ${region}`); } else { - const legacyFBMQTTMatch = html.match(/(\["MqttWebConfig",\[\],{fbid:")(.+?)(",appID:219994525426954,endpoint:")(.+?)(",pollingEndpoint:")(.+?)(3790])/); + const legacyFBMQTTMatch = html.match(/(\["MqttWebConfig",\[\],\{"fbid":")(.+?)(","appID":219994525426954,"endpoint":")(.+?)(","pollingEndpoint":")(.+?)(","subscribedTopics".*3790])/); if (legacyFBMQTTMatch) { - mqttEndpoint = legacyFBMQTTMatch[4]; + mqttEndpoint = legacyFBMQTTMatch[4]?.replaceAll("\\", ""); region = new URL(mqttEndpoint).searchParams.get("region").toUpperCase(); + let dtsgTokenMatch = html.match(/\[\"DTSGInitialData\",\[\],\{\"token\":\"(.+?)\"/); + if(dtsgTokenMatch) { + ctx.fb_dtsg = dtsgTokenMatch[1]; + } log.warn("login", `Cannot get sequence ID with new RegExp. Fallback to old RegExp (without seqID)...`); log.info("login", `Got this account's message region: ${region}`); log.info("login", `[Unused] Polling endpoint: ${legacyFBMQTTMatch[6]}`); @@ -805,14 +809,34 @@ function getSeqId(defaultFuncs, api, ctx, globalCallback) { } } } - ctx.lastSeqId = irisSeqID; + if(!irisSeqID) { + if(!ctx.fb_dtsg) { + log.warn("seqId", "fb_dtsg token is required for getting seqId"); + } + let seqId = await utils + .post('https://www.facebook.com/api/graphql/', jar, { + 'av': ctx.userID, + 'fb_dtsg': ctx.fb_dtsg, + 'jazoest': Math.ceil(Math.random() * (99_999 - 10_000) + 10_000), + 'variables': '{includeSeqID: true, limit: 0}', + 'server_timestamps': 'true', + 'doc_id': '1349387578499440' // from https://github.com/fbchat-dev/fbchat/blob/master/fbchat/_listen.py fetch_sequence_id + }, ctx.globalOptions, {noRef: true}, true) + .then(utils.saveCookies(jar)) + .then(resData => { + return JSON.parse(resData.body)?.['data']?.['viewer']?.['message_threads']?.['sync_sequence_id']; + }) + if(!seqId) { + log.warn("seqId", "could not get seqId from graphql endpoint"); + } + ctx.lastSeqId = seqId; + } ctx.mqttEndpoint = mqttEndpoint; ctx.region = region; if (noMqttData) { api["htmlData"] = noMqttData; } - listenMqtt(defaultFuncs, api, ctx, globalCallback); }) .catch(function (err) {