Source: index.js

const inquirer = require("inquirer");
                const colors = require('colors');// Used to prettify messages send by the console

                var BotAccount = require('./classes/BotAccount.js');
                var DataControl = require('./components/DataControl.js');
                var APIControl = require('./components/API_Control.js');


                // Import events module
                BotManager.prototype.dataControl = new DataControl("config");
                BotManager.prototype.APIControl = null;
                BotManager.prototype.__proto__ = require('events').EventEmitter.prototype;


                BotManager.prototype.BotAccounts = [];

                /**
                * Creates a new BotManager instance.
                * @class
                */
                function BotManager() {


                }

                BotManager.prototype.startManager = function () {
                var self = this;
                self.dataControl.on('error', function (err) {
                self.errorDebug(err);
                });

                self.dataControl.on('loadedConfig', function (configResponse) {
                config = configResponse;
                if (self.dataControl.getConfig().hasOwnProperty("api_port")) {
                self.APIControl = new APIControl(self.dataControl.getConfig());
                self.APIControl.on('apiLoaded', function () {
                self.emit('loadedAPI');
                });
                self.APIControl.startAPI();
                }
                });


                self.dataControl.on('debug', function (msg) {
                //self.infoDebug(msg);
                });


                self.dataControl.on('loadedAccount', function (accountInfo) {
                var botAccount = new BotAccount(accountInfo);
                if (botAccount.getAccount().shared_secret) {
                botAccount.loginAccount();
                }
                botAccount.on('displayBotMenu', function () {
                self.displayBotMenu();
                });
                botAccount.on('sentOfferChanged', function (offer, oldState) {
                self.emit('sentOfferChanged', offer, oldState);
                });
                botAccount.on('newOffer', function (offer) {
                self.emit('newOffer', botAccount, offer);
                });
                botAccount.on('newOffer', function (offer) {
                self.emit('newOffer', botAccount, offer);
                });


                botAccount.on('loggedIn', function () {
                // User just logged in
                if (botAccount.getDisplayName() != null) {
                botAccount.changeName(botAccount.getDisplayName(), config.bot_prefix, function (err) {
                if (err) {
                self.errorDebug("Failed to change name. Error: " + err);
                }
                })
                }
                self.emit('loggedIn', botAccount);
                });


                botAccount.on('updatedAccountDetails', function () {
                self.saveAccounts(function (err) {
                if (err)
                self.errorDebug(err);
                });
                self.emit('updatedAccountDetails', botAccount);
                });

                botAccount.on('incorrectCredentials', function (accountDetails) {
                // We must ask user for new details...
                self.emit('incorrectCredentials', accountDetails);
                self.errorDebug("The following details are incorrect: \nusername: {0}\npassword:
                {1}".format(accountDetails.accountName, accountDetails.password));
                });


                self.emit('loadedAccount', accountInfo);
                self.BotAccounts.push(botAccount);
                });


                self.dataControl.initData(function (err, botAccountsList) {
                if (err)
                self.errorDebug(err);
                else {
                // Finished loading...
                self.displayBotMenu();
                }
                });
                };

                BotManager.prototype.apiEndpoint = function (method, url, callback) {
                var self = this;
                self.APIControl.apiEndpoint(method, url, callback);
                };
                BotManager.prototype.restartAPI = function () {
                var self = this;
                self.APIControl.restartAPI();
                };


                BotManager.prototype.displayBotMenu = function () {
                var self = this;
                var tempList = [];
                var botAccounts = self.getAccounts();
                for (var accountIndex in botAccounts) {
                if (botAccounts.hasOwnProperty(accountIndex)) {
                tempList.push(botAccounts[accountIndex].getAccountName());
                }
                }
                tempList.push(new inquirer.Separator());
                tempList.push("register");
                tempList.push("exit");

                var botList = [
                {
                type: 'list',
                name: 'accountName',
                message: 'Choose the bot you would like to operate:',
                choices: tempList
                }
                ];
                inquirer.prompt(botList, function (result) {
                switch (result.accountName) {

                case 'register':

                var questions = [
                {
                type: 'input',
                name: 'accountName',
                message: 'What\'s the bot username?'
                },
                {
                type: 'password',
                name: 'password',
                message: 'What\'s the bot password?'
                }
                ];

                inquirer.prompt(questions, function (result) {
                self.dataControl.registerAccount({accountName: result.accountName, password: result.password});
                self.displayBotMenu();
                });

                break;
                case 'exit':
                process.exit();
                break;
                default:
                self.botLookup(result.accountName, function (err, accountDetails) {
                // Check if bot is online or offline
                if (err) {
                self.errorDebug(err);
                }
                else {
                self.displayMenu(accountDetails);
                }
                });
                break;
                }


                });
                };


                BotManager.prototype.botLookup = function (keyData, callback) {
                var self = this;
                try {
                if (self.getAccounts()[parseInt(keyData)]) {
                callback(null, self.getAccounts()[parseInt(keyData)]);
                }
                else {
                var botAccounts = self.getAccounts();
                for (var botAccountIndex in botAccounts) {
                if (botAccounts.hasOwnProperty(botAccountIndex)) {
                if (botAccounts[botAccountIndex].getAccountName().toLowerCase() == keyData.toLowerCase()) {
                callback(null, botAccounts[botAccountIndex]);
                }
                }
                }
                }
                } catch (e) {
                callback(e, null);
                }
                };

                BotManager.prototype.processChat = function (botAccount, target) {
                var self = this;
                var chatMessage = [
                {
                message: 'Enter your message (\'quit\' to leave): ',
                type: 'input',
                name: 'message'
                }
                ];
                inquirer.prompt(chatMessage, function (result) {
                if (result.message.toLowerCase() == "quit" || result.message.toLowerCase() == "exit") {
                botAccount.setChatting(null);
                self.displayMenu(botAccount);
                }
                else {
                botAccount.sendMessage(target, result.message);
                self.processChat(botAccount, target);
                }
                });
                };

                BotManager.prototype.displayMenu = function (botAccount) {
                var self = this;
                botAccount.community.loggedIn(function (err, loggedIn, familyView) {
                var menuOptions = [
                "Chat",
                "Send trade",
                //"Calculate Inventory", This option was temporary, but may maybe added later.
                new inquirer.Separator(),
                loggedIn ? "Logout" : "Login",
                new inquirer.Separator(),
                "Manage",
                "Delete",
                "Back"
                ];
                var mainMenu = [
                {
                type: 'list',
                name: 'menuOption',
                message: 'What would you like to do:',
                choices: menuOptions
                }
                ];
                inquirer.prompt(mainMenu, function (result) {
                var menuEntry = menuOptions.indexOf(result.menuOption);
                switch (menuEntry) {
                case 0:
                botAccount.getFriends(function (err, friendsList) {
                friendsList.unshift({accountName: "Back"});
                var nameList = [];
                for (var friendId in friendsList) {
                if (friendsList.hasOwnProperty(friendId)) {
                nameList.push(friendsList[friendId].accountName);
                }
                }

                var chatMenu = [
                {
                type: 'list',
                name: 'chatOption',
                message: 'Who would you like to chat with? (only active chats visible)',
                choices: nameList
                }
                ];
                inquirer.prompt(chatMenu, function (result) {
                var menuEntry = nameList.indexOf(result.chatOption);
                // We will open chat with...
                switch (menuEntry) {
                case 0:
                self.displayMenu(botAccount);
                break;
                default:
                // User wants to actually chat with someone...
                botAccount.setChatting({
                accountName: friendsList[menuEntry].accountName,
                sid: friendsList[menuEntry].accountSid
                });
                self.processChat(botAccount, friendsList[menuEntry].accountSid);
                break;
                }
                });
                });


                break;
                case 1:
                botAccount.getFriends(function (err, friendsList) {
                friendsList.unshift({accountName: "Other SID/Name"});// Add to second pos
                friendsList.unshift({accountName: "Back"});// Add to first pos
                var nameList = [];
                for (var friendId in friendsList) {
                if (friendsList.hasOwnProperty(friendId)) {
                nameList.push(friendsList[friendId].accountName);
                }
                }

                var tradeMenu = [
                {
                type: 'list',
                name: 'tradeOption',
                message: 'Who would you like to trade with?',
                choices: nameList
                }
                ];
                inquirer.prompt(tradeMenu, function (result) {
                var menuEntry = nameList.indexOf(result.tradeOption);
                // We will open chat with...
                switch (menuEntry) {
                case 0:
                // Go back
                self.displayMenu(botAccount);
                break;
                case 1:
                // Trade with custom steam id.
                // TODO: add trade to steam id
                self.displayMenu(botAccount);
                break;
                default:
                // Trade with user selected.
                var currentOffer = botAccount.createOffer(friendsList[menuEntry].accountSid);
                botAccount.getInventory(730, 2, true, function (err, inventory, currencies) {
                var nameList = [];
                for (var id in inventory) {
                if (inventory.hasOwnProperty(id)) {
                nameList.push(inventory[id].name);
                if (id > 30)
                break;
                }
                }


                var tradeMenu = [
                {
                type: 'list',
                name: 'tradeOption',
                message: 'What would you like to offer?',
                choices: nameList
                }
                ];
                inquirer.prompt(tradeMenu, function (result) {
                var menuEntry = nameList.indexOf(result.tradeOption);
                currentOffer.addMyItem(inventory[menuEntry]);
                currentOffer.send("Manual offer triggered by Bot Manager.", null, function (err, status) {
                console.log(err);// if 50, then too many trades to this user currently...
                var time = botAccount.getUnixTime();
                botAccount.getConfirmations(time, botAccount.generateMobileConfirmationCode(time, "conf"), function
                (err, confirmations) {
                if (err) {
                console.log(err);
                }
                else {
                for (var confirmId in confirmations) {
                if (confirmations.hasOwnProperty(confirmId)) {
                confirmations[confirmId].respond(time, botAccount.generateMobileConfirmationCode(time, "allow"), true,
                function (err) {
                if (err) {
                self.errorDebug("Trade failed to confirm");
                }
                });
                }
                }
                }
                });
                });
                });
                });
                break;
                }
                });
                });
                break;
                case 3:
                // Handle logout/login logic and return to menu.
                if (!loggedIn) {
                self.successDebug("Trying to authenticate into {0}".format(botAccount.getAccountName()));
                botAccount.setTempSetting('displayBotMenu', true);
                botAccount.loginAccount(null);
                } else {
                botAccount.logoutAccount();
                self.displayBotMenu();
                }
                break;
                case 5:

                var authOptions = [];
                authOptions.push("Edit Display name");
                authOptions.push(new inquirer.Separator());
                authOptions.push((botAccount.has_shared_secret() ? "[ON]" : "[OFF]") + " Two Factor Authentication");
                authOptions.push("Generate 2-factor-authentication code");
                authOptions.push("Back");

                var authMenu = [
                {
                type: 'list',
                name: 'authOption',
                message: 'Choose the authentication option you would like to activate.',
                choices: authOptions
                }
                ];
                inquirer.prompt(authMenu, function (result) {
                var optionIndex = authOptions.indexOf(result.authOption);
                switch (optionIndex) {
                case 0:
                var questions = [
                {
                type: 'input',
                name: 'newName',
                message: "Enter the new name of the bot: "
                },
                {
                type: 'confirm',
                name: 'prefix',
                default: true,
                message: "Give default prefix of '{0}'?".format(config.bot_prefix)
                }
                ];

                inquirer.prompt(questions, function (result) {
                botAccount.changeName(result.newName, config.bot_prefix, function (err) {
                if (err) {
                self.errorDebug("Failed to change name. Error: ", err);
                }
                else {
                self.infoDebug("Successfully changed display name");
                }
                self.displayMenu(botAccount);
                })
                });


                break;
                case 2:
                if (!botAccount.shared_secret) {
                // Enable 2FA
                self.enableTwoFactor(botAccount);
                } else {
                // TODO: Move to BotAccount class
                //disable2FA(botAccount);

                }
                break;
                case 3:
                if (botAccount.has_shared_secret()) {
                // Send the auth key.
                self.successDebug("Your authentication code for {0} is {1}".format(botAccount.getAccountName(),
                botAccount.generateMobileAuthenticationCode()));
                } else {
                // Authn not enabled?
                self.errorDebug("2-factor-authentication is not enabled. Check your email.");
                }
                self.displayMenu(botAccount);
                break;
                default:
                self.displayMenu(botAccount);
                break;
                }
                });


                break;
                case 6:
                var questions = [
                {
                type: 'confirm',
                name: 'askDelete',
                message: 'Are you sure you want to delete \'' + botAccount.accountName + '\' account?'
                }
                ];
                inquirer.prompt(questions, function (answers) {
                if (answers.askDelete) {
                self.unregisterAccount(botAccount, function (err) {
                if (err) {
                // Failed...
                self.errorDebug(err);
                }
                else {
                self.displayBotMenu();
                }
                });
                }
                else {
                self.displayMenu(botAccount);
                }
                });

                break;
                case 7:
                self.displayBotMenu();
                break;
                }

                });
                });
                };
                /**
                * Start the two-factor-authentication process using the GUI
                * @param {BotAccount} botAccount - The bot chosen to enable two-factor authentication for.
                */
                BotManager.prototype.enableTwoFactor = function (botAccount) {
                var self = this;
                botAccount.hasPhone(function (err, hasPhone, lastDigits) {
                if (hasPhone) {
                botAccount.enableTwoFactor(function (response) {
                if (response.result == 84) {
                // Rate limit exceeded. So delay the next request
                self.successDebug("Please wait 2 seconds to continue...");
                setTimeout(function () {
                self.enableTwoFactor(botAccount);
                }, 2000);
                }
                else if (response.result == 1) {
                self.successDebug("Make sure to save the following code saved somewhere secure:
                {0}".format(response.revocation_code));
                var questions = [
                {
                type: 'input',
                name: 'code',
                message: "Enter the code texted to the phone number associated (-{0}) to the account:
                ".format(lastDigits)
                }
                ];

                inquirer.prompt(questions, function (result) {
                if (result.code) {
                var steamCode = result.code;
                botAccount.finalizeTwoFactor(response.shared_secret, steamCode, function (err, keyInformation) {
                if (err) {
                self.errorDebug(err);
                }
                else {
                self.saveAccounts(function (err) {
                if (err) {
                self.errorDebug(err);
                }
                self.displayBotMenu();
                });
                }
                });
                }
                });
                }
                else {
                self.errorDebug("Error encountered while trying to enable two-factor-authentication, error code: " +
                response.result);
                self.displayBotMenu();
                }
                });
                }
                else {
                var questions = [
                {
                type: 'confirm',
                name: 'confirmAddition',
                message: "A phone number is required to activate 2-factor-authentication. Would you like to set your
                phone number?",
                default: false
                }
                ];

                inquirer.prompt(questions, function (result) {
                if (result.confirmAddition) {

                var questions = [
                {
                type: 'input',
                name: 'phoneNumber',
                message: "Enter the number you would like to link to the account (ex. +18885550123)",
                validate: function (value) {
                var pass =
                value.match(/\+(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{1,14}$/i);
                if (pass) {
                return true;
                }
                return 'Please enter a valid phone number (ex. +18885550123)';
                }
                }
                ];

                inquirer.prompt(questions, function (result) {
                botAccount.addPhoneNumber(result.phoneNumber, function (err) {
                if (err) {
                self.errorDebug(err);
                self.displayMenu(botAccount);
                }
                else {
                var questions = [
                {
                type: 'input',
                name: 'code',
                message: "Enter the code sent to your phone number at " + result.phoneNumber
                }
                ];

                inquirer.prompt(questions, function (result) {
                botAccount.verifyPhoneNumber(result.code, function (err) {
                if (err) {
                self.errorDebug(err);
                self.displayMenu(botAccount);
                }
                else {
                // Verified phone number...
                self.enableTwoFactor(botAccount);
                }
                });
                });
                }
                });
                });
                }
                else {
                // Take back to main menu.
                self.errorDebug("Declined addition of phone number.");
                self.displayMenu(botAccount);
                }
                });
                }
                });
                };

                /**
                *
                * @param {BotAccount} botAccount - The bot chosen as part of the random choice
                * @param {errorCallback} errorCallback - A callback returned with possible errors
                */
                BotManager.prototype.unregisterAccount = function (botAccount, errorCallback) {
                var self = this;
                self.BotAccounts.splice(self.BotAccounts.indexOf(botAccount), self.BotAccounts.indexOf(botAccount) + 1);
                // Temporarily disable the save part as we want to make sure we unregister the AUTH before deleting.
                //self.saveAccounts(function(err){
                // self.errorDebug(err);
                //});
                errorCallback(null);
                };

                /**
                * Retrieve accounts registered within the instance
                * @returns {Array} - Array of BotAccount objects
                */
                BotManager.prototype.getAccounts = function () {
                var self = this;
                return self.BotAccounts;
                };

                /**
                * @callback errorCallback
                * @param {Error} error - An error message if the process failed, null if successful
                */

                /**
                * Save bot accounts into json file
                * @param {errorCallback} errorCallback - A callback returned with possible errors
                */
                BotManager.prototype.saveAccounts = function (errorCallback) {
                var self = this;
                self.dataControl.saveAccounts(self.getAccounts(), function (err) {
                if (err) {
                errorCallback(err);
                }
                errorCallback(null);
                });
                };

                /**
                * Post/log an informational message.
                * @param {string} message - Informational message to log
                */
                BotManager.prototype.infoDebug = function (message) {
                console.log((message + " ").grey);
                };

                /**
                * Post/log an error-type message
                * @param {string} message - Error message to log
                */
                BotManager.prototype.errorDebug = function (message) {
                console.log((message + " ").red);
                };

                /**
                * Post/log a success-type message
                * @param {String} message - Success message to log
                */
                BotManager.prototype.successDebug = function (message) {
                console.log((message + " ").green);
                };


                /**
                * Callback response
                * @callback botAccountCallback
                * @param {Error} error - An error message if the process failed, null if successful
                * @param {BotAccount} botAccount - The bot chosen as part of the random choice
                */

                /**
                * Choose a random bot
                * @param {botAccountCallback} botAccountCallback - A callback to run
                */
                BotManager.prototype.chooseRandomBot = function (botAccountCallback) {
                var self = this;
                var randomBotIndex = Math.round(Math.random() * self.getAccounts().length);
                if (randomBotIndex >= self.getAccounts().length) randomBotIndex--;
                randomBotIndex = 0;
                botAccountCallback(null, self.getAccounts()[randomBotIndex]);
                };


                /**
                * Format the string based on arguments provided after the string
                * @returns {String}
                */
                String.prototype.format = function () {
                var content = this;
                for (var i = 0; i < arguments.length; i++) {
                var replacement = '{' + i + '}';
                content = content.replace(replacement, arguments[i]);
                }
                return content;
                };

                //new BotManager();

                module.exports = BotManager;