stfca 1.0.27 → 1.2.27
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/README.md +226 -2
- package/checkUpdate.js +53 -49
- package/index.js +151 -15
- package/package.json +13 -12
- package/src/OldMessage.js +2 -2
- package/src/editMessage.js +14 -1
- package/src/listenMqtt.js +407 -395
- package/src/sendMessage.js +390 -278
- package/src/sendMessageMqtt.js +0 -1
- package/src/sendTypingIndicator.js +54 -45
- package/src/setMessageReaction.js +25 -0
- package/src/unsendMessage.js +21 -0
- package/src/uploadAttachment.js +99 -77
- package/utils.js +75 -13
package/src/listenMqtt.js
CHANGED
|
@@ -3,46 +3,220 @@
|
|
|
3
3
|
var utils = require("../utils");
|
|
4
4
|
var log = require("npmlog");
|
|
5
5
|
var mqtt = require('mqtt');
|
|
6
|
-
var
|
|
7
|
-
var
|
|
6
|
+
var WebSocket = require('ws');
|
|
7
|
+
var Transform = require('stream').Transform;
|
|
8
8
|
const EventEmitter = require('events');
|
|
9
|
-
|
|
9
|
+
var e2eeBridge = require("../e2ee");
|
|
10
|
+
|
|
11
|
+
// ─── ANSI colour helpers ───────────────────────────────────────────────────────
|
|
12
|
+
var C = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bold: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
// foregrounds
|
|
17
|
+
black: '\x1b[30m',
|
|
18
|
+
red: '\x1b[31m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
magenta: '\x1b[35m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
white: '\x1b[37m',
|
|
25
|
+
// bright foregrounds
|
|
26
|
+
bBlack: '\x1b[90m',
|
|
27
|
+
bRed: '\x1b[91m',
|
|
28
|
+
bGreen: '\x1b[92m',
|
|
29
|
+
bYellow: '\x1b[93m',
|
|
30
|
+
bBlue: '\x1b[94m',
|
|
31
|
+
bMagenta: '\x1b[95m',
|
|
32
|
+
bCyan: '\x1b[96m',
|
|
33
|
+
bWhite: '\x1b[97m',
|
|
34
|
+
// backgrounds
|
|
35
|
+
bgBlue: '\x1b[44m',
|
|
36
|
+
bgCyan: '\x1b[46m',
|
|
37
|
+
bgMagenta: '\x1b[45m',
|
|
38
|
+
bgGreen: '\x1b[42m',
|
|
39
|
+
bgBlack: '\x1b[40m',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// ─── MQTT Spinner ──────────────────────────────────────────────────────────────
|
|
43
|
+
var _mqttSpinner = null;
|
|
44
|
+
|
|
45
|
+
function startMqttSpinner(region) {
|
|
46
|
+
var frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
47
|
+
var fi = 0;
|
|
48
|
+
var regionStr = region ? (' ' + C.dim + C.bCyan + '[' + region.toUpperCase() + ']' + C.reset) : '';
|
|
49
|
+
process.stdout.write('\n');
|
|
50
|
+
_mqttSpinner = setInterval(function () {
|
|
51
|
+
var frame = frames[fi++ % frames.length];
|
|
52
|
+
process.stdout.write(
|
|
53
|
+
'\r ' +
|
|
54
|
+
C.bold + C.bCyan + frame + C.reset + ' ' +
|
|
55
|
+
C.cyan + 'ST-FCA' + C.reset + ' ' +
|
|
56
|
+
C.dim + 'connecting to MQTT' + C.reset +
|
|
57
|
+
regionStr +
|
|
58
|
+
C.dim + ' ...' + C.reset +
|
|
59
|
+
' '
|
|
60
|
+
);
|
|
61
|
+
}, 80);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function stopMqttSpinner() {
|
|
65
|
+
if (_mqttSpinner) {
|
|
66
|
+
clearInterval(_mqttSpinner);
|
|
67
|
+
_mqttSpinner = null;
|
|
68
|
+
}
|
|
69
|
+
// erase the spinner line completely
|
|
70
|
+
process.stdout.write('\r\x1b[2K');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function printMqttBanner(region, autoReconnect) {
|
|
74
|
+
stopMqttSpinner();
|
|
75
|
+
|
|
76
|
+
var titleClr = C.bold + C.bGreen;
|
|
77
|
+
var labelClr = C.bold + C.bWhite;
|
|
78
|
+
var valClr = C.bYellow;
|
|
79
|
+
var accentClr = C.bold + C.bMagenta;
|
|
80
|
+
var urlClr = C.bBlue;
|
|
81
|
+
var rst = C.reset;
|
|
82
|
+
|
|
83
|
+
var regionVal = (region || '').toUpperCase();
|
|
84
|
+
var reconnTxt = autoReconnect ? 'Enabled (3s)' : 'Disabled';
|
|
85
|
+
var reconnClr = autoReconnect ? C.bGreen : C.bRed;
|
|
86
|
+
var reconnVal = reconnClr + reconnTxt + rst;
|
|
87
|
+
|
|
88
|
+
var rows = [
|
|
89
|
+
titleClr + ' ✅ ST-FCA MQTT Connected' + rst,
|
|
90
|
+
'',
|
|
91
|
+
labelClr + ' 📍 Region ' + rst + valClr + regionVal + rst,
|
|
92
|
+
labelClr + ' 🔄 Auto-reconnect ' + rst + reconnVal,
|
|
93
|
+
urlClr + ' 🌐 github.com/sheikhtamimlover/ST-BOT' + rst,
|
|
94
|
+
'',
|
|
95
|
+
accentClr + ' 💎 Author ST | Sheikh Tamim' + rst
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
process.stdout.write('\n');
|
|
99
|
+
rows.forEach(function (line) {
|
|
100
|
+
console.log(line);
|
|
101
|
+
});
|
|
102
|
+
process.stdout.write('\n');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Facebook sends non-standard MQTT packets where PUBACK/SUBACK have
|
|
107
|
+
* non-zero reserved flag bits (e.g. 0x4F instead of 0x40).
|
|
108
|
+
* mqtt-packet strictly rejects these. This transform stream patches
|
|
109
|
+
* the first byte of each MQTT frame to clear the lower nibble (flags),
|
|
110
|
+
* keeping only the packet type (upper nibble).
|
|
111
|
+
*
|
|
112
|
+
* MQTT fixed header: byte[0] = (type << 4) | flags
|
|
113
|
+
* For PUBACK (type=4): valid = 0x40, FB may send 0x4F → we clear to 0x40
|
|
114
|
+
*/
|
|
115
|
+
function createMqttPatchStream() {
|
|
116
|
+
var buf = null;
|
|
117
|
+
|
|
118
|
+
// Walk frame by frame. For types that must have flags=0 per the MQTT spec
|
|
119
|
+
// (CONNACK=2, PUBACK=4, SUBACK=9, UNSUBACK=11, PINGRESP=13), clear the
|
|
120
|
+
// lower nibble that Facebook sets to non-zero values.
|
|
121
|
+
var stream = new Transform({
|
|
122
|
+
transform: function (chunk, encoding, callback) {
|
|
123
|
+
if (!Buffer.isBuffer(chunk)) chunk = Buffer.from(chunk, encoding);
|
|
124
|
+
|
|
125
|
+
// Prepend any leftover bytes from the previous chunk
|
|
126
|
+
var out;
|
|
127
|
+
if (buf) {
|
|
128
|
+
out = Buffer.concat([buf, chunk]);
|
|
129
|
+
buf = null;
|
|
130
|
+
} else {
|
|
131
|
+
out = Buffer.from(chunk);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
var i = 0;
|
|
135
|
+
while (i < out.length) {
|
|
136
|
+
var b = out[i];
|
|
137
|
+
var type = (b >> 4) & 0x0F;
|
|
138
|
+
var flags = b & 0x0F;
|
|
139
|
+
// Types that MUST have flags=0:
|
|
140
|
+
if (flags !== 0 && (type === 4 || type === 9 || type === 11 || type === 13 || type === 2)) {
|
|
141
|
+
out[i] = (b & 0xF0); // clear lower nibble
|
|
142
|
+
}
|
|
143
|
+
// Skip past this frame: read the varint length
|
|
144
|
+
i++;
|
|
145
|
+
var multiplier = 1;
|
|
146
|
+
var frameLen = 0;
|
|
147
|
+
var lenOk = false;
|
|
148
|
+
while (i < out.length) {
|
|
149
|
+
var lb = out[i++];
|
|
150
|
+
frameLen += (lb & 0x7F) * multiplier;
|
|
151
|
+
multiplier *= 128;
|
|
152
|
+
if ((lb & 0x80) === 0) { lenOk = true; break; }
|
|
153
|
+
if (multiplier > 128 * 128 * 128) break; // malformed
|
|
154
|
+
}
|
|
155
|
+
if (!lenOk) {
|
|
156
|
+
// Incomplete frame — save remainder for next chunk
|
|
157
|
+
buf = out.slice(i - 1);
|
|
158
|
+
out = out.slice(0, i - 1);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
i += frameLen;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
callback(null, out);
|
|
165
|
+
},
|
|
166
|
+
flush: function (callback) {
|
|
167
|
+
if (buf && buf.length > 0) callback(null, buf);
|
|
168
|
+
else callback();
|
|
169
|
+
buf = null;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return stream;
|
|
173
|
+
}
|
|
174
|
+
|
|
10
175
|
var identity = function () { };
|
|
11
176
|
var form = {};
|
|
12
177
|
var getSeqID = function () { };
|
|
178
|
+
|
|
13
179
|
var topics = [
|
|
14
180
|
"/legacy_web",
|
|
15
181
|
"/webrtc",
|
|
16
182
|
"/rtc_multi",
|
|
17
183
|
"/onevc",
|
|
18
|
-
"/br_sr",
|
|
19
|
-
//Need to publish /br_sr right after this
|
|
184
|
+
"/br_sr",
|
|
20
185
|
"/sr_res",
|
|
21
186
|
"/t_ms",
|
|
22
187
|
"/thread_typing",
|
|
23
188
|
"/orca_typing_notifications",
|
|
24
189
|
"/notify_disconnect",
|
|
25
|
-
//Need to publish /messenger_sync_create_queue right after this
|
|
26
190
|
"/orca_presence",
|
|
27
|
-
//Will receive /sr_res right here.
|
|
28
|
-
|
|
29
191
|
"/inbox",
|
|
30
192
|
"/mercury",
|
|
31
193
|
"/messaging_events",
|
|
32
194
|
"/orca_message_notifications",
|
|
33
195
|
"/pp",
|
|
34
196
|
"/webrtc_response",
|
|
197
|
+
"/ls_resp"
|
|
35
198
|
];
|
|
36
199
|
|
|
200
|
+
function sanitizeHeaderValue(value) {
|
|
201
|
+
if (value === null || value === undefined) return "";
|
|
202
|
+
var str = String(value);
|
|
203
|
+
if (str.trim().startsWith("[") && str.trim().endsWith("]")) {
|
|
204
|
+
try {
|
|
205
|
+
var parsed = JSON.parse(str);
|
|
206
|
+
if (Array.isArray(parsed)) return "";
|
|
207
|
+
} catch (_) { }
|
|
208
|
+
}
|
|
209
|
+
str = str.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\r\n\[\]]/g, "").trim();
|
|
210
|
+
return str;
|
|
211
|
+
}
|
|
212
|
+
|
|
37
213
|
function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
38
|
-
//Don't really know what this does but I think it's for the active state?
|
|
39
|
-
//TODO: Move to ctx when implemented
|
|
40
214
|
var chatOn = ctx.globalOptions.online;
|
|
41
215
|
var foreground = false;
|
|
42
|
-
|
|
43
216
|
var sessionID = Math.floor(Math.random() * 9007199254740991) + 1;
|
|
44
217
|
var GUID = utils.getGUID();
|
|
45
|
-
|
|
218
|
+
|
|
219
|
+
var username = {
|
|
46
220
|
u: ctx.userID,
|
|
47
221
|
s: sessionID,
|
|
48
222
|
chat_on: chatOn,
|
|
@@ -63,89 +237,138 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
63
237
|
p: null,
|
|
64
238
|
php_override: ""
|
|
65
239
|
};
|
|
66
|
-
var cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
|
|
67
240
|
|
|
241
|
+
var cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
|
|
68
242
|
var host;
|
|
69
|
-
if (ctx.mqttEndpoint)
|
|
70
|
-
|
|
71
|
-
|
|
243
|
+
if (ctx.mqttEndpoint) {
|
|
244
|
+
// Ensure no duplicate sid/cid — strip any existing ones then append fresh
|
|
245
|
+
var baseEndpoint = ctx.mqttEndpoint
|
|
246
|
+
.replace(/[?&]sid=[^&]*/g, '')
|
|
247
|
+
.replace(/[?&]cid=[^&]*/g, '');
|
|
248
|
+
// Re-attach the ? if it was stripped along with the first param
|
|
249
|
+
if (baseEndpoint.indexOf('?') === -1 && ctx.mqttEndpoint.indexOf('?') !== -1) {
|
|
250
|
+
baseEndpoint = baseEndpoint.replace(/&/, '?');
|
|
251
|
+
}
|
|
252
|
+
var sep = baseEndpoint.indexOf('?') === -1 ? '?' : '&';
|
|
253
|
+
host = baseEndpoint + sep + "sid=" + sessionID + "&cid=" + GUID;
|
|
254
|
+
} else if (ctx.region) {
|
|
255
|
+
host = "wss://edge-chat.facebook.com/chat?region=" + ctx.region.toLowerCase() + "&sid=" + sessionID + "&cid=" + GUID;
|
|
256
|
+
} else {
|
|
257
|
+
host = "wss://edge-chat.facebook.com/chat?sid=" + sessionID + "&cid=" + GUID;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
var ua = ctx.globalOptions.userAgent ||
|
|
261
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15";
|
|
262
|
+
|
|
263
|
+
var wsHeaders = {
|
|
264
|
+
Cookie: sanitizeHeaderValue(cookies),
|
|
265
|
+
Origin: "https://www.facebook.com",
|
|
266
|
+
"User-Agent": sanitizeHeaderValue(ua),
|
|
267
|
+
Referer: "https://www.facebook.com/",
|
|
268
|
+
Host: "edge-chat.facebook.com",
|
|
269
|
+
Connection: "Upgrade",
|
|
270
|
+
Upgrade: "websocket",
|
|
271
|
+
"Sec-WebSocket-Version": "13",
|
|
272
|
+
"Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits",
|
|
273
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
274
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
275
|
+
"Cache-Control": "no-cache",
|
|
276
|
+
Pragma: "no-cache"
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
if (ctx.region) wsHeaders["X-MSGR-Region"] = sanitizeHeaderValue(ctx.region);
|
|
280
|
+
|
|
281
|
+
var wsOptions = {
|
|
282
|
+
headers: wsHeaders,
|
|
283
|
+
origin: "https://www.facebook.com",
|
|
284
|
+
protocolVersion: 13,
|
|
285
|
+
binaryType: "arraybuffer"
|
|
286
|
+
};
|
|
72
287
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
288
|
+
if (typeof ctx.globalOptions.proxy !== "undefined") {
|
|
289
|
+
var { HttpsProxyAgent } = require('https-proxy-agent');
|
|
290
|
+
wsOptions.agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
var mqttOptions = {
|
|
294
|
+
clientId: "mqttwsclient",
|
|
295
|
+
protocolId: "MQIsdp",
|
|
76
296
|
protocolVersion: 3,
|
|
77
297
|
username: JSON.stringify(username),
|
|
78
298
|
clean: true,
|
|
79
|
-
|
|
80
|
-
headers: {
|
|
81
|
-
Cookie: cookies,
|
|
82
|
-
Origin: 'https://www.facebook.com',
|
|
83
|
-
'User-Agent': ctx.globalOptions.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36',
|
|
84
|
-
Referer: 'https://www.facebook.com/',
|
|
85
|
-
Host: new URL(host).hostname,
|
|
86
|
-
},
|
|
87
|
-
origin: 'https://www.facebook.com',
|
|
88
|
-
protocolVersion: 13,
|
|
89
|
-
binaryType: 'arraybuffer',
|
|
90
|
-
},
|
|
91
|
-
keepalive: 60,
|
|
299
|
+
keepalive: 30,
|
|
92
300
|
reschedulePings: true,
|
|
93
|
-
reconnectPeriod:
|
|
301
|
+
reconnectPeriod: 0,
|
|
302
|
+
connectTimeout: 12000
|
|
94
303
|
};
|
|
95
304
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
305
|
+
// Use MqttClient with a proper Duplex:
|
|
306
|
+
// Write side: mqtt → duplex.write → wsStream.write → WebSocket (send to FB)
|
|
307
|
+
// Read side: WebSocket msg → wsStream readable → patcher (fix FB header bits) → duplex.push → mqtt reads
|
|
308
|
+
function buildStream() {
|
|
309
|
+
var Duplex = require('stream').Duplex;
|
|
310
|
+
var ws = new WebSocket(host, wsOptions);
|
|
311
|
+
ws.on('error', function () { }); // suppress unhandled ws errors
|
|
312
|
+
|
|
313
|
+
var wsStream = WebSocket.createWebSocketStream(ws, { objectMode: false });
|
|
314
|
+
var patcher = createMqttPatchStream();
|
|
315
|
+
|
|
316
|
+
// Wire: wsStream readable → patcher → push into duplex
|
|
317
|
+
wsStream.pipe(patcher);
|
|
318
|
+
|
|
319
|
+
var duplex = new Duplex({
|
|
320
|
+
read: function () { },
|
|
321
|
+
write: function (chunk, enc, cb) {
|
|
322
|
+
wsStream.write(chunk, enc, cb);
|
|
323
|
+
},
|
|
324
|
+
final: function (cb) {
|
|
325
|
+
wsStream.end(cb);
|
|
326
|
+
},
|
|
327
|
+
destroy: function (err, cb) {
|
|
328
|
+
try { wsStream.destroy(err); } catch (_) { }
|
|
329
|
+
cb(err);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
patcher.on('data', function (data) {
|
|
334
|
+
if (!duplex.destroyed) duplex.push(data);
|
|
335
|
+
});
|
|
336
|
+
patcher.on('end', function () {
|
|
337
|
+
if (!duplex.destroyed) duplex.push(null);
|
|
338
|
+
});
|
|
339
|
+
patcher.on('error', function (e) {
|
|
340
|
+
if (!duplex.destroyed) duplex.destroy(e);
|
|
341
|
+
});
|
|
342
|
+
wsStream.on('error', function (e) {
|
|
343
|
+
if (!duplex.destroyed) duplex.destroy(e);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return duplex;
|
|
99
347
|
}
|
|
100
348
|
|
|
101
|
-
ctx.
|
|
349
|
+
startMqttSpinner(ctx.region);
|
|
102
350
|
|
|
351
|
+
ctx.mqttClient = new mqtt.MqttClient(buildStream, mqttOptions);
|
|
103
352
|
global.mqttClient = ctx.mqttClient;
|
|
104
353
|
|
|
354
|
+
var mqttClient = ctx.mqttClient;
|
|
355
|
+
|
|
105
356
|
mqttClient.on('error', function (err) {
|
|
357
|
+
stopMqttSpinner();
|
|
106
358
|
log.error("listenMqtt", err);
|
|
107
359
|
mqttClient.end();
|
|
108
360
|
if (ctx.globalOptions.autoReconnect) getSeqID();
|
|
109
361
|
else globalCallback({ type: "stop_listen", error: "Connection refused: Server unavailable" }, null);
|
|
110
362
|
});
|
|
111
363
|
|
|
364
|
+
mqttClient.on('close', function () { });
|
|
365
|
+
mqttClient.on('offline', function () { });
|
|
366
|
+
mqttClient.on('reconnect', function () { });
|
|
367
|
+
|
|
112
368
|
mqttClient.on('connect', function () {
|
|
113
|
-
topics.forEach(
|
|
114
|
-
|
|
115
|
-
// Display connection success message with branding and loading animation
|
|
116
|
-
const messages = [
|
|
117
|
-
'\n✅ ST-FCA MQTT Connected',
|
|
118
|
-
`📍 Region: ${ctx.region || 'PNB'}`,
|
|
119
|
-
`🔄 Auto-reconnect: ${ctx.globalOptions.autoReconnect ? 'Enabled' : 'Disabled'}${ctx.globalOptions.autoReconnect ? ' (reconnects every 3s on disconnect)' : ''}`,
|
|
120
|
-
`⏱️ MQTT Restart Interval: ${ctx.globalOptions.restartListenMqtt?.enable ? `${ctx.globalOptions.restartListenMqtt.timeRestart / 1000}s` : 'Disabled'}`,
|
|
121
|
-
'� This FCA is specially for ST BOT',
|
|
122
|
-
'🌐 GitHub: https://github.com/sheikhtamimlover/ST-BOT',
|
|
123
|
-
'📱 Owner IG: https://www.instagram.com/sheikh.tamim_lover',
|
|
124
|
-
'�🎨 Maintained & Enhanced by ST | Sheikh Tamim\n'
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
let index = 0;
|
|
128
|
-
const displayMessages = () => {
|
|
129
|
-
if (index < messages.length) {
|
|
130
|
-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
131
|
-
let frameIndex = 0;
|
|
132
|
-
|
|
133
|
-
const loadingInterval = setInterval(() => {
|
|
134
|
-
process.stdout.write(`\r${frames[frameIndex]} Loading...`);
|
|
135
|
-
frameIndex = (frameIndex + 1) % frames.length;
|
|
136
|
-
}, 80);
|
|
137
|
-
|
|
138
|
-
setTimeout(() => {
|
|
139
|
-
clearInterval(loadingInterval);
|
|
140
|
-
process.stdout.write('\r' + ' '.repeat(20) + '\r');
|
|
141
|
-
console.log(messages[index]);
|
|
142
|
-
index++;
|
|
143
|
-
displayMessages();
|
|
144
|
-
}, 500);
|
|
145
|
-
}
|
|
146
|
-
};
|
|
369
|
+
topics.forEach(function (topic) { mqttClient.subscribe(topic); });
|
|
147
370
|
|
|
148
|
-
|
|
371
|
+
printMqttBanner(ctx.region, ctx.globalOptions.autoReconnect);
|
|
149
372
|
|
|
150
373
|
var topic;
|
|
151
374
|
var queue = {
|
|
@@ -160,8 +383,7 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
160
383
|
topic = "/messenger_sync_get_diffs";
|
|
161
384
|
queue.last_seq_id = ctx.lastSeqId;
|
|
162
385
|
queue.sync_token = ctx.syncToken;
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
386
|
+
} else {
|
|
165
387
|
topic = "/messenger_sync_create_queue";
|
|
166
388
|
queue.initial_titan_sequence_id = ctx.lastSeqId;
|
|
167
389
|
queue.device_params = null;
|
|
@@ -176,23 +398,34 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
176
398
|
|
|
177
399
|
ctx.tmsWait = function () {
|
|
178
400
|
clearTimeout(rTimeout);
|
|
179
|
-
ctx.globalOptions.emitReady
|
|
180
|
-
type: "ready",
|
|
181
|
-
error: null
|
|
182
|
-
}) : "";
|
|
401
|
+
if (ctx.globalOptions.emitReady) globalCallback({ type: "ready", error: null });
|
|
183
402
|
delete ctx.tmsWait;
|
|
403
|
+
|
|
404
|
+
// ── E2EE bridge init ──────────────────────────────────────────────────
|
|
405
|
+
ctx._globalCallback = globalCallback;
|
|
406
|
+
if (ctx.globalOptions.enableE2EE === true) {
|
|
407
|
+
var bridge = e2eeBridge.createBridge(ctx);
|
|
408
|
+
if (global.GoatBot) global.GoatBot._e2eeBridge = bridge;
|
|
409
|
+
bridge.connect(globalCallback).catch(function (err) {
|
|
410
|
+
log.error("listenMqtt", "E2EE bridge connect error:", err && err.message ? err.message : err);
|
|
411
|
+
});
|
|
412
|
+
} else {
|
|
413
|
+
if (global.GoatBot) global.GoatBot._e2eeBridge = null;
|
|
414
|
+
}
|
|
415
|
+
// ── end E2EE bridge init ──────────────────────────────────────────────
|
|
184
416
|
};
|
|
185
417
|
});
|
|
186
418
|
|
|
187
|
-
mqttClient.on('message', function (topic, message
|
|
419
|
+
mqttClient.on('message', function (topic, message) {
|
|
420
|
+
var jsonMessage;
|
|
188
421
|
try {
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
catch (ex) {
|
|
422
|
+
jsonMessage = JSON.parse(message.toString());
|
|
423
|
+
} catch (ex) {
|
|
192
424
|
return log.error("listenMqtt", ex);
|
|
193
425
|
}
|
|
426
|
+
|
|
194
427
|
if (topic === "/t_ms") {
|
|
195
|
-
if (ctx.tmsWait && typeof ctx.tmsWait
|
|
428
|
+
if (ctx.tmsWait && typeof ctx.tmsWait === "function") ctx.tmsWait();
|
|
196
429
|
|
|
197
430
|
if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
|
|
198
431
|
ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
|
|
@@ -201,13 +434,11 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
201
434
|
|
|
202
435
|
if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
|
|
203
436
|
|
|
204
|
-
//If it contains more than 1 delta
|
|
205
437
|
for (var i in jsonMessage.deltas) {
|
|
206
438
|
var delta = jsonMessage.deltas[i];
|
|
207
439
|
parseDelta(defaultFuncs, api, ctx, globalCallback, { "delta": delta });
|
|
208
440
|
}
|
|
209
|
-
}
|
|
210
|
-
else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
|
|
441
|
+
} else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
|
|
211
442
|
var typ = {
|
|
212
443
|
type: "typ",
|
|
213
444
|
isTyping: !!jsonMessage.state,
|
|
@@ -215,17 +446,13 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
215
446
|
threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
|
|
216
447
|
};
|
|
217
448
|
(function () { globalCallback(null, typ); })();
|
|
218
|
-
}
|
|
219
|
-
else if (topic === "/orca_presence") {
|
|
449
|
+
} else if (topic === "/orca_presence") {
|
|
220
450
|
if (!ctx.globalOptions.updatePresence) {
|
|
221
451
|
for (var i in jsonMessage.list) {
|
|
222
452
|
var data = jsonMessage.list[i];
|
|
223
|
-
var userID = data["u"];
|
|
224
|
-
|
|
225
453
|
var presence = {
|
|
226
454
|
type: "presence",
|
|
227
|
-
userID:
|
|
228
|
-
//Convert to ms
|
|
455
|
+
userID: data["u"].toString(),
|
|
229
456
|
timestamp: data["l"] * 1000,
|
|
230
457
|
statuses: data["p"]
|
|
231
458
|
};
|
|
@@ -233,57 +460,43 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
233
460
|
}
|
|
234
461
|
}
|
|
235
462
|
}
|
|
236
|
-
|
|
237
463
|
});
|
|
238
464
|
|
|
239
465
|
mqttClient.on('close', function () { });
|
|
240
466
|
}
|
|
241
467
|
|
|
242
|
-
function
|
|
243
|
-
if (!
|
|
244
|
-
api.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
}
|
|
468
|
+
function attachImageUrlToAttachment(api, attachment) {
|
|
469
|
+
if (!attachment || attachment.type !== "photo" || !attachment.url) return;
|
|
470
|
+
if (api && api._imgUpload) {
|
|
471
|
+
api._imgUpload(attachment.url).then(function (url) {
|
|
472
|
+
if (url) attachment.imgUrl = url;
|
|
473
|
+
}).catch(function () { });
|
|
474
|
+
}
|
|
249
475
|
}
|
|
250
476
|
|
|
251
477
|
function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
252
478
|
if (v.delta.class == "NewMessage") {
|
|
253
|
-
//Not tested for pages
|
|
254
479
|
if (ctx.globalOptions.pageID && ctx.globalOptions.pageID != v.queue) return;
|
|
255
480
|
|
|
256
481
|
(function resolveAttachmentUrl(i) {
|
|
257
482
|
if (i == (v.delta.attachments || []).length) {
|
|
258
|
-
|
|
483
|
+
var fmtMsg;
|
|
259
484
|
try {
|
|
260
485
|
fmtMsg = utils.formatDeltaMessage(v);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const threadFbId = v.delta.messageMetadata.threadKey.threadFbId;
|
|
264
|
-
|
|
265
|
-
// A thread is a DM if it has otherUserFbId and no threadFbId
|
|
486
|
+
var otherUserFbId = v.delta.messageMetadata.threadKey.otherUserFbId;
|
|
487
|
+
var threadFbId = v.delta.messageMetadata.threadKey.threadFbId;
|
|
266
488
|
fmtMsg.isSingleUser = !!otherUserFbId && !threadFbId;
|
|
267
489
|
fmtMsg.isGroup = !!threadFbId;
|
|
268
|
-
|
|
269
|
-
// Store thread type in context for sendMessage to use
|
|
270
490
|
if (!ctx.threadTypes) ctx.threadTypes = {};
|
|
271
491
|
ctx.threadTypes[fmtMsg.threadID] = fmtMsg.isSingleUser ? 'dm' : 'group';
|
|
272
492
|
if (fmtMsg.attachments && Array.isArray(fmtMsg.attachments)) {
|
|
273
|
-
fmtMsg.attachments.forEach(att
|
|
493
|
+
fmtMsg.attachments.forEach(function (att) { attachImageUrlToAttachment(api, att); });
|
|
274
494
|
}
|
|
275
495
|
} catch (err) {
|
|
276
|
-
return globalCallback({
|
|
277
|
-
error: "Problem parsing message object.",
|
|
278
|
-
detail: err,
|
|
279
|
-
res: v,
|
|
280
|
-
type: "parse_error"
|
|
281
|
-
});
|
|
496
|
+
return globalCallback({ error: "Problem parsing message object.", detail: err, res: v, type: "parse_error" });
|
|
282
497
|
}
|
|
283
|
-
if (fmtMsg) {
|
|
284
|
-
|
|
285
|
-
markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
|
|
286
|
-
}
|
|
498
|
+
if (fmtMsg && ctx.globalOptions.autoMarkDelivery) {
|
|
499
|
+
markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
|
|
287
500
|
}
|
|
288
501
|
return !ctx.globalOptions.selfListen &&
|
|
289
502
|
(fmtMsg.senderID === ctx.i_userID || fmtMsg.senderID === ctx.userID) ?
|
|
@@ -291,16 +504,10 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
291
504
|
(function () { globalCallback(null, fmtMsg); })();
|
|
292
505
|
} else {
|
|
293
506
|
if (v.delta.attachments[i].mercury.attach_type == "photo") {
|
|
294
|
-
api.resolvePhotoUrl(
|
|
295
|
-
v.delta.attachments[i].
|
|
296
|
-
(
|
|
297
|
-
|
|
298
|
-
v.delta.attachments[
|
|
299
|
-
i
|
|
300
|
-
].mercury.metadata.url = url;
|
|
301
|
-
return resolveAttachmentUrl(i + 1);
|
|
302
|
-
}
|
|
303
|
-
);
|
|
507
|
+
api.resolvePhotoUrl(v.delta.attachments[i].fbid, function (err, url) {
|
|
508
|
+
if (!err) v.delta.attachments[i].mercury.metadata.url = url;
|
|
509
|
+
return resolveAttachmentUrl(i + 1);
|
|
510
|
+
});
|
|
304
511
|
} else {
|
|
305
512
|
return resolveAttachmentUrl(i + 1);
|
|
306
513
|
}
|
|
@@ -324,8 +531,7 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
324
531
|
userID: delta.deltaMessageReaction.userId.toString()
|
|
325
532
|
});
|
|
326
533
|
})();
|
|
327
|
-
}
|
|
328
|
-
else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
|
|
534
|
+
} else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
|
|
329
535
|
(function () {
|
|
330
536
|
globalCallback(null, {
|
|
331
537
|
type: "message_unsend",
|
|
@@ -336,21 +542,17 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
336
542
|
timestamp: delta.deltaRecallMessageData.timestamp
|
|
337
543
|
});
|
|
338
544
|
})();
|
|
339
|
-
}
|
|
340
|
-
else if (delta.deltaMessageReply) {
|
|
341
|
-
//Mention block - #1
|
|
545
|
+
} else if (delta.deltaMessageReply) {
|
|
342
546
|
var mdata = delta.deltaMessageReply.message === undefined ? [] :
|
|
343
547
|
delta.deltaMessageReply.message.data === undefined ? [] :
|
|
344
548
|
delta.deltaMessageReply.message.data.prng === undefined ? [] :
|
|
345
549
|
JSON.parse(delta.deltaMessageReply.message.data.prng);
|
|
346
|
-
var m_id = mdata.map(u
|
|
347
|
-
var m_offset = mdata.map(u
|
|
348
|
-
var m_length = mdata.map(u
|
|
349
|
-
|
|
550
|
+
var m_id = mdata.map(function (u) { return u.i; });
|
|
551
|
+
var m_offset = mdata.map(function (u) { return u.o; });
|
|
552
|
+
var m_length = mdata.map(function (u) { return u.l; });
|
|
350
553
|
var mentions = {};
|
|
351
|
-
|
|
352
554
|
for (var i = 0; i < m_id.length; i++) mentions[m_id[i]] = (delta.deltaMessageReply.message.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
|
|
353
|
-
|
|
555
|
+
|
|
354
556
|
var callbackToReturn = {
|
|
355
557
|
type: "message_reply",
|
|
356
558
|
threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
|
|
@@ -360,16 +562,10 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
360
562
|
var mercury = JSON.parse(att.mercuryJSON);
|
|
361
563
|
Object.assign(att, mercury);
|
|
362
564
|
return att;
|
|
363
|
-
}).map(att
|
|
565
|
+
}).map(function (att) {
|
|
364
566
|
var x;
|
|
365
|
-
try {
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
catch (ex) {
|
|
369
|
-
x = att;
|
|
370
|
-
x.error = ex;
|
|
371
|
-
x.type = "unknown";
|
|
372
|
-
}
|
|
567
|
+
try { x = utils._formatAttachment(att); }
|
|
568
|
+
catch (ex) { x = att; x.error = ex; x.type = "unknown"; }
|
|
373
569
|
return x;
|
|
374
570
|
}),
|
|
375
571
|
args: (delta.deltaMessageReply.message.body || "").trim().split(/\s+/),
|
|
@@ -377,26 +573,24 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
377
573
|
isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
|
|
378
574
|
mentions: mentions,
|
|
379
575
|
timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
|
|
380
|
-
participantIDs: (delta.deltaMessageReply.message.messageMetadata.cid.canonicalParticipantFbids || delta.deltaMessageReply.message.participants || []).map(e
|
|
576
|
+
participantIDs: (delta.deltaMessageReply.message.messageMetadata.cid.canonicalParticipantFbids || delta.deltaMessageReply.message.participants || []).map(function (e) { return e.toString(); })
|
|
381
577
|
};
|
|
578
|
+
|
|
382
579
|
if (callbackToReturn.attachments && Array.isArray(callbackToReturn.attachments)) {
|
|
383
|
-
callbackToReturn.attachments.forEach(att
|
|
580
|
+
callbackToReturn.attachments.forEach(function (att) { attachImageUrlToAttachment(api, att); });
|
|
384
581
|
}
|
|
385
582
|
|
|
386
583
|
if (delta.deltaMessageReply.repliedToMessage) {
|
|
387
|
-
//Mention block - #2
|
|
388
584
|
mdata = delta.deltaMessageReply.repliedToMessage === undefined ? [] :
|
|
389
585
|
delta.deltaMessageReply.repliedToMessage.data === undefined ? [] :
|
|
390
586
|
delta.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] :
|
|
391
587
|
JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng);
|
|
392
|
-
m_id = mdata.map(u
|
|
393
|
-
m_offset = mdata.map(u
|
|
394
|
-
m_length = mdata.map(u
|
|
395
|
-
|
|
588
|
+
m_id = mdata.map(function (u) { return u.i; });
|
|
589
|
+
m_offset = mdata.map(function (u) { return u.o; });
|
|
590
|
+
m_length = mdata.map(function (u) { return u.l; });
|
|
396
591
|
var rmentions = {};
|
|
397
|
-
|
|
398
592
|
for (var i = 0; i < m_id.length; i++) rmentions[m_id[i]] = (delta.deltaMessageReply.repliedToMessage.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
|
|
399
|
-
|
|
593
|
+
|
|
400
594
|
callbackToReturn.messageReply = {
|
|
401
595
|
threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
|
|
402
596
|
messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
|
|
@@ -405,17 +599,11 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
405
599
|
var mercury = JSON.parse(att.mercuryJSON);
|
|
406
600
|
Object.assign(att, mercury);
|
|
407
601
|
return att;
|
|
408
|
-
}).map(att
|
|
602
|
+
}).map(function (att) {
|
|
409
603
|
var x;
|
|
410
|
-
try {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
catch (ex) {
|
|
414
|
-
x = att;
|
|
415
|
-
x.error = ex;
|
|
416
|
-
x.type = "unknown";
|
|
417
|
-
}
|
|
418
|
-
attachImgbbUrlToAttachment(api, x);
|
|
604
|
+
try { x = utils._formatAttachment(att); }
|
|
605
|
+
catch (ex) { x = att; x.error = ex; x.type = "unknown"; }
|
|
606
|
+
attachImageUrlToAttachment(api, x);
|
|
419
607
|
return x;
|
|
420
608
|
}),
|
|
421
609
|
args: (delta.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
|
|
@@ -424,14 +612,12 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
424
612
|
mentions: rmentions,
|
|
425
613
|
timestamp: delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp
|
|
426
614
|
};
|
|
427
|
-
}
|
|
428
|
-
else if (delta.deltaMessageReply.replyToMessageId) {
|
|
615
|
+
} else if (delta.deltaMessageReply.replyToMessageId) {
|
|
429
616
|
return defaultFuncs
|
|
430
617
|
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
|
|
431
618
|
"av": ctx.globalOptions.pageID,
|
|
432
619
|
"queries": JSON.stringify({
|
|
433
620
|
"o0": {
|
|
434
|
-
//Using the same doc_id as forcedFetch
|
|
435
621
|
"doc_id": "2848441488556444",
|
|
436
622
|
"query_params": {
|
|
437
623
|
"thread_and_message_id": {
|
|
@@ -443,28 +629,21 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
443
629
|
})
|
|
444
630
|
})
|
|
445
631
|
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
446
|
-
.then((resData)
|
|
632
|
+
.then(function (resData) {
|
|
447
633
|
if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
|
|
448
634
|
if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
|
|
449
635
|
var fetchData = resData[0].o0.data.message;
|
|
450
636
|
var mobj = {};
|
|
451
637
|
for (var n in fetchData.message.ranges) mobj[fetchData.message.ranges[n].entity.id] = (fetchData.message.text || "").substr(fetchData.message.ranges[n].offset, fetchData.message.ranges[n].length);
|
|
452
|
-
|
|
453
638
|
callbackToReturn.messageReply = {
|
|
454
639
|
threadID: callbackToReturn.threadID,
|
|
455
640
|
messageID: fetchData.message_id,
|
|
456
641
|
senderID: fetchData.message_sender.id.toString(),
|
|
457
|
-
attachments: fetchData.message.blob_attachment.map(att
|
|
642
|
+
attachments: fetchData.message.blob_attachment.map(function (att) {
|
|
458
643
|
var x;
|
|
459
|
-
try {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
catch (ex) {
|
|
463
|
-
x = att;
|
|
464
|
-
x.error = ex;
|
|
465
|
-
x.type = "unknown";
|
|
466
|
-
}
|
|
467
|
-
attachImgbbUrlToAttachment(api, x);
|
|
644
|
+
try { x = utils._formatAttachment({ blob_attachment: att }); }
|
|
645
|
+
catch (ex) { x = att; x.error = ex; x.type = "unknown"; }
|
|
646
|
+
attachImageUrlToAttachment(api, x);
|
|
468
647
|
return x;
|
|
469
648
|
}),
|
|
470
649
|
args: (fetchData.message.text || "").trim().split(/\s+/) || [],
|
|
@@ -474,16 +653,16 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
474
653
|
timestamp: parseInt(fetchData.timestamp_precise)
|
|
475
654
|
};
|
|
476
655
|
})
|
|
477
|
-
.catch(err
|
|
656
|
+
.catch(function (err) { log.error("forcedFetch", err); })
|
|
478
657
|
.finally(function () {
|
|
479
658
|
if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
|
|
480
659
|
!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID ? undefined : (function () { globalCallback(null, callbackToReturn); })();
|
|
481
660
|
});
|
|
661
|
+
} else {
|
|
662
|
+
callbackToReturn.delta = delta;
|
|
482
663
|
}
|
|
483
|
-
else callbackToReturn.delta = delta;
|
|
484
664
|
|
|
485
665
|
if (ctx.globalOptions.autoMarkDelivery) markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
|
|
486
|
-
|
|
487
666
|
return !ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID ? undefined : (function () { globalCallback(null, callbackToReturn); })();
|
|
488
667
|
}
|
|
489
668
|
}
|
|
@@ -492,22 +671,17 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
492
671
|
}
|
|
493
672
|
|
|
494
673
|
if (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents) return;
|
|
674
|
+
|
|
495
675
|
switch (v.delta.class) {
|
|
496
676
|
case "JoinableMode": {
|
|
497
|
-
|
|
498
|
-
try {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
return globalCallback({
|
|
502
|
-
error: "Lỗi gòi!!",
|
|
503
|
-
detail: err,
|
|
504
|
-
res: v.delta,
|
|
505
|
-
type: "parse_error"
|
|
506
|
-
});
|
|
677
|
+
var fmtMsg;
|
|
678
|
+
try { fmtMsg = utils.formatDeltaEvent(v.delta); }
|
|
679
|
+
catch (err) {
|
|
680
|
+
return globalCallback({ error: "Problem parsing message object.", detail: err, res: v.delta, type: "parse_error" });
|
|
507
681
|
}
|
|
508
682
|
return globalCallback(null, fmtMsg);
|
|
509
683
|
}
|
|
510
|
-
case "AdminTextMessage":
|
|
684
|
+
case "AdminTextMessage": {
|
|
511
685
|
switch (v.delta.type) {
|
|
512
686
|
case 'confirm_friend_request':
|
|
513
687
|
case 'shared_album_delete':
|
|
@@ -524,58 +698,44 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
524
698
|
case "magic_words":
|
|
525
699
|
case "change_thread_approval_mode":
|
|
526
700
|
case "messenger_call_log":
|
|
527
|
-
case "participant_joined_group_call":
|
|
701
|
+
case "participant_joined_group_call": {
|
|
528
702
|
var fmtMsg;
|
|
529
|
-
try {
|
|
530
|
-
fmtMsg = utils.formatDeltaEvent(v.delta);
|
|
531
|
-
}
|
|
703
|
+
try { fmtMsg = utils.formatDeltaEvent(v.delta); }
|
|
532
704
|
catch (err) {
|
|
533
|
-
return globalCallback({
|
|
534
|
-
error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
|
|
535
|
-
detail: err,
|
|
536
|
-
res: v.delta,
|
|
537
|
-
type: "parse_error"
|
|
538
|
-
});
|
|
705
|
+
return globalCallback({ error: "Problem parsing message object.", detail: err, res: v.delta, type: "parse_error" });
|
|
539
706
|
}
|
|
540
707
|
return (function () { globalCallback(null, fmtMsg); })();
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return;
|
|
708
|
+
}
|
|
709
|
+
default: return;
|
|
544
710
|
}
|
|
545
|
-
|
|
546
|
-
case "ForcedFetch":
|
|
711
|
+
}
|
|
712
|
+
case "ForcedFetch": {
|
|
547
713
|
if (!v.delta.threadKey) return;
|
|
548
714
|
var mid = v.delta.messageId;
|
|
549
715
|
var tid = v.delta.threadKey.threadFbId;
|
|
550
716
|
if (mid && tid) {
|
|
551
|
-
|
|
717
|
+
var fetchForm = {
|
|
552
718
|
"av": ctx.globalOptions.pageID,
|
|
553
719
|
"queries": JSON.stringify({
|
|
554
720
|
"o0": {
|
|
555
|
-
//This doc_id is valid as of March 25, 2020
|
|
556
721
|
"doc_id": "2848441488556444",
|
|
557
722
|
"query_params": {
|
|
558
723
|
"thread_and_message_id": {
|
|
559
724
|
"thread_id": tid.toString(),
|
|
560
|
-
"message_id": mid
|
|
725
|
+
"message_id": mid
|
|
561
726
|
}
|
|
562
727
|
}
|
|
563
728
|
}
|
|
564
729
|
})
|
|
565
730
|
};
|
|
566
|
-
|
|
567
731
|
defaultFuncs
|
|
568
|
-
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar,
|
|
732
|
+
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, fetchForm)
|
|
569
733
|
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
570
|
-
.then((resData)
|
|
734
|
+
.then(function (resData) {
|
|
571
735
|
if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
|
|
572
|
-
|
|
573
736
|
if (resData[resData.length - 1].successful_results === 0) throw { error: "forcedFetch: there was no successful_results", res: resData };
|
|
574
|
-
|
|
575
737
|
var fetchData = resData[0].o0.data.message;
|
|
576
|
-
|
|
577
738
|
if (utils.getType(fetchData) == "Object") {
|
|
578
|
-
log.info("forcedFetch", fetchData);
|
|
579
739
|
switch (fetchData.__typename) {
|
|
580
740
|
case "ThreadImageMessage":
|
|
581
741
|
(!ctx.globalOptions.selfListen && fetchData.message_sender.id.toString() === ctx.userID) ||
|
|
@@ -596,35 +756,6 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
596
756
|
})();
|
|
597
757
|
break;
|
|
598
758
|
case "UserMessage":
|
|
599
|
-
log.info("ff-Return", {
|
|
600
|
-
type: "message",
|
|
601
|
-
senderID: utils.formatID(fetchData.message_sender.id),
|
|
602
|
-
body: fetchData.message.text || "",
|
|
603
|
-
threadID: utils.formatID(tid.toString()),
|
|
604
|
-
messageID: fetchData.message_id,
|
|
605
|
-
attachments: [{
|
|
606
|
-
type: "share",
|
|
607
|
-
ID: fetchData.extensible_attachment.legacy_attachment_id,
|
|
608
|
-
url: fetchData.extensible_attachment.story_attachment.url,
|
|
609
|
-
|
|
610
|
-
title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
|
|
611
|
-
description: fetchData.extensible_attachment.story_attachment.description.text,
|
|
612
|
-
source: fetchData.extensible_attachment.story_attachment.source,
|
|
613
|
-
|
|
614
|
-
image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
|
|
615
|
-
width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
|
|
616
|
-
height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
|
|
617
|
-
playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
|
|
618
|
-
duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
|
|
619
|
-
|
|
620
|
-
subattachments: fetchData.extensible_attachment.subattachments,
|
|
621
|
-
properties: fetchData.extensible_attachment.story_attachment.properties,
|
|
622
|
-
}],
|
|
623
|
-
mentions: {},
|
|
624
|
-
timestamp: parseInt(fetchData.timestamp_precise),
|
|
625
|
-
participantIDs: (fetchData.participants || (fetchData.messageMetadata ? fetchData.messageMetadata.cid ? fetchData.messageMetadata.cid.canonicalParticipantFbids : fetchData.messageMetadata.participantIds : []) || []),
|
|
626
|
-
isGroup: (fetchData.message_sender.id != tid.toString())
|
|
627
|
-
});
|
|
628
759
|
globalCallback(null, {
|
|
629
760
|
type: "message",
|
|
630
761
|
senderID: utils.formatID(fetchData.message_sender.id),
|
|
@@ -635,178 +766,64 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
635
766
|
type: "share",
|
|
636
767
|
ID: fetchData.extensible_attachment.legacy_attachment_id,
|
|
637
768
|
url: fetchData.extensible_attachment.story_attachment.url,
|
|
638
|
-
|
|
639
769
|
title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
|
|
640
770
|
description: fetchData.extensible_attachment.story_attachment.description.text,
|
|
641
771
|
source: fetchData.extensible_attachment.story_attachment.source,
|
|
642
|
-
|
|
643
772
|
image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
|
|
644
773
|
width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
|
|
645
774
|
height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
|
|
646
775
|
playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
|
|
647
776
|
duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
|
|
648
|
-
|
|
649
777
|
subattachments: fetchData.extensible_attachment.subattachments,
|
|
650
778
|
properties: fetchData.extensible_attachment.story_attachment.properties,
|
|
651
779
|
}],
|
|
652
780
|
mentions: {},
|
|
653
781
|
timestamp: parseInt(fetchData.timestamp_precise),
|
|
654
|
-
participantIDs: (fetchData.participants || (fetchData.messageMetadata ? fetchData.messageMetadata.cid ? fetchData.messageMetadata.cid.canonicalParticipantFbids : fetchData.messageMetadata.participantIds : []) || []),
|
|
655
782
|
isGroup: (fetchData.message_sender.id != tid.toString())
|
|
656
783
|
});
|
|
784
|
+
break;
|
|
657
785
|
}
|
|
658
|
-
}
|
|
659
|
-
else log.error("forcedFetch", fetchData);
|
|
786
|
+
} else log.error("forcedFetch", fetchData);
|
|
660
787
|
})
|
|
661
|
-
.catch((err)
|
|
788
|
+
.catch(function (err) { log.error("forcedFetch", err); });
|
|
662
789
|
}
|
|
663
790
|
break;
|
|
791
|
+
}
|
|
664
792
|
case "ThreadName":
|
|
665
793
|
case "ParticipantsAddedToGroupThread":
|
|
666
|
-
case "ParticipantLeftGroupThread":
|
|
794
|
+
case "ParticipantLeftGroupThread": {
|
|
667
795
|
var formattedEvent;
|
|
668
|
-
try {
|
|
669
|
-
formattedEvent = utils.formatDeltaEvent(v.delta);
|
|
670
|
-
}
|
|
796
|
+
try { formattedEvent = utils.formatDeltaEvent(v.delta); }
|
|
671
797
|
catch (err) {
|
|
672
|
-
return globalCallback({
|
|
673
|
-
error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
|
|
674
|
-
detail: err,
|
|
675
|
-
res: v.delta,
|
|
676
|
-
type: "parse_error"
|
|
677
|
-
});
|
|
798
|
+
return globalCallback({ error: "Problem parsing message object.", detail: err, res: v.delta, type: "parse_error" });
|
|
678
799
|
}
|
|
679
800
|
return (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID) || !ctx.loggedIn ? undefined : (function () { globalCallback(null, formattedEvent); })();
|
|
801
|
+
}
|
|
680
802
|
}
|
|
681
803
|
}
|
|
682
804
|
|
|
683
805
|
function markDelivery(ctx, api, threadID, messageID) {
|
|
684
806
|
if (threadID && messageID) {
|
|
685
|
-
api.markAsDelivered(threadID, messageID, (err)
|
|
807
|
+
api.markAsDelivered(threadID, messageID, function (err) {
|
|
686
808
|
if (err) log.error("markAsDelivered", err);
|
|
687
|
-
else {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
});
|
|
692
|
-
}
|
|
809
|
+
else if (ctx.globalOptions.autoMarkRead) {
|
|
810
|
+
api.markAsRead(threadID, function (err) {
|
|
811
|
+
if (err) log.error("markAsDelivered", err);
|
|
812
|
+
});
|
|
693
813
|
}
|
|
694
814
|
});
|
|
695
815
|
}
|
|
696
816
|
}
|
|
697
817
|
|
|
698
818
|
module.exports = function (defaultFuncs, api, ctx) {
|
|
699
|
-
|
|
700
|
-
// function getSeqID() {
|
|
701
|
-
// ctx.t_mqttCalled = false;
|
|
702
|
-
// async function attemptRequest(retries = 3) {
|
|
703
|
-
// try {
|
|
704
|
-
// if (!ctx.fb_dtsg) {
|
|
705
|
-
// const dtsg = await api.getFreshDtsg();
|
|
706
|
-
// if (!dtsg) {
|
|
707
|
-
// if (retries > 0) {
|
|
708
|
-
// logger.Warning("Failed to get fb_dtsg, retrying...");
|
|
709
|
-
// await utils.sleep(2000); // Longer delay for token retry
|
|
710
|
-
// return attemptRequest(retries - 1);
|
|
711
|
-
// }
|
|
712
|
-
// throw { error: "Could not obtain fb_dtsg after multiple attempts" };
|
|
713
|
-
// }
|
|
714
|
-
// ctx.fb_dtsg = dtsg;
|
|
715
|
-
// }
|
|
716
|
-
|
|
717
|
-
// const form = {
|
|
718
|
-
// av: ctx.userID,
|
|
719
|
-
// fb_dtsg: ctx.fb_dtsg,
|
|
720
|
-
// queries: JSON.stringify({
|
|
721
|
-
// o0: {
|
|
722
|
-
// doc_id: '3336396659757871',
|
|
723
|
-
// query_params: {
|
|
724
|
-
// limit: 1,
|
|
725
|
-
// before: null,
|
|
726
|
-
// tags: ['INBOX'],
|
|
727
|
-
// includeDeliveryReceipts: false,
|
|
728
|
-
// includeSeqID: true
|
|
729
|
-
// }
|
|
730
|
-
// }
|
|
731
|
-
// }),
|
|
732
|
-
// __user: ctx.userID,
|
|
733
|
-
// __a: '1',
|
|
734
|
-
// __req: '8',
|
|
735
|
-
// __hs: '19577.HYP:comet_pkg.2.1..2.1',
|
|
736
|
-
// dpr: '1',
|
|
737
|
-
// fb_api_caller_class: 'RelayModern',
|
|
738
|
-
// fb_api_req_friendly_name: 'MessengerGraphQLThreadlistFetcher'
|
|
739
|
-
// };
|
|
740
|
-
|
|
741
|
-
// const headers = {
|
|
742
|
-
// 'Content-Type': 'application/x-www-form-urlencoded',
|
|
743
|
-
// 'Referer': 'https://www.facebook.com/',
|
|
744
|
-
// 'Origin': 'https://www.facebook.com',
|
|
745
|
-
// 'sec-fetch-site': 'same-origin',
|
|
746
|
-
// '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',
|
|
747
|
-
// 'Cookie': ctx.jar.getCookieString('https://www.facebook.com'),
|
|
748
|
-
// 'accept': '*/*',
|
|
749
|
-
// 'accept-encoding': 'gzip, deflate, br'
|
|
750
|
-
// };
|
|
751
|
-
|
|
752
|
-
// const resData = await defaultFuncs
|
|
753
|
-
// .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form, { headers })
|
|
754
|
-
// .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
755
|
-
|
|
756
|
-
// if (debugSeq) {
|
|
757
|
-
// console.log('GraphQL SeqID Response:', JSON.stringify(resData, null, 2));
|
|
758
|
-
// }
|
|
759
|
-
|
|
760
|
-
// if (resData.error === 1357004 || resData.error === 1357001) {
|
|
761
|
-
// if (retries > 0) {
|
|
762
|
-
// logger.Warning("Session error, refreshing token and retrying...");
|
|
763
|
-
// ctx.fb_dtsg = null; // Force new token
|
|
764
|
-
// await utils.sleep(2000);
|
|
765
|
-
// return attemptRequest(retries - 1);
|
|
766
|
-
// }
|
|
767
|
-
// throw { error: "Session refresh failed after retries" };
|
|
768
|
-
// }
|
|
769
|
-
|
|
770
|
-
// if (!Array.isArray(resData)) {
|
|
771
|
-
// throw { error: "Invalid response format", res: resData };
|
|
772
|
-
// }
|
|
773
|
-
|
|
774
|
-
// const seqID = resData[0]?.o0?.data?.viewer?.message_threads?.sync_sequence_id;
|
|
775
|
-
// if (!seqID) {
|
|
776
|
-
// throw { error: "Missing sync_sequence_id", res: resData };
|
|
777
|
-
// }
|
|
778
|
-
|
|
779
|
-
// ctx.lastSeqId = seqID;
|
|
780
|
-
// if (debugSeq) {
|
|
781
|
-
// console.log('Got SeqID:', ctx.lastSeqId);
|
|
782
|
-
// }
|
|
783
|
-
|
|
784
|
-
// return listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
785
|
-
|
|
786
|
-
// } catch (err) {
|
|
787
|
-
// if (retries > 0) {
|
|
788
|
-
// console.log("Request failed, retrying...");
|
|
789
|
-
|
|
790
|
-
// return attemptRequest(retries - 1);
|
|
791
|
-
// }
|
|
792
|
-
// throw err;
|
|
793
|
-
// }
|
|
794
|
-
// }
|
|
795
|
-
|
|
796
|
-
// return attemptRequest()
|
|
797
|
-
// .catch((err) => {
|
|
798
|
-
// log.error("getSeqId", err);
|
|
799
|
-
// if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
|
|
800
|
-
// return globalCallback(err);
|
|
801
|
-
// });
|
|
802
|
-
// }
|
|
819
|
+
var globalCallback = identity;
|
|
803
820
|
|
|
804
821
|
getSeqID = function getSeqID() {
|
|
805
822
|
ctx.t_mqttCalled = false;
|
|
806
823
|
defaultFuncs
|
|
807
824
|
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
|
|
808
825
|
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
809
|
-
.then((resData)
|
|
826
|
+
.then(function (resData) {
|
|
810
827
|
if (utils.getType(resData) != "Array") throw { error: "Not logged in", res: resData };
|
|
811
828
|
if (resData && resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
|
|
812
829
|
if (resData[resData.length - 1].successful_results === 0) throw { error: "getSeqId: there was no successful_results", res: resData };
|
|
@@ -815,7 +832,7 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
815
832
|
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
816
833
|
} else throw { error: "getSeqId: no sync_sequence_id found.", res: resData };
|
|
817
834
|
})
|
|
818
|
-
.catch((err)
|
|
835
|
+
.catch(function (err) {
|
|
819
836
|
log.error("getSeqId", err);
|
|
820
837
|
if (utils.getType(err) == "Object" && err.error === "Not logged in") ctx.loggedIn = false;
|
|
821
838
|
return globalCallback(err);
|
|
@@ -825,8 +842,7 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
825
842
|
return function (callback) {
|
|
826
843
|
class MessageEmitter extends EventEmitter {
|
|
827
844
|
stopListening(callback) {
|
|
828
|
-
|
|
829
|
-
callback = callback || (() => { });
|
|
845
|
+
callback = callback || (function () { });
|
|
830
846
|
globalCallback = identity;
|
|
831
847
|
if (ctx.mqttClient) {
|
|
832
848
|
ctx.mqttClient.unsubscribe("/webrtc");
|
|
@@ -841,23 +857,19 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
841
857
|
}
|
|
842
858
|
|
|
843
859
|
async stopListeningAsync() {
|
|
844
|
-
return new Promise((resolve)
|
|
860
|
+
return new Promise(function (resolve) {
|
|
845
861
|
this.stopListening(resolve);
|
|
846
|
-
});
|
|
862
|
+
}.bind(this));
|
|
847
863
|
}
|
|
848
864
|
}
|
|
849
865
|
|
|
850
|
-
|
|
866
|
+
var msgEmitter = new MessageEmitter();
|
|
851
867
|
globalCallback = (callback || function (error, message) {
|
|
852
|
-
if (error)
|
|
853
|
-
return msgEmitter.emit("error", error);
|
|
854
|
-
}
|
|
868
|
+
if (error) return msgEmitter.emit("error", error);
|
|
855
869
|
msgEmitter.emit("message", message);
|
|
856
870
|
});
|
|
857
871
|
|
|
858
|
-
|
|
859
|
-
if (!ctx.firstListen)
|
|
860
|
-
ctx.lastSeqId = null;
|
|
872
|
+
if (!ctx.firstListen) ctx.lastSeqId = null;
|
|
861
873
|
ctx.syncToken = undefined;
|
|
862
874
|
ctx.t_mqttCalled = false;
|
|
863
875
|
|
|
@@ -883,8 +895,8 @@ module.exports = function (defaultFuncs, api, ctx) {
|
|
|
883
895
|
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
884
896
|
}
|
|
885
897
|
|
|
886
|
-
api.stopListening = msgEmitter.stopListening;
|
|
887
|
-
api.stopListeningAsync = msgEmitter.stopListeningAsync;
|
|
898
|
+
api.stopListening = msgEmitter.stopListening.bind(msgEmitter);
|
|
899
|
+
api.stopListeningAsync = msgEmitter.stopListeningAsync.bind(msgEmitter);
|
|
888
900
|
return msgEmitter;
|
|
889
901
|
};
|
|
890
902
|
};
|