ws3-fca
Version:
A node.js package for automating Facebook Messenger bot, and is one of the most advanced next-generation Facebook Chat API (FCA) by @NethWs3Dev & @ExocoreCommunity
169 lines (152 loc) • 7.19 kB
JavaScript
;
const utils = require('../../utils');
const axios = require("axios");
const path = require('path');
const fs = require('fs');
const qs = require("querystring");
/**
* The main login helper function, orchestrating the login process.
*
* @param {object} credentials User credentials or appState.
* @param {object} globalOptions Global options for the API.
* @param {function} callback The final callback function.
* @param {function} setOptionsFunc Reference to the setOptions function from models.
* @param {function} buildAPIFunc Reference to the buildAPI function from models.
* @param {object} initialApi The initial API object to extend.
* @param {function} fbLinkFunc A function to generate Facebook links.
* @param {string} errorRetrievingMsg The error message for retrieving user ID.
* @returns {Promise<void>}
*/
async function loginHelper(credentials, globalOptions, callback, setOptionsFunc, buildAPIFunc, initialApi, fbLinkFunc, errorRetrievingMsg) {
let ctx = null;
let defaultFuncs = null;
let api = initialApi;
try {
const jar = utils.getJar();
utils.log("Logging in...");
const appState = credentials.appState;
if (appState) {
let cookieStrings = [];
if (Array.isArray(appState)) {
cookieStrings = appState.map(c => [c.name || c.key, c.value].join('='));
} else if (typeof appState === 'string') {
cookieStrings = appState.split(';').map(s => s.trim()).filter(Boolean);
} else {
throw new Error("Invalid appState format. Please provide an array of cookie objects or a cookie string.");
}
cookieStrings.forEach(cookieString => {
const domain = ".facebook.com";
const expires = new Date().getTime() + 1000 * 60 * 60 * 24 * 365;
const str = `${cookieString}; expires=${expires}; domain=${domain}; path=/;`;
jar.setCookie(str, `https://${domain}`);
});
} else if (credentials.email && credentials.password) {
// Rui
const url = "https://api.facebook.com/method/auth.login";
const params = {
access_token: "350685531728|62f8ce9f74b12f84c123cc23437a4a32",
format: "json",
sdk_version: 2,
email: credentials.email,
locale: "en_US",
password: credentials.password,
generate_session_cookies: 1,
sig: "c1c640010993db92e5afd11634ced864",
}
const query = qs.stringify(params);
const xurl = `${url}?${query}`;
try {
const resp = await axios.get(xurl);
if (resp.status !== 200) {
throw new Error("Wrong password / email");
}
let cstrs = resp.data["session_cookies"].map(c => `${c.name}=${c.value}`);
cstrs.forEach(cstr => {
const domain = ".facebook.com";
const expires = new Date().getTime() + 1000 * 60 * 60 *24 * 365;
const str = `${cstr}; expires=${expires}; domain=${domain}; path=/;`;
jar.setCookie(str, `https://${domain}`);
});
} catch (e) {
throw new Error("Wrong password / email");
}
} else {
throw new Error("No cookie or credentials found. Please provide cookies or credentials.");
}
if (!api) {
api = {
setOptions: setOptionsFunc.bind(null, globalOptions),
getAppState() {
const appState = utils.getAppState(jar);
if (!Array.isArray(appState)) return [];
const uniqueAppState = appState.filter((item, index, self) => self.findIndex((t) => t.key === item.key) === index);
return uniqueAppState.length > 0 ? uniqueAppState : appState;
},
};
}
const resp = await utils.get(fbLinkFunc(), jar, null, globalOptions, { noRef: true }).then(utils.saveCookies(jar));
const extractNetData = (html) => {
const allScriptsData = [];
const scriptRegex = /<script type="application\/json"[^>]*>(.*?)<\/script>/g;
let match;
while ((match = scriptRegex.exec(html)) !== null) {
try {
allScriptsData.push(JSON.parse(match[1]));
} catch (e) {
utils.error(`Failed to parse a JSON blob from HTML`, e.message);
}
}
return allScriptsData;
};
const netData = extractNetData(resp.body);
const [newCtx, newDefaultFuncs] = await buildAPIFunc(resp.body, jar, netData, globalOptions, fbLinkFunc, errorRetrievingMsg);
ctx = newCtx;
defaultFuncs = newDefaultFuncs;
api.message = new Map();
api.timestamp = {};
/**
* Loads API modules from the deltas/apis directory.
*
* @returns {void}
*/
const loadApiModules = () => {
// CORRECTED PATH: From src/core/models/ to src/deltas/apis
const apiPath = path.join(__dirname, '..', '..', 'deltas', 'apis');
const apiFolders = fs.readdirSync(apiPath)
.filter(name => fs.lstatSync(path.join(apiPath, name)).isDirectory());
apiFolders.forEach(folder => {
const modulePath = path.join(apiPath, folder);
fs.readdirSync(modulePath)
.filter(file => file.endsWith('.js'))
.forEach(file => {
const moduleName = path.basename(file, '.js');
const fullPath = path.join(modulePath, file);
try {
api[moduleName] = require(fullPath)(defaultFuncs, api, ctx);
} catch (e) {
utils.error(`Failed to load module ${moduleName} from ${folder}:`, e);
}
});
});
const listenPath = path.join(__dirname, '..', '..', 'deltas', 'apis', 'mqtt', 'listenMqtt.js');
const realtimePath = path.join(__dirname, '..', '..', 'deltas', 'apis', 'mqtt', 'realtime.js');
if (fs.existsSync(realtimePath)) {
api['realtime'] = require(realtimePath)(defaultFuncs, api, ctx);
}
if (fs.existsSync(listenPath)) {
api['listenMqtt'] = require(listenPath)(defaultFuncs, api, ctx);
}
};
api.getCurrentUserID = () => ctx.userID;
api.getOptions = (key) => key ? globalOptions[key] : globalOptions;
loadApiModules();
api.ctx = ctx;
api.defaultFuncs = defaultFuncs;
api.globalOptions = globalOptions;
return callback(null, api);
} catch (error) {
utils.error("loginHelper", error.error || error);
return callback(error);
}
}
module.exports = loginHelper;