module.exports = BotManager;
// Loading Loggers
const Winston = require('winston');
const GA_Tracking = require('universal-analytics');
const uuidV4 = require('uuid/v4');
// Loading internal classes
const GUI = require('../gui/GUI_Handler');
const BotAccount = require('../classes/Bot.js');
const Webserver = require('./webserver.js');
const ConfigManager = require('./ConfigManager.js');
const AccountsManager = require('./AccountsManager.js');
const FileManager = require('./FileManager.js');
const packageJson = require('../package.json');
// Import events module
BotManager.prototype.__proto__ = require('events').EventEmitter.prototype;
/**
* Creates a new BotManager instance.
* @class
*/
function BotManager() {
var self = this;
self.ActiveBotAccounts = [];
self.logger = new (Winston.Logger)({
transports: [
new (Winston.transports.Console)({
level: 'info',
timestamp: true,
colorize: true
}),
new (Winston.transports.File)({
level: 'debug',
timestamp: true,
filename: 'debug.log',
json: false
})
]
});
self.GUI = new GUI(self);
self.fileManager = new FileManager("config", self.logger);
self.ConfigManager = new ConfigManager(self.fileManager, self.logger);
self.AccountsManager = new AccountsManager(self.fileManager, self.logger);
}
BotManager.prototype.startWebserver = function(trySSL){
var self = this;
if (self.config.hasOwnProperty("api_port") && self.config.api_port != null) {
var options = null;
if (self.config.hasOwnProperty("ssl") && trySSL == true) {
if (self.config.ssl.hasOwnProperty("key")) {
self.fileManager.getFileUnparsed(self.config.ssl.key + ".key", null, function (err, keyFile) {
if (err) {
self.errorDebug("Failed to access key file... Disabling SSL");
self.startWebserver(false);
return;
}
self.fileManager.getFileUnparsed(self.config.ssl.cert + ".cert", null, function (err, certFile) {
if (err) {
self.errorDebug("Failed to access cert file... Disabling SSL");
self.startWebserver(false);
return;
}
options = {
ssl: {
key: keyFile,
cert: certFile
}
};
self.webserver = new Webserver(self.logger, self.config.api_port, options);
self.webserver.start(function (err) {
if (err)
self.errorDebug("Failed to start the API webserver - ensure port is not occupied... " + err);
else
self.emit('loadedAPI');
});
});
});
}
} else {
self.webserver = new Webserver(self.logger, self.config.api_port, options);
self.webserver.start(function (err) {
if (err)
self.errorDebug("Failed to start the API webserver - ensure port is not occupied..." + err);
else
self.emit('loadedAPI');
});
}
}
};
BotManager.prototype.startManager = function (callback) {
var self = this;
self.ConfigManager.loadConfig(function (err, config) {
if (err) {
return callback(err);
}
else {
self.config = config;
self.startWebserver(true);
/**
* This section allows me (Undeadkillz) to track usage of the tool (fix issues asap, know how many people use the tool) - don't worry, the data is aggregated and sent to me anonymously.
*/
if (config.hasOwnProperty("statistics") && config.statistics != null) {
if (config.statistics) {
var uuid = uuidV4();
self.fileManager.getFile("statistics.json", {uuid: uuid}, function (err, statistics) {
if (statistics.hasOwnProperty("uuid")) {
self.uuid = statistics.uuid;
} else {
self.fileManager.saveFile("statistics.json", {uuid: uuid}, function (err, statistics) {
self.uuid = statistics.uuid;
})
}
self.track = GA_Tracking('UA-63794417-8', self.uuid);// Init the tracking code
self.track.event(packageJson.version, "Status", "Online").send();// Trigger the tool's startup.
setInterval(function () {
self.track.event(packageJson.version, "Status", "Online").send();// Trigger the tool's startup.
}, 5 * 1000 * 60);
self.track.event(packageJson.version, "Collection", "on").send();// Trigger the tool's startup.
});
}
}
self.AccountsManager.getAccounts(function (accounts) {
for (var botIndex in accounts) {
if (accounts.hasOwnProperty(botIndex)) {
var options = accounts[botIndex];
if (options != null) {
}
self.registerAccount(accounts[botIndex].hasOwnProperty("accountName") ? accounts[botIndex].accountName : accounts[botIndex].username, accounts[botIndex].password, options, function (err, botAccount) {
if (err)
self.errorDebug("Error while loading bot info - " + err);
if (config.hasOwnProperty("autologin") && config.autologin == true) {
botAccount.Auth.loginAccount();
}
});
}
}
self.GUI.displayBotMenu();
if (callback)
return callback(null);
});
}
});
};
/**
* Add an API Endpoint (via webserver) at chosen location.
* @param method
* @param url
* @param callback
*/
BotManager.prototype.addEndpoint = function (method, url, callback) {
var self = this;
if (self.track)
self.track.event(packageJson.version, "Endpoints", "Added").send()// Trigger the tool's startup.
self.webserver.addEndpoint(method, url, callback);
};
/**
* Remove an API Endpoint (via webserver) at chosen location.
* @param method
* @param url
* @param callback
*/
BotManager.prototype.removeEndpoint = function (method, url) {
var self = this;
if (self.track)
self.track.event(packageJson.version, "Endpoints", "Removed").send()// Trigger the tool's startup.
self.webserver.removeEndpoint(method, url);
};
/**
* Add an API Endpoint (via webserver) at chosen location.
* @param method
* @param url
* @param callback
* @deprecated
*/
BotManager.prototype.apiEndpoint = function (method, url, callback) {
var self = this;
self.addEndpoint(method, url, callback);
};
BotManager.prototype.restartAPI = function () {
var self = this;
if (self.track)
self.track.event(packageJson.version, "Endpoints", "Restarted API").send()// Trigger the tool's startup.
self.webserver.restart();
};
BotManager.prototype.getAppID = function () {
var self = this;
return self.ConfigManager.getConfig().appid;
};
/**
*
* @param {BotAccount} botAccount - The bot chosen as part of the random choice
* @param {callback} unregisterCallback - A callback returned with possible errors
*/
BotManager.prototype.deleteAccount = function (botAccount, callbackErrorOnly) {
var self = this;
if (self.track)
self.track.event(packageJson.version, "Accounts", "Unregistered").send();// Trigger the tool's startup.
var accountName = botAccount.getAccountName();
self.ActiveBotAccounts.splice(self.ActiveBotAccounts.indexOf(botAccount), self.getAccounts().indexOf(botAccount) + 1);
self.fileManager.createFolderIfNotExist("deleted", function(err) {
if (err) {
self.logger.log("error", "Failed to create folder named 'deleted' under config. Error encountered: " + err);
callbackErrorOnly(err);
}
else {
self.fileManager.renameFile("data/{0}.dat".format(accountName), "deleted/{0}.dat".format(accountName), function(err){
callbackErrorOnly(err);
})
}
});
};
/**
* Lookup a botAccount using the username of the bot or the index of the bot in the list.
* @param keyData
* @param callback
* @deprecated
*/
BotManager.prototype.botLookup = function (keyData, callback) {
var self = this;
self.findBot(keyData, callback);
};
/**
* Lookup a botAccount using the username of the bot or the index of the bot or even the SteamID2, SteamID3, and SteamID64
* @param keyData
* @param callback
* @returns {*}
*/
BotManager.prototype.findBot = function (keyData, callback) {
var self = this;
var botAccounts = self.getAccounts();
for (var botAccountIndex in botAccounts) {
if (botAccounts.hasOwnProperty(botAccountIndex)) {
if (botAccounts[botAccountIndex].loggedIn) {
if (botAccounts[botAccountIndex].hasOwnProperty("SteamID"))
if (botAccounts[botAccountIndex].SteamID.getSteam3RenderedID().indexOf(keyData) != -1 || botAccounts[botAccountIndex].SteamID.getSteamID64().indexOf(keyData) != -1 || botAccounts[botAccountIndex].SteamID.getSteam2RenderedID(true).indexOf(keyData) != -1 || botAccounts[botAccountIndex].SteamID.getSteam2RenderedID().indexOf(keyData) != -1) {
return callback(null, botAccounts[botAccountIndex]);
}
}
if (botAccounts[botAccountIndex].getAccountName().indexOf(keyData) != -1) {
return callback(null, botAccounts[botAccountIndex]);
}
else if (botAccountIndex == botAccounts.length - 1) {
if (self.getAccounts()[parseInt(keyData)]) {
try {
return callback(null, self.getAccounts()[parseInt(keyData)]);
} catch (e) {
return callback({Error: "Failed to locate bot."}, null);
}
} else {
return callback({Error: "Failed to locate bot."}, null);
}
}
}
}
return callback({Error: "Failed to locate bot."}, null);
};
/**
* Retrieve accounts registered within the instance
* @returns {Array} - Array of BotAccount objects
*/
BotManager.prototype.getAccounts = function () {
var self = this;
return self.ActiveBotAccounts;
};
/**
* Register an account for use with the steam-bot-manager
* @param username
* @param password
* @param options
* @param callback
*/
BotManager.prototype.registerAccount = function (username, password, options, callback) {
var self = this;
var config = self.ConfigManager.getConfig();
var botAccount = new BotAccount(username, password, options, config, self.logger);
botAccount.on('sentOfferChanged', function (offer, oldState) {
self.emit('sentOfferChanged', botAccount, offer, oldState);
});
botAccount.on('receivedOfferChanged', function (offer, oldState) {
self.emit('receivedOfferChanged', botAccount, offer, oldState);
});
botAccount.on('offerList', function (filter, sent, received) {
self.emit('offerList', botAccount, filter, sent, received);
});
botAccount.on('sessionExpired', function () {
self.emit('sessionExpired', botAccount);
});
botAccount.on('newOffer', function (offer) {
self.emit('newOffer', botAccount, offer);
});
botAccount.on('loggedIn', function (botAccount) {
self.emit('loggedIn', botAccount);
});
botAccount.on('updatedAccountDetails', function (accountDetails) {
self.AccountsManager.saveAccount(accountDetails, function (err) {
if (err)
self.logger.log("error", "Failed to update account information for %j", botAccount.getAccountName());
else {
self.emit('updatedAccountDetails', botAccount);
self.logger.log("debug", "Updated account information to file.");
}
});
});
botAccount.on('rateLimitedSteam', function () {
for (var botAccountIndex in self.getAccounts()) {
if (self.ActiveBotAccounts.hasOwnProperty(botAccountIndex)) {
self.ActiveBotAccounts[botAccountIndex].setRateLimited(true);
}
}
setTimeout(function () {
for (var botAccountIndex in self.getAccounts()) {
if (self.ActiveBotAccounts.hasOwnProperty(botAccountIndex)) {
self.ActiveBotAccounts[botAccountIndex].setRateLimited(false);
}
}
}, 60000);
});
if (self.track)
self.track.event(packageJson.version, "Accounts", "Registered").send()// Trigger the tool's startup.
self.emit('loadedAccount', username, password);
self.ActiveBotAccounts.push(botAccount);
return callback(null, botAccount);
};
/**
* Choose a random bot (not checked if online)
* @returns {*}
* @deprecated
*/
BotManager.prototype.chooseRandomBot = function () {
var self = this;
var randomBotIndex = Math.floor((Math.random() * self.getAccounts().length) % 1 == 0 && (Math.random() * self.getAccounts().length) > 0 ? self.getAccounts().length - 1 : (Math.random() * self.getAccounts().length));
return self.getAccounts()[randomBotIndex];
};
/**
* Choose a random bot - with filters
* Will simple loop until it find a bot that meets all filters - otherwise it will just randomly choose one.
* Make sure to set filters based on your use-case.
* filters: in array of Strings
* 'canTrade' - Bot can access the API and can trade
* 'steamID:XX' - where XX is replaced with SteamID2, SteamID3, or SteamID64 or even TradeLink
* @returns {*}
*/
BotManager.prototype.randomBot = function (filters) {
var self = this;
if (filters.constructor !== Array)
filters = [];
var randomBotIndex;
if (filters.length == 0) {
randomBotIndex = Math.floor((Math.random() * self.getAccounts().length) % 1 == 0 && (Math.random() * self.getAccounts().length) > 0 ? self.getAccounts().length - 1 : (Math.random() * self.getAccounts().length));
return self.getAccounts()[randomBotIndex];
}
else {
var botsMeetingFilters = [];
for (var botIndex in self.getAccounts()) {
if (filters.indexOf("canTrade") != -1) {
if (self.getAccounts()[botIndex].Trade.api_access)
botsMeetingFilters.push(botIndex);// Push the index of the bot from original list (saving memory by only saving index)
}
if (botIndex == self.getAccounts().length - 1) {
// No bot meets our needs... Return undefined.
randomBotIndex = Math.floor((Math.random() * botsMeetingFilters.length) % 1 == 0 && (Math.random() * botsMeetingFilters) > 0 ? botsMeetingFilters.length - 1 : (Math.random() * botsMeetingFilters.length));
return self.getAccounts()[botsMeetingFilters[randomBotIndex]];
}
}
}
};
/**
* Post/log an informational message.
* @param {string} message - Informational message to log
*/
BotManager.prototype.infoDebug = function (message) {
var self = this;
self.logger.log('info', message);
};
/**
* Post/log an debug message.
* @param {string} message - debug message to log
*/
BotManager.prototype.logDebug = function (message) {
var self = this;
self.logger.log('debug', message);
};
/**
* Post/log an error-type message
* @param {string} message - Error message to log
*/
BotManager.prototype.errorDebug = function (message) {
var self = this;
if (self.track)
self.track.exception(message, true).send();// Trigger the tool's startup.
self.logger.log('error', message);
};