const inquirer = require("inquirer");
GUI_Handler.prototype.__proto__ = require('events').EventEmitter.prototype;
/**
* Creates a new GUI_Handler instance.
* @class
*/
function GUI_Handler(main) {
var self = this;
self.main = main;
self.logger = main.logger;
}
GUI_Handler.prototype.displayBotMenu = function () {
var self = this;
var tempList = [];
var accounts = self.main.getAccounts();
for (var accountIndex in accounts) {
if (accounts.hasOwnProperty(accountIndex)) {
tempList.push(accounts[accountIndex].getAccountName() + "[{0}]".format(accounts[accountIndex].getDisplayName()));
}
}
tempList.push(new inquirer.Separator());
tempList.push("register");
tempList.push("exit");
var botList = [
{
type: 'list',
name: 'username',
message: 'Choose the bot you would like to operate:',
choices: tempList
}
];
inquirer.prompt(botList, function (result) {
switch (result.username) {
case 'register':
var tempList = [];
tempList.push("new account");
tempList.push(new inquirer.Separator());
tempList.push("import account");
var optionList = [
{
type: 'list',
name: 'registerOption',
message: 'Choose how you would like to register?:',
choices: tempList
}
];
inquirer.prompt(optionList, function (result) {
var accountQuestions = [
{
type: 'input',
name: 'username',
message: 'What\'s the bot username?'
},
{
type: 'password',
name: 'password',
message: 'What\'s the bot password?'
}
];
switch (result.registerOption) {
case 'new account':
inquirer.prompt(accountQuestions, function (result) {
self.main.registerAccount(result.username, result.password, {}, function (err, botAccount) {
if (err)
self.logger.log("error", "The following details are incorrect: \nusername: {0}\npassword: {1}".format(result.username, result.password));
self.emit('updatedAccountDetails', botAccount);
self.displayBotMenu(accounts);
});
});
break;
case 'import account':
accountQuestions = [
{
type: 'input',
name: 'username',
message: 'What\'s the bot username? [Required]'
},
{
type: 'password',
name: 'password',
message: 'What\'s the bot password? [Required]'
},
{
type: 'input',
name: 'shared_secret',
message: 'What\'s the shared_secret?'
},
{
type: 'input',
name: 'identity_secret',
message: 'What\'s the identity_secret?'
},
{
type: 'input',
name: 'revocation',
message: 'What\'s the revocation code?'
}
];
inquirer.prompt(accountQuestions, function (result) {
// self.registerAccount()
if (result.username.length == 0 || result.password.length == 0){
self.displayBotMenu();
self.logger.log("error", "One or more fields that are required are empty.");
}
else {
self.main.registerAccount(result.username, result.password, {
shared_secret: result.shared_secret,
identity_secret: result.identity_secret,
revocation: result.revocation
}, function (err) {
self.displayBotMenu();
if (err)
self.logger.log("error", "The following details are incorrect: \nusername: {0}\npassword: {1}".format(result.username, result.password));
});
}
});
break;
}
});
break;
case 'exit':
process.exit();
break;
default:
self.main.botLookup(result.username.split("\[")[0], function (err, accountDetails) {
// Check if bot is online or offline
if (err) {
self.logger.log("error", err);
}
else {
self.displayMenu(accountDetails);
}
});
break;
}
});
};
GUI_Handler.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.Friends.sendMessage(target, result.message, function (err) {
self.processChat(botAccount, target);
});
}
});
};
GUI_Handler.prototype.tradeMenu = function (botAccount, tradeMenuOption) {
var self = this;
botAccount.Friends.getFriends(function (err, friendsList) {
if (err) {
self.logger.log("error", err.toString());
self.displayMenu(botAccount);
}
else {
friendsList.unshift({username: "Back"});// Add to first pos
var nameList = [];
for (var friendId in friendsList) {
if (friendsList.hasOwnProperty(friendId)) {
nameList.push(friendsList[friendId].username);
}
}
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...
var partner = friendsList[menuEntry];
switch (menuEntry) {
case 0:
// Go back
self.initTradeMenu(botAccount);
break;
default:
// Trade with user selected.
botAccount.Trade.createOffer(partner.accountSid, function (err, currentOffer) {
if (err) {
self.logger.log("error", "Failed to create offer due to ", err);
return self.displayMenu(botAccount);
}
switch (tradeMenuOption) {
case 0:
botAccount.getUserInventory(partner.accountSid, self.main.getAppID(), 2, true, function (err, inventory, currencies) {
if (err) {
self.logger.log("error", "User does not have game - " + err);
self.displayMenu(botAccount);
}
else {
if (inventory == null || inventory.length < 1) {
self.logger.log("info", "Other user has no items in inventory. Redirecting to menu...");
self.initTradeMenu(botAccount);
return;
}
var nameList = [];
for (var id in inventory) {
if (inventory.hasOwnProperty(id)) {
nameList.push(inventory[id].name);
}
}
var tradeMenu = [
{
type: 'checkbox',
name: 'tradeOption',
message: 'What would you like to take? (\'Enter\' to send trade)',
choices: nameList,
}
];
inquirer.prompt(tradeMenu, function (result) {
if (result.tradeOption.length > 0) {
for (var itemNameIndex in result.tradeOption) {
if (result.tradeOption.hasOwnProperty(itemNameIndex)) {
var itemName = result.tradeOption[itemNameIndex];
currentOffer.addTheirItem(inventory[nameList.indexOf(itemName)]);
nameList[nameList.indexOf(itemName)] = {
name: itemName,
displayed: true
};
}
}
currentOffer.send("Manual offer triggered by Bot Manager.", null, function (err, status) {
if (err) {
self.logger.log("error", err);
self.displayMenu(botAccount);
} else {
botAccount.Trade.confirmOutstandingTrades(function (err, confirmedTrades) {
if (err)
self.logger.log("error", err);
self.logger.log("info", "Sent trade offer to %s.", partner.username);
self.displayMenu(botAccount);
});
}
});
} else {
self.tradeMenu(botAccount, tradeMenuOption);
}
});
}
});
break;
case 1:
botAccount.getInventory(self.main.getAppID(), 2, true, function (err, inventory, currencies) {
if (inventory == null || inventory.length < 1) {
self.logger.log("info", "Bot has no items in inventory. Redirecting to menu...");
self.initTradeMenu(botAccount);
return;
}
var nameList = [];
for (var id in inventory) {
if (inventory.hasOwnProperty(id)) {
nameList.push(inventory[id].name);
}
}
var tradeMenu = [
{
type: 'checkbox',
name: 'tradeOption',
message: 'What would you like to offer? (\'Enter\' to send trade)',
choices: nameList
}
];
inquirer.prompt(tradeMenu, function (result) {
if (result.tradeOption.length > 0) {
for (var itemNameIndex in result.tradeOption) {
if (result.tradeOption.hasOwnProperty(itemNameIndex)) {
var itemName = result.tradeOption[itemNameIndex];
currentOffer.addMyItem(inventory[nameList.indexOf(itemName)]);
nameList[nameList.indexOf(itemName)] = {
name: itemName,
displayed: true
};
}
}
currentOffer.send("Manual offer triggered by Bot Manager.", null, function (err, status) {
if (err) {
self.logger.log("error", err);
self.displayMenu(botAccount);
} else {
botAccount.Trade.confirmOutstandingTrades(function (confirmedTrades) {
self.logger.log("info", "Sent trade offer to %s, and confirmed %s.", partner.username, confirmedTrades.length);
self.displayMenu(botAccount);
});
}
});
} else {
self.tradeMenu(botAccount, tradeMenuOption);
}
});
});
break;
default:
self.tradeMenu(botAccount, tradeMenuOption);
break;
}
});
break;
}
});
}
});
};
GUI_Handler.prototype.initTradeMenu = function (botAccount) {
var self = this;
var tradeOptions = [
"Request Items",
"Give Items",
"Back"
];
var tradeMenuOptions = [
{
type: 'list',
name: 'tradeOption',
message: 'What trade action would you like?',
choices: tradeOptions
}
];
inquirer.prompt(tradeMenuOptions, function (result) {
var tradeMenuEntry = tradeOptions.indexOf(result.tradeOption);
switch (tradeMenuEntry) {
case 0:
self.tradeMenu(botAccount, 0);
break;
case 1:
self.tradeMenu(botAccount, 1);
break;
default:
self.displayMenu(botAccount);
break;
}
});
};
GUI_Handler.prototype.displayMenu = function (botAccount) {
var self = this;
var menuOptions = [
"Chat",
"Send trade offer",
//"Calculate Inventory", This option was temporary, but may maybe added later.
new inquirer.Separator(),
botAccount.loggedIn ? "Logout" : "Login",
new inquirer.Separator(),
"Manage",
"Delete",
"Back"
];
// Disabled chat and trade systems..
if (self.main.config.api_key == undefined){
menuOptions[0] = "Chat [Disabled - missing 'api_key' in config]";
menuOptions[1] = "Send trade offer [Disabled - missing 'api_key' in config]";
}
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:
if (self.main.config.api_key == undefined){
self.displayMenu(botAccount);
} else {
botAccount.Friends.getFriends(function (err, friendsList) {
if (err) {
self.logger.log("error", err.toString());
self.displayMenu(botAccount);
}
else {
friendsList.unshift({username: "Back"});
var nameList = [];
for (var friendId in friendsList) {
if (friendsList.hasOwnProperty(friendId)) {
nameList.push(friendsList[friendId].username);
}
}
var chatMenu = [
{
type: 'list',
name: 'chatOption',
message: 'Who would you like to chat with?',
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({
username: friendsList[menuEntry].username,
sid: friendsList[menuEntry].accountSid
});
self.processChat(botAccount, friendsList[menuEntry].accountSid);
break;
}
});
}
});
}
break;
case 1:
if (self.main.config.api_key == undefined){
self.displayMenu(botAccount);
} else {
self.initTradeMenu(botAccount);
}
break;
case 3:
// Handle logout/login logic and return to menu.
if (!botAccount.loggedIn) {
self.logger.log("info", "Trying to authenticate into {0}".format(botAccount.getAccountName()));
botAccount.Auth.loginAccount(null, function (err) {
if (err) {
if (err.Error == "SteamGuardMobile") {
var authenticator = [
{
type: 'input',
name: 'code',
message: "Enter the authenticator code for " + botAccount.username
}
];
inquirer.prompt(authenticator, function (result) {
botAccount.Auth.loginAccount({twoFactorCode: result.code}, function (err) {
if (err)
self.logger.log("error", "Failed to login due to %j", err);
self.displayBotMenu();
});
});
} else {
self.logger.log("error", "Failed to login due to %j", err);
}
} else {
self.displayBotMenu();
}
});
} else {
botAccount.logoutAccount();
self.displayBotMenu();
}
break;
case 5:
var authOptions = [];
authOptions.push("Edit Display name");
authOptions.push(new inquirer.Separator());
authOptions.push((botAccount.Auth.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(self.main.config.bot_prefix)
}
];
inquirer.prompt(questions, function (result) {
botAccount.changeName(result.newName, self.main.config.bot_prefix, function (err) {
if (err) {
self.logger.log("error", "Failed to change name. Error: {0}".format(err));
}
else {
self.logger.log("info", "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:
self.displayMenu(botAccount);
if (botAccount.Auth.has_shared_secret()) {
// Send the auth key.
self.logger.log("info", "Your authentication code for {0} is {1}".format(botAccount.getAccountName(), botAccount.Auth.generateMobileAuthenticationCode()));
} else {
// Auth not enabled?
self.logger.log("error", "2-factor-authentication is not enabled. Check your email.");
}
break;
default:
self.displayMenu(botAccount);
break;
}
});
break;
case 6:
var questions = [
{
type: 'confirm',
name: 'askDelete',
message: 'Are you sure you want to delete \'' + botAccount.username + '\' account?'
}
];
inquirer.prompt(questions, function (answers) {
if (answers.askDelete) {
self.main.unregisterAccount(botAccount, function (err) {
if (err) {
// Failed...
self.logger.log("error", "Failed to unregister the account - " + 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.
*/
GUI_Handler.prototype.enableTwoFactor = function (botAccount) {
var self = this;
botAccount.hasPhone(function (err, hasPhone, lastDigits) {
if (hasPhone) {
botAccount.Auth.enableTwoFactor(function (err, response) {
if (status == 84) {
// Rate limit exceeded. So delay the next request
self.logger.log("info", "Please wait 5 seconds to continue... Possibly blocked by Steam for sending out too many SMS's. Retry in 24 hours, please.");
setTimeout(function () {
self.enableTwoFactor(botAccount);
}, 5000);
}
else if (status == 1) {
self.logger.log("info", "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.Auth.finalizeTwoFactor(steamCode, function (err, accountDetails) {
if (err) {
self.logger.log("error", "Failed to enable 2 factor auth - " + err);
}
else {
self.main.AccountsManager.saveAccount(accountDetails, function (err) {
if (err) {
self.logger.log("error", "Failed to save accounts during 2 factor auth enable - " + err);
}
self.displayBotMenu();
});
}
});
}
});
}
else {
self.logger.log("error", "Error encountered while trying to enable two-factor-authentication, error code: " + response);
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.logger.log("error", "Error while adding phone number: " + 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.logger.log("error", "Error while verifying phone number: " + err);
self.displayMenu(botAccount);
}
else {
// Verified phone number...
self.enableTwoFactor(botAccount);
}
});
});
}
});
});
}
else {
// Take back to main menu.
self.logger.log("error", "Declined addition of phone number.");
self.displayMenu(botAccount);
}
});
}
});
};
/**
* 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;
};
module.exports = GUI_Handler;