'use strict' const os = require('os') const fs = require('fs') const path = require('path') const homedir = require('homedir') const Store = require('./store') const phantomloader = require('./phantomloader') const webserver = require('./webserver') const Cookies = require('./cookies') const Headers = require('./headers') const Login = require('./login') const proxy = require('./proxy') // file & directory names const DATA_DIR = '.alexis' const PHANTOMFILE = os.platform() === 'win32' ? 'phantomjs.exe' : 'phantomjs' // computes the data directory const getDataDir = () => path.join(homedir(), DATA_DIR) // checks if the data directory exists const checkDataDirExists = () => fs.existsSync(getDataDir()) // creates the data directory const createDataDir = () => fs.mkdirSync(getDataDir()) // checks if the PhantomJS binary exists const checkPhantomExist = () => fs.existsSync(path.join(getDataDir(), PHANTOMFILE)) // checks if the given options are valid const validateOptions = (options, verbose) => { let missing = []; ['httpPort', 'email', 'pass'].forEach(option => { if (!options[option]) missing.push(option) }) if (verbose && missing.length > 0) return missing.join(', ') if (missing.length > 0) return false return true } // validate options const optionsAreValid = (options = {}, invalidOptionsCb) => { if (!options.interval) options.interval = 10 options.interval = options.interval * 1000 if(!validateOptions(options)) { invalidOptionsCb() return false } return true } // checks if data dir exists and creates it if not const doCreateUserdataDirectory = (data_dir, logger) => { if (!checkDataDirExists()) { logger.log('SETUP', `Creating data directory: ${data_dir}`) createDataDir() } } // checks if Phantom is installed, downloads it if not const mountPhantom = async (data_dir, logger) => { if (!checkPhantomExist()) { phantom_mounted = false const phantom_path_name = path.join(data_dir, PHANTOMFILE) logger.log('SETUP', 'Downloading Phantom') return await Phantom.download(phantom_path_name, data_dir) } return true } // Initializing flow const run = async (logger, options, invalidOptionsCb) => { // helper variable to determine if cookies exist let cookies_exist = false // helper variable to determine if cookies are valid let cookies_valid = false // helper variable to determine if login did work let login_success = false // init store let store = null // device data store let device_data = null // init PhantomJS downlaoder const Phantom = phantomloader(logger) // cache data dir location const data_dir = getDataDir() // validate options if (!optionsAreValid(options, invalidOptionsCb)) return false // set up redux store logger.log('SETUP', 'Creating in-memory store') store = Store() // check if data dir exists, else create doCreateUserdataDirectory(data_dir, logger) // checks if Phantom is installed, downloads it if not const phantom_mounted = await mountPhantom(data_dir, logger) // check if phantom is mounted if (phantom_mounted !== true) { logger.log('ERROR', 'Error with mounting Phantom') return false } // everythings okay logger.log('SETUP', 'Dependency check successful') // cookie check if (Cookies.checkJsonCookiesExist(data_dir) && Cookies.checkJsonCookiesExist(data_dir)) { logger.log('SETUP', 'Cookies exist, trying to reuse them') // validate existing cookies try { cookies_valid = await Cookies.validateCookie(data_dir) device_data = cookies_valid[1] cookies_valid = cookies_valid[0] } catch (e) { cookies_valid = false } // set flasg & log cookie state if (cookies_valid) { cookies_exist = true login_success = true logger.log('SETUP', 'Cookies valid, connection could be established') } else { cookies_exist = false logger.log('SETUP', 'Cookies invalid, connection could not be established') } } // create a new set of cookies by logging in if (!cookies_exist) { logger.log('SETUP', 'No cookies found or invalid, logging in to create a fresh pair') try { login_success = await Login.fetchInitialCookies( Cookies.getJsonCookiePath(data_dir), Cookies.getRawCookiePath(data_dir), Headers.ua, options.email, options.pass, path.join(data_dir, PHANTOMFILE), ) } catch (error) { console.log(error) if (error === 'captcha') { logger.log('ERROR', 'Login failed, we got a captcha notice. Please change your public IP and try again.') } else { logger.log('ERROR', 'Login failed, please check your credentials') } login_success = false return false } // Login should´ve been successful, lets validate logger.log('SETUP', 'Login successful') // cookie check if (Cookies.checkJsonCookiesExist(getDataDir()) && Cookies.checkJsonCookiesExist(getDataDir())) { // validate existing cookies try { cookies_valid = await Cookies.validateCookie(getDataDir()) device_data = cookies_valid[1] cookies_valid = cookies_valid[0] } catch (e) { cookies_valid = false } // set flasg & log cookie state if (cookies_valid) { logger.log('SETUP', 'Cookies valid, connection could be established') } else { logger.log('SETUP', 'Cookies invalid, connection could not be established') return false } } } // set up proxy dialer const Proxy = await proxy(logger, data_dir, store) // initial fetch of data logger.log('SETUP', 'Fetching initial data') const initial_data = await Proxy.fetchAll() logger.log('SETUP', 'Fetch finished') // start webserver logger.log('SETUP', 'Starting webserver') try { await webserver(logger, options.httpPort, store, Proxy) } catch (error) { logger.log('ERROR', `Problem starting webserver: ${error}`) return false } // set up scheduler to periodically fetch data logger.log('SETUP', `Setting up scheduler with interval: ${options.interval}ms`) // display data about the echo devices we found logger.log('ECHO', `Found ${device_data.devices.length} Device(s) connected with this account`) device_data.devices.forEach(device => { logger.log('ECHO', `---------------------------------`) logger.log('ECHO', `Name: ${device.accountName}`) logger.log('ECHO', `Id: ${device.deviceAccountId}`) logger.log('ECHO', `Type: ${device.deviceFamily}`) logger.log('ECHO', `Active: ${device.online}`) logger.log('ECHO', `Mac Address: ${device.macAddress}`) }) logger.log('ECHO', `---------------------------------`) // setup done, system is ready to use logger.log('SETUP', 'Setup finished. Ready to use. Have Fun!') return true } module.exports = (logger) => { return { getDataDir, checkDataDirExists, createDataDir, checkPhantomExist, run: run.bind(this, logger) } }