diff --git a/CONTRIBUTORS.js b/CONTRIBUTORS.js index 7b47a0de0..1ba80e050 100644 --- a/CONTRIBUTORS.js +++ b/CONTRIBUTORS.js @@ -4,7 +4,7 @@ Currently this will cause the /players/:id endpoint to return is_contributor = true for your profile */ -module.exports = { +export default { 88367253: {}, // howardchung 102344608: {}, // albertcui 9977887: {}, // gu3st diff --git a/config.js b/config.js index f84eb2c80..5525f9b33 100755 --- a/config.js +++ b/config.js @@ -1,11 +1,11 @@ /** * File managing configuration for the application * */ -const dotenv = require('dotenv'); -const fs = require('fs'); +import { config } from 'dotenv'; +import { existsSync } from 'fs'; -if (fs.existsSync('.env')) { - dotenv.config(); +if (existsSync('.env')) { + config(); } const defaults = { @@ -97,4 +97,4 @@ if (process.env.NODE_ENV === 'test') { process.env.PARSER_PORT = 5201; } // now processes can use either process.env or config -module.exports = process.env; +export default process.env; diff --git a/dev/checkAccounts.mjs b/dev/checkAccounts.mjs index 35398f668..0a3c54b00 100644 --- a/dev/checkAccounts.mjs +++ b/dev/checkAccounts.mjs @@ -1,8 +1,9 @@ import fs from 'fs'; import Steam from 'steam'; import async from 'async'; +import {EOL} from 'os'; const accountData = fs.readFileSync('./STEAM_ACCOUNT_DATA_BAD.txt', 'utf8'); -const accountArray = accountData.split(require('os').EOL); +const accountArray = accountData.split(EOL); let index = Number(process.argv[2]) || -1; async.whilst( diff --git a/index.js b/index.cjs similarity index 100% rename from index.js rename to index.cjs diff --git a/package-lock.json b/package-lock.json index 3a4e2c1f3..77bf2bf75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "steam-resources": "github:odota/node-steam-resources", "stripe": "^9.12.0", "supertest": "^4.0.2", - "uuid": "^3.3.3" + "uuid": "^9.0.1" }, "devDependencies": { "@apidevtools/swagger-parser": "^10.0.2", @@ -1292,6 +1292,15 @@ "semver": "bin/semver" } }, + "node_modules/@opencensus/core/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/@opencensus/propagation-b3": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz", @@ -1327,6 +1336,15 @@ "semver": "bin/semver" } }, + "node_modules/@opencensus/propagation-b3/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/@passport-next/passport-openid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@passport-next/passport-openid/-/passport-openid-1.0.0.tgz", @@ -8770,6 +8788,15 @@ "node": ">=0.6" } }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10260,12 +10287,15 @@ } }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8flags": { @@ -11895,6 +11925,11 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -11923,6 +11958,11 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -17789,6 +17829,11 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -18957,9 +19002,9 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8flags": { "version": "3.2.0", diff --git a/package.json b/package.json index db0034f41..fc8ba1dd6 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "version": "21.0.2", "license": "MIT", "main": "index.js", + "type": "module", "scripts": { "start": "node index", "test": "NODE_ENV=test mocha --exit", @@ -62,7 +63,7 @@ "steam-resources": "github:odota/node-steam-resources", "stripe": "^9.12.0", "supertest": "^4.0.2", - "uuid": "^3.3.3" + "uuid": "^9.0.1" }, "devDependencies": { "@apidevtools/swagger-parser": "^10.0.2", diff --git a/processors/createParsedDataBlob.js b/processors/createParsedDataBlob.js index 6c5c2e94d..b5d2d7c87 100644 --- a/processors/createParsedDataBlob.js +++ b/processors/createParsedDataBlob.js @@ -1,12 +1,11 @@ -const { Console } = require('console'); -const readline = require('readline'); -const processAllPlayers = require('./processAllPlayers'); -const processTeamfights = require('./processTeamfights'); -// const processUploadProps = require('../processors/processUploadProps'); -const processParsedData = require('./processParsedData'); -const processMetadata = require('./processMetadata'); -const processExpand = require('./processExpand'); -const processDraftTimings = require('./processDraftTimings'); +import { Console } from 'console'; +import { createInterface } from 'readline'; +import processAllPlayers from './processAllPlayers.js'; +import processTeamfights from './processTeamfights.js'; +import processParsedData from './processParsedData.js'; +import processMetadata from './processMetadata.js'; +import processExpand from './processExpand.js'; +import processDraftTimings from './processDraftTimings.js'; function getParseSchema() { return { @@ -118,7 +117,7 @@ function createParsedDataBlob(entries, matchId) { const entries = []; let complete = false; const matchId = process.argv[2]; -const parseStream = readline.createInterface({ +const parseStream = createInterface({ input: process.stdin, }); parseStream.on('line', (e) => { diff --git a/processors/populate.js b/processors/populate.js index 18005b6d4..0346ed34b 100644 --- a/processors/populate.js +++ b/processors/populate.js @@ -1,4 +1,4 @@ -const performanceOthers = require('./performanceOthers'); +import performanceOthers from './performanceOthers.js'; function populate(e, container, meta) { let t; @@ -134,4 +134,4 @@ function populate(e, container, meta) { break; } } -module.exports = populate; +export default populate; diff --git a/processors/processAllPlayers.js b/processors/processAllPlayers.js index 5ae6fe31c..d490b9318 100644 --- a/processors/processAllPlayers.js +++ b/processors/processAllPlayers.js @@ -1,4 +1,4 @@ -const utility = require('../util/utility'); +import { isRadiant } from '../util/utility.js'; /** * Compute data requiring all players in a match for storage in match table @@ -13,12 +13,12 @@ function processAllPlayers(entries, meta) { for (let i = 0; i < entries.length; i += 1) { const e = entries[i]; if (e.time >= 0 && e.time % 60 === 0 && e.type === 'interval') { - const g = utility.isRadiant({ + const g = isRadiant({ player_slot: meta.slot_to_playerslot[e.slot], }) ? e.gold : -e.gold; - const x = utility.isRadiant({ + const x = isRadiant({ player_slot: meta.slot_to_playerslot[e.slot], }) ? e.xp @@ -34,4 +34,4 @@ function processAllPlayers(entries, meta) { }); return res; } -module.exports = processAllPlayers; +export default processAllPlayers; diff --git a/processors/processParsedData.js b/processors/processParsedData.js index 795e14446..a6a9afaa6 100644 --- a/processors/processParsedData.js +++ b/processors/processParsedData.js @@ -1,4 +1,4 @@ -const populate = require('./populate'); +import populate from './populate.js'; function processParsedData(entries, container, meta) { for (let i = 0; i < entries.length; i += 1) { @@ -8,4 +8,4 @@ function processParsedData(entries, container, meta) { return container; } -module.exports = processParsedData; +export default processParsedData; diff --git a/processors/processTeamfights.js b/processors/processTeamfights.js index e9de1180c..899dcac03 100644 --- a/processors/processTeamfights.js +++ b/processors/processTeamfights.js @@ -1,4 +1,4 @@ -const populate = require('./populate'); +import populate from './populate.js'; /** * A processor to compute teamfights that occurred given an event stream @@ -135,4 +135,4 @@ function processTeamfights(entries, meta) { } return teamfights; } -module.exports = processTeamfights; +export default processTeamfights; diff --git a/processors/processUploadProps.js b/processors/processUploadProps.js index 9b4e3c4d9..9aec40fa3 100644 --- a/processors/processUploadProps.js +++ b/processors/processUploadProps.js @@ -17,7 +17,7 @@ function processUploadProps(entries) { container.radiant_win = dota.gameWinner_ === 2; // NOTE: following needs some extraction/transformation // container.picks_bans = dota.picksBans_; - // require('fs').writeFileSync('./outputEpilogue.json', JSON.stringify(JSON.parse(e.key))); + // fs.writeFileSync('./outputEpilogue.json', JSON.stringify(JSON.parse(e.key))); break; case 'interval': if (!container.player_map[e.player_slot]) { @@ -39,4 +39,4 @@ function processUploadProps(entries) { } return container; } -module.exports = processUploadProps; +export default processUploadProps; diff --git a/routes/api.js b/routes/api.js index 817b92c06..c9136cce0 100644 --- a/routes/api.js +++ b/routes/api.js @@ -1,24 +1,24 @@ -const express = require('express'); -const moment = require('moment'); -const async = require('async'); -const playerFields = require('./playerFields.json'); -const filterDeps = require('../util/filterDeps'); -const config = require('../config'); -const spec = require('./spec'); -const cacheFunctions = require('../store/cacheFunctions'); -const db = require('../store/db'); -const redis = require('../store/redis'); +import { Router } from 'express'; +import moment from 'moment'; +import { parallel } from 'async'; +import playerFields from './playerFields.json'; +import filterDeps from '../util/filterDeps.js'; +import { ADMIN_ACCOUNT_IDS } from '../config.js'; +import spec, { paths } from './spec.js'; +import { read } from '../store/cacheFunctions.js'; +import { raw } from '../store/db.js'; +import { zrevrange, zcard } from '../store/redis.js'; -const api = new express.Router(); +const api = new Router(); const { subkeys } = playerFields; -const admins = config.ADMIN_ACCOUNT_IDS.split(',').map((e) => Number(e)); +const admins = ADMIN_ACCOUNT_IDS.split(',').map((e) => Number(e)); // Player caches middleware api.use('/players/:account_id/:info?', (req, res, cb) => { // Check cache if (!Object.keys(req.query).length && req.params.info) { - return cacheFunctions.read( + return read( { key: req.params.info, account_id: req.params.account_id, @@ -107,10 +107,10 @@ api.get('/admin/apiMetrics', (req, res) => { const startTime = moment().startOf('month').format('YYYY-MM-DD'); const endTime = moment().endOf('month').format('YYYY-MM-DD'); - async.parallel( + parallel( { topAPI: (cb) => { - db.raw( + raw( ` SELECT account_id, @@ -136,7 +136,7 @@ api.get('/admin/apiMetrics', (req, res) => { ).asCallback((err, res) => cb(err, err ? null : res.rows)); }, topAPIIP: (cb) => { - db.raw( + raw( ` SELECT ip, @@ -163,7 +163,7 @@ api.get('/admin/apiMetrics', (req, res) => { ).asCallback((err, res) => cb(err, err ? null : res.rows)); }, numAPIUsers: (cb) => { - db.raw( + raw( ` SELECT COUNT(DISTINCT account_id) @@ -176,10 +176,10 @@ api.get('/admin/apiMetrics', (req, res) => { ).asCallback((err, res) => cb(err, err ? null : res.rows)); }, topUsersIP: (cb) => { - redis.zrevrange('user_usage_count', 0, 24, 'WITHSCORES', cb); + zrevrange('user_usage_count', 0, 24, 'WITHSCORES', cb); }, numUsersIP: (cb) => { - redis.zcard('user_usage_count', cb); + zcard('user_usage_count', cb); }, }, (err, result) => { @@ -197,9 +197,9 @@ api.get('/', (req, res) => { }); // API endpoints -Object.keys(spec.paths).forEach((path) => { - Object.keys(spec.paths[path]).forEach((verb) => { - const { route, func } = spec.paths[path][verb]; +Object.keys(paths).forEach((path) => { + Object.keys(paths[path]).forEach((verb) => { + const { route, func } = paths[path][verb]; // Use the 'route' function to get the route path if it's available; otherwise, transform the OpenAPI path to the Express format. const routePath = route ? route() @@ -216,4 +216,4 @@ Object.keys(spec.paths).forEach((path) => { }); }); -module.exports = api; +export default api; diff --git a/routes/generateOperationId.js b/routes/generateOperationId.js index e995337c0..0cdd053c1 100644 --- a/routes/generateOperationId.js +++ b/routes/generateOperationId.js @@ -34,4 +34,4 @@ function generateOperationId(method, path) { return `${method}_${snakeCaseBase}`; } -module.exports = generateOperationId; +export default generateOperationId; diff --git a/routes/keyManagement.js b/routes/keyManagement.js index 8bdfdf073..9912aaca0 100644 --- a/routes/keyManagement.js +++ b/routes/keyManagement.js @@ -1,20 +1,20 @@ -const express = require('express'); -const uuid = require('uuid/v4'); -const bodyParser = require('body-parser'); -const moment = require('moment'); -const async = require('async'); -const stripeLib = require('stripe'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const config = require('../config'); - -const stripe = stripeLib(config.STRIPE_SECRET); -const stripeAPIPlan = config.STRIPE_API_PLAN; -const keys = express.Router(); - -keys.use(bodyParser.json()); +import { Router } from 'express'; +import {v4 as uuid} from 'uuid'; +import { json, urlencoded } from 'body-parser'; +import moment from 'moment'; +import { parallel } from 'async'; +import stripeLib from 'stripe'; +import { from, raw } from '../store/db.js'; +import { srem, sadd } from '../store/redis.js'; +import { STRIPE_SECRET, STRIPE_API_PLAN } from '../config.js'; + +const stripe = stripeLib(STRIPE_SECRET); +const stripeAPIPlan = STRIPE_API_PLAN; +const keys = Router(); + +keys.use(json()); keys.use( - bodyParser.urlencoded({ + urlencoded({ extended: true, }) ); @@ -62,7 +62,7 @@ async function getOpenInvoices(customerId) { keys .route('/') .all(async (req, res, next) => { - const rows = await db.from('api_keys').where({ + const rows = await from('api_keys').where({ account_id: req.user.account_id, }); @@ -78,7 +78,7 @@ keys return res.json({}); } - async.parallel( + parallel( { customer: (cb) => { if (!keyRecord) { @@ -125,7 +125,7 @@ keys }); }, usage: (cb) => { - db.raw( + raw( ` SELECT account_id, @@ -179,8 +179,7 @@ keys // Immediately bill the customer for any unpaid usage await stripe.subscriptions.del(subscription_id, { invoice_now: true }); - await db - .from('api_keys') + await from('api_keys') .where({ account_id: req.user.account_id, subscription_id, @@ -190,7 +189,7 @@ keys }); // Force the key to be disabled - redis.srem('api_keys', api_key, (err) => { + srem('api_keys', api_key, (err) => { if (err) { throw err; } @@ -270,7 +269,7 @@ keys }, }); - await db.raw( + await raw( ` INSERT INTO api_keys (account_id, api_key, customer_id, subscription_id) VALUES (?, ?, ?, ?) @@ -289,7 +288,7 @@ keys ); // Add the key to Redis so that it works immediately - redis.sadd('api_keys', apiKey, (err) => { + sadd('api_keys', apiKey, (err) => { if (err) { throw err; } @@ -328,4 +327,4 @@ keys res.sendStatus(200); }); -module.exports = keys; +export default keys; diff --git a/routes/requests/queryParams/heroParams.js b/routes/requests/heroParams.js similarity index 61% rename from routes/requests/queryParams/heroParams.js rename to routes/requests/heroParams.js index 164d60d3e..975d9fd60 100644 --- a/routes/requests/queryParams/heroParams.js +++ b/routes/requests/heroParams.js @@ -1,11 +1,9 @@ -module.exports = { - heroIdPathParam: { +export const heroIdPathParam = { name: 'hero_id', in: 'path', description: 'Hero ID', required: true, schema: { - type: 'integer', + type: 'integer', }, - }, }; diff --git a/routes/requests/importParams.js b/routes/requests/importParams.js deleted file mode 100644 index 29bfd8046..000000000 --- a/routes/requests/importParams.js +++ /dev/null @@ -1,28 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -// Recursive function to import all files in a directory and its subdirectories -function importAll(directory) { - let files = {}; - - // Read all files and subdirectories in the directory - fs.readdirSync(directory).forEach((item) => { - const itemPath = path.join(directory, item); - - if (fs.lstatSync(itemPath).isDirectory()) { - // If the item is a subdirectory, call this function with the subdirectory as the new starting point - files = { ...files, ...importAll(itemPath) }; - } else if (path.extname(item) === '.js' && itemPath !== __filename) { - // If the item is a JS file and not the current file, import it - const fileName = path.basename(item, '.js'); - files[fileName] = require(itemPath); - } - }); - - return files; -} - -// Import all files in the responses directory and its subdirectories -const queryParams = importAll(__dirname); - -module.exports = queryParams; diff --git a/routes/requests/index.js b/routes/requests/index.js new file mode 100644 index 000000000..fb4a0cb84 --- /dev/null +++ b/routes/requests/index.js @@ -0,0 +1,15 @@ +import heroParams from './heroParams.js'; +import leagueParams from './leagueParams.js'; +import matchParams from './matchParams.js'; +import playerParams from './playerParams.js'; +import scenarioParams from './scenarioParams.js'; +import teamParams from './teamParams.js'; + +export default { + heroParams, + leagueParams, + matchParams, + playerParams, + scenarioParams, + teamParams, +} diff --git a/routes/requests/queryParams/leagueParams.js b/routes/requests/leagueParams.js similarity index 61% rename from routes/requests/queryParams/leagueParams.js rename to routes/requests/leagueParams.js index 91c3b4a0f..c1d6dfff9 100644 --- a/routes/requests/queryParams/leagueParams.js +++ b/routes/requests/leagueParams.js @@ -1,11 +1,9 @@ -module.exports = { - leagueIdPathParam: { +export const leagueIdPathParam = { name: 'league_id', in: 'path', description: 'League ID', required: true, schema: { - type: 'integer', + type: 'integer', }, - }, }; diff --git a/routes/requests/matchParams.js b/routes/requests/matchParams.js new file mode 100644 index 000000000..7f0c48c0e --- /dev/null +++ b/routes/requests/matchParams.js @@ -0,0 +1,53 @@ +export const matchIdParam = { + name: 'match_id', + in: 'path', + required: true, + schema: { + type: 'integer', + }, +}; +export const lessThanMatchIdParam = { + name: 'less_than_match_id', + in: 'query', + description: 'Get matches with a match ID lower than this value', + required: false, + schema: { + type: 'integer', + }, +}; +export const minRankParam = { + name: 'min_rank', + in: 'query', + description: 'Minimum rank for the matches. Ranks are represented by integers (10-15: Herald, 20-25: Guardian, 30-35: Crusader, 40-45: Archon, 50-55: Legend, 60-65: Ancient, 70-75: Divine, 80-85: Immortal). Each increment represents an additional star.', + required: false, + schema: { + type: 'integer', + }, +}; +export const maxRankParam = { + name: 'max_rank', + in: 'query', + description: 'Maximum rank for the matches. Ranks are represented by integers (10-15: Herald, 20-25: Guardian, 30-35: Crusader, 40-45: Archon, 50-55: Legend, 60-65: Ancient, 70-75: Divine, 80-85: Immortal). Each increment represents an additional star.', + required: false, + schema: { + type: 'integer', + }, +}; +export const mmrAscendingParam = { + name: 'mmr_ascending', + in: 'query', + description: 'Order by MMR ascending', + required: false, + schema: { + type: 'integer', + }, +}; +export const mmrDescendingParam = { + name: 'mmr_descending', + in: 'query', + description: 'Order by MMR descending', + required: false, + schema: { + type: 'integer', + }, +}; diff --git a/routes/requests/queryParams/playerParams.js b/routes/requests/playerParams.js similarity index 65% rename from routes/requests/queryParams/playerParams.js rename to routes/requests/playerParams.js index 9de4389c0..7953b344f 100644 --- a/routes/requests/queryParams/playerParams.js +++ b/routes/requests/playerParams.js @@ -1,195 +1,189 @@ -module.exports = { - accountIdParam: { +export const accountIdParam = { name: 'account_id', in: 'path', description: 'Steam32 account ID', required: true, schema: { - type: 'integer', + type: 'integer', }, - }, - // for /players/{account_id}/histograms/{field}: - fieldParam: { +}; +export const fieldParam = { name: 'field', in: 'path', description: 'Field to aggregate on', required: true, schema: { - type: 'string', + type: 'string', }, - }, - // for /players/{account_id}/matches - projectParam: { +}; +export const projectParam = { name: 'project', in: 'query', description: 'Fields to project (array)', required: false, schema: { - type: 'string', + type: 'string', }, - }, - // playerParamNames: - limitParam: { +}; +export const limitParam = { name: 'limit', in: 'query', description: 'Number of matches to limit to', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - offsetParam: { +}; +export const offsetParam = { name: 'offset', in: 'query', description: 'Number of matches to offset start by', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - winParam: { +}; +export const winParam = { name: 'win', in: 'query', description: 'Whether the player won', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - patchParam: { +}; +export const patchParam = { name: 'patch', in: 'query', description: 'Patch ID', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - gameModeParam: { +}; +export const gameModeParam = { name: 'game_mode', in: 'query', description: 'Game Mode ID', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - lobbyTypeParam: { +}; +export const lobbyTypeParam = { name: 'lobby_type', in: 'query', description: 'Lobby type ID', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - regionParam: { +}; +export const regionParam = { name: 'region', in: 'query', description: 'Region ID', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - dateParam: { +}; +export const dateParam = { name: 'date', in: 'query', description: 'Days previous', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - laneRoleParam: { +}; +export const laneRoleParam = { name: 'lane_role', in: 'query', description: 'Lane Role ID', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - heroIdParam: { +}; +export const heroIdParam = { name: 'hero_id', in: 'query', description: 'Hero ID', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - isRadiantParam: { +}; +export const isRadiantParam = { name: 'is_radiant', in: 'query', description: 'Whether the player was radiant', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - includedAccountIdParam: { +}; +export const includedAccountIdParam = { name: 'included_account_id', in: 'query', description: 'Account IDs in the match (array)', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - excludedAccountIdParam: { +}; +export const excludedAccountIdParam = { name: 'excluded_account_id', in: 'query', description: 'Account IDs not in the match (array)', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - withHeroIdParam: { +}; +export const withHeroIdParam = { name: 'with_hero_id', in: 'query', description: "Hero IDs on the player's team (array)", required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - againstHeroIdParam: { +}; +export const againstHeroIdParam = { name: 'against_hero_id', in: 'query', description: "Hero IDs against the player's team (array)", required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - significantParam: { +}; +export const significantParam = { name: 'significant', in: 'query', - description: - 'Whether the match was significant for aggregation purposes. Defaults to 1 (true), set this to 0 to return data for non-standard modes/matches.', + description: 'Whether the match was significant for aggregation purposes. Defaults to 1 (true), set this to 0 to return data for non-standard modes/matches.', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - havingParam: { +}; +export const havingParam = { name: 'having', in: 'query', description: 'The minimum number of games played, for filtering hero stats', required: false, schema: { - type: 'integer', + type: 'integer', }, - }, - sortParam: { +}; +export const sortParam = { name: 'sort', in: 'query', description: 'The field to return matches sorted by in descending order', required: false, schema: { - type: 'string', + type: 'string', }, - }, }; diff --git a/routes/requests/queryParams/matchParams.js b/routes/requests/queryParams/matchParams.js deleted file mode 100644 index ee338039d..000000000 --- a/routes/requests/queryParams/matchParams.js +++ /dev/null @@ -1,58 +0,0 @@ -module.exports = { - matchIdParam: { - name: 'match_id', - in: 'path', - required: true, - schema: { - type: 'integer', - }, - }, - // for /publicMatches: - lessThanMatchIdParam: { - name: 'less_than_match_id', - in: 'query', - description: 'Get matches with a match ID lower than this value', - required: false, - schema: { - type: 'integer', - }, - }, - minRankParam: { - name: 'min_rank', - in: 'query', - description: - 'Minimum rank for the matches. Ranks are represented by integers (10-15: Herald, 20-25: Guardian, 30-35: Crusader, 40-45: Archon, 50-55: Legend, 60-65: Ancient, 70-75: Divine, 80-85: Immortal). Each increment represents an additional star.', - required: false, - schema: { - type: 'integer', - }, - }, - maxRankParam: { - name: 'max_rank', - in: 'query', - description: - 'Maximum rank for the matches. Ranks are represented by integers (10-15: Herald, 20-25: Guardian, 30-35: Crusader, 40-45: Archon, 50-55: Legend, 60-65: Ancient, 70-75: Divine, 80-85: Immortal). Each increment represents an additional star.', - required: false, - schema: { - type: 'integer', - }, - }, - mmrAscendingParam: { - name: 'mmr_ascending', - in: 'query', - description: 'Order by MMR ascending', - required: false, - schema: { - type: 'integer', - }, - }, - mmrDescendingParam: { - name: 'mmr_descending', - in: 'query', - description: 'Order by MMR descending', - required: false, - schema: { - type: 'integer', - }, - }, -}; diff --git a/routes/requests/queryParams/scenarioParams.js b/routes/requests/queryParams/scenarioParams.js deleted file mode 100644 index 5e23eabeb..000000000 --- a/routes/requests/queryParams/scenarioParams.js +++ /dev/null @@ -1,13 +0,0 @@ -const su = require('../../../util/scenariosUtil'); - -module.exports = { - scenarioParam: { - name: 'scenario', - in: 'query', - description: su.teamScenariosQueryParams.toString(), - required: false, - schema: { - type: 'string', - }, - }, -}; diff --git a/routes/requests/scenarioParams.js b/routes/requests/scenarioParams.js new file mode 100644 index 000000000..1b5d56a67 --- /dev/null +++ b/routes/requests/scenarioParams.js @@ -0,0 +1,11 @@ +import { teamScenariosQueryParams } from '../../../util/scenariosUtil.js'; + +export const scenarioParam = { + name: 'scenario', + in: 'query', + description: teamScenariosQueryParams.toString(), + required: false, + schema: { + type: 'string', + }, +}; diff --git a/routes/requests/queryParams/teamParams.js b/routes/requests/teamParams.js similarity index 61% rename from routes/requests/queryParams/teamParams.js rename to routes/requests/teamParams.js index 1adbdce54..c2bdd15b6 100644 --- a/routes/requests/queryParams/teamParams.js +++ b/routes/requests/teamParams.js @@ -1,11 +1,9 @@ -module.exports = { - teamIdPathParam: { +export const teamIdPathParam = { name: 'team_id', in: 'path', description: 'Team ID', required: true, schema: { - type: 'integer', + type: 'integer', }, - }, }; diff --git a/routes/responses/BenchmarksResponse.js b/routes/responses/BenchmarksResponse.js new file mode 100644 index 000000000..0aee25030 --- /dev/null +++ b/routes/responses/BenchmarksResponse.js @@ -0,0 +1,127 @@ +import { hero_id as _hero_id } from '../../properties/commonProperties'; + +export const BenchmarksResponse = { + title: 'BenchmarksResponse', + type: 'object', + properties: { + hero_id: _hero_id, + result: { + description: 'result', + type: 'object', + properties: { + gold_per_min: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'number', + }, + }, + }, + }, + xp_per_min: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'number', + }, + }, + }, + }, + kills_per_min: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'number', + }, + }, + }, + }, + last_hits_per_min: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'number', + }, + }, + }, + }, + hero_damage_per_min: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'number', + }, + }, + }, + }, + hero_healing_per_min: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'number', + }, + }, + }, + }, + tower_damage: { + type: 'array', + items: { + type: 'object', + properties: { + percentile: { + description: 'percentile', + type: 'number', + }, + value: { + description: 'value', + type: 'integer', + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/routes/responses/DistributionsResponse.js b/routes/responses/DistributionsResponse.js new file mode 100644 index 000000000..ec619f7f8 --- /dev/null +++ b/routes/responses/DistributionsResponse.js @@ -0,0 +1,260 @@ +import { field_name } from '../../properties/commonProperties'; + +export const DistributionsResponse = { + title: 'DistributionsResponse', + type: 'object', + properties: { + ranks: { + description: 'ranks', + type: 'object', + properties: { + commmand: { + description: 'command', + type: 'string', + }, + rowCount: { + description: 'rowCount', + type: 'integer', + }, + rows: { + description: 'rows', + type: 'array', + items: { + type: 'object', + properties: { + bin: { + description: 'bin', + type: 'integer', + }, + bin_name: { + description: 'bin_name', + type: 'integer', + }, + count: { + description: 'count', + type: 'integer', + }, + cumulative_sum: { + description: 'cumulative_sum', + type: 'integer', + }, + }, + }, + }, + fields: { + description: 'fields', + type: 'array', + items: { + type: 'object', + properties: { + name: field_name, + tableID: { + description: 'tableID', + type: 'integer', + }, + columnID: { + description: 'columnID', + type: 'integer', + }, + dataTypeID: { + description: 'dataTypeID', + type: 'integer', + }, + dataTypeSize: { + description: 'dataTypeSize', + type: 'integer', + }, + dataTypeModifier: { + description: 'dataTypeModifier', + type: 'integer', + }, + format: { + description: 'format', + type: 'string', + }, + }, + }, + }, + rowAsArray: { + description: 'rowAsArray', + type: 'boolean', + }, + sum: { + description: 'sum', + type: 'object', + properties: { + count: { + description: 'count', + type: 'integer', + }, + }, + }, + }, + }, + mmr: { + description: 'mmr', + type: 'object', + properties: { + commmand: { + description: 'command', + type: 'string', + }, + rowCount: { + description: 'rowCount', + type: 'integer', + }, + rows: { + description: 'rows', + type: 'array', + items: { + type: 'object', + properties: { + bin: { + description: 'bin', + type: 'integer', + }, + bin_name: { + description: 'bin_name', + type: 'integer', + }, + count: { + description: 'count', + type: 'integer', + }, + cumulative_sum: { + description: 'cumulative_sum', + type: 'integer', + }, + }, + }, + }, + fields: { + description: 'fields', + type: 'array', + items: { + type: 'object', + properties: { + name: field_name, + tableID: { + description: 'tableID', + type: 'integer', + }, + columnID: { + description: 'columnID', + type: 'integer', + }, + dataTypeID: { + description: 'dataTypeID', + type: 'integer', + }, + dataTypeSize: { + description: 'dataTypeSize', + type: 'integer', + }, + dataTypeModifier: { + description: 'dataTypeModifier', + type: 'integer', + }, + format: { + description: 'format', + type: 'string', + }, + }, + }, + }, + rowAsArray: { + description: 'rowAsArray', + type: 'boolean', + }, + sum: { + description: 'sum', + type: 'object', + properties: { + count: { + description: 'count', + type: 'integer', + }, + }, + }, + }, + }, + country_mmr: { + description: 'country_mmr', + type: 'object', + properties: { + commmand: { + description: 'command', + type: 'string', + }, + rowCount: { + description: 'rowCount', + type: 'integer', + }, + rows: { + description: 'rows', + type: 'array', + items: { + type: 'object', + properties: { + loccountrycode: { + description: 'loccountrycode', + type: 'string', + nullable: true, + }, + count: { + description: 'count', + type: 'integer', + }, + avg: { + description: 'avg', + type: 'string', + }, + common: { + description: 'common', + type: 'string', + }, + }, + }, + }, + fields: { + description: 'fields', + type: 'array', + items: { + type: 'object', + properties: { + name: field_name, + tableID: { + description: 'tableID', + type: 'integer', + }, + columnID: { + description: 'columnID', + type: 'integer', + }, + dataTypeID: { + description: 'dataTypeID', + type: 'integer', + }, + dataTypeSize: { + description: 'dataTypeSize', + type: 'integer', + }, + dataTypeModifier: { + description: 'dataTypeModifier', + type: 'integer', + }, + format: { + description: 'format', + type: 'string', + }, + }, + }, + }, + rowAsArray: { + description: 'rowAsArray', + type: 'boolean', + }, + }, + }, + }, +}; diff --git a/routes/responses/HeroDurationsResponse.js b/routes/responses/HeroDurationsResponse.js new file mode 100644 index 000000000..5589c7420 --- /dev/null +++ b/routes/responses/HeroDurationsResponse.js @@ -0,0 +1,18 @@ +export const HeroDurationsResponse = { + title: 'HeroDurationsResponse', + type: 'object', + properties: { + duration_bin: { + description: 'Lower bound of number of seconds the match lasted', + type: 'string', + }, + games_played: { + description: 'Number of games played', + type: 'integer', + }, + wins: { + description: 'Number of wins', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/HeroItemPopularityResponse.js b/routes/responses/HeroItemPopularityResponse.js new file mode 100644 index 000000000..ca00f0fa4 --- /dev/null +++ b/routes/responses/HeroItemPopularityResponse.js @@ -0,0 +1,46 @@ +export const HeroItemPopularityResponse = { + title: 'HeroItemPopularityResponse', + type: 'object', + properties: { + start_game_items: { + description: 'Items bought before game started', + type: 'object', + properties: { + item: { + description: 'Number of item bought', + type: 'integer', + }, + }, + }, + early_game_items: { + description: 'Items bought in the first 10 min of the game, with cost at least 700', + type: 'object', + properties: { + item: { + description: 'Number of item bought', + type: 'integer', + }, + }, + }, + mid_game_items: { + description: 'Items bought between 10 and 25 min of the game, with cost at least 2000', + type: 'object', + properties: { + item: { + description: 'Number of item bought', + type: 'integer', + }, + }, + }, + late_game_items: { + description: 'Items bought at least 25 min after game started, with cost at least 4000', + type: 'object', + properties: { + item: { + description: 'Number of item bought', + type: 'integer', + }, + }, + }, + }, +}; diff --git a/routes/responses/HeroMatchupsResponse.js b/routes/responses/HeroMatchupsResponse.js new file mode 100644 index 000000000..eb2001e79 --- /dev/null +++ b/routes/responses/HeroMatchupsResponse.js @@ -0,0 +1,17 @@ +import { hero_id as _hero_id } from '../../properties/commonProperties'; + +export const HeroMatchupsResponse = { + title: 'HeroMatchupsResponse', + type: 'object', + properties: { + hero_id: _hero_id, + games_played: { + description: 'Number of games played', + type: 'integer', + }, + wins: { + description: 'Number of games won', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/HeroObjectResponse.js b/routes/responses/HeroObjectResponse.js new file mode 100644 index 000000000..f09006eab --- /dev/null +++ b/routes/responses/HeroObjectResponse.js @@ -0,0 +1,27 @@ +import { hero_id, hero_command_name, hero_name } from '../../properties/commonProperties'; + +export const HeroObjectResponse = { + title: 'HeroObjectResponse', + type: 'object', + properties: { + id: hero_id, + name: hero_command_name, + localized_name: hero_name, + primary_attr: { + description: "Hero primary shorthand attribute name, e.g. 'agi'", + type: 'string', + }, + attack_type: { + description: "Hero attack type, either 'Melee' or 'Ranged'", + type: 'string', + }, + roles: { + type: 'array', + items: { + description: "A hero's role in the game", + type: 'string', + }, + }, + }, + required: ['id'], +}; diff --git a/routes/responses/HeroStatsResponse.js b/routes/responses/HeroStatsResponse.js new file mode 100644 index 000000000..51c9c4bc1 --- /dev/null +++ b/routes/responses/HeroStatsResponse.js @@ -0,0 +1,228 @@ +import { hero_id as _hero_id, hero_command_name, hero_name } from '../../properties/commonProperties'; + +export const HeroStatsResponse = { + title: 'HeroStatsResponse', + type: 'object', + properties: { + id: _hero_id, + name: hero_command_name, + localized_name: hero_name, + primary_attr: { + description: 'primary_attr', + type: 'string', + }, + attack_type: { + description: 'attack_type', + type: 'string', + }, + roles: { + description: 'roles', + type: 'array', + items: { + type: 'string', + }, + }, + img: { + description: 'img', + type: 'string', + }, + icon: { + description: 'icon', + type: 'string', + }, + base_health: { + description: 'base_health', + type: 'integer', + }, + base_health_regen: { + description: 'base_health_regen', + type: 'number', + }, + base_mana: { + description: 'base_mana', + type: 'integer', + }, + base_mana_regen: { + description: 'base_mana_regen', + type: 'integer', + }, + base_armor: { + description: 'base_armor', + type: 'integer', + }, + base_mr: { + description: 'base_mr', + type: 'integer', + }, + base_attack_min: { + description: 'base_attack_min', + type: 'integer', + }, + base_attack_max: { + description: 'base_attack_max', + type: 'integer', + }, + base_str: { + description: 'base_str', + type: 'integer', + }, + base_agi: { + description: 'base_agi', + type: 'integer', + }, + base_int: { + description: 'base_int', + type: 'integer', + }, + str_gain: { + description: 'str_gain', + type: 'number', + }, + agi_gain: { + description: 'agi_gain', + type: 'number', + }, + int_gain: { + description: 'int_gain', + type: 'number', + }, + attack_range: { + description: 'attack_range', + type: 'integer', + }, + projectile_speed: { + description: 'projectile_speed', + type: 'integer', + }, + attack_rate: { + description: 'attack_rate', + type: 'number', + }, + base_attack_time: { + description: 'base_attack_time', + type: 'integer', + }, + attack_point: { + description: 'attack_point', + type: 'number', + }, + move_speed: { + description: 'move_speed', + type: 'integer', + }, + turn_rate: { + description: 'turn_rate', + type: 'number', + }, + cm_enabled: { + description: 'cm_enabled', + type: 'boolean', + }, + legs: { + description: 'legs', + type: 'integer', + }, + day_vision: { + description: 'day_vision', + type: 'integer', + }, + night_vision: { + description: 'night_vision', + type: 'integer', + }, + hero_id: _hero_id, + turbo_picks: { + description: 'Picks in Turbo mode this month', + type: 'integer', + }, + turbo_wins: { + description: 'Wins in Turbo mode this month', + type: 'integer', + }, + pro_ban: { + description: 'pro_ban', + type: 'integer', + }, + pro_win: { + description: 'pro_win', + type: 'integer', + }, + pro_pick: { + description: 'pro_pick', + type: 'integer', + }, + '1_pick': { + description: 'Herald picks', + type: 'integer', + }, + '1_win': { + description: 'Herald wins', + type: 'integer', + }, + '2_pick': { + description: 'Guardian picks', + type: 'integer', + }, + '2_win': { + description: 'Guardian wins', + type: 'integer', + }, + '3_pick': { + description: 'Crusader picks', + type: 'integer', + }, + '3_win': { + description: 'Crusader wins', + type: 'integer', + }, + '4_pick': { + description: 'Archon picks', + type: 'integer', + }, + '4_win': { + description: 'Archon wins', + type: 'integer', + }, + '5_pick': { + description: 'Legend picks', + type: 'integer', + }, + '5_win': { + description: 'Legend wins', + type: 'integer', + }, + '6_pick': { + description: 'Ancient picks', + type: 'integer', + }, + '6_win': { + description: 'Ancient wins', + type: 'integer', + }, + '7_pick': { + description: 'Divine picks', + type: 'integer', + }, + '7_win': { + description: 'Divine wins', + type: 'integer', + }, + '8_pick': { + description: 'Immortal picks', + type: 'integer', + }, + '8_win': { + description: 'Immortal wins', + type: 'integer', + }, + // TODO: Should the following remain in the response? + null_pick: { + description: 'null_pick', + type: 'integer', + }, + null_win: { + description: 'null_win', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/LeagueObjectResponse.js b/routes/responses/LeagueObjectResponse.js new file mode 100644 index 000000000..ff4ed4920 --- /dev/null +++ b/routes/responses/LeagueObjectResponse.js @@ -0,0 +1,25 @@ +import { league_name } from '../../properties/commonProperties'; + +export const LeagueObjectResponse = { + title: 'LeagueObjectResponse', + type: 'object', + properties: { + leagueid: { + description: 'leagueid', + type: 'integer', + }, + ticket: { + description: 'ticket', + type: 'string', + }, + banner: { + description: 'banner', + type: 'string', + }, + tier: { + description: 'tier', + type: 'string', + }, + name: league_name, + }, +}; diff --git a/routes/responses/MatchObjectResponse.js b/routes/responses/MatchObjectResponse.js new file mode 100644 index 000000000..fed6e0db3 --- /dev/null +++ b/routes/responses/MatchObjectResponse.js @@ -0,0 +1,50 @@ +import { match_id as _match_id, duration as _duration, start_time as _start_time, radiant_score as _radiant_score, dire_score as _dire_score, radiant_win as _radiant_win } from '../../properties/commonProperties'; + +export const MatchObjectResponse = { + title: 'MatchObjectResponse', + type: 'object', + properties: { + match_id: _match_id, + duration: _duration, + start_time: _start_time, + radiant_team_id: { + description: "The Radiant's team_id", + type: 'integer', + }, + radiant_name: { + description: "The Radiant's team name", + type: 'string', + }, + dire_team_id: { + description: "The Dire's team_id", + type: 'integer', + }, + dire_name: { + description: "The Dire's team name", + type: 'string', + }, + leagueid: { + description: 'Identifier for the league the match took place in', + type: 'integer', + }, + league_name: { + description: 'Name of league the match took place in', + type: 'string', + }, + series_id: { + description: 'Identifier for the series of the match', + type: 'integer', + }, + series_type: { + description: 'Type of series the match was', + type: 'integer', + }, + radiant_score: _radiant_score, + dire_score: _dire_score, + radiant_win: _radiant_win, + radiant: { + description: 'Whether the team/player/hero was on Radiant', + type: 'boolean', + }, + }, +}; diff --git a/routes/responses/MatchResponse.js b/routes/responses/MatchResponse.js new file mode 100644 index 000000000..ec34a8486 --- /dev/null +++ b/routes/responses/MatchResponse.js @@ -0,0 +1,886 @@ +import { match_id as _match_id, player_slot as _player_slot, dire_score as _dire_score, hero_id as _hero_id, duration as _duration, radiant_score as _radiant_score, radiant_win as _radiant_win, start_time as _start_time, account_id as _account_id, persona_name, general_name } from '../../properties/commonProperties'; + +export const MatchResponse = { + title: 'MatchResponse', + type: 'object', + properties: { + match_id: _match_id, + barracks_status_dire: { + description: 'Bitmask. An integer that represents a binary of which barracks are still standing. 63 would mean all barracks still stand at the end of the game.', + type: 'integer', + }, + barracks_status_radiant: { + description: 'Bitmask. An integer that represents a binary of which barracks are still standing. 63 would mean all barracks still stand at the end of the game.', + type: 'integer', + }, + chat: { + description: 'Array containing information on the chat of the game', + type: 'array', + items: { + type: 'object', + properties: { + time: { + description: 'Time in seconds at which the message was said', + type: 'integer', + }, + unit: { + description: 'Name of the player who sent the message', + type: 'string', + }, + key: { + description: 'The message the player sent', + type: 'string', + }, + slot: { + description: 'slot', + type: 'integer', + }, + player_slot: _player_slot, + }, + }, + }, + cluster: { + description: 'cluster', + type: 'integer', + }, + cosmetics: { + description: 'cosmetics', + type: 'object', + additionalProperties: { + type: 'integer', + }, + }, + dire_score: _dire_score, + draft_timings: { + description: 'draft_timings', + type: 'array', + items: { + description: 'draft_stage', + type: 'object', + properties: { + order: { + description: 'order', + type: 'integer', + }, + pick: { + description: 'pick', + type: 'boolean', + }, + active_team: { + description: 'active_team', + type: 'integer', + }, + hero_id: _hero_id, + player_slot: _player_slot, + extra_time: { + description: 'extra_time', + type: 'integer', + }, + total_time_taken: { + description: 'total_time_taken', + type: 'integer', + }, + }, + }, + }, + duration: _duration, + engine: { + description: 'engine', + type: 'integer', + }, + first_blood_time: { + description: 'Time in seconds at which first blood occurred', + type: 'integer', + }, + game_mode: { + description: 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', + type: 'integer', + }, + human_players: { + description: 'Number of human players in the game', + type: 'integer', + }, + leagueid: { + description: 'leagueid', + type: 'integer', + }, + lobby_type: { + description: 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', + type: 'integer', + }, + match_seq_num: { + description: 'match_seq_num', + type: 'integer', + }, + negative_votes: { + description: 'Number of negative votes the replay received in the in-game client', + type: 'integer', + }, + objectives: { + description: 'objectives', + type: 'array', + items: { + type: 'object', + }, + }, + picks_bans: { + description: 'Array containing information on the draft. Each item contains a boolean relating to whether the choice is a pick or a ban, the hero ID, the team the picked or banned it, and the order.', + type: 'array', + items: { + type: 'object', + properties: { + is_pick: { + description: 'Boolean indicating whether the choice is a pick or a ban', + type: 'boolean', + }, + hero_id: _hero_id, + team: { + description: 'The team that picked or banned the hero', + type: 'integer', + }, + order: { + description: 'The order of the pick or ban', + type: 'integer', + }, + }, + }, + }, + positive_votes: { + description: 'Number of positive votes the replay received in the in-game client', + type: 'integer', + }, + radiant_gold_adv: { + description: 'Array of the Radiant gold advantage at each minute in the game. A negative number means that Radiant is behind, and thus it is their gold disadvantage. ', + type: 'array', + items: { + type: 'number', + }, + }, + radiant_score: _radiant_score, + radiant_win: _radiant_win, + radiant_xp_adv: { + description: 'Array of the Radiant experience advantage at each minute in the game. A negative number means that Radiant is behind, and thus it is their experience disadvantage. ', + type: 'array', + items: { + type: 'number', + }, + }, + start_time: _start_time, + teamfights: { + description: 'teamfights', + type: 'array', + items: { + type: 'object', + }, + nullable: true, + }, + tower_status_dire: { + description: 'Bitmask. An integer that represents a binary of which Dire towers are still standing.', + type: 'integer', + }, + tower_status_radiant: { + description: 'Bitmask. An integer that represents a binary of which Radiant towers are still standing.', + type: 'integer', + }, + version: { + description: 'Parse version, used internally by OpenDota', + type: 'integer', + }, + replay_salt: { + description: 'replay_salt', + type: 'integer', + }, + series_id: { + description: 'series_id', + type: 'integer', + }, + series_type: { + description: 'series_type', + type: 'integer', + }, + radiant_team: { + description: 'radiant_team', + type: 'object', + }, + dire_team: { + description: 'dire_team', + type: 'object', + }, + league: { + description: 'league', + type: 'object', + }, + skill: { + description: 'Skill bracket assigned by Valve (Normal, High, Very High)', + type: 'integer', + nullable: true, + }, + players: { + description: 'Array of information on individual players', + type: 'array', + items: { + description: 'player', + type: 'object', + properties: { + match_id: _match_id, + player_slot: _player_slot, + ability_upgrades_arr: { + description: 'An array describing how abilities were upgraded', + type: 'array', + items: { + type: 'integer', + }, + }, + ability_uses: { + description: 'Object containing information on how many times the played used their abilities', + type: 'object', + }, + ability_targets: { + description: 'Object containing information on who the player used their abilities on', + type: 'object', + }, + damage_targets: { + description: 'Object containing information on how and how much damage the player dealt to other heroes', + type: 'object', + }, + account_id: _account_id, + actions: { + description: 'Object containing information on how many and what type of actions the player issued to their hero', + type: 'object', + }, + additional_units: { + description: 'Object containing information on additional units the player had under their control', + type: 'array', + items: { + type: 'object', + }, + nullable: true, + }, + assists: { + description: 'Number of assists the player had', + type: 'integer', + }, + backpack_0: { + description: 'Item in backpack slot 0', + type: 'integer', + }, + backpack_1: { + description: 'Item in backpack slot 1', + type: 'integer', + }, + backpack_2: { + description: 'Item in backpack slot 2', + type: 'integer', + }, + buyback_log: { + description: 'Array containing information about buybacks', + type: 'array', + items: { + type: 'object', + properties: { + time: { + description: 'Time in seconds the buyback occurred', + type: 'integer', + }, + slot: { + description: 'slot', + type: 'integer', + }, + player_slot: _player_slot, + }, + }, + }, + camps_stacked: { + description: 'Number of camps stacked', + type: 'integer', + }, + connection_log: { + description: "Array containing information about the player's disconnections and reconnections", + type: 'array', + items: { + type: 'object', + properties: { + time: { + description: 'Game time in seconds the event ocurred', + type: 'integer', + }, + event: { + description: 'Event that occurred', + type: 'string', + }, + player_slot: _player_slot, + }, + }, + }, + creeps_stacked: { + description: 'Number of creeps stacked', + type: 'integer', + }, + damage: { + description: 'Object containing information about damage dealt by the player to different units', + type: 'object', + }, + damage_inflictor: { + description: "Object containing information about about the sources of this player's damage to heroes", + type: 'object', + }, + damage_inflictor_received: { + description: 'Object containing information about the sources of damage received by this player from heroes', + type: 'object', + }, + damage_taken: { + description: 'Object containing information about from whom the player took damage', + type: 'object', + }, + deaths: { + description: 'Number of deaths', + type: 'integer', + }, + denies: { + description: 'Number of denies', + type: 'integer', + }, + dn_t: { + description: 'Array containing number of denies at different times of the match', + type: 'array', + items: { + type: 'integer', + }, + }, + gold: { + description: 'Gold at the end of the game', + type: 'integer', + }, + gold_per_min: { + description: 'Gold Per Minute obtained by this player', + type: 'integer', + }, + gold_reasons: { + description: 'Object containing information on how the player gainined gold over the course of the match', + type: 'object', + }, + gold_spent: { + description: 'How much gold the player spent', + type: 'integer', + }, + gold_t: { + description: 'Array containing total gold at different times of the match', + type: 'array', + items: { + type: 'integer', + }, + }, + hero_damage: { + description: 'Hero Damage Dealt', + type: 'integer', + }, + hero_healing: { + description: 'Hero Healing Done', + type: 'integer', + }, + hero_hits: { + description: 'Object containing information on how many ticks of damages the hero inflicted with different spells and damage inflictors', + type: 'object', + }, + hero_id: _hero_id, + item_0: { + description: "Item in the player's first slot", + type: 'integer', + }, + item_1: { + description: "Item in the player's second slot", + type: 'integer', + }, + item_2: { + description: "Item in the player's third slot", + type: 'integer', + }, + item_3: { + description: "Item in the player's fourth slot", + type: 'integer', + }, + item_4: { + description: "Item in the player's fifth slot", + type: 'integer', + }, + item_5: { + description: "Item in the player's sixth slot", + type: 'integer', + }, + item_uses: { + description: 'Object containing information about how many times a player used items', + type: 'object', + }, + kill_streaks: { + description: "Object containing information about the player's killstreaks", + type: 'object', + }, + killed: { + description: 'Object containing information about what units the player killed', + type: 'object', + }, + killed_by: { + description: 'Object containing information about who killed the player', + type: 'object', + }, + kills: { + description: 'Number of kills', + type: 'integer', + }, + kills_log: { + description: 'Array containing information on which hero the player killed at what time', + type: 'array', + items: { + type: 'object', + properties: { + time: { + description: 'Time in seconds the player killed the hero', + type: 'integer', + }, + key: { + description: 'Hero killed', + type: 'string', + }, + }, + }, + }, + lane_pos: { + description: 'Object containing information on lane position', + type: 'object', + }, + last_hits: { + description: 'Number of last hits', + type: 'integer', + }, + leaver_status: { + description: "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", + type: 'integer', + }, + level: { + description: 'Level at the end of the game', + type: 'integer', + }, + lh_t: { + description: 'Array describing last hits at each minute in the game', + type: 'array', + items: { + type: 'integer', + }, + }, + life_state: { + description: 'life_state', + type: 'object', + }, + max_hero_hit: { + description: 'Object with information on the highest damage instance the player inflicted', + type: 'object', + }, + multi_kills: { + description: 'Object with information on the number of the number of multikills the player had', + type: 'object', + }, + obs: { + description: 'Object with information on where the player placed observer wards. The location takes the form (outer number, inner number) and are from ~64-192.', + type: 'object', + }, + obs_left_log: { + description: 'obs_left_log', + type: 'array', + items: { + type: 'object', + }, + }, + obs_log: { + description: 'Object containing information on when and where the player placed observer wards', + type: 'array', + items: { + type: 'object', + }, + }, + obs_placed: { + description: 'Total number of observer wards placed', + type: 'integer', + }, + party_id: { + description: 'party_id', + type: 'integer', + }, + permanent_buffs: { + description: 'Array describing permanent buffs the player had at the end of the game. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/permanent_buffs.json', + type: 'array', + items: { + type: 'object', + }, + }, + pings: { + description: 'Total number of pings', + type: 'integer', + }, + purchase: { + description: 'Object containing information on the items the player purchased', + type: 'object', + }, + purchase_log: { + description: 'Object containing information on when items were purchased', + type: 'array', + items: { + type: 'object', + properties: { + time: { + description: 'Time in seconds the item was bought', + type: 'integer', + }, + key: { + description: 'String item ID', + type: 'string', + }, + charges: { + description: 'Integer amount of charges', + type: 'integer', + }, + }, + }, + }, + rune_pickups: { + description: 'Number of runes picked up', + type: 'integer', + }, + runes: { + description: 'Object with information about which runes the player picked up', + type: 'object', + additionalProperties: { + type: 'integer', + }, + }, + runes_log: { + description: 'Array with information on when runes were picked up', + type: 'array', + items: { + type: 'object', + properties: { + time: { + description: 'Time in seconds rune picked up', + type: 'integer', + }, + key: { + description: 'key', + type: 'integer', + }, + }, + }, + }, + sen: { + description: 'Object with information on where sentries were placed. The location takes the form (outer number, inner number) and are from ~64-192.', + type: 'object', + }, + sen_left_log: { + description: 'Array containing information on when and where the player placed sentries', + type: 'array', + items: { + type: 'object', + }, + }, + sen_log: { + description: 'Array with information on when and where sentries were placed by the player', + type: 'array', + items: { + type: 'object', + }, + }, + sen_placed: { + description: 'How many sentries were placed by the player', + type: 'integer', + }, + stuns: { + description: 'Total stun duration of all stuns by the player', + type: 'number', + }, + times: { + description: 'Time in seconds corresponding to the time of entries of other arrays in the match.', + type: 'array', + items: { + type: 'integer', + }, + }, + tower_damage: { + description: 'Total tower damage done by the player', + type: 'integer', + }, + xp_per_min: { + description: 'Experience Per Minute obtained by the player', + type: 'integer', + }, + xp_reasons: { + description: "Object containing information on the sources of this player's experience", + type: 'object', + }, + xp_t: { + description: 'Experience at each minute of the game', + type: 'array', + items: { + type: 'integer', + }, + }, + personaname: persona_name, + name: general_name, + last_login: { + description: "Time of player's last login", + type: 'string', + format: 'date-time', + nullable: true, + }, + radiant_win: _radiant_win, + start_time: _start_time, + duration: _duration, + cluster: { + description: 'cluster', + type: 'integer', + }, + lobby_type: { + description: 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', + type: 'integer', + }, + game_mode: { + description: 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', + type: 'integer', + }, + patch: { + description: 'Integer representing the patch the game was played on', + type: 'integer', + }, + region: { + description: 'Integer corresponding to the region the game was played on', + type: 'integer', + }, + isRadiant: { + description: 'Boolean for whether or not the player is on Radiant', + type: 'boolean', + }, + win: { + description: 'Binary integer representing whether or not the player won', + type: 'integer', + }, + lose: { + description: 'Binary integer representing whether or not the player lost', + type: 'integer', + }, + total_gold: { + description: 'Total gold at the end of the game', + type: 'integer', + }, + total_xp: { + description: 'Total experience at the end of the game', + type: 'integer', + }, + kills_per_min: { + description: 'Number of kills per minute', + type: 'number', + }, + kda: { + description: 'kda', + type: 'number', + }, + abandons: { + description: 'abandons', + type: 'integer', + }, + neutral_kills: { + description: 'Total number of neutral creeps killed', + type: 'integer', + }, + tower_kills: { + description: 'Total number of tower kills the player had', + type: 'integer', + }, + courier_kills: { + description: 'Total number of courier kills the player had', + type: 'integer', + }, + lane_kills: { + description: 'Total number of lane creeps killed by the player', + type: 'integer', + }, + hero_kills: { + description: 'Total number of heroes killed by the player', + type: 'integer', + }, + observer_kills: { + description: 'Total number of observer wards killed by the player', + type: 'integer', + }, + sentry_kills: { + description: 'Total number of sentry wards killed by the player', + type: 'integer', + }, + roshan_kills: { + description: 'Total number of roshan kills (last hit on roshan) the player had', + type: 'integer', + }, + necronomicon_kills: { + description: 'Total number of Necronomicon creeps killed by the player', + type: 'integer', + }, + ancient_kills: { + description: 'Total number of Ancient creeps killed by the player', + type: 'integer', + }, + buyback_count: { + description: 'Total number of buyback the player used', + type: 'integer', + }, + observer_uses: { + description: 'Number of observer wards used', + type: 'integer', + }, + sentry_uses: { + description: 'Number of sentry wards used', + type: 'integer', + }, + lane_efficiency: { + description: 'lane_efficiency', + type: 'number', + }, + lane_efficiency_pct: { + description: 'lane_efficiency_pct', + type: 'number', + }, + lane: { + description: 'Integer referring to which lane the hero laned in', + type: 'integer', + nullable: true, + }, + lane_role: { + description: 'lane_role', + type: 'integer', + nullable: true, + }, + is_roaming: { + description: 'Boolean referring to whether or not the player roamed', + type: 'boolean', + nullable: true, + }, + purchase_time: { + description: 'Object with information on when the player last purchased an item', + type: 'object', + }, + first_purchase_time: { + description: 'Object with information on when the player first puchased an item', + type: 'object', + }, + item_win: { + description: 'Object with information on whether or not the item won', + type: 'object', + }, + item_usage: { + description: 'Object containing binary integers the tell whether the item was purchased by the player (note: this is always 1)', + type: 'object', + }, + purchase_tpscroll: { + description: 'Total number of TP scrolls purchased by the player', + type: 'integer', + }, + actions_per_min: { + description: 'Actions per minute', + type: 'integer', + }, + life_state_dead: { + description: 'life_state_dead', + type: 'integer', + }, + rank_tier: { + description: 'The rank tier of the player. Tens place indicates rank, ones place indicates stars.', + type: 'integer', + }, + cosmetics: { + description: 'cosmetics', + type: 'array', + items: { + type: 'object', + properties: { + item_id: { + type: 'integer', + }, + name: general_name, + prefab: { + type: 'string', + }, + creation_date: { + type: 'string', + format: 'date-time', + nullable: true, + }, + image_inventory: { + type: 'string', + nullable: true, + }, + image_path: { + type: 'string', + nullable: true, + }, + item_description: { + type: 'string', + nullable: true, + }, + item_name: { + type: 'string', + }, + item_rarity: { + type: 'string', + nullable: true, + }, + item_type_name: { + type: 'string', + nullable: true, + }, + used_by_heroes: { + type: 'string', + nullable: true, + }, + }, + }, + }, + benchmarks: { + description: 'Object containing information on certain benchmarks like GPM, XPM, KDA, tower damage, etc', + type: 'object', + }, + }, + }, + }, + patch: { + description: 'Information on the patch version the game is played on', + type: 'integer', + }, + region: { + description: 'Integer corresponding to the region the game was played on', + type: 'integer', + }, + all_word_counts: { + description: "Word counts of the all chat messages in the player's games", + type: 'object', + }, + my_word_counts: { + description: "Word counts of the player's all chat messages", + type: 'object', + }, + throw: { + description: "Maximum gold advantage of the player's team if they lost the match", + type: 'integer', + }, + comeback: { + description: "Maximum gold disadvantage of the player's team if they won the match", + type: 'integer', + }, + loss: { + description: "Maximum gold disadvantage of the player's team if they lost the match", + type: 'integer', + }, + win: { + description: "Maximum gold advantage of the player's team if they won the match", + type: 'integer', + }, + replay_url: { + description: 'replay_url', + type: 'string', + }, + }, +}; diff --git a/routes/responses/MetadataResponse.js b/routes/responses/MetadataResponse.js new file mode 100644 index 000000000..04f875566 --- /dev/null +++ b/routes/responses/MetadataResponse.js @@ -0,0 +1,11 @@ +export const MetadataResponse = { + title: 'MetadataResponse', + type: 'object', + properties: { + banner: { + description: 'banner', + type: 'object', + nullable: true, + }, + }, +}; diff --git a/routes/responses/ParsedMatchesResponse.js b/routes/responses/ParsedMatchesResponse.js new file mode 100644 index 000000000..9b23d521f --- /dev/null +++ b/routes/responses/ParsedMatchesResponse.js @@ -0,0 +1,9 @@ +import { match_id as _match_id } from '../../properties/commonProperties'; + +export const ParsedMatchesResponse = { + title: 'ParsedMatchesResponse', + type: 'object', + properties: { + match_id: _match_id, + }, +}; diff --git a/routes/responses/PlayerCountsResponse.js b/routes/responses/PlayerCountsResponse.js new file mode 100644 index 000000000..4c407aab3 --- /dev/null +++ b/routes/responses/PlayerCountsResponse.js @@ -0,0 +1,32 @@ +import commonProperties from '../../properties/commonProperties'; + +export const PlayerCountsResponse = { + title: 'PlayerCountsResponse', + type: 'object', + properties: { + leaver_status: { + description: "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", + type: 'object', + }, + game_mode: { + description: 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', + type: 'object', + }, + lobby_type: { + description: 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', + type: 'object', + }, + lane_role: { + description: 'lane_role', + type: 'object', + }, + region: { + description: 'Integer corresponding to the region the game was played on', + type: 'object', + }, + patch: { + description: 'patch', + type: 'object', + }, + }, +}; diff --git a/routes/responses/PlayerHeroesResponse.js b/routes/responses/PlayerHeroesResponse.js new file mode 100644 index 000000000..bd30f3bf3 --- /dev/null +++ b/routes/responses/PlayerHeroesResponse.js @@ -0,0 +1,38 @@ +import { hero_id as _hero_id } from '../../properties/commonProperties'; + +export const PlayerHeroesResponse = { + title: 'PlayerHeroesResponse', + description: 'hero', + type: 'object', + properties: { + hero_id: _hero_id, + last_played: { + description: 'last_played', + type: 'integer', + }, + games: { + description: 'games', + type: 'integer', + }, + win: { + description: 'win', + type: 'integer', + }, + with_games: { + description: 'with_games', + type: 'integer', + }, + with_win: { + description: 'with_win', + type: 'integer', + }, + against_games: { + description: 'against_games', + type: 'integer', + }, + against_win: { + description: 'against_win', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/PlayerMatchesResponse.js b/routes/responses/PlayerMatchesResponse.js new file mode 100644 index 000000000..d7b29fc6e --- /dev/null +++ b/routes/responses/PlayerMatchesResponse.js @@ -0,0 +1,59 @@ +import { match_id as _match_id, player_slot as _player_slot, radiant_win as _radiant_win, duration as _duration, hero_id as _hero_id, start_time as _start_time } from '../../properties/commonProperties'; + +export const PlayerMatchesResponse = { + title: 'PlayerMatchesResponse', + description: 'Object containing information on the match', + type: 'object', + properties: { + match_id: _match_id, + player_slot: _player_slot, + radiant_win: _radiant_win, + duration: _duration, + game_mode: { + description: 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', + type: 'integer', + }, + lobby_type: { + description: 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', + type: 'integer', + }, + hero_id: _hero_id, + start_time: _start_time, + version: { + description: 'version', + type: 'integer', + nullable: true, + }, + kills: { + description: 'Total kills the player had at the end of the game', + type: 'integer', + }, + deaths: { + description: 'Total deaths the player had at the end of the game', + type: 'integer', + }, + assists: { + description: 'Total assists the player had at the end of the game', + type: 'integer', + }, + skill: { + description: 'Skill bracket assigned by Valve (Normal, High, Very High)', + type: 'integer', + nullable: true, + }, + average_rank: { + description: 'Average rank of players with public match data', + type: 'integer', + nullable: true, + }, + leaver_status: { + description: "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", + type: 'integer', + }, + party_size: { + description: "Size of the player's party", + type: 'integer', + nullable: true, + }, + }, +}; diff --git a/routes/responses/PlayerObjectResponse.js b/routes/responses/PlayerObjectResponse.js new file mode 100644 index 000000000..0adcfd50b --- /dev/null +++ b/routes/responses/PlayerObjectResponse.js @@ -0,0 +1,85 @@ +import { account_id as _account_id, persona_name, team_name as _team_name } from '../../properties/commonProperties'; + +export const PlayerObjectResponse = { + title: 'PlayerObjectResponse', + type: 'object', + properties: { + account_id: _account_id, + steamid: { + description: "Player's steam identifier", + type: 'string', + }, + avatar: { + description: 'Steam picture URL (small picture)', + type: 'string', + }, + avatarmedium: { + description: 'Steam picture URL (medium picture)', + type: 'string', + }, + avatarfull: { + description: 'Steam picture URL (full picture)', + type: 'string', + }, + profileurl: { + description: 'Steam profile URL', + type: 'string', + }, + personaname: persona_name, + last_login: { + description: 'Date and time of last login to OpenDota', + type: 'string', + format: 'date-time', + }, + full_history_time: { + description: "Date and time of last request to refresh player's match history", + type: 'string', + format: 'date-time', + }, + cheese: { + description: 'Amount of dollars the player has donated to OpenDota', + type: 'integer', + }, + fh_unavailable: { + description: "Whether the refresh of player' match history failed", + type: 'boolean', + }, + loccountrycode: { + description: "Player's country identifier, e.g. US", + type: 'string', + }, + name: { + description: "Verified player name, e.g. 'Miracle-'", + type: 'string', + }, + country_code: { + description: "Player's country code", + type: 'string', + }, + fantasy_role: { + description: "Player's ingame role (core: 1 or support: 2)", + type: 'integer', + }, + team_id: { + description: "Player's team identifier", + type: 'integer', + }, + team_name: _team_name, + team_tag: { + description: "Player's team shorthand tag, e.g. 'EG'", + type: 'string', + }, + is_locked: { + description: 'Whether the roster lock is active', + type: 'boolean', + }, + is_pro: { + description: 'Whether the player is professional or not', + type: 'boolean', + }, + locked_until: { + description: 'When the roster lock will end', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/PlayerPeersResponse.js b/routes/responses/PlayerPeersResponse.js new file mode 100644 index 000000000..7fbe35965 --- /dev/null +++ b/routes/responses/PlayerPeersResponse.js @@ -0,0 +1,70 @@ +import { account_id as _account_id, persona_name, general_name } from '../../properties/commonProperties'; + +export const PlayerPeersResponse = { + title: 'PlayerPeersResponse', + type: 'object', + properties: { + account_id: _account_id, + last_played: { + description: 'last_played', + type: 'integer', + }, + win: { + description: 'win', + type: 'integer', + }, + games: { + description: 'games', + type: 'integer', + }, + with_win: { + description: 'with_win', + type: 'integer', + }, + with_games: { + description: 'with_games', + type: 'integer', + }, + against_win: { + description: 'against_win', + type: 'integer', + }, + against_games: { + description: 'against_games', + type: 'integer', + }, + with_gpm_sum: { + description: 'with_gpm_sum', + type: 'integer', + }, + with_xpm_sum: { + description: 'with_xpm_sum', + type: 'integer', + }, + personaname: persona_name, + name: general_name, + is_contributor: { + description: 'is_contributor', + type: 'boolean', + }, + is_subscriber: { + description: 'is_subscriber', + type: 'boolean', + }, + last_login: { + description: 'last_login', + type: 'string', + nullable: true, + }, + avatar: { + description: 'avatar', + type: 'string', + nullable: true, + }, + avatarfull: { + description: 'avatarfull', + type: 'string', + nullable: true, + }, + }, +}; diff --git a/routes/responses/PlayerProsResponse.js b/routes/responses/PlayerProsResponse.js new file mode 100644 index 000000000..e54dd0399 --- /dev/null +++ b/routes/responses/PlayerProsResponse.js @@ -0,0 +1,132 @@ +import { account_id as _account_id, general_name, team_name as _team_name } from '../../properties/commonProperties'; + +export const PlayerProsResponse = { + title: 'PlayerProsResponse', + type: 'object', + properties: { + account_id: _account_id, + name: general_name, + country_code: { + description: 'country_code', + type: 'string', + }, + fantasy_role: { + description: 'fantasy_role', + type: 'integer', + }, + team_id: { + description: 'team_id', + type: 'integer', + }, + team_name: _team_name, + team_tag: { + description: 'team_tag', + type: 'string', + nullable: true, + }, + is_locked: { + description: 'is_locked', + type: 'boolean', + }, + is_pro: { + description: 'is_pro', + type: 'boolean', + }, + locked_until: { + description: 'locked_until', + type: 'integer', + nullable: true, + }, + steamid: { + description: 'steamid', + type: 'string', + nullable: true, + }, + avatar: { + description: 'avatar', + type: 'string', + nullable: true, + }, + avatarmedium: { + description: 'avatarmedium', + type: 'string', + nullable: true, + }, + avatarfull: { + description: 'avatarfull', + type: 'string', + nullable: true, + }, + profileurl: { + description: 'profileurl', + type: 'string', + nullable: true, + }, + last_login: { + description: 'last_login', + type: 'string', + format: 'date-time', + nullable: true, + }, + full_history_time: { + description: 'full_history_time', + type: 'string', + format: 'date-time', + nullable: true, + }, + cheese: { + description: 'cheese', + type: 'integer', + nullable: true, + }, + fh_unavailable: { + description: 'fh_unavailable', + type: 'boolean', + nullable: true, + }, + loccountrycode: { + description: 'loccountrycode', + type: 'string', + nullable: true, + }, + last_played: { + description: 'last_played', + type: 'integer', + nullable: true, + }, + win: { + description: 'win', + type: 'integer', + }, + games: { + description: 'games', + type: 'integer', + }, + with_win: { + description: 'with_win', + type: 'integer', + }, + with_games: { + description: 'with_games', + type: 'integer', + }, + against_win: { + description: 'against_win', + type: 'integer', + }, + against_games: { + description: 'against_games', + type: 'integer', + }, + with_gpm_sum: { + description: 'with_gpm_sum', + type: 'integer', + nullable: true, + }, + with_xpm_sum: { + description: 'with_xpm_sum', + type: 'integer', + nullable: true, + }, + }, +}; diff --git a/routes/responses/PlayerRankingsResponse.js b/routes/responses/PlayerRankingsResponse.js new file mode 100644 index 000000000..fc0c70c39 --- /dev/null +++ b/routes/responses/PlayerRankingsResponse.js @@ -0,0 +1,21 @@ +import { hero_id as _hero_id } from '../../properties/commonProperties'; + +export const PlayerRankingsResponse = { + title: 'PlayerRankingsResponse', + type: 'object', + properties: { + hero_id: _hero_id, + score: { + description: 'Hero score', + type: 'number', + }, + percent_rank: { + description: 'percent_rank', + type: 'number', + }, + card: { + description: 'numeric_rank', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/PlayerRatingsResponse.js b/routes/responses/PlayerRatingsResponse.js new file mode 100644 index 000000000..968aa0ee0 --- /dev/null +++ b/routes/responses/PlayerRatingsResponse.js @@ -0,0 +1,23 @@ +import { account_id as _account_id, match_id as _match_id } from '../../properties/commonProperties'; + +export const PlayerRatingsResponse = { + title: 'PlayerRatingsResponse', + type: 'object', + properties: { + account_id: _account_id, + match_id: _match_id, + solo_competitive_rank: { + description: 'solo_competitive_rank', + type: 'integer', + nullable: true, + }, + competitive_rank: { + description: 'competitive_rank', + type: 'integer', + }, + time: { + description: 'time', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/PlayerRecentMatchesResponse.js b/routes/responses/PlayerRecentMatchesResponse.js new file mode 100644 index 000000000..d9b3e4a31 --- /dev/null +++ b/routes/responses/PlayerRecentMatchesResponse.js @@ -0,0 +1,98 @@ +import { match_id as _match_id, player_slot as _player_slot, radiant_win as _radiant_win, duration as _duration, hero_id as _hero_id, start_time as _start_time } from '../../properties/commonProperties'; + +export const PlayerRecentMatchesResponse = { + title: 'PlayerRecentMatchesResponse', + description: 'match', + type: 'object', + properties: { + match_id: _match_id, + player_slot: _player_slot, + radiant_win: _radiant_win, + duration: _duration, + game_mode: { + description: 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', + type: 'integer', + }, + lobby_type: { + description: 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', + type: 'integer', + }, + hero_id: _hero_id, + start_time: _start_time, + version: { + description: 'version', + type: 'integer', + nullable: true, + }, + kills: { + description: 'Total kills the player had at the end of the match', + type: 'integer', + }, + deaths: { + description: 'Total deaths the player had at the end of the match', + type: 'integer', + }, + assists: { + description: 'Total assists the player had at the end of the match', + type: 'integer', + }, + skill: { + description: 'Skill bracket assigned by Valve (Normal, High, Very High). If the skill is unknown, will return null.', + type: 'integer', + nullable: true, + }, + average_rank: { + description: 'Average rank of players with public match data', + type: 'integer', + nullable: true, + }, + xp_per_min: { + description: 'Experience Per Minute obtained by the player', + type: 'integer', + }, + gold_per_min: { + description: 'Average gold per minute of the player', + type: 'integer', + }, + hero_damage: { + description: 'Total hero damage to enemy heroes', + type: 'integer', + }, + hero_healing: { + description: 'Total healing of ally heroes', + type: 'integer', + }, + last_hits: { + description: 'Total last hits the player had at the end of the match', + type: 'integer', + }, + lane: { + description: 'Integer corresponding to which lane the player laned in for the match', + type: 'integer', + nullable: true, + }, + lane_role: { + description: 'lane_role', + type: 'integer', + nullable: true, + }, + is_roaming: { + description: 'Boolean describing whether or not the player roamed', + type: 'boolean', + nullable: true, + }, + cluster: { + description: 'cluster', + type: 'integer', + }, + leaver_status: { + description: "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", + type: 'integer', + }, + party_size: { + description: 'Size of the players party. If not in a party, will return 1.', + type: 'integer', + nullable: true, + }, + }, +}; diff --git a/routes/responses/PlayerTotalsResponse.js b/routes/responses/PlayerTotalsResponse.js new file mode 100644 index 000000000..759b8cc05 --- /dev/null +++ b/routes/responses/PlayerTotalsResponse.js @@ -0,0 +1,18 @@ +export const PlayerTotalsResponse = { + title: 'PlayerTotalsResponse', + type: 'object', + properties: { + field: { + description: 'field', + type: 'string', + }, + n: { + description: 'number', + type: 'integer', + }, + sum: { + description: 'sum', + type: 'number', + }, + }, +}; diff --git a/routes/responses/PlayerWardMapResponse.js b/routes/responses/PlayerWardMapResponse.js new file mode 100644 index 000000000..20db5695b --- /dev/null +++ b/routes/responses/PlayerWardMapResponse.js @@ -0,0 +1,14 @@ +export const PlayerWardMapResponse = { + title: 'PlayerWardMapResponse', + type: 'object', + properties: { + obs: { + description: 'obs', + type: 'object', + }, + sen: { + description: 'sen', + type: 'object', + }, + }, +}; diff --git a/routes/responses/PlayerWinLossResponse.js b/routes/responses/PlayerWinLossResponse.js new file mode 100644 index 000000000..f3e86e395 --- /dev/null +++ b/routes/responses/PlayerWinLossResponse.js @@ -0,0 +1,14 @@ +export const PlayerWinLossResponse = { + title: 'PlayerWinLossResponse', + type: 'object', + properties: { + win: { + description: 'Number of wins', + type: 'integer', + }, + lose: { + description: 'Number of loses', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/PlayerWordCloudResponse.js b/routes/responses/PlayerWordCloudResponse.js new file mode 100644 index 000000000..8d1f411e6 --- /dev/null +++ b/routes/responses/PlayerWordCloudResponse.js @@ -0,0 +1,14 @@ +export const PlayerWordCloudResponse = { + title: 'PlayerWordCloudResponse', + type: 'object', + properties: { + my_word_counts: { + description: 'my_word_counts', + type: 'object', + }, + all_word_counts: { + description: 'all_word_counts', + type: 'object', + }, + }, +}; diff --git a/routes/responses/PlayersByRankResponse.js b/routes/responses/PlayersByRankResponse.js new file mode 100644 index 000000000..94ff618bb --- /dev/null +++ b/routes/responses/PlayersByRankResponse.js @@ -0,0 +1,21 @@ +import { account_id as _account_id } from '../../properties/commonProperties'; + +export const PlayersByRankResponse = { + title: 'PlayersByRankResponse', + type: 'array', + items: { + type: 'object', + properties: { + account_id: _account_id, + rank_tier: { + description: 'Integer indicating the rank/medal of the player', + type: 'number', + }, + fh_unavailable: { + description: 'Indicates if we were unable to fetch full history for this player due to privacy settings', + type: 'boolean', + nullable: true, + }, + }, + }, +}; diff --git a/routes/responses/PlayersResponse.js b/routes/responses/PlayersResponse.js new file mode 100644 index 000000000..6653a2918 --- /dev/null +++ b/routes/responses/PlayersResponse.js @@ -0,0 +1,102 @@ +import { account_id as _account_id, persona_name, general_name } from '../../properties/commonProperties'; + +export const PlayersResponse = { + title: 'PlayerResponse', + type: 'object', + properties: { + solo_competitive_rank: { + description: 'solo_competitive_rank', + type: 'integer', + nullable: true, + }, + competitive_rank: { + description: 'competitive_rank', + type: 'integer', + nullable: true, + }, + rank_tier: { + description: 'rank_tier', + type: 'number', + nullable: true, + }, + leaderboard_rank: { + description: 'leaderboard_rank', + type: 'number', + nullable: true, + }, + mmr_estimate: { + description: 'mmr_estimate', + type: 'object', + properties: { + estimate: { + description: 'estimate', + type: 'number', + nullable: true, + }, + }, + }, + profile: { + description: 'profile', + type: 'object', + properties: { + account_id: _account_id, + personaname: persona_name, + name: general_name, + plus: { + description: 'Boolean indicating status of current Dota Plus subscription', + type: 'boolean', + }, + cheese: { + description: 'cheese', + type: 'integer', + nullable: true, + }, + steamid: { + description: 'steamid', + type: 'string', + nullable: true, + }, + avatar: { + description: 'avatar', + type: 'string', + nullable: true, + }, + avatarmedium: { + description: 'avatarmedium', + type: 'string', + nullable: true, + }, + avatarfull: { + description: 'avatarfull', + type: 'string', + nullable: true, + }, + profileurl: { + description: 'profileurl', + type: 'string', + nullable: true, + }, + last_login: { + description: 'last_login', + type: 'string', + nullable: true, + }, + loccountrycode: { + description: 'loccountrycode', + type: 'string', + nullable: true, + }, + is_contributor: { + description: 'Boolean indicating if the user contributed to the development of OpenDota', + type: 'boolean', + default: false, + }, + is_subscriber: { + description: 'Boolean indicating if the user subscribed to OpenDota', + type: 'boolean', + default: false, + }, + }, + }, + }, +}; diff --git a/routes/responses/PublicMatchesResponse.js b/routes/responses/PublicMatchesResponse.js new file mode 100644 index 000000000..5c0e9afcd --- /dev/null +++ b/routes/responses/PublicMatchesResponse.js @@ -0,0 +1,45 @@ +import { match_id as _match_id, radiant_win as _radiant_win, start_time as _start_time, duration as _duration } from '../../properties/commonProperties'; + +export const PublicMatchesResponse = { + title: 'PublicMatchesResponse', + type: 'object', + properties: { + match_id: _match_id, + match_seq_num: { + description: 'match_seq_num', + type: 'integer', + }, + radiant_win: _radiant_win, + start_time: _start_time, + duration: _duration, + avg_mmr: { + type: 'integer', + }, + num_mmr: { + type: 'integer', + }, + lobby_type: { + type: 'integer', + }, + game_mode: { + type: 'integer', + }, + avg_rank_tier: { + type: 'integer', + }, + num_rank_tier: { + type: 'integer', + }, + cluster: { + type: 'integer', + }, + radiant_team: { + description: 'radiant_team', + type: 'string', + }, + dire_team: { + description: 'dire_team', + type: 'string', + }, + }, +}; diff --git a/routes/responses/RankingsResponse.js b/routes/responses/RankingsResponse.js new file mode 100644 index 000000000..9b3f99840 --- /dev/null +++ b/routes/responses/RankingsResponse.js @@ -0,0 +1,80 @@ +import { hero_id as _hero_id, account_id as _account_id, persona_name } from '../../properties/commonProperties'; + +export const RankingsResponse = { + title: 'RankingsResponse', + type: 'object', + properties: { + hero_id: _hero_id, + rankings: { + description: 'rankings', + type: 'array', + items: { + type: 'object', + properties: { + account_id: _account_id, + score: { + description: 'Score', + type: 'number', + }, + steamid: { + description: 'steamid', + type: 'string', + nullable: true, + }, + avatar: { + description: 'avatar', + type: 'string', + nullable: true, + }, + avatarmedium: { + description: 'avatarmedium', + type: 'string', + nullable: true, + }, + avatarfull: { + description: 'avatarfull', + type: 'string', + nullable: true, + }, + profileurl: { + description: 'profileurl', + type: 'string', + nullable: true, + }, + personaname: persona_name, + last_login: { + description: 'last_login', + type: 'string', + format: 'date-time', + nullable: true, + }, + full_history_time: { + description: 'full_history_time', + type: 'string', + format: 'date-time', + }, + cheese: { + description: 'cheese', + type: 'integer', + nullable: true, + }, + fh_unavailable: { + description: 'fh_unavailable', + type: 'boolean', + nullable: true, + }, + loccountrycode: { + description: 'loccountrycode', + type: 'string', + nullable: true, + }, + rank_tier: { + description: 'rank_tier', + type: 'integer', + nullable: true, + }, + }, + }, + }, + }, +}; diff --git a/routes/responses/RecordsResponse.js b/routes/responses/RecordsResponse.js new file mode 100644 index 000000000..a7fd13bc4 --- /dev/null +++ b/routes/responses/RecordsResponse.js @@ -0,0 +1,15 @@ +import { match_id as _match_id, start_time as _start_time, hero_id as _hero_id } from '../../properties/commonProperties'; + +export const RecordsResponse = { + title: 'RecordsResponse', + type: 'object', + properties: { + match_id: _match_id, + start_time: _start_time, + hero_id: _hero_id, + score: { + description: 'Record score', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/ReplaysResponse.js b/routes/responses/ReplaysResponse.js new file mode 100644 index 000000000..15d7a9b53 --- /dev/null +++ b/routes/responses/ReplaysResponse.js @@ -0,0 +1,17 @@ +import { match_id as _match_id } from "../../properties/commonProperties"; + +export const ReplaysResponse = { + title: "ReplaysResponse", + type: "object", + properties: { + match_id: _match_id, + cluster: { + description: "cluster", + type: "integer", + }, + replay_salt: { + description: "replay_salt", + type: "integer", + }, + }, +}; diff --git a/routes/responses/ScenarioItemTimingsResponse.js b/routes/responses/ScenarioItemTimingsResponse.js new file mode 100644 index 000000000..9f46c3be3 --- /dev/null +++ b/routes/responses/ScenarioItemTimingsResponse.js @@ -0,0 +1,25 @@ +import { hero_id as _hero_id } from '../../properties/commonProperties'; + +export const ScenarioItemTimingsResponse = { + title: 'ScenarioItemTimingsResponse', + type: 'object', + properties: { + hero_id: _hero_id, + item: { + description: 'Purchased item', + type: 'string', + }, + time: { + description: 'Ingame time in seconds before the item was purchased', + type: 'integer', + }, + games: { + description: 'The number of games where the hero bought this item before this time', + type: 'string', + }, + wins: { + description: 'The number of games won where the hero bought this item before this time', + type: 'string', + }, + }, +}; diff --git a/routes/responses/ScenarioLaneRolesResponse.js b/routes/responses/ScenarioLaneRolesResponse.js new file mode 100644 index 000000000..9a12f6bf5 --- /dev/null +++ b/routes/responses/ScenarioLaneRolesResponse.js @@ -0,0 +1,25 @@ +import { hero_id as _hero_id } from '../../properties/commonProperties'; + +export const ScenarioLaneRolesResponse = { + title: 'ScenarioLaneRolesResponse', + type: 'object', + properties: { + hero_id: _hero_id, + lane_role: { + description: "The hero's lane role", + type: 'integer', + }, + time: { + description: 'Maximum game length in seconds', + type: 'integer', + }, + games: { + description: 'The number of games where the hero played in this lane role', + type: 'string', + }, + wins: { + description: 'The number of games won where the hero played in this lane role', + type: 'string', + }, + }, +}; diff --git a/routes/responses/ScenarioMiscResponse.js b/routes/responses/ScenarioMiscResponse.js new file mode 100644 index 000000000..1bb0ac9b1 --- /dev/null +++ b/routes/responses/ScenarioMiscResponse.js @@ -0,0 +1,26 @@ +export const ScenarioMiscResponse = { + title: 'ScenarioMiscResponse', + type: 'object', + properties: { + scenario: { + description: "The scenario's name or description", + type: 'string', + }, + is_radiant: { + description: 'Boolean indicating whether Radiant executed this scenario', + type: 'boolean', + }, + region: { + description: 'Region the game was played in', + type: 'integer', + }, + games: { + description: 'The number of games where this scenario occurred', + type: 'string', + }, + wins: { + description: 'The number of games won where this scenario occured', + type: 'string', + }, + }, +}; diff --git a/routes/responses/SchemaResponse.js b/routes/responses/SchemaResponse.js new file mode 100644 index 000000000..e3ddd53b0 --- /dev/null +++ b/routes/responses/SchemaResponse.js @@ -0,0 +1,18 @@ +export const SchemaResponse = { + title: 'SchemaResponse', + type: 'object', + properties: { + table_name: { + description: 'table_name', + type: 'string', + }, + column_name: { + description: 'column_name', + type: 'string', + }, + data_type: { + description: 'data_type', + type: 'string', + }, + }, +}; diff --git a/routes/responses/SearchResponse.js b/routes/responses/SearchResponse.js new file mode 100644 index 000000000..690bce3d1 --- /dev/null +++ b/routes/responses/SearchResponse.js @@ -0,0 +1,23 @@ +import { account_id as _account_id, persona_name } from '../../properties/commonProperties'; + +export const SearchResponse = { + title: 'SearchResponse', + type: 'object', + properties: { + account_id: _account_id, + avatarfull: { + description: 'avatarfull', + type: 'string', + nullable: true, + }, + personaname: persona_name, + last_match_time: { + description: 'last_match_time. May not be present or null.', + type: 'string', + }, + similarity: { + description: 'similarity', + type: 'number', + }, + }, +}; diff --git a/routes/responses/TeamHeroesResponse.js b/routes/responses/TeamHeroesResponse.js new file mode 100644 index 000000000..346ad18e0 --- /dev/null +++ b/routes/responses/TeamHeroesResponse.js @@ -0,0 +1,18 @@ +import { hero_id as _hero_id, hero_name } from '../../properties/commonProperties'; + +export const TeamHeroesResponse = { + title: 'TeamHeroesResponse', + type: 'object', + properties: { + hero_id: _hero_id, + name: hero_name, + games_played: { + description: 'Number of games played', + type: 'integer', + }, + wins: { + description: 'Number of wins', + type: 'integer', + }, + }, +}; diff --git a/routes/responses/TeamMatchObjectResponse.js b/routes/responses/TeamMatchObjectResponse.js new file mode 100644 index 000000000..f197271ac --- /dev/null +++ b/routes/responses/TeamMatchObjectResponse.js @@ -0,0 +1,43 @@ +import { match_id as _match_id, radiant_win as _radiant_win, radiant_score as _radiant_score, dire_score as _dire_score, duration as _duration, start_time as _start_time } from '../../properties/commonProperties'; + +export const TeamMatchObjectResponse = { + title: 'TeamMatchObjectResponse', + type: 'object', + properties: { + match_id: _match_id, + radiant: { + description: 'Whether the team/player/hero was on Radiant', + type: 'boolean', + }, + radiant_win: _radiant_win, + radiant_score: _radiant_score, + dire_score: _dire_score, + duration: _duration, + start_time: _start_time, + leagueid: { + description: 'Identifier for the league the match took place in', + type: 'integer', + }, + league_name: { + description: 'Name of league the match took place in', + type: 'string', + }, + cluster: { + description: 'cluster', + type: 'integer', + }, + opposing_team_id: { + description: 'Opposing team identifier', + type: 'integer', + }, + opposing_team_name: { + description: "Opposing team name, e.g. 'Evil Geniuses'", + type: 'string', + nullable: true, + }, + opposing_team_logo: { + description: 'Opposing team logo url', + type: 'string', + }, + }, +}; diff --git a/routes/responses/TeamObjectResponse.js b/routes/responses/TeamObjectResponse.js new file mode 100644 index 000000000..76592feed --- /dev/null +++ b/routes/responses/TeamObjectResponse.js @@ -0,0 +1,33 @@ +import { team_name } from '../../properties/commonProperties'; + +export const TeamObjectResponse = { + title: 'TeamObjectResponse', + type: 'object', + properties: { + team_id: { + description: "Team's identifier", + type: 'integer', + }, + rating: { + description: 'The Elo rating of the team', + type: 'number', + }, + wins: { + description: 'The number of games won by this team', + type: 'integer', + }, + losses: { + description: 'The number of losses by this team', + type: 'integer', + }, + last_match_time: { + description: 'The Unix timestamp of the last match played by this team', + type: 'integer', + }, + name: team_name, + tag: { + description: 'The team tag/abbreviation', + type: 'string', + }, + }, +}; diff --git a/routes/responses/TeamPlayersResponse.js b/routes/responses/TeamPlayersResponse.js new file mode 100644 index 000000000..193697cb4 --- /dev/null +++ b/routes/responses/TeamPlayersResponse.js @@ -0,0 +1,22 @@ +import { account_id as _account_id, general_name } from '../../properties/commonProperties'; + +export const TeamPlayersResponse = { + title: 'TeamPlayersResponse', + type: 'object', + properties: { + account_id: _account_id, + name: general_name, + games_played: { + description: 'Number of games played', + type: 'integer', + }, + wins: { + description: 'Number of wins', + type: 'integer', + }, + is_current_team_member: { + description: 'If this player is on the current roster', + type: 'boolean', + }, + }, +}; diff --git a/routes/responses/properties/commonProperties.js b/routes/responses/commonProperties.js similarity index 98% rename from routes/responses/properties/commonProperties.js rename to routes/responses/commonProperties.js index d4702ccd5..18792cb6f 100644 --- a/routes/responses/properties/commonProperties.js +++ b/routes/responses/commonProperties.js @@ -1,4 +1,4 @@ -module.exports = { +export default { hero_id: { description: 'The ID value of the hero played', type: 'integer', diff --git a/routes/responses/index.js b/routes/responses/index.js new file mode 100644 index 000000000..e0ed35901 --- /dev/null +++ b/routes/responses/index.js @@ -0,0 +1,46 @@ +// TODO import everything +/* +BenchmarksResponse.js +DistributionsResponse.js +HeroDurationsResponse.js +HeroItemPopularityResponse.js +HeroMatchupsResponse.js +HeroObjectResponse.js +HeroStatsResponse.js +LeagueObjectResponse.js +MatchObjectResponse.js +MatchResponse.js +MetadataResponse.js +ParsedMatchesResponse.js +PlayerCountsResponse.js +PlayerHeroesResponse.js +PlayerMatchesResponse.js +PlayerObjectResponse.js +PlayerPeersResponse.js +PlayerProsResponse.js +PlayerRankingsResponse.js +PlayerRatingsResponse.js +PlayerRecentMatchesResponse.js +PlayerTotalsResponse.js +PlayerWardMapResponse.js +PlayerWinLossResponse.js +PlayerWordCloudResponse.js +PlayersByRankResponse.js +PlayersResponse.js +PublicMatchesResponse.js +RankingsResponse.js +RecordsResponse.js +ReplaysResponse.js +ScenarioItemTimingsResponse.js +ScenarioLaneRolesResponse.js +ScenarioMiscResponse.js +SchemaResponse.js +SearchResponse.js +TeamHeroesResponse.js +TeamMatchObjectResponse.js +TeamObjectResponse.js +TeamPlayersResponse.js +*/ +export default { + +} \ No newline at end of file diff --git a/routes/responses/schemas/hero/HeroDurationsResponse.js b/routes/responses/schemas/hero/HeroDurationsResponse.js deleted file mode 100644 index 68ea142e6..000000000 --- a/routes/responses/schemas/hero/HeroDurationsResponse.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - HeroDurationsResponse: { - title: 'HeroDurationsResponse', - type: 'object', - properties: { - duration_bin: { - description: 'Lower bound of number of seconds the match lasted', - type: 'string', - }, - games_played: { - description: 'Number of games played', - type: 'integer', - }, - wins: { - description: 'Number of wins', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/hero/HeroItemPopularityResponse.js b/routes/responses/schemas/hero/HeroItemPopularityResponse.js deleted file mode 100644 index a5d64dd73..000000000 --- a/routes/responses/schemas/hero/HeroItemPopularityResponse.js +++ /dev/null @@ -1,51 +0,0 @@ -module.exports = { - HeroItemPopularityResponse: { - title: 'HeroItemPopularityResponse', - type: 'object', - properties: { - start_game_items: { - description: 'Items bought before game started', - type: 'object', - properties: { - item: { - description: 'Number of item bought', - type: 'integer', - }, - }, - }, - early_game_items: { - description: - 'Items bought in the first 10 min of the game, with cost at least 700', - type: 'object', - properties: { - item: { - description: 'Number of item bought', - type: 'integer', - }, - }, - }, - mid_game_items: { - description: - 'Items bought between 10 and 25 min of the game, with cost at least 2000', - type: 'object', - properties: { - item: { - description: 'Number of item bought', - type: 'integer', - }, - }, - }, - late_game_items: { - description: - 'Items bought at least 25 min after game started, with cost at least 4000', - type: 'object', - properties: { - item: { - description: 'Number of item bought', - type: 'integer', - }, - }, - }, - }, - }, -}; diff --git a/routes/responses/schemas/hero/HeroMatchupsResponse.js b/routes/responses/schemas/hero/HeroMatchupsResponse.js deleted file mode 100644 index fce077872..000000000 --- a/routes/responses/schemas/hero/HeroMatchupsResponse.js +++ /dev/null @@ -1,19 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - HeroMatchupsResponse: { - title: 'HeroMatchupsResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - games_played: { - description: 'Number of games played', - type: 'integer', - }, - wins: { - description: 'Number of games won', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/hero/HeroObjectResponse.js b/routes/responses/schemas/hero/HeroObjectResponse.js deleted file mode 100644 index 695177ae9..000000000 --- a/routes/responses/schemas/hero/HeroObjectResponse.js +++ /dev/null @@ -1,29 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - HeroObjectResponse: { - title: 'HeroObjectResponse', - type: 'object', - properties: { - id: commonProperties.hero_id, - name: commonProperties.hero_command_name, - localized_name: commonProperties.hero_name, - primary_attr: { - description: "Hero primary shorthand attribute name, e.g. 'agi'", - type: 'string', - }, - attack_type: { - description: "Hero attack type, either 'Melee' or 'Ranged'", - type: 'string', - }, - roles: { - type: 'array', - items: { - description: "A hero's role in the game", - type: 'string', - }, - }, - }, - required: ['id'], - }, -}; diff --git a/routes/responses/schemas/hero/HeroStatsResponse.js b/routes/responses/schemas/hero/HeroStatsResponse.js deleted file mode 100644 index 4f1a18cb6..000000000 --- a/routes/responses/schemas/hero/HeroStatsResponse.js +++ /dev/null @@ -1,230 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - HeroStatsResponse: { - title: 'HeroStatsResponse', - type: 'object', - properties: { - id: commonProperties.hero_id, - name: commonProperties.hero_command_name, - localized_name: commonProperties.hero_name, - primary_attr: { - description: 'primary_attr', - type: 'string', - }, - attack_type: { - description: 'attack_type', - type: 'string', - }, - roles: { - description: 'roles', - type: 'array', - items: { - type: 'string', - }, - }, - img: { - description: 'img', - type: 'string', - }, - icon: { - description: 'icon', - type: 'string', - }, - base_health: { - description: 'base_health', - type: 'integer', - }, - base_health_regen: { - description: 'base_health_regen', - type: 'number', - }, - base_mana: { - description: 'base_mana', - type: 'integer', - }, - base_mana_regen: { - description: 'base_mana_regen', - type: 'integer', - }, - base_armor: { - description: 'base_armor', - type: 'integer', - }, - base_mr: { - description: 'base_mr', - type: 'integer', - }, - base_attack_min: { - description: 'base_attack_min', - type: 'integer', - }, - base_attack_max: { - description: 'base_attack_max', - type: 'integer', - }, - base_str: { - description: 'base_str', - type: 'integer', - }, - base_agi: { - description: 'base_agi', - type: 'integer', - }, - base_int: { - description: 'base_int', - type: 'integer', - }, - str_gain: { - description: 'str_gain', - type: 'number', - }, - agi_gain: { - description: 'agi_gain', - type: 'number', - }, - int_gain: { - description: 'int_gain', - type: 'number', - }, - attack_range: { - description: 'attack_range', - type: 'integer', - }, - projectile_speed: { - description: 'projectile_speed', - type: 'integer', - }, - attack_rate: { - description: 'attack_rate', - type: 'number', - }, - base_attack_time: { - description: 'base_attack_time', - type: 'integer', - }, - attack_point: { - description: 'attack_point', - type: 'number', - }, - move_speed: { - description: 'move_speed', - type: 'integer', - }, - turn_rate: { - description: 'turn_rate', - type: 'number', - }, - cm_enabled: { - description: 'cm_enabled', - type: 'boolean', - }, - legs: { - description: 'legs', - type: 'integer', - }, - day_vision: { - description: 'day_vision', - type: 'integer', - }, - night_vision: { - description: 'night_vision', - type: 'integer', - }, - hero_id: commonProperties.hero_id, // TODO: Duplicate - turbo_picks: { - description: 'Picks in Turbo mode this month', - type: 'integer', - }, - turbo_wins: { - description: 'Wins in Turbo mode this month', - type: 'integer', - }, - pro_ban: { - description: 'pro_ban', - type: 'integer', - }, - pro_win: { - description: 'pro_win', - type: 'integer', - }, - pro_pick: { - description: 'pro_pick', - type: 'integer', - }, - '1_pick': { - description: 'Herald picks', - type: 'integer', - }, - '1_win': { - description: 'Herald wins', - type: 'integer', - }, - '2_pick': { - description: 'Guardian picks', - type: 'integer', - }, - '2_win': { - description: 'Guardian wins', - type: 'integer', - }, - '3_pick': { - description: 'Crusader picks', - type: 'integer', - }, - '3_win': { - description: 'Crusader wins', - type: 'integer', - }, - '4_pick': { - description: 'Archon picks', - type: 'integer', - }, - '4_win': { - description: 'Archon wins', - type: 'integer', - }, - '5_pick': { - description: 'Legend picks', - type: 'integer', - }, - '5_win': { - description: 'Legend wins', - type: 'integer', - }, - '6_pick': { - description: 'Ancient picks', - type: 'integer', - }, - '6_win': { - description: 'Ancient wins', - type: 'integer', - }, - '7_pick': { - description: 'Divine picks', - type: 'integer', - }, - '7_win': { - description: 'Divine wins', - type: 'integer', - }, - '8_pick': { - description: 'Immortal picks', - type: 'integer', - }, - '8_win': { - description: 'Immortal wins', - type: 'integer', - }, - // TODO: Should the following remain in the response? - null_pick: { - description: 'null_pick', - type: 'integer', - }, - null_win: { - description: 'null_win', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/importResponseSchemas.js b/routes/responses/schemas/importResponseSchemas.js deleted file mode 100644 index 55426d86a..000000000 --- a/routes/responses/schemas/importResponseSchemas.js +++ /dev/null @@ -1,26 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -// Recursive function to import all files in a directory and its subdirectories -function importAll(directory) { - let files = {}; - - // Read all files and subdirectories in the directory - fs.readdirSync(directory).forEach((item) => { - const itemPath = path.join(directory, item); - - if (fs.lstatSync(itemPath).isDirectory()) { - // If the item is a subdirectory, call this function with the subdirectory as the new starting point - files = { ...files, ...importAll(itemPath) }; - } else if (path.extname(item) === '.js' && itemPath !== __filename) { - // If the item is a JS file and not the current file, import it - const fileName = path.basename(item, '.js'); - files[fileName] = require(itemPath); - } - }); - - return files; -} - -// Import all files in the responses directory and its subdirectories -module.exports = importAll(__dirname); diff --git a/routes/responses/schemas/match/MatchObjectResponse.js b/routes/responses/schemas/match/MatchObjectResponse.js deleted file mode 100644 index 845b47f2c..000000000 --- a/routes/responses/schemas/match/MatchObjectResponse.js +++ /dev/null @@ -1,52 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - MatchObjectResponse: { - title: 'MatchObjectResponse', - type: 'object', - properties: { - match_id: commonProperties.match_id, - duration: commonProperties.duration, - start_time: commonProperties.start_time, - radiant_team_id: { - description: "The Radiant's team_id", - type: 'integer', - }, - radiant_name: { - description: "The Radiant's team name", - type: 'string', - }, - dire_team_id: { - description: "The Dire's team_id", - type: 'integer', - }, - dire_name: { - description: "The Dire's team name", - type: 'string', - }, - leagueid: { - description: 'Identifier for the league the match took place in', - type: 'integer', - }, - league_name: { - description: 'Name of league the match took place in', - type: 'string', - }, - series_id: { - description: 'Identifier for the series of the match', - type: 'integer', - }, - series_type: { - description: 'Type of series the match was', - type: 'integer', - }, - radiant_score: commonProperties.radiant_score, - dire_score: commonProperties.dire_score, - radiant_win: commonProperties.radiant_win, - radiant: { - description: 'Whether the team/player/hero was on Radiant', - type: 'boolean', - }, - }, - }, -}; diff --git a/routes/responses/schemas/match/MatchResponse.js b/routes/responses/schemas/match/MatchResponse.js deleted file mode 100644 index c44a77907..000000000 --- a/routes/responses/schemas/match/MatchResponse.js +++ /dev/null @@ -1,960 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - MatchResponse: { - title: 'MatchResponse', - type: 'object', - properties: { - match_id: commonProperties.match_id, - barracks_status_dire: { - description: - 'Bitmask. An integer that represents a binary of which barracks are still standing. 63 would mean all barracks still stand at the end of the game.', - type: 'integer', - }, - barracks_status_radiant: { - description: - 'Bitmask. An integer that represents a binary of which barracks are still standing. 63 would mean all barracks still stand at the end of the game.', - type: 'integer', - }, - chat: { - description: 'Array containing information on the chat of the game', - type: 'array', - items: { - type: 'object', - properties: { - time: { - description: 'Time in seconds at which the message was said', - type: 'integer', - }, - unit: { - description: 'Name of the player who sent the message', - type: 'string', - }, - key: { - description: 'The message the player sent', - type: 'string', - }, - slot: { - description: 'slot', - type: 'integer', - }, - player_slot: commonProperties.player_slot, - }, - }, - }, - cluster: { - description: 'cluster', - type: 'integer', - }, - cosmetics: { - description: 'cosmetics', - type: 'object', - additionalProperties: { - type: 'integer', - }, - }, - dire_score: commonProperties.dire_score, - draft_timings: { - description: 'draft_timings', - type: 'array', - items: { - description: 'draft_stage', - type: 'object', - properties: { - order: { - description: 'order', - type: 'integer', - }, - pick: { - description: 'pick', - type: 'boolean', - }, - active_team: { - description: 'active_team', - type: 'integer', - }, - hero_id: commonProperties.hero_id, - player_slot: commonProperties.player_slot, - extra_time: { - description: 'extra_time', - type: 'integer', - }, - total_time_taken: { - description: 'total_time_taken', - type: 'integer', - }, - }, - }, - }, - duration: commonProperties.duration, - engine: { - description: 'engine', - type: 'integer', - }, - first_blood_time: { - description: 'Time in seconds at which first blood occurred', - type: 'integer', - }, - game_mode: { - description: - 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', - type: 'integer', - }, - human_players: { - description: 'Number of human players in the game', - type: 'integer', - }, - leagueid: { - description: 'leagueid', - type: 'integer', - }, - lobby_type: { - description: - 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', - type: 'integer', - }, - match_seq_num: { - description: 'match_seq_num', - type: 'integer', - }, - negative_votes: { - description: - 'Number of negative votes the replay received in the in-game client', - type: 'integer', - }, - objectives: { - description: 'objectives', - type: 'array', - items: { - type: 'object', - }, - }, - picks_bans: { - description: - 'Array containing information on the draft. Each item contains a boolean relating to whether the choice is a pick or a ban, the hero ID, the team the picked or banned it, and the order.', - type: 'array', - items: { - type: 'object', - properties: { - is_pick: { - description: - 'Boolean indicating whether the choice is a pick or a ban', - type: 'boolean', - }, - hero_id: commonProperties.hero_id, - team: { - description: 'The team that picked or banned the hero', - type: 'integer', - }, - order: { - description: 'The order of the pick or ban', - type: 'integer', - }, - }, - }, - }, - positive_votes: { - description: - 'Number of positive votes the replay received in the in-game client', - type: 'integer', - }, - radiant_gold_adv: { - description: - 'Array of the Radiant gold advantage at each minute in the game. A negative number means that Radiant is behind, and thus it is their gold disadvantage. ', - type: 'array', - items: { - type: 'number', - }, - }, - radiant_score: commonProperties.radiant_score, - radiant_win: commonProperties.radiant_win, - radiant_xp_adv: { - description: - 'Array of the Radiant experience advantage at each minute in the game. A negative number means that Radiant is behind, and thus it is their experience disadvantage. ', - type: 'array', - items: { - type: 'number', - }, - }, - start_time: commonProperties.start_time, - teamfights: { - description: 'teamfights', - type: 'array', - items: { - type: 'object', - }, - nullable: true, - }, - tower_status_dire: { - description: - 'Bitmask. An integer that represents a binary of which Dire towers are still standing.', - type: 'integer', - }, - tower_status_radiant: { - description: - 'Bitmask. An integer that represents a binary of which Radiant towers are still standing.', - type: 'integer', - }, - version: { - description: 'Parse version, used internally by OpenDota', - type: 'integer', - }, - replay_salt: { - description: 'replay_salt', - type: 'integer', - }, - series_id: { - description: 'series_id', - type: 'integer', - }, - series_type: { - description: 'series_type', - type: 'integer', - }, - radiant_team: { - description: 'radiant_team', - type: 'object', - }, - dire_team: { - description: 'dire_team', - type: 'object', - }, - league: { - description: 'league', - type: 'object', - }, - skill: { - description: - 'Skill bracket assigned by Valve (Normal, High, Very High)', - type: 'integer', - nullable: true, - }, - players: { - description: 'Array of information on individual players', - type: 'array', - items: { - description: 'player', - type: 'object', - properties: { - match_id: commonProperties.match_id, - player_slot: commonProperties.player_slot, - ability_upgrades_arr: { - description: 'An array describing how abilities were upgraded', - type: 'array', - items: { - type: 'integer', - }, - }, - ability_uses: { - description: - 'Object containing information on how many times the played used their abilities', - type: 'object', - }, - ability_targets: { - description: - 'Object containing information on who the player used their abilities on', - type: 'object', - }, - damage_targets: { - description: - 'Object containing information on how and how much damage the player dealt to other heroes', - type: 'object', - }, - account_id: commonProperties.account_id, - actions: { - description: - 'Object containing information on how many and what type of actions the player issued to their hero', - type: 'object', - }, - additional_units: { - description: - 'Object containing information on additional units the player had under their control', - type: 'array', - items: { - type: 'object', - }, - nullable: true, - }, - assists: { - description: 'Number of assists the player had', - type: 'integer', - }, - backpack_0: { - description: 'Item in backpack slot 0', - type: 'integer', - }, - backpack_1: { - description: 'Item in backpack slot 1', - type: 'integer', - }, - backpack_2: { - description: 'Item in backpack slot 2', - type: 'integer', - }, - buyback_log: { - description: 'Array containing information about buybacks', - type: 'array', - items: { - type: 'object', - properties: { - time: { - description: 'Time in seconds the buyback occurred', - type: 'integer', - }, - slot: { - description: 'slot', - type: 'integer', - }, - player_slot: commonProperties.player_slot, - }, - }, - }, - camps_stacked: { - description: 'Number of camps stacked', - type: 'integer', - }, - connection_log: { - description: - "Array containing information about the player's disconnections and reconnections", - type: 'array', - items: { - type: 'object', - properties: { - time: { - description: 'Game time in seconds the event ocurred', - type: 'integer', - }, - event: { - description: 'Event that occurred', - type: 'string', - }, - player_slot: commonProperties.player_slot, - }, - }, - }, - creeps_stacked: { - description: 'Number of creeps stacked', - type: 'integer', - }, - damage: { - description: - 'Object containing information about damage dealt by the player to different units', - type: 'object', - }, - damage_inflictor: { - description: - "Object containing information about about the sources of this player's damage to heroes", - type: 'object', - }, - damage_inflictor_received: { - description: - 'Object containing information about the sources of damage received by this player from heroes', - type: 'object', - }, - damage_taken: { - description: - 'Object containing information about from whom the player took damage', - type: 'object', - }, - deaths: { - description: 'Number of deaths', - type: 'integer', - }, - denies: { - description: 'Number of denies', - type: 'integer', - }, - dn_t: { - description: - 'Array containing number of denies at different times of the match', - type: 'array', - items: { - type: 'integer', - }, - }, - gold: { - description: 'Gold at the end of the game', - type: 'integer', - }, - gold_per_min: { - description: 'Gold Per Minute obtained by this player', - type: 'integer', - }, - gold_reasons: { - description: - 'Object containing information on how the player gainined gold over the course of the match', - type: 'object', - }, - gold_spent: { - description: 'How much gold the player spent', - type: 'integer', - }, - gold_t: { - description: - 'Array containing total gold at different times of the match', - type: 'array', - items: { - type: 'integer', - }, - }, - hero_damage: { - description: 'Hero Damage Dealt', - type: 'integer', - }, - hero_healing: { - description: 'Hero Healing Done', - type: 'integer', - }, - hero_hits: { - description: - 'Object containing information on how many ticks of damages the hero inflicted with different spells and damage inflictors', - type: 'object', - }, - hero_id: commonProperties.hero_id, - item_0: { - description: "Item in the player's first slot", - type: 'integer', - }, - item_1: { - description: "Item in the player's second slot", - type: 'integer', - }, - item_2: { - description: "Item in the player's third slot", - type: 'integer', - }, - item_3: { - description: "Item in the player's fourth slot", - type: 'integer', - }, - item_4: { - description: "Item in the player's fifth slot", - type: 'integer', - }, - item_5: { - description: "Item in the player's sixth slot", - type: 'integer', - }, - item_uses: { - description: - 'Object containing information about how many times a player used items', - type: 'object', - }, - kill_streaks: { - description: - "Object containing information about the player's killstreaks", - type: 'object', - }, - killed: { - description: - 'Object containing information about what units the player killed', - type: 'object', - }, - killed_by: { - description: - 'Object containing information about who killed the player', - type: 'object', - }, - kills: { - description: 'Number of kills', - type: 'integer', - }, - kills_log: { - description: - 'Array containing information on which hero the player killed at what time', - type: 'array', - items: { - type: 'object', - properties: { - time: { - description: 'Time in seconds the player killed the hero', - type: 'integer', - }, - key: { - description: 'Hero killed', - type: 'string', - }, - }, - }, - }, - lane_pos: { - description: 'Object containing information on lane position', - type: 'object', - }, - last_hits: { - description: 'Number of last hits', - type: 'integer', - }, - leaver_status: { - description: - "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", - type: 'integer', - }, - level: { - description: 'Level at the end of the game', - type: 'integer', - }, - lh_t: { - description: - 'Array describing last hits at each minute in the game', - type: 'array', - items: { - type: 'integer', - }, - }, - life_state: { - description: 'life_state', - type: 'object', - }, - max_hero_hit: { - description: - 'Object with information on the highest damage instance the player inflicted', - type: 'object', - }, - multi_kills: { - description: - 'Object with information on the number of the number of multikills the player had', - type: 'object', - }, - obs: { - description: - 'Object with information on where the player placed observer wards. The location takes the form (outer number, inner number) and are from ~64-192.', - type: 'object', - }, - obs_left_log: { - description: 'obs_left_log', - type: 'array', - items: { - type: 'object', - }, - }, - obs_log: { - description: - 'Object containing information on when and where the player placed observer wards', - type: 'array', - items: { - type: 'object', - }, - }, - obs_placed: { - description: 'Total number of observer wards placed', - type: 'integer', - }, - party_id: { - description: 'party_id', - type: 'integer', - }, - permanent_buffs: { - description: - 'Array describing permanent buffs the player had at the end of the game. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/permanent_buffs.json', - type: 'array', - items: { - type: 'object', - }, - }, - pings: { - description: 'Total number of pings', - type: 'integer', - }, - purchase: { - description: - 'Object containing information on the items the player purchased', - type: 'object', - }, - purchase_log: { - description: - 'Object containing information on when items were purchased', - type: 'array', - items: { - type: 'object', - properties: { - time: { - description: 'Time in seconds the item was bought', - type: 'integer', - }, - key: { - description: 'String item ID', - type: 'string', - }, - charges: { - description: 'Integer amount of charges', - type: 'integer', - }, - }, - }, - }, - rune_pickups: { - description: 'Number of runes picked up', - type: 'integer', - }, - runes: { - description: - 'Object with information about which runes the player picked up', - type: 'object', - additionalProperties: { - type: 'integer', - }, - }, - runes_log: { - description: - 'Array with information on when runes were picked up', - type: 'array', - items: { - type: 'object', - properties: { - time: { - description: 'Time in seconds rune picked up', - type: 'integer', - }, - key: { - description: 'key', - type: 'integer', - }, - }, - }, - }, - sen: { - description: - 'Object with information on where sentries were placed. The location takes the form (outer number, inner number) and are from ~64-192.', - type: 'object', - }, - sen_left_log: { - description: - 'Array containing information on when and where the player placed sentries', - type: 'array', - items: { - type: 'object', - }, - }, - sen_log: { - description: - 'Array with information on when and where sentries were placed by the player', - type: 'array', - items: { - type: 'object', - }, - }, - sen_placed: { - description: 'How many sentries were placed by the player', - type: 'integer', - }, - stuns: { - description: 'Total stun duration of all stuns by the player', - type: 'number', - }, - times: { - description: - 'Time in seconds corresponding to the time of entries of other arrays in the match.', - type: 'array', - items: { - type: 'integer', - }, - }, - tower_damage: { - description: 'Total tower damage done by the player', - type: 'integer', - }, - xp_per_min: { - description: 'Experience Per Minute obtained by the player', - type: 'integer', - }, - xp_reasons: { - description: - "Object containing information on the sources of this player's experience", - type: 'object', - }, - xp_t: { - description: 'Experience at each minute of the game', - type: 'array', - items: { - type: 'integer', - }, - }, - personaname: commonProperties.persona_name, - name: commonProperties.general_name, - last_login: { - description: "Time of player's last login", - type: 'string', - format: 'date-time', - nullable: true, - }, - radiant_win: commonProperties.radiant_win, - start_time: commonProperties.start_time, - duration: commonProperties.duration, - cluster: { - description: 'cluster', - type: 'integer', - }, - lobby_type: { - description: - 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', - type: 'integer', - }, - game_mode: { - description: - 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', - type: 'integer', - }, - patch: { - description: - 'Integer representing the patch the game was played on', - type: 'integer', - }, - region: { - description: - 'Integer corresponding to the region the game was played on', - type: 'integer', - }, - isRadiant: { - description: - 'Boolean for whether or not the player is on Radiant', - type: 'boolean', - }, - win: { - description: - 'Binary integer representing whether or not the player won', - type: 'integer', - }, - lose: { - description: - 'Binary integer representing whether or not the player lost', - type: 'integer', - }, - total_gold: { - description: 'Total gold at the end of the game', - type: 'integer', - }, - total_xp: { - description: 'Total experience at the end of the game', - type: 'integer', - }, - kills_per_min: { - description: 'Number of kills per minute', - type: 'number', - }, - kda: { - description: 'kda', - type: 'number', - }, - abandons: { - description: 'abandons', - type: 'integer', - }, - neutral_kills: { - description: 'Total number of neutral creeps killed', - type: 'integer', - }, - tower_kills: { - description: 'Total number of tower kills the player had', - type: 'integer', - }, - courier_kills: { - description: 'Total number of courier kills the player had', - type: 'integer', - }, - lane_kills: { - description: 'Total number of lane creeps killed by the player', - type: 'integer', - }, - hero_kills: { - description: 'Total number of heroes killed by the player', - type: 'integer', - }, - observer_kills: { - description: - 'Total number of observer wards killed by the player', - type: 'integer', - }, - sentry_kills: { - description: 'Total number of sentry wards killed by the player', - type: 'integer', - }, - roshan_kills: { - description: - 'Total number of roshan kills (last hit on roshan) the player had', - type: 'integer', - }, - necronomicon_kills: { - description: - 'Total number of Necronomicon creeps killed by the player', - type: 'integer', - }, - ancient_kills: { - description: - 'Total number of Ancient creeps killed by the player', - type: 'integer', - }, - buyback_count: { - description: 'Total number of buyback the player used', - type: 'integer', - }, - observer_uses: { - description: 'Number of observer wards used', - type: 'integer', - }, - sentry_uses: { - description: 'Number of sentry wards used', - type: 'integer', - }, - lane_efficiency: { - description: 'lane_efficiency', - type: 'number', - }, - lane_efficiency_pct: { - description: 'lane_efficiency_pct', - type: 'number', - }, - lane: { - description: 'Integer referring to which lane the hero laned in', - type: 'integer', - nullable: true, - }, - lane_role: { - description: 'lane_role', - type: 'integer', - nullable: true, - }, - is_roaming: { - description: - 'Boolean referring to whether or not the player roamed', - type: 'boolean', - nullable: true, - }, - purchase_time: { - description: - 'Object with information on when the player last purchased an item', - type: 'object', - }, - first_purchase_time: { - description: - 'Object with information on when the player first puchased an item', - type: 'object', - }, - item_win: { - description: - 'Object with information on whether or not the item won', - type: 'object', - }, - item_usage: { - description: - 'Object containing binary integers the tell whether the item was purchased by the player (note: this is always 1)', - type: 'object', - }, - purchase_tpscroll: { - description: 'Total number of TP scrolls purchased by the player', - type: 'integer', - }, - actions_per_min: { - description: 'Actions per minute', - type: 'integer', - }, - life_state_dead: { - description: 'life_state_dead', - type: 'integer', - }, - rank_tier: { - description: - 'The rank tier of the player. Tens place indicates rank, ones place indicates stars.', - type: 'integer', - }, - cosmetics: { - description: 'cosmetics', - type: 'array', - items: { - type: 'object', - properties: { - item_id: { - type: 'integer', - }, - name: commonProperties.general_name, - prefab: { - type: 'string', - }, - creation_date: { - type: 'string', - format: 'date-time', - nullable: true, - }, - image_inventory: { - type: 'string', - nullable: true, - }, - image_path: { - type: 'string', - nullable: true, - }, - item_description: { - type: 'string', - nullable: true, - }, - item_name: { - type: 'string', - }, - item_rarity: { - type: 'string', - nullable: true, - }, - item_type_name: { - type: 'string', - nullable: true, - }, - used_by_heroes: { - type: 'string', - nullable: true, - }, - }, - }, - }, - benchmarks: { - description: - 'Object containing information on certain benchmarks like GPM, XPM, KDA, tower damage, etc', - type: 'object', - }, - }, - }, - }, - patch: { - description: 'Information on the patch version the game is played on', - type: 'integer', - }, - region: { - description: - 'Integer corresponding to the region the game was played on', - type: 'integer', - }, - all_word_counts: { - description: - "Word counts of the all chat messages in the player's games", - type: 'object', - }, - my_word_counts: { - description: "Word counts of the player's all chat messages", - type: 'object', - }, - throw: { - description: - "Maximum gold advantage of the player's team if they lost the match", - type: 'integer', - }, - comeback: { - description: - "Maximum gold disadvantage of the player's team if they won the match", - type: 'integer', - }, - loss: { - description: - "Maximum gold disadvantage of the player's team if they lost the match", - type: 'integer', - }, - win: { - description: - "Maximum gold advantage of the player's team if they won the match", - type: 'integer', - }, - replay_url: { - description: 'replay_url', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/match/ParsedMatchesResponse.js b/routes/responses/schemas/match/ParsedMatchesResponse.js deleted file mode 100644 index a6f362c5d..000000000 --- a/routes/responses/schemas/match/ParsedMatchesResponse.js +++ /dev/null @@ -1,11 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - ParsedMatchesResponse: { - title: 'ParsedMatchesResponse', - type: 'object', - properties: { - match_id: commonProperties.match_id, - }, - }, -}; diff --git a/routes/responses/schemas/match/PublicMatchesResponse.js b/routes/responses/schemas/match/PublicMatchesResponse.js deleted file mode 100644 index 524dd4112..000000000 --- a/routes/responses/schemas/match/PublicMatchesResponse.js +++ /dev/null @@ -1,47 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PublicMatchesResponse: { - title: 'PublicMatchesResponse', - type: 'object', - properties: { - match_id: commonProperties.match_id, - match_seq_num: { - description: 'match_seq_num', - type: 'integer', - }, - radiant_win: commonProperties.radiant_win, - start_time: commonProperties.start_time, - duration: commonProperties.duration, - avg_mmr: { - type: 'integer', - }, - num_mmr: { - type: 'integer', - }, - lobby_type: { - type: 'integer', - }, - game_mode: { - type: 'integer', - }, - avg_rank_tier: { - type: 'integer', - }, - num_rank_tier: { - type: 'integer', - }, - cluster: { - type: 'integer', - }, - radiant_team: { - description: 'radiant_team', - type: 'string', - }, - dire_team: { - description: 'dire_team', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/BenchmarksResponse.js b/routes/responses/schemas/miscellaneous/BenchmarksResponse.js deleted file mode 100644 index 94a007760..000000000 --- a/routes/responses/schemas/miscellaneous/BenchmarksResponse.js +++ /dev/null @@ -1,129 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - BenchmarksResponse: { - title: 'BenchmarksResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - result: { - description: 'result', - type: 'object', - properties: { - gold_per_min: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'number', - }, - }, - }, - }, - xp_per_min: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'number', - }, - }, - }, - }, - kills_per_min: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'number', - }, - }, - }, - }, - last_hits_per_min: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'number', - }, - }, - }, - }, - hero_damage_per_min: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'number', - }, - }, - }, - }, - hero_healing_per_min: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'number', - }, - }, - }, - }, - tower_damage: { - type: 'array', - items: { - type: 'object', - properties: { - percentile: { - description: 'percentile', - type: 'number', - }, - value: { - description: 'value', - type: 'integer', - }, - }, - }, - }, - }, - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/DistributionsResponse.js b/routes/responses/schemas/miscellaneous/DistributionsResponse.js deleted file mode 100644 index 6fef7a5d0..000000000 --- a/routes/responses/schemas/miscellaneous/DistributionsResponse.js +++ /dev/null @@ -1,262 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - DistributionsResponse: { - title: 'DistributionsResponse', - type: 'object', - properties: { - ranks: { - description: 'ranks', - type: 'object', - properties: { - commmand: { - description: 'command', - type: 'string', - }, - rowCount: { - description: 'rowCount', - type: 'integer', - }, - rows: { - description: 'rows', - type: 'array', - items: { - type: 'object', - properties: { - bin: { - description: 'bin', - type: 'integer', - }, - bin_name: { - description: 'bin_name', - type: 'integer', - }, - count: { - description: 'count', - type: 'integer', - }, - cumulative_sum: { - description: 'cumulative_sum', - type: 'integer', - }, - }, - }, - }, - fields: { - description: 'fields', - type: 'array', - items: { - type: 'object', - properties: { - name: commonProperties.field_name, - tableID: { - description: 'tableID', - type: 'integer', - }, - columnID: { - description: 'columnID', - type: 'integer', - }, - dataTypeID: { - description: 'dataTypeID', - type: 'integer', - }, - dataTypeSize: { - description: 'dataTypeSize', - type: 'integer', - }, - dataTypeModifier: { - description: 'dataTypeModifier', - type: 'integer', - }, - format: { - description: 'format', - type: 'string', - }, - }, - }, - }, - rowAsArray: { - description: 'rowAsArray', - type: 'boolean', - }, - sum: { - description: 'sum', - type: 'object', - properties: { - count: { - description: 'count', - type: 'integer', - }, - }, - }, - }, - }, - mmr: { - description: 'mmr', - type: 'object', - properties: { - commmand: { - description: 'command', - type: 'string', - }, - rowCount: { - description: 'rowCount', - type: 'integer', - }, - rows: { - description: 'rows', - type: 'array', - items: { - type: 'object', - properties: { - bin: { - description: 'bin', - type: 'integer', - }, - bin_name: { - description: 'bin_name', - type: 'integer', - }, - count: { - description: 'count', - type: 'integer', - }, - cumulative_sum: { - description: 'cumulative_sum', - type: 'integer', - }, - }, - }, - }, - fields: { - description: 'fields', - type: 'array', - items: { - type: 'object', - properties: { - name: commonProperties.field_name, - tableID: { - description: 'tableID', - type: 'integer', - }, - columnID: { - description: 'columnID', - type: 'integer', - }, - dataTypeID: { - description: 'dataTypeID', - type: 'integer', - }, - dataTypeSize: { - description: 'dataTypeSize', - type: 'integer', - }, - dataTypeModifier: { - description: 'dataTypeModifier', - type: 'integer', - }, - format: { - description: 'format', - type: 'string', - }, - }, - }, - }, - rowAsArray: { - description: 'rowAsArray', - type: 'boolean', - }, - sum: { - description: 'sum', - type: 'object', - properties: { - count: { - description: 'count', - type: 'integer', - }, - }, - }, - }, - }, - country_mmr: { - description: 'country_mmr', - type: 'object', - properties: { - commmand: { - description: 'command', - type: 'string', - }, - rowCount: { - description: 'rowCount', - type: 'integer', - }, - rows: { - description: 'rows', - type: 'array', - items: { - type: 'object', - properties: { - loccountrycode: { - description: 'loccountrycode', - type: 'string', - nullable: true, - }, - count: { - description: 'count', - type: 'integer', - }, - avg: { - description: 'avg', - type: 'string', - }, - common: { - description: 'common', - type: 'string', - }, - }, - }, - }, - fields: { - description: 'fields', - type: 'array', - items: { - type: 'object', - properties: { - name: commonProperties.field_name, - tableID: { - description: 'tableID', - type: 'integer', - }, - columnID: { - description: 'columnID', - type: 'integer', - }, - dataTypeID: { - description: 'dataTypeID', - type: 'integer', - }, - dataTypeSize: { - description: 'dataTypeSize', - type: 'integer', - }, - dataTypeModifier: { - description: 'dataTypeModifier', - type: 'integer', - }, - format: { - description: 'format', - type: 'string', - }, - }, - }, - }, - rowAsArray: { - description: 'rowAsArray', - type: 'boolean', - }, - }, - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js b/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js deleted file mode 100644 index b38953c3e..000000000 --- a/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js +++ /dev/null @@ -1,27 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - LeagueObjectResponse: { - title: 'LeagueObjectResponse', - type: 'object', - properties: { - leagueid: { - description: 'leagueid', - type: 'integer', - }, - ticket: { - description: 'ticket', - type: 'string', - }, - banner: { - description: 'banner', - type: 'string', - }, - tier: { - description: 'tier', - type: 'string', - }, - name: commonProperties.league_name, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/MetadataResponse.js b/routes/responses/schemas/miscellaneous/MetadataResponse.js deleted file mode 100644 index fdba9b630..000000000 --- a/routes/responses/schemas/miscellaneous/MetadataResponse.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - MetadataResponse: { - title: 'MetadataResponse', - type: 'object', - properties: { - banner: { - description: 'banner', - type: 'object', - nullable: true, - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/RankingsResponse.js b/routes/responses/schemas/miscellaneous/RankingsResponse.js deleted file mode 100644 index 07c7ce58b..000000000 --- a/routes/responses/schemas/miscellaneous/RankingsResponse.js +++ /dev/null @@ -1,82 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - RankingsResponse: { - title: 'RankingsResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - rankings: { - description: 'rankings', - type: 'array', - items: { - type: 'object', - properties: { - account_id: commonProperties.account_id, - score: { - description: 'Score', - type: 'number', - }, - steamid: { - description: 'steamid', - type: 'string', - nullable: true, - }, - avatar: { - description: 'avatar', - type: 'string', - nullable: true, - }, - avatarmedium: { - description: 'avatarmedium', - type: 'string', - nullable: true, - }, - avatarfull: { - description: 'avatarfull', - type: 'string', - nullable: true, - }, - profileurl: { - description: 'profileurl', - type: 'string', - nullable: true, - }, - personaname: commonProperties.persona_name, - last_login: { - description: 'last_login', - type: 'string', - format: 'date-time', - nullable: true, - }, - full_history_time: { - description: 'full_history_time', - type: 'string', - format: 'date-time', - }, - cheese: { - description: 'cheese', - type: 'integer', - nullable: true, - }, - fh_unavailable: { - description: 'fh_unavailable', - type: 'boolean', - nullable: true, - }, - loccountrycode: { - description: 'loccountrycode', - type: 'string', - nullable: true, - }, - rank_tier: { - description: 'rank_tier', - type: 'integer', - nullable: true, - }, - }, - }, - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/RecordsResponse.js b/routes/responses/schemas/miscellaneous/RecordsResponse.js deleted file mode 100644 index ff708bcdb..000000000 --- a/routes/responses/schemas/miscellaneous/RecordsResponse.js +++ /dev/null @@ -1,17 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - RecordsResponse: { - title: 'RecordsResponse', - type: 'object', - properties: { - match_id: commonProperties.match_id, - start_time: commonProperties.start_time, - hero_id: commonProperties.hero_id, - score: { - description: 'Record score', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/ReplaysResponse.js b/routes/responses/schemas/miscellaneous/ReplaysResponse.js deleted file mode 100644 index d4ce95fac..000000000 --- a/routes/responses/schemas/miscellaneous/ReplaysResponse.js +++ /dev/null @@ -1,19 +0,0 @@ -const commonProperties = require("../../properties/commonProperties"); - -module.exports = { - ReplaysResponse: { - title: "ReplaysResponse", - type: "object", - properties: { - match_id: commonProperties.match_id, - cluster: { - description: "cluster", - type: "integer", - }, - replay_salt: { - description: "replay_salt", - type: "integer", - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js b/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js deleted file mode 100644 index 3114d25f3..000000000 --- a/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js +++ /dev/null @@ -1,29 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - ScenarioItemTimingsResponse: { - title: 'ScenarioItemTimingsResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - item: { - description: 'Purchased item', - type: 'string', - }, - time: { - description: 'Ingame time in seconds before the item was purchased', - type: 'integer', - }, - games: { - description: - 'The number of games where the hero bought this item before this time', - type: 'string', - }, - wins: { - description: - 'The number of games won where the hero bought this item before this time', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/ScenarioLaneRolesResponse.js b/routes/responses/schemas/miscellaneous/ScenarioLaneRolesResponse.js deleted file mode 100644 index 39fd96fa1..000000000 --- a/routes/responses/schemas/miscellaneous/ScenarioLaneRolesResponse.js +++ /dev/null @@ -1,29 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - ScenarioLaneRolesResponse: { - title: 'ScenarioLaneRolesResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - lane_role: { - description: "The hero's lane role", - type: 'integer', - }, - time: { - description: 'Maximum game length in seconds', - type: 'integer', - }, - games: { - description: - 'The number of games where the hero played in this lane role', - type: 'string', - }, - wins: { - description: - 'The number of games won where the hero played in this lane role', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/ScenarioMiscResponse.js b/routes/responses/schemas/miscellaneous/ScenarioMiscResponse.js deleted file mode 100644 index 27fef039e..000000000 --- a/routes/responses/schemas/miscellaneous/ScenarioMiscResponse.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - ScenarioMiscResponse: { - title: 'ScenarioMiscResponse', - type: 'object', - properties: { - scenario: { - description: "The scenario's name or description", - type: 'string', - }, - is_radiant: { - description: - 'Boolean indicating whether Radiant executed this scenario', - type: 'boolean', - }, - region: { - description: 'Region the game was played in', - type: 'integer', - }, - games: { - description: 'The number of games where this scenario occurred', - type: 'string', - }, - wins: { - description: 'The number of games won where this scenario occured', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/SchemaResponse.js b/routes/responses/schemas/miscellaneous/SchemaResponse.js deleted file mode 100644 index 143017293..000000000 --- a/routes/responses/schemas/miscellaneous/SchemaResponse.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - SchemaResponse: { - title: 'SchemaResponse', - type: 'object', - properties: { - table_name: { - description: 'table_name', - type: 'string', - }, - column_name: { - description: 'column_name', - type: 'string', - }, - data_type: { - description: 'data_type', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/miscellaneous/SearchResponse.js b/routes/responses/schemas/miscellaneous/SearchResponse.js deleted file mode 100644 index 32bb38292..000000000 --- a/routes/responses/schemas/miscellaneous/SearchResponse.js +++ /dev/null @@ -1,25 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - SearchResponse: { - title: 'SearchResponse', - type: 'object', - properties: { - account_id: commonProperties.account_id, - avatarfull: { - description: 'avatarfull', - type: 'string', - nullable: true, - }, - personaname: commonProperties.persona_name, - last_match_time: { - description: 'last_match_time. May not be present or null.', - type: 'string', - }, - similarity: { - description: 'similarity', - type: 'number', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerCountsResponse.js b/routes/responses/schemas/player/PlayerCountsResponse.js deleted file mode 100644 index 48432649d..000000000 --- a/routes/responses/schemas/player/PlayerCountsResponse.js +++ /dev/null @@ -1,38 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerCountsResponse: { - title: 'PlayerCountsResponse', - type: 'object', - properties: { - leaver_status: { - description: - "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", - type: 'object', - }, - game_mode: { - description: - 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', - type: 'object', - }, - lobby_type: { - description: - 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', - type: 'object', - }, - lane_role: { - description: 'lane_role', - type: 'object', - }, - region: { - description: - 'Integer corresponding to the region the game was played on', - type: 'object', - }, - patch: { - description: 'patch', - type: 'object', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerHeroesResponse.js b/routes/responses/schemas/player/PlayerHeroesResponse.js deleted file mode 100644 index 34a136a79..000000000 --- a/routes/responses/schemas/player/PlayerHeroesResponse.js +++ /dev/null @@ -1,40 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerHeroesResponse: { - title: 'PlayerHeroesResponse', - description: 'hero', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - last_played: { - description: 'last_played', - type: 'integer', - }, - games: { - description: 'games', - type: 'integer', - }, - win: { - description: 'win', - type: 'integer', - }, - with_games: { - description: 'with_games', - type: 'integer', - }, - with_win: { - description: 'with_win', - type: 'integer', - }, - against_games: { - description: 'against_games', - type: 'integer', - }, - against_win: { - description: 'against_win', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerMatchesResponse.js b/routes/responses/schemas/player/PlayerMatchesResponse.js deleted file mode 100644 index 54e803515..000000000 --- a/routes/responses/schemas/player/PlayerMatchesResponse.js +++ /dev/null @@ -1,65 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerMatchesResponse: { - title: 'PlayerMatchesResponse', - description: 'Object containing information on the match', - type: 'object', - properties: { - match_id: commonProperties.match_id, - player_slot: commonProperties.player_slot, - radiant_win: commonProperties.radiant_win, - duration: commonProperties.duration, - game_mode: { - description: - 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', - type: 'integer', - }, - lobby_type: { - description: - 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', - type: 'integer', - }, - hero_id: commonProperties.hero_id, - start_time: commonProperties.start_time, - version: { - description: 'version', - type: 'integer', - nullable: true, - }, - kills: { - description: 'Total kills the player had at the end of the game', - type: 'integer', - }, - deaths: { - description: 'Total deaths the player had at the end of the game', - type: 'integer', - }, - assists: { - description: 'Total assists the player had at the end of the game', - type: 'integer', - }, - skill: { - description: - 'Skill bracket assigned by Valve (Normal, High, Very High)', - type: 'integer', - nullable: true, - }, - average_rank: { - description: 'Average rank of players with public match data', - type: 'integer', - nullable: true, - }, - leaver_status: { - description: - "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", - type: 'integer', - }, - party_size: { - description: "Size of the player's party", - type: 'integer', - nullable: true, - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerObjectResponse.js b/routes/responses/schemas/player/PlayerObjectResponse.js deleted file mode 100644 index db2acd977..000000000 --- a/routes/responses/schemas/player/PlayerObjectResponse.js +++ /dev/null @@ -1,88 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerObjectResponse: { - title: 'PlayerObjectResponse', - type: 'object', - properties: { - account_id: commonProperties.account_id, - steamid: { - description: "Player's steam identifier", - type: 'string', - }, - avatar: { - description: 'Steam picture URL (small picture)', - type: 'string', - }, - avatarmedium: { - description: 'Steam picture URL (medium picture)', - type: 'string', - }, - avatarfull: { - description: 'Steam picture URL (full picture)', - type: 'string', - }, - profileurl: { - description: 'Steam profile URL', - type: 'string', - }, - personaname: commonProperties.persona_name, - last_login: { - description: 'Date and time of last login to OpenDota', - type: 'string', - format: 'date-time', - }, - full_history_time: { - description: - "Date and time of last request to refresh player's match history", - type: 'string', - format: 'date-time', - }, - cheese: { - description: 'Amount of dollars the player has donated to OpenDota', - type: 'integer', - }, - fh_unavailable: { - description: "Whether the refresh of player' match history failed", - type: 'boolean', - }, - loccountrycode: { - description: "Player's country identifier, e.g. US", - type: 'string', - }, - name: { - description: "Verified player name, e.g. 'Miracle-'", - type: 'string', - }, - country_code: { - description: "Player's country code", - type: 'string', - }, - fantasy_role: { - description: "Player's ingame role (core: 1 or support: 2)", - type: 'integer', - }, - team_id: { - description: "Player's team identifier", - type: 'integer', - }, - team_name: commonProperties.team_name, - team_tag: { - description: "Player's team shorthand tag, e.g. 'EG'", - type: 'string', - }, - is_locked: { - description: 'Whether the roster lock is active', - type: 'boolean', - }, - is_pro: { - description: 'Whether the player is professional or not', - type: 'boolean', - }, - locked_until: { - description: 'When the roster lock will end', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerPeersResponse.js b/routes/responses/schemas/player/PlayerPeersResponse.js deleted file mode 100644 index c4faab8a0..000000000 --- a/routes/responses/schemas/player/PlayerPeersResponse.js +++ /dev/null @@ -1,72 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerPeersResponse: { - title: 'PlayerPeersResponse', - type: 'object', - properties: { - account_id: commonProperties.account_id, - last_played: { - description: 'last_played', - type: 'integer', - }, - win: { - description: 'win', - type: 'integer', - }, - games: { - description: 'games', - type: 'integer', - }, - with_win: { - description: 'with_win', - type: 'integer', - }, - with_games: { - description: 'with_games', - type: 'integer', - }, - against_win: { - description: 'against_win', - type: 'integer', - }, - against_games: { - description: 'against_games', - type: 'integer', - }, - with_gpm_sum: { - description: 'with_gpm_sum', - type: 'integer', - }, - with_xpm_sum: { - description: 'with_xpm_sum', - type: 'integer', - }, - personaname: commonProperties.persona_name, - name: commonProperties.general_name, - is_contributor: { - description: 'is_contributor', - type: 'boolean', - }, - is_subscriber: { - description: 'is_subscriber', - type: 'boolean', - }, - last_login: { - description: 'last_login', - type: 'string', - nullable: true, - }, - avatar: { - description: 'avatar', - type: 'string', - nullable: true, - }, - avatarfull: { - description: 'avatarfull', - type: 'string', - nullable: true, - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerProsResponse.js b/routes/responses/schemas/player/PlayerProsResponse.js deleted file mode 100644 index 55afff2de..000000000 --- a/routes/responses/schemas/player/PlayerProsResponse.js +++ /dev/null @@ -1,134 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerProsResponse: { - title: 'PlayerProsResponse', - type: 'object', - properties: { - account_id: commonProperties.account_id, - name: commonProperties.general_name, - country_code: { - description: 'country_code', - type: 'string', - }, - fantasy_role: { - description: 'fantasy_role', - type: 'integer', - }, - team_id: { - description: 'team_id', - type: 'integer', - }, - team_name: commonProperties.team_name, - team_tag: { - description: 'team_tag', - type: 'string', - nullable: true, - }, - is_locked: { - description: 'is_locked', - type: 'boolean', - }, - is_pro: { - description: 'is_pro', - type: 'boolean', - }, - locked_until: { - description: 'locked_until', - type: 'integer', - nullable: true, - }, - steamid: { - description: 'steamid', - type: 'string', - nullable: true, - }, - avatar: { - description: 'avatar', - type: 'string', - nullable: true, - }, - avatarmedium: { - description: 'avatarmedium', - type: 'string', - nullable: true, - }, - avatarfull: { - description: 'avatarfull', - type: 'string', - nullable: true, - }, - profileurl: { - description: 'profileurl', - type: 'string', - nullable: true, - }, - last_login: { - description: 'last_login', - type: 'string', - format: 'date-time', - nullable: true, - }, - full_history_time: { - description: 'full_history_time', - type: 'string', - format: 'date-time', - nullable: true, - }, - cheese: { - description: 'cheese', - type: 'integer', - nullable: true, - }, - fh_unavailable: { - description: 'fh_unavailable', - type: 'boolean', - nullable: true, - }, - loccountrycode: { - description: 'loccountrycode', - type: 'string', - nullable: true, - }, - last_played: { - description: 'last_played', - type: 'integer', - nullable: true, - }, - win: { - description: 'win', - type: 'integer', - }, - games: { - description: 'games', - type: 'integer', - }, - with_win: { - description: 'with_win', - type: 'integer', - }, - with_games: { - description: 'with_games', - type: 'integer', - }, - against_win: { - description: 'against_win', - type: 'integer', - }, - against_games: { - description: 'against_games', - type: 'integer', - }, - with_gpm_sum: { - description: 'with_gpm_sum', - type: 'integer', - nullable: true, - }, - with_xpm_sum: { - description: 'with_xpm_sum', - type: 'integer', - nullable: true, - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerRankingsResponse.js b/routes/responses/schemas/player/PlayerRankingsResponse.js deleted file mode 100644 index dfed8dff4..000000000 --- a/routes/responses/schemas/player/PlayerRankingsResponse.js +++ /dev/null @@ -1,23 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerRankingsResponse: { - title: 'PlayerRankingsResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - score: { - description: 'Hero score', - type: 'number', - }, - percent_rank: { - description: 'percent_rank', - type: 'number', - }, - card: { - description: 'numeric_rank', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerRatingsResponse.js b/routes/responses/schemas/player/PlayerRatingsResponse.js deleted file mode 100644 index 37d3c46b6..000000000 --- a/routes/responses/schemas/player/PlayerRatingsResponse.js +++ /dev/null @@ -1,25 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerRatingsResponse: { - title: 'PlayerRatingsResponse', - type: 'object', - properties: { - account_id: commonProperties.account_id, - match_id: commonProperties.match_id, - solo_competitive_rank: { - description: 'solo_competitive_rank', - type: 'integer', - nullable: true, - }, - competitive_rank: { - description: 'competitive_rank', - type: 'integer', - }, - time: { - description: 'time', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerRecentMatchesResponse.js b/routes/responses/schemas/player/PlayerRecentMatchesResponse.js deleted file mode 100644 index c51b31fcb..000000000 --- a/routes/responses/schemas/player/PlayerRecentMatchesResponse.js +++ /dev/null @@ -1,106 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayerRecentMatchesResponse: { - title: 'PlayerRecentMatchesResponse', - description: 'match', - type: 'object', - properties: { - match_id: commonProperties.match_id, - player_slot: commonProperties.player_slot, - radiant_win: commonProperties.radiant_win, - duration: commonProperties.duration, - game_mode: { - description: - 'Integer corresponding to game mode played. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/game_mode.json', - type: 'integer', - }, - lobby_type: { - description: - 'Integer corresponding to lobby type of match. List of constants can be found here: https://github.com/odota/dotaconstants/blob/master/json/lobby_type.json', - type: 'integer', - }, - hero_id: commonProperties.hero_id, - start_time: commonProperties.start_time, - version: { - description: 'version', - type: 'integer', - nullable: true, - }, - kills: { - description: 'Total kills the player had at the end of the match', - type: 'integer', - }, - deaths: { - description: 'Total deaths the player had at the end of the match', - type: 'integer', - }, - assists: { - description: 'Total assists the player had at the end of the match', - type: 'integer', - }, - skill: { - description: - 'Skill bracket assigned by Valve (Normal, High, Very High). If the skill is unknown, will return null.', - type: 'integer', - nullable: true, - }, - average_rank: { - description: 'Average rank of players with public match data', - type: 'integer', - nullable: true, - }, - xp_per_min: { - description: 'Experience Per Minute obtained by the player', - type: 'integer', - }, - gold_per_min: { - description: 'Average gold per minute of the player', - type: 'integer', - }, - hero_damage: { - description: 'Total hero damage to enemy heroes', - type: 'integer', - }, - hero_healing: { - description: 'Total healing of ally heroes', - type: 'integer', - }, - last_hits: { - description: 'Total last hits the player had at the end of the match', - type: 'integer', - }, - lane: { - description: - 'Integer corresponding to which lane the player laned in for the match', - type: 'integer', - nullable: true, - }, - lane_role: { - description: 'lane_role', - type: 'integer', - nullable: true, - }, - is_roaming: { - description: 'Boolean describing whether or not the player roamed', - type: 'boolean', - nullable: true, - }, - cluster: { - description: 'cluster', - type: 'integer', - }, - leaver_status: { - description: - "Integer describing whether or not the player left the game. 0: didn't leave. 1: left safely. 2+: Abandoned", - type: 'integer', - }, - party_size: { - description: - 'Size of the players party. If not in a party, will return 1.', - type: 'integer', - nullable: true, - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerTotalsResponse.js b/routes/responses/schemas/player/PlayerTotalsResponse.js deleted file mode 100644 index 8d9f8b26a..000000000 --- a/routes/responses/schemas/player/PlayerTotalsResponse.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - PlayerTotalsResponse: { - title: 'PlayerTotalsResponse', - type: 'object', - properties: { - field: { - description: 'field', - type: 'string', - }, - n: { - description: 'number', - type: 'integer', - }, - sum: { - description: 'sum', - type: 'number', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerWardMapResponse.js b/routes/responses/schemas/player/PlayerWardMapResponse.js deleted file mode 100644 index eb2bd72c5..000000000 --- a/routes/responses/schemas/player/PlayerWardMapResponse.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - PlayerWardMapResponse: { - title: 'PlayerWardMapResponse', - type: 'object', - properties: { - obs: { - description: 'obs', - type: 'object', - }, - sen: { - description: 'sen', - type: 'object', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerWinLossResponse.js b/routes/responses/schemas/player/PlayerWinLossResponse.js deleted file mode 100644 index f83dbb67b..000000000 --- a/routes/responses/schemas/player/PlayerWinLossResponse.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - PlayerWinLossResponse: { - title: 'PlayerWinLossResponse', - type: 'object', - properties: { - win: { - description: 'Number of wins', - type: 'integer', - }, - lose: { - description: 'Number of loses', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayerWordCloudResponse.js b/routes/responses/schemas/player/PlayerWordCloudResponse.js deleted file mode 100644 index d29572891..000000000 --- a/routes/responses/schemas/player/PlayerWordCloudResponse.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - PlayerWordCloudResponse: { - title: 'PlayerWordCloudResponse', - type: 'object', - properties: { - my_word_counts: { - description: 'my_word_counts', - type: 'object', - }, - all_word_counts: { - description: 'all_word_counts', - type: 'object', - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayersByRankResponse.js b/routes/responses/schemas/player/PlayersByRankResponse.js deleted file mode 100644 index 4cc2b4e39..000000000 --- a/routes/responses/schemas/player/PlayersByRankResponse.js +++ /dev/null @@ -1,24 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayersByRankResponse: { - title: 'PlayersByRankResponse', - type: 'array', - items: { - type: 'object', - properties: { - account_id: commonProperties.account_id, - rank_tier: { - description: 'Integer indicating the rank/medal of the player', - type: 'number', - }, - fh_unavailable: { - description: - 'Indicates if we were unable to fetch full history for this player due to privacy settings', - type: 'boolean', - nullable: true, - }, - }, - }, - }, -}; diff --git a/routes/responses/schemas/player/PlayersResponse.js b/routes/responses/schemas/player/PlayersResponse.js deleted file mode 100644 index 3943ac08b..000000000 --- a/routes/responses/schemas/player/PlayersResponse.js +++ /dev/null @@ -1,107 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - PlayersResponse: { - title: 'PlayerResponse', - type: 'object', - properties: { - solo_competitive_rank: { - description: 'solo_competitive_rank', - type: 'integer', - nullable: true, - }, - competitive_rank: { - description: 'competitive_rank', - type: 'integer', - nullable: true, - }, - rank_tier: { - description: 'rank_tier', - type: 'number', - nullable: true, - }, - leaderboard_rank: { - description: 'leaderboard_rank', - type: 'number', - nullable: true, - }, - mmr_estimate: { - description: 'mmr_estimate', - type: 'object', - properties: { - estimate: { - description: 'estimate', - type: 'number', - nullable: true, - }, - }, - }, - profile: { - description: 'profile', - type: 'object', - properties: { - account_id: commonProperties.account_id, - personaname: commonProperties.persona_name, - name: commonProperties.general_name, - plus: { - description: - 'Boolean indicating status of current Dota Plus subscription', - type: 'boolean', - }, - cheese: { - description: 'cheese', - type: 'integer', - nullable: true, - }, - steamid: { - description: 'steamid', - type: 'string', - nullable: true, - }, - avatar: { - description: 'avatar', - type: 'string', - nullable: true, - }, - avatarmedium: { - description: 'avatarmedium', - type: 'string', - nullable: true, - }, - avatarfull: { - description: 'avatarfull', - type: 'string', - nullable: true, - }, - profileurl: { - description: 'profileurl', - type: 'string', - nullable: true, - }, - last_login: { - description: 'last_login', - type: 'string', - nullable: true, - }, - loccountrycode: { - description: 'loccountrycode', - type: 'string', - nullable: true, - }, - is_contributor: { - description: - 'Boolean indicating if the user contributed to the development of OpenDota', - type: 'boolean', - default: false, - }, - is_subscriber: { - description: - 'Boolean indicating if the user subscribed to OpenDota', - type: 'boolean', - default: false, - }, - }, - }, - }, - }, -}; diff --git a/routes/responses/schemas/team/TeamHeroesResponse.js b/routes/responses/schemas/team/TeamHeroesResponse.js deleted file mode 100644 index 4ad5e2859..000000000 --- a/routes/responses/schemas/team/TeamHeroesResponse.js +++ /dev/null @@ -1,20 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - TeamHeroesResponse: { - title: 'TeamHeroesResponse', - type: 'object', - properties: { - hero_id: commonProperties.hero_id, - name: commonProperties.hero_name, - games_played: { - description: 'Number of games played', - type: 'integer', - }, - wins: { - description: 'Number of wins', - type: 'integer', - }, - }, - }, -}; diff --git a/routes/responses/schemas/team/TeamMatchObjectResponse.js b/routes/responses/schemas/team/TeamMatchObjectResponse.js deleted file mode 100644 index 682fd0e64..000000000 --- a/routes/responses/schemas/team/TeamMatchObjectResponse.js +++ /dev/null @@ -1,45 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - TeamMatchObjectResponse: { - title: 'TeamMatchObjectResponse', - type: 'object', - properties: { - match_id: commonProperties.match_id, - radiant: { - description: 'Whether the team/player/hero was on Radiant', - type: 'boolean', - }, - radiant_win: commonProperties.radiant_win, - radiant_score: commonProperties.radiant_score, - dire_score: commonProperties.dire_score, - duration: commonProperties.duration, - start_time: commonProperties.start_time, - leagueid: { - description: 'Identifier for the league the match took place in', - type: 'integer', - }, - league_name: { - description: 'Name of league the match took place in', - type: 'string', - }, - cluster: { - description: 'cluster', - type: 'integer', - }, - opposing_team_id: { - description: 'Opposing team identifier', - type: 'integer', - }, - opposing_team_name: { - description: "Opposing team name, e.g. 'Evil Geniuses'", - type: 'string', - nullable: true, - }, - opposing_team_logo: { - description: 'Opposing team logo url', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/team/TeamObjectResponse.js b/routes/responses/schemas/team/TeamObjectResponse.js deleted file mode 100644 index 6a9001091..000000000 --- a/routes/responses/schemas/team/TeamObjectResponse.js +++ /dev/null @@ -1,35 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - TeamObjectResponse: { - title: 'TeamObjectResponse', - type: 'object', - properties: { - team_id: { - description: "Team's identifier", - type: 'integer', - }, - rating: { - description: 'The Elo rating of the team', - type: 'number', - }, - wins: { - description: 'The number of games won by this team', - type: 'integer', - }, - losses: { - description: 'The number of losses by this team', - type: 'integer', - }, - last_match_time: { - description: 'The Unix timestamp of the last match played by this team', - type: 'integer', - }, - name: commonProperties.team_name, - tag: { - description: 'The team tag/abbreviation', - type: 'string', - }, - }, - }, -}; diff --git a/routes/responses/schemas/team/TeamPlayersResponse.js b/routes/responses/schemas/team/TeamPlayersResponse.js deleted file mode 100644 index 7924c35e5..000000000 --- a/routes/responses/schemas/team/TeamPlayersResponse.js +++ /dev/null @@ -1,24 +0,0 @@ -const commonProperties = require('../../properties/commonProperties'); - -module.exports = { - TeamPlayersResponse: { - title: 'TeamPlayersResponse', - type: 'object', - properties: { - account_id: commonProperties.account_id, - name: commonProperties.general_name, - games_played: { - description: 'Number of games played', - type: 'integer', - }, - wins: { - description: 'Number of wins', - type: 'integer', - }, - is_current_team_member: { - description: 'If this player is on the current roster', - type: 'boolean', - }, - }, - }, -}; diff --git a/routes/spec.js b/routes/spec.js index de8c49d6a..5c2cb4281 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,26 +1,24 @@ -const async = require('async'); -const constants = require('dotaconstants'); -const moment = require('moment'); -const { Client } = require('pg'); -const config = require('../config'); -// const crypto = require("crypto"); -// const uuidV4 = require("uuid/v4"); -const queue = require('../store/queue'); -const queries = require('../store/queries'); -const search = require('../store/search'); -const searchES = require('../store/searchES'); -const buildMatch = require('../store/buildMatch'); -const buildStatus = require('../store/buildStatus'); -const playerFields = require('./playerFields.json'); -const utility = require('../util/utility'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const packageJson = require('../package.json'); -const cacheFunctions = require('../store/cacheFunctions'); -const params = require('./requests/importParams'); -const responses = require('./responses/schemas/importResponseSchemas'); -const generateOperationId = require('./generateOperationId'); -const { insertMatchPromise } = require('../store/queries'); +import { parallel } from 'async'; +import constants, { heroes as _heroes } from 'dotaconstants'; +import moment from 'moment'; +import { Client } from 'pg'; +import { READONLY_POSTGRES_URL, ES_SEARCH_PERCENT } from '../config.js'; +import { getJob } from '../store/queue.js'; +import { getPlayer, getMmrEstimate, getPlayerMatches, getPeers, getProPeers, getPlayerRatings, getPlayerHeroRankings, getMetadata, getDistributions, getHeroRankings, getHeroBenchmarks, getHeroItemPopularity, getItemTimings, getLaneRoles, getTeamScenarios } from '../store/queries.js'; +import search from '../store/search.js'; +import searchES from '../store/searchES.js'; +import buildMatch from '../store/buildMatch.js'; +import buildStatus from '../store/buildStatus.js'; +import playerFields from './playerFields.json'; +import utility, { isRadiant as _isRadiant, mergeObjects, checkIfInExperiment, getData, generateJob } from '../util/utility.js'; +import db, { raw, first, select } from '../store/db.js'; +import { version as _version } from '../package.json'; +import { sendDataWithCache } from '../store/cacheFunctions.js'; +import params from './requests/index.js'; +import responses from './responses/schemas/index.js'; +import generateOperationId from './generateOperationId.js'; +import { insertMatchPromise } from '../store/queries.js'; +import redis from '../store/redis.js'; const { redisCount, countPeers, isContributor, matchupToString } = utility; const { subkeys, countCats } = playerFields; @@ -85,7 +83,7 @@ You can find data that can be used to convert hero and ability IDs and other inf The OpenDota API offers 50,000 free calls per month and a rate limit of 60 requests/minute. We also offer a Premium Tier with unlimited API calls and higher rate limits. Check out the [API page](https://www.opendota.com/api-keys) to learn more. `, - version: packageJson.version, + version: _version, }, servers: [ { @@ -152,7 +150,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/playersByRank', func: (req, res, cb) => { - db.raw( + raw( ` SELECT account_id, rating, fh_unavailable FROM players @@ -193,10 +191,10 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque route: () => '/players/:account_id', func: (req, res, cb) => { const accountId = Number(req.params.account_id); - async.parallel( + parallel( { profile(cb) { - queries.getPlayer(db, accountId, (err, playerData) => { + getPlayer(db, accountId, (err, playerData) => { if (playerData !== null && playerData !== undefined) { playerData.is_contributor = isContributor(accountId); playerData.is_subscriber = Boolean(playerData?.status); @@ -205,7 +203,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }); }, solo_competitive_rank(cb) { - db.first() + first() .from('solo_competitive_rank') .where({ account_id: accountId }) .asCallback((err, row) => { @@ -213,7 +211,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }); }, competitive_rank(cb) { - db.first() + first() .from('competitive_rank') .where({ account_id: accountId }) .asCallback((err, row) => { @@ -221,7 +219,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }); }, rank_tier(cb) { - db.first() + first() .from('rank_tier') .where({ account_id: accountId }) .asCallback((err, row) => { @@ -229,7 +227,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }); }, leaderboard_rank(cb) { - db.first() + first() .from('leaderboard_rank') .where({ account_id: accountId }) .asCallback((err, row) => { @@ -237,7 +235,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }); }, mmr_estimate(cb) { - queries.getMmrEstimate(accountId, (err, est) => + getMmrEstimate(accountId, (err, est) => cb(err, est || {}) ); }, @@ -281,7 +279,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque 'player_slot', 'radiant_win' ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -289,13 +287,13 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque return cb(err); } cache.forEach((m) => { - if (utility.isRadiant(m) === m.radiant_win) { + if (_isRadiant(m) === m.radiant_win) { result.win += 1; } else { result.lose += 1; } }); - return cacheFunctions.sendDataWithCache(req, res, result, 'wl'); + return sendDataWithCache(req, res, result, 'wl'); } ); }, @@ -331,7 +329,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/players/:account_id/recentMatches', func: (req, res, cb) => { - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, { project: req.queryObj.project.concat([ @@ -424,7 +422,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque 'party_size', ]; req.queryObj.project = req.queryObj.project.concat(additionalFields); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -463,7 +461,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque func: (req, res, cb) => { const heroes = {}; // prefill heroes with every hero - Object.keys(constants.heroes).forEach((heroId) => { + Object.keys(_heroes).forEach((heroId) => { hero_id_int = parseInt(heroId); const hero = { hero_id: hero_id_int, @@ -484,7 +482,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque 'player_slot', 'radiant_win' ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -526,7 +524,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque hero.games >= Number(req.queryObj.having) ) .sort((a, b) => b.games - a.games); - return cacheFunctions.sendDataWithCache( + return sendDataWithCache( req, res, result, @@ -569,7 +567,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque 'gold_per_min', 'xp_per_min' ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -577,7 +575,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque return cb(err); } const teammates = countPeers(cache); - return queries.getPeers( + return getPeers( db, teammates, { @@ -587,7 +585,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque if (err) { return cb(err); } - return cacheFunctions.sendDataWithCache( + return sendDataWithCache( req, res, result, @@ -630,7 +628,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque 'player_slot', 'radiant_win' ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -638,7 +636,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque return cb(err); } const teammates = countPeers(cache); - return queries.getProPeers( + return getProPeers( db, teammates, { @@ -691,7 +689,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req.queryObj.project = req.queryObj.project.concat( Object.keys(subkeys) ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -740,7 +738,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req.queryObj.project = req.queryObj.project.concat( Object.keys(countCats) ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -748,7 +746,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque return cb(err); } cache.forEach((m) => { - m.is_radiant = utility.isRadiant(m); + m.is_radiant = _isRadiant(m); Object.keys(countCats).forEach((key) => { if (!result[key][Math.floor(m[key])]) { result[key][Math.floor(m[key])] = { @@ -757,7 +755,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }; } result[key][Math.floor(m[key])].games += 1; - const won = Number(m.radiant_win === utility.isRadiant(m)); + const won = Number(m.radiant_win === _isRadiant(m)); result[key][Math.floor(m[key])].win += won; }); }); @@ -804,7 +802,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req.queryObj.project = req.queryObj.project .concat('radiant_win', 'player_slot') .concat([field].filter((f) => subkeys[f])); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -832,7 +830,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque if (bucketArray[index]) { bucketArray[index].games += 1; bucketArray[index].win += - utility.isRadiant(m) === m.radiant_win ? 1 : 0; + _isRadiant(m) === m.radiant_win ? 1 : 0; } } }); @@ -873,7 +871,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req.queryObj.project = req.queryObj.project.concat( Object.keys(result) ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -882,7 +880,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque } cache.forEach((m) => { Object.keys(result).forEach((key) => { - utility.mergeObjects(result[key], m[key]); + mergeObjects(result[key], m[key]); }); }); return res.json(result); @@ -922,7 +920,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req.queryObj.project = req.queryObj.project.concat( Object.keys(result) ); - queries.getPlayerMatches( + getPlayerMatches( req.params.account_id, req.queryObj, (err, cache) => { @@ -931,7 +929,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque } cache.forEach((m) => { Object.keys(result).forEach((key) => { - utility.mergeObjects(result[key], m[key]); + mergeObjects(result[key], m[key]); }); }); return res.json(result); @@ -967,7 +965,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/players/:account_id/ratings', func: (req, res, cb) => { - queries.getPlayerRatings(db, req.params.account_id, (err, result) => { + getPlayerRatings(db, req.params.account_id, (err, result) => { if (err) { return cb(err); } @@ -1003,7 +1001,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/players/:account_id/rankings', func: (req, res, cb) => { - queries.getPlayerHeroRankings( + getPlayerHeroRankings( req.params.account_id, (err, result) => { if (err) { @@ -1076,7 +1074,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/proPlayers', func: (req, res, cb) => { - db.select() + select() .from('players') .rightJoin( 'notable_players', @@ -1117,7 +1115,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/proMatches', func: (req, res, cb) => { - db.raw( + raw( ` SELECT match_id, duration, start_time, radiant_team_id, radiant.name as radiant_name, @@ -1196,7 +1194,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque ? `AND avg_rank_tier <= ${req.query.max_rank}` : ''; - db.raw( + raw( ` WITH match_ids AS (SELECT match_id FROM public_matches WHERE TRUE @@ -1254,7 +1252,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque const lessThan = req.query.less_than_match_id || Number.MAX_SAFE_INTEGER; - db.raw( + raw( ` SELECT * FROM parsed_matches WHERE match_id < ? @@ -1305,7 +1303,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque func: async (req, res) => { const input = req.query.sql; const client = new Client({ - connectionString: config.READONLY_POSTGRES_URL, + connectionString: READONLY_POSTGRES_URL, statement_timeout: 10000, }); client.connect(); @@ -1342,7 +1340,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/metadata', func: (req, res, cb) => { - queries.getMetadata(req, (err, result) => { + getMetadata(req, (err, result) => { if (err) { return cb(err); } @@ -1371,7 +1369,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/distributions', func: (req, res, cb) => { - queries.getDistributions(redis, (err, result) => { + getDistributions(redis, (err, result) => { if (err) { return cb(err); } @@ -1420,7 +1418,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque if ( req.query.es || - utility.checkIfInExperiment(res.locals.ip, config.ES_SEARCH_PERCENT) + checkIfInExperiment(res.locals.ip, ES_SEARCH_PERCENT) ) { return searchES(req.query, (err, result) => { if (err) { @@ -1469,7 +1467,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/rankings', func: (req, res, cb) => { - queries.getHeroRankings( + getHeroRankings( db, redis, req.query.hero_id, @@ -1515,7 +1513,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/benchmarks', func: (req, res, cb) => { - queries.getHeroBenchmarks( + getHeroBenchmarks( db, redis, { @@ -1632,7 +1630,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/request/:jobId', func: (req, res, cb) => { - queue.getJob(req.params.jobId, (err, job) => { + getJob(req.params.jobId, (err, job) => { if (err) { return cb(err); } @@ -1684,8 +1682,8 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque } if (match && match.match_id) { // match id request, get data from API - return utility.getData( - utility.generateJob('api_details', match).url, + return getData( + generateJob('api_details', match).url, async (err, body) => { if (err) { // couldn't get data from api, non-retryable @@ -1702,7 +1700,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque // Check if match is already parsed const isAlreadyParsed = Boolean( ( - await db.raw( + await raw( 'select match_id from parsed_matches where match_id = ?', [match.match_id] ) @@ -1802,8 +1800,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque const teamA = inverted ? t1 : t0; const teamB = inverted ? t0 : t1; - return db - .raw( + return raw( 'select * from hero_search where (teamA @> ? AND teamB @> ?) OR (teamA @> ? AND teamB @> ?) order by match_id desc limit 10', [teamA, teamB, teamB, teamA] ) @@ -1841,7 +1838,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/heroes', func: (req, res, cb) => { - db.select() + select() .from('heroes') .orderBy('id', 'asc') .asCallback((err, result) => { @@ -1912,7 +1909,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque route: () => '/heroes/:hero_id/matches', func: (req, res, cb) => { const heroId = req.params.hero_id; - db.raw( + raw( `SELECT matches.match_id, matches.start_time, @@ -1968,7 +1965,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque route: () => '/heroes/:hero_id/matchups', func: (req, res, cb) => { const heroId = req.params.hero_id; - db.raw( + raw( `SELECT pm2.hero_id, count(player_matches.match_id) games_played, @@ -2015,7 +2012,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque route: () => '/heroes/:hero_id/durations', func: (req, res, cb) => { const heroId = req.params.hero_id; - db.raw( + raw( `SELECT (matches.duration / 300 * 300) duration_bin, count(match_id) games_played, @@ -2062,7 +2059,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque route: () => '/heroes/:hero_id/players', func: (req, res, cb) => { const heroId = req.params.hero_id; - db.raw( + raw( `SELECT account_id, count(match_id) games_played, @@ -2108,7 +2105,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque route: () => '/heroes/:hero_id/itemPopularity', func: (req, res, cb) => { const heroId = req.params.hero_id; - queries.getHeroItemPopularity( + getHeroItemPopularity( db, redis, heroId, @@ -2146,7 +2143,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/leagues', func: (req, res, cb) => { - db.select() + select() .from('leagues') .asCallback((err, result) => { if (err) { @@ -2181,7 +2178,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/leagues/:league_id', func: (req, res, cb) => { - db.raw( + raw( `SELECT leagues.* FROM leagues WHERE leagues.leagueid = ?`, @@ -2216,7 +2213,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/leagues/:league_id/matches', func: (req, res, cb) => { - db.raw( + raw( `SELECT matches.* FROM matches WHERE matches.leagueid = ?`, @@ -2251,7 +2248,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/leagues/:league_id/teams', func: (req, res, cb) => { - db.raw( + raw( `SELECT team_rating.*, teams.* FROM matches LEFT JOIN team_match using(match_id) @@ -2304,7 +2301,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/teams', func: (req, res, cb) => { - db.raw( + raw( `SELECT team_rating.*, teams.* FROM teams LEFT JOIN team_rating using(team_id) @@ -2342,7 +2339,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/teams/:team_id', func: (req, res, cb) => { - db.raw( + raw( `SELECT team_rating.*, teams.* FROM teams LEFT JOIN team_rating using(team_id) @@ -2378,7 +2375,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/teams/:team_id/matches', func: (req, res, cb) => { - db.raw( + raw( ` SELECT team_match.match_id, radiant_win, radiant_score, dire_score, team_match.radiant, duration, start_time, leagueid, leagues.name as league_name, cluster, tm2.team_id opposing_team_id, teams2.name opposing_team_name, teams2.logo_url opposing_team_logo FROM team_match @@ -2420,7 +2417,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/teams/:team_id/players', func: (req, res, cb) => { - db.raw( + raw( `SELECT account_id, notable_players.name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins, notable_players.team_id = teams.team_id is_current_team_member FROM matches JOIN team_match USING(match_id) @@ -2461,7 +2458,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/teams/:team_id/heroes', func: (req, res, cb) => { - db.raw( + raw( `SELECT hero_id, localized_name, count(matches.match_id) games_played, sum(case when (player_matches.player_slot < 128) = matches.radiant_win then 1 else 0 end) wins FROM matches JOIN team_match USING(match_id) @@ -2505,7 +2502,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/replays', func: (req, res, cb) => { - db.select(['match_id', 'cluster', 'replay_salt']) + select(['match_id', 'cluster', 'replay_salt']) .from('match_gcdata') .whereIn( 'match_id', @@ -2664,7 +2661,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/scenarios/itemTimings', func: (req, res, cb) => { - queries.getItemTimings(req, (err, result) => { + getItemTimings(req, (err, result) => { if (err) { return cb(err); } @@ -2708,7 +2705,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/scenarios/laneRoles', func: (req, res, cb) => { - queries.getLaneRoles(req, (err, result) => { + getLaneRoles(req, (err, result) => { if (err) { return cb(err); } @@ -2741,7 +2738,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/scenarios/misc', func: (req, res, cb) => { - queries.getTeamScenarios(req, (err, result) => { + getTeamScenarios(req, (err, result) => { if (err) { return cb(err); } @@ -2774,7 +2771,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, route: () => '/schema', func: (req, res, cb) => { - db.select(['table_name', 'column_name', 'data_type']) + select(['table_name', 'column_name', 'data_type']) .from('information_schema.columns') .where({ table_schema: 'public', @@ -2884,4 +2881,4 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }; -module.exports = spec; +export default spec; diff --git a/scripts/convertSpec.js b/scripts/convertSpec.js deleted file mode 100644 index ba596231f..000000000 --- a/scripts/convertSpec.js +++ /dev/null @@ -1,4 +0,0 @@ -const fs = require('fs'); -const spec = require('../routes/spec.js'); - -fs.writeFileSync('../spec.json', JSON.stringify(spec, null, 2), 'utf-8'); diff --git a/store/archive.js b/store/archive.js index f5af37296..ee4bc2a70 100644 --- a/store/archive.js +++ b/store/archive.js @@ -1,10 +1,6 @@ -const config = require('../config'); -const { gzipSync, gunzipSync } = require('zlib'); -const { - S3Client, - PutObjectCommand, - GetObjectCommand, -} = require('@aws-sdk/client-s3'); +import config from '../config.js'; +import { gzipSync, gunzipSync } from 'zlib'; +import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'; const client = config.MATCH_ARCHIVE_S3_ENDPOINT ? new S3Client({ @@ -79,7 +75,7 @@ async function archivePut(key, blob) { } } -module.exports = { +export default { archiveGet, archivePut, }; diff --git a/store/buildMatch.js b/store/buildMatch.js index fd6cd6b91..b7ce2e8bf 100644 --- a/store/buildMatch.js +++ b/store/buildMatch.js @@ -1,21 +1,16 @@ /** * Functions to build/cache match object * */ -const constants = require('dotaconstants'); -const { promisify } = require('util'); -const config = require('../config'); -const queries = require('./queries'); -const compute = require('../util/compute'); -const utility = require('../util/utility'); -const cassandra = require('./cassandra'); -const redis = require('./redis'); -const db = require('./db'); -const { - getPlayerMatchData, - getMatchData, - insertMatchPromise, - getArchivedMatch, -} = require('./queries'); +import { heroes } from 'dotaconstants'; +import { promisify } from 'util'; +import { ENABLE_MATCH_CACHE, MATCH_CACHE_SECONDS } from '../config.js'; +import { getMatchBenchmarksPromisified } from './queries.js'; +import compute from '../util/compute.js'; +import utility, { getData, generateJob, redisCount } from '../util/utility.js'; +import { execute } from './cassandra.js'; +import { first, raw } from './db.js'; +import { getPlayerMatchData, getMatchData, insertMatchPromise, getArchivedMatch } from './queries.js'; +import redis from './redis.js'; const { computeMatchData } = compute; const { buildReplayUrl, isContributor } = utility; @@ -33,13 +28,11 @@ async function extendPlayerData(player, match) { is_contributor: isContributor(player.account_id), }; computeMatchData(p); - const row = await db - .first() + const row = await first() .from('rank_tier') .where({ account_id: p.account_id || null }); p.rank_tier = row ? row.rating : null; - const subscriber = await db - .first() + const subscriber = await first() .from('subscriber') .where({ account_id: p.account_id || null }); p.is_subscriber = Boolean(subscriber?.status); @@ -47,8 +40,7 @@ async function extendPlayerData(player, match) { } async function prodataInfo(matchId) { - const result = await db - .first([ + const result = await first([ 'radiant_team_id', 'dire_team_id', 'leagueid', @@ -62,13 +54,13 @@ async function prodataInfo(matchId) { if (!result) { return Promise.resolve({}); } - const leaguePromise = db.first().from('leagues').where({ + const leaguePromise = first().from('leagues').where({ leagueid: result.leagueid, }); - const radiantTeamPromise = db.first().from('teams').where({ + const radiantTeamPromise = first().from('teams').where({ team_id: result.radiant_team_id, }); - const direTeamPromise = db.first().from('teams').where({ + const direTeamPromise = first().from('teams').where({ team_id: result.dire_team_id, }); const [league, radiantTeam, direTeam] = await Promise.all([ @@ -90,8 +82,8 @@ async function backfill(matchId) { match_id: Number(matchId), }; await new Promise((resolve, reject) => { - utility.getData( - utility.generateJob('api_details', match).url, + getData( + generateJob('api_details', match).url, async (err, body) => { if (err) { console.error(err); @@ -108,7 +100,7 @@ async function backfill(matchId) { reject(e); } // Count for logging - utility.redisCount(redis, 'steam_api_backfill'); + redisCount(redis, 'steam_api_backfill'); resolve(); } ); @@ -123,7 +115,7 @@ async function getMatch(matchId) { // if so we prefer the archive since Cassandra may contain an unparsed version const isParsed = Boolean( ( - await db.raw( + await raw( 'select match_id from parsed_matches where match_id = ?', [matchId] ) @@ -144,7 +136,7 @@ async function getMatch(matchId) { // Still don't have it return Promise.resolve(); } - utility.redisCount(redis, 'build_match'); + redisCount(redis, 'build_match'); let playersMatchData = []; try { // If we fetched from archive we already have players @@ -161,9 +153,9 @@ async function getMatch(matchId) { e.message.startsWith('Unexpected') || e.message.includes('Attempt to access memory outside buffer bounds') ) { - utility.redisCount(redis, 'cassandra_repair'); + redisCount(redis, 'cassandra_repair'); // Delete corrupted data and backfill - await cassandra.execute( + await execute( 'DELETE FROM player_matches where match_id = ?', [Number(matchId)], { prepare: true } @@ -177,8 +169,7 @@ async function getMatch(matchId) { // Get names, last login for players from DB playersMatchData = await Promise.all( playersMatchData.map((r) => - db - .raw( + raw( ` SELECT personaname, name, last_login FROM players @@ -194,12 +185,12 @@ async function getMatch(matchId) { const playersPromise = Promise.all( playersMatchData.map((p) => extendPlayerData(p, match)) ); - const gcdataPromise = db.first().from('match_gcdata').where({ + const gcdataPromise = first().from('match_gcdata').where({ match_id: matchId, }); const cosmeticsPromise = Promise.all( Object.keys(match.cosmetics || {}).map((itemId) => - db.first().from('cosmetics').where({ + first().from('cosmetics').where({ item_id: itemId, }) ) @@ -222,7 +213,7 @@ async function getMatch(matchId) { if (cosmetics) { const playersWithCosmetics = matchResult.players.map((p) => { - const hero = constants.heroes[p.hero_id] || {}; + const hero = heroes[p.hero_id] || {}; const playerCosmetics = cosmetics .filter(Boolean) .filter( @@ -249,7 +240,7 @@ async function getMatch(matchId) { ); } const matchWithBenchmarks = - await queries.getMatchBenchmarksPromisified(matchResult); + await getMatchBenchmarksPromisified(matchResult); return Promise.resolve(matchWithBenchmarks); } @@ -263,10 +254,10 @@ async function buildMatch(matchId) { if (!match) { return Promise.resolve(); } - if (match.version && config.ENABLE_MATCH_CACHE) { - await redis.setex(key, config.MATCH_CACHE_SECONDS, JSON.stringify(match)); + if (match.version && ENABLE_MATCH_CACHE) { + await redis.setex(key, MATCH_CACHE_SECONDS, JSON.stringify(match)); } return Promise.resolve(match); } -module.exports = buildMatch; +export default buildMatch; diff --git a/store/buildSets.js b/store/buildSets.js index 0461c0a1a..81228080f 100644 --- a/store/buildSets.js +++ b/store/buildSets.js @@ -1,12 +1,12 @@ /** * Function to build/cache sets of players * */ -const async = require('async'); -const moment = require('moment'); +import { parallel } from 'async'; +import moment from 'moment'; -module.exports = function buildSets(db, redis, cb) { +export default function buildSets(db, redis, cb) { console.log('rebuilding sets'); - async.parallel( + parallel( { // users in this set are added to the trackedPlayers set subscribers(cb) { diff --git a/store/buildStatus.js b/store/buildStatus.js index e9ba0b91f..158f2c506 100644 --- a/store/buildStatus.js +++ b/store/buildStatus.js @@ -1,11 +1,10 @@ /** * Function to build status data * */ -// const config = require('../config'); -const async = require('async'); -const utility = require('../util/utility'); +import { series } from 'async'; +import { getRedisCountDay, getRedisCountHour } from '../util/utility.js'; -module.exports = function buildStatus(db, redis, cb) { +export default function buildStatus(db, redis, cb) { function generatePercentiles(arr) { // sort the list arr.sort((a, b) => Number(a) - Number(b)); @@ -21,7 +20,7 @@ module.exports = function buildStatus(db, redis, cb) { }); return result; } - async.series( + series( { user_players(cb) { redis.zcard('visitors', cb); @@ -30,58 +29,58 @@ module.exports = function buildStatus(db, redis, cb) { redis.zcard('tracked', cb); }, matches_last_day(cb) { - utility.getRedisCountDay(redis, 'added_match', cb); + getRedisCountDay(redis, 'added_match', cb); }, matches_last_hour(cb) { - utility.getRedisCountHour(redis, 'added_match', cb); + getRedisCountHour(redis, 'added_match', cb); }, retriever_matches_last_day(cb) { - utility.getRedisCountDay(redis, 'retriever', cb); + getRedisCountDay(redis, 'retriever', cb); }, retriever_players_last_day(cb) { - utility.getRedisCountDay(redis, 'retriever_player', cb); + getRedisCountDay(redis, 'retriever_player', cb); }, // backup_retriever_last_day(cb) { // utility.getRedisCountDay(redis, "backup", cb); // }, parsed_matches_last_day(cb) { - utility.getRedisCountDay(redis, 'parser', cb); + getRedisCountDay(redis, 'parser', cb); }, cached_gcdata_last_day(cb) { - utility.getRedisCountDay(redis, 'cached_gcdata', cb); + getRedisCountDay(redis, 'cached_gcdata', cb); }, requests_last_day(cb) { - utility.getRedisCountDay(redis, 'request', cb); + getRedisCountDay(redis, 'request', cb); }, requests_api_key_last_day(cb) { - utility.getRedisCountDay(redis, 'request_api_key', cb); + getRedisCountDay(redis, 'request_api_key', cb); }, steam_api_backfill_last_day(cb) { - utility.getRedisCountDay(redis, 'steam_api_backfill', cb); + getRedisCountDay(redis, 'steam_api_backfill', cb); }, match_archive_read_last_day(cb) { - utility.getRedisCountDay(redis, 'match_archive_read', cb); + getRedisCountDay(redis, 'match_archive_read', cb); }, cassandra_repair_last_day(cb) { - utility.getRedisCountDay(redis, 'cassandra_repair', cb); + getRedisCountDay(redis, 'cassandra_repair', cb); }, build_match_last_day(cb) { - utility.getRedisCountDay(redis, 'build_match', cb); + getRedisCountDay(redis, 'build_match', cb); }, error_last_day(cb) { - utility.getRedisCountDay(redis, '500_error', cb); + getRedisCountDay(redis, '500_error', cb); }, fullhistory_last_day(cb) { - utility.getRedisCountDay(redis, 'fullhistory', cb); + getRedisCountDay(redis, 'fullhistory', cb); }, skip_seq_num_last_day(cb) { - utility.getRedisCountDay(redis, 'skip_seq_num', cb); + getRedisCountDay(redis, 'skip_seq_num', cb); }, api_hits_last_day(cb) { - utility.getRedisCountDay(redis, 'api_hits', cb); + getRedisCountDay(redis, 'api_hits', cb); }, api_hits_ui_last_day(cb) { - utility.getRedisCountDay(redis, 'api_hits_ui', cb); + getRedisCountDay(redis, 'api_hits_ui', cb); }, fhQueue(cb) { redis.llen('fhQueue', cb); diff --git a/store/cacheFunctions.js b/store/cacheFunctions.js index 93dcb8b30..c41523e99 100644 --- a/store/cacheFunctions.js +++ b/store/cacheFunctions.js @@ -1,5 +1,5 @@ -const redis = require('./redis'); -const config = require('../config'); +import redis from './redis.js'; +import config from '../config.js'; const write = (req, data, cb) => { // console.log(`[WRITECACHE] cache:${req.key}:${req.account_id}`); @@ -12,7 +12,7 @@ const write = (req, data, cb) => { }; const getKeys = () => ['wl', 'heroes', 'peers', 'counts']; -module.exports = { +export default { read: (req, cb) => { // console.log(`[READCACHE] cache:${req.key}:${req.account_id}`); redis.get(`cache:${req.key}:${req.account_id}`, cb); diff --git a/store/cassandra.js b/store/cassandra.js index ecbb71045..2686bc345 100644 --- a/store/cassandra.js +++ b/store/cassandra.js @@ -1,16 +1,16 @@ /** * Interface to Cassandra client * */ -const cassandraDriver = require('cassandra-driver'); -const url = require('url'); -const config = require('../config'); +import { Client } from 'cassandra-driver'; +import { parse } from 'url'; +import config from '../config.js'; const spl = config.CASSANDRA_URL.split(','); -const cps = spl.map((u) => url.parse(u).host); +const cps = spl.map((u) => parse(u).host); console.log('connecting %s', config.CASSANDRA_URL); -const cassandra = new cassandraDriver.Client({ +const cassandra = new Client({ contactPoints: cps, localDataCenter: 'datacenter1', - keyspace: url.parse(spl[0]).path.substring(1), + keyspace: parse(spl[0]).path.substring(1), }); -module.exports = cassandra; +export default cassandra; diff --git a/store/db.js b/store/db.js index 7e9f45dc6..d54e2ca0a 100644 --- a/store/db.js +++ b/store/db.js @@ -1,9 +1,9 @@ /** * Interface to PostgreSQL client * */ -const pg = require('pg'); -const knex = require('knex'); -const config = require('../config'); +import pg from 'pg'; +import knex from 'knex'; +import config from '../config.js'; // remember: all values returned from the server are either NULL or a string pg.types.setTypeParser(20, (val) => (val === null ? null : parseInt(val, 10))); @@ -26,4 +26,4 @@ const db = knex({ // db.on('query-error', (err) => { // console.error(err); // }); -module.exports = db; +export default db; diff --git a/store/elasticsearch.js b/store/elasticsearch.js index 6e15d336c..c9328cd32 100644 --- a/store/elasticsearch.js +++ b/store/elasticsearch.js @@ -1,18 +1,18 @@ /** * Interface to ElasticSearch client * */ -const elasticsearch = require('@elastic/elasticsearch'); -const config = require('../config'); +import { Client } from '@elastic/elasticsearch'; +import config from '../config.js'; console.log('connecting %s', config.ELASTICSEARCH_URL); -const es = new elasticsearch.Client({ +const es = new Client({ node: `http://${config.ELASTICSEARCH_URL}`, apiVersion: '6.8', }); const INDEX = config.NODE_ENV === 'test' ? 'dota-test' : 'dota'; -module.exports = { +export default { es, INDEX, }; diff --git a/store/queries.js b/store/queries.js index cef09ed7b..6b958d102 100644 --- a/store/queries.js +++ b/store/queries.js @@ -1,25 +1,10 @@ /** * Provides functions to get/insert data into data stores. * */ -const async = require('async'); -const constants = require('dotaconstants'); -const util = require('util'); -const utility = require('../util/utility'); -const config = require('../config'); -const queue = require('./queue'); -const su = require('../util/scenariosUtil'); -const filter = require('../util/filter'); -const compute = require('../util/compute'); -const db = require('./db'); -const redis = require('./redis'); -const { es, INDEX } = require('./elasticsearch'); -const cassandra = require('./cassandra'); -const cacheFunctions = require('./cacheFunctions'); -const benchmarksUtil = require('../util/benchmarksUtil'); -const { archiveGet } = require('./archive'); - -const { - redisCount, +import { map, eachSeries, each, series, some, parallel } from 'async'; +import { items as _items, patch as _patch } from 'dotaconstants'; +import { format, promisify } from 'util'; +import { getStartOfBlockMinutes, getAnonymousAccountId, isProMatch, getLaneFromPosData, getPatchIndex, redisCount, convert64to32, @@ -33,8 +18,21 @@ const { countItemPopularity, - averageMedal, -} = utility; + averageMedal } from '../util/utility.js'; +import config from '../config.js'; +import { addJob } from './queue.js'; +import { teamScenariosQueryParams, metadata } from '../util/scenariosUtil.js'; +import filter from '../util/filter.js'; +import compute from '../util/compute.js'; +import db from './db.js'; +import redis from './redis.js'; +import esClient from './elasticsearch.js'; +import cassandra from './cassandra.js'; +import cacheFunctions from './cacheFunctions.js'; +import benchmarksUtil from '../util/benchmarksUtil.js'; +import { archiveGet } from './archive.js'; + +const { es, INDEX } = esClient; const { computeMatchData } = compute; const columnInfo = {}; const cassandraColumnInfo = {}; @@ -106,17 +104,17 @@ function getAPIKeys(db, cb) { * Benchmarks a match against stored data in Redis. * */ function getMatchBenchmarks(m, cb) { - async.map( + map( m.players, (p, cb) => { p.benchmarks = {}; - async.eachSeries( + eachSeries( Object.keys(benchmarks), (metric, cb) => { // Use data from previous epoch let key = [ 'benchmarks', - utility.getStartOfBlockMinutes( + getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, -1 ), @@ -125,7 +123,7 @@ function getMatchBenchmarks(m, cb) { ].join(':'); const backupKey = [ 'benchmarks', - utility.getStartOfBlockMinutes( + getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, 0 ), @@ -192,7 +190,7 @@ function getDistributions(redis, cb) { 'distribution:country_mmr', ]; const result = {}; - async.each( + each( keys, (r, cb) => { redis.get(r, (err, blob) => { @@ -312,7 +310,7 @@ function getHeroItemPopularity(db, redis, heroId, options, cb) { .filter((item) => item && item.key && item.time != null) .map((item) => { const time = parseInt(item.time, 10); - const { cost, id } = constants.items[item.key]; + const { cost, id } = _items[item.key]; return { cost, id, time }; }); @@ -346,17 +344,17 @@ function getHeroItemPopularity(db, redis, heroId, options, cb) { function getHeroBenchmarks(db, redis, options, cb) { const heroId = options.hero_id; const ret = {}; - async.each( + each( Object.keys(benchmarks), (metric, cb) => { const arr = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99]; - async.each( + each( arr, (percentile, cb) => { // Use data from previous epoch let key = [ 'benchmarks', - utility.getStartOfBlockMinutes( + getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, -1 ), @@ -365,7 +363,7 @@ function getHeroBenchmarks(db, redis, options, cb) { ].join(':'); const backupKey = [ 'benchmarks', - utility.getStartOfBlockMinutes( + getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, 0 ), @@ -434,7 +432,7 @@ function getPlayerMatches(accountId, queryObj, cb) { return cb(err); } // console.log(queryObj.project, cassandraColumnInfo.player_caches); - const query = util.format( + const query = format( ` SELECT %s FROM player_caches WHERE account_id = ? @@ -560,7 +558,7 @@ function getPeers(db, input, player, cb) { if ( numId && numId !== Number(player.account_id) && - numId !== utility.getAnonymousAccountId() && + numId !== getAnonymousAccountId() && tm.games >= 5 ) { teammatesArr.push(tm); @@ -569,7 +567,7 @@ function getPeers(db, input, player, cb) { teammatesArr.sort((a, b) => b.games - a.games); // limit to 200 max players teammatesArr = teammatesArr.slice(0, 200); - return async.each( + return each( teammatesArr, (t, cb) => { db.first( @@ -638,7 +636,7 @@ function getProPeers(db, input, player, cb) { } function getMatchRating(match, cb) { - async.map( + map( match.players, (player, cb) => { if (!player.account_id) { @@ -667,7 +665,7 @@ function getMatchRating(match, cb) { } function getMatchRankTier(match, cb) { - async.map( + map( match.players, (player, cb) => { if (!player.account_id) { @@ -701,9 +699,9 @@ function upsert(db, table, row, conflict, cb) { } const values = Object.keys(row).map(() => '?'); const update = Object.keys(row).map((key) => - util.format('%s=%s', key, `EXCLUDED.${key}`) + format('%s=%s', key, `EXCLUDED.${key}`) ); - const query = util.format( + const query = format( 'INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO UPDATE SET %s', table, Object.keys(row).join(','), @@ -727,7 +725,7 @@ function insertPlayer(db, player, indexPlayer, cb) { } if ( !player.account_id || - player.account_id === utility.getAnonymousAccountId() + player.account_id === getAnonymousAccountId() ) { return cb(); } @@ -780,7 +778,7 @@ function bulkIndexPlayer(bulkActions, cb) { } function insertPlayerRating(db, row, cb) { - async.series( + series( { pr(cb) { if ( @@ -867,7 +865,7 @@ function insertPlayerRating(db, row, cb) { } function writeCache(accountId, cache, cb) { - return async.each( + return each( cache.raw, (match, cb) => { cleanRowCassandra( @@ -879,7 +877,7 @@ function writeCache(accountId, cache, cb) { return cb(err); } const serializedMatch = serialize(cleanedMatch); - const query = util.format( + const query = format( 'INSERT INTO player_caches (%s) VALUES (%s)', Object.keys(serializedMatch).join(','), Object.keys(serializedMatch) @@ -916,12 +914,12 @@ function insertPlayerCache(match, cb) { } }); } - return async.eachSeries( + return eachSeries( players, (playerMatch, cb) => { if ( playerMatch.account_id && - playerMatch.account_id !== utility.getAnonymousAccountId() + playerMatch.account_id !== getAnonymousAccountId() ) { // join player with match to form player_match Object.keys(match).forEach((key) => { @@ -1008,7 +1006,7 @@ function createMatchCopy(match, players) { return copy; } -const insertMatchPromise = util.promisify(insertMatch); +const insertMatchPromise = promisify(insertMatch); function insertMatch(match, options, cb) { const players = match.players @@ -1024,7 +1022,7 @@ function insertMatch(match, options, cb) { // don't insert anonymous account id if (players) { players.forEach((p) => { - if (p.account_id === utility.getAnonymousAccountId()) { + if (p.account_id === getAnonymousAccountId()) { delete p.account_id; } }); @@ -1096,7 +1094,7 @@ function insertMatch(match, options, cb) { } async function upsertMatchPostgres(cb) { - if (options.type === 'api' && !utility.isProMatch(match)) { + if (options.type === 'api' && !isProMatch(match)) { // Check if the match is legit if this is API insert return cb(); } @@ -1126,13 +1124,13 @@ function insertMatch(match, options, cb) { } function upsertPlayerMatches(cb) { - async.each( + each( players || [], (pm, cb) => { pm.match_id = match.match_id; // Add lane data if (pm.lane_pos) { - const laneData = utility.getLaneFromPosData( + const laneData = getLaneFromPosData( pm.lane_pos, isRadiant(pm) ); @@ -1156,7 +1154,7 @@ function insertMatch(match, options, cb) { } function upsertPicksBans(cb) { - async.each( + each( match.picks_bans || [], (p, cb) => { // order is a reserved keyword @@ -1185,7 +1183,7 @@ function insertMatch(match, options, cb) { { match_id: match.match_id, patch: - constants.patch[utility.getPatchIndex(match.start_time)].name, + _patch[getPatchIndex(match.start_time)].name, }, { match_id: match.match_id, @@ -1212,7 +1210,7 @@ function insertMatch(match, options, cb) { radiant: false, }); } - async.each( + each( arr, (tm, cb) => { upsert( @@ -1244,7 +1242,7 @@ function insertMatch(match, options, cb) { cb(err); } - async.series( + series( { upsertMatch, upsertPlayerMatches, @@ -1281,7 +1279,7 @@ function insertMatch(match, options, cb) { if (!Object.keys(obj).length) { return cb(err); } - const query = util.format( + const query = format( 'INSERT INTO matches (%s) VALUES (%s)', Object.keys(obj).join(','), Object.keys(obj) @@ -1301,7 +1299,7 @@ function insertMatch(match, options, cb) { if (err) { return cb(err); } - return async.each( + return each( players || [], (pm, cb) => { pm.match_id = match.match_id; @@ -1313,7 +1311,7 @@ function insertMatch(match, options, cb) { if (!Object.keys(obj2).length) { return cb(err); } - const query2 = util.format( + const query2 = format( 'INSERT INTO player_matches (%s) VALUES (%s)', Object.keys(obj2).join(','), Object.keys(obj2) @@ -1388,10 +1386,10 @@ function insertMatch(match, options, cb) { } function clearRedisPlayer(cb) { - async.each( + each( (match.players || []).filter((player) => Boolean(player.account_id)), (player, cb) => { - async.each( + each( cacheFunctions.getKeys(), (key, cb) => { cacheFunctions.update({ key, account_id: player.account_id }, cb); @@ -1434,14 +1432,14 @@ function insertMatch(match, options, cb) { } function decideMmr(cb) { - async.each( + each( match.players, (p, cb) => { if ( options.origin === 'scanner' && match.lobby_type === 7 && p.account_id && - p.account_id !== utility.getAnonymousAccountId() && + p.account_id !== getAnonymousAccountId() && config.ENABLE_RANDOM_MMR_UPDATE ) { redis.rpush( @@ -1461,14 +1459,14 @@ function insertMatch(match, options, cb) { } function decideProfile(cb) { - async.each( + each( match.players, (p, cb) => { if ( match.match_id % 100 < Number(config.SCANNER_PLAYER_PERCENT) && options.origin === 'scanner' && p.account_id && - p.account_id !== utility.getAnonymousAccountId() + p.account_id !== getAnonymousAccountId() ) { upsert( db, @@ -1517,7 +1515,7 @@ function insertMatch(match, options, cb) { return cb(); } // determine if any player in the match is tracked - return async.some( + return some( match.players, (p, cb) => { redis.zscore('tracked', String(p.account_id), (err, score) => @@ -1537,7 +1535,7 @@ function insertMatch(match, options, cb) { if (hasTrackedPlayer) { priority = -2; } - return queue.addJob( + return addJob( 'parse', { data: { @@ -1561,7 +1559,7 @@ function insertMatch(match, options, cb) { } ); } - async.series( + series( { preprocess, updateMatchSeriesType, @@ -1621,7 +1619,7 @@ function getLaneRoles(req, cb) { function getTeamScenarios(req, cb) { const scenario = - (su.teamScenariosQueryParams.includes(req.query.scenario) && + (teamScenariosQueryParams.includes(req.query.scenario) && req.query.scenario) || ''; db.raw( @@ -1635,10 +1633,10 @@ function getTeamScenarios(req, cb) { } function getMetadata(req, callback) { - async.parallel( + parallel( { scenarios(cb) { - cb(null, su.metadata); + cb(null, metadata); }, banner(cb) { redis.get('banner', cb); @@ -1695,7 +1693,7 @@ async function getArchivedMatch(matchId) { try { const result = JSON.parse(await archiveGet(matchId.toString())); if (result) { - utility.redisCount(redis, 'match_archive_read'); + redisCount(redis, 'match_archive_read'); return result; } } catch(e) { @@ -1704,7 +1702,7 @@ async function getArchivedMatch(matchId) { return null; } -module.exports = { +export default { upsert, insertPlayer, bulkIndexPlayer, diff --git a/store/queue.js b/store/queue.js index 1e324c6bb..50ea500b1 100644 --- a/store/queue.js +++ b/store/queue.js @@ -1,10 +1,10 @@ /** * Provides methods for working with the job queue * */ -const moment = require('moment'); -const async = require('async'); -const redis = require('./redis'); -const db = require('./db'); +import moment from 'moment'; +import { forever } from 'async'; +import redis from './redis.js'; +import db from './db.js'; function runQueue(queueName, parallelism, processor) { function processOneJob(cb) { @@ -22,7 +22,7 @@ function runQueue(queueName, parallelism, processor) { }); } for (let i = 0; i < parallelism; i += 1) { - async.forever(processOneJob, (err) => { + forever(processOneJob, (err) => { throw err; }); } @@ -85,7 +85,7 @@ function runReliableQueue(queueName, parallelism, processor) { }); } for (let i = 0; i < parallelism; i += 1) { - async.forever(processOneJob, (err) => { + forever(processOneJob, (err) => { throw err; }); } @@ -123,7 +123,7 @@ function getJob(jobId, cb) { ); } -module.exports = { +export default { runQueue, runReliableQueue, addJob, diff --git a/store/redis.js b/store/redis.js index 9ac6a532a..e0dc496ca 100644 --- a/store/redis.js +++ b/store/redis.js @@ -1,15 +1,15 @@ /** * Interface to Redis client * */ -const redis = require('redis'); -const config = require('../config'); +import { createClient } from 'redis'; +import config from '../config.js'; console.log('connecting %s', config.REDIS_URL); -const client = redis.createClient(config.REDIS_URL, { +const client = createClient(config.REDIS_URL, { detect_buffers: true, }); client.on('error', (err) => { console.error(err); process.exit(1); }); -module.exports = client; +export default client; diff --git a/store/search.js b/store/search.js index be6e22a52..28a688d22 100644 --- a/store/search.js +++ b/store/search.js @@ -1,15 +1,15 @@ /** * Methods for search functionality * */ -const async = require('async'); -const db = require('./db'); +import { parallel } from 'async'; +import { first, raw } from './db.js'; /** * @param db - database object * @param search - object for where parameter of query * @param cb - callback */ function findPlayer(search, cb) { - db.first(['account_id', 'personaname', 'avatarfull']) + first(['account_id', 'personaname', 'avatarfull']) .from('players') .where(search) .asCallback(cb); @@ -17,7 +17,7 @@ function findPlayer(search, cb) { function search(options, cb) { const query = options.q; - async.parallel( + parallel( { account_id(callback) { if (Number.isNaN(Number(query))) { @@ -31,7 +31,7 @@ function search(options, cb) { ); }, personaname(callback) { - db.raw( + raw( ` SELECT * FROM (SELECT account_id, avatarfull, personaname, last_match_time @@ -63,4 +63,4 @@ function search(options, cb) { } ); } -module.exports = search; +export default search; diff --git a/store/searchES.js b/store/searchES.js index 7f20e23ea..5d2c3f6a9 100644 --- a/store/searchES.js +++ b/store/searchES.js @@ -1,16 +1,16 @@ /** * Methods for search functionality * */ -const async = require('async'); -const db = require('./db'); -const { es, INDEX } = require('./elasticsearch'); +import { parallel } from 'async'; +import { first } from './db.js'; +import { es, INDEX } from './elasticsearch.js'; /** * @param db - database object * @param search - object for where parameter of query * @param cb - callback */ function findPlayer(search, cb) { - db.first(['account_id', 'personaname', 'avatarfull']) + first(['account_id', 'personaname', 'avatarfull']) .from('players') .where(search) .asCallback(cb); @@ -18,7 +18,7 @@ function findPlayer(search, cb) { function search(options, cb) { const query = options.q; - async.parallel( + parallel( { account_id(callback) { if (Number.isNaN(Number(query))) { @@ -80,4 +80,4 @@ function search(options, cb) { } ); } -module.exports = search; +export default search; diff --git a/svc/apiadmin.js b/svc/apiadmin.js index 6a056146b..d9fdd75a6 100644 --- a/svc/apiadmin.js +++ b/svc/apiadmin.js @@ -1,13 +1,13 @@ -const async = require('async'); -const moment = require('moment'); -const stripeLib = require('stripe'); -const redis = require('../store/redis'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const queries = require('../store/queries'); -const config = require('../config'); +import { eachOfLimit } from 'async'; +import moment, { unix } from 'moment'; +import stripeLib from 'stripe'; +import redis from '../store/redis.js'; +import db, { from, raw } from '../store/db.js'; +import utility from '../util/utility.js'; +import { getAPIKeys } from '../store/queries.js'; +import { STRIPE_SECRET, ENABLE_API_LIMIT, STRIPE_API_PLAN, API_BILLING_UNIT } from '../config.js'; -const stripe = stripeLib(config.STRIPE_SECRET); +const stripe = stripeLib(STRIPE_SECRET); const { invokeInterval } = utility; function storeUsageCounts(cursor, cb) { @@ -20,7 +20,7 @@ function storeUsageCounts(cursor, cb) { const apiTimestamp = moment().startOf('day'); - async.eachOfLimit( + eachOfLimit( values, 5, (e, i, cb2) => { @@ -28,18 +28,18 @@ function storeUsageCounts(cursor, cb) { cb2(); } else if (e.includes(':')) { cb2(); - } else if (config.ENABLE_API_LIMIT) { + } else if (ENABLE_API_LIMIT) { const split = e; // console.log("Updating usage for", e, "usage", values[i + 1]); let apiRecord; - db.from('api_keys') + from('api_keys') .where({ api_key: split, }) .then((rows) => { if (rows.length > 0) { [apiRecord] = rows; - return db.raw( + return raw( ` INSERT INTO api_key_usage (account_id, api_key, customer_id, timestamp, ip, usage_count) VALUES @@ -89,7 +89,7 @@ function storeUsageCounts(cursor, cb) { async function updateStripeUsage(cb) { const options = { - plan: config.STRIPE_API_PLAN, + plan: STRIPE_API_PLAN, limit: 100, // From the docs: // By default, returns a list of subscriptions that have not been canceled. @@ -105,7 +105,7 @@ async function updateStripeUsage(cb) { // updateAPIKeysInRedis deletes the keys so just do that there if (sub.status === 'canceled') { console.log('updateStripeUsage CANCELED SUBSCRIPTION', sub.id); - await db.raw( + await raw( ` UPDATE api_keys SET is_canceled = true WHERE subscription_id = ? `, @@ -115,12 +115,11 @@ async function updateStripeUsage(cb) { continue; } - const startTime = moment - .unix(sub.current_period_end - 1) + const startTime = unix(sub.current_period_end - 1) .startOf('month'); - const endTime = moment.unix(sub.current_period_end - 1).endOf('month'); + const endTime = unix(sub.current_period_end - 1).endOf('month'); - const res = await db.raw( + const res = await raw( ` SELECT SUM(usage) as usage_count @@ -148,7 +147,7 @@ async function updateStripeUsage(cb) { // TODO(albert): We could break this out by day for the invoice // but we'd have to make changes to web.js and metrics await stripe.subscriptionItems.createUsageRecord(sub.items.data[0].id, { - quantity: Math.ceil(usageCount / config.API_BILLING_UNIT), + quantity: Math.ceil(usageCount / API_BILLING_UNIT), action: 'set', timestamp: sub.current_period_end - 1, }); @@ -156,7 +155,7 @@ async function updateStripeUsage(cb) { 'updateStripeUsage updated', sub.id, usageCount, - Math.ceil(usageCount / config.API_BILLING_UNIT) + Math.ceil(usageCount / API_BILLING_UNIT) ); } else { // console.log(`updateStripeUsage No usage for ${sub.id}`); @@ -172,7 +171,7 @@ async function updateStripeUsage(cb) { invokeInterval( (cb) => { - queries.getAPIKeys(db, (err, rows) => { + getAPIKeys(db, (err, rows) => { if (err) { cb(err); } else if (rows.length > 0) { diff --git a/svc/autofullhistory.js b/svc/autofullhistory.js index 1ef7e37be..2a4a8276a 100644 --- a/svc/autofullhistory.js +++ b/svc/autofullhistory.js @@ -1,22 +1,22 @@ /** * Worker to auto-queue full history requests for random players * */ -const async = require('async'); -const db = require('../store/db'); -const redis = require('../store/redis'); +import { each } from 'async'; +import { raw } from '../store/db.js'; +import { rpush } from '../store/redis.js'; function getSummaries(cb) { - db.raw( + raw( "SELECT account_id from players TABLESAMPLE SYSTEM_ROWS(100) where last_match_time > (now() - interval '7 day')" ).asCallback((err, result) => { if (err) { return cb(err); } console.log(result.rows); - return async.each( + return each( result.rows, (row, cb) => { - return redis.rpush( + return rpush( 'fhQueue', JSON.stringify({ account_id: row.account_id, diff --git a/svc/backupscanner.js b/svc/backupscanner.js index fb8648814..9245696be 100644 --- a/svc/backupscanner.js +++ b/svc/backupscanner.js @@ -1,13 +1,13 @@ -const async = require('async'); -const utility = require('../util/utility'); -const redis = require('../store/redis'); -const queries = require('../store/queries'); -const config = require('../config'); +import { eachLimit } from 'async'; +import utility from '../util/utility.js'; +import queries from '../store/queries.js'; +import { STEAM_API_KEY, STEAM_API_HOST } from '../config.js'; +import redis from '../store/redis.js'; const { generateJob, getData } = utility; const { insertMatchPromise } = queries; -const apiKeys = config.STEAM_API_KEY.split(','); -const apiHosts = config.STEAM_API_HOST.split(','); +const apiKeys = STEAM_API_KEY.split(','); +const apiHosts = STEAM_API_HOST.split(','); const parallelism = Math.min(apiHosts.length * 1, apiKeys.length); const delay = 1000; @@ -80,7 +80,7 @@ function processPlayer(accountId, cb) { const matches = body.result.matches .filter((m) => m.match_seq_num > Number(res)) .map((m) => m.match_id); - return async.eachLimit(matches, 1, processMatch, cb); + return eachLimit(matches, 1, processMatch, cb); }); } ); @@ -96,7 +96,7 @@ function start(err) { if (err) { throw err; } - async.eachLimit(ids, parallelism, processPlayer, start); + eachLimit(ids, parallelism, processPlayer, start); }); }, 1000); } diff --git a/svc/benchmarks.js b/svc/benchmarks.js index 989f6ea55..4deabae6f 100644 --- a/svc/benchmarks.js +++ b/svc/benchmarks.js @@ -1,16 +1,16 @@ -const queue = require('../store/queue'); -const benchmarksUtil = require('../util/benchmarksUtil'); -const buildMatch = require('../store/buildMatch'); -const utility = require('../util/utility'); -const config = require('../config'); -const redis = require('../store/redis'); +import { runQueue } from '../store/queue.js'; +import benchmarksUtil from '../util/benchmarksUtil.js'; +import buildMatch from '../store/buildMatch.js'; +import { isSignificant, getStartOfBlockMinutes } from '../util/utility.js'; +import { BENCHMARK_RETENTION_MINUTES } from '../config.js'; +import redis from '../store/redis.js'; const { benchmarks } = benchmarksUtil; async function doBenchmarks(matchID, cb) { try { const match = await buildMatch(matchID); - if (match.players && utility.isSignificant(match)) { + if (match.players && isSignificant(match)) { for (let i = 0; i < match.players.length; i += 1) { const p = match.players[i]; // only do if all players have heroes @@ -24,8 +24,8 @@ async function doBenchmarks(matchID, cb) { ) { const rkey = [ 'benchmarks', - utility.getStartOfBlockMinutes( - config.BENCHMARK_RETENTION_MINUTES, + getStartOfBlockMinutes( + BENCHMARK_RETENTION_MINUTES, 0 ), key, @@ -33,8 +33,8 @@ async function doBenchmarks(matchID, cb) { ].join(':'); redis.zadd(rkey, metric, match.match_id); // expire at time two epochs later (after prev/current cycle) - const expiretime = utility.getStartOfBlockMinutes( - config.BENCHMARK_RETENTION_MINUTES, + const expiretime = getStartOfBlockMinutes( + BENCHMARK_RETENTION_MINUTES, 2 ); redis.expireat(rkey, expiretime); @@ -50,4 +50,4 @@ async function doBenchmarks(matchID, cb) { } } -queue.runQueue('parsedBenchmarksQueue', 1, doBenchmarks); +runQueue('parsedBenchmarksQueue', 1, doBenchmarks); diff --git a/svc/buildsets.js b/svc/buildsets.js index 7f30fea4d..86a10e6ba 100644 --- a/svc/buildsets.js +++ b/svc/buildsets.js @@ -1,7 +1,7 @@ -const buildSets = require('../store/buildSets'); -const redis = require('../store/redis'); -const db = require('../store/db'); -const utility = require('../util/utility'); +import buildSets from '../store/buildSets.js'; +import db from '../store/db.js'; +import utility from '../util/utility.js'; +import redis from '../store/redis.js'; const { invokeInterval } = utility; diff --git a/svc/cassandraDelete.js b/svc/cassandraDelete.js index 9e5ac5d50..8fd4fd18d 100644 --- a/svc/cassandraDelete.js +++ b/svc/cassandraDelete.js @@ -1,12 +1,12 @@ -const crypto = require('crypto'); -const cassandra = require('../store/cassandra'); -const db = require('../store/db'); -const { archivePut } = require('../store/archive'); -const { getMatchData, getPlayerMatchData } = require('../store/queries'); -const config = require('../config'); +import { randomBytes } from 'crypto'; +import cassandra from '../store/cassandra.js'; +import db from '../store/db.js'; +import { archivePut } from '../store/archive.js'; +import{ getMatchData, getPlayerMatchData } from '../store/queries.js'; +import config from '../config.js'; function genRandomNumber(byteCount, radix) { - return BigInt(`0x${crypto.randomBytes(byteCount).toString('hex')}`).toString( + return BigInt(`0x${randomBytes(byteCount).toString('hex')}`).toString( radix ); } diff --git a/svc/cosmetics.js b/svc/cosmetics.js index c40e9376c..7d38c7236 100644 --- a/svc/cosmetics.js +++ b/svc/cosmetics.js @@ -1,13 +1,13 @@ -const vdf = require('simple-vdf'); -const async = require('async'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const queries = require('../store/queries'); +import { parse } from 'simple-vdf'; +import { eachLimit } from 'async'; +import db from '../store/db.js'; +import utility, { getData, generateJob } from '../util/utility.js'; +import { upsert } from '../store/queries.js'; const { invokeInterval, cleanItemSchema } = utility; function doCosmetics(cb) { - utility.getData( + getData( { url: 'https://raw.githubusercontent.com/SteamDatabase/GameTracking-Dota2/master/game/dota/pak01_dir/scripts/items/items_game.txt', raw: true, @@ -16,9 +16,9 @@ function doCosmetics(cb) { if (err) { return cb(err); } - const itemData = vdf.parse(cleanItemSchema(body)); + const itemData = parse(cleanItemSchema(body)); console.log(Object.keys(itemData.items_game.items).length); - return async.eachLimit( + return eachLimit( Object.keys(itemData.items_game.items), 10, (itemId, cb) => { @@ -31,7 +31,7 @@ function doCosmetics(cb) { function insert(cb) { // console.log(item); - return queries.upsert( + return upsert( db, 'cosmetics', item, @@ -51,9 +51,9 @@ function doCosmetics(cb) { if (item.image_inventory) { const spl = item.image_inventory.split('/'); const iconname = spl[spl.length - 1]; - return utility.getData( + return getData( { - url: utility.generateJob('api_item_icon', { + url: generateJob('api_item_icon', { iconname, }).url, noRetry: true, diff --git a/svc/counts.js b/svc/counts.js index 48e511433..ad8e8326c 100644 --- a/svc/counts.js +++ b/svc/counts.js @@ -1,14 +1,14 @@ /** * Worker to update counts based on incoming match data * */ -const async = require('async'); -const moment = require('moment'); -const redis = require('../store/redis'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const queries = require('../store/queries'); -const queue = require('../store/queue'); -const config = require('../config'); +import { each, series, parallel } from 'async'; +import moment from 'moment'; +import db, { select, raw, transaction } from '../store/db.js'; +import utility, { getAnonymousAccountId as _getAnonymousAccountId } from '../util/utility.js'; +import queries from '../store/queries.js'; +import { runQueue } from '../store/queue.js'; +import { PUBLIC_SAMPLE_PERCENT } from '../config.js'; +import redis from '../store/redis.js'; const { getMatchRankTier, @@ -29,7 +29,7 @@ function updateHeroRankings(match, cb) { if (!matchScore) { return cb(); } - return async.each( + return each( match.players, (player, cb) => { if ( @@ -43,8 +43,7 @@ function updateHeroRankings(match, cb) { // Treat the result as an Elo rating change where the opponent is the average rank tier of the match * 100 const win = Number(isRadiant(player) === player.radiant_win); const kFactor = 100; - return db - .select('score') + return select('score') .from('hero_ranking') .where({ account_id: player.account_id, @@ -62,8 +61,7 @@ function updateHeroRankings(match, cb) { const e1 = r1 / (r1 + r2); const ratingDiff1 = kFactor * (win - e1); const newScore = currRating1 + ratingDiff1; - return db - .raw( + return raw( 'INSERT INTO hero_ranking VALUES(?, ?, ?) ON CONFLICT(account_id, hero_id) DO UPDATE SET score = ?', [player.account_id, player.hero_id, newScore, newScore] ) @@ -78,15 +76,14 @@ function updateHeroRankings(match, cb) { function updateMmrEstimate(match, cb) { getMatchRating(match, (err, avg) => { if (avg && !Number.isNaN(Number(avg))) { - return async.each( + return each( match.players, (player, cb) => { if ( player.account_id && - player.account_id !== utility.getAnonymousAccountId() + player.account_id !== _getAnonymousAccountId() ) { - return db - .raw( + return raw( ` INSERT INTO mmr_estimates VALUES(?, ?) ON CONFLICT(account_id) @@ -116,7 +113,7 @@ function upsertMatchSample(match, cb) { if (!avgRankTier || numRankTier < 2) { return cb(); } - return db.transaction((trx) => { + return transaction((trx) => { function upsertMatchSample(cb) { const matchMmrData = { avg_mmr: avg || null, @@ -137,7 +134,7 @@ function upsertMatchSample(match, cb) { } function upsertPlayerMatchesSample(cb) { - async.each( + each( match.players || [], (pm, cb) => { pm.match_id = match.match_id; @@ -166,7 +163,7 @@ function upsertMatchSample(match, cb) { cb(err); } - async.series( + series( { upsertMatchSample, upsertPlayerMatchesSample, @@ -239,7 +236,7 @@ function updateLastPlayed(match, cb) { } }); - async.each( + each( filteredPlayers, (player, cb) => { insertPlayer( @@ -288,8 +285,7 @@ function updateHeroSearch(match, cb) { const teamB = inverted ? radiant : dire; const teamAWin = inverted ? !match.radiant_win : match.radiant_win; - return db - .raw( + return raw( 'INSERT INTO hero_search (match_id, teamA, teamB, teamAWin, start_time) VALUES (?, ?, ?, ?, ?)', [match.match_id, teamA, teamB, teamAWin, match.start_time] ) @@ -347,7 +343,7 @@ function updateMatchups(match, cb) { function processCounts(match, cb) { console.log('match %s', match.match_id); - return async.parallel( + return parallel( { updateRankings(cb) { if (isSignificant(match)) { @@ -364,7 +360,7 @@ function processCounts(match, cb) { upsertMatchSample(cb) { if ( isSignificant(match) && - match.match_id % 100 < config.PUBLIC_SAMPLE_PERCENT + match.match_id % 100 < PUBLIC_SAMPLE_PERCENT ) { return upsertMatchSample(match, cb); } @@ -407,4 +403,4 @@ function processCounts(match, cb) { ); } -queue.runQueue('countsQueue', 1, processCounts); +runQueue('countsQueue', 1, processCounts); diff --git a/svc/distributions.js b/svc/distributions.js index 65f33dc4e..6360b8a13 100644 --- a/svc/distributions.js +++ b/svc/distributions.js @@ -1,16 +1,16 @@ -const fs = require('fs'); -const async = require('async'); -const constants = require('dotaconstants'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const utility = require('../util/utility'); +import { readdirSync, readFileSync } from 'fs'; +import { parallel } from 'async'; +import { countries } from 'dotaconstants'; +import { raw } from '../store/db.js'; +import utility from '../util/utility.js'; +import redis from '../store/redis.js'; const { invokeInterval } = utility; const sql = {}; -const sqlq = fs.readdirSync('./sql'); +const sqlq = readdirSync('./sql'); sqlq.forEach((f) => { - sql[f.split('.')[0]] = fs.readFileSync(`./sql/${f}`, 'utf8'); + sql[f.split('.')[0]] = readFileSync(`./sql/${f}`, 'utf8'); }); function mapMmr(results) { @@ -39,7 +39,7 @@ function mapMmr(results) { function mapCountry(results) { results.rows = results.rows.map((r) => { - const ref = constants.countries[r.loccountrycode]; + const ref = countries[r.loccountrycode]; r.common = ref ? ref.name.common : r.loccountrycode; return r; }); @@ -47,7 +47,7 @@ function mapCountry(results) { } function loadData(key, mapFunc, cb) { - db.raw(sql[key]).asCallback((err, results) => { + raw(sql[key]).asCallback((err, results) => { if (err) { return cb(err); } @@ -56,7 +56,7 @@ function loadData(key, mapFunc, cb) { } function doDistributions(cb) { - async.parallel( + parallel( { country_mmr(cb) { loadData('country_mmr', mapCountry, cb); diff --git a/svc/fullhistory.js b/svc/fullhistory.js index 78888ad40..9571de670 100644 --- a/svc/fullhistory.js +++ b/svc/fullhistory.js @@ -1,18 +1,18 @@ /** * Worker to fetch full match histories for players * */ -const async = require('async'); -const urllib = require('url'); -const constants = require('dotaconstants'); -const config = require('../config'); -const { redisCount, getData, generateJob } = require('../util/utility'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const queue = require('../store/queue'); -const queries = require('../store/queries'); +import { eachLimit } from 'async'; +import { parse as _parse, format } from 'url'; +import constants from 'dotaconstants'; +import { STEAM_API_KEY } from '../config.js'; +import { redisCount, getData, generateJob } from '../util/utility.js'; +import db from '../store/db.js'; +import { runQueue } from '../store/queue.js'; +import queries, { getPlayerMatches } from '../store/queries.js'; +import redis from '../store/redis.js'; const { insertMatchPromise } = queries; -const apiKeys = config.STEAM_API_KEY.split(','); +const apiKeys = STEAM_API_KEY.split(','); // number of api requests to send at once const parallelism = Math.min(20, apiKeys.length); @@ -63,10 +63,10 @@ function processFullHistory(job, cb) { return cb(err); } // paginate through to max 500 games if necessary with start_at_match_id= - const parse = urllib.parse(url, true); + const parse = _parse(url, true); parse.query.start_at_match_id = startId - 1; parse.search = null; - url = urllib.format(parse); + url = format(parse); return getApiMatchPage(player, url, cb); }); } @@ -81,7 +81,7 @@ function processFullHistory(job, cb) { const heroArray = ['0']; // use steamapi via specific player history and specific hero id (up to 500 games per hero) player.match_ids = {}; - return async.eachLimit( + return eachLimit( heroArray, parallelism, (heroId, cb) => { @@ -104,7 +104,7 @@ function processFullHistory(job, cb) { updatePlayer(player, cb); } else { // check what matches the player is already associated with - queries.getPlayerMatches( + getPlayerMatches( player.account_id, { project: ['match_id'], @@ -126,7 +126,7 @@ function processFullHistory(job, cb) { delete player.match_ids[matchId]; } // iterate through keys, make api_details requests - return async.eachLimit( + return eachLimit( Object.keys(player.match_ids), parallelism, (matchId, cb) => { @@ -164,4 +164,4 @@ function processFullHistory(job, cb) { ); } -queue.runQueue('fhQueue', 1, processFullHistory); +runQueue('fhQueue', 1, processFullHistory); diff --git a/svc/gcdata.js b/svc/gcdata.js index 59383fb41..fd62af5b3 100644 --- a/svc/gcdata.js +++ b/svc/gcdata.js @@ -1,10 +1,10 @@ /** * Worker to fetch GC (Game Coordinator) data for matches * */ -const getGcData = require('../util/getGcData'); -const queue = require('../store/queue'); -const config = require('../config'); -const utility = require('../util/utility'); +import getGcData from '../util/getGcData.js'; +import { runQueue } from '../store/queue.js'; +import { GCDATA_PARALLELISM } from '../config.js'; +import utility from '../util/utility.js'; const { getRetrieverArr } = utility; const retrieverArr = getRetrieverArr(); @@ -14,8 +14,8 @@ function processGcData(job, cb) { getGcData(job, cb); } -queue.runQueue( +runQueue( 'gcQueue', - Number(config.GCDATA_PARALLELISM) * retrieverArr.length, + Number(GCDATA_PARALLELISM) * retrieverArr.length, processGcData ); diff --git a/svc/heroes.js b/svc/heroes.js index 21ec5f306..6f2e6adc1 100644 --- a/svc/heroes.js +++ b/svc/heroes.js @@ -1,7 +1,7 @@ -const async = require('async'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const queries = require('../store/queries'); +import { eachSeries } from 'async'; +import db from '../store/db.js'; +import utility from '../util/utility.js'; +import { upsert } from '../store/queries.js'; const { invokeInterval, generateJob, getData } = utility; @@ -22,11 +22,11 @@ function doHeroes(cb) { if (err || !heroData) { return cb(); } - return async.eachSeries( + return eachSeries( body.result.heroes, (hero, cb) => { const heroDataHero = heroData[hero.id] || {}; - queries.upsert( + upsert( db, 'heroes', { diff --git a/svc/herostats.js b/svc/herostats.js index b8ed855d3..2ebd994dc 100644 --- a/svc/herostats.js +++ b/svc/herostats.js @@ -1,19 +1,19 @@ -const constants = require('dotaconstants'); -const moment = require('moment'); -const async = require('async'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const utility = require('../util/utility'); +import { heroes } from 'dotaconstants'; +import moment from 'moment'; +import { parallel } from 'async'; +import { raw } from '../store/db.js'; +import utility from '../util/utility.js'; +import redis from '../store/redis.js'; const { invokeInterval } = utility; function doHeroStats(cb) { const minTime = moment().subtract(30, 'day').format('X'); const maxTime = moment().format('X'); - async.parallel( + parallel( { publicHeroes(cb) { - db.raw( + raw( ` SELECT floor(avg_rank_tier / 10) as rank_tier, @@ -37,7 +37,7 @@ function doHeroStats(cb) { ).asCallback(cb); }, proHeroes(cb) { - db.raw( + raw( ` SELECT sum(case when radiant_win = (player_slot < 128) then 1 else 0 end) as pro_win, @@ -56,7 +56,7 @@ function doHeroStats(cb) { ).asCallback(cb); }, proBans(cb) { - db.raw( + raw( ` SELECT count(hero_id) as pro_ban, @@ -95,7 +95,7 @@ function doHeroStats(cb) { return cb(err); } // Build object keyed by hero_id for each result array - const objectResponse = JSON.parse(JSON.stringify(constants.heroes)); + const objectResponse = JSON.parse(JSON.stringify(heroes)); Object.keys(result).forEach((key) => { result[key].rows.forEach((row) => { objectResponse[row.hero_id] = { diff --git a/svc/items.js b/svc/items.js index 6291285c5..caac6a08e 100644 --- a/svc/items.js +++ b/svc/items.js @@ -1,25 +1,25 @@ -const async = require('async'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const queries = require('../store/queries'); +import { eachSeries } from 'async'; +import db from '../store/db.js'; +import utility, { generateJob, getData } from '../util/utility.js'; +import { upsert } from '../store/queries.js'; const { invokeInterval } = utility; function doItems(cb) { - const container = utility.generateJob('api_items', { + const container = generateJob('api_items', { language: 'english', }); - utility.getData(container.url, (err, body) => { + getData(container.url, (err, body) => { if (err) { return cb(err); } if (!body || !body.result || !body.result.items) { return cb(); } - return async.eachSeries( + return eachSeries( body.result.items, (item, cb) => { - queries.upsert( + upsert( db, 'items', item, diff --git a/svc/leagues.js b/svc/leagues.js index 80ba5fea5..e0022f0bc 100644 --- a/svc/leagues.js +++ b/svc/leagues.js @@ -1,7 +1,7 @@ -const async = require('async'); -const utility = require('../util/utility'); -const db = require('../store/db'); -const queries = require('../store/queries'); +import { each } from 'async'; +import utility from '../util/utility.js'; +import db from '../store/db.js'; +import { upsert } from '../store/queries.js'; const { invokeInterval, generateJob, getData } = utility; @@ -12,7 +12,7 @@ function doLeagues(cb) { return cb(err); } - return async.each( + return each( apiLeagues.infos, (league, cb) => { const openQualifierTier = @@ -30,7 +30,7 @@ function doLeagues(cb) { league.ticket = null; league.banner = null; league.leagueid = league.league_id; - queries.upsert( + upsert( db, 'leagues', league, diff --git a/svc/livegames.js b/svc/livegames.js index e0e3ddd16..6341b8763 100644 --- a/svc/livegames.js +++ b/svc/livegames.js @@ -1,29 +1,29 @@ -const async = require('async'); -const JSONbig = require('json-bigint'); -const request = require('request'); -const redis = require('../store/redis'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const config = require('../config'); +import { eachSeries } from 'async'; +import { parse } from 'json-bigint'; +import { get } from 'request'; +import { select } from '../store/db.js'; +import utility from '../util/utility.js'; +import { STEAM_API_KEY } from '../config.js'; +import redis from '../store/redis.js'; const { invokeInterval } = utility; function doLiveGames(cb) { // Get the list of pro players - db.select() + select() .from('notable_players') .asCallback((err, proPlayers) => { // Get the list of live games - const apiKeys = config.STEAM_API_KEY.split(','); + const apiKeys = STEAM_API_KEY.split(','); const liveGamesUrl = `https://api.steampowered.com/IDOTA2Match_570/GetTopLiveGame/v1/?key=${apiKeys[0]}&partner=0`; - request.get(liveGamesUrl, (err, resp, body) => { + get(liveGamesUrl, (err, resp, body) => { if (err) { return cb(err); } - const json = JSONbig.parse(body); + const json = parse(body); // If a match contains a pro player // add their name to the match object, save it to redis zset, keyed by server_steam_id - return async.eachSeries( + return eachSeries( json.game_list, (match, cb) => { // let addToRedis = false; diff --git a/svc/migrater.js b/svc/migrater.js index ba532b9b7..55598e017 100644 --- a/svc/migrater.js +++ b/svc/migrater.js @@ -1,17 +1,16 @@ -const async = require('async'); -const fs = require('fs'); -const db = require('../store/db'); -// const cassandra = require('../store/cassandra'); -const utility = require('../util/utility'); +import { series } from 'async'; +import { readFileSync } from 'fs'; +import { raw } from '../store/db.js'; +import utility from '../util/utility.js'; -const sqlQuery = fs.readFileSync('./sql/create_tables.sql', 'utf8'); +const sqlQuery = readFileSync('./sql/create_tables.sql', 'utf8'); // const cassQuery = fs.readFileSync('./sql/create_tables.cql', 'utf8'); const { invokeInterval } = utility; function doMigrate(cb) { - async.series( + series( { - sql: (cb) => db.raw(sqlQuery).asCallback(cb), + sql: (cb) => raw(sqlQuery).asCallback(cb), /* cassandra: cb => async.eachSeries(cassQuery.split(';').filter(cql => cql.length > 1), (cql, cb) => { diff --git a/svc/mmr.js b/svc/mmr.js index ffe01c60c..409691308 100644 --- a/svc/mmr.js +++ b/svc/mmr.js @@ -1,12 +1,12 @@ /** * Worker to fetch MMR and Dota Plus data for players * */ -const queue = require('../store/queue'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const { insertPlayer, insertPlayerRating } = require('../store/queries'); -const config = require('../config'); -const { getData, redisCount, getRetrieverArr } = require('../util/utility'); +import { runQueue } from '../store/queue.js'; +import db from '../store/db.js'; +import { insertPlayer, insertPlayerRating } from '../store/queries.js'; +import { RETRIEVER_SECRET, MMR_PARALLELISM } from '../config.js'; +import { getData, redisCount, getRetrieverArr } from '../util/utility.js'; +import redis from '../store/redis.js'; const retrieverArr = getRetrieverArr(); @@ -17,7 +17,7 @@ function processMmr(job, cb) { } const accountId = job.account_id; const urls = retrieverArr.map( - (r) => `http://${r}?key=${config.RETRIEVER_SECRET}&account_id=${accountId}` + (r) => `http://${r}?key=${RETRIEVER_SECRET}&account_id=${accountId}` ); return getData({ url: urls }, (err, data) => { if (err) { @@ -47,8 +47,8 @@ function processMmr(job, cb) { }); } -queue.runQueue( +runQueue( 'mmrQueue', - config.MMR_PARALLELISM * retrieverArr.length, + MMR_PARALLELISM * retrieverArr.length, processMmr ); diff --git a/svc/monitor.js b/svc/monitor.js index 4befa4d45..79ef17132 100644 --- a/svc/monitor.js +++ b/svc/monitor.js @@ -1,14 +1,14 @@ /** * Worker that monitors health metrics and saves results * */ -const request = require('request'); -const config = require('../config'); -const redis = require('../store/redis'); -const db = require('../store/db'); -const cassandra = require('../store/cassandra'); -const utility = require('../util/utility'); +import request from 'request'; +import { STEAM_API_KEY } from '../config.js'; +import { hset, expire, get, llen, info, server_info } from '../store/redis.js'; +import { raw } from '../store/db.js'; +import { execute } from '../store/cassandra.js'; +import { getData, generateJob } from '../util/utility.js'; -const apiKey = config.STEAM_API_KEY.split(',')[0]; +const apiKey = STEAM_API_KEY.split(',')[0]; function invokeInterval(func) { // invokes the function immediately, waits for callback, waits the delay, and then calls it again @@ -24,8 +24,8 @@ function invokeInterval(func) { threshold: 0, }; final.timestamp = Math.floor(new Date() / 1000); - redis.hset('health', func.name, JSON.stringify(final)); - redis.expire('health', 900); + hset('health', func.name, JSON.stringify(final)); + expire('health', 900); console.timeEnd(func.name); setTimeout(invoker, final && final.delay ? final.delay : 10000); }); @@ -56,13 +56,13 @@ function steamApi(cb) { } function seqNumDelay(cb) { - utility.getData(utility.generateJob('api_history', {}).url, (err, body) => { + getData(generateJob('api_history', {}).url, (err, body) => { if (err) { return cb('failed to get current sequence number'); } // get match_seq_num, compare with real seqnum const currSeqNum = body.result.matches[0].match_seq_num; - return redis.get('match_seq_num', (err, num) => { + return get('match_seq_num', (err, num) => { if (err) { return cb(err); } @@ -77,7 +77,7 @@ function seqNumDelay(cb) { } function parseDelay(cb) { - db.raw("select count(*) from queue where type = 'parse'").asCallback( + raw("select count(*) from queue where type = 'parse'").asCallback( (err, result) => { if (err) { return cb(err); @@ -91,7 +91,7 @@ function parseDelay(cb) { } function gcDelay(cb) { - redis.llen('gcQueue', (err, result) => { + llen('gcQueue', (err, result) => { if (err) { return cb(err); } @@ -103,7 +103,7 @@ function gcDelay(cb) { } function postgresUsage(cb) { - db.raw("select pg_database_size('yasp')").asCallback((err, result) => { + raw("select pg_database_size('yasp')").asCallback((err, result) => { if (err) { return cb(err); } @@ -115,7 +115,7 @@ function postgresUsage(cb) { } function cassandraUsage(cb) { - cassandra.execute( + execute( "select mean_partition_size, partitions_count from system.size_estimates where keyspace_name = 'yasp'", (err, result) => { if (err) { @@ -134,13 +134,13 @@ function cassandraUsage(cb) { } function redisUsage(cb) { - redis.info((err) => { + info((err) => { if (err) { return cb(err); } // console.log(info); return cb(err, { - metric: Number(redis.server_info.used_memory), + metric: Number(server_info.used_memory), threshold: 4 * 10 ** 9, }); }); diff --git a/svc/parser.js b/svc/parser.js index 6188adee0..7580f5445 100755 --- a/svc/parser.js +++ b/svc/parser.js @@ -5,16 +5,17 @@ * Stream is run through a series of processors to count/aggregate it into a single object * This object is passed to insertMatch to persist the data into the database. * */ -const cp = require('child_process'); -const async = require('async'); -const numCPUs = require('os').cpus().length; -const express = require('express'); -const utility = require('../util/utility'); -const getGcData = require('../util/getGcData'); -const config = require('../config'); -const queue = require('../store/queue'); -const queries = require('../store/queries'); +import { exec } from 'child_process'; +import { series } from 'async'; +import os from 'os'; +import express from 'express'; +import utility from '../util/utility.js'; +import getGcData from '../util/getGcData.js'; +import { PORT, PARSER_PORT, NODE_ENV, PARSER_HOST, PARSER_PARALLELISM } from '../config.js'; +import { runReliableQueue } from '../store/queue.js'; +import queries from '../store/queries.js'; +const numCPUs = os.cpus().length; const { insertMatchPromise } = queries; const { buildReplayUrl } = utility; @@ -22,19 +23,19 @@ const app = express(); app.get('/healthz', (req, res) => { res.end('ok'); }); -app.listen(config.PORT || config.PARSER_PORT); +app.listen(PORT || PARSER_PORT); function runParse(match, job, cb) { let { url } = match; - if (config.NODE_ENV === 'test') { + if (NODE_ENV === 'test') { url = `https://odota.github.io/testfiles/${match.match_id}_1.dem`; } console.log(new Date(), url); - cp.exec( + exec( `curl --max-time 180 --fail ${url} | ${ url && url.slice(-3) === 'bz2' ? 'bunzip2' : 'cat' } | curl -X POST -T - ${ - config.PARSER_HOST + PARSER_HOST } | node processors/createParsedDataBlob.js ${match.match_id}`, { shell: true, maxBuffer: 10 * 1024 * 1024 }, async (err, stdout) => { @@ -57,7 +58,7 @@ function runParse(match, job, cb) { function parseProcessor(job, cb) { const match = job; - async.series( + series( { getDataSource(cb) { getGcData(match, (err, result) => { @@ -87,8 +88,8 @@ function parseProcessor(job, cb) { ); } -queue.runReliableQueue( +runReliableQueue( 'parse', - Number(config.PARSER_PARALLELISM) || numCPUs, + Number(PARSER_PARALLELISM) || numCPUs, parseProcessor ); diff --git a/svc/profiler.js b/svc/profiler.js index 39c614516..292766c48 100644 --- a/svc/profiler.js +++ b/svc/profiler.js @@ -1,17 +1,16 @@ /** * Worker to fetch updated player profiles * */ -const async = require('async'); -const queries = require('../store/queries'); -const db = require('../store/db'); -// const redis = require('../store/redis'); -const utility = require('../util/utility'); +import { each } from 'async'; +import queries from '../store/queries.js'; +import db, { raw } from '../store/db.js'; +import utility from '../util/utility.js'; const { insertPlayer, bulkIndexPlayer } = queries; const { getData, generateJob, convert64to32 } = utility; function getSummaries(cb) { - db.raw( + raw( 'SELECT account_id from players TABLESAMPLE SYSTEM_ROWS(100)' ).asCallback((err, result) => { if (err) { @@ -61,7 +60,7 @@ function getSummaries(cb) { }); // player summaries response - return async.each( + return each( results, (player, cb) => { insertPlayer(db, player, false, cb); diff --git a/svc/proplayers.js b/svc/proplayers.js index 62b9ae404..77167b2d8 100644 --- a/svc/proplayers.js +++ b/svc/proplayers.js @@ -1,7 +1,7 @@ -const async = require('async'); -const db = require('../store/db'); -const queries = require('../store/queries'); -const utility = require('../util/utility'); +import { each } from 'async'; +import db from '../store/db.js'; +import { upsert } from '../store/queries.js'; +import utility from '../util/utility.js'; const { invokeInterval, generateJob, getData } = utility; @@ -11,10 +11,10 @@ function doProPlayers(cb) { if (err) { return cb(err); } - return async.each( + return each( body.player_infos, (p, cb) => { - queries.upsert( + upsert( db, 'notable_players', p, diff --git a/svc/proxy.js b/svc/proxy.js index 3d7d57761..bdd2411e0 100644 --- a/svc/proxy.js +++ b/svc/proxy.js @@ -1,17 +1,17 @@ /** * Worker proxying requests to the Steam API. * */ -const httpProxy = require('http-proxy'); -const http = require('http'); -const config = require('../config'); +import { createProxyServer } from 'http-proxy'; +import { createServer } from 'http'; +import { PORT as _PORT, PROXY_PORT } from '../config.js'; -const PORT = config.PORT || config.PROXY_PORT; -const proxy = httpProxy.createProxyServer({ +const PORT = _PORT || PROXY_PORT; +const proxy = createProxyServer({ target: 'http://api.steampowered.com', changeOrigin: true, }); -const server = http.createServer((req, res) => { +const server = createServer((req, res) => { if (req.url === '/healthz') { return res.end('ok'); } diff --git a/svc/repatch.js b/svc/repatch.js index 711713361..b79e09b21 100644 --- a/svc/repatch.js +++ b/svc/repatch.js @@ -1,31 +1,31 @@ /** * Periodically recalculate patch ID for matches in match table * */ -const async = require('async'); -const constants = require('dotaconstants'); -const db = require('../store/db'); -const queries = require('../store/queries'); -const utility = require('../util/utility'); +import { eachSeries } from 'async'; +import { patch as _patch } from 'dotaconstants'; +import db, { select } from '../store/db.js'; +import { upsert } from '../store/queries.js'; +import utility, { getPatchIndex } from '../util/utility.js'; const { invokeInterval } = utility; function rePatch() { - db.select(['match_id', 'start_time']) + select(['match_id', 'start_time']) .from('matches') .asCallback((err, matchIds) => { if (err) { throw err; } - async.eachSeries( + eachSeries( matchIds, (match, cb) => { - queries.upsert( + upsert( db, 'match_patch', { match_id: match.match_id, patch: - constants.patch[utility.getPatchIndex(match.start_time)].name, + _patch[getPatchIndex(match.start_time)].name, }, { match_id: match.match_id, diff --git a/svc/retriever.js b/svc/retriever.js index 122192a2c..73ca0ef96 100644 --- a/svc/retriever.js +++ b/svc/retriever.js @@ -2,21 +2,19 @@ * Worker interfacing with the Steam GC. * Provides HTTP endpoints for other workers. * */ -const Steam = require('steam'); -const Dota2 = require('dota2'); -const async = require('async'); -const express = require('express'); -const compression = require('compression'); -const cp = require('child_process'); -const os = require('os'); -const config = require('../config'); - -const advancedAuth = config.ENABLE_RETRIEVER_ADVANCED_AUTH +import { servers, SteamClient, SteamUser, EResult } from 'steam'; +import { Dota2Client } from 'dota2'; +import { each } from 'async'; +import express from 'express'; +import compression from 'compression'; +import { execSync } from 'child_process'; +import { uptime as _uptime, hostname as _hostname } from 'os'; +import { ENABLE_RETRIEVER_ADVANCED_AUTH, PORT, RETRIEVER_PORT, STEAM_USER, STEAM_PASS, STEAM_ACCOUNT_DATA, RETRIEVER_SECRET, NODE_ENV } from '../config.js'; + +const advancedAuth = ENABLE_RETRIEVER_ADVANCED_AUTH ? { - /* eslint-disable global-require */ - redis: require('../store/redis'), - crypto: require('crypto'), - /* eslint-enable global-require */ + // redis + // crypto pendingTwoFactorAuth: {}, pendingSteamGuardAuth: {}, } @@ -30,7 +28,7 @@ const timeoutMs = 5000; const accountsToUse = 5; // maybe can do 1000 per IP now? const matchRequestLimit = 600; -const port = config.PORT || config.RETRIEVER_PORT; +const port = PORT || RETRIEVER_PORT; const matchRequestDelay = 500; const matchRequestDelayStep = 3; @@ -41,11 +39,11 @@ let matchSuccesses = 0; let profileRequests = 0; let profileSuccesses = 0; let allReady = false; -let users = config.STEAM_USER.split(','); -let passes = config.STEAM_PASS.split(','); +let users = STEAM_USER.split(','); +let passes = STEAM_PASS.split(','); // For the latest list: https://api.steampowered.com/ISteamDirectory/GetCMList/v1/?format=json&cellid=0 -Steam.servers = [ +servers = [ { host: '155.133.242.9', port: 27018 }, { host: '185.25.180.15', port: 27019 }, { host: '185.25.180.15', port: 27018 }, @@ -138,7 +136,7 @@ function getUptime() { } function getServerUptime() { - return os.uptime(); + return _uptime(); } function genStats() { @@ -149,7 +147,7 @@ function genStats() { profileSuccesses, uptime: getUptime(), serverUptime: getServerUptime(), - hostname: os.hostname(), + hostname: _hostname(), numReadyAccounts: Object.keys(steamObj).length, totalAccounts: users.length, }; @@ -223,13 +221,13 @@ function getGcMatchData(idx, matchId, cb) { } function init() { - async.each( + each( Array.from(new Array(Math.min(accountsToUse, users.length)), (v, i) => i), (i, cb) => { - const client = new Steam.SteamClient(); - client.steamUser = new Steam.SteamUser(client); + const client = new SteamClient(); + client.steamUser = new SteamUser(client); // client.steamFriends = new Steam.SteamFriends(client); - client.Dota2 = new Dota2.Dota2Client(client, false); + client.Dota2 = new Dota2Client(client, false); client.Dota2.on('ready', () => { console.log('acct %s ready', i); // As of 2023-06-04 seeing some double ready which crashes the process @@ -274,7 +272,7 @@ function init() { } */ - if (logOnResp.eresult !== Steam.EResult.OK) { + if (logOnResp.eresult !== EResult.OK) { console.error(logOnResp); // give up // cb(); @@ -398,9 +396,8 @@ function init() { } let accountData = []; -if (config.STEAM_ACCOUNT_DATA) { - accountData = cp - .execSync(`curl '${config.STEAM_ACCOUNT_DATA}'`, { +if (STEAM_ACCOUNT_DATA) { + accountData = execSync(`curl '${STEAM_ACCOUNT_DATA}'`, { maxBuffer: 8 * 1024 * 1024, }) .toString() @@ -427,7 +424,7 @@ app.get('/healthz', (req, res, cb) => { return res.end('ok'); }); app.use((req, res, cb) => { - if (config.RETRIEVER_SECRET && config.RETRIEVER_SECRET !== req.query.key) { + if (RETRIEVER_SECRET && RETRIEVER_SECRET !== req.query.key) { // reject request if it doesn't have key return cb('invalid key'); } @@ -501,7 +498,7 @@ app.use((req, res, cb) => { // (matchSuccesses / matchRequests < 0.1 && matchRequests > 100 && getUptime() > minUpTimeSeconds) || (matchRequests > matchRequestLimit && getUptime() > minUpTimeSeconds) || (!allReady && getUptime() > minUpTimeSeconds); - if (shouldRestart && config.NODE_ENV !== 'development') { + if (shouldRestart && NODE_ENV !== 'development') { return selfDestruct(); } if (!allReady) { diff --git a/svc/scanner.js b/svc/scanner.js index 6641d8327..e599167d3 100755 --- a/svc/scanner.js +++ b/svc/scanner.js @@ -4,16 +4,15 @@ * The endpoint usually takes around 2 seconds to return data * Therefore each IP should generally avoid requesting more than once every 10 seconds * */ -const async = require('async'); -const utility = require('../util/utility'); -const config = require('../config'); -const redis = require('../store/redis'); -const queries = require('../store/queries'); +import { each } from 'async'; +import utility, { redisCount } from '../util/utility.js'; +import { SCANNER_DELAY, SCANNER_PERCENT, START_SEQ_NUM, NODE_ENV } from '../config.js'; +import queries, { insertMatchPromise } from '../store/queries.js'; +import redis from '../store/redis.js'; -const { insertMatch } = queries; const { getData, generateJob } = utility; // const api_hosts = config.STEAM_API_HOST.split(','); -const delay = Number(config.SCANNER_DELAY); +const delay = Number(SCANNER_DELAY); const PAGE_SIZE = 100; function scanApi(seqNum) { @@ -28,7 +27,7 @@ function scanApi(seqNum) { return cb(err); } // Optionally throttle inserts to prevent overload - if (match.match_id % 100 >= Number(config.SCANNER_PERCENT)) { + if (match.match_id % 100 >= Number(SCANNER_PERCENT)) { return finishMatch(null, cb); } // check if match was previously processed @@ -41,7 +40,7 @@ function scanApi(seqNum) { // don't insert this match if we already processed it recently if (!result) { try { - await queries.insertMatchPromise(match, { + await insertMatchPromise(match, { type: 'api', origin: 'scanner', }); @@ -71,7 +70,7 @@ function scanApi(seqNum) { // On non-retryable error, increment match seq num by 1 and continue if (err.result.status === 2) { nextSeqNum += 1; - utility.redisCount(redis, 'skip_seq_num'); + redisCount(redis, 'skip_seq_num'); return cb(); } return cb(err); @@ -89,7 +88,7 @@ function scanApi(seqNum) { matchSeqNum, resp.length ); - return async.each(resp, processMatch, cb); + return each(resp, processMatch, cb); } ); } @@ -109,7 +108,7 @@ function scanApi(seqNum) { processPage(seqNum, finishPageSet); } -if (config.START_SEQ_NUM) { +if (START_SEQ_NUM) { redis.get('match_seq_num', (err, result) => { if (err || !result) { throw new Error( @@ -119,7 +118,7 @@ if (config.START_SEQ_NUM) { const numResult = Number(result); scanApi(numResult); }); -} else if (config.NODE_ENV !== 'production') { +} else if (NODE_ENV !== 'production') { // Never do this in production to avoid skipping sequence number if we didn't pull .env properly const container = generateJob('api_history', {}); getData(container.url, (err, data) => { diff --git a/svc/scenarios.js b/svc/scenarios.js index abcdba28e..d18710b28 100644 --- a/svc/scenarios.js +++ b/svc/scenarios.js @@ -1,31 +1,31 @@ -const async = require('async'); -const util = require('util'); -const queue = require('../store/queue'); -const buildMatch = require('../store/buildMatch'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const su = require('../util/scenariosUtil'); +import { eachSeries } from 'async'; +import { format } from 'util'; +import { runQueue } from '../store/queue.js'; +import buildMatch from '../store/buildMatch.js'; +import { raw } from '../store/db.js'; +import { epochWeek } from '../util/utility.js'; +import { validateMatchProperties, scenarioChecks } from '../util/scenariosUtil.js'; async function processScenarios(matchID, cb) { try { const match = await buildMatch(matchID); - if (!su.validateMatchProperties(match)) { + if (!validateMatchProperties(match)) { console.error( `Skipping scenario checks for match ${matchID}. Invalid match object.` ); return cb(); } - const currentWeek = utility.epochWeek(); - Object.keys(su.scenarioChecks).forEach((table) => { - su.scenarioChecks[table].forEach((scenarioCheck) => { + const currentWeek = epochWeek(); + Object.keys(scenarioChecks).forEach((table) => { + scenarioChecks[table].forEach((scenarioCheck) => { const rows = scenarioCheck(match); - async.eachSeries(rows, (row, cb) => { + eachSeries(rows, (row, cb) => { row = Object.assign(row, { epoch_week: currentWeek, wins: row.wins ? '1' : '0', }); const values = Object.keys(row).map(() => '?'); - const query = util.format( + const query = format( 'INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO UPDATE SET wins = %s.wins + EXCLUDED.wins, games = %s.games + 1', table, Object.keys(row).join(','), @@ -36,7 +36,7 @@ async function processScenarios(matchID, cb) { table, table ); - db.raw( + raw( query, Object.keys(row).map((key) => row[key]) ).asCallback(cb); @@ -49,4 +49,4 @@ async function processScenarios(matchID, cb) { } } -queue.runQueue('scenariosQueue', 1, processScenarios); +runQueue('scenariosQueue', 1, processScenarios); diff --git a/svc/scenariosCleanup.js b/svc/scenariosCleanup.js index 6290e9c64..ffcce866e 100644 --- a/svc/scenariosCleanup.js +++ b/svc/scenariosCleanup.js @@ -1,11 +1,11 @@ -const async = require('async'); -const db = require('../store/db'); -const config = require('../config'); -const utility = require('../util/utility'); +import { parallel } from 'async'; +import db, { raw } from '../store/db.js'; +import { MAXIMUM_AGE_SCENARIOS_ROWS } from '../config.js'; +import { epochWeek, invokeInterval } from '../util/utility.js'; function clearScenariosTables(cb) { - const currentWeek = utility.epochWeek(); - async.parallel( + const currentWeek = epochWeek(); + parallel( [ (cb) => { db('team_scenarios') @@ -13,7 +13,7 @@ function clearScenariosTables(cb) { .orWhere( 'epoch_week', '<=', - currentWeek - config.MAXIMUM_AGE_SCENARIOS_ROWS + currentWeek - MAXIMUM_AGE_SCENARIOS_ROWS ) .del() .asCallback(cb); @@ -24,13 +24,13 @@ function clearScenariosTables(cb) { .orWhere( 'epoch_week', '<=', - currentWeek - config.MAXIMUM_AGE_SCENARIOS_ROWS + currentWeek - MAXIMUM_AGE_SCENARIOS_ROWS ) .del() .asCallback(cb); }, (cb) => { - db.raw( + raw( "DELETE from public_matches where start_time < extract(epoch from now() - interval '6 month')::int" ).asCallback(cb); }, @@ -38,7 +38,7 @@ function clearScenariosTables(cb) { // db.raw('delete from match_gcdata where match_id not in (select match_id from matches) and match_id < (select max(match_id) - 50000000 from match_gcdata)').asCallback(cb); }, (cb) => { - db.raw( + raw( 'delete from hero_search where match_id < (select max(match_id) - 150000000 from hero_search)' ).asCallback(cb); }, @@ -47,4 +47,4 @@ function clearScenariosTables(cb) { ); } -utility.invokeInterval(clearScenariosTables, 1000 * 60 * 60 * 6); +invokeInterval(clearScenariosTables, 1000 * 60 * 60 * 6); diff --git a/svc/syncSubs.js b/svc/syncSubs.js index 096f81ee3..82e6a958e 100644 --- a/svc/syncSubs.js +++ b/svc/syncSubs.js @@ -1,12 +1,12 @@ /** * Function to sync subs between Stripe and DB * */ -const db = require('../store/db'); -const utility = require('../util/utility'); -const config = require('../config'); -const stripeLib = require('stripe'); +import { raw } from '../store/db.js'; +import utility from '../util/utility.js'; +import { STRIPE_SECRET } from '../config.js'; +import stripeLib from 'stripe'; -const stripe = stripeLib(config.STRIPE_SECRET); +const stripe = stripeLib(STRIPE_SECRET); s; const { invokeInterval } = utility; @@ -21,18 +21,18 @@ async function run(cb) { result.push(sub); } console.log(result.length, 'subs'); - await db.raw('BEGIN TRANSACTION'); + await raw('BEGIN TRANSACTION'); // Delete all status from subscribers - await db.raw('UPDATE subscriber SET status = NULL'); + await raw('UPDATE subscriber SET status = NULL'); for (let i = 0; i < result.length; i++) { const sub = result[i]; // Mark list of subscribers as active - await db.raw('UPDATE subscriber SET status = ? WHERE customer_id = ?', [ + await raw('UPDATE subscriber SET status = ? WHERE customer_id = ?', [ sub.status, sub.customer, ]); } - await db.raw('COMMIT'); + await raw('COMMIT'); } invokeInterval(run, 60 * 1000); diff --git a/svc/teams.js b/svc/teams.js index 470445feb..1dab043f0 100644 --- a/svc/teams.js +++ b/svc/teams.js @@ -1,18 +1,18 @@ -const async = require('async'); -const db = require('../store/db'); -const utility = require('../util/utility'); -const queries = require('../store/queries'); +import { eachSeries } from 'async'; +import db, { raw as _raw } from '../store/db.js'; +import utility, { generateJob, getData } from '../util/utility.js'; +import { upsert } from '../store/queries.js'; const { invokeInterval } = utility; function doTeams(cb) { - db.raw( + _raw( 'select distinct team_id from team_match order by team_id desc' ).asCallback((err, result) => { if (err) { return cb(err); } - return async.eachSeries( + return eachSeries( result.rows, (m, cb) => { if (!m.team_id) { @@ -25,10 +25,10 @@ function doTeams(cb) { team_id: m.team_id || 2, }); */ - const container = utility.generateJob('api_team_info_by_team_id', { + const container = generateJob('api_team_info_by_team_id', { start_at_team_id: m.team_id, }); - return utility.getData( + return getData( { url: container.url, raw: true }, (err, body) => { if (err) { @@ -46,20 +46,20 @@ function doTeams(cb) { const logoRegex = /^"logo":(.*),$/m; const match = logoRegex.exec(raw); const logoUgc = match[1]; - const ugcJob = utility.generateJob('api_get_ugc_file_details', { + const ugcJob = generateJob('api_get_ugc_file_details', { ugcid: logoUgc, }); - const cdnJob = utility.generateJob('steam_cdn_team_logos', { + const cdnJob = generateJob('steam_cdn_team_logos', { team_id: m.team_id, }); // Steam's CDN sometimes has better versions of team logos available - return utility.getData( + return getData( { url: cdnJob.url, noRetry: true }, (err, body) => { if (!err && body) { t.team_id = m.team_id; t.logo_url = cdnJob.url; - return queries.upsert( + return upsert( db, 'teams', t, @@ -69,7 +69,7 @@ function doTeams(cb) { cb ); } - return utility.getData( + return getData( { url: ugcJob.url, noRetry: true }, (err, body) => { if (err) { @@ -80,7 +80,7 @@ function doTeams(cb) { if (body && body.data) { t.logo_url = body.data.url; } - return queries.upsert( + return upsert( db, 'teams', t, diff --git a/svc/web.js b/svc/web.js index 6879e5a03..16a7ed595 100644 --- a/svc/web.js +++ b/svc/web.js @@ -2,36 +2,35 @@ * Worker serving as main web application * Serves web/API requests * */ -const request = require('request'); -const compression = require('compression'); -const session = require('cookie-session'); -const moment = require('moment'); -const express = require('express'); -// const requestIp = require('request-ip'); -const passport = require('passport'); -const SteamStrategy = require('passport-steam').Strategy; -const cors = require('cors'); -const bodyParser = require('body-parser'); -const stripeLib = require('stripe'); -const keys = require('../routes/keyManagement'); -const api = require('../routes/api'); -const queries = require('../store/queries'); -const db = require('../store/db'); -const redis = require('../store/redis'); -const utility = require('../util/utility'); -const config = require('../config'); +import request from 'request'; +import compression from 'compression'; +import session from 'cookie-session'; +import moment from 'moment'; +import express from 'express'; +import { serializeUser, deserializeUser, use, initialize, session as _session, authenticate } from 'passport'; +import { Strategy as SteamStrategy } from 'passport-steam'; +import cors from 'cors'; +import { json } from 'body-parser'; +import stripeLib from 'stripe'; +import keys from '../routes/keyManagement.js'; +import api from '../routes/api.js'; +import { insertPlayer } from '../store/queries.js'; +import db, { raw } from '../store/db.js'; +import utility, { getStartOfBlockMinutes, getEndOfMonth } from '../util/utility.js'; +import { STRIPE_SECRET, STEAM_API_KEY, ROOT_URL, COOKIE_DOMAIN, SESSION_SECRET, NODE_ENV, ENABLE_API_LIMIT, API_KEY_PER_MIN_LIMIT, NO_API_KEY_PER_MIN_LIMIT, API_FREE_LIMIT, UI_HOST, PORT, FRONTEND_PORT } from '../config.js'; +import redis from '../store/redis.js'; -const stripe = stripeLib(config.STRIPE_SECRET); +const stripe = stripeLib(STRIPE_SECRET); const { redisCount } = utility; const app = express(); -const apiKey = config.STEAM_API_KEY.split(',')[0]; -const host = config.ROOT_URL; +const apiKey = STEAM_API_KEY.split(',')[0]; +const host = ROOT_URL; const sessOptions = { - domain: config.COOKIE_DOMAIN, + domain: COOKIE_DOMAIN, maxAge: 52 * 7 * 24 * 60 * 60 * 1000, - secret: config.SESSION_SECRET, + secret: SESSION_SECRET, }; const whitelistedPaths = [ @@ -50,15 +49,15 @@ const pathCosts = { }; // PASSPORT config -passport.serializeUser((user, done) => { +serializeUser((user, done) => { done(null, user.account_id); }); -passport.deserializeUser((accountId, done) => { +deserializeUser((accountId, done) => { done(null, { account_id: accountId, }); }); -passport.use( +use( new SteamStrategy( { providerURL: 'https://steamcommunity.com/openid', @@ -69,7 +68,7 @@ passport.use( (identifier, profile, cb) => { const player = profile._json; player.last_login = new Date(); - queries.insertPlayer(db, player, true, (err) => { + insertPlayer(db, player, true, (err) => { if (err) { return cb(err); } @@ -98,13 +97,13 @@ app.route('/healthz').get((req, res) => { }); // Session/Passport middleware app.use(session(sessOptions)); -app.use(passport.initialize()); -app.use(passport.session()); +app.use(initialize()); +app.use(_session()); // Get client IP to use for rate limiting; // app.use(requestIp.mw()); // Dummy User ID for testing -if (config.NODE_ENV === 'test') { +if (NODE_ENV === 'test') { app.use((req, res, cb) => { if (req.query.loggedin) { req.user = { @@ -127,7 +126,7 @@ app.use((req, res, cb) => { (req.headers.authorization && req.headers.authorization.replace('Bearer ', '')) || req.query.api_key; - if (config.ENABLE_API_LIMIT && apiKey) { + if (ENABLE_API_LIMIT && apiKey) { redis.sismember('api_keys', apiKey, (err, resp) => { if (err) { cb(err); @@ -153,17 +152,17 @@ app.use((req, res, cb) => { req.headers.authorization.replace('Bearer ', '')) || req.query.api_key; res.locals.usageIdentifier = requestAPIKey; - rateLimit = config.API_KEY_PER_MIN_LIMIT; + rateLimit = API_KEY_PER_MIN_LIMIT; // console.log('[KEY] %s visit %s, ip %s', requestAPIKey, req.originalUrl, ip); } else { res.locals.usageIdentifier = ip; - rateLimit = config.NO_API_KEY_PER_MIN_LIMIT; + rateLimit = NO_API_KEY_PER_MIN_LIMIT; // console.log('[USER] %s visit %s, ip %s', req.user ? req.user.account_id : 'anonymous', req.originalUrl, ip); } const multi = redis .multi() .hincrby('rate_limit', res.locals.usageIdentifier, pathCosts[req.path] || 1) - .expireat('rate_limit', utility.getStartOfBlockMinutes(1, 1)); + .expireat('rate_limit', getStartOfBlockMinutes(1, 1)); if (!res.locals.isAPIRequest) { multi.zscore('user_usage_count', res.locals.usageIdentifier); // not API request so check previous usage. @@ -182,22 +181,22 @@ app.use((req, res, cb) => { if (!res.locals.isAPIRequest) { res.set( 'X-Rate-Limit-Remaining-Month', - config.API_FREE_LIMIT - Number(resp[2]) + API_FREE_LIMIT - Number(resp[2]) ); } - if (config.NODE_ENV === 'development') { + if (NODE_ENV === 'development') { console.log('rate limit increment', resp); } - if (resp[0] > rateLimit && config.NODE_ENV !== 'test') { + if (resp[0] > rateLimit && NODE_ENV !== 'test') { return res.status(429).json({ error: 'rate limit exceeded', }); } if ( - config.ENABLE_API_LIMIT && + ENABLE_API_LIMIT && !whitelistedPaths.includes(req.path) && !res.locals.isAPIRequest && - Number(resp[2]) >= config.API_FREE_LIMIT + Number(resp[2]) >= API_FREE_LIMIT ) { return res.status(429).json({ error: 'monthly api limit exceeded', @@ -213,7 +212,7 @@ app.use((req, res, cb) => { res.once('finish', () => { const timeEnd = new Date(); const elapsed = timeEnd - timeStart; - if (elapsed > 2000 || config.NODE_ENV === 'development') { + if (elapsed > 2000 || NODE_ENV === 'development') { console.log('[SLOWLOG] %s, %s', req.originalUrl, elapsed); } @@ -230,15 +229,15 @@ app.use((req, res, cb) => { if (res.locals.isAPIRequest) { multi .hincrby('usage_count', res.locals.usageIdentifier, 1) - .expireat('usage_count', utility.getEndOfMonth()); + .expireat('usage_count', getEndOfMonth()); } else { multi .zincrby('user_usage_count', 1, res.locals.usageIdentifier) - .expireat('user_usage_count', utility.getEndOfMonth()); + .expireat('user_usage_count', getEndOfMonth()); } multi.exec((err, res) => { - if (config.NODE_ENV === 'development') { + if (NODE_ENV === 'development') { console.log('usage count increment', err, res); } }); @@ -268,7 +267,7 @@ app.use((req, res, next) => { if ( req.method !== 'GET' && req.header('Origin') && - req.header('Origin') !== config.UI_HOST + req.header('Origin') !== UI_HOST ) { // Make an exception for replay parse request if (req.method === 'POST' && req.path.startsWith('/api/request/')) { @@ -285,19 +284,19 @@ app.use( credentials: true, }) ); -app.use(bodyParser.json()); +app.use(json()); app.route('/login').get( - passport.authenticate('steam', { + authenticate('steam', { failureRedirect: '/api', }) ); app.route('/return').get( - passport.authenticate('steam', { + authenticate('steam', { failureRedirect: '/api', }), (req, res) => { - if (config.UI_HOST) { - return res.redirect(`${config.UI_HOST}/players/${req.user.account_id}`); + if (UI_HOST) { + return res.redirect(`${UI_HOST}/players/${req.user.account_id}`); } return res.redirect('/api'); } @@ -305,8 +304,8 @@ app.route('/return').get( app.route('/logout').get((req, res) => { req.logout(); req.session = null; - if (config.UI_HOST) { - return res.redirect(config.UI_HOST); + if (UI_HOST) { + return res.redirect(UI_HOST); } return res.redirect('/api'); }); @@ -322,18 +321,18 @@ app.route('/subscribeSuccess').get(async (req, res) => { const customer = await stripe.customers.retrieve(session.customer); const accountId = req.user.account_id; // associate the customer id with the steam account ID (req.user.account_id) - await db.raw( + await raw( 'INSERT INTO subscriber(account_id, customer_id, status) VALUES (?, ?, ?) ON CONFLICT(account_id) DO UPDATE SET account_id = EXCLUDED.account_id, customer_id = EXCLUDED.customer_id, status = EXCLUDED.status', [accountId, customer.id, 'active'] ); // Send the user back to the subscribe page - return res.redirect(`${config.UI_HOST}/subscribe`); + return res.redirect(`${UI_HOST}/subscribe`); }); app.route('/manageSub').post(async (req, res) => { if (!req.user?.account_id) { return res.status(400).json({ error: 'no account ID' }); } - const result = await db.raw( + const result = await raw( "SELECT customer_id FROM subscriber where account_id = ? AND status = 'active'", [req.user.account_id] @@ -363,7 +362,7 @@ app.use((req, res) => app.use((err, req, res, cb) => { console.log('[ERR]', req.originalUrl); redisCount(redis, '500_error'); - if (config.NODE_ENV === 'development' || config.NODE_ENV === 'test') { + if (NODE_ENV === 'development' || NODE_ENV === 'test') { // default express handler return cb(err); } @@ -372,7 +371,7 @@ app.use((err, req, res, cb) => { error: 'Internal Server Error', }); }); -const port = config.PORT || config.FRONTEND_PORT; +const port = PORT || FRONTEND_PORT; const server = app.listen(port, () => { console.log('[WEB] listening on %s', port); }); @@ -397,4 +396,4 @@ function gracefulShutdown() { process.once('SIGTERM', gracefulShutdown); // listen for INT signal e.g. Ctrl-C process.once('SIGINT', gracefulShutdown); -module.exports = app; +export default app; diff --git a/test/test.js b/test/test.cjs similarity index 100% rename from test/test.js rename to test/test.cjs diff --git a/util/benchmarksUtil.js b/util/benchmarksUtil.js index 968cce3f1..ab4c30766 100644 --- a/util/benchmarksUtil.js +++ b/util/benchmarksUtil.js @@ -28,6 +28,6 @@ const benchmarks = { // }, }; -module.exports = { +export default { benchmarks, }; diff --git a/util/compute.js b/util/compute.js index 3eba72b27..fd2c1e7db 100644 --- a/util/compute.js +++ b/util/compute.js @@ -1,8 +1,8 @@ -const constants = require('dotaconstants'); -const utility = require('./utility'); +import constants from 'dotaconstants'; +import utility from './utility.js'; +const { ancients, heroes, cluster } = constants; const { max, min, isRadiant } = utility; -const { ancients } = constants; /** * Count the words that occur in a set of messages @@ -44,13 +44,13 @@ function countWords(playerMatch, playerFilter) { * Computes additional properties from a match/player_match * */ function computeMatchData(pm) { - const selfHero = constants.heroes[pm.hero_id]; + const selfHero = heroes[pm.hero_id]; // Compute patch based on start_time if (pm.start_time) { pm.patch = utility.getPatchIndex(pm.start_time); } if (pm.cluster) { - pm.region = constants.cluster[pm.cluster]; + pm.region = cluster[pm.cluster]; } if (pm.player_slot !== undefined && pm.radiant_win !== undefined) { pm.isRadiant = isRadiant(pm); @@ -241,6 +241,6 @@ function computeMatchData(pm) { } } -module.exports = { +export default { computeMatchData, }; diff --git a/util/filter.js b/util/filter.js index 1c63d4875..62aa2e7c9 100644 --- a/util/filter.js +++ b/util/filter.js @@ -1,4 +1,4 @@ -const utility = require('./utility'); +import utility from './utility.js'; const { isRadiant } = utility; @@ -144,4 +144,4 @@ function filter(matches, filters) { } return filtered; } -module.exports = filter; +export default filter; diff --git a/util/filterDeps.js b/util/filterDeps.js index 763e7fb19..f6ba9f473 100644 --- a/util/filterDeps.js +++ b/util/filterDeps.js @@ -1,7 +1,7 @@ /** * Object listing dependent columns for each filter * */ -module.exports = { +export default { win: ['player_slot', 'radiant_win'], patch: ['patch'], game_mode: ['game_mode'], diff --git a/util/getGcData.js b/util/getGcData.js index 2512a343d..e2c2524f2 100644 --- a/util/getGcData.js +++ b/util/getGcData.js @@ -2,20 +2,20 @@ * Issues a request to the retriever to get GC (Game Coordinator) data for a match * Calls back with an object containing the GC data * */ -const moment = require('moment'); -const { promisify } = require('util'); -const utility = require('./utility'); -const config = require('../config'); -const queries = require('../store/queries'); -const db = require('../store/db'); -const redis = require('../store/redis'); +import moment from 'moment'; +import { promisify } from 'util'; +import utility, { getRetrieverArr } from './utility.js'; +import { RETRIEVER_SECRET } from '../config.js'; +import queries, { upsert } from '../store/queries.js'; +import db, { raw } from '../store/db.js'; +import redis from '../store/redis.js'; -const secret = config.RETRIEVER_SECRET; +const secret = RETRIEVER_SECRET; const { getData, redisCount } = utility; const { insertMatchPromise } = queries; async function getGcDataFromRetriever(match, cb) { - const retrieverArr = utility.getRetrieverArr(match.useGcDataArr); + const retrieverArr = getRetrieverArr(match.useGcDataArr); // make array of retriever urls and use a random one on each retry const urls = retrieverArr.map( (r) => `http://${r}?key=${secret}&match_id=${match.match_id}` @@ -71,7 +71,7 @@ async function getGcDataFromRetriever(match, cb) { skipParse: true, }); // Persist GC data to database - await promisify(queries.upsert)(db, 'match_gcdata', gcdata, { + await promisify(upsert)(db, 'match_gcdata', gcdata, { match_id: match.match_id, }); cb(null, gcdata); @@ -82,13 +82,13 @@ async function getGcDataFromRetriever(match, cb) { ); } -module.exports = async function getGcData(match, cb) { +export default async function getGcData(match, cb) { const matchId = match.match_id; if (!matchId || Number.isNaN(Number(matchId)) || Number(matchId) <= 0) { return cb(new Error('invalid match_id')); } // Check if we have it in DB already - const saved = await db.raw( + const saved = await raw( 'select match_id, cluster, replay_salt from match_gcdata where match_id = ?', [match.match_id] ); diff --git a/util/laneMappings.js b/util/laneMappings.js index a7b96afcb..2a0ea1f5d 100644 --- a/util/laneMappings.js +++ b/util/laneMappings.js @@ -22,4 +22,4 @@ for (let i = 0; i < 128; i += 1) { laneMappings[i].push(lane); } } -module.exports = laneMappings; +export default laneMappings; diff --git a/util/scenariosUtil.js b/util/scenariosUtil.js index 3694d2591..f38d1aa5e 100644 --- a/util/scenariosUtil.js +++ b/util/scenariosUtil.js @@ -1,12 +1,13 @@ -const constants = require('dotaconstants'); -const utility = require('./utility'); +import constants from 'dotaconstants'; +import utility from './utility.js'; const { playerWon } = utility; +const { items } = constants; // all items that cost at least 1400 const itemCost = 1400; -const dotaItems = Object.keys(constants.items) - .map((k) => [constants.items[k], k]) +const dotaItems = Object.keys(items) + .map((k) => [items[k], k]) .filter((x) => x[0].cost >= itemCost) .map((x) => x[1]); const timings = [7.5, 10, 12, 15, 20, 25, 30].map((x) => x * 60); @@ -180,7 +181,7 @@ function validateMatchProperties(match) { ); } -module.exports = { +export default { scenarioChecks, validateMatchProperties, teamScenariosQueryParams, diff --git a/util/utility.js b/util/utility.js index bde7694b3..9fb9e1b9f 100644 --- a/util/utility.js +++ b/util/utility.js @@ -2,16 +2,18 @@ * Provides utility functions. * All functions should have external dependencies (DB, etc.) passed as parameters * */ -const constants = require('dotaconstants'); -const request = require('request'); -const Long = require('long'); -const urllib = require('url'); -const uuidV4 = require('uuid/v4'); -const moment = require('moment'); -const crypto = require('crypto'); -const laneMappings = require('./laneMappings'); -const config = require('../config'); -const contributors = require('../CONTRIBUTORS'); +import constants from 'dotaconstants'; +import request from 'request'; +import Long from 'long'; +import { parse as _parse, format } from 'url'; +import {v4 as uuidV4} from 'uuid'; +import moment from 'moment'; +import { createHash } from 'crypto'; +import laneMappings from './laneMappings.js'; +import config from '../config.js'; +import contributors from '../CONTRIBUTORS.js'; + +const { game_mode, lobby_type, patch } = constants; /** * Tokenizes an input string. @@ -221,7 +223,7 @@ function getData(url, cb) { } else { u = url; } - const parse = urllib.parse(u, true); + const parse = _parse(u, true); const steamApi = parse.host === 'api.steampowered.com'; if (steamApi) { // choose an api key to use @@ -232,7 +234,7 @@ function getData(url, cb) { const apiHosts = config.STEAM_API_HOST.split(','); parse.host = apiHosts[Math.floor(Math.random() * apiHosts.length)]; } - const target = urllib.format(parse); + const target = format(parse); console.log('%s - getData: %s', new Date(), target); return setTimeout(() => { request( @@ -385,10 +387,10 @@ function mode(array) { * */ function isSignificant(match) { return Boolean( - constants.game_mode[match.game_mode] && - constants.game_mode[match.game_mode].balanced && - constants.lobby_type[match.lobby_type] && - constants.lobby_type[match.lobby_type].balanced && + game_mode[match.game_mode] && + game_mode[match.game_mode].balanced && + lobby_type[match.lobby_type] && + lobby_type[match.lobby_type].balanced && match.radiant_win !== undefined && match.duration > 360 && (match.players || []).every((player) => (player.gold_per_min || 0) < 2500) @@ -524,8 +526,8 @@ function median(data) { function getPatchIndex(startTime) { const date = new Date(startTime * 1000); let i; - for (i = 1; i < constants.patch.length; i += 1) { - const pd = new Date(constants.patch[i].date); + for (i = 1; i < patch.length; i += 1) { + const pd = new Date(patch[i].date); // stop when patch date is past the start time if (pd > date) { break; @@ -807,7 +809,7 @@ function getRetrieverArr(useGcDataArr) { const output = []; const arr = input.split(','); arr.forEach((element) => { - const parsedUrl = urllib.parse(`http://${element}`, true); + const parsedUrl = _parse(`http://${element}`, true); for (let i = 0; i < (parsedUrl.query.size || 1); i += 1) { output.push(parsedUrl.host); } @@ -872,11 +874,11 @@ function cleanItemSchema(input) { function checkIfInExperiment(ip, mod) { return ( - crypto.createHash('md5').update(ip).digest().readInt32BE(0) % 100 < mod + createHash('md5').update(ip).digest().readInt32BE(0) % 100 < mod ); } -module.exports = { +export default { tokenize, generateJob, getData,