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;