cyber-fca
Version:
A Facebook chat api for messenger bots.
461 lines (437 loc) • 19.5 kB
JavaScript
var utils = require("./utils");
var cheerio = require("cheerio");
var log = require("npmlog");
/*var { getThemeColors } = require("../../func/utils/log.js");
var logger = require("../../func/utils/log.js");
var { cra, cv, cb, co } = getThemeColors();*/
log.maxRecordSize = 100;
var checkVerified = null;
const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
global.ditconmemay = false;
function setOptions(globalOptions, options) {
Object.keys(options).map(function (key) {
switch (Boolean_Option.includes(key)) {
case true: {
globalOptions[key] = Boolean(options[key]);
break;
}
case false: {
switch (key) {
case 'pauseLog': {
if (options.pauseLog) log.pause();
else log.resume();
break;
}
case 'logLevel': {
log.level = options.logLevel;
globalOptions.logLevel = options.logLevel;
break;
}
case 'logRecordSize': {
log.maxRecordSize = options.logRecordSize;
globalOptions.logRecordSize = options.logRecordSize;
break;
}
case 'pageID': {
globalOptions.pageID = options.pageID.toString();
break;
}
case 'userAgent': {
globalOptions.userAgent = (options.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36');
break;
}
case 'proxy': {
if (typeof options.proxy != "string") {
delete globalOptions.proxy;
utils.setProxy();
} else {
globalOptions.proxy = options.proxy;
utils.setProxy(globalOptions.proxy);
}
break;
}
default: {
log.warn("setOptions", "Unrecognized option given to setOptions: " + key);
break;
}
}
break;
}
}
});
}
function buildAPI(globalOptions, html, jar) {
let fb_dtsg = null;
let irisSeqID = null;
function extractFromHTML() {
try {
const $ = cheerio.load(html);
$('script').each((i, script) => {
if (!fb_dtsg) {
const scriptText = $(script).html() || '';
const patterns = [
/\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
/\["DTSGInitData",\[\],{"token":"([^"]+)"/,
/"token":"([^"]+)"/,
/{\\"token\\":\\"([^\\]+)\\"/,
/,\{"token":"([^"]+)"\},\d+\]/,
/"async_get_token":"([^"]+)"/,
/"dtsg":\{"token":"([^"]+)"/,
/DTSGInitialData[^>]+>([^<]+)/
];
for (const pattern of patterns) {
const match = scriptText.match(pattern);
if (match && match[1]) {
try {
const possibleJson = match[1].replace(/\\"/g, '"');
const parsed = JSON.parse(possibleJson);
fb_dtsg = parsed.token || parsed;
} catch {
fb_dtsg = match[1];
}
if (fb_dtsg) break;
}
}
}
});
if (!fb_dtsg) {
const dtsgInput = $('input[name="fb_dtsg"]').val();
if (dtsgInput) fb_dtsg = dtsgInput;
}
const seqMatches = html.match(/irisSeqID":"([^"]+)"/);
if (seqMatches && seqMatches[1]) {
irisSeqID = seqMatches[1];
}
try {
const jsonMatches = html.match(/\{"dtsg":({[^}]+})/);
if (jsonMatches && jsonMatches[1]) {
const dtsgData = JSON.parse(jsonMatches[1]);
if (dtsgData.token) fb_dtsg = dtsgData.token;
}
} catch { }
if (fb_dtsg) {
console.log("Found fb_dtsg!");
}
} catch (e) {
console.log("Error finding fb_dtsg:", e);
}
}
extractFromHTML();
var userID;
var cookies = jar.getCookies("https://www.facebook.com");
var userCookie = cookies.find(cookie => cookie.cookieString().startsWith("c_user="));
var tiktikCookie = cookies.find(cookie => cookie.cookieString().startsWith("i_user="));
if (!userCookie && !tiktikCookie) {
return log.error("Error! Your cookiestate is not valid!");
}
if (html.includes("/checkpoint/block/?next")) {
return log.error('error', "Appstate is dead rechange it!", 'error');
}
userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
//logger.log(`${cra(`[ CONNECT ]`)} Logged in as ${userID}`, "DATABASE");
try { clearInterval(checkVerified); } catch (_) { }
const clientID = (Math.random() * 2147483648 | 0).toString(16);
let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=pnb&sid=${userID}`;
let region = "PNB";
try {
const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
if (endpointMatch.input.includes("601051028565049")) {
console.log(`login error.`);
ditconmemay = true;
}
if (endpointMatch) {
mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
const url = new URL(mqttEndpoint);
region = url.searchParams.get('region')?.toUpperCase() || "PNB";
}
} catch (e) {
console.log('Using default MQTT endpoint');
}
log.info('Logging in...');
var ctx = {
userID: userID,
jar: jar,
clientID: clientID,
globalOptions: globalOptions,
loggedIn: true,
access_token: 'NONE',
clientMutationId: 0,
mqttClient: undefined,
lastSeqId: irisSeqID,
syncToken: undefined,
mqttEndpoint: mqttEndpoint,
region: region,
firstListen: true,
fb_dtsg: fb_dtsg,
req_ID: 0,
callback_Task: {},
wsReqNumber: 0,
wsTaskNumber: 0,
reqCallbacks: {}
};
var api = {
setOptions: setOptions.bind(null, globalOptions),
getAppState: () => utils.getAppState(jar),
postFormData: (url, body) => utils.makeDefaults(html, userID, ctx).postFormData(url, ctx.jar, body)
};
var defaultFuncs = utils.makeDefaults(html, userID, ctx);
api.postFormData = function (url, body) {
return defaultFuncs.postFormData(url, ctx.jar, body);
};
api.getFreshDtsg = async function () {
try {
const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
const $ = cheerio.load(res.body);
let newDtsg;
const patterns = [
/\["DTSGInitialData",\[\],{"token":"([^"]+)"}]/,
/\["DTSGInitData",\[\],{"token":"([^"]+)"/,
/"token":"([^"]+)"/,
/name="fb_dtsg" value="([^"]+)"/
];
$('script').each((i, script) => {
if (!newDtsg) {
const scriptText = $(script).html() || '';
for (const pattern of patterns) {
const match = scriptText.match(pattern);
if (match && match[1]) {
newDtsg = match[1];
break;
}
}
}
});
if (!newDtsg) {
newDtsg = $('input[name="fb_dtsg"]').val();
}
return newDtsg;
} catch (e) {
console.log("Error getting fresh dtsg:", e);
return null;
}
};
//if (noMqttData) api.htmlData = noMqttData;
require('fs').readdirSync(__dirname + '/src/').filter(v => v.endsWith('.js')).forEach(v => { api[v.replace('.js', '')] = require(`./src/${v}`)(utils.makeDefaults(html, userID, ctx), api, ctx); });
api.listen = api.listenMqtt;
return {
ctx,
defaultFuncs,
api
};
}
function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
return async function (res) {
try {
const html = res.body;
const $ = cheerio.load(html);
let arr = [];
$("#login_form input").each((i, v) => arr.push({ val: $(v).val(), name: $(v).attr("name") }));
arr = arr.filter(v => v.val && v.val.length);
let form = utils.arrToForm(arr);
form.lsd = utils.getFrom(html, "[\"LSD\",[],{\"token\":\"", "\"}");
form.lgndim = Buffer.from(JSON.stringify({ w: 1440, h: 900, aw: 1440, ah: 834, c: 24 })).toString('base64');
form.email = email;
form.pass = password;
form.default_persistent = '0';
form.lgnrnd = utils.getFrom(html, "name=\"lgnrnd\" value=\"", "\"");
form.locale = 'en_US';
form.timezone = '240';
form.lgnjs = Math.floor(Date.now() / 1000);
const willBeCookies = html.split("\"_js_");
willBeCookies.slice(1).forEach(val => {
const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
});
log.info("ULLASH FCA Logging in...");
const loginRes = await utils.post(
"https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
jar,
form,
loginOptions
);
await utils.saveCookies(jar)(loginRes);
const headers = loginRes.headers;
if (!headers.location) throw new Error("Wrong username/password.");
if (headers.location.includes('https://www.facebook.com/checkpoint/')) {
log.info("login", "You have login approvals turned on.");
const checkpointRes = await utils.get(headers.location, jar, null, loginOptions);
await utils.saveCookies(jar)(checkpointRes);
const checkpointHtml = checkpointRes.body;
const $ = cheerio.load(checkpointHtml);
let checkpointForm = [];
$("form input").each((i, v) => checkpointForm.push({ val: $(v).val(), name: $(v).attr("name") }));
checkpointForm = checkpointForm.filter(v => v.val && v.val.length);
const form = utils.arrToForm(checkpointForm);
if (checkpointHtml.includes("checkpoint/?next")) {
return new Promise((resolve, reject) => {
const submit2FA = async (code) => {
try {
form.approvals_code = code;
form['submit[Continue]'] = $("#checkpointSubmitButton").html();
const approvalRes = await utils.post(
"https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
jar,
form,
loginOptions
);
await utils.saveCookies(jar)(approvalRes);
const approvalError = $("#approvals_code").parent().attr("data-xui-error");
if (approvalError) throw new Error("Invalid 2FA code.");
form.name_action_selected = 'dont_save';
const finalRes = await utils.post(
"https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php",
jar,
form,
loginOptions
);
await utils.saveCookies(jar)(finalRes);
const appState = utils.getAppState(jar);
resolve(await loginHelper(appState, email, password, loginOptions, callback));
} catch (error) {
reject(error);
}
};
throw {
error: 'login-approval',
continue: submit2FA
};
});
}
if (!loginOptions.forceLogin) throw new Error("Couldn't login. Facebook might have blocked this account.");
form['submit[This was me]'] = checkpointHtml.includes("Suspicious Login Attempt") ? "This was me" : "This Is Okay";
await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
form.name_action_selected = 'save_device';
const reviewRes = await utils.post("https://www.facebook.com/checkpoint/?next=https%3A%2F%2Fwww.facebook.com%2Fhome.php", jar, form, loginOptions);
const appState = utils.getAppState(jar);
return await loginHelper(appState, email, password, loginOptions, callback);
}
await utils.get('https://www.facebook.com/', jar, null, loginOptions);
return await utils.saveCookies(jar);
} catch (error) {
callback(error);
}
};
}
function loginHelper(appState, email, password, globalOptions, callback, prCallback) {
let mainPromise = null;
const jar = utils.getJar();
if (appState) {
try {
appState = JSON.parse(appState);
} catch (e) {
try {
appState = appState;
} catch (e) {
return callback(new Error("Failed to parse appState"));
}
}
try {
appState.forEach(c => {
const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
jar.setCookie(str, "http://" + c.domain);
});
mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
.then(utils.saveCookies(jar));
} catch (e) {
process.exit(0);
}
} else {
mainPromise = utils
.get("https://www.facebook.com/", null, null, globalOptions, { noRef: true })
.then(utils.saveCookies(jar))
.then(makeLogin(jar, email, password, globalOptions, callback, prCallback))
.then(() => utils.get('https://www.facebook.com/', jar, null, globalOptions).then(utils.saveCookies(jar)));
}
function handleRedirect(res) {
const reg = /<meta http-equiv="refresh" content="0;url=([^"]+)[^>]+>/;
const redirect = reg.exec(res.body);
if (redirect && redirect[1]) {
return utils.get(redirect[1], jar, null, globalOptions).then(utils.saveCookies(jar));
}
return res;
}
let ctx, api;
mainPromise = mainPromise
.then(handleRedirect)
.then(res => {
const mobileAgentRegex = /MPageLoadClientMetrics/gs;
if (!mobileAgentRegex.test(res.body)) {
globalOptions.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36";
return utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
}
return res;
})
.then(handleRedirect)
.then(res => {
const html = res.body;
const Obj = buildAPI(globalOptions, html, jar);
ctx = Obj.ctx;
api = Obj.api;
return res;
});
if (globalOptions.pageID) {
mainPromise = mainPromise
.then(() => utils.get(`https://www.facebook.com/${globalOptions.pageID}/messages/?section=messages&subsection=inbox`, jar, null, globalOptions))
.then(resData => {
let url = utils.getFrom(resData.body, 'window.location.replace("https:\\/\\/www.facebook.com\\', '");').split('\\').join('');
url = url.substring(0, url.length - 1);
return utils.get('https://www.facebook.com' + url, jar, null, globalOptions);
});
}
mainPromise
.then(async () => {
log.info('ULLASH FCA Login successful');
callback(null, api);
})
.catch(e => {
callback(e);
});
}
function login(loginData, options, callback) {
if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
callback = options;
options = {};
}
var globalOptions = {
selfListen: false,
listenEvents: true,
listenTyping: false,
updatePresence: false,
forceLogin: false,
autoMarkDelivery: false,
autoMarkRead: false,
autoReconnect: true,
logRecordSize: 100,
online: false,
emitReady: false,
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
};
var prCallback = null;
if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
var rejectFunc = null;
var resolveFunc = null;
var returnPromise = new Promise(function (resolve, reject) {
resolveFunc = resolve;
rejectFunc = reject;
});
prCallback = function (error, api) {
if (error) return rejectFunc(error);
return resolveFunc(api);
};
callback = prCallback;
}
if (loginData.email && loginData.password) {
setOptions(globalOptions, {
logLevel: "silent",
forceLogin: true,
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
});
loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
} else if (loginData.appState) {
setOptions(globalOptions, options);
return loginHelper(loginData.appState, loginData.email, loginData.password, globalOptions, callback, prCallback);
}
return returnPromise;
}
module.exports = login;
;