PixelPiggy/CS_float
0
1global._mckay_statistics_opt_out = true; // Opt out of node-steam-user stats2 3const optionDefinitions = [4 { name: 'config', alias: 'c', type: String, defaultValue: './config.js' }, // Config file location5 { name: 'steam_data', alias: 's', type: String } // Steam data directory6];7 8const winston = require('winston'),9 args = require('command-line-args')(optionDefinitions),10 bodyParser = require('body-parser'),11 rateLimit = require('express-rate-limit'),12 utils = require('./lib/utils'),13 queue = new (require('./lib/queue'))(),14 InspectURL = require('./lib/inspect_url'),15 botController = new (require('./lib/bot_controller'))(),16 CONFIG = require(args.config),17 postgres = new (require('./lib/postgres'))(CONFIG.database_url, CONFIG.enable_bulk_inserts),18 gameData = new (require('./lib/game_data'))(CONFIG.game_files_update_interval, CONFIG.enable_game_file_updates),19 errors = require('./errors'),20 Job = require('./lib/job');21 22if (CONFIG.max_simultaneous_requests === undefined) {23 CONFIG.max_simultaneous_requests = 1;24}25 26winston.level = CONFIG.logLevel || 'debug';27 28if (CONFIG.logins.length === 0) {29 console.log('There are no bot logins. Please add some in config.json');30 process.exit(1);31}32 33if (args.steam_data) {34 CONFIG.bot_settings.steam_user.dataDirectory = args.steam_data;35}36 37for (let [i, loginData] of CONFIG.logins.entries()) {38 const settings = Object.assign({}, CONFIG.bot_settings);39 if (CONFIG.proxies && CONFIG.proxies.length > 0) {40 const proxy = CONFIG.proxies[i % CONFIG.proxies.length];41 42 if (proxy.startsWith('http://')) {43 settings.steam_user = Object.assign({}, settings.steam_user, {httpProxy: proxy});44 } else if (proxy.startsWith('socks5://')) {45 settings.steam_user = Object.assign({}, settings.steam_user, {socksProxy: proxy});46 } else {47 console.log(`Invalid proxy '${proxy}' in config, must prefix with http:// or socks5://`);48 process.exit(1);49 }50 }51 52 botController.addBot(loginData, settings);53}54 55postgres.connect();56 57// Setup and configure express58const app = require('express')();59app.use(function (req, res, next) {60 if (req.method === 'POST') {61 // Default content-type62 req.headers['content-type'] = 'application/json';63 }64 next();65});66app.use(bodyParser.json({limit: '5mb'}));67 68app.use(function (error, req, res, next) {69 // Handle bodyParser errors70 if (error instanceof SyntaxError) {71 errors.BadBody.respond(res);72 }73 else next();74});75 76 77if (CONFIG.trust_proxy === true) {78 app.enable('trust proxy');79}80 81CONFIG.allowed_regex_origins = CONFIG.allowed_regex_origins || [];82CONFIG.allowed_origins = CONFIG.allowed_origins || [];83const allowedRegexOrigins = CONFIG.allowed_regex_origins.map((origin) => new RegExp(origin));84 85 86async function handleJob(job) {87 // See which items have already been cached88 const itemData = await postgres.getItemData(job.getRemainingLinks().map(e => e.link));89 for (let item of itemData) {90 const link = job.getLink(item.a);91 92 if (!item.price && link.price) {93 postgres.updateItemPrice(item.a, link.price);94 }95 96 gameData.addAdditionalItemProperties(item);97 item = utils.removeNullValues(item);98 99 job.setResponse(item.a, item);100 }101 102 if (!botController.hasBotOnline()) {103 return job.setResponseRemaining(errors.SteamOffline);104 }105 106 if (CONFIG.max_simultaneous_requests > 0 &&107 (queue.getUserQueuedAmt(job.ip) + job.remainingSize()) > CONFIG.max_simultaneous_requests) {108 return job.setResponseRemaining(errors.MaxRequests);109 }110 111 if (CONFIG.max_queue_size > 0 && (queue.size() + job.remainingSize()) > CONFIG.max_queue_size) {112 return job.setResponseRemaining(errors.MaxQueueSize);113 }114 115 if (job.remainingSize() > 0) {116 queue.addJob(job, CONFIG.bot_settings.max_attempts);117 }118}119 120function canSubmitPrice(key, link, price) {121 return CONFIG.price_key && key === CONFIG.price_key && price && link.isMarketLink() && utils.isOnlyDigits(price);122}123 124app.use(function (req, res, next) {125 if (CONFIG.allowed_origins.length > 0 && req.get('origin') != undefined) {126 // check to see if its a valid domain127 const allowed = CONFIG.allowed_origins.indexOf(req.get('origin')) > -1 ||128 allowedRegexOrigins.findIndex((reg) => reg.test(req.get('origin'))) > -1;129 130 if (allowed) {131 res.header('Access-Control-Allow-Origin', req.get('origin'));132 res.header('Access-Control-Allow-Methods', 'GET');133 }134 }135 next()136});137 138if (CONFIG.rate_limit && CONFIG.rate_limit.enable) {139 app.use(rateLimit({140 windowMs: CONFIG.rate_limit.window_ms,141 max: CONFIG.rate_limit.max,142 headers: false,143 handler: function (req, res) {144 errors.RateLimit.respond(res);145 }146 }))147}148 149app.get('/', function(req, res) {150 // Get and parse parameters151 let link;152 153 if ('url' in req.query) {154 link = new InspectURL(req.query.url);155 }156 else if ('a' in req.query && 'd' in req.query && ('s' in req.query || 'm' in req.query)) {157 link = new InspectURL(req.query);158 }159 160 if (!link || !link.getParams()) {161 return errors.InvalidInspect.respond(res);162 }163 164 const job = new Job(req, res, /* bulk */ false);165 166 let price;167 168 if (canSubmitPrice(req.query.priceKey, link, req.query.price)) {169 price = parseInt(req.query.price);170 }171 172 job.add(link, price);173 174 try {175 handleJob(job);176 } catch (e) {177 winston.warn(e);178 errors.GenericBad.respond(res);179 }180});181 182app.post('/bulk', (req, res) => {183 if (!req.body || (CONFIG.bulk_key && req.body.bulk_key != CONFIG.bulk_key)) {184 return errors.BadSecret.respond(res);185 }186 187 if (!req.body.links || req.body.links.length === 0) {188 return errors.BadBody.respond(res);189 }190 191 if (CONFIG.max_simultaneous_requests > 0 && req.body.links.length > CONFIG.max_simultaneous_requests) {192 return errors.MaxRequests.respond(res);193 }194 195 const job = new Job(req, res, /* bulk */ true);196 197 for (const data of req.body.links) {198 const link = new InspectURL(data.link);199 if (!link.valid) {200 return errors.InvalidInspect.respond(res);201 }202 203 let price;204 205 if (canSubmitPrice(req.body.priceKey, link, data.price)) {206 price = parseInt(req.query.price);207 }208 209 job.add(link, price);210 }211 212 try {213 handleJob(job);214 } catch (e) {215 winston.warn(e);216 errors.GenericBad.respond(res);217 }218});219 220app.get('/stats', (req, res) => {221 res.json({222 bots_online: botController.getReadyAmount(),223 bots_total: botController.bots.length,224 queue_size: queue.queue.length,225 queue_concurrency: queue.concurrency,226 });227});228 229const http_server = require('http').Server(app);230http_server.listen(CONFIG.http.port);231winston.info('Listening for HTTP on port: ' + CONFIG.http.port);232 233queue.process(CONFIG.logins.length, botController, async (job) => {234 const itemData = await botController.lookupFloat(job.data.link);235 winston.debug(`Received itemData for ${job.data.link.getParams().a}`);236 237 // Save and remove the delay attribute238 let delay = itemData.delay;239 delete itemData.delay;240 241 // add the item info to the DB242 await postgres.insertItemData(itemData.iteminfo, job.data.price);243 244 // Get rank, annotate with game files245 itemData.iteminfo = Object.assign(itemData.iteminfo, await postgres.getItemRank(itemData.iteminfo.a));246 gameData.addAdditionalItemProperties(itemData.iteminfo);247 248 itemData.iteminfo = utils.removeNullValues(itemData.iteminfo);249 itemData.iteminfo.stickers = itemData.iteminfo.stickers.map((s) => utils.removeNullValues(s));250 itemData.iteminfo.keychains = itemData.iteminfo.keychains.map((s) => utils.removeNullValues(s));251 252 job.data.job.setResponse(job.data.link.getParams().a, itemData.iteminfo);253 254 return delay;255});256 257queue.on('job failed', (job, err) => {258 const params = job.data.link.getParams();259 winston.warn(`Job Failed! S: ${params.s} A: ${params.a} D: ${params.d} M: ${params.m} IP: ${job.ip}, Err: ${(err || '').toString()}`);260 261 job.data.job.setResponse(params.a, errors.TTLExceeded);262});263 