fca-nazrul-remastered
Version:
Facebook-chat-api protect and deploy by Kanzu and HZI Team
956 lines (862 loc) • 35.1 kB
JavaScript
/* eslint-disable linebreak-style */
;
const utils = require('../utils');
const log = require('npmlog');
const mqtt = require('mqtt');
const WebSocket = require('ws');
const HttpsProxyAgent = require('https-proxy-agent');
const EventEmitter = require('events');
const Duplexify = require('duplexify');
const {
Transform
} = require('stream');
var identity = function() {};
var form = {};
var getSeqID = function() {};
global.Fca.Data.MsgCount = new Map();
global.Fca.Data.event = new Map();
const topics = ['/ls_req', '/ls_resp', '/legacy_web', '/webrtc', '/rtc_multi', '/onevc', '/br_sr', '/sr_res', '/t_ms', '/thread_typing', '/orca_typing_notifications', '/notify_disconnect', '/orca_presence', '/inbox', '/mercury', '/messaging_events', '/orca_message_notifications', '/pp', '/webrtc_response'];
let WebSocket_Global;
function buildProxy() {
const Proxy = new Transform({
objectMode: false,
transform(chunk, enc, next) {
if (WebSocket_Global.readyState !== WebSocket_Global.OPEN) {
return next();
}
let data;
if (typeof chunk === 'string') {
data = Buffer.from(chunk, 'utf8');
} else {
data = chunk;
}
WebSocket_Global.send(data);
next();
},
flush(done) {
WebSocket_Global.close();
done();
},
writev(chunks, cb) {
const buffers = chunks.map(({ chunk }) => {
if (typeof chunk === 'string') {
return Buffer.from(chunk, 'utf8');
}
return chunk;
});
this._write(Buffer.concat(buffers), 'binary', cb);
},
});
return Proxy;
}
function buildStream(options, WebSocket, Proxy) {
const Stream = Duplexify(undefined, undefined, options);
Stream.socket = WebSocket;
WebSocket.onclose = () => {
Stream.end();
Stream.destroy();
};
WebSocket.onerror = (err) => {
Stream.destroy(err);
};
WebSocket.onmessage = (event) => {
const data = event.data instanceof ArrayBuffer ? Buffer.from(event.data) : Buffer.from(event.data, 'utf8');
Stream.push(data);
};
WebSocket.onopen = () => {
Stream.setReadable(Proxy);
Stream.setWritable(Proxy);
Stream.emit('connect');
};
WebSocket_Global = WebSocket;
Proxy.on('close', () => WebSocket.close());
return Stream;
}
function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
const chatOn = ctx.globalOptions.online;
const foreground = false;
const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
const GUID = utils.getGUID()
const username = {
u: ctx.userID,
s: sessionID,
chat_on: chatOn,
fg: foreground,
d: GUID,
ct: 'websocket',
aid: '219994525426954',
aids: null,
mqtt_sid: '',
cp: 3,
ecp: 10,
st: [],
pm: [],
dc: '',
no_auto_fg: true,
gas: null,
pack: [],
p: null,
php_override: ""
};
const cookies = ctx.jar.getCookies('https://www.facebook.com').join('; ');
let host;
if (ctx.mqttEndpoint) {
host = `${ctx.mqttEndpoint}&sid=${sessionID}&cid=${GUID}`;
} else if (ctx.region) {
host = `wss://edge-chat.facebook.com/chat?region=${ctx.region.toLowerCase()}&sid=${sessionID}&cid=${GUID}`;
} else {
host = `wss://edge-chat.facebook.com/chat?sid=${sessionID}&cid=${GUID}`;
}
const options = {
clientId: 'mqttwsclient',
protocolId: 'MQIsdp',
protocolVersion: 3,
username: JSON.stringify(username),
clean: true,
wsOptions: {
headers: {
Cookie: cookies,
Origin: 'https://www.facebook.com',
'User-Agent': ctx.globalOptions.userAgent,
Referer: 'https://www.facebook.com/',
Host: new URL(host).hostname,
},
origin: 'https://www.facebook.com',
protocolVersion: 13,
binaryType: 'arraybuffer',
},
keepalive: 60,
reschedulePings: true,
reconnectPeriod: 3,
};
if (ctx.globalOptions.proxy !== undefined) {
const agent = new HttpsProxyAgent(ctx.globalOptions.proxy);
options.wsOptions.agent = agent;
}
ctx.mqttClient = new mqtt.Client(() => buildStream(options, new WebSocket(host, options.wsOptions), buildProxy()), options);
global.mqttClient = ctx.mqttClient;
global.mqttClient.on('error', (err) => {
log.error('listenMqtt', err);
global.mqttClient.end();
if (ctx.globalOptions.autoReconnect) {
getSeqID();
} else {
globalCallback({
type: 'stop_listen',
error: 'Server Đã Sập - Auto Restart'
}, null);
return process.exit(1);
}
});
global.mqttClient.on('connect', () => {
if (!global.Fca.Data.Setup || global.Fca.Data.Setup === undefined) {
if (global.Fca.Require.FastConfig.RestartMQTT_Minutes !== 0 && global.Fca.Data.StopListening !== true) {
global.Fca.Data.Setup = true;
setTimeout(() => {
global.Fca.Require.logger.Warning('Closing MQTT Client...');
ctx.mqttClient.end();
global.Fca.Require.logger.Warning('Reconnecting MQTT Client...');
global.Fca.Data.Setup = false;
getSeqID();
}, Number(global.Fca.Require.FastConfig.RestartMQTT_Minutes) * 60 * 1000);
}
}
if (process.env.OnStatus === undefined) {
global.Fca.Require.logger.Normal('Bạn Đang Sài Phiên Bản: Premium Access');
if (Number(global.Fca.Require.FastConfig.AutoRestartMinutes) === 0) {
// something
} else if (Number(global.Fca.Require.FastConfig.AutoRestartMinutes) < 10) {
log.warn('AutoRestartMinutes', 'The number of minutes to automatically restart must be more than 10 minutes');
} else if (Number(global.Fca.Require.FastConfig.AutoRestartMinutes) < 0) {
log.warn('AutoRestartMinutes', 'Invalid auto-restart minutes!');
} else {
global.Fca.Require.logger.Normal(global.Fca.getText(global.Fca.Require.Language.Src.AutoRestart, global.Fca.Require.FastConfig.AutoRestartMinutes));
global.Fca.Require.logger.Normal(`Auto Restart MQTT Client After: ${global.Fca.Require.FastConfig.RestartMQTT_Minutes} Minutes`);
setTimeout(() => {
global.Fca.Require.logger.Normal(global.Fca.Require.Language.Src.OnRestart);
process.exit(1);
}, Number(global.Fca.Require.FastConfig.AutoRestartMinutes) * 60000);
}
require('../broadcast').startBroadcasting();
const MemoryManager = require('../Extra/Src/Release_Memory');
const path = require('path');
const SettingMemoryManager = {
warningThreshold: 0.7,
releaseThreshold: 0.8,
maxThreshold: 0.9,
interval: 300 * 1000,
logLevel: 'warn',
logFile: path.join(process.cwd(), 'Horizon_Database' ,'memory.log'),
smartReleaseEnabled: true,
allowLog: (global.Fca.Require.FastConfig.AntiStuckAndMemoryLeak.LogFile.Use || false)
};
const memoryManager = new MemoryManager(SettingMemoryManager);
memoryManager.autoStart(60 * 60 * 1000);
if (global.Fca.Require.FastConfig.AntiStuckAndMemoryLeak.AutoRestart.Use) {
memoryManager.onMaxMemory(function() {
global.Fca.Require.logger.Warning('Memory Usage >= 90% - Auto Restart Avoid Crash');
process.exit(1);
});
}
process.env.OnStatus = true;
}
topics.forEach((topicsub) => global.mqttClient.subscribe(topicsub));
let topic;
const queue = {
sync_api_version: 11,
max_deltas_able_to_process: 100,
delta_batch_size: 500,
encoding: 'JSON',
entity_fbid: ctx.userID,
};
topic = "/messenger_sync_create_queue";
queue.initial_titan_sequence_id = ctx.lastSeqId;
queue.device_params = null;
global.mqttClient.publish(topic, JSON.stringify(queue), {
qos: 1,
retain: false
});
var rTimeout = setTimeout(function() {
global.mqttClient.end();
getSeqID();
}, 3000);
ctx.tmsWait = function() {
clearTimeout(rTimeout);
ctx.globalOptions.emitReady ? globalCallback({
type: "ready",
error: null
}) : '';
delete ctx.tmsWait;
};
});
const HandleMessage = function(topic, message, _packet) {
const jsonMessage = JSON.parse(message.toString());
if (topic === "/t_ms") {
if (ctx.tmsWait && typeof ctx.tmsWait == "function") ctx.tmsWait();
if (jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
ctx.syncToken = jsonMessage.syncToken;
}
if (jsonMessage.lastIssuedSeqId) ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
//If it contains more than 1 delta
for (var i in jsonMessage.deltas) {
var delta = jsonMessage.deltas[i];
parseDelta(defaultFuncs, api, ctx, globalCallback, {
"delta": delta
});
}
} else if (topic === "/ls_resp") {
const payload = JSON.parse(jsonMessage.payload); //'{"name":null,"step":[1,[1,[4,0,1,[5,"taskExists",[19,"415"]]],[23,[2,0],[1,[5,"replaceOptimsiticMessage","7192532113093667880","mid.$gABfX5li9LA6VdUymnWPRAdlkiawo"]]]],[1,[4,0,1,[5,"taskExists",[19,"415"]]],[23,[2,0],[1,[5,"mailboxTaskCompletionApiOnTaskCompletion",[19,"415"],true]]]],[1,[4,0,1,[5,"taskExists",[19,"415"]]],[23,[2,0],[1,[5,"removeTask",[19,"415"],[9]]]]]]}'
const request_ID = jsonMessage.request_id;
if (ctx.callback_Task[request_ID] != undefined && ctx.callback_Task[request_ID].type != undefined) {
const {
callback,
type
} = ctx.callback_Task[request_ID];
const Data = new getRespData(type, payload);
if (!callback) {
return;
}
else if (!Data) {
callback("Something went wrong 🐳", null);
} else {
callback(null, Data);
}
}
} else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
var typ = {
type: "typ",
isTyping: !!jsonMessage.state,
from: jsonMessage.sender_fbid.toString(),
threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
};
(function() {
globalCallback(null, typ);
})();
} else if (topic === "/orca_presence") {
if (!ctx.globalOptions.updatePresence) {
for (var i in jsonMessage.list) {
var data = jsonMessage.list[i];
var userID = data["u"];
var presence = {
type: "presence",
userID: userID.toString(),
//Convert to ms
timestamp: data["l"] * 1000,
statuses: data["p"]
};
(function() {
globalCallback(null, presence);
})();
}
}
}
};
global.mqttClient.on('message', HandleMessage);
process.on('SIGINT', () => {
LogUptime();
process.kill(process.pid);
});
process.on('exit', LogUptime);
}
function getRespData(Type, payload) {
try {
switch (Type) {
case "sendMqttMessage": {
return {
type: Type,
threadID: payload.step[1][2][2][1][2], //this is sick bro
messageID: payload.step[1][2][2][1][3],
payload: payload.step[1][2]
};
}
default: { //!very LAZY :> cook yourself
return {
Data: payload.step[1][2][2][1],
type: Type,
payload: payload.step[1][2]
};
}
}
} catch (e) {
return null;
}
}
function LogUptime() {
const uptime = process.uptime();
const {
join
} = require('path');
const filePath = join(__dirname, '../CountTime.json');
let time1;
if (global.Fca.Require.fs.existsSync(filePath)) {
time1 = Number(global.Fca.Require.fs.readFileSync(filePath, 'utf8')) || 0;
} else {
time1 = 0;
}
global.Fca.Require.fs.writeFileSync(filePath, String(Number(uptime) + time1), 'utf8');
}
if (global.Fca.Require.FastConfig.AntiGetInfo.AntiGetThreadInfo) {
setInterval(() => {
try {
const { updateMessageCount, getData, hasData } = require('../Extra/ExtraGetThread');
const Data = global.Fca.Data.MsgCount;
const Arr = Array.from(Data.keys());
for (let i of Arr) {
const Count = parseInt(Data.get(i));
if (hasData(i)) {
let x = getData(i);
x.messageCount += Count;
updateMessageCount(i, x);
Data.delete(i);
}
}
} catch (e) {
console.log(e);
}
}, 30 * 1000);
}
function parseDelta(defaultFuncs, api, ctx, globalCallback, {
delta
}) {
if (delta.class === 'NewMessage') {
if (ctx.globalOptions.pageID && ctx.globalOptions.pageID !== delta.queue) return;
const resolveAttachmentUrl = (i) => {
if (!delta.attachments || i === delta.attachments.length || utils.getType(delta.attachments) !== 'Array') {
let fmtMsg;
try {
fmtMsg = utils.formatDeltaMessage(delta);
} catch (err) {
return log.error('Lỗi Nhẹ', err);
}
if (fmtMsg) {
const isGroup = fmtMsg.isGroup;
const threadID = fmtMsg.threadID;
const messageID = fmtMsg.messageID;
global.Fca.Data.event.set("Data", {
isGroup,
threadID,
messageID
});
if (global.Fca.Require.FastConfig.AntiGetInfo.AntiGetThreadInfo) {
global.Fca.Data.MsgCount.set(fmtMsg.threadID, ((global.Fca.Data.MsgCount.get(fmtMsg.threadID)) + 1 || 1));
}
if (ctx.globalOptions.autoMarkDelivery) {
markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
}
if (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID) return;
globalCallback(null, fmtMsg);
}
} else {
const attachment = delta.attachments[i];
if (attachment.mercury.attach_type === 'photo') {
api.resolvePhotoUrl(attachment.fbid, (err, url) => {
if (!err) attachment.mercury.metadata.url = url;
resolveAttachmentUrl(i + 1);
});
} else {
resolveAttachmentUrl(i + 1);
}
}
};
resolveAttachmentUrl(0);
} else if (delta.class === 'ClientPayload') {
const clientPayload = utils.decodeClientPayload(delta.payload);
if (clientPayload && clientPayload.deltas) {
for (const delta of clientPayload.deltas) {
if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
const messageReaction = {
type: 'message_reaction',
threadID: (delta.deltaMessageReaction.threadKey.threadFbId ? delta.deltaMessageReaction.threadKey.threadFbId : delta.deltaMessageReaction.threadKey.otherUserFbId).toString(),
messageID: delta.deltaMessageReaction.messageId,
reaction: delta.deltaMessageReaction.reaction,
senderID: delta.deltaMessageReaction.senderId.toString(),
userID: delta.deltaMessageReaction.userId.toString(),
};
globalCallback(null, messageReaction);
} else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
const messageUnsend = {
type: 'message_unsend',
threadID: (delta.deltaRecallMessageData.threadKey.threadFbId ? delta.deltaRecallMessageData.threadKey.threadFbId : delta.deltaRecallMessageData.threadKey.otherUserFbId).toString(),
messageID: delta.deltaRecallMessageData.messageID,
senderID: delta.deltaRecallMessageData.senderID.toString(),
deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
timestamp: delta.deltaRecallMessageData.timestamp,
};
globalCallback(null, messageUnsend);
} else if (delta.deltaMessageReply) {
const mdata =
delta.deltaMessageReply.message === undefined ?
[] :
delta.deltaMessageReply.message.data === undefined ?
[] :
delta.deltaMessageReply.message.data.prng === undefined ?
[] :
JSON.parse(delta.deltaMessageReply.message.data.prng);
const m_id = mdata.map((u) => u.i);
const m_offset = mdata.map((u) => u.o);
const m_length = mdata.map((u) => u.l);
const mentions = {};
for (let 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]);
}
const callbackToReturn = {
type: 'message_reply',
threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
messageID: delta.deltaMessageReply.message.messageMetadata.messageId,
senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
attachments: ( delta.deltaMessageReply.message.attachments || [] )
.map((att) => {
const mercury = JSON.parse(att.mercuryJSON);
Object.assign(att, mercury);
return att;
})
.map((att) => {
let x;
try {
x = utils._formatAttachment(att);
} catch (ex) {
x = att;
x.error = ex;
x.type = 'unknown';
}
return x;
}),
args: (delta.deltaMessageReply.message.body || '').trim().split(/\s+/),
body: delta.deltaMessageReply.message.body || '',
isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
mentions,
timestamp: parseInt(delta.deltaMessageReply.message.messageMetadata.timestamp),
participantIDs: (delta.deltaMessageReply.message.participants || []).map((e) => e.toString()),
};
if (delta.deltaMessageReply.repliedToMessage) {
const mdata =
delta.deltaMessageReply.repliedToMessage === undefined ?
[] :
delta.deltaMessageReply.repliedToMessage.data === undefined ?
[] :
delta.deltaMessageReply.repliedToMessage.data.prng === undefined ?
[] :
JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng);
const m_id = mdata.map((u) => u.i);
const m_offset = mdata.map((u) => u.o);
const m_length = mdata.map((u) => u.l);
const rmentions = {};
for (let 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]);
}
callbackToReturn.messageReply = {
threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
attachments: delta.deltaMessageReply.repliedToMessage.attachments
.map((att) => {
let mercury;
try {
mercury = JSON.parse(att.mercuryJSON);
Object.assign(att, mercury);
} catch (ex) {
mercury = {};
}
return att;
})
.map((att) => {
let x;
try {
x = utils._formatAttachment(att);
} catch (ex) {
x = att;
x.error = ex;
x.type = 'unknown';
}
return x;
}),
args: (delta.deltaMessageReply.repliedToMessage.body || '').trim().split(/\s+/),
body: delta.deltaMessageReply.repliedToMessage.body || '',
isGroup: !!delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
mentions: rmentions,
timestamp: parseInt(delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp),
participantIDs: (delta.deltaMessageReply.repliedToMessage.participants || []).map((e) => e.toString()),
};
} else if (delta.deltaMessageReply.replyToMessageId) {
return defaultFuncs
.post('https://www.facebook.com/api/graphqlbatch/', ctx.jar, {
av: ctx.globalOptions.pageID,
queries: JSON.stringify({
o0: {
doc_id: '2848441488556444',
query_params: {
thread_and_message_id: {
thread_id: callbackToReturn.threadID,
message_id: delta.deltaMessageReply.replyToMessageId.id,
},
},
},
}),
})
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
.then((resData) => {
if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
if (resData[resData.length - 1].successful_results === 0) throw {
error: 'forcedFetch: there was no successful_results',
res: resData
};
const fetchData = resData[0].o0.data.message;
const mobj = {};
for (const 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);
}
callbackToReturn.messageReply = {
type: 'Message',
threadID: callbackToReturn.threadID,
messageID: fetchData.message_id,
senderID: fetchData.message_sender.id.toString(),
attachments: fetchData.message.blob_attachment.map((att) => utils._formatAttachment({
blob_attachment: att
})),
args: (fetchData.message.text || '').trim().split(/\s+/) || [],
body: fetchData.message.text || '',
isGroup: callbackToReturn.isGroup,
mentions: mobj,
timestamp: parseInt(fetchData.timestamp_precise),
};
})
.catch((err) => log.error('forcedFetch', err))
.finally(() => {
if (ctx.globalOptions.autoMarkDelivery) {
markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
}
if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
globalCallback(null, callbackToReturn);
});
} else {
callbackToReturn.delta = delta;
}
if (ctx.globalOptions.autoMarkDelivery) {
markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
}
if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
globalCallback(null, callbackToReturn);
}
}
return;
}
}
switch (delta.class) {
case 'ReadReceipt': {
let fmtMsg;
try {
fmtMsg = utils.formatDeltaReadReceipt(delta);
} catch (err) {
return log.error('Lỗi Nhẹ', err);
}
globalCallback(null, fmtMsg);
break;
}
case 'AdminTextMessage': {
switch (delta.type) {
case 'joinable_group_link_mode_change':
case 'magic_words':
case 'pin_messages_v2':
case 'change_thread_theme':
case 'change_thread_icon':
case 'change_thread_nickname':
case 'change_thread_admins':
case 'change_thread_approval_mode':
case 'group_poll':
case 'messenger_call_log':
case 'participant_joined_group_call': {
let fmtMsg;
try {
fmtMsg = utils.formatDeltaEvent(delta);
} catch (err) {
console.log(delta);
return log.error('Lỗi Nhẹ', err);
}
globalCallback(null, fmtMsg);
break;
}
}
break;
}
//For group images
case 'ForcedFetch': {
if (!delta.threadKey) return;
const mid = delta.messageId;
const tid = delta.threadKey.threadFbId;
if (mid && tid) {
const form = {
av: ctx.globalOptions.pageID,
queries: JSON.stringify({
o0: {
doc_id: '2848441488556444',
query_params: {
thread_and_message_id: {
thread_id: tid.toString(),
message_id: mid,
},
},
},
}),
};
defaultFuncs
.post('https://www.facebook.com/api/graphqlbatch/', ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
.then((resData) => {
if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
if (resData[resData.length - 1].successful_results === 0) throw {
error: 'forcedFetch: there was no successful_results',
res: resData
};
const fetchData = resData[0].o0.data.message;
if (utils.getType(fetchData) === 'Object') {
log.info('forcedFetch', fetchData);
switch (fetchData.__typename) {
case 'ThreadImageMessage':
if (!ctx.globalOptions.selfListen && fetchData.message_sender.id.toString() === ctx.userID) return;
if (!ctx.loggedIn) return;
globalCallback(null, {
type: 'change_thread_image',
threadID: utils.formatID(tid.toString()),
snippet: fetchData.snippet,
timestamp: fetchData.timestamp_precise,
author: fetchData.message_sender.id,
image: {
attachmentID: fetchData.image_with_metadata && fetchData.image_with_metadata.legacy_attachment_id,
width: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.x,
height: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.y,
url: fetchData.image_with_metadata && fetchData.image_with_metadata.preview.uri,
},
});
break;
case 'UserMessage': {
const event = {
type: 'message',
senderID: utils.formatID(fetchData.message_sender.id),
body: fetchData.message.text || '',
threadID: utils.formatID(tid.toString()),
messageID: fetchData.message_id,
attachments: [{
type: 'share',
ID: fetchData.extensible_attachment.legacy_attachment_id,
url: fetchData.extensible_attachment.story_attachment.url,
title: fetchData.extensible_attachment.story_attachment.title_with_entities.text,
description: fetchData.extensible_attachment.story_attachment.description.text,
source: fetchData.extensible_attachment.story_attachment.source,
image: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).uri,
width: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).width,
height: ((fetchData.extensible_attachment.story_attachment.media || {}).image || {}).height,
playable: (fetchData.extensible_attachment.story_attachment.media || {}).is_playable || false,
duration: (fetchData.extensible_attachment.story_attachment.media || {}).playable_duration_in_ms || 0,
subattachments: fetchData.extensible_attachment.subattachments,
properties: fetchData.extensible_attachment.story_attachment.properties,
}],
mentions: {},
timestamp: parseInt(fetchData.timestamp_precise),
isGroup: (fetchData.message_sender.id !== tid.toString()),
};
log.info('ff-Return', event);
globalCallback(null, event);
break;
}
default:
log.error('forcedFetch', fetchData);
}
} else {
log.error('forcedFetch', fetchData);
}
})
.catch((err) => log.error('forcedFetch', err));
}
break;
}
case 'ThreadName':
case 'ParticipantsAddedToGroupThread':
case 'ParticipantLeftGroupThread': {
let formattedEvent;
try {
formattedEvent = utils.formatDeltaEvent(delta);
} catch (err) {
console.log(err);
return log.error('Lỗi Nhẹ', err);
}
if (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID) return;
if (!ctx.loggedIn) return;
globalCallback(null, formattedEvent);
break;
}
case 'NewMessage': {
const hasLiveLocation = delta => {
const attachment = delta.attachments?.[0]?.mercury?.extensible_attachment;
const storyAttachment = attachment?.story_attachment;
return storyAttachment?.style_list?.includes('message_live_location');
};
if (delta.attachments?.length === 1 && hasLiveLocation(delta)) {
delta.class = 'UserLocation';
try {
const fmtMsg = utils.formatDeltaEvent(delta);
globalCallback(null, fmtMsg);
} catch (err) {
console.log(delta);
log.error('Lỗi Nhẹ', err);
}
}
break;
}
}
}
function markDelivery(ctx, api, threadID, messageID) {
if (threadID && messageID) {
api.markAsDelivered(threadID, messageID, (err) => {
if (err) log.error('markAsDelivered', err);
else {
if (ctx.globalOptions.autoMarkRead) {
api.markAsRead(threadID, (err) => {
if (err) log.error('markAsDelivered', err);
});
}
}
});
}
}
module.exports = function(defaultFuncs, api, ctx) {
var globalCallback = identity;
var okeoke;
getSeqID = function getSeqID() {
ctx.t_mqttCalled = false;
defaultFuncs
.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
.then(res => {
okeoke = res;
return res;
})
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
.then((resData) => {
if (utils.getType(resData) != "Array") {
if (okeoke.request.uri && okeoke.request.uri.href.includes("https://www.facebook.com/checkpoint/")) {
if (okeoke.request.uri.href.includes('601051028565049')) {
return global.Fca.BypassAutomationNotification(undefined, ctx.jar, ctx.globalOptions, undefined ,process.env.UID)
}
}
if (global.Fca.Require.FastConfig.AutoLogin) {
return global.Fca.Require.logger.Warning(global.Fca.Require.Language.Index.AutoLogin, function() {
return global.Fca.Action('AutoLogin');
});
} else if (!global.Fca.Require.FastConfig.AutoLogin) {
return global.Fca.Require.logger.Error(global.Fca.Require.Language.Index.ErrAppState);
}
return;
} else {
if (resData && resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
if (resData[resData.length - 1].successful_results === 0) throw {
error: "getSeqId: there was no successful_results",
res: resData
};
if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
ctx.lastSeqId = resData[0].o0.data.viewer.message_threads.sync_sequence_id;
listenMqtt(defaultFuncs, api, ctx, globalCallback);
} else throw {
error: "getSeqId: no sync_sequence_id found.",
res: resData
};
}
})
.catch((err) => {
log.error("getSeqId", err);
if (okeoke.request.uri && okeoke.request.uri.href.includes("https://www.facebook.com/checkpoint/")) {
if (okeoke.request.uri.href.includes('601051028565049')) {
return global.Fca.BypassAutomationNotification(undefined, ctx.jar, ctx.globalOptions, undefined ,process.env.UID)
}
}
if (utils.getType(err) == "Object" && err.error === global.Fca.Require.Language.Index.ErrAppState) ctx.loggedIn = false;
return globalCallback(err);
});
};
return function(callback) {
class MessageEmitter extends EventEmitter {
stopListening(callback) {
callback = callback || (() => {});
globalCallback = identity;
if (ctx.mqttClient) {
ctx.mqttClient.unsubscribe("/webrtc");
ctx.mqttClient.unsubscribe("/rtc_multi");
ctx.mqttClient.unsubscribe("/onevc");
ctx.mqttClient.publish("/browser_close", "{}");
ctx.mqttClient.end(false, function(...data) {
ctx.mqttClient = undefined;
});
}
global.Fca.Data.StopListening = true;
}
}
var msgEmitter = new MessageEmitter();
globalCallback = (callback || function(error, message) {
if (error) return msgEmitter.emit("error", error);
msgEmitter.emit("message", message);
});
//Reset some stuff
if (!ctx.firstListen) ctx.lastSeqId = null;
ctx.syncToken = undefined;
ctx.t_mqttCalled = false;
//Same request as getThreadList
form = {
av: ctx.globalOptions.pageID,
queries: JSON.stringify({
o0: {
doc_id: '3336396659757871',
query_params: {
limit: 1,
before: null,
tags: ['INBOX'],
includeDeliveryReceipts: false,
includeSeqID: true,
},
},
}),
};
if (!ctx.firstListen || !ctx.lastSeqId) getSeqID();
else listenMqtt(defaultFuncs, api, ctx, globalCallback);
ctx.firstListen = false;
return msgEmitter;
};
};