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